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.UUID;
27 import java.util.stream.Collectors;
28 import lombok.NonNull;
29 import lombok.RequiredArgsConstructor;
30 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
31 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionAcHandler;
32 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
33 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
34 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
35 import org.onap.policy.clamp.models.acm.concepts.DeployState;
36 import org.onap.policy.clamp.models.acm.concepts.LockState;
37 import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
38 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
39 import org.onap.policy.clamp.models.acm.concepts.SubState;
40 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
41 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
42 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
43 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
44 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.SubOrder;
45 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
46 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
47 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
48 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
49 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
50 import org.onap.policy.common.parameters.BeanValidationResult;
51 import org.onap.policy.common.parameters.ObjectValidationResult;
52 import org.onap.policy.common.parameters.ValidationStatus;
53 import org.onap.policy.models.base.PfKey;
54 import org.onap.policy.models.base.PfModelRuntimeException;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57 import org.springframework.stereotype.Service;
58 import org.springframework.transaction.annotation.Transactional;
61 * This class is dedicated to the Instantiation of Commissioned automation composition.
65 @RequiredArgsConstructor
66 public class AutomationCompositionInstantiationProvider {
67 private static final String DO_NOT_MATCH = " do not match with ";
69 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionInstantiationProvider.class);
71 private final AutomationCompositionProvider automationCompositionProvider;
72 private final AcDefinitionProvider acDefinitionProvider;
73 private final AcInstanceStateResolver acInstanceStateResolver;
74 private final SupervisionAcHandler supervisionAcHandler;
75 private final ParticipantProvider participantProvider;
76 private final AcRuntimeParameterGroup acRuntimeParameterGroup;
79 * Create automation composition.
81 * @param compositionId The UUID of the automation composition definition
82 * @param automationComposition the automation composition
83 * @return the result of the instantiation operation
85 public InstantiationResponse createAutomationComposition(UUID compositionId,
86 AutomationComposition automationComposition) {
87 if (!compositionId.equals(automationComposition.getCompositionId())) {
88 throw new PfModelRuntimeException(Status.BAD_REQUEST,
89 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
91 var checkAutomationCompositionOpt =
92 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
93 if (checkAutomationCompositionOpt.isPresent()) {
94 throw new PfModelRuntimeException(Status.BAD_REQUEST,
95 automationComposition.getKey().asIdentifier() + " already defined");
98 var validationResult = validateAutomationComposition(automationComposition);
99 if (!validationResult.isValid()) {
100 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
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 if (!compositionId.equals(acToUpdate.getCompositionId())) {
126 throw new PfModelRuntimeException(Status.BAD_REQUEST,
127 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
129 if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
130 acToUpdate.setElements(automationComposition.getElements());
131 acToUpdate.setName(automationComposition.getName());
132 acToUpdate.setVersion(automationComposition.getVersion());
133 acToUpdate.setDescription(automationComposition.getDescription());
134 acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
135 var validationResult = validateAutomationComposition(acToUpdate);
136 if (!validationResult.isValid()) {
137 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
139 automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
140 return createInstantiationResponse(automationComposition);
144 if (automationComposition.getRestarting() != null) {
145 throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Update not allowed");
148 var deployOrder = DeployOrder.UPDATE;
149 var subOrder = SubOrder.NONE;
151 if (automationComposition.getCompositionTargetId() != null) {
153 if (Boolean.TRUE.equals(automationComposition.getPrecheck())) {
154 subOrder = SubOrder.MIGRATE_PRECHECK;
155 deployOrder = DeployOrder.NONE;
157 deployOrder = DeployOrder.MIGRATE;
160 var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder,
161 acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(),
162 acToUpdate.getStateChangeResult());
163 return switch (result) {
164 case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate);
166 case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate);
168 case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate);
170 default -> throw new PfModelRuntimeException(Status.BAD_REQUEST,
171 "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState());
176 * Update deployed AC Element properties.
178 * @param automationComposition the automation composition
179 * @param acToBeUpdated the composition to be updated
180 * @return the result of the update
182 private InstantiationResponse updateDeployedAutomationComposition(
183 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
185 // Iterate and update the element property values
186 for (var element : automationComposition.getElements().entrySet()) {
187 var elementId = element.getKey();
188 var dbAcElement = acToBeUpdated.getElements().get(elementId);
189 if (dbAcElement == null) {
190 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Element id not present " + elementId);
192 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
195 var validationResult = validateAutomationComposition(acToBeUpdated);
196 if (!validationResult.isValid()) {
197 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
199 // Publish property update event to the participants
200 supervisionAcHandler.update(acToBeUpdated);
202 automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
203 return createInstantiationResponse(automationComposition);
206 private InstantiationResponse migrateAutomationComposition(
207 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
209 if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
210 throw new PfModelRuntimeException(Status.BAD_REQUEST,
211 "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
214 // Iterate and update the element property values
215 for (var element : automationComposition.getElements().entrySet()) {
216 var elementId = element.getKey();
217 var dbAcElement = acToBeUpdated.getElements().get(elementId);
218 if (dbAcElement == null) {
219 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Element id not present " + elementId);
221 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
222 var newDefinition = element.getValue().getDefinition();
224 newDefinition.asConceptKey().getCompatibility(dbAcElement.getDefinition().asConceptKey());
225 if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
226 throw new PfModelRuntimeException(Status.BAD_REQUEST,
227 dbAcElement.getDefinition() + " is not compatible with " + newDefinition);
229 if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR.equals(compatibility)) {
230 LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ",
231 automationComposition.getInstanceId(), newDefinition, compatibility, dbAcElement.getDefinition());
233 dbAcElement.setDefinition(element.getValue().getDefinition());
236 var validationResult =
237 validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
238 if (!validationResult.isValid()) {
239 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
241 acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
243 // Publish migrate event to the participants
244 supervisionAcHandler.migrate(acToBeUpdated);
246 automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
247 return createInstantiationResponse(automationComposition);
250 private InstantiationResponse migratePrecheckAc(
251 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
253 acToBeUpdated.setPrecheck(true);
254 var copyAc = new AutomationComposition(acToBeUpdated);
255 // Iterate and update the element property values
256 for (var element : automationComposition.getElements().entrySet()) {
257 var elementId = element.getKey();
258 var copyElement = copyAc.getElements().get(elementId);
259 if (copyElement == null) {
260 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Element id not present " + elementId);
262 AcmUtils.recursiveMerge(copyElement.getProperties(), element.getValue().getProperties());
263 var newDefinition = element.getValue().getDefinition();
265 newDefinition.asConceptKey().getCompatibility(copyElement.getDefinition().asConceptKey());
266 if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
267 throw new PfModelRuntimeException(Status.BAD_REQUEST,
268 copyElement.getDefinition() + " is not compatible with " + newDefinition);
270 copyElement.setDefinition(element.getValue().getDefinition());
273 var validationResult =
274 validateAutomationComposition(copyAc, automationComposition.getCompositionTargetId());
275 if (!validationResult.isValid()) {
276 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
278 copyAc.setCompositionTargetId(automationComposition.getCompositionTargetId());
280 // Publish migrate event to the participants
281 supervisionAcHandler.migratePrecheck(copyAc);
283 AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
284 SubState.MIGRATION_PRECHECKING);
285 acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
287 return createInstantiationResponse(automationCompositionProvider.updateAutomationComposition(acToBeUpdated));
290 private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
291 return validateAutomationComposition(automationComposition, automationComposition.getCompositionId());
295 * Validate AutomationComposition.
297 * @param automationComposition AutomationComposition to validate
298 * @param compositionId the composition id
299 * @return the result of validation
301 private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
302 UUID compositionId) {
304 var result = new BeanValidationResult("AutomationComposition", automationComposition);
305 var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
306 if (acDefinitionOpt.isEmpty()) {
307 result.addResult(new ObjectValidationResult("ServiceTemplate", compositionId, ValidationStatus.INVALID,
308 "Commissioned automation composition definition not found"));
311 if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
312 result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
313 ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
316 if (acDefinitionOpt.get().getRestarting() != null) {
318 new ObjectValidationResult("ServiceTemplate.restarting", acDefinitionOpt.get().getRestarting(),
319 ValidationStatus.INVALID, "There is a restarting process in composition"));
322 var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
323 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
325 participantProvider.verifyParticipantState(participantIds);
327 result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
328 acDefinitionOpt.get().getServiceTemplate(),
329 acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
331 result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
333 if (result.isValid()) {
334 for (var element : automationComposition.getElements().values()) {
335 var name = element.getDefinition().getName();
336 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
337 element.setParticipantId(participantId);
345 * Get Automation Composition.
347 * @param compositionId The UUID of the automation composition definition
348 * @param instanceId The UUID of the automation composition instance
349 * @return the Automation Composition
351 @Transactional(readOnly = true)
352 public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
353 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
354 if (!compositionId.equals(automationComposition.getCompositionId())
355 && !compositionId.equals(automationComposition.getCompositionTargetId())) {
356 throw new PfModelRuntimeException(Status.BAD_REQUEST,
357 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
359 return automationComposition;
363 * Delete the automation composition with the given name and version.
365 * @param compositionId The UUID of the automation composition definition
366 * @param instanceId The UUID of the automation composition instance
367 * @return the result of the deletion
369 public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
370 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
371 if (!compositionId.equals(automationComposition.getCompositionId())) {
372 throw new PfModelRuntimeException(Status.BAD_REQUEST,
373 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
375 if (!DeployState.UNDEPLOYED.equals(automationComposition.getDeployState())
376 && !DeployState.DELETING.equals(automationComposition.getDeployState())) {
377 throw new PfModelRuntimeException(Status.BAD_REQUEST,
378 "Automation composition state is still " + automationComposition.getDeployState());
380 if (DeployState.DELETING.equals(automationComposition.getDeployState())
381 && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) {
382 throw new PfModelRuntimeException(Status.BAD_REQUEST,
383 "Automation composition state is still " + automationComposition.getDeployState());
385 if (automationComposition.getRestarting() != null) {
386 throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Delete not allowed");
388 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
389 var participantIds = acDefinition.getElementStateMap().values().stream()
390 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
391 participantProvider.verifyParticipantState(participantIds);
392 supervisionAcHandler.delete(automationComposition, acDefinition);
393 var response = new InstantiationResponse();
394 response.setInstanceId(automationComposition.getInstanceId());
395 response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
400 * Get the requested automation compositions.
402 * @param name the name of the automation composition to get, null for all automation compositions
403 * @param version the version of the automation composition to get, null for all automation compositions
404 * @return the automation compositions
406 @Transactional(readOnly = true)
407 public AutomationCompositions getAutomationCompositions(UUID compositionId, String name, String version) {
408 var automationCompositions = new AutomationCompositions();
409 automationCompositions.setAutomationCompositionList(
410 automationCompositionProvider.getAutomationCompositions(compositionId, name, version));
412 return automationCompositions;
416 * Handle Composition Instance State.
418 * @param compositionId the compositionId
419 * @param instanceId the instanceId
420 * @param acInstanceStateUpdate the AcInstanceStateUpdate
422 public void compositionInstanceState(UUID compositionId, UUID instanceId,
423 @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
424 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
425 if (!compositionId.equals(automationComposition.getCompositionId())) {
426 throw new PfModelRuntimeException(Status.BAD_REQUEST,
427 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
429 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
431 var participantIds = acDefinition.getElementStateMap().values().stream()
432 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
434 participantProvider.verifyParticipantState(participantIds);
435 var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
436 acInstanceStateUpdate.getLockOrder(), acInstanceStateUpdate.getSubOrder(),
437 automationComposition.getDeployState(), automationComposition.getLockState(),
438 automationComposition.getSubState(), automationComposition.getStateChangeResult());
441 supervisionAcHandler.deploy(automationComposition, acDefinition);
445 supervisionAcHandler.undeploy(automationComposition, acDefinition);
449 supervisionAcHandler.lock(automationComposition, acDefinition);
453 supervisionAcHandler.unlock(automationComposition, acDefinition);
457 supervisionAcHandler.prepare(automationComposition);
461 supervisionAcHandler.review(automationComposition);
465 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Not valid " + acInstanceStateUpdate);