Add support for stage in Prepare event 01/140701/2
authorFrancescoFioraEst <francesco.fiora@est.tech>
Thu, 13 Mar 2025 13:06:12 +0000 (13:06 +0000)
committerFrancescoFioraEst <francesco.fiora@est.tech>
Tue, 15 Apr 2025 16:27:16 +0000 (17:27 +0100)
Issue-ID: POLICY-5264
Change-Id: I1d5f3b2f329a2aa5713066aa9681dcaca597085e
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
23 files changed:
participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandler.java [moved from participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV3.java with 93% similarity]
participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorService.java
participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerTest.java [moved from participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV3Test.java with 96% similarity]
participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorServiceTest.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/AutomationCompositionElementListener.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AcElementListenerV3.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AcElementListenerV4.java [new file with mode: 0644]
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AutomationCompositionElementListenerV3.java [new file with mode: 0644]
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandlerTest.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcPreparePublisher.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/MonitoringScanner.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/comm/SupervisionMessagesTest.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScannerTest.java

@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2024-2025 Nordix Foundation.
+ *  Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElement
 import org.onap.policy.clamp.acm.participant.intermediary.api.ElementState;
 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
 import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
-import org.onap.policy.clamp.acm.participant.intermediary.api.impl.AcElementListenerV3;
+import org.onap.policy.clamp.acm.participant.intermediary.api.impl.AcElementListenerV4;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
@@ -38,13 +38,13 @@ import org.springframework.stereotype.Component;
 @Getter
 @Setter
 @Component
-public class AutomationCompositionElementHandlerV3 extends AcElementListenerV3 {
+public class AutomationCompositionElementHandler extends AcElementListenerV4 {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionElementHandlerV3.class);
+    private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionElementHandler.class);
 
     private final SimulatorService simulatorService;
 
-    public AutomationCompositionElementHandlerV3(ParticipantIntermediaryApi intermediaryApi,
+    public AutomationCompositionElementHandler(ParticipantIntermediaryApi intermediaryApi,
         SimulatorService simulatorService) {
         super(intermediaryApi);
         this.simulatorService = simulatorService;
@@ -145,9 +145,10 @@ public class AutomationCompositionElementHandlerV3 extends AcElementListenerV3 {
     }
 
     @Override
-    public void prepare(CompositionElementDto compositionElement, InstanceElementDto instanceElement) {
+    public void prepare(CompositionElementDto compositionElement, InstanceElementDto instanceElement, int stage) {
         LOGGER.debug("prepare call compositionElement: {}, instanceElement: {}", compositionElement, instanceElement);
-        simulatorService.prepare(instanceElement.instanceId(), instanceElement.elementId());
+        simulatorService.prepare(instanceElement.instanceId(), instanceElement.elementId(),
+                stage, compositionElement.inProperties(), instanceElement.outProperties());
     }
 
     @Override
index b689a78..93a9ee7 100644 (file)
@@ -55,6 +55,8 @@ public class SimulatorService {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(SimulatorService.class);
     private static final String INTERNAL_STATE = "InternalState";
+    private static final String MIGRATION_PROPERTY = "stage";
+    private static final String PREPARE_PROPERTY = "prepareStage";
 
     @Getter
     @Setter
@@ -358,6 +360,8 @@ public class SimulatorService {
      * @param instanceId the instanceId
      * @param elementId the elementId
      * @param stage the stage
+     * @param compositionInProperties in Properties from composition definition element
+     * @param instanceOutProperties in Properties from instance element
      */
     public void migrate(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
             Map<String, Object> instanceOutProperties) {
@@ -374,9 +378,9 @@ public class SimulatorService {
                     nextStage = Math.min(s, nextStage);
                 }
             }
-            instanceOutProperties.putIfAbsent("stage", new ArrayList<>());
+            instanceOutProperties.putIfAbsent(MIGRATION_PROPERTY, new ArrayList<>());
             @SuppressWarnings("unchecked")
-            var stageList = (List<Integer>) instanceOutProperties.get("stage");
+            var stageList = (List<Integer>) instanceOutProperties.get(MIGRATION_PROPERTY);
             stageList.add(stage);
             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
             if (nextStage == 1000) {
@@ -421,16 +425,38 @@ public class SimulatorService {
      *
      * @param instanceId the instanceId
      * @param elementId the elementId
+     * @param stage the stage
+     * @param compositionInProperties in Properties from composition definition element
+     * @param instanceOutProperties in Properties from instance element
      */
-    public void prepare(UUID instanceId, UUID elementId) {
+    public void prepare(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
+            Map<String, Object> instanceOutProperties) {
         if (!execution(config.getPrepareTimerMs(),
                 "Current Thread prepare is Interrupted during execution {}", elementId)) {
             return;
         }
 
         if (config.isPrepare()) {
-            intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
-                    DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Prepare completed");
+            var stageSet = ParticipantUtils.findStageSetPrepare(compositionInProperties);
+            var nextStage = 1000;
+            for (var s : stageSet) {
+                if (s > stage) {
+                    nextStage = Math.min(s, nextStage);
+                }
+            }
+            instanceOutProperties.putIfAbsent(PREPARE_PROPERTY, new ArrayList<>());
+            @SuppressWarnings("unchecked")
+            var stageList = (List<Integer>) instanceOutProperties.get(PREPARE_PROPERTY);
+            stageList.add(stage);
+            intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
+            if (nextStage == 1000) {
+                intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
+                        DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Prepare completed");
+            } else {
+                intermediaryApi.updateAutomationCompositionElementStage(
+                        instanceId, elementId,
+                        StateChangeResult.NO_ERROR, nextStage, "stage " + stage + " Prepared");
+            }
         } else {
             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
                     DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Prepare failed");
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2024-2025 Nordix Foundation.
+ *  Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 
-class AutomationCompositionElementHandlerV3Test {
+class AutomationCompositionElementHandlerTest {
 
     private static final ToscaConceptIdentifier ELEMENT_DEFINITION_ID = new ToscaConceptIdentifier("name", "1.0.0");
     private static final CompositionElementDto COMPOSITION_ELEMENT =
@@ -55,7 +55,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         acElementHandler.deploy(COMPOSITION_ELEMENT, INSTANCE_ELEMENT);
         verify(intermediaryApi).updateAutomationCompositionElementState(
@@ -74,7 +74,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         acElementHandler.undeploy(COMPOSITION_ELEMENT, INSTANCE_ELEMENT);
         verify(intermediaryApi).updateAutomationCompositionElementState(
@@ -93,7 +93,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         acElementHandler.lock(COMPOSITION_ELEMENT, INSTANCE_ELEMENT);
         verify(intermediaryApi).updateAutomationCompositionElementState(
@@ -112,7 +112,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         acElementHandler.unlock(COMPOSITION_ELEMENT, INSTANCE_ELEMENT);
         verify(intermediaryApi).updateAutomationCompositionElementState(
@@ -131,7 +131,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         var instanceElementUpdated = new InstanceElementDto(
                 INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
@@ -153,7 +153,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         acElementHandler.delete(COMPOSITION_ELEMENT, INSTANCE_ELEMENT);
         verify(intermediaryApi).updateAutomationCompositionElementState(
@@ -172,7 +172,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         acElementHandler.prime(COMPOSITION);
         verify(intermediaryApi).updateCompositionState(
@@ -189,7 +189,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         acElementHandler.deprime(COMPOSITION);
         verify(intermediaryApi).updateCompositionState(
@@ -206,7 +206,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         var compositionElementTarget = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
                 Map.of(), Map.of());
@@ -232,7 +232,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         var compositionElementTarget = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
                 Map.of("stage", List.of(1, 2)), Map.of());
@@ -250,7 +250,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         var compositionElement = new CompositionElementDto(
                 UUID.randomUUID(), new ToscaConceptIdentifier(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
@@ -274,7 +274,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
 
         var compoElTargetRemove = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
@@ -297,7 +297,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         var compositionElementTarget = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
                 Map.of(), Map.of());
@@ -325,15 +325,15 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
-        acElementHandler.prepare(COMPOSITION_ELEMENT, INSTANCE_ELEMENT);
+        acElementHandler.prepare(COMPOSITION_ELEMENT, INSTANCE_ELEMENT, 0);
         verify(intermediaryApi).updateAutomationCompositionElementState(
                 INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(), DeployState.UNDEPLOYED,
                 null, StateChangeResult.NO_ERROR, "Prepare completed");
 
         config.setPrepare(false);
-        acElementHandler.prepare(COMPOSITION_ELEMENT, INSTANCE_ELEMENT);
+        acElementHandler.prepare(COMPOSITION_ELEMENT, INSTANCE_ELEMENT, 0);
         verify(intermediaryApi).updateAutomationCompositionElementState(
                 INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(), DeployState.UNDEPLOYED,
                 null, StateChangeResult.FAILED, "Prepare failed");
@@ -344,7 +344,7 @@ class AutomationCompositionElementHandlerV3Test {
         var config = CommonTestData.createSimConfig();
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         var simulatorService = new SimulatorService(intermediaryApi);
-        var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
+        var acElementHandler = new AutomationCompositionElementHandler(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
         acElementHandler.review(COMPOSITION_ELEMENT, INSTANCE_ELEMENT);
         verify(intermediaryApi).updateAutomationCompositionElementState(
index 1744979..3087183 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2024-2025 Nordix Foundation.
+ *  Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -144,10 +144,8 @@ class SimulatorServiceTest {
         simulatorService.delete(UUID.randomUUID(), UUID.randomUUID());
         simulatorService.update(UUID.randomUUID(), UUID.randomUUID());
         simulatorService.review(UUID.randomUUID(), UUID.randomUUID());
-        simulatorService.prepare(UUID.randomUUID(), UUID.randomUUID());
+        simulatorService.prepare(UUID.randomUUID(), UUID.randomUUID(), 0, new HashMap<>(), new HashMap<>());
         simulatorService.migratePrecheck(UUID.randomUUID(), UUID.randomUUID());
         verify(intermediaryApi, times(0)).sendAcDefinitionInfo(any(), any(), any());
-
     }
-
 }
index 5994328..c4edf6e 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2024 Nordix Foundation.
+ *  Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -106,6 +106,6 @@ public interface AutomationCompositionElementListener {
     void review(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
         throws PfModelException;
 
-    void prepare(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
+    void prepare(CompositionElementDto compositionElement, InstanceElementDto instanceElement, int nextStage)
         throws PfModelException;
 }
index d63323d..0d5a6c5 100644 (file)
@@ -1,6 +1,6 @@
 /*-\r
  * ============LICENSE_START=======================================================\r
- *  Copyright (C) 2024 Nordix Foundation.\r
+ *  Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.\r
  * ================================================================================\r
  * Licensed under the Apache License, Version 2.0 (the "License");\r
  * you may not use this file except in compliance with the License.\r
 package org.onap.policy.clamp.acm.participant.intermediary.api.impl;\r
 \r
 import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;\r
-import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;\r
 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;\r
 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;\r
 import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;\r
-import org.onap.policy.clamp.models.acm.concepts.AcTypeState;\r
 import org.onap.policy.clamp.models.acm.concepts.DeployState;\r
-import org.onap.policy.clamp.models.acm.concepts.LockState;\r
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;\r
 import org.onap.policy.models.base.PfModelException;\r
 \r
@@ -35,78 +32,11 @@ import org.onap.policy.models.base.PfModelException;
  * Wrapper of AutomationCompositionElementListener.\r
  * Valid since 8.0.1 release.\r
  */\r
-public abstract class AcElementListenerV3 implements AutomationCompositionElementListener {\r
-    protected final ParticipantIntermediaryApi intermediaryApi;\r
+public abstract class AcElementListenerV3 extends AcElementListenerV4\r
+        implements AutomationCompositionElementListener, AutomationCompositionElementListenerV3 {\r
 \r
     protected AcElementListenerV3(ParticipantIntermediaryApi intermediaryApi) {\r
-        this.intermediaryApi = intermediaryApi;\r
-    }\r
-\r
-    @Override\r
-    public void lock(CompositionElementDto compositionElement, InstanceElementDto instanceElement)\r
-        throws PfModelException {\r
-        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),\r
-            instanceElement.elementId(), null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");\r
-    }\r
-\r
-    @Override\r
-    public void unlock(CompositionElementDto compositionElement, InstanceElementDto instanceElement)\r
-        throws PfModelException {\r
-        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),\r
-            instanceElement.elementId(), null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");\r
-    }\r
-\r
-    @Override\r
-    public void delete(CompositionElementDto compositionElement, InstanceElementDto instanceElement)\r
-        throws PfModelException {\r
-        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),\r
-            instanceElement.elementId(), DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");\r
-    }\r
-\r
-    @Override\r
-    public void update(CompositionElementDto compositionElement, InstanceElementDto instanceElement,\r
-                       InstanceElementDto instanceElementUpdated) throws PfModelException {\r
-        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),\r
-            instanceElement.elementId(), DeployState.DEPLOYED, null,\r
-            StateChangeResult.NO_ERROR, "Update not supported");\r
-\r
-    }\r
-\r
-    @Override\r
-    public void prime(CompositionDto composition) throws PfModelException {\r
-        intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,\r
-            StateChangeResult.NO_ERROR, "Primed");\r
-    }\r
-\r
-    @Override\r
-    public void deprime(CompositionDto composition) throws PfModelException {\r
-        intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,\r
-            StateChangeResult.NO_ERROR, "Deprimed");\r
-    }\r
-\r
-    @Override\r
-    public void migrate(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget,\r
-                        InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate, int stage)\r
-        throws PfModelException {\r
-        intermediaryApi.updateAutomationCompositionElementState(instanceElementMigrate.instanceId(),\r
-            instanceElementMigrate.elementId(), DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");\r
-    }\r
-\r
-    @Override\r
-    public void migratePrecheck(CompositionElementDto compositionElement,\r
-            CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement,\r
-            InstanceElementDto instanceElementMigrate) throws PfModelException {\r
-        intermediaryApi.updateAutomationCompositionElementState(instanceElementMigrate.instanceId(),\r
-                instanceElementMigrate.elementId(), DeployState.DEPLOYED, null,\r
-                StateChangeResult.NO_ERROR, "Migration Precheck completed");\r
-    }\r
-\r
-    @Override\r
-    public void review(CompositionElementDto compositionElement, InstanceElementDto instanceElement)\r
-            throws PfModelException {\r
-        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),\r
-                instanceElement.elementId(), DeployState.DEPLOYED, null,\r
-                StateChangeResult.NO_ERROR, "Review completed");\r
+        super(intermediaryApi);\r
     }\r
 \r
     @Override\r
diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AcElementListenerV4.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AcElementListenerV4.java
new file mode 100644 (file)
index 0000000..002b67d
--- /dev/null
@@ -0,0 +1,119 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.intermediary.api.impl;
+
+import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
+import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
+import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
+import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
+import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
+import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
+import org.onap.policy.clamp.models.acm.concepts.DeployState;
+import org.onap.policy.clamp.models.acm.concepts.LockState;
+import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
+import org.onap.policy.models.base.PfModelException;
+
+/**
+ * Wrapper of AutomationCompositionElementListener.
+ * Valid since 8.1.1 release.
+ */
+public abstract class AcElementListenerV4 implements AutomationCompositionElementListener {
+    protected final ParticipantIntermediaryApi intermediaryApi;
+
+    protected AcElementListenerV4(ParticipantIntermediaryApi intermediaryApi) {
+        this.intermediaryApi = intermediaryApi;
+    }
+
+    @Override
+    public void lock(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
+        throws PfModelException {
+        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
+            instanceElement.elementId(), null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");
+    }
+
+    @Override
+    public void unlock(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
+        throws PfModelException {
+        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
+            instanceElement.elementId(), null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");
+    }
+
+    @Override
+    public void delete(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
+        throws PfModelException {
+        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
+            instanceElement.elementId(), DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
+    }
+
+    @Override
+    public void update(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
+                       InstanceElementDto instanceElementUpdated) throws PfModelException {
+        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
+            instanceElement.elementId(), DeployState.DEPLOYED, null,
+            StateChangeResult.NO_ERROR, "Update not supported");
+
+    }
+
+    @Override
+    public void prime(CompositionDto composition) throws PfModelException {
+        intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
+            StateChangeResult.NO_ERROR, "Primed");
+    }
+
+    @Override
+    public void deprime(CompositionDto composition) throws PfModelException {
+        intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
+            StateChangeResult.NO_ERROR, "Deprimed");
+    }
+
+    @Override
+    public void migrate(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget,
+                        InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate, int stage)
+        throws PfModelException {
+        intermediaryApi.updateAutomationCompositionElementState(instanceElementMigrate.instanceId(),
+            instanceElementMigrate.elementId(), DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
+    }
+
+    @Override
+    public void migratePrecheck(CompositionElementDto compositionElement,
+            CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement,
+            InstanceElementDto instanceElementMigrate) throws PfModelException {
+        intermediaryApi.updateAutomationCompositionElementState(instanceElementMigrate.instanceId(),
+                instanceElementMigrate.elementId(), DeployState.DEPLOYED, null,
+                StateChangeResult.NO_ERROR, "Migration Precheck completed");
+    }
+
+    @Override
+    public void review(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
+            throws PfModelException {
+        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
+                instanceElement.elementId(), DeployState.DEPLOYED, null,
+                StateChangeResult.NO_ERROR, "Review completed");
+    }
+
+    @Override
+    public void prepare(CompositionElementDto compositionElement, InstanceElementDto instanceElement, int nextStage)
+            throws PfModelException {
+        intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
+                instanceElement.elementId(), DeployState.UNDEPLOYED, null,
+                StateChangeResult.NO_ERROR, "Prepare completed");
+    }
+}
diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AutomationCompositionElementListenerV3.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AutomationCompositionElementListenerV3.java
new file mode 100644 (file)
index 0000000..1e5bb56
--- /dev/null
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.intermediary.api.impl;
+
+import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
+import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
+import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
+import org.onap.policy.models.base.PfModelException;
+
+public interface AutomationCompositionElementListenerV3 {
+
+    void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
+
+    void undeploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
+
+    void lock(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
+
+    void unlock(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
+
+    void delete(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
+
+    void update(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
+            InstanceElementDto instanceElementUpdated) throws PfModelException;
+
+    void prime(CompositionDto composition) throws PfModelException;
+
+    void deprime(CompositionDto composition) throws PfModelException;
+
+    void migrate(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget,
+            InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate,
+            int nextStage) throws PfModelException;
+
+    void migratePrecheck(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget,
+            InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate) throws PfModelException;
+
+    void review(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
+        throws PfModelException;
+
+    void prepare(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
+        throws PfModelException;
+}
index 4c5baf6..5b33221 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2024 Nordix Foundation.
+ *  Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
 import org.onap.policy.clamp.models.acm.concepts.DeployState;
+import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
 import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare;
@@ -155,7 +156,7 @@ public class AcSubStateHandler {
                         acPrepareMsg.getAutomationCompositionId(), participantPrepare, DeployState.UNDEPLOYED,
                         SubState.PREPARING);
                     callParticipanPrepare(acPrepareMsg.getMessageId(), participantPrepare.getAcElementList(),
-                        acPrepareMsg.getAutomationCompositionId());
+                        acPrepareMsg.getStage(), acPrepareMsg.getAutomationCompositionId());
                 }
             }
         } else {
@@ -166,7 +167,8 @@ public class AcSubStateHandler {
         }
     }
 
-    private void callParticipanPrepare(UUID messageId, List<AcElementDeploy> acElementList, UUID instanceId) {
+    private void callParticipanPrepare(UUID messageId, List<AcElementDeploy> acElementList, Integer stageMsg,
+            UUID instanceId) {
         var automationComposition = cacheProvider.getAutomationComposition(instanceId);
         for (var elementDeploy : acElementList) {
             var element = automationComposition.getElements().get(elementDeploy.getId());
@@ -174,9 +176,13 @@ public class AcSubStateHandler {
                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
             var compositionElement = cacheProvider.createCompositionElementDto(automationComposition.getCompositionId(),
                 element, compositionInProperties);
-            var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
-                elementDeploy.getProperties(), element.getOutProperties());
-            listener.prepare(messageId, compositionElement, instanceElement);
+            var stageSet = ParticipantUtils.findStageSetPrepare(compositionInProperties);
+            if (stageSet.contains(stageMsg)) {
+                var instanceElement =
+                        new InstanceElementDto(instanceId, elementDeploy.getId(), elementDeploy.getProperties(),
+                                element.getOutProperties());
+                listener.prepare(messageId, compositionElement, instanceElement, stageMsg);
+            }
         }
     }
 
index d9f2b7a..9fc6b1d 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2023-2024 Nordix Foundation.
+ *  Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -155,11 +155,9 @@ public class AutomationCompositionOutHandler {
             return;
         }
 
-        if (deployState != null && !SubState.NONE.equals(element.getSubState())) {
-            handleSubState(automationComposition, element);
+        if (!SubState.NONE.equals(element.getSubState())) {
             if (!StateChangeResult.NO_ERROR.equals(stateChangeResult)) {
-                stateChangeResult = StateChangeResult.NO_ERROR;
-                LOGGER.warn("SubState has always NO_ERROR result!");
+                handleSubState(automationComposition, element, stateChangeResult);
             }
         } else if (deployState != null) {
             handleDeployState(automationComposition, element, deployState, stateChangeResult);
@@ -225,7 +223,11 @@ public class AutomationCompositionOutHandler {
         }
     }
 
-    private void handleSubState(AutomationComposition automationComposition, AutomationCompositionElement element) {
+    private void handleSubState(AutomationComposition automationComposition, AutomationCompositionElement element,
+            StateChangeResult stateChangeResult) {
+        if (StateChangeResult.FAILED.equals(stateChangeResult)) {
+            return;
+        }
         element.setSubState(SubState.NONE);
         var checkOpt = automationComposition.getElements().values().stream()
                 .filter(acElement -> !SubState.NONE.equals(acElement.getSubState())).findAny();
index c422b22..027127a 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2023-2024 Nordix Foundation.
+ *  Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -397,17 +397,19 @@ public class ThreadHandler implements Closeable {
      * @param messageId the messageId
      * @param compositionElement the information of the Automation Composition Definition Element
      * @param instanceElement the information of the Automation Composition Instance Element
+     * @param stage the stage
      */
     public void prepare(UUID messageId, CompositionElementDto compositionElement,
-        InstanceElementDto instanceElement) {
+        InstanceElementDto instanceElement, int stage) {
         cleanExecution(instanceElement.elementId(), messageId);
-        var result = executor.submit(() -> this.prepareProcess(compositionElement, instanceElement));
+        var result = executor.submit(() -> this.prepareProcess(compositionElement, instanceElement, stage));
         executionMap.put(instanceElement.elementId(), result);
     }
 
-    private void prepareProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) {
+    private void prepareProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
+        int stage) {
         try {
-            listener.prepare(compositionElement, instanceElement);
+            listener.prepare(compositionElement, instanceElement, stage);
         } catch (PfModelException e) {
             LOGGER.error("Automation composition element prepare Pre Deploy failed {} {}",
                 instanceElement.elementId(), e.getMessage());
index 8fad1d2..d9df764 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2024 Nordix Foundation.
+ *  Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ package org.onap.policy.clamp.acm.participant.intermediary.handler;
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -141,6 +142,7 @@ class AcSubStateHandlerTest {
 
         var acPrepareMsg = new AutomationCompositionPrepare();
         acPrepareMsg.setPreDeploy(true);
+        acPrepareMsg.setStage(0);
         assertDoesNotThrow(() -> ach.handleAcPrepare(acPrepareMsg));
 
         acPrepareMsg.setParticipantId(CommonTestData.getParticipantId());
@@ -165,7 +167,7 @@ class AcSubStateHandlerTest {
             .thenReturn(Map.of(automationComposition.getCompositionId(), map));
 
         ach.handleAcPrepare(acPrepareMsg);
-        verify(listener, times(automationComposition.getElements().size())).prepare(any(), any(), any());
+        verify(listener, times(automationComposition.getElements().size())).prepare(any(), any(), any(), anyInt());
     }
 
     @Test
index 8502bec..7e5ca57 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2023-2024 Nordix Foundation.
+ *  Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -260,8 +260,8 @@ class ThreadHandlerTest {
             var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
                 properties, properties);
             var messageId = UUID.randomUUID();
-            threadHandler.prepare(messageId, compositionElement, instanceElement);
-            verify(listener, timeout(TIMEOUT)).prepare(compositionElement, instanceElement);
+            threadHandler.prepare(messageId, compositionElement, instanceElement, 0);
+            verify(listener, timeout(TIMEOUT)).prepare(compositionElement, instanceElement, 0);
 
             clearInvocations(listener);
             threadHandler.review(messageId, compositionElement, instanceElement);
@@ -295,9 +295,9 @@ class ThreadHandlerTest {
             var element = new AcElementDeploy();
             element.setId(elementId);
             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
-                .prepare(compositionElement, instanceElement);
+                .prepare(compositionElement, instanceElement, 0);
             var messageId = UUID.randomUUID();
-            threadHandler.prepare(messageId, compositionElement, instanceElement);
+            threadHandler.prepare(messageId, compositionElement, instanceElement, 0);
             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
                 "Automation composition element prepare Pre Deploy failed");
index 0966ab3..1ae309d 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2025 Nordix Foundation.
+ * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
  * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -427,25 +427,22 @@ public class AutomationCompositionInstantiationProvider {
             throw new PfModelRuntimeException(Status.BAD_REQUEST,
                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
         }
-        if (!DeployState.UNDEPLOYED.equals(automationComposition.getDeployState())
-                && !DeployState.DELETING.equals(automationComposition.getDeployState())) {
-            throw new PfModelRuntimeException(Status.BAD_REQUEST,
-                    "Automation composition state is still " + automationComposition.getDeployState());
-        }
-        if (DeployState.DELETING.equals(automationComposition.getDeployState())
-                && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) {
-            throw new PfModelRuntimeException(Status.BAD_REQUEST,
-                    "Automation composition state is still " + automationComposition.getDeployState());
-        }
         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
         var participantIds = acDefinition.getElementStateMap().values().stream()
             .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
         participantProvider.verifyParticipantState(participantIds);
+        var result = acInstanceStateResolver.resolve(DeployOrder.DELETE,
+                null, null,
+                automationComposition.getDeployState(), automationComposition.getLockState(),
+                automationComposition.getSubState(), automationComposition.getStateChangeResult());
+        if (!DeployOrder.DELETE.name().equals(result)) {
+            var msg = String.format(NOT_VALID_ORDER, DeployOrder.DELETE,
+                    automationComposition.getDeployState(), automationComposition.getLockState(),
+                    automationComposition.getSubState(), automationComposition.getStateChangeResult());
+            throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
+        }
         supervisionAcHandler.delete(automationComposition, acDefinition);
-        var response = new InstantiationResponse();
-        response.setInstanceId(automationComposition.getInstanceId());
-        response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
-        return response;
+        return createInstantiationResponse(automationComposition);
     }
 
     /**
@@ -506,7 +503,7 @@ public class AutomationCompositionInstantiationProvider {
                 break;
 
             case "PREPARE":
-                supervisionAcHandler.prepare(automationComposition);
+                supervisionAcHandler.prepare(automationComposition, acDefinition);
                 break;
 
             case "REVIEW":
index f436eb2..d332339 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2023-2025 Nordix Foundation.
+ *  Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -156,15 +156,18 @@ public class SupervisionAcHandler {
      * Handle prepare Pre Deploy an AutomationComposition instance.
      *
      * @param automationComposition the AutomationComposition
+     * @param acDefinition the AutomationCompositionDefinition
      */
-    public void prepare(AutomationComposition automationComposition) {
+    public void prepare(AutomationComposition automationComposition, AutomationCompositionDefinition acDefinition) {
         AcmUtils.setCascadedState(automationComposition, DeployState.UNDEPLOYED, LockState.NONE, SubState.PREPARING);
         automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
+        var stage = ParticipantUtils.getFirstStage(automationComposition, acDefinition.getServiceTemplate());
+        automationComposition.setPhase(stage);
         automationCompositionProvider.updateAutomationComposition(automationComposition);
         executor.execute(() -> {
             var acToSend = new AutomationComposition(automationComposition);
             decryptInstanceProperties(acToSend);
-            acPreparePublisher.sendPrepare(acToSend);
+            acPreparePublisher.sendPrepare(acToSend, stage);
         });
     }
 
index acf4035..fd465b7 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2024 Nordix Foundation.
+ * Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -42,11 +42,13 @@ public class AcPreparePublisher extends AbstractParticipantPublisher<AutomationC
      * Send AutomationCompositionPrepare Prepare message to Participant.
      *
      * @param automationComposition the AutomationComposition
+     * @param stage the stage
      */
     @Timed(value = "publisher.prepare", description = "AC Prepare Pre Deploy published")
-    public void sendPrepare(AutomationComposition automationComposition) {
+    public void sendPrepare(AutomationComposition automationComposition, int stage) {
         var acPrepare = createAutomationCompositionPrepare(automationComposition.getCompositionId(),
             automationComposition.getInstanceId());
+        acPrepare.setStage(stage);
         acPrepare.setParticipantList(
             AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.NONE));
         LOGGER.debug("AC Prepare sent {}", acPrepare);
index 66875fa..dcf0881 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2025 Nordix Foundation.
+ * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
@@ -109,11 +109,11 @@ public class MonitoringScanner {
             return;
         }
 
-        if (DeployState.MIGRATING.equals(automationComposition.getDeployState())) {
+        if (DeployState.MIGRATING.equals(automationComposition.getDeployState())
+                || SubState.PREPARING.equals(automationComposition.getSubState())) {
             stageScanner.scanStage(automationComposition, serviceTemplate, updateSync);
         } else if (DeployState.UPDATING.equals(automationComposition.getDeployState())
                 || SubState.REVIEWING.equals(automationComposition.getSubState())
-                || SubState.PREPARING.equals(automationComposition.getSubState())
                 || SubState.MIGRATION_PRECHECKING.equals(automationComposition.getSubState())) {
             simpleScanner.simpleScan(automationComposition, updateSync);
         } else {
index f05df6b..65caa87 100644 (file)
@@ -23,10 +23,13 @@ package org.onap.policy.clamp.acm.runtime.supervision.scanner;
 import java.util.Comparator;
 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
 import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.AcPreparePublisher;
 import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionMigrationPublisher;
 import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
+import org.onap.policy.clamp.models.acm.concepts.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
@@ -36,6 +39,7 @@ import org.springframework.stereotype.Component;
 public class StageScanner extends AbstractScanner {
 
     private final AutomationCompositionMigrationPublisher acMigrationPublisher;
+    private final AcPreparePublisher acPreparePublisher;
 
     /**
      * Constructor for instantiating StageScanner.
@@ -44,14 +48,18 @@ public class StageScanner extends AbstractScanner {
      * @param participantSyncPublisher the Participant Sync Publisher
      * @param acMigrationPublisher the AutomationComposition Migration Publisher
      * @param acRuntimeParameterGroup the parameters for the automation composition runtime
+     * @param encryptionUtils the EncryptionUtils
      */
-    public StageScanner(final AutomationCompositionProvider acProvider,
-                        final ParticipantSyncPublisher participantSyncPublisher,
-                        final AutomationCompositionMigrationPublisher acMigrationPublisher,
-                        final AcRuntimeParameterGroup acRuntimeParameterGroup,
-                        final EncryptionUtils encryptionUtils) {
+    public StageScanner(
+            final AutomationCompositionProvider acProvider,
+            final ParticipantSyncPublisher participantSyncPublisher,
+            final AutomationCompositionMigrationPublisher acMigrationPublisher,
+            final AcPreparePublisher acPreparePublisher,
+            final AcRuntimeParameterGroup acRuntimeParameterGroup,
+            final EncryptionUtils encryptionUtils) {
         super(acProvider, participantSyncPublisher, acRuntimeParameterGroup, encryptionUtils);
         this.acMigrationPublisher = acMigrationPublisher;
+        this.acPreparePublisher = acPreparePublisher;
     }
 
     /**
@@ -70,7 +78,9 @@ public class StageScanner extends AbstractScanner {
                     element.getSubState())) {
                 var toscaNodeTemplate = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
                         .get(element.getDefinition().getName());
-                var stageSet = ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties());
+                var stageSet = DeployState.MIGRATING.equals(automationComposition.getDeployState())
+                        ? ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties())
+                        : ParticipantUtils.findStageSetPrepare(toscaNodeTemplate.getProperties());
                 var minStage = stageSet.stream().min(Comparator.comparing(Integer::valueOf)).orElse(0);
                 int stage = element.getStage() != null ? element.getStage() : minStage;
                 minStageNotCompleted = Math.min(minStageNotCompleted, stage);
@@ -91,10 +101,21 @@ public class StageScanner extends AbstractScanner {
                 LOGGER.debug("retry message AutomationCompositionMigration");
                 var acToSend = new AutomationComposition(automationComposition);
                 decryptInstanceProperties(acToSend);
-                acMigrationPublisher.send(acToSend, minStageNotCompleted);
+                sendNextStage(acToSend, minStageNotCompleted);
             } else {
                 handleTimeout(automationComposition, updateSync);
             }
         }
     }
+
+    private void sendNextStage(final AutomationComposition automationComposition, int minStageNotCompleted) {
+        if (DeployState.MIGRATING.equals(automationComposition.getDeployState())) {
+            LOGGER.debug("retry message AutomationCompositionMigration");
+            acMigrationPublisher.send(automationComposition, minStageNotCompleted);
+        }
+        if (SubState.PREPARING.equals(automationComposition.getSubState())) {
+            LOGGER.debug("retry message AutomationCompositionPrepare");
+            acPreparePublisher.sendPrepare(automationComposition, minStageNotCompleted);
+        }
+    }
 }
index a3d11b5..759b6ee 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2025 Nordix Foundation.
+ *  Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -79,7 +79,7 @@ class AutomationCompositionInstantiationProviderTest {
             "src/test/resources/rest/acm/AutomationCompositionElementsNotFound.json";
     private static final String AC_INSTANTIATION_AC_DEFINITION_NOT_FOUND_JSON =
             "src/test/resources/rest/acm/AutomationCompositionNotFound.json";
-    private static final String DELETE_BAD_REQUEST = "Automation composition state is still %s";
+    private static final String DELETE_BAD_REQUEST = "Not valid order DELETE;";
 
     private static final String AC_ELEMENT_NAME_NOT_FOUND = """
             "AutomationComposition" INVALID, item has status INVALID
@@ -118,7 +118,7 @@ class AutomationCompositionInstantiationProviderTest {
         var participantProvider = mock(ParticipantProvider.class);
         var encryptionUtils = new EncryptionUtils(CommonTestData.getTestParamaterGroup());
         var instantiationProvider = new AutomationCompositionInstantiationProvider(acProvider, acDefinitionProvider,
-                null, supervisionAcHandler, participantProvider,
+                new AcInstanceStateResolver(), supervisionAcHandler, participantProvider,
                 CommonTestData.getTestParamaterGroup(), encryptionUtils);
         var automationCompositionCreate =
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Crud");
@@ -520,7 +520,7 @@ class AutomationCompositionInstantiationProviderTest {
         var encryptionUtils = mock(EncryptionUtils.class);
 
         var instantiationProvider = new AutomationCompositionInstantiationProvider(acProvider, acDefinitionProvider,
-                null, supervisionAcHandler, participantProvider, acRuntimeParameterGroup,
+                new AcInstanceStateResolver(), supervisionAcHandler, participantProvider, acRuntimeParameterGroup,
                 encryptionUtils);
 
         when(acProvider.getAutomationComposition(automationComposition.getInstanceId()))
@@ -531,38 +531,47 @@ class AutomationCompositionInstantiationProviderTest {
         assertThatThrownBy(() -> instantiationProvider.deleteAutomationComposition(wrongCompositionId, instanceId))
                 .hasMessageMatching(compositionId + DO_NOT_MATCH + wrongCompositionId);
 
-        assertThatDeleteThrownBy(automationComposition, DeployState.DEPLOYED, LockState.LOCKED);
-        assertThatDeleteThrownBy(automationComposition, DeployState.DEPLOYING, LockState.NONE);
-        assertThatDeleteThrownBy(automationComposition, DeployState.UNDEPLOYING, LockState.LOCKED);
-        assertThatDeleteThrownBy(automationComposition, DeployState.DELETING, LockState.NONE);
-
         automationComposition.setDeployState(DeployState.UNDEPLOYED);
         automationComposition.setLockState(LockState.NONE);
         when(acProvider.deleteAutomationComposition(instanceId)).thenReturn(automationComposition);
-
         instantiationProvider.deleteAutomationComposition(compositionId, instanceId);
         verify(supervisionAcHandler).delete(any(), any());
     }
 
+    @Test
+    void testInstantiationDeleteError() {
+        var automationComposition =
+                InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Delete");
+        automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
+        assertThatDeleteThrownBy(automationComposition, DeployState.DEPLOYED, LockState.LOCKED);
+        assertThatDeleteThrownBy(automationComposition, DeployState.DEPLOYING, LockState.NONE);
+        assertThatDeleteThrownBy(automationComposition, DeployState.UNDEPLOYING, LockState.LOCKED);
+        assertThatDeleteThrownBy(automationComposition, DeployState.DELETING, LockState.NONE);
+    }
+
     private void assertThatDeleteThrownBy(AutomationComposition automationComposition, DeployState deployState,
             LockState lockState) {
         automationComposition.setDeployState(deployState);
         automationComposition.setLockState(lockState);
         var acProvider = mock(AutomationCompositionProvider.class);
+        when(acProvider.getAutomationComposition(automationComposition.getInstanceId()))
+                .thenReturn(automationComposition);
         var acDefinitionProvider = mock(AcDefinitionProvider.class);
         var acRuntimeParamaterGroup = mock(AcRuntimeParameterGroup.class);
 
         var instantiationProvider =
-                new AutomationCompositionInstantiationProvider(acProvider, acDefinitionProvider, null, null, null,
+                new AutomationCompositionInstantiationProvider(acProvider, acDefinitionProvider,
+                        new AcInstanceStateResolver(), null, mock(ParticipantProvider.class),
                         acRuntimeParamaterGroup, null);
 
-        when(acProvider.getAutomationComposition(automationComposition.getInstanceId()))
-                .thenReturn(automationComposition);
+        var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
+        var compositionId = acDefinition.getCompositionId();
+        when(acDefinitionProvider.getAcDefinition(compositionId)).thenReturn(acDefinition);
+        automationComposition.setCompositionId(compositionId);
 
-        var compositionId = automationComposition.getCompositionId();
         var instanceId = automationComposition.getInstanceId();
         assertThatThrownBy(() -> instantiationProvider.deleteAutomationComposition(compositionId, instanceId))
-                .hasMessageMatching(String.format(DELETE_BAD_REQUEST, deployState));
+                .hasMessageStartingWith(String.format(DELETE_BAD_REQUEST));
     }
 
     @Test
@@ -762,7 +771,7 @@ class AutomationCompositionInstantiationProviderTest {
         acInstanceStateUpdate.setLockOrder(LockOrder.NONE);
         acInstanceStateUpdate.setSubOrder(SubOrder.PREPARE);
         provider.compositionInstanceState(compositionId, instanceId, acInstanceStateUpdate);
-        verify(supervisionAcHandler).prepare(any(AutomationComposition.class));
+        verify(supervisionAcHandler).prepare(any(AutomationComposition.class), any());
 
         automationComposition.setDeployState(DeployState.DEPLOYED);
         automationComposition.setLockState(LockState.LOCKED);
index e3baa6f..2218932 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2023-2025 Nordix Foundation.
+ *  Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -437,10 +437,12 @@ class SupervisionAcHandlerTest {
                 mock(AutomationCompositionDeployPublisher.class), mock(AutomationCompositionStateChangePublisher.class),
                 mock(AcElementPropertiesPublisher.class), mock(AutomationCompositionMigrationPublisher.class),
                 acPreparePublisher, mock(MessageProvider.class), mock(EncryptionUtils.class));
+        var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+        var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
         var automationComposition =
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Migrate");
-        handler.prepare(automationComposition);
-        verify(acPreparePublisher, timeout(1000)).sendPrepare(any(AutomationComposition.class));
+        handler.prepare(automationComposition, acDefinition);
+        verify(acPreparePublisher, timeout(1000)).sendPrepare(any(AutomationComposition.class), anyInt());
     }
 
     @Test
index bd18500..2f75ba0 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2025 Nordix Foundation.
+ *  Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -322,14 +322,6 @@ class SupervisionScannerTest {
         verify(simpleScanner).simpleScan(automationComposition, new UpdateSync());
         verify(messageProvider).removeJob(JOB_ID);
 
-        clearInvocations(simpleScanner);
-        clearInvocations(messageProvider);
-        automationComposition.setDeployState(DeployState.UNDEPLOYED);
-        automationComposition.setSubState(SubState.PREPARING);
-        supervisionScanner.run();
-        verify(simpleScanner).simpleScan(automationComposition, new UpdateSync());
-        verify(messageProvider).removeJob(JOB_ID);
-
         clearInvocations(simpleScanner);
         clearInvocations(messageProvider);
         automationComposition.setDeployState(DeployState.DEPLOYED);
index 4e862c4..ad3feeb 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2024 Nordix Foundation.
+ *  Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -243,7 +243,7 @@ class SupervisionMessagesTest {
         publisher.active(topicSink);
         var automationComposition =
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_UPDATE_JSON, "Crud");
-        publisher.sendPrepare(automationComposition);
+        publisher.sendPrepare(automationComposition, 0);
         verify(topicSink).send(anyString());
     }
 
index 077580d..1cd1146 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2025 Nordix Foundation.
+ * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,12 +35,14 @@ import java.util.UUID;
 import org.junit.jupiter.api.Test;
 import org.onap.policy.clamp.acm.runtime.instantiation.InstantiationUtils;
 import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.AcPreparePublisher;
 import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionMigrationPublisher;
 import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
 import org.onap.policy.clamp.acm.runtime.util.CommonTestData;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 import org.onap.policy.clamp.models.acm.concepts.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
 import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
 
@@ -69,12 +71,11 @@ class StageScannerTest {
 
         var acProvider = mock(AutomationCompositionProvider.class);
         when(acProvider.updateAutomationComposition(any())).thenReturn(automationComposition);
-
         var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
         var encryptionUtils = new EncryptionUtils(acRuntimeParameterGroup);
         var supervisionScanner = new StageScanner(acProvider, mock(ParticipantSyncPublisher.class),
-                mock(AutomationCompositionMigrationPublisher.class), acRuntimeParameterGroup, encryptionUtils);
-
+                mock(AutomationCompositionMigrationPublisher.class), mock(AcPreparePublisher.class),
+                acRuntimeParameterGroup, encryptionUtils);
         var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
         supervisionScanner.scanStage(automationComposition, serviceTemplate, new UpdateSync());
         verify(acProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
@@ -99,4 +100,56 @@ class StageScannerTest {
         assertEquals(DeployState.DEPLOYED, automationComposition.getDeployState());
         assertEquals(compositionTargetId, automationComposition.getCompositionId());
     }
+
+    @Test
+    void testSendAutomationCompositionPrepare() {
+        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+        automationComposition.setDeployState(DeployState.UNDEPLOYED);
+        automationComposition.setSubState(SubState.PREPARING);
+        automationComposition.setLockState(LockState.NONE);
+        automationComposition.setCompositionId(COMPOSITION_ID);
+        automationComposition.setLastMsg(TimestampHelper.now());
+        automationComposition.setPhase(0);
+        for (var element : automationComposition.getElements().values()) {
+            element.setDeployState(DeployState.UNDEPLOYED);
+            element.setLockState(LockState.NONE);
+            element.setSubState(SubState.NONE);
+        }
+        // first element is not prepared yet
+        var element = automationComposition.getElements().entrySet().iterator().next().getValue();
+        element.setSubState(SubState.PREPARING);
+
+        var acProvider = mock(AutomationCompositionProvider.class);
+        when(acProvider.updateAutomationComposition(any())).thenReturn(automationComposition);
+
+        var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+        var encryptionUtils = new EncryptionUtils(acRuntimeParameterGroup);
+        var supervisionScanner = new StageScanner(acProvider, mock(ParticipantSyncPublisher.class),
+                mock(AutomationCompositionMigrationPublisher.class), mock(AcPreparePublisher.class),
+                acRuntimeParameterGroup, encryptionUtils);
+
+        var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+        supervisionScanner.scanStage(automationComposition, serviceTemplate, new UpdateSync());
+        verify(acProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
+        assertEquals(SubState.PREPARING, automationComposition.getSubState());
+
+        // send message for next stage
+        clearInvocations(acProvider);
+        var toscaNodeTemplate = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
+                .get(element.getDefinition().getName());
+        var prepare = Map.of("prepare", List.of(1));
+        toscaNodeTemplate.setProperties(Map.of("stage", prepare));
+
+        supervisionScanner.scanStage(automationComposition, serviceTemplate, new UpdateSync());
+        verify(acProvider).updateAutomationComposition(any(AutomationComposition.class));
+        assertEquals(SubState.PREPARING, automationComposition.getSubState());
+
+        // first element is prepared
+        clearInvocations(acProvider);
+        element.setSubState(SubState.NONE);
+        supervisionScanner.scanStage(automationComposition, serviceTemplate, new UpdateSync());
+        verify(acProvider).updateAutomationComposition(any(AutomationComposition.class));
+
+        assertEquals(SubState.NONE, automationComposition.getSubState());
+    }
 }