c015ceb7130d494de89e146528631e97ddd40f50
[policy/clamp.git] /
1 /*-
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.clamp.acm.runtime.instantiation;
23
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.AutomationCompositionDefinition;
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.NodeTemplateState;
41 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
42 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
43 import org.onap.policy.clamp.models.acm.concepts.SubState;
44 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
45 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
46 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
47 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
48 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.SubOrder;
49 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
50 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
51 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
52 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
53 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
54 import org.onap.policy.common.parameters.BeanValidationResult;
55 import org.onap.policy.common.parameters.ObjectValidationResult;
56 import org.onap.policy.common.parameters.ValidationStatus;
57 import org.onap.policy.models.base.PfConceptKey;
58 import org.onap.policy.models.base.PfKey;
59 import org.onap.policy.models.base.PfModelRuntimeException;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62 import org.springframework.data.domain.Pageable;
63 import org.springframework.stereotype.Service;
64 import org.springframework.transaction.annotation.Transactional;
65
66 /**
67  * This class is dedicated to the Instantiation of Commissioned automation composition.
68  */
69 @Service
70 @Transactional
71 @RequiredArgsConstructor
72 public class AutomationCompositionInstantiationProvider {
73     private static final String DO_NOT_MATCH = " do not match with ";
74     private static final String ELEMENT_ID_NOT_PRESENT = "Element id not present ";
75     private static final String NOT_VALID_ORDER =
76             "Not valid order %s; DeployState: %s; LockState: %s; SubState: %s; StateChangeResult: %s";
77
78     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionInstantiationProvider.class);
79
80     private final AutomationCompositionProvider automationCompositionProvider;
81     private final AcDefinitionProvider acDefinitionProvider;
82     private final AcInstanceStateResolver acInstanceStateResolver;
83     private final SupervisionAcHandler supervisionAcHandler;
84     private final ParticipantProvider participantProvider;
85     private final AcRuntimeParameterGroup acRuntimeParameterGroup;
86     private final EncryptionUtils encryptionUtils;
87
88     /**
89      * Create automation composition.
90      *
91      * @param compositionId The UUID of the automation composition definition
92      * @param automationComposition the automation composition
93      * @return the result of the instantiation operation
94      */
95     public InstantiationResponse createAutomationComposition(UUID compositionId,
96             AutomationComposition automationComposition) {
97         if (!compositionId.equals(automationComposition.getCompositionId())) {
98             throw new PfModelRuntimeException(Status.BAD_REQUEST,
99                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
100         }
101         var checkAutomationCompositionOpt =
102                 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
103         if (checkAutomationCompositionOpt.isPresent()) {
104             throw new PfModelRuntimeException(Status.BAD_REQUEST,
105                     automationComposition.getKey().asIdentifier() + " already defined");
106         }
107
108         var validationResult = validateAutomationComposition(automationComposition);
109         if (!validationResult.isValid()) {
110             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
111         }
112         encryptInstanceProperties(automationComposition, compositionId);
113         automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
114
115         return createInstantiationResponse(automationComposition);
116     }
117
118     private InstantiationResponse createInstantiationResponse(AutomationComposition automationComposition) {
119         var response = new InstantiationResponse();
120         response.setInstanceId(automationComposition.getInstanceId());
121         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
122         return response;
123     }
124
125     /**
126      * Update automation composition.
127      *
128      * @param compositionId The UUID of the automation composition definition
129      * @param automationComposition the automation composition
130      * @return the result of the update
131      */
132     public InstantiationResponse updateAutomationComposition(UUID compositionId,
133             AutomationComposition automationComposition) {
134         var instanceId = automationComposition.getInstanceId();
135         var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
136         if (!compositionId.equals(acToUpdate.getCompositionId())) {
137             throw new PfModelRuntimeException(Status.BAD_REQUEST,
138                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
139         }
140         if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
141             acToUpdate.setElements(automationComposition.getElements());
142             acToUpdate.setName(automationComposition.getName());
143             acToUpdate.setVersion(automationComposition.getVersion());
144             acToUpdate.setDescription(automationComposition.getDescription());
145             acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
146             var validationResult = validateAutomationComposition(acToUpdate);
147             if (!validationResult.isValid()) {
148                 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
149             }
150             encryptInstanceProperties(acToUpdate, compositionId);
151             automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
152             return createInstantiationResponse(automationComposition);
153
154         }
155
156         var deployOrder = DeployOrder.UPDATE;
157         var subOrder = SubOrder.NONE;
158
159         if (automationComposition.getCompositionTargetId() != null) {
160
161             if (Boolean.TRUE.equals(automationComposition.getPrecheck())) {
162                 subOrder = SubOrder.MIGRATE_PRECHECK;
163                 deployOrder = DeployOrder.NONE;
164             } else {
165                 deployOrder = DeployOrder.MIGRATE;
166             }
167         }
168         var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder,
169                 acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(),
170                 acToUpdate.getStateChangeResult());
171         return switch (result) {
172             case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate);
173
174             case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate);
175
176             case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate);
177
178             default -> throw new PfModelRuntimeException(Status.BAD_REQUEST,
179                     "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState());
180         };
181     }
182
183     /**
184      * Update deployed AC Element properties.
185      *
186      * @param automationComposition the automation composition
187      * @param acToBeUpdated the composition to be updated
188      * @return the result of the update
189      */
190     private InstantiationResponse updateDeployedAutomationComposition(
191             AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
192
193         // Iterate and update the element property values
194         for (var element : automationComposition.getElements().entrySet()) {
195             var elementId = element.getKey();
196             var dbAcElement = acToBeUpdated.getElements().get(elementId);
197             if (dbAcElement == null) {
198                 throw new PfModelRuntimeException(Status.BAD_REQUEST, ELEMENT_ID_NOT_PRESENT + elementId);
199             }
200             AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
201         }
202
203         var validationResult = validateAutomationComposition(acToBeUpdated);
204         if (!validationResult.isValid()) {
205             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
206         }
207         updateAcForProperties(acToBeUpdated);
208
209         var acToPublish = new AutomationComposition(acToBeUpdated);
210
211         encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionId());
212
213         automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
214         // Publish property update event to the participants
215         supervisionAcHandler.update(acToPublish);
216         return createInstantiationResponse(automationComposition);
217     }
218
219     private InstantiationResponse migrateAutomationComposition(
220         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
221
222         if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
223             throw new PfModelRuntimeException(Status.BAD_REQUEST,
224                 "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
225         }
226
227         // Iterate and update the element property values
228         for (var element : automationComposition.getElements().entrySet()) {
229             var elementId = element.getKey();
230             var dbAcElement = acToBeUpdated.getElements().get(elementId);
231             // Add additional elements if present for migration
232             if (dbAcElement == null) {
233                 LOGGER.info("New Ac element {} added in Migration", elementId);
234                 acToBeUpdated.getElements().put(elementId, element.getValue());
235             } else {
236                 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
237                 var newDefinition = element.getValue().getDefinition().asConceptKey();
238                 var dbElementDefinition = dbAcElement.getDefinition().asConceptKey();
239                 checkCompatibility(newDefinition, dbElementDefinition, automationComposition.getInstanceId());
240                 dbAcElement.setDefinition(element.getValue().getDefinition());
241             }
242         }
243         // Remove element which is not present in the new Ac instance
244         var elementsRemoved = getElementRemoved(acToBeUpdated, automationComposition);
245         elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().remove(uuid));
246
247         var validationResult =
248                 validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
249         if (!validationResult.isValid()) {
250             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
251         }
252         acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
253         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
254
255         updateAcForMigration(acToBeUpdated, acDefinition);
256
257         var acToPublish = new AutomationComposition(acToBeUpdated);
258
259         encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
260
261         var ac = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
262         elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
263
264         // Publish migrate event to the participants
265         supervisionAcHandler.migrate(acToPublish);
266         return createInstantiationResponse(ac);
267     }
268
269     private void updateAcForMigration(AutomationComposition acToBeUpdated,
270                                       AutomationCompositionDefinition acDefinition) {
271         AcmUtils.setCascadedState(acToBeUpdated, DeployState.MIGRATING, LockState.LOCKED);
272         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
273         var stage = ParticipantUtils.getFirstStage(acToBeUpdated, acDefinition.getServiceTemplate());
274         acToBeUpdated.setPhase(stage);
275     }
276
277     private void updateAcForProperties(AutomationComposition acToBeUpdated) {
278         AcmUtils.setCascadedState(acToBeUpdated, DeployState.UPDATING, acToBeUpdated.getLockState());
279         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
280     }
281
282     private List<UUID> getElementRemoved(AutomationComposition acFromDb, AutomationComposition acFromMigration) {
283         return acFromDb.getElements().keySet().stream()
284                 .filter(id -> acFromMigration.getElements().get(id) == null).toList();
285     }
286
287     void checkCompatibility(PfConceptKey newDefinition, PfConceptKey dbElementDefinition,
288                             UUID instanceId) {
289         var compatibility = newDefinition.getCompatibility(dbElementDefinition);
290         if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
291             throw new PfModelRuntimeException(Status.BAD_REQUEST,
292                     dbElementDefinition + " is not compatible with " + newDefinition);
293         }
294         if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR
295                 .equals(compatibility)) {
296             LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ", instanceId, newDefinition,
297                     compatibility, dbElementDefinition);
298         }
299     }
300
301     private InstantiationResponse migratePrecheckAc(
302             AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
303
304         acToBeUpdated.setPrecheck(true);
305         var copyAc = new AutomationComposition(acToBeUpdated);
306         // Iterate and update the element property values
307         for (var element : automationComposition.getElements().entrySet()) {
308             var elementId = element.getKey();
309             var copyElement = copyAc.getElements().get(elementId);
310             // Add additional elements if present for migration
311             if (copyElement == null) {
312                 LOGGER.info("New Ac element {} added in Migration", elementId);
313                 copyAc.getElements().put(elementId, element.getValue());
314             } else {
315                 AcmUtils.recursiveMerge(copyElement.getProperties(), element.getValue().getProperties());
316                 var newDefinition = element.getValue().getDefinition().asConceptKey();
317                 var copyElementDefinition = copyElement.getDefinition().asConceptKey();
318                 checkCompatibility(newDefinition, copyElementDefinition, automationComposition.getInstanceId());
319                 copyElement.setDefinition(element.getValue().getDefinition());
320             }
321         }
322         // Remove element which is not present in the new Ac instance
323         var elementsRemoved = getElementRemoved(copyAc, automationComposition);
324         elementsRemoved.forEach(uuid -> copyAc.getElements().remove(uuid));
325
326         var validationResult =
327                 validateAutomationComposition(copyAc, automationComposition.getCompositionTargetId());
328         if (!validationResult.isValid()) {
329             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
330         }
331         copyAc.setCompositionTargetId(automationComposition.getCompositionTargetId());
332
333         // Publish migrate event to the participants
334         supervisionAcHandler.migratePrecheck(copyAc);
335
336         AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
337             SubState.MIGRATION_PRECHECKING);
338         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
339
340         return createInstantiationResponse(automationCompositionProvider.updateAutomationComposition(acToBeUpdated));
341     }
342
343     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
344         return validateAutomationComposition(automationComposition, automationComposition.getCompositionId());
345     }
346
347     /**
348      * Validate AutomationComposition.
349      *
350      * @param automationComposition AutomationComposition to validate
351      * @param compositionId the composition id
352      * @return the result of validation
353      */
354     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
355             UUID compositionId) {
356
357         var result = new BeanValidationResult("AutomationComposition", automationComposition);
358         var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
359         if (acDefinitionOpt.isEmpty()) {
360             result.addResult(new ObjectValidationResult("ServiceTemplate", compositionId, ValidationStatus.INVALID,
361                     "Commissioned automation composition definition not found"));
362             return result;
363         }
364         if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
365             result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
366                     ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
367             return result;
368         }
369         var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
370                 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
371
372         participantProvider.verifyParticipantState(participantIds);
373
374         result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
375                 acDefinitionOpt.get().getServiceTemplate(),
376                 acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
377
378         result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
379
380         if (result.isValid()) {
381             for (var element : automationComposition.getElements().values()) {
382                 var name = element.getDefinition().getName();
383                 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
384                 element.setParticipantId(participantId);
385             }
386         }
387
388         return result;
389     }
390
391
392     private void encryptInstanceProperties(AutomationComposition automationComposition, UUID compositionId) {
393         if (encryptionUtils.encryptionEnabled()) {
394             var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
395             acDefinitionOpt.ifPresent(acDefinition
396                     -> encryptionUtils.findAndEncryptSensitiveData(acDefinition, automationComposition));
397         }
398     }
399
400     /**
401      * Get Automation Composition.
402      *
403      * @param compositionId The UUID of the automation composition definition
404      * @param instanceId The UUID of the automation composition instance
405      * @return the Automation Composition
406      */
407     @Transactional(readOnly = true)
408     public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
409         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
410         if (!compositionId.equals(automationComposition.getCompositionId())
411                         && !compositionId.equals(automationComposition.getCompositionTargetId())) {
412             throw new PfModelRuntimeException(Status.BAD_REQUEST,
413                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
414         }
415         return automationComposition;
416     }
417
418     /**
419      * Delete the automation composition with the given name and version.
420      *
421      * @param compositionId The UUID of the automation composition definition
422      * @param instanceId The UUID of the automation composition instance
423      * @return the result of the deletion
424      */
425     public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
426         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
427         if (!compositionId.equals(automationComposition.getCompositionId())) {
428             throw new PfModelRuntimeException(Status.BAD_REQUEST,
429                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
430         }
431         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
432         var participantIds = acDefinition.getElementStateMap().values().stream()
433             .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
434         participantProvider.verifyParticipantState(participantIds);
435         var result = acInstanceStateResolver.resolve(DeployOrder.DELETE,
436                 null, null,
437                 automationComposition.getDeployState(), automationComposition.getLockState(),
438                 automationComposition.getSubState(), automationComposition.getStateChangeResult());
439         if (!DeployOrder.DELETE.name().equals(result)) {
440             var msg = String.format(NOT_VALID_ORDER, DeployOrder.DELETE,
441                     automationComposition.getDeployState(), automationComposition.getLockState(),
442                     automationComposition.getSubState(), automationComposition.getStateChangeResult());
443             throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
444         }
445         supervisionAcHandler.delete(automationComposition, acDefinition);
446         return createInstantiationResponse(automationComposition);
447     }
448
449     /**
450      * Get the requested automation compositions.
451      *
452      * @param name the name of the automation composition to get, null for all automation compositions
453      * @param version the version of the automation composition to get, null for all automation compositions
454      * @param pageable the Pageable
455      * @return the automation compositions
456      */
457     @Transactional(readOnly = true)
458     public AutomationCompositions getAutomationCompositions(@NonNull final UUID compositionId,
459             final String name, final String version, @NonNull final Pageable pageable) {
460         var automationCompositions = new AutomationCompositions();
461         automationCompositions.setAutomationCompositionList(
462                 automationCompositionProvider.getAutomationCompositions(compositionId, name, version, pageable));
463
464         return automationCompositions;
465     }
466
467     /**
468      * Handle Composition Instance State.
469      *
470      * @param compositionId the compositionId
471      * @param instanceId the instanceId
472      * @param acInstanceStateUpdate the AcInstanceStateUpdate
473      */
474     public void compositionInstanceState(UUID compositionId, UUID instanceId,
475             @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
476         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
477         if (!compositionId.equals(automationComposition.getCompositionId())) {
478             throw new PfModelRuntimeException(Status.BAD_REQUEST,
479                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
480         }
481         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
482
483         var participantIds = acDefinition.getElementStateMap().values().stream()
484                 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
485
486         participantProvider.verifyParticipantState(participantIds);
487         var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
488                 acInstanceStateUpdate.getLockOrder(), acInstanceStateUpdate.getSubOrder(),
489                 automationComposition.getDeployState(), automationComposition.getLockState(),
490                 automationComposition.getSubState(), automationComposition.getStateChangeResult());
491         switch (result) {
492             case "DEPLOY":
493                 supervisionAcHandler.deploy(automationComposition, acDefinition);
494                 break;
495
496             case "UNDEPLOY":
497                 supervisionAcHandler.undeploy(automationComposition, acDefinition);
498                 break;
499
500             case "LOCK":
501                 supervisionAcHandler.lock(automationComposition, acDefinition);
502                 break;
503
504             case "UNLOCK":
505                 supervisionAcHandler.unlock(automationComposition, acDefinition);
506                 break;
507
508             case "PREPARE":
509                 supervisionAcHandler.prepare(automationComposition, acDefinition);
510                 break;
511
512             case "REVIEW":
513                 supervisionAcHandler.review(automationComposition);
514                 break;
515
516             default:
517                 var msg = String.format(NOT_VALID_ORDER, acInstanceStateUpdate,
518                         automationComposition.getDeployState(), automationComposition.getLockState(),
519                         automationComposition.getSubState(), automationComposition.getStateChangeResult());
520                 throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
521         }
522     }
523 }