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