2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2025 Nordix Foundation.
4 * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.clamp.acm.runtime.instantiation;
24 import jakarta.validation.Valid;
25 import jakarta.ws.rs.core.Response.Status;
26 import java.util.List;
27 import java.util.UUID;
28 import java.util.stream.Collectors;
29 import lombok.NonNull;
30 import lombok.RequiredArgsConstructor;
31 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
32 import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils;
33 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionAcHandler;
34 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
37 import org.onap.policy.clamp.models.acm.concepts.DeployState;
38 import org.onap.policy.clamp.models.acm.concepts.LockState;
39 import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
40 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
41 import org.onap.policy.clamp.models.acm.concepts.SubState;
42 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
43 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
44 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
45 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
46 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.SubOrder;
47 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
48 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
49 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
50 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
51 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
52 import org.onap.policy.common.parameters.BeanValidationResult;
53 import org.onap.policy.common.parameters.ObjectValidationResult;
54 import org.onap.policy.common.parameters.ValidationStatus;
55 import org.onap.policy.models.base.PfConceptKey;
56 import org.onap.policy.models.base.PfKey;
57 import org.onap.policy.models.base.PfModelRuntimeException;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60 import org.springframework.stereotype.Service;
61 import org.springframework.transaction.annotation.Transactional;
64 * This class is dedicated to the Instantiation of Commissioned automation composition.
68 @RequiredArgsConstructor
69 public class AutomationCompositionInstantiationProvider {
70 private static final String DO_NOT_MATCH = " do not match with ";
71 private static final String ELEMENT_ID_NOT_PRESENT = "Element id not present ";
72 private static final String NOT_VALID_ORDER =
73 "Not valid order %s; DeployState: %s; LockState: %s; SubState: %s; StateChangeResult: %s";
75 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionInstantiationProvider.class);
77 private final AutomationCompositionProvider automationCompositionProvider;
78 private final AcDefinitionProvider acDefinitionProvider;
79 private final AcInstanceStateResolver acInstanceStateResolver;
80 private final SupervisionAcHandler supervisionAcHandler;
81 private final ParticipantProvider participantProvider;
82 private final AcRuntimeParameterGroup acRuntimeParameterGroup;
83 private final EncryptionUtils encryptionUtils;
86 * Create automation composition.
88 * @param compositionId The UUID of the automation composition definition
89 * @param automationComposition the automation composition
90 * @return the result of the instantiation operation
92 public InstantiationResponse createAutomationComposition(UUID compositionId,
93 AutomationComposition automationComposition) {
94 if (!compositionId.equals(automationComposition.getCompositionId())) {
95 throw new PfModelRuntimeException(Status.BAD_REQUEST,
96 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
98 var checkAutomationCompositionOpt =
99 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
100 if (checkAutomationCompositionOpt.isPresent()) {
101 throw new PfModelRuntimeException(Status.BAD_REQUEST,
102 automationComposition.getKey().asIdentifier() + " already defined");
105 var validationResult = validateAutomationComposition(automationComposition);
106 if (!validationResult.isValid()) {
107 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
109 encryptInstanceProperties(automationComposition, compositionId);
110 automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
112 return createInstantiationResponse(automationComposition);
115 private InstantiationResponse createInstantiationResponse(AutomationComposition automationComposition) {
116 var response = new InstantiationResponse();
117 response.setInstanceId(automationComposition.getInstanceId());
118 response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
123 * Update automation composition.
125 * @param compositionId The UUID of the automation composition definition
126 * @param automationComposition the automation composition
127 * @return the result of the update
129 public InstantiationResponse updateAutomationComposition(UUID compositionId,
130 AutomationComposition automationComposition) {
131 var instanceId = automationComposition.getInstanceId();
132 var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
133 if (!compositionId.equals(acToUpdate.getCompositionId())) {
134 throw new PfModelRuntimeException(Status.BAD_REQUEST,
135 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
137 if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
138 acToUpdate.setElements(automationComposition.getElements());
139 acToUpdate.setName(automationComposition.getName());
140 acToUpdate.setVersion(automationComposition.getVersion());
141 acToUpdate.setDescription(automationComposition.getDescription());
142 acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
143 var validationResult = validateAutomationComposition(acToUpdate);
144 if (!validationResult.isValid()) {
145 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
147 encryptInstanceProperties(acToUpdate, compositionId);
148 automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
149 return createInstantiationResponse(automationComposition);
153 var deployOrder = DeployOrder.UPDATE;
154 var subOrder = SubOrder.NONE;
156 if (automationComposition.getCompositionTargetId() != null) {
158 if (Boolean.TRUE.equals(automationComposition.getPrecheck())) {
159 subOrder = SubOrder.MIGRATE_PRECHECK;
160 deployOrder = DeployOrder.NONE;
162 deployOrder = DeployOrder.MIGRATE;
165 var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder,
166 acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(),
167 acToUpdate.getStateChangeResult());
168 return switch (result) {
169 case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate);
171 case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate);
173 case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate);
175 default -> throw new PfModelRuntimeException(Status.BAD_REQUEST,
176 "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState());
181 * Update deployed AC Element properties.
183 * @param automationComposition the automation composition
184 * @param acToBeUpdated the composition to be updated
185 * @return the result of the update
187 private InstantiationResponse updateDeployedAutomationComposition(
188 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
190 // Iterate and update the element property values
191 for (var element : automationComposition.getElements().entrySet()) {
192 var elementId = element.getKey();
193 var dbAcElement = acToBeUpdated.getElements().get(elementId);
194 if (dbAcElement == null) {
195 throw new PfModelRuntimeException(Status.BAD_REQUEST, ELEMENT_ID_NOT_PRESENT + elementId);
197 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
200 var validationResult = validateAutomationComposition(acToBeUpdated);
201 if (!validationResult.isValid()) {
202 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
204 // Publish property update event to the participants
205 supervisionAcHandler.update(acToBeUpdated);
207 encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionId());
209 automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
210 return createInstantiationResponse(automationComposition);
213 private InstantiationResponse migrateAutomationComposition(
214 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
216 if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
217 throw new PfModelRuntimeException(Status.BAD_REQUEST,
218 "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
221 // Iterate and update the element property values
222 for (var element : automationComposition.getElements().entrySet()) {
223 var elementId = element.getKey();
224 var dbAcElement = acToBeUpdated.getElements().get(elementId);
225 // Add additional elements if present for migration
226 if (dbAcElement == null) {
227 LOGGER.info("New Ac element {} added in Migration", elementId);
228 acToBeUpdated.getElements().put(elementId, element.getValue());
230 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
231 var newDefinition = element.getValue().getDefinition().asConceptKey();
232 var dbElementDefinition = dbAcElement.getDefinition().asConceptKey();
233 checkCompatibility(newDefinition, dbElementDefinition, automationComposition.getInstanceId());
234 dbAcElement.setDefinition(element.getValue().getDefinition());
237 // Remove element which is not present in the new Ac instance
238 var elementsRemoved = getElementRemoved(acToBeUpdated, automationComposition);
239 elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().remove(uuid));
241 var validationResult =
242 validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
243 if (!validationResult.isValid()) {
244 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
246 acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
247 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
248 // Publish migrate event to the participants
249 supervisionAcHandler.migrate(acToBeUpdated, acDefinition.getServiceTemplate());
251 encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
253 var ac = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
254 elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
255 return createInstantiationResponse(ac);
258 private List<UUID> getElementRemoved(AutomationComposition acFromDb, AutomationComposition acFromMigration) {
259 return acFromDb.getElements().keySet().stream()
260 .filter(id -> acFromMigration.getElements().get(id) == null).toList();
263 void checkCompatibility(PfConceptKey newDefinition, PfConceptKey dbElementDefinition,
265 var compatibility = newDefinition.getCompatibility(dbElementDefinition);
266 if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
267 throw new PfModelRuntimeException(Status.BAD_REQUEST,
268 dbElementDefinition + " is not compatible with " + newDefinition);
270 if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR
271 .equals(compatibility)) {
272 LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ", instanceId, newDefinition,
273 compatibility, dbElementDefinition);
277 private InstantiationResponse migratePrecheckAc(
278 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
280 acToBeUpdated.setPrecheck(true);
281 var copyAc = new AutomationComposition(acToBeUpdated);
282 // Iterate and update the element property values
283 for (var element : automationComposition.getElements().entrySet()) {
284 var elementId = element.getKey();
285 var copyElement = copyAc.getElements().get(elementId);
286 // Add additional elements if present for migration
287 if (copyElement == null) {
288 LOGGER.info("New Ac element {} added in Migration", elementId);
289 copyAc.getElements().put(elementId, element.getValue());
291 AcmUtils.recursiveMerge(copyElement.getProperties(), element.getValue().getProperties());
292 var newDefinition = element.getValue().getDefinition().asConceptKey();
293 var copyElementDefinition = copyElement.getDefinition().asConceptKey();
294 checkCompatibility(newDefinition, copyElementDefinition, automationComposition.getInstanceId());
295 copyElement.setDefinition(element.getValue().getDefinition());
298 // Remove element which is not present in the new Ac instance
299 var elementsRemoved = getElementRemoved(copyAc, automationComposition);
300 elementsRemoved.forEach(uuid -> copyAc.getElements().remove(uuid));
302 var validationResult =
303 validateAutomationComposition(copyAc, automationComposition.getCompositionTargetId());
304 if (!validationResult.isValid()) {
305 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
307 copyAc.setCompositionTargetId(automationComposition.getCompositionTargetId());
309 // Publish migrate event to the participants
310 supervisionAcHandler.migratePrecheck(copyAc);
312 AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
313 SubState.MIGRATION_PRECHECKING);
314 acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
316 return createInstantiationResponse(automationCompositionProvider.updateAutomationComposition(acToBeUpdated));
319 private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
320 return validateAutomationComposition(automationComposition, automationComposition.getCompositionId());
324 * Validate AutomationComposition.
326 * @param automationComposition AutomationComposition to validate
327 * @param compositionId the composition id
328 * @return the result of validation
330 private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
331 UUID compositionId) {
333 var result = new BeanValidationResult("AutomationComposition", automationComposition);
334 var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
335 if (acDefinitionOpt.isEmpty()) {
336 result.addResult(new ObjectValidationResult("ServiceTemplate", compositionId, ValidationStatus.INVALID,
337 "Commissioned automation composition definition not found"));
340 if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
341 result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
342 ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
345 var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
346 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
348 participantProvider.verifyParticipantState(participantIds);
350 result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
351 acDefinitionOpt.get().getServiceTemplate(),
352 acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
354 result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
356 if (result.isValid()) {
357 for (var element : automationComposition.getElements().values()) {
358 var name = element.getDefinition().getName();
359 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
360 element.setParticipantId(participantId);
368 private void encryptInstanceProperties(AutomationComposition automationComposition, UUID compositionId) {
369 if (encryptionUtils.encryptionEnabled()) {
370 var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
371 acDefinitionOpt.ifPresent(acDefinition
372 -> encryptionUtils.findAndEncryptSensitiveData(acDefinition, automationComposition));
377 * Get Automation Composition.
379 * @param compositionId The UUID of the automation composition definition
380 * @param instanceId The UUID of the automation composition instance
381 * @return the Automation Composition
383 @Transactional(readOnly = true)
384 public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
385 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
386 if (!compositionId.equals(automationComposition.getCompositionId())
387 && !compositionId.equals(automationComposition.getCompositionTargetId())) {
388 throw new PfModelRuntimeException(Status.BAD_REQUEST,
389 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
391 return automationComposition;
395 * Delete the automation composition with the given name and version.
397 * @param compositionId The UUID of the automation composition definition
398 * @param instanceId The UUID of the automation composition instance
399 * @return the result of the deletion
401 public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
402 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
403 if (!compositionId.equals(automationComposition.getCompositionId())) {
404 throw new PfModelRuntimeException(Status.BAD_REQUEST,
405 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
407 if (!DeployState.UNDEPLOYED.equals(automationComposition.getDeployState())
408 && !DeployState.DELETING.equals(automationComposition.getDeployState())) {
409 throw new PfModelRuntimeException(Status.BAD_REQUEST,
410 "Automation composition state is still " + automationComposition.getDeployState());
412 if (DeployState.DELETING.equals(automationComposition.getDeployState())
413 && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) {
414 throw new PfModelRuntimeException(Status.BAD_REQUEST,
415 "Automation composition state is still " + automationComposition.getDeployState());
417 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
418 var participantIds = acDefinition.getElementStateMap().values().stream()
419 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
420 participantProvider.verifyParticipantState(participantIds);
421 supervisionAcHandler.delete(automationComposition, acDefinition);
422 var response = new InstantiationResponse();
423 response.setInstanceId(automationComposition.getInstanceId());
424 response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
429 * Get the requested automation compositions.
431 * @param name the name of the automation composition to get, null for all automation compositions
432 * @param version the version of the automation composition to get, null for all automation compositions
433 * @return the automation compositions
435 @Transactional(readOnly = true)
436 public AutomationCompositions getAutomationCompositions(UUID compositionId, String name, String version) {
437 var automationCompositions = new AutomationCompositions();
438 automationCompositions.setAutomationCompositionList(
439 automationCompositionProvider.getAutomationCompositions(compositionId, name, version));
441 return automationCompositions;
445 * Handle Composition Instance State.
447 * @param compositionId the compositionId
448 * @param instanceId the instanceId
449 * @param acInstanceStateUpdate the AcInstanceStateUpdate
451 public void compositionInstanceState(UUID compositionId, UUID instanceId,
452 @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
453 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
454 if (!compositionId.equals(automationComposition.getCompositionId())) {
455 throw new PfModelRuntimeException(Status.BAD_REQUEST,
456 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
458 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
460 var participantIds = acDefinition.getElementStateMap().values().stream()
461 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
463 participantProvider.verifyParticipantState(participantIds);
464 var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
465 acInstanceStateUpdate.getLockOrder(), acInstanceStateUpdate.getSubOrder(),
466 automationComposition.getDeployState(), automationComposition.getLockState(),
467 automationComposition.getSubState(), automationComposition.getStateChangeResult());
470 supervisionAcHandler.deploy(automationComposition, acDefinition);
474 supervisionAcHandler.undeploy(automationComposition, acDefinition);
478 supervisionAcHandler.lock(automationComposition, acDefinition);
482 supervisionAcHandler.unlock(automationComposition, acDefinition);
486 supervisionAcHandler.prepare(automationComposition);
490 supervisionAcHandler.review(automationComposition);
494 var msg = String.format(NOT_VALID_ORDER, acInstanceStateUpdate,
495 automationComposition.getDeployState(), automationComposition.getLockState(),
496 automationComposition.getSubState(), automationComposition.getStateChangeResult());
497 throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);