2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
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.AutomationComposition;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
38 import org.onap.policy.clamp.models.acm.concepts.DeployState;
39 import org.onap.policy.clamp.models.acm.concepts.LockState;
40 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
41 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
42 import org.onap.policy.clamp.models.acm.concepts.SubState;
43 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
44 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
45 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
46 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
47 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.SubOrder;
48 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
49 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
50 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
51 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
52 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
53 import org.onap.policy.common.parameters.BeanValidationResult;
54 import org.onap.policy.models.base.PfModelRuntimeException;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57 import org.springframework.data.domain.Pageable;
58 import org.springframework.stereotype.Service;
59 import org.springframework.transaction.annotation.Transactional;
62 * This class is dedicated to the Instantiation of Commissioned automation composition.
66 @RequiredArgsConstructor
67 public class AutomationCompositionInstantiationProvider {
68 private static final String DO_NOT_MATCH = " do not match with ";
69 private static final String ELEMENT_ID_NOT_PRESENT = "Element id not present ";
70 private static final String NOT_VALID_ORDER =
71 "Not valid order %s; DeployState: %s; LockState: %s; SubState: %s; StateChangeResult: %s";
73 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionInstantiationProvider.class);
75 private final AutomationCompositionProvider automationCompositionProvider;
76 private final AcDefinitionProvider acDefinitionProvider;
77 private final AcInstanceStateResolver acInstanceStateResolver;
78 private final SupervisionAcHandler supervisionAcHandler;
79 private final ParticipantProvider participantProvider;
80 private final AcRuntimeParameterGroup acRuntimeParameterGroup;
81 private final EncryptionUtils encryptionUtils;
84 * Create automation composition.
86 * @param compositionId The UUID of the automation composition definition
87 * @param automationComposition the automation composition
88 * @return the result of the instantiation operation
90 public InstantiationResponse createAutomationComposition(UUID compositionId,
91 AutomationComposition automationComposition) {
92 AutomationCompositionProvider.validateInstanceEndpoint(compositionId, automationComposition);
93 automationCompositionProvider.validateNameVersion(automationComposition.getKey().asIdentifier());
95 var acDefinition = acDefinitionProvider.getAcDefinition(compositionId);
96 AcDefinitionProvider.checkPrimedComposition(acDefinition);
97 var validationResult = validateAutomationComposition(automationComposition, acDefinition);
98 if (!validationResult.isValid()) {
99 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
101 encryptInstanceProperties(automationComposition, compositionId);
102 automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
104 return createInstantiationResponse(automationComposition);
107 private InstantiationResponse createInstantiationResponse(AutomationComposition automationComposition) {
108 var response = new InstantiationResponse();
109 response.setInstanceId(automationComposition.getInstanceId());
110 response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
115 * Update automation composition.
117 * @param compositionId The UUID of the automation composition definition
118 * @param automationComposition the automation composition
119 * @return the result of the update
121 public InstantiationResponse updateAutomationComposition(UUID compositionId,
122 AutomationComposition automationComposition) {
123 var instanceId = automationComposition.getInstanceId();
124 var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
125 AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acToUpdate);
126 var acDefinition = acDefinitionProvider.getAcDefinition(compositionId);
127 AcDefinitionProvider.checkPrimedComposition(acDefinition);
128 if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
129 acToUpdate.setElements(automationComposition.getElements());
130 acToUpdate.setName(automationComposition.getName());
131 acToUpdate.setVersion(automationComposition.getVersion());
132 acToUpdate.setDescription(automationComposition.getDescription());
133 acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
134 var validationResult = validateAutomationComposition(acToUpdate, acDefinition);
135 if (!validationResult.isValid()) {
136 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
138 encryptInstanceProperties(acToUpdate, compositionId);
139 automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
140 return createInstantiationResponse(automationComposition);
144 var deployOrder = DeployOrder.UPDATE;
145 var subOrder = SubOrder.NONE;
147 if (automationComposition.getCompositionTargetId() != null) {
149 if (Boolean.TRUE.equals(automationComposition.getPrecheck())) {
150 subOrder = SubOrder.MIGRATE_PRECHECK;
151 deployOrder = DeployOrder.NONE;
153 deployOrder = DeployOrder.MIGRATE;
156 var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder,
157 acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(),
158 acToUpdate.getStateChangeResult());
159 return switch (result) {
160 case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate, acDefinition);
162 case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate, acDefinition);
164 case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate, acDefinition);
166 default -> throw new PfModelRuntimeException(Status.BAD_REQUEST,
167 "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState());
172 * Update deployed AC Element properties.
174 * @param automationComposition the automation composition
175 * @param acToBeUpdated the composition to be updated
176 * @return the result of the update
178 private InstantiationResponse updateDeployedAutomationComposition(
179 AutomationComposition automationComposition, AutomationComposition acToBeUpdated,
180 AutomationCompositionDefinition acDefinition) {
181 // save copy in case of a rollback
182 automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated);
184 // Iterate and update the element property values
185 for (var element : automationComposition.getElements().entrySet()) {
186 var elementId = element.getKey();
187 var dbAcElement = acToBeUpdated.getElements().get(elementId);
188 if (dbAcElement == null) {
189 throw new PfModelRuntimeException(Status.BAD_REQUEST, ELEMENT_ID_NOT_PRESENT + elementId);
191 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
194 var validationResult = validateAutomationComposition(acToBeUpdated, acDefinition);
195 if (!validationResult.isValid()) {
196 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
198 updateAcForProperties(acToBeUpdated);
200 var acToPublish = new AutomationComposition(acToBeUpdated);
201 encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionId());
203 automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
204 // Publish property update event to the participants
205 supervisionAcHandler.update(acToPublish, acDefinition.getRevisionId());
206 return createInstantiationResponse(automationComposition);
209 private InstantiationResponse migrateAutomationComposition(
210 AutomationComposition automationComposition, AutomationComposition acToBeUpdated,
211 AutomationCompositionDefinition acDefinition) {
213 if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
214 throw new PfModelRuntimeException(Status.BAD_REQUEST,
215 "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
217 // make copy for rollback
218 automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated);
220 var acDefinitionTarget = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
221 AcDefinitionProvider.checkPrimedComposition(acDefinitionTarget);
222 // Iterate and update the element property values
223 var elementsRemoved = updateElementsProperties(automationComposition, acToBeUpdated, acDefinitionTarget);
225 updateAcForMigration(acToBeUpdated, acDefinitionTarget, DeployState.MIGRATING);
227 var acToPublish = new AutomationComposition(acToBeUpdated);
228 encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
230 var ac = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
231 elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
233 // Publish migrate event to the participants
234 supervisionAcHandler.migrate(acToPublish, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
235 return createInstantiationResponse(ac);
238 private void updateAcForMigration(AutomationComposition acToBeUpdated,
239 AutomationCompositionDefinition acDefinition, DeployState deployState) {
240 AcmUtils.setCascadedState(acToBeUpdated, deployState, LockState.LOCKED);
241 acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
242 var stage = ParticipantUtils.getFirstStage(acToBeUpdated, acDefinition.getServiceTemplate());
243 acToBeUpdated.setPhase(stage);
246 private void updateAcForProperties(AutomationComposition acToBeUpdated) {
247 AcmUtils.setCascadedState(acToBeUpdated, DeployState.UPDATING, acToBeUpdated.getLockState());
248 acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
251 private List<UUID> getElementRemoved(AutomationComposition acFromDb, AutomationComposition acFromMigration) {
252 return acFromDb.getElements().keySet().stream()
253 .filter(id -> acFromMigration.getElements().get(id) == null).toList();
257 private InstantiationResponse migratePrecheckAc(
258 AutomationComposition automationComposition, AutomationComposition acToBeUpdated,
259 AutomationCompositionDefinition acDefinition) {
261 acToBeUpdated.setPrecheck(true);
262 var copyAc = new AutomationComposition(acToBeUpdated);
263 var acDefinitionTarget = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
264 AcDefinitionProvider.checkPrimedComposition(acDefinitionTarget);
265 // Iterate and update the element property values
266 updateElementsProperties(automationComposition, copyAc, acDefinitionTarget);
268 // Publish migrate event to the participants
269 supervisionAcHandler.migratePrecheck(copyAc, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
271 AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
272 SubState.MIGRATION_PRECHECKING);
273 acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
275 return createInstantiationResponse(automationCompositionProvider.updateAutomationComposition(acToBeUpdated));
279 * Validate AutomationComposition.
281 * @param automationComposition AutomationComposition to validate
282 * @param acDefinition the Composition Definition
283 * @return the result of validation
285 private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
286 AutomationCompositionDefinition acDefinition) {
287 var result = new BeanValidationResult("AutomationComposition", automationComposition);
288 participantProvider.checkRegisteredParticipant(acDefinition);
289 result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
290 acDefinition.getServiceTemplate(),
291 acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
293 result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
295 if (result.isValid()) {
296 for (var element : automationComposition.getElements().values()) {
297 var name = element.getDefinition().getName();
298 var participantId = acDefinition.getElementStateMap().get(name).getParticipantId();
299 element.setParticipantId(participantId);
307 private void encryptInstanceProperties(AutomationComposition automationComposition, UUID compositionId) {
308 if (encryptionUtils.encryptionEnabled()) {
309 var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
310 acDefinitionOpt.ifPresent(acDefinition
311 -> encryptionUtils.findAndEncryptSensitiveData(acDefinition, automationComposition));
316 * Get Automation Composition.
318 * @param compositionId The UUID of the automation composition definition
319 * @param instanceId The UUID of the automation composition instance
320 * @return the Automation Composition
322 @Transactional(readOnly = true)
323 public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
324 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
325 if (!compositionId.equals(automationComposition.getCompositionId())
326 && !compositionId.equals(automationComposition.getCompositionTargetId())) {
327 throw new PfModelRuntimeException(Status.BAD_REQUEST,
328 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
330 return automationComposition;
334 * Delete the automation composition with the given name and version.
336 * @param compositionId The UUID of the automation composition definition
337 * @param instanceId The UUID of the automation composition instance
338 * @return the result of the deletion
340 public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
341 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
342 var acDefinition = getAcDefinition(compositionId, automationComposition);
343 var result = acInstanceStateResolver.resolve(DeployOrder.DELETE,
345 automationComposition.getDeployState(), automationComposition.getLockState(),
346 automationComposition.getSubState(), automationComposition.getStateChangeResult());
347 if (!DeployOrder.DELETE.name().equals(result)) {
348 var msg = String.format(NOT_VALID_ORDER, DeployOrder.DELETE,
349 automationComposition.getDeployState(), automationComposition.getLockState(),
350 automationComposition.getSubState(), automationComposition.getStateChangeResult());
351 throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
353 supervisionAcHandler.delete(automationComposition, acDefinition);
354 return createInstantiationResponse(automationComposition);
358 * Get the requested automation compositions.
360 * @param name the name of the automation composition to get, null for all automation compositions
361 * @param version the version of the automation composition to get, null for all automation compositions
362 * @param pageable the Pageable
363 * @return the automation compositions
365 @Transactional(readOnly = true)
366 public AutomationCompositions getAutomationCompositions(@NonNull final UUID compositionId,
367 final String name, final String version,
368 @NonNull final Pageable pageable) {
369 var automationCompositions = new AutomationCompositions();
370 automationCompositions.setAutomationCompositionList(
371 automationCompositionProvider.getAutomationCompositions(compositionId, name, version, pageable));
373 return automationCompositions;
377 * Handle Composition Instance State.
379 * @param compositionId the compositionId
380 * @param instanceId the instanceId
381 * @param acInstanceStateUpdate the AcInstanceStateUpdate
383 public void compositionInstanceState(UUID compositionId, UUID instanceId,
384 @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
385 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
386 var acDefinition = getAcDefinition(compositionId, automationComposition);
387 var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
388 acInstanceStateUpdate.getLockOrder(), acInstanceStateUpdate.getSubOrder(),
389 automationComposition.getDeployState(), automationComposition.getLockState(),
390 automationComposition.getSubState(), automationComposition.getStateChangeResult());
393 supervisionAcHandler.deploy(automationComposition, acDefinition);
397 supervisionAcHandler.undeploy(automationComposition, acDefinition);
401 supervisionAcHandler.lock(automationComposition, acDefinition);
405 supervisionAcHandler.unlock(automationComposition, acDefinition);
409 supervisionAcHandler.prepare(automationComposition, acDefinition);
413 supervisionAcHandler.review(automationComposition, acDefinition);
417 var msg = String.format(NOT_VALID_ORDER, acInstanceStateUpdate,
418 automationComposition.getDeployState(), automationComposition.getLockState(),
419 automationComposition.getSubState(), automationComposition.getStateChangeResult());
420 throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
425 * Rollback AC Instance.
427 * @param compositionId The UUID of the automation composition definition
428 * @param instanceId The UUID of the automation composition instance
430 public void rollback(UUID compositionId, UUID instanceId) {
431 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
432 AutomationCompositionProvider.validateInstanceEndpoint(compositionId, automationComposition);
434 if (!DeployOrder.MIGRATION_REVERT.name().equals(acInstanceStateResolver.resolve(
435 DeployOrder.MIGRATION_REVERT, LockOrder.NONE,
436 SubOrder.NONE, automationComposition.getDeployState(), automationComposition.getLockState(),
437 automationComposition.getSubState(), automationComposition.getStateChangeResult()))) {
438 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Invalid state for rollback");
441 var automationCompositionToRollback =
442 automationCompositionProvider.getAutomationCompositionRollback(instanceId);
443 var acToBeUpdated = new AutomationComposition(automationComposition);
444 acToBeUpdated.setCompositionTargetId(automationCompositionToRollback.getCompositionId());
445 acToBeUpdated.setElements(automationCompositionToRollback.getElements().values().stream()
446 .collect(Collectors.toMap(AutomationCompositionElement::getId, AutomationCompositionElement::new)));
448 var acDefinitionTarget = acDefinitionProvider.getAcDefinition(acToBeUpdated.getCompositionTargetId());
449 var validationResult = validateAutomationComposition(acToBeUpdated, acDefinitionTarget);
450 if (!validationResult.isValid()) {
451 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
454 updateAcForMigration(acToBeUpdated, acDefinitionTarget, DeployState.MIGRATION_REVERTING);
455 var elementsRemoved = getElementRemoved(automationComposition, acToBeUpdated);
456 automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
457 elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
458 var acDefinition = acDefinitionProvider.getAcDefinition(acToBeUpdated.getCompositionId());
459 supervisionAcHandler.migrate(acToBeUpdated, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
462 private List<UUID> updateElementsProperties(AutomationComposition automationComposition,
463 AutomationComposition acToBeUpdated, AutomationCompositionDefinition acDefinitionTarget) {
464 for (var element : automationComposition.getElements().entrySet()) {
465 var elementId = element.getKey();
466 var dbAcElement = acToBeUpdated.getElements().get(elementId);
467 // Add additional elements if present for migration
468 if (dbAcElement == null) {
469 LOGGER.info("New Ac element {} added in Migration", elementId);
470 acToBeUpdated.getElements().put(elementId, element.getValue());
472 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
473 var newDefinition = element.getValue().getDefinition().asConceptKey();
474 var dbElementDefinition = dbAcElement.getDefinition().asConceptKey();
475 AutomationCompositionProvider.checkCompatibility(
476 newDefinition, dbElementDefinition, automationComposition.getInstanceId());
477 dbAcElement.setDefinition(element.getValue().getDefinition());
480 // Remove element which is not present in the new Ac instance
481 var elementsRemoved = getElementRemoved(acToBeUpdated, automationComposition);
482 elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().remove(uuid));
484 var validationResult = validateAutomationComposition(acToBeUpdated, acDefinitionTarget);
485 if (!validationResult.isValid()) {
486 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
488 acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
489 return elementsRemoved;
492 private AutomationCompositionDefinition getAcDefinition(UUID compositionId,
493 AutomationComposition automationComposition) {
494 AutomationCompositionProvider.validateInstanceEndpoint(compositionId, automationComposition);
495 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
496 participantProvider.checkRegisteredParticipant(acDefinition);