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