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;
26 import jakarta.ws.rs.core.Response.Status;
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.participants.AcmParticipantProvider;
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.messages.rest.instantiation.AcInstanceStateUpdate;
42 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
43 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
44 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
45 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
46 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
47 import org.onap.policy.common.parameters.BeanValidationResult;
48 import org.onap.policy.common.parameters.ObjectValidationResult;
49 import org.onap.policy.common.parameters.ValidationStatus;
50 import org.onap.policy.models.base.PfKey;
51 import org.onap.policy.models.base.PfModelRuntimeException;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54 import org.springframework.stereotype.Service;
55 import org.springframework.transaction.annotation.Transactional;
58 * This class is dedicated to the Instantiation of Commissioned automation composition.
62 @RequiredArgsConstructor
63 public class AutomationCompositionInstantiationProvider {
64 private static final String DO_NOT_MATCH = " do not match with ";
66 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionInstantiationProvider.class);
68 private final AutomationCompositionProvider automationCompositionProvider;
69 private final AcDefinitionProvider acDefinitionProvider;
70 private final AcInstanceStateResolver acInstanceStateResolver;
71 private final SupervisionAcHandler supervisionAcHandler;
72 private final AcmParticipantProvider acmParticipantProvider;
73 private final AcRuntimeParameterGroup acRuntimeParameterGroup;
76 * Create automation composition.
78 * @param compositionId The UUID of the automation composition definition
79 * @param automationComposition the automation composition
80 * @return the result of the instantiation operation
82 public InstantiationResponse createAutomationComposition(UUID compositionId,
83 AutomationComposition automationComposition) {
84 if (!compositionId.equals(automationComposition.getCompositionId())) {
85 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
86 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
88 var checkAutomationCompositionOpt =
89 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
90 if (checkAutomationCompositionOpt.isPresent()) {
91 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
92 automationComposition.getKey().asIdentifier() + " already defined");
95 var validationResult = validateAutomationComposition(automationComposition);
96 if (!validationResult.isValid()) {
97 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
99 automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
101 return createInstantiationResponse(automationComposition);
104 private InstantiationResponse createInstantiationResponse(AutomationComposition automationComposition) {
105 var response = new InstantiationResponse();
106 response.setInstanceId(automationComposition.getInstanceId());
107 response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
112 * Update automation composition.
114 * @param compositionId The UUID of the automation composition definition
115 * @param automationComposition the automation composition
116 * @return the result of the update
118 public InstantiationResponse updateAutomationComposition(UUID compositionId,
119 AutomationComposition automationComposition) {
120 var instanceId = automationComposition.getInstanceId();
121 var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
122 if (!compositionId.equals(acToUpdate.getCompositionId())) {
123 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
124 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
126 if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
127 acToUpdate.setElements(automationComposition.getElements());
128 acToUpdate.setName(automationComposition.getName());
129 acToUpdate.setVersion(automationComposition.getVersion());
130 acToUpdate.setDescription(automationComposition.getDescription());
131 acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
132 var validationResult = validateAutomationComposition(acToUpdate);
133 if (!validationResult.isValid()) {
134 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
136 automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
137 return createInstantiationResponse(automationComposition);
139 } else if ((DeployState.DEPLOYED.equals(acToUpdate.getDeployState())
140 || DeployState.UPDATING.equals(acToUpdate.getDeployState()))
141 && LockState.LOCKED.equals(acToUpdate.getLockState())) {
142 if (automationComposition.getCompositionTargetId() != null) {
143 return migrateAutomationComposition(automationComposition, acToUpdate);
145 return updateDeployedAutomationComposition(automationComposition, acToUpdate);
148 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
149 "Not allowed to update in the state " + acToUpdate.getDeployState());
153 * Update deployed AC Element properties.
155 * @param automationComposition the automation composition
156 * @param acToBeUpdated the composition to be updated
157 * @return the result of the update
159 private InstantiationResponse updateDeployedAutomationComposition(
160 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
162 // Iterate and update the element property values
163 for (var element : automationComposition.getElements().entrySet()) {
164 var elementId = element.getKey();
165 var dbAcElement = acToBeUpdated.getElements().get(elementId);
166 if (dbAcElement == null) {
167 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, "Element id not present " + elementId);
169 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
171 if (automationComposition.getRestarting() != null) {
172 throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Update not allowed");
175 var validationResult = validateAutomationComposition(acToBeUpdated);
176 if (!validationResult.isValid()) {
177 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
179 // Publish property update event to the participants
180 supervisionAcHandler.update(acToBeUpdated);
182 automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
183 return createInstantiationResponse(automationComposition);
186 private InstantiationResponse migrateAutomationComposition(
187 AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
189 if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
190 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
191 "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
193 if (automationComposition.getRestarting() != null) {
194 throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Migrate not allowed");
197 // Iterate and update the element property values
198 for (var element : automationComposition.getElements().entrySet()) {
199 var elementId = element.getKey();
200 var dbAcElement = acToBeUpdated.getElements().get(elementId);
201 if (dbAcElement == null) {
202 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, "Element id not present " + elementId);
204 dbAcElement.getProperties().putAll(element.getValue().getProperties());
205 var newDefinition = element.getValue().getDefinition();
207 newDefinition.asConceptKey().getCompatibility(dbAcElement.getDefinition().asConceptKey());
208 if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
209 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
210 dbAcElement.getDefinition() + " is not compatible with " + newDefinition);
212 if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR.equals(compatibility)) {
213 LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ",
214 automationComposition.getInstanceId(), newDefinition, compatibility, dbAcElement.getDefinition());
216 dbAcElement.setDefinition(element.getValue().getDefinition());
219 var validationResult =
220 validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
221 if (!validationResult.isValid()) {
222 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
224 acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
226 // Publish migrate event to the participants
227 supervisionAcHandler.migrate(acToBeUpdated, automationComposition.getCompositionTargetId());
229 automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
230 return createInstantiationResponse(automationComposition);
233 private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
234 return validateAutomationComposition(automationComposition, automationComposition.getCompositionId());
238 * Validate AutomationComposition.
240 * @param automationComposition AutomationComposition to validate
241 * @param compositionId the composition id
242 * @return the result of validation
244 private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
245 UUID compositionId) {
247 var result = new BeanValidationResult("AutomationComposition", automationComposition);
248 var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
249 if (acDefinitionOpt.isEmpty()) {
250 result.addResult(new ObjectValidationResult("ServiceTemplate", compositionId, ValidationStatus.INVALID,
251 "Commissioned automation composition definition not found"));
254 if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
255 result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
256 ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
259 if (acDefinitionOpt.get().getRestarting() != null) {
261 new ObjectValidationResult("ServiceTemplate.restarting", acDefinitionOpt.get().getRestarting(),
262 ValidationStatus.INVALID, "There is a restarting process in composition"));
265 var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
266 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
268 acmParticipantProvider.verifyParticipantState(participantIds);
270 result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
271 acDefinitionOpt.get().getServiceTemplate(),
272 acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
274 result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
276 if (result.isValid()) {
277 for (var element : automationComposition.getElements().values()) {
278 var name = element.getDefinition().getName();
279 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
280 element.setParticipantId(participantId);
288 * Get Automation Composition.
290 * @param compositionId The UUID of the automation composition definition
291 * @param instanceId The UUID of the automation composition instance
292 * @return the Automation Composition
294 @Transactional(readOnly = true)
295 public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
296 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
297 if (!compositionId.equals(automationComposition.getCompositionId())
298 && !compositionId.equals(automationComposition.getCompositionTargetId())) {
299 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
300 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
302 return automationComposition;
306 * Delete the automation composition with the given name and version.
308 * @param compositionId The UUID of the automation composition definition
309 * @param instanceId The UUID of the automation composition instance
310 * @return the result of the deletion
312 public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
313 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
314 if (!compositionId.equals(automationComposition.getCompositionId())) {
315 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
316 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
318 if (!DeployState.UNDEPLOYED.equals(automationComposition.getDeployState())
319 && !DeployState.DELETING.equals(automationComposition.getDeployState())) {
320 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
321 "Automation composition state is still " + automationComposition.getDeployState());
323 if (DeployState.DELETING.equals(automationComposition.getDeployState())
324 && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) {
325 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
326 "Automation composition state is still " + automationComposition.getDeployState());
328 if (automationComposition.getRestarting() != null) {
329 throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Delete not allowed");
331 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
332 var participantIds = acDefinition.getElementStateMap().values().stream()
333 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
334 acmParticipantProvider.verifyParticipantState(participantIds);
335 supervisionAcHandler.delete(automationComposition, acDefinition);
336 var response = new InstantiationResponse();
337 response.setInstanceId(automationComposition.getInstanceId());
338 response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
343 * Get the requested automation compositions.
345 * @param name the name of the automation composition to get, null for all automation compositions
346 * @param version the version of the automation composition to get, null for all automation compositions
347 * @return the automation compositions
349 @Transactional(readOnly = true)
350 public AutomationCompositions getAutomationCompositions(UUID compositionId, String name, String version) {
351 var automationCompositions = new AutomationCompositions();
352 automationCompositions.setAutomationCompositionList(
353 automationCompositionProvider.getAutomationCompositions(compositionId, name, version));
355 return automationCompositions;
359 * Handle Composition Instance State.
361 * @param compositionId the compositionId
362 * @param instanceId the instanceId
363 * @param acInstanceStateUpdate the AcInstanceStateUpdate
365 public void compositionInstanceState(UUID compositionId, UUID instanceId,
366 @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
367 var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
368 if (!compositionId.equals(automationComposition.getCompositionId())) {
369 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
370 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
372 var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
374 var participantIds = acDefinition.getElementStateMap().values().stream()
375 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
377 acmParticipantProvider.verifyParticipantState(participantIds);
378 var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
379 acInstanceStateUpdate.getLockOrder(), automationComposition.getDeployState(),
380 automationComposition.getLockState(), automationComposition.getStateChangeResult());
383 supervisionAcHandler.deploy(automationComposition, acDefinition);
387 supervisionAcHandler.undeploy(automationComposition, acDefinition);
391 supervisionAcHandler.lock(automationComposition, acDefinition);
395 supervisionAcHandler.unlock(automationComposition, acDefinition);
399 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Not valid " + acInstanceStateUpdate);