Allow migration to be performed in stages in ACM intermediary 37/138637/1
authorFrancescoFioraEst <francesco.fiora@est.tech>
Wed, 20 Mar 2024 16:17:46 +0000 (16:17 +0000)
committerFrancesco Fiora <francesco.fiora@est.tech>
Tue, 6 Aug 2024 10:33:25 +0000 (10:33 +0000)
Allow migration to be performed in stages,
and replace newElement and removedElement with an Enum
in CompositionElementDto and InstanceElementDto.

Issue-ID: POLICY-5091
Change-Id: I2d66abc453776fd708fc18fd9019ca248f8d2eee
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
21 files changed:
participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV2.java
participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV3.java
participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV2Test.java
participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV3Test.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/CompositionElementDto.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/ElementState.java [new file with mode: 0644]
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/InstanceElementDto.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/ParticipantIntermediaryApiImpl.java
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/AutomationCompositionHandler.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/CacheProvider.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/api/impl/AcElementListenerV2Test.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/AutomationCompositionHandlerTest.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandlerTest.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/SupervisionScanner.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java

index a7f72ec..82602c1 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.policy.clamp.acm.participant.sim.main.handler;
 
 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.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.AcElementListenerV2;
@@ -116,11 +117,11 @@ public class AutomationCompositionElementHandlerV2 extends AcElementListenerV2 {
                         + " instanceElementMigrate: {}",
                 compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate);
 
-        if (instanceElementMigrate.newElement()) {
+        if (ElementState.NEW.equals(instanceElementMigrate.state())) {
             LOGGER.debug("new element scenario");
 
         }
-        if (instanceElementMigrate.removedElement()) {
+        if (ElementState.REMOVED.equals(instanceElementMigrate.state())) {
             simulatorService.undeploy(instanceElement.instanceId(), instanceElement.elementId());
             simulatorService.delete(instanceElement.instanceId(), instanceElement.elementId());
         } else {
index 66f4c30..3e9db90 100644 (file)
@@ -24,6 +24,7 @@ import lombok.Getter;
 import lombok.Setter;
 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.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;
@@ -120,10 +121,10 @@ public class AutomationCompositionElementHandlerV3 extends AcElementListenerV3 {
                 + " instanceElementMigrate: {}, stage: {}",
             compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate, stage);
 
-        if (instanceElementMigrate.newElement()) {
+        if (ElementState.NEW.equals(instanceElementMigrate.state())) {
             LOGGER.debug("new element scenario");
         }
-        if (instanceElementMigrate.removedElement()) {
+        if (ElementState.REMOVED.equals(instanceElementMigrate.state())) {
             simulatorService.undeploy(instanceElement.instanceId(), instanceElement.elementId());
             simulatorService.delete(instanceElement.instanceId(), instanceElement.elementId());
         } else {
index e8e9b76..f3a1839 100644 (file)
@@ -28,6 +28,7 @@ import java.util.UUID;
 import org.junit.jupiter.api.Test;
 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.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.sim.comm.CommonTestData;
@@ -229,15 +230,21 @@ class AutomationCompositionElementHandlerV2Test {
         var simulatorService = new SimulatorService(intermediaryApi);
         var acElementHandler = new AutomationCompositionElementHandlerV2(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
-        var compoElTargetAdd = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
-                Map.of(), Map.of(), true, false);
+
+        var compositionElement = new CompositionElementDto(
+                UUID.randomUUID(), new ToscaConceptIdentifier(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
+
+        var instanceElement = new InstanceElementDto(
+                UUID.randomUUID(), UUID.randomUUID(), null, Map.of(), Map.of(), ElementState.NOT_PRESENT);
+
+        var compoElTargetAdd = new CompositionElementDto(
+                UUID.randomUUID(), new ToscaConceptIdentifier(), Map.of(), Map.of(), ElementState.NEW);
         var inElMigratedAdd = new InstanceElementDto(
-                INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
-                null, Map.of("key", "value"), Map.of(), true, false);
+                instanceElement.instanceId(), instanceElement.elementId(), null, Map.of(), Map.of(), ElementState.NEW);
         acElementHandler
-                .migrate(COMPOSITION_ELEMENT, compoElTargetAdd, INSTANCE_ELEMENT, inElMigratedAdd);
+                .migrate(compositionElement, compoElTargetAdd, instanceElement, inElMigratedAdd);
         verify(intermediaryApi).updateAutomationCompositionElementState(
-                INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
+                instanceElement.instanceId(), instanceElement.elementId(),
                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
     }
 
@@ -250,10 +257,10 @@ class AutomationCompositionElementHandlerV2Test {
         simulatorService.setConfig(config);
 
         var compoElTargetRemove = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
-                Map.of(), Map.of(), false, true);
+                Map.of(), Map.of(), ElementState.REMOVED);
         var inElMigratedRemove = new InstanceElementDto(
                 INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
-                null, Map.of("key", "value"), Map.of(), false, true);
+                null, Map.of("key", "value"), Map.of(), ElementState.REMOVED);
         acElementHandler
                 .migrate(COMPOSITION_ELEMENT, compoElTargetRemove, INSTANCE_ELEMENT, inElMigratedRemove);
         verify(intermediaryApi).updateAutomationCompositionElementState(
index 3a7af24..12703c7 100644 (file)
@@ -29,6 +29,7 @@ import java.util.UUID;
 import org.junit.jupiter.api.Test;
 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.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.sim.comm.CommonTestData;
@@ -249,15 +250,20 @@ class AutomationCompositionElementHandlerV3Test {
         var simulatorService = new SimulatorService(intermediaryApi);
         var acElementHandler = new AutomationCompositionElementHandlerV3(intermediaryApi, simulatorService);
         simulatorService.setConfig(config);
-        var compoElTargetAdd = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
-                Map.of(), Map.of(), true, false);
+        var compositionElement = new CompositionElementDto(
+                UUID.randomUUID(), new ToscaConceptIdentifier(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
+
+        var instanceElement = new InstanceElementDto(
+                UUID.randomUUID(), UUID.randomUUID(), null, Map.of(), Map.of(), ElementState.NOT_PRESENT);
+
+        var compoElTargetAdd = new CompositionElementDto(
+                UUID.randomUUID(), new ToscaConceptIdentifier(), Map.of(), Map.of(), ElementState.NEW);
         var inElMigratedAdd = new InstanceElementDto(
-                INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
-                null, Map.of("key", "value"), Map.of(), true, false);
+                instanceElement.instanceId(), instanceElement.elementId(), null, Map.of(), Map.of(), ElementState.NEW);
         acElementHandler
-                .migrate(COMPOSITION_ELEMENT, compoElTargetAdd, INSTANCE_ELEMENT, inElMigratedAdd, 0);
+                .migrate(compositionElement, compoElTargetAdd, instanceElement, inElMigratedAdd, 0);
         verify(intermediaryApi).updateAutomationCompositionElementState(
-                INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
+                instanceElement.instanceId(), instanceElement.elementId(),
                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
     }
 
@@ -270,10 +276,10 @@ class AutomationCompositionElementHandlerV3Test {
         simulatorService.setConfig(config);
 
         var compoElTargetRemove = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
-                Map.of(), Map.of(), false, true);
+                Map.of(), Map.of(), ElementState.REMOVED);
         var inElMigratedRemove = new InstanceElementDto(
                 INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
-                null, Map.of("key", "value"), Map.of(), false, true);
+                null, Map.of("key", "value"), Map.of(), ElementState.REMOVED);
         acElementHandler
                 .migrate(COMPOSITION_ELEMENT, compoElTargetRemove, INSTANCE_ELEMENT, inElMigratedRemove, 0);
         verify(intermediaryApi).updateAutomationCompositionElementState(
index 51800ce..50699e2 100644 (file)
@@ -26,11 +26,11 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 
 public record CompositionElementDto(UUID compositionId, ToscaConceptIdentifier elementDefinitionId,
                                     Map<String, Object> inProperties, Map<String, Object> outProperties,
-                                    boolean newElement, boolean removedElement) {
+                                    ElementState state) {
 
     public CompositionElementDto(UUID compositionId, ToscaConceptIdentifier elementDefinitionId,
                                  Map<String, Object> inProperties, Map<String, Object> outProperties) {
-        this(compositionId, elementDefinitionId, inProperties, outProperties, false, false);
+        this(compositionId, elementDefinitionId, inProperties, outProperties, ElementState.PRESENT);
 
     }
 }
diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/ElementState.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/ElementState.java
new file mode 100644 (file)
index 0000000..afb01d0
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * 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;
+
+public enum ElementState {
+    PRESENT,
+    NOT_PRESENT,
+    REMOVED,
+    NEW
+}
index 65a83e2..b4fdefb 100644 (file)
@@ -26,10 +26,10 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 
 public record InstanceElementDto(UUID instanceId, UUID elementId, ToscaServiceTemplate toscaServiceTemplateFragment,
                                  Map<String, Object> inProperties, Map<String, Object> outProperties,
-                                 boolean newElement, boolean removedElement) {
+                                 ElementState state) {
 
     public InstanceElementDto(UUID instanceId, UUID elementId, ToscaServiceTemplate toscaServiceTemplateFragment,
                               Map<String, Object> inProperties, Map<String, Object> outProperties) {
-        this(instanceId, elementId, toscaServiceTemplateFragment, inProperties, outProperties, false, false);
+        this(instanceId, elementId, toscaServiceTemplateFragment, inProperties, outProperties, ElementState.PRESENT);
     }
 }
index e1bd064..233b559 100644 (file)
@@ -59,7 +59,8 @@ public class ParticipantIntermediaryApiImpl implements ParticipantIntermediaryAp
     @Override
     public void updateAutomationCompositionElementStage(UUID instance, UUID elementId,
             StateChangeResult stateChangeResult, int stage, String message) {
-        //
+        automationCompositionHandler.updateAutomationCompositionElementStage(instance, elementId, stateChangeResult,
+            stage, message);
     }
 
     @Override
index 3044080..1dbf2c9 100644 (file)
 package org.onap.policy.clamp.acm.participant.intermediary.handler;
 
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 import lombok.RequiredArgsConstructor;
+import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
+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.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.SubState;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare;
 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
@@ -77,24 +82,64 @@ public class AcSubStateHandler {
         var acElementList = automationComposition.getElements();
         for (var acElement : acElements) {
             var element = acElementList.get(acElement.getId());
-            element.setSubState(SubState.MIGRATION_PRECHECKING);
+            if (element != null) {
+                element.setSubState(SubState.MIGRATION_PRECHECKING);
+            }
         }
         var acCopyMigrateTo = new AutomationComposition(automationComposition);
-        acElementList = acCopyMigrateTo.getElements();
+        var acElementCopyList = acCopyMigrateTo.getElements();
         for (var acElement : acElements) {
-            var element = acElementList.get(acElement.getId());
-            AcmUtils.recursiveMerge(element.getProperties(), acElement.getProperties());
-            element.setDefinition(acElement.getDefinition());
+            var element = acElementCopyList.get(acElement.getId());
+            if (element != null) {
+                AcmUtils.recursiveMerge(element.getProperties(), acElement.getProperties());
+                element.setDefinition(acElement.getDefinition());
+            } else {
+                element = CacheProvider.createAutomationCompositionElement(acElement);
+                element.setSubState(SubState.MIGRATION_PRECHECKING);
+                acElementCopyList.put(element.getId(), element);
+            }
         }
+        var toDelete = acElementCopyList.values().stream()
+                .filter(el -> !SubState.MIGRATION_PRECHECKING.equals(el.getSubState()))
+                .map(AutomationCompositionElement::getId)
+                .toList();
+        toDelete.forEach(acElementCopyList::remove);
 
         var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(acCopyMigrateTo,
             compositionTargetId);
         var instanceElementMigrateMap = cacheProvider.getInstanceElementDtoMap(acCopyMigrateTo);
 
         for (var acElement : acElements) {
-            listener.migratePrecheck(messageId, compositionElementMap.get(acElement.getId()),
-                compositionElementTargetMap.get(acElement.getId()),
-                instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
+            var compositionElement = compositionElementMap.get(acElement.getId());
+            var compositionElementTarget = compositionElementTargetMap.get(acElement.getId());
+            var instanceElement = instanceElementMap.get(acElement.getId());
+            var instanceElementMigrate = instanceElementMigrateMap.get(acElement.getId());
+
+            if (instanceElement == null) {
+                // new element scenario
+                compositionElement = new CompositionElementDto(automationComposition.getCompositionId(),
+                        acElement.getDefinition(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
+                instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), acElement.getId(),
+                        new ToscaServiceTemplate(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
+                compositionElementTarget = CacheProvider.changeStateToNew(compositionElementTarget);
+                instanceElementMigrate = CacheProvider.changeStateToNew(instanceElementMigrate);
+            }
+
+            listener.migratePrecheck(messageId, compositionElement, compositionElementTarget,
+                    instanceElement, instanceElementMigrate);
+        }
+
+        for (var elementId : toDelete) {
+            var compositionDtoTarget =
+                    new CompositionElementDto(compositionTargetId,
+                            automationComposition.getElements().get(elementId).getDefinition(),
+                            Map.of(), Map.of(), ElementState.REMOVED);
+            var instanceDtoTarget =
+                    new InstanceElementDto(automationComposition.getInstanceId(), elementId,
+                            null, Map.of(), Map.of(), ElementState.REMOVED);
+
+            listener.migratePrecheck(messageId, compositionElementMap.get(elementId), compositionDtoTarget,
+                    instanceElementMap.get(elementId), instanceDtoTarget);
         }
     }
 
index 6c560e7..6d94efb 100644 (file)
 
 package org.onap.policy.clamp.acm.participant.intermediary.handler;
 
-
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
+import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
+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.comm.ParticipantMessagePublisher;
 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
@@ -47,7 +47,6 @@ import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMe
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.PropertiesUpdate;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
-import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
@@ -124,8 +123,7 @@ public class AutomationCompositionHandler {
                         updateMsg.getAutomationCompositionId());
                 automationComposition.setDeployState(DeployState.UPDATING);
                 var acCopy = new AutomationComposition(automationComposition);
-                updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy,
-                        DeployState.UPDATING);
+                updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy);
 
                 callParticipantUpdateProperty(updateMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy);
             }
@@ -188,56 +186,56 @@ public class AutomationCompositionHandler {
         }
     }
 
-    private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy,
-        DeployState deployState) {
-        var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
+    private void migrateExistingElementsOnThisParticipant(UUID instanceId, UUID compositionTargetId,
+        ParticipantDeploy participantDeploy, int stage) {
+        var automationComposition = cacheProvider.getAutomationComposition(instanceId);
+        var acElementList = automationComposition.getElements();
         for (var element : participantDeploy.getAcElementList()) {
-            var acElement = acElementList.get(element.getId());
-            if (acElement == null && deployState.equals(DeployState.MIGRATING)) {
-                var newElement = new AutomationCompositionElement();
-                newElement.setId(element.getId());
-                newElement.setParticipantId(participantDeploy.getParticipantId());
-                newElement.setDefinition(element.getDefinition());
-                newElement.setDeployState(deployState);
-                newElement.setSubState(SubState.NONE);
-                newElement.setLockState(LockState.LOCKED);
-                newElement.setProperties(element.getProperties());
+            var compositionInProperties =
+                    cacheProvider.getCommonProperties(compositionTargetId, element.getDefinition());
+            var stageSet = ParticipantUtils.findStageSet(compositionInProperties);
+            if (stageSet.contains(stage)) {
+                var acElement = acElementList.get(element.getId());
+                if (acElement == null) {
+                    var newElement = CacheProvider.createAutomationCompositionElement(element);
+                    newElement.setParticipantId(participantDeploy.getParticipantId());
+                    newElement.setDeployState(DeployState.MIGRATING);
+                    newElement.setLockState(LockState.LOCKED);
+                    newElement.setStage(stage);
 
-                acElementList.put(element.getId(), newElement);
-                LOGGER.info("New Ac Element with id {} is added in Migration", element.getId());
-            } else if (acElement != null) {
-                AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
-                acElement.setDeployState(deployState);
-                acElement.setSubState(SubState.NONE);
-                acElement.setDefinition(element.getDefinition());
+                    acElementList.put(element.getId(), newElement);
+                    LOGGER.info("New Ac Element with id {} is added in Migration", element.getId());
+                } else {
+                    AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
+                    acElement.setDeployState(DeployState.MIGRATING);
+                    acElement.setStage(stage);
+                    acElement.setDefinition(element.getDefinition());
+                }
             }
         }
-        if (deployState.equals(DeployState.MIGRATING)) {
-            // Check for missing elements and remove them from cache
-            List<UUID> elementsToRemove = findElementsToRemove(participantDeploy.getAcElementList(), acElementList);
-            for (UUID key : elementsToRemove) {
-                acElementList.remove(key);
-                LOGGER.info("Element with id {} is removed in Migration", key);
-            }
+        // Check for missing elements and remove them from cache
+        var elementsToRemove = findElementsToRemove(participantDeploy.getAcElementList(), acElementList);
+        for (var key : elementsToRemove) {
+            acElementList.remove(key);
+            LOGGER.info("Element with id {} is removed in Migration", key);
+        }
+    }
+
+    private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy) {
+        var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
+        for (var element : participantDeploy.getAcElementList()) {
+            var acElement = acElementList.get(element.getId());
+            AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
+            acElement.setDeployState(DeployState.UPDATING);
+            acElement.setSubState(SubState.NONE);
+            acElement.setDefinition(element.getDefinition());
         }
     }
 
     private List<UUID> findElementsToRemove(List<AcElementDeploy> acElementDeployList, Map<UUID,
             AutomationCompositionElement> acElementList) {
-        List<UUID> elementsToRemove = new ArrayList<>();
-        for (var elementInCache : acElementList.entrySet()) {
-            boolean found = false;
-            for (var element : acElementDeployList) {
-                if (element.getId().equals(elementInCache.getValue().getId())) {
-                    found = true;
-                    break;
-                }
-            }
-            if (!found) {
-                elementsToRemove.add(elementInCache.getKey());
-            }
-        }
-        return elementsToRemove;
+        var acElementDeploySet = acElementDeployList.stream().map(AcElementDeploy::getId).collect(Collectors.toSet());
+        return acElementList.keySet().stream().filter(id -> !acElementDeploySet.contains(id)).toList();
     }
 
     /**
@@ -311,17 +309,17 @@ public class AutomationCompositionHandler {
         for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
 
-                updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
-                    DeployState.MIGRATING);
+                migrateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(),
+                        migrationMsg.getCompositionTargetId(), participantDeploy, migrationMsg.getStage());
 
                 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
-                    acCopy, migrationMsg.getCompositionTargetId());
+                    acCopy, migrationMsg.getCompositionTargetId(), migrationMsg.getStage());
             }
         }
     }
 
     private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
-            AutomationComposition acCopy, UUID compositionTargetId) {
+            AutomationComposition acCopy, UUID compositionTargetId, int stage) {
         var compositionElementMap = cacheProvider.getCompositionElementDtoMap(acCopy);
         var instanceElementMap = cacheProvider.getInstanceElementDtoMap(acCopy);
         var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
@@ -331,32 +329,46 @@ public class AutomationCompositionHandler {
 
         // Call migrate for newly added and updated elements
         for (var acElement : acElements) {
-            if (instanceElementMap.get(acElement.getId()) == null) {
-                var compositionDto = new CompositionElementDto(acElement.getId(), acElement.getDefinition(),
-                        Map.of(), Map.of(), true, false);
-                var instanceDto = new InstanceElementDto(acCopy.getInstanceId(), acElement.getId(),
-                        new ToscaServiceTemplate(), Map.of(), Map.of(), true, false);
+            var compositionInProperties = cacheProvider
+                    .getCommonProperties(compositionTargetId, acElement.getDefinition());
+            var stageSet = ParticipantUtils.findStageSet(compositionInProperties);
+            if (stageSet.contains(stage)) {
+                if (instanceElementMap.get(acElement.getId()) == null) {
+                    var compositionElementDto =
+                            new CompositionElementDto(acCopy.getCompositionId(), acElement.getDefinition(),
+                                    Map.of(), Map.of(), ElementState.NOT_PRESENT);
+                    var instanceElementDto = new InstanceElementDto(acCopy.getInstanceId(), acElement.getId(),
+                            null, Map.of(), Map.of(), ElementState.NOT_PRESENT);
+                    var compositionElementTargetDto = CacheProvider.changeStateToNew(
+                            compositionElementTargetMap.get(acElement.getId()));
+                    var instanceElementMigrateDto = CacheProvider
+                            .changeStateToNew(instanceElementMigrateMap.get(acElement.getId()));
 
-                listener.migrate(messageId, compositionDto,
-                        compositionElementTargetMap.get(acElement.getId()),
-                        instanceDto, instanceElementMigrateMap.get(acElement.getId()));
-            } else {
-                listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
-                        compositionElementTargetMap.get(acElement.getId()),
-                        instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
+                    listener.migrate(messageId, compositionElementDto, compositionElementTargetDto, instanceElementDto,
+                            instanceElementMigrateDto, stage);
+                } else {
+                    listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
+                            compositionElementTargetMap.get(acElement.getId()),
+                            instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()),
+                            stage);
+                }
             }
         }
-        // Call migrate for removed elements
-        List<UUID> removedElements = findElementsToRemove(acElements, acCopy.getElements());
-        for (var elementId : removedElements) {
-            var compositionDtoTarget = new CompositionElementDto(elementId, acCopy.getElements().get(elementId)
-                    .getDefinition(), Map.of(), Map.of(), false, true);
-            var instanceDtoTarget = new InstanceElementDto(acCopy.getInstanceId(), elementId,
-                    new ToscaServiceTemplate(), Map.of(), Map.of(), false, true);
+        if (stage == 0) {
+            // Call migrate for removed elements
+            List<UUID> removedElements = findElementsToRemove(acElements, acCopy.getElements());
+            for (var elementId : removedElements) {
+                var compositionDtoTarget =
+                        new CompositionElementDto(compositionTargetId,
+                                acCopy.getElements().get(elementId).getDefinition(),
+                                Map.of(), Map.of(), ElementState.REMOVED);
+                var instanceDtoTarget =
+                        new InstanceElementDto(acCopy.getInstanceId(), elementId, null, Map.of(),
+                                Map.of(), ElementState.REMOVED);
 
-            listener.migrate(messageId, compositionElementMap.get(elementId),
-                    compositionDtoTarget,
-                    instanceElementMap.get(elementId), instanceDtoTarget);
+                listener.migrate(messageId, compositionElementMap.get(elementId), compositionDtoTarget,
+                        instanceElementMap.get(elementId), instanceDtoTarget, 0);
+            }
         }
     }
 }
index 06b11bb..77bcb19 100644 (file)
@@ -55,28 +55,78 @@ public class AutomationCompositionOutHandler {
     private final ParticipantMessagePublisher publisher;
     private final CacheProvider cacheProvider;
 
+    /**
+     * Handle a automation composition element stage change message.
+     *
+     * @param instance the automationComposition Id
+     * @param elementId the automationComposition Element Id
+     * @param stage the next stage
+     * @param message the message
+     * @param stateChangeResult the indicator if error occurs
+     */
+    public void updateAutomationCompositionElementStage(UUID instance, UUID elementId,
+        StateChangeResult stateChangeResult, int stage, String message) {
+
+        if (instance == null || elementId == null) {
+            LOGGER.error("Cannot update Automation composition element stage, id is null");
+            return;
+        }
+
+        var automationComposition = cacheProvider.getAutomationComposition(instance);
+        if (automationComposition == null) {
+            LOGGER.error("Cannot update Automation composition element stage, Automation composition id {} not present",
+                instance);
+            return;
+        }
+
+        var element = automationComposition.getElements().get(elementId);
+        if (element == null) {
+            var msg = "Cannot update Automation composition element stage, AC Element id {} not present";
+            LOGGER.error(msg, elementId);
+            return;
+        }
+
+        element.setRestarting(null);
+
+        var automationCompositionStateChangeAck =
+            new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
+        automationCompositionStateChangeAck.setParticipantId(cacheProvider.getParticipantId());
+        automationCompositionStateChangeAck.setMessage(message);
+        automationCompositionStateChangeAck.setResponseTo(cacheProvider.getMsgIdentification().get(element.getId()));
+        automationCompositionStateChangeAck.setStateChangeResult(stateChangeResult);
+        automationCompositionStateChangeAck.setStage(stage);
+        automationCompositionStateChangeAck.setAutomationCompositionId(instance);
+        automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(element.getId(),
+            new AcElementDeployAck(element.getDeployState(), element.getLockState(), element.getOperationalState(),
+                element.getUseState(), element.getOutProperties(), true, message));
+        LOGGER.debug("Automation composition element {} stage changed to {}", elementId, stage);
+        automationCompositionStateChangeAck.setResult(true);
+        publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
+        cacheProvider.getMsgIdentification().remove(element.getId());
+    }
+
     /**
      * Handle a automation composition element state change message.
      *
-     * @param automationCompositionId the automationComposition Id
+     * @param instance the automationComposition Id
      * @param elementId the automationComposition Element Id
      * @param deployState the DeployState state
      * @param lockState the LockState state
      * @param message the message
      * @param stateChangeResult the indicator if error occurs
      */
-    public void updateAutomationCompositionElementState(UUID automationCompositionId, UUID elementId,
+    public void updateAutomationCompositionElementState(UUID instance, UUID elementId,
             DeployState deployState, LockState lockState, StateChangeResult stateChangeResult, String message) {
 
-        if (automationCompositionId == null || elementId == null) {
+        if (instance == null || elementId == null) {
             LOGGER.error("Cannot update Automation composition element state, id is null");
             return;
         }
 
-        var automationComposition = cacheProvider.getAutomationComposition(automationCompositionId);
+        var automationComposition = cacheProvider.getAutomationComposition(instance);
         if (automationComposition == null) {
             LOGGER.error("Cannot update Automation composition element state, Automation composition id {} not present",
-                    automationCompositionId);
+                instance);
             return;
         }
 
@@ -114,7 +164,7 @@ public class AutomationCompositionOutHandler {
         automationCompositionStateChangeAck.setMessage(message);
         automationCompositionStateChangeAck.setResponseTo(cacheProvider.getMsgIdentification().get(element.getId()));
         automationCompositionStateChangeAck.setStateChangeResult(stateChangeResult);
-        automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
+        automationCompositionStateChangeAck.setAutomationCompositionId(instance);
         automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(element.getId(),
                 new AcElementDeployAck(element.getDeployState(), element.getLockState(), element.getOperationalState(),
                         element.getUseState(), element.getOutProperties(), true, message));
index f56fabf..7aa06ba 100644 (file)
@@ -30,8 +30,10 @@ import lombok.Getter;
 import lombok.NonNull;
 import lombok.Setter;
 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
+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.parameters.ParticipantParameters;
+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.AutomationCompositionElementDefinition;
@@ -181,14 +183,10 @@ public class CacheProvider {
         var acLast = automationCompositions.get(instanceId);
         Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
         for (var element : participantDeploy.getAcElementList()) {
-            var acElement = new AutomationCompositionElement();
-            acElement.setId(element.getId());
+            var acElement = createAutomationCompositionElement(element);
             acElement.setParticipantId(getParticipantId());
-            acElement.setDefinition(element.getDefinition());
             acElement.setDeployState(deployState);
-            acElement.setLockState(LockState.NONE);
             acElement.setSubState(subState);
-            acElement.setProperties(element.getProperties());
             var acElementLast = acLast != null ? acLast.getElements().get(element.getId()) : null;
             if (acElementLast != null) {
                 acElement.setOutProperties(acElementLast.getOutProperties());
@@ -248,6 +246,22 @@ public class CacheProvider {
         automationCompositions.put(automationComposition.getInstanceId(), automationComposition);
     }
 
+    /**
+     * Create AutomationCompositionElement to save in memory.
+     *
+     * @param element AcElementDeploy
+     * @return a new AutomationCompositionElement
+     */
+    public static AutomationCompositionElement createAutomationCompositionElement(AcElementDeploy element) {
+        var acElement = new AutomationCompositionElement();
+        acElement.setId(element.getId());
+        acElement.setDefinition(element.getDefinition());
+        acElement.setProperties(element.getProperties());
+        acElement.setSubState(SubState.NONE);
+        acElement.setLockState(LockState.LOCKED);
+        return acElement;
+    }
+
     /**
      * Create CompositionElementDto.
      *
@@ -302,4 +316,27 @@ public class CacheProvider {
         }
         return map;
     }
+
+    /**
+     * Create a new InstanceElementDto record with state New.
+     *
+     * @param instanceElement the InstanceElementDto
+     * @return a new InstanceElementDto
+     */
+    public static InstanceElementDto changeStateToNew(InstanceElementDto instanceElement) {
+        return new InstanceElementDto(instanceElement.instanceId(), instanceElement.elementId(),
+                instanceElement.toscaServiceTemplateFragment(),
+                instanceElement.inProperties(), instanceElement.outProperties(), ElementState.NEW);
+    }
+
+    /**
+     * Create a new CompositionElementDto record with state New.
+     *
+     * @param compositionElement the CompositionElementDto
+     * @return a new CompositionElementDto
+     */
+    public static CompositionElementDto changeStateToNew(CompositionElementDto compositionElement) {
+        return new CompositionElementDto(compositionElement.compositionId(), compositionElement.elementDefinitionId(),
+                compositionElement.inProperties(), compositionElement.outProperties(), ElementState.NEW);
+    }
 }
index 24b7f3a..9a43bf4 100644 (file)
@@ -53,7 +53,7 @@ public class ThreadHandler implements Closeable {
     private final ParticipantIntermediaryApi intermediaryApi;
     private final CacheProvider cacheProvider;
 
-    private final Map<UUID, Future> executionMap = new ConcurrentHashMap<>();
+    private final Map<UUID, Future<?>> executionMap = new ConcurrentHashMap<>();
 
     private final ExecutorService executor =
             Context.taskWrapping(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
@@ -295,21 +295,24 @@ public class ThreadHandler implements Closeable {
      * @param compositionElementTarget the information of the Automation Composition Definition Element Target
      * @param instanceElement the information of the Automation Composition Instance Element
      * @param instanceElementMigrate the information of the Automation Composition Instance Element updated
+     * @param stage the stage
      */
     public void migrate(UUID messageId, CompositionElementDto compositionElement,
         CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement,
-        InstanceElementDto instanceElementMigrate) {
+        InstanceElementDto instanceElementMigrate, int stage) {
         cleanExecution(instanceElement.elementId(), messageId);
         var result = executor.submit(() ->
-            this.migrateProcess(compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate));
+            this.migrateProcess(compositionElement, compositionElementTarget,
+                instanceElement, instanceElementMigrate, stage));
         executionMap.put(instanceElement.elementId(), result);
     }
 
     private void migrateProcess(CompositionElementDto compositionElement,
         CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement,
-        InstanceElementDto instanceElementMigrate) {
+        InstanceElementDto instanceElementMigrate, int stage) {
         try {
-            listener.migrate(compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate, 0);
+            listener.migrate(compositionElement, compositionElementTarget,
+                instanceElement, instanceElementMigrate, stage);
         } catch (PfModelException e) {
             LOGGER.error("Automation composition element migrate failed {} {}",
                 instanceElement.elementId(), e.getMessage());
index be8275c..a6cb7b0 100644 (file)
@@ -134,7 +134,7 @@ class AcElementListenerV2Test {
         var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
             Map.of(), Map.of());
         var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(), null, Map.of(), Map.of());
-        acElementListenerV2.migrate(compositionElement, compositionElement, instanceElement, instanceElement);
+        acElementListenerV2.migrate(compositionElement, compositionElement, instanceElement, instanceElement, 0);
         verify(intermediaryApi).updateAutomationCompositionElementState(instanceElement.instanceId(),
             instanceElement.elementId(), DeployState.DEPLOYED, null,
             StateChangeResult.NO_ERROR, "Migrated");
index 293a441..8fad1d2 100644 (file)
@@ -28,11 +28,13 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import org.junit.jupiter.api.Test;
 import org.onap.policy.clamp.acm.participant.intermediary.main.parameters.CommonTestData;
 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.AutomationCompositionElementDefinition;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
@@ -64,37 +66,70 @@ class AcSubStateHandlerTest {
 
     @Test
     void handleAcMigrationPrecheckTest() {
-        var listener = mock(ThreadHandler.class);
-        var cacheProvider = mock(CacheProvider.class);
-        var ach = new AcSubStateHandler(cacheProvider, listener);
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        automationComposition.setCompositionId(UUID.randomUUID());
+        automationComposition.setInstanceId(UUID.randomUUID());
+        automationComposition.setCompositionTargetId(UUID.randomUUID());
+        var cacheProvider = new CacheProvider(CommonTestData.getParticipantParameters());
+        var definitions =
+                CommonTestData.createAutomationCompositionElementDefinitionList(automationComposition);
+        cacheProvider.addElementDefinition(automationComposition.getCompositionId(), definitions);
+        cacheProvider.addElementDefinition(automationComposition.getCompositionTargetId(), definitions);
+        var participantDeploy =
+                CommonTestData.createparticipantDeploy(cacheProvider.getParticipantId(), automationComposition);
+        cacheProvider.initializeAutomationComposition(automationComposition.getCompositionId(),
+                automationComposition.getInstanceId(), participantDeploy);
         var migrationMsg = new AutomationCompositionMigration();
+        migrationMsg.setStage(0);
+        migrationMsg.setCompositionId(automationComposition.getCompositionId());
+        migrationMsg.setAutomationCompositionId(automationComposition.getInstanceId());
+        migrationMsg.setCompositionTargetId(automationComposition.getCompositionTargetId());
+        migrationMsg.setParticipantUpdatesList(List.of(participantDeploy));
         migrationMsg.setPrecheck(true);
-        assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(migrationMsg));
+        var listener = mock(ThreadHandler.class);
+        var ach = new AcSubStateHandler(cacheProvider, listener);
+        ach.handleAcMigrationPrecheck(migrationMsg);
+        verify(listener, times(automationComposition.getElements().size()))
+                .migratePrecheck(any(), any(), any(), any(), any());
+    }
+
+    @Test
+    void handleAcMigrationPrecheckAddRemoveTest() {
         var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
-        migrationMsg.setCompositionTargetId(UUID.randomUUID());
-        migrationMsg.setAutomationCompositionId(automationComposition.getInstanceId());
-        assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(migrationMsg));
-        when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
-                .thenReturn(automationComposition);
-        var participantDeploy = new ParticipantDeploy();
-        participantDeploy.setParticipantId(CommonTestData.getParticipantId());
-        when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
-        migrationMsg.getParticipantUpdatesList().add(participantDeploy);
-        Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>();
-        for (var element : automationComposition.getElements().values()) {
-            var acElementDeploy = new AcElementDeploy();
-            acElementDeploy.setProperties(Map.of());
-            acElementDeploy.setId(element.getId());
-            acElementDeploy.setDefinition(element.getDefinition());
-            participantDeploy.getAcElementList().add(acElementDeploy);
-            map.put(element.getDefinition(), new AutomationCompositionElementDefinition());
-        }
-        when(cacheProvider.getAcElementsDefinitions())
-                .thenReturn(Map.of(automationComposition.getCompositionId(), map,
-                        migrationMsg.getCompositionTargetId(), map));
+        automationComposition.setCompositionId(UUID.randomUUID());
+        automationComposition.setInstanceId(UUID.randomUUID());
+        var cacheProvider = new CacheProvider(CommonTestData.getParticipantParameters());
+        var definitions =
+                CommonTestData.createAutomationCompositionElementDefinitionList(automationComposition);
+        cacheProvider.addElementDefinition(automationComposition.getCompositionId(), definitions);
+        var participantDeploy =
+                CommonTestData.createparticipantDeploy(cacheProvider.getParticipantId(), automationComposition);
+        cacheProvider.initializeAutomationComposition(automationComposition.getCompositionId(),
+                automationComposition.getInstanceId(), participantDeploy);
+
+        var acMigrate = new AutomationComposition(automationComposition);
+        acMigrate.setCompositionTargetId(UUID.randomUUID());
+
+        // replacing first element with new one
+        var element = acMigrate.getElements().values().iterator().next();
+        element.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.0.0"));
+        element.setId(UUID.randomUUID());
+
+        var migrateDefinitions =
+                CommonTestData.createAutomationCompositionElementDefinitionList(acMigrate);
+        cacheProvider.addElementDefinition(acMigrate.getCompositionTargetId(), migrateDefinitions);
 
+        var migrationMsg = new AutomationCompositionMigration();
+        migrationMsg.setStage(0);
+        migrationMsg.setCompositionId(acMigrate.getCompositionId());
+        migrationMsg.setAutomationCompositionId(acMigrate.getInstanceId());
+        migrationMsg.setCompositionTargetId(acMigrate.getCompositionTargetId());
+        var participantMigrate = CommonTestData.createparticipantDeploy(cacheProvider.getParticipantId(), acMigrate);
+        migrationMsg.setParticipantUpdatesList(List.of(participantMigrate));
+        var listener = mock(ThreadHandler.class);
+        var ach = new AcSubStateHandler(cacheProvider, listener);
         ach.handleAcMigrationPrecheck(migrationMsg);
-        verify(listener, times(automationComposition.getElements().size()))
+        verify(listener, times(acMigrate.getElements().size() + 1))
                 .migratePrecheck(any(), any(), any(), any(), any());
     }
 
index 5973508..7f21264 100644 (file)
@@ -23,12 +23,14 @@ package org.onap.policy.clamp.acm.participant.intermediary.handler;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 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;
 import static org.mockito.Mockito.when;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import org.junit.jupiter.api.Test;
@@ -197,57 +199,82 @@ class AutomationCompositionHandlerTest {
     }
 
     @Test
-    void handleAutomationCompositionMigrationTest() {
-        var listener = mock(ThreadHandler.class);
-        var cacheProvider = mock(CacheProvider.class);
-        var ach = new AutomationCompositionHandler(cacheProvider, mock(ParticipantMessagePublisher.class), listener);
+    void handleMigrationNullTest() {
+        var ach = new AutomationCompositionHandler(
+                mock(CacheProvider.class), mock(ParticipantMessagePublisher.class), mock(ThreadHandler.class));
         var migrationMsg = new AutomationCompositionMigration();
+        migrationMsg.setStage(0);
         assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(migrationMsg));
-        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
-
+        migrationMsg.setAutomationCompositionId(UUID.randomUUID());
+        migrationMsg.setCompositionTargetId(UUID.randomUUID());
         assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(migrationMsg));
-        when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
-                .thenReturn(automationComposition);
-        when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
-
-        Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>();
-        var participantDeploy = new ParticipantDeploy();
-        populateMigrationMsg(automationComposition, migrationMsg, map, participantDeploy);
-        when(cacheProvider.getAcElementsDefinitions())
-                .thenReturn(Map.of(automationComposition.getCompositionId(), map,
-                        migrationMsg.getCompositionTargetId(), map));
+    }
 
+    @Test
+    void handleAutomationCompositionMigrationTest() {
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        automationComposition.setCompositionId(UUID.randomUUID());
+        automationComposition.setInstanceId(UUID.randomUUID());
+        automationComposition.setCompositionTargetId(UUID.randomUUID());
+        var cacheProvider = new CacheProvider(CommonTestData.getParticipantParameters());
+        var definitions =
+                CommonTestData.createAutomationCompositionElementDefinitionList(automationComposition);
+        cacheProvider.addElementDefinition(automationComposition.getCompositionId(), definitions);
+        cacheProvider.addElementDefinition(automationComposition.getCompositionTargetId(), definitions);
+        var participantDeploy =
+                CommonTestData.createparticipantDeploy(cacheProvider.getParticipantId(), automationComposition);
+        cacheProvider.initializeAutomationComposition(automationComposition.getCompositionId(),
+                automationComposition.getInstanceId(), participantDeploy);
+        var migrationMsg = new AutomationCompositionMigration();
+        migrationMsg.setStage(0);
+        migrationMsg.setCompositionId(automationComposition.getCompositionId());
+        migrationMsg.setAutomationCompositionId(automationComposition.getInstanceId());
+        migrationMsg.setCompositionTargetId(automationComposition.getCompositionTargetId());
+        migrationMsg.setParticipantUpdatesList(List.of(participantDeploy));
+        var listener = mock(ThreadHandler.class);
+        var ach = new AutomationCompositionHandler(cacheProvider, mock(ParticipantMessagePublisher.class), listener);
         ach.handleAutomationCompositionMigration(migrationMsg);
-        verify(listener, times(automationComposition.getElements().size() + 1))
-                .migrate(any(), any(), any(), any(), any());
+        verify(listener, times(automationComposition.getElements().size()))
+                .migrate(any(), any(), any(), any(), any(), anyInt());
     }
 
-    private void populateMigrationMsg(AutomationComposition automationComposition,
-                                      AutomationCompositionMigration migrationMsg,
-                                      Map<ToscaConceptIdentifier,
-                                              AutomationCompositionElementDefinition> map,
-                                      ParticipantDeploy participantDeploy) {
+    @Test
+    void handleMigrationAddRemoveTest() {
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        automationComposition.setCompositionId(UUID.randomUUID());
+        automationComposition.setInstanceId(UUID.randomUUID());
+        var cacheProvider = new CacheProvider(CommonTestData.getParticipantParameters());
+        var definitions =
+                CommonTestData.createAutomationCompositionElementDefinitionList(automationComposition);
+        cacheProvider.addElementDefinition(automationComposition.getCompositionId(), definitions);
+        var participantDeploy =
+                CommonTestData.createparticipantDeploy(cacheProvider.getParticipantId(), automationComposition);
+        cacheProvider.initializeAutomationComposition(automationComposition.getCompositionId(),
+                automationComposition.getInstanceId(), participantDeploy);
 
-        participantDeploy.setParticipantId(CommonTestData.getParticipantId());
-        migrationMsg.setCompositionTargetId(UUID.randomUUID());
-        migrationMsg.setAutomationCompositionId(automationComposition.getInstanceId());
-        migrationMsg.getParticipantUpdatesList().add(participantDeploy);
-        for (var element : automationComposition.getElements().values()) {
-            var acElementDeploy = new AcElementDeploy();
-            acElementDeploy.setProperties(Map.of());
-            acElementDeploy.setId(element.getId());
-            acElementDeploy.setDefinition(element.getDefinition());
-            participantDeploy.getAcElementList().add(acElementDeploy);
-            map.put(element.getDefinition(), new AutomationCompositionElementDefinition());
-        }
-        // remove an element
-        participantDeploy.getAcElementList().remove(0);
-        // Add a new element
-        var acElementDeploy = new AcElementDeploy();
-        acElementDeploy.setProperties(Map.of());
-        acElementDeploy.setId(UUID.randomUUID());
-        acElementDeploy.setDefinition(new ToscaConceptIdentifier("1.2.3", "policy.clamp.new.element"));
-        participantDeploy.getAcElementList().add(acElementDeploy);
+        var acMigrate = new AutomationComposition(automationComposition);
+        acMigrate.setCompositionTargetId(UUID.randomUUID());
+
+        // replacing first element with new one
+        var element = acMigrate.getElements().values().iterator().next();
+        element.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.0.0"));
+        element.setId(UUID.randomUUID());
 
+        var migrateDefinitions =
+                CommonTestData.createAutomationCompositionElementDefinitionList(acMigrate);
+        cacheProvider.addElementDefinition(acMigrate.getCompositionTargetId(), migrateDefinitions);
+
+        var migrationMsg = new AutomationCompositionMigration();
+        migrationMsg.setStage(0);
+        migrationMsg.setCompositionId(acMigrate.getCompositionId());
+        migrationMsg.setAutomationCompositionId(acMigrate.getInstanceId());
+        migrationMsg.setCompositionTargetId(acMigrate.getCompositionTargetId());
+        var participantMigrate = CommonTestData.createparticipantDeploy(cacheProvider.getParticipantId(), acMigrate);
+        migrationMsg.setParticipantUpdatesList(List.of(participantMigrate));
+        var listener = mock(ThreadHandler.class);
+        var ach = new AutomationCompositionHandler(cacheProvider, mock(ParticipantMessagePublisher.class), listener);
+        ach.handleAutomationCompositionMigration(migrationMsg);
+        verify(listener, times(acMigrate.getElements().size() + 1))
+                .migrate(any(), any(), any(), any(), any(), anyInt());
     }
 }
index 2781398..dd8747f 100644 (file)
@@ -65,11 +65,35 @@ class AutomationCompositionOutHandlerTest {
         when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
                 .thenReturn(automationComposition);
         assertDoesNotThrow(() -> acOutHandler.updateAutomationCompositionElementState(
-                automationComposition.getInstanceId(), UUID.randomUUID(), DeployState.DEPLOYED, null, null, null));
+                automationComposition.getInstanceId(), UUID.randomUUID(), DeployState.DEPLOYED,
+            null, null, null));
 
         var elementId = automationComposition.getElements().values().iterator().next().getId();
         assertDoesNotThrow(() -> acOutHandler.updateAutomationCompositionElementState(
                 automationComposition.getInstanceId(), elementId, null, null, null, null));
+
+        assertDoesNotThrow(() -> acOutHandler.updateAutomationCompositionElementStage(
+                elementId, null, null, 0, null));
+        assertDoesNotThrow(() -> acOutHandler.updateAutomationCompositionElementStage(
+                null, elementId, null, 0, null));
+        assertDoesNotThrow(() -> acOutHandler.updateAutomationCompositionElementStage(
+                UUID.randomUUID(), elementId, null, 0, null));
+        assertDoesNotThrow(() -> acOutHandler.updateAutomationCompositionElementStage(
+                automationComposition.getInstanceId(), UUID.randomUUID(), null, 0, null));
+    }
+
+    @Test
+    void updateAutomationCompositionElementStageTest() {
+        var publisher = mock(ParticipantMessagePublisher.class);
+        var cacheProvider = mock(CacheProvider.class);
+        var acOutHandler = new AutomationCompositionOutHandler(publisher, cacheProvider);
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
+                .thenReturn(automationComposition);
+        var elementId = automationComposition.getElements().values().iterator().next().getId();
+        acOutHandler.updateAutomationCompositionElementStage(
+                automationComposition.getInstanceId(), elementId, StateChangeResult.NO_ERROR, 0, "OK");
+        verify(publisher).sendAutomationCompositionAck(any(AutomationCompositionDeployAck.class));
     }
 
     @Test
index 90d011f..d054719 100644 (file)
@@ -117,7 +117,7 @@ class ThreadHandlerTest {
             var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
                 properties, properties);
             threadHandler.migrate(messageId, compositionElement, compositionElementTarget,
-                instanceElement, instanceElementUpdated);
+                instanceElement, instanceElementUpdated, 0);
             verify(listener, timeout(TIMEOUT)).migrate(compositionElement, compositionElementTarget,
                 instanceElement, instanceElementUpdated, 0);
 
@@ -170,7 +170,7 @@ class ThreadHandlerTest {
             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
                 .migrate(compositionElement, compositionElementTarget, instanceElement, instanceElementUpdated, 0);
             threadHandler.migrate(messageId, compositionElement, compositionElementTarget,
-                instanceElement, instanceElementUpdated);
+                instanceElement, instanceElementUpdated, 0);
             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
                 "Automation composition element migrate failed");
index 51b7b71..867148d 100644 (file)
@@ -306,8 +306,6 @@ public class AutomationCompositionInstantiationProvider {
         AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
             SubState.MIGRATION_PRECHECKING);
         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
-        // excluding removed element in MIGRATION_PRECHECKING
-        elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().get(uuid).setSubState(SubState.NONE));
 
         return createInstantiationResponse(automationCompositionProvider.updateAutomationComposition(acToBeUpdated));
     }
index 31cc8ba..d746b33 100644 (file)
@@ -145,17 +145,26 @@ public class SupervisionScanner {
             return;
         }
 
-        if (DeployState.UPDATING.equals(automationComposition.getDeployState())
-                || DeployState.MIGRATING.equals(automationComposition.getDeployState())
+        if (DeployState.MIGRATING.equals(automationComposition.getDeployState())) {
+            scanStage(automationComposition, serviceTemplate);
+        } else if (DeployState.UPDATING.equals(automationComposition.getDeployState())
+                || SubState.PREPARING.equals(automationComposition.getSubState())
+                || SubState.REVIEWING.equals(automationComposition.getSubState())
                 || SubState.MIGRATION_PRECHECKING.equals(automationComposition.getSubState())) {
-
-            scanMigrate(automationComposition, serviceTemplate);
+            simpleScan(automationComposition, serviceTemplate);
         } else {
-            scanDeploy(automationComposition, serviceTemplate);
+            scanWithPhase(automationComposition, serviceTemplate);
         }
     }
 
-    private void scanDeploy(final AutomationComposition automationComposition, ToscaServiceTemplate serviceTemplate) {
+    /**
+     * Scan with startPhase: DEPLOY, UNDEPLOY, LOCK and UNLOCK.
+     *
+     * @param automationComposition the AutomationComposition
+     * @param serviceTemplate the ToscaServiceTemplate
+     */
+    private void scanWithPhase(final AutomationComposition automationComposition,
+            ToscaServiceTemplate serviceTemplate) {
         var completed = true;
         var minSpNotCompleted = 1000; // min startPhase not completed
         var maxSpNotCompleted = 0; // max startPhase not completed
@@ -176,9 +185,6 @@ public class SupervisionScanner {
         }
 
         if (completed) {
-            LOGGER.debug("automation composition scan: transition state {} {} completed",
-                    automationComposition.getDeployState(), automationComposition.getLockState());
-
             complete(automationComposition, serviceTemplate);
         } else {
             LOGGER.debug("automation composition scan: transition state {} {} not completed",
@@ -197,7 +203,31 @@ public class SupervisionScanner {
         }
     }
 
-    private void scanMigrate(final AutomationComposition automationComposition, ToscaServiceTemplate serviceTemplate) {
+    /**
+     * Simple scan: UPDATE, PREPARE, REVIEW, MIGRATE_PRECHECKING.
+     *
+     * @param automationComposition the AutomationComposition
+     * @param serviceTemplate the ToscaServiceTemplate
+     */
+    private void simpleScan(final AutomationComposition automationComposition, ToscaServiceTemplate serviceTemplate) {
+        var completed = automationComposition.getElements().values().stream()
+                .filter(element -> AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState(),
+                        element.getSubState())).findFirst().isEmpty();
+
+        if (completed) {
+            complete(automationComposition, serviceTemplate);
+        } else {
+            handleTimeout(automationComposition);
+        }
+    }
+
+    /**
+     * Scan with stage: MIGRATE.
+     *
+     * @param automationComposition the AutomationComposition
+     * @param serviceTemplate the ToscaServiceTemplate
+     */
+    private void scanStage(final AutomationComposition automationComposition, ToscaServiceTemplate serviceTemplate) {
         var completed = true;
         var minStageNotCompleted = 1000; // min stage not completed
         for (var element : automationComposition.getElements().values()) {
@@ -214,16 +244,12 @@ public class SupervisionScanner {
         }
 
         if (completed) {
-            LOGGER.debug("automation composition scan: transition state {} {} ", automationComposition.getDeployState(),
-                    automationComposition.getLockState());
-
             complete(automationComposition, serviceTemplate);
         } else {
             LOGGER.debug("automation composition scan: transition from state {} to {} not completed",
                     automationComposition.getDeployState(), automationComposition.getLockState());
 
-            if (DeployState.MIGRATING.equals(automationComposition.getDeployState())
-                    && minStageNotCompleted != automationComposition.getPhase()) {
+            if (minStageNotCompleted != automationComposition.getPhase()) {
                 savePahese(automationComposition, minStageNotCompleted);
                 LOGGER.debug("retry message AutomationCompositionMigration");
                 automationCompositionMigrationPublisher.send(automationComposition, minStageNotCompleted);
@@ -235,6 +261,10 @@ public class SupervisionScanner {
 
     private void complete(final AutomationComposition automationComposition,
             ToscaServiceTemplate serviceTemplate) {
+        LOGGER.debug("automation composition scan: transition state {} {} {} completed",
+                automationComposition.getDeployState(), automationComposition.getLockState(),
+                automationComposition.getSubState());
+
         var deployState = automationComposition.getDeployState();
         if (DeployState.MIGRATING.equals(automationComposition.getDeployState())) {
             // migration scenario
@@ -274,6 +304,10 @@ public class SupervisionScanner {
     }
 
     private void handleTimeout(AutomationComposition automationComposition) {
+        LOGGER.debug("automation composition scan: transition from state {} to {} {} not completed",
+                automationComposition.getDeployState(), automationComposition.getLockState(),
+                automationComposition.getSubState());
+
         if (StateChangeResult.TIMEOUT.equals(automationComposition.getStateChangeResult())) {
             LOGGER.debug("The ac instance is in timeout {}", automationComposition.getInstanceId());
             return;
index f7ccfe6..1e3c890 100644 (file)
@@ -35,6 +35,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.UUID;
+import java.util.function.Consumer;
 import org.junit.jupiter.api.Test;
 import org.onap.policy.clamp.acm.runtime.instantiation.InstantiationUtils;
 import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionDeployPublisher;
@@ -49,6 +50,7 @@ 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.NodeTemplateState;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
 import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
@@ -57,6 +59,8 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 class SupervisionScannerTest {
 
     private static final String AC_JSON = "src/test/resources/rest/acm/AutomationCompositionSmoke.json";
+    private static final String ELEMENT_NAME =
+            "org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement";
 
     private static final UUID compositionId = UUID.randomUUID();
 
@@ -295,8 +299,7 @@ class SupervisionScannerTest {
         automationComposition.setPhase(0);
         automationComposition.setCompositionId(compositionId);
         for (var element : automationComposition.getElements().values()) {
-            if ("org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement"
-                    .equals(element.getDefinition().getName())) {
+            if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
                 element.setDeployState(DeployState.DEPLOYING);
                 element.setLockState(LockState.NONE);
             } else {
@@ -371,6 +374,93 @@ class SupervisionScannerTest {
         assertEquals(compositionTargetId, automationComposition.getCompositionId());
     }
 
+    @Test
+    void testSendAutomationCompositionUpdate() {
+        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+        automationComposition.setLockState(LockState.LOCKED);
+        automationComposition.setDeployState(DeployState.UPDATING);
+        for (var element : automationComposition.getElements().values()) {
+            element.setSubState(SubState.NONE);
+            element.setLockState(LockState.LOCKED);
+            if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+                element.setDeployState(DeployState.UPDATING);
+            } else {
+                element.setDeployState(DeployState.DEPLOYED);
+            }
+        }
+        testSimpleScan(automationComposition, element -> element.setDeployState(DeployState.DEPLOYED));
+    }
+
+    @Test
+    void testSendAutomationCompositionMigratingPrecheck() {
+        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+        automationComposition.setLockState(LockState.LOCKED);
+        automationComposition.setDeployState(DeployState.DEPLOYED);
+        automationComposition.setSubState(SubState.MIGRATION_PRECHECKING);
+        for (var element : automationComposition.getElements().values()) {
+            element.setDeployState(DeployState.DEPLOYED);
+            element.setSubState(SubState.NONE);
+            element.setLockState(LockState.LOCKED);
+            if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+                element.setSubState(SubState.MIGRATION_PRECHECKING);
+            }
+        }
+        testSimpleScan(automationComposition, element -> element.setSubState(SubState.NONE));
+    }
+
+    @Test
+    void testSendAutomationCompositionPrepare() {
+        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+        automationComposition.setLockState(LockState.NONE);
+        automationComposition.setDeployState(DeployState.UNDEPLOYED);
+        automationComposition.setSubState(SubState.PREPARING);
+        for (var element : automationComposition.getElements().values()) {
+            element.setDeployState(DeployState.UNDEPLOYED);
+            element.setSubState(SubState.NONE);
+            element.setLockState(LockState.NONE);
+            if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+                element.setSubState(SubState.PREPARING);
+            }
+        }
+        testSimpleScan(automationComposition, element -> element.setSubState(SubState.NONE));
+    }
+
+    @Test
+    void testSendAutomationCompositionReview() {
+        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+        automationComposition.setLockState(LockState.LOCKED);
+        automationComposition.setDeployState(DeployState.DEPLOYED);
+        automationComposition.setSubState(SubState.REVIEWING);
+        for (var element : automationComposition.getElements().values()) {
+            element.setDeployState(DeployState.DEPLOYED);
+            element.setSubState(SubState.NONE);
+            element.setLockState(LockState.LOCKED);
+            if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+                element.setSubState(SubState.REVIEWING);
+            }
+        }
+        testSimpleScan(automationComposition, element -> element.setSubState(SubState.NONE));
+    }
+
+    private void testSimpleScan(AutomationComposition automationComposition, Consumer<AutomationCompositionElement> c) {
+        automationComposition.setLockState(LockState.NONE);
+        automationComposition.setCompositionId(compositionId);
+        automationComposition.setLastMsg(TimestampHelper.now());
+        var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+        when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
+
+        var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+        var supervisionScanner = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
+                null, null,
+                mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
+        supervisionScanner.run();
+        verify(automationCompositionProvider, times(0)).updateAcState(any());
+
+        automationComposition.getElements().values().forEach(c);
+        supervisionScanner.run();
+        verify(automationCompositionProvider).updateAcState(any());
+    }
+
     @Test
     void testSendAutomationCompositionMsgUnlocking() {
         var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
@@ -379,8 +469,7 @@ class SupervisionScannerTest {
         automationComposition.setCompositionId(compositionId);
         automationComposition.setPhase(0);
         for (var element : automationComposition.getElements().values()) {
-            if ("org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement"
-                    .equals(element.getDefinition().getName())) {
+            if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
                 element.setDeployState(DeployState.DEPLOYED);
                 element.setLockState(LockState.UNLOCKING);
             } else {