2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2024 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.ArrayList;
27 import java.util.List;
28 import java.util.UUID;
29 import java.util.stream.Collectors;
30 import lombok.NonNull;
31 import lombok.RequiredArgsConstructor;
32 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
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 ";
72 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionInstantiationProvider.class);
74 private final AutomationCompositionProvider automationCompositionProvider;
75 private final AcDefinitionProvider acDefinitionProvider;
76 private final AcInstanceStateResolver acInstanceStateResolver;
77 private final SupervisionAcHandler supervisionAcHandler;
78 private final ParticipantProvider participantProvider;
79 private final AcRuntimeParameterGroup acRuntimeParameterGroup;
82 * Create automation composition.
84 * @param compositionId The UUID of the automation composition definition
85 * @param automationComposition the automation composition
86 * @return the result of the instantiation operation
88 public InstantiationResponse createAutomationComposition(UUID compositionId,
89 AutomationComposition automationComposition) {
90 if (!compositionId.equals(automationComposition.getCompositionId())) {
91 throw new PfModelRuntimeException(Status.BAD_REQUEST,
92 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
94 var checkAutomationCompositionOpt =
95 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
96 if (checkAutomationCompositionOpt.isPresent()) {
97 throw new PfModelRuntimeException(Status.BAD_REQUEST,
98 automationComposition.getKey().asIdentifier() + " already defined");
101 var validationResult = validateAutomationComposition(automationComposition);
102 if (!validationResult.isValid()) {
103 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
105 automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
107 return createInstantiationResponse(automationComposition);
110 private InstantiationResponse createInstantiationResponse(AutomationComposition automationComposition) {
111 var response = new InstantiationResponse();
112 response.setInstanceId(automationComposition.getInstanceId());
113 response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
118 * Update automation composition.
120 * @param compositionId The UUID of the automation composition definition
121 * @param automationComposition the automation composition
122 * @return the result of the update
124 public InstantiationResponse updateAutomationComposition(UUID compositionId,
125 AutomationComposition automationComposition) {
126 var instanceId = automationComposition.getInstanceId();
127 var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
128 if (!compositionId.equals(acToUpdate.getCompositionId())) {
129 throw new PfModelRuntimeException(Status.BAD_REQUEST,
130 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
132 if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
133 acToUpdate.setElements(automationComposition.getElements());
134 acToUpdate.setName(automationComposition.getName());
135 acToUpdate.setVersion(automationComposition.getVersion());
136 acToUpdate.setDescription(automationComposition.getDescription());
137 acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
138 var validationResult = validateAutomationComposition(acToUpdate);
139 if (!validationResult.isValid()) {
140 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
142 automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
143 return createInstantiationResponse(automationComposition);
147 if (automationComposition.getRestarting() != null) {
148 throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Update not allowed");
151 var deployOrder = DeployOrder.UPDATE;
152 var subOrder = SubOrder.NONE;
154 if (automationComposition.getCompositionTargetId() != null) {
156 if (Boolean.TRUE.equals(automationComposition.getPrecheck())) {
157 subOrder = SubOrder.MIGRATE_PRECHECK;
158 deployOrder = DeployOrder.NONE;
160 deployOrder = DeployOrder.MIGRATE;
163 var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder,
164 acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(),
165 acToUpdate.getStateChangeResult());
166 return switch (result) {
167 case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate);
169 case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate);
171 case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate);
173 default -> throw new PfModelRuntimeException(Status.BAD_REQUEST,
174 "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState());
179 * Update deployed AC Element properties.
181 * @param automationComposition the automation composition
182 * @param acToBeUpdated the composition to be updated
183 * @return the result of the update
185 private InstantiationResponse updateDeployedAutomationComposition(
186 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
188 // Iterate and update the element property values
189 for (var element : automationComposition.getElements().entrySet()) {
190 var elementId = element.getKey();
191 var dbAcElement = acToBeUpdated.getElements().get(elementId);
192 if (dbAcElement == null) {
193 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Element id not present " + elementId);
195 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
198 var validationResult = validateAutomationComposition(acToBeUpdated);
199 if (!validationResult.isValid()) {
200 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
202 // Publish property update event to the participants
203 supervisionAcHandler.update(acToBeUpdated);
205 automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
206 return createInstantiationResponse(automationComposition);
209 private InstantiationResponse migrateAutomationComposition(
210 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
212 if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
213 throw new PfModelRuntimeException(Status.BAD_REQUEST,
214 "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
217 // Iterate and update the element property values
218 for (var element : automationComposition.getElements().entrySet()) {
219 var elementId = element.getKey();
220 var dbAcElement = acToBeUpdated.getElements().get(elementId);
221 // Add additional elements if present for migration
222 if (dbAcElement == null) {
223 LOGGER.info("New Ac element {} added in Migration", elementId);
224 acToBeUpdated.getElements().put(elementId, automationComposition.getElements().get(elementId));
226 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
227 var newDefinition = element.getValue().getDefinition().asConceptKey();
228 var dbElementDefinition = dbAcElement.getDefinition().asConceptKey();
229 checkCompatibility(newDefinition, dbElementDefinition, automationComposition.getInstanceId());
230 dbAcElement.setDefinition(element.getValue().getDefinition());
233 // Remove element which is not present in the new Ac instance
234 List<UUID> elementsRemoved = new ArrayList<>();
235 for (var dbElement : acToBeUpdated.getElements().entrySet()) {
236 var dbElementId = dbElement.getKey();
237 if (automationComposition.getElements().get(dbElementId) == null) {
238 LOGGER.info("Element with id {} is removed in Migration", dbElementId);
239 elementsRemoved.add(dbElementId);
240 automationCompositionProvider.deleteAutomationCompositionElement(dbElementId);
243 elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().remove(uuid));
245 var validationResult =
246 validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
247 if (!validationResult.isValid()) {
248 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
250 acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
252 // Publish migrate event to the participants
253 supervisionAcHandler.migrate(acToBeUpdated);
255 automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
256 elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
257 return createInstantiationResponse(automationComposition);
260 void checkCompatibility(PfConceptKey newDefinition, PfConceptKey dbElementDefinition,
262 var compatibility = newDefinition.getCompatibility(dbElementDefinition);
263 if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
264 throw new PfModelRuntimeException(Status.BAD_REQUEST,
265 dbElementDefinition + " is not compatible with " + newDefinition);
267 if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR
268 .equals(compatibility)) {
269 LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ", instanceId, newDefinition,
270 compatibility, dbElementDefinition);
274 private InstantiationResponse migratePrecheckAc(
275 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
277 acToBeUpdated.setPrecheck(true);
278 var copyAc = new AutomationComposition(acToBeUpdated);
279 // Iterate and update the element property values
280 for (var element : automationComposition.getElements().entrySet()) {
281 var elementId = element.getKey();
282 var copyElement = copyAc.getElements().get(elementId);
283 // Add additional elements if present for migration
284 if (copyElement == null) {
285 LOGGER.info("New Ac element {} added in Migration", elementId);
286 copyAc.getElements().put(elementId, automationComposition.getElements().get(elementId));
288 AcmUtils.recursiveMerge(copyElement.getProperties(), element.getValue().getProperties());
289 var newDefinition = element.getValue().getDefinition().asConceptKey();
290 var copyElementDefinition = copyElement.getDefinition().asConceptKey();
291 checkCompatibility(newDefinition, copyElementDefinition, automationComposition.getInstanceId());
292 copyElement.setDefinition(element.getValue().getDefinition());
295 // Remove element which is not present in the new Ac instance
296 List<UUID> elementsRemoved = new ArrayList<>();
297 for (var dbElement : copyAc.getElements().entrySet()) {
298 var dbElementId = dbElement.getKey();
299 if (automationComposition.getElements().get(dbElementId) == null) {
300 LOGGER.info("Element with id {} is removed in Migration", dbElementId);
301 elementsRemoved.add(dbElementId);
305 elementsRemoved.forEach(uuid -> copyAc.getElements().remove(uuid));
307 var validationResult =
308 validateAutomationComposition(copyAc, automationComposition.getCompositionTargetId());
309 if (!validationResult.isValid()) {
310 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
312 copyAc.setCompositionTargetId(automationComposition.getCompositionTargetId());
314 // Publish migrate event to the participants
315 supervisionAcHandler.migratePrecheck(copyAc);
317 AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
318 SubState.MIGRATION_PRECHECKING);
319 acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
321 return createInstantiationResponse(automationCompositionProvider.updateAutomationComposition(acToBeUpdated));
324 private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
325 return validateAutomationComposition(automationComposition, automationComposition.getCompositionId());
329 * Validate AutomationComposition.
331 * @param automationComposition AutomationComposition to validate
332 * @param compositionId the composition id
333 * @return the result of validation
335 private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
336 UUID compositionId) {
338 var result = new BeanValidationResult("AutomationComposition", automationComposition);
339 var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
340 if (acDefinitionOpt.isEmpty()) {
341 result.addResult(new ObjectValidationResult("ServiceTemplate", compositionId, ValidationStatus.INVALID,
342 "Commissioned automation composition definition not found"));
345 if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
346 result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
347 ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
350 if (acDefinitionOpt.get().getRestarting() != null) {
352 new ObjectValidationResult("ServiceTemplate.restarting", acDefinitionOpt.get().getRestarting(),
353 ValidationStatus.INVALID, "There is a restarting process in composition"));
356 var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
357 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
359 participantProvider.verifyParticipantState(participantIds);
361 result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
362 acDefinitionOpt.get().getServiceTemplate(),
363 acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
365 result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
367 if (result.isValid()) {
368 for (var element : automationComposition.getElements().values()) {
369 var name = element.getDefinition().getName();
370 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
371 element.setParticipantId(participantId);
379 * Get Automation Composition.
381 * @param compositionId The UUID of the automation composition definition
382 * @param instanceId The UUID of the automation composition instance
383 * @return the Automation Composition
385 @Transactional(readOnly = true)
386 public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
387 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
388 if (!compositionId.equals(automationComposition.getCompositionId())
389 && !compositionId.equals(automationComposition.getCompositionTargetId())) {
390 throw new PfModelRuntimeException(Status.BAD_REQUEST,
391 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
393 return automationComposition;
397 * Delete the automation composition with the given name and version.
399 * @param compositionId The UUID of the automation composition definition
400 * @param instanceId The UUID of the automation composition instance
401 * @return the result of the deletion
403 public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
404 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
405 if (!compositionId.equals(automationComposition.getCompositionId())) {
406 throw new PfModelRuntimeException(Status.BAD_REQUEST,
407 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
409 if (!DeployState.UNDEPLOYED.equals(automationComposition.getDeployState())
410 && !DeployState.DELETING.equals(automationComposition.getDeployState())) {
411 throw new PfModelRuntimeException(Status.BAD_REQUEST,
412 "Automation composition state is still " + automationComposition.getDeployState());
414 if (DeployState.DELETING.equals(automationComposition.getDeployState())
415 && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) {
416 throw new PfModelRuntimeException(Status.BAD_REQUEST,
417 "Automation composition state is still " + automationComposition.getDeployState());
419 if (automationComposition.getRestarting() != null) {
420 throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Delete not allowed");
422 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
423 var participantIds = acDefinition.getElementStateMap().values().stream()
424 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
425 participantProvider.verifyParticipantState(participantIds);
426 supervisionAcHandler.delete(automationComposition, acDefinition);
427 var response = new InstantiationResponse();
428 response.setInstanceId(automationComposition.getInstanceId());
429 response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
434 * Get the requested automation compositions.
436 * @param name the name of the automation composition to get, null for all automation compositions
437 * @param version the version of the automation composition to get, null for all automation compositions
438 * @return the automation compositions
440 @Transactional(readOnly = true)
441 public AutomationCompositions getAutomationCompositions(UUID compositionId, String name, String version) {
442 var automationCompositions = new AutomationCompositions();
443 automationCompositions.setAutomationCompositionList(
444 automationCompositionProvider.getAutomationCompositions(compositionId, name, version));
446 return automationCompositions;
450 * Handle Composition Instance State.
452 * @param compositionId the compositionId
453 * @param instanceId the instanceId
454 * @param acInstanceStateUpdate the AcInstanceStateUpdate
456 public void compositionInstanceState(UUID compositionId, UUID instanceId,
457 @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
458 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
459 if (!compositionId.equals(automationComposition.getCompositionId())) {
460 throw new PfModelRuntimeException(Status.BAD_REQUEST,
461 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
463 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
465 var participantIds = acDefinition.getElementStateMap().values().stream()
466 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
468 participantProvider.verifyParticipantState(participantIds);
469 var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
470 acInstanceStateUpdate.getLockOrder(), acInstanceStateUpdate.getSubOrder(),
471 automationComposition.getDeployState(), automationComposition.getLockState(),
472 automationComposition.getSubState(), automationComposition.getStateChangeResult());
475 supervisionAcHandler.deploy(automationComposition, acDefinition);
479 supervisionAcHandler.undeploy(automationComposition, acDefinition);
483 supervisionAcHandler.lock(automationComposition, acDefinition);
487 supervisionAcHandler.unlock(automationComposition, acDefinition);
491 supervisionAcHandler.prepare(automationComposition);
495 supervisionAcHandler.review(automationComposition);
499 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Not valid " + acInstanceStateUpdate);