Fix runtime and intermediary to support Migration with new participant 03/142303/2
authorrameshiyer27 <ramesh.murugan.iyer@est.tech>
Sat, 4 Oct 2025 13:48:24 +0000 (14:48 +0100)
committerrameshiyer27 <ramesh.murugan.iyer@est.tech>
Wed, 22 Oct 2025 10:04:45 +0000 (11:04 +0100)
   - Removed elements are retained in the migration state REMOVED until
     they are deleted by the participant.
   - New elements are retained in the migration state NEW
   - All the element states are reverted to DEFAULT after successful Migration
   - Validations are fixed to ignore the REMOVED elements present for the target composition
   - startPhase and stage values are updated as 0 for REMOVED elements.
   - startPhase for undeploy of NEW elements are set to 0
   - Intermediary is retaining the removed elements in cache.
   - commpon properties for NEW and REMOVED elements are fetched from their respective compositions in Intermediary.
   - INFO logging for message executor and cache provider for better visibility of the flow.

Issue-ID: POLICY-5463
Change-Id: I090c5053655263b8e5a2a3a46c8f2f887f9c9883
Signed-off-by: rameshiyer27 <ramesh.murugan.iyer@est.tech>
30 files changed:
models/src/main/java/org/onap/policy/clamp/models/acm/concepts/ParticipantUtils.java
models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java
models/src/test/java/org/onap/policy/clamp/models/acm/utils/AcmUtilsTest.java
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/SimulatorServiceTest.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcDefinitionHandler.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/MsgExecutor.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProvider.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/ThreadHandlerTest.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProviderTest.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/main/parameters/CommonTestData.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/AcElementPropertiesPublisher.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/comm/AutomationCompositionMigrationPublisher.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AbstractScanner.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.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/instantiation/InstantiationUtils.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/SimpleScannerTest.java

index ded715b..d7069e2 100644 (file)
@@ -87,12 +87,15 @@ public final class ParticipantUtils {
             ToscaServiceTemplate toscaServiceTemplate) {
         Set<Integer> minStage = new HashSet<>();
         for (var element : automationComposition.getElements().values()) {
-            var toscaNodeTemplate = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates()
-                .get(element.getDefinition().getName());
-            var stage = DeployState.MIGRATING.equals(automationComposition.getDeployState())
-                    ? ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties())
-                    : ParticipantUtils.findStageSetPrepare(toscaNodeTemplate.getProperties());
-            minStage.addAll(stage);
+            if (! MigrationState.REMOVED.equals(element.getMigrationState())) {
+                var toscaNodeTemplate = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates()
+                        .get(element.getDefinition().getName());
+                var stage = DeployState.MIGRATING.equals(automationComposition.getDeployState())
+                        ? ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties())
+                        : ParticipantUtils.findStageSetPrepare(toscaNodeTemplate.getProperties());
+                minStage.addAll(stage);
+            }
+
         }
         return minStage.stream().min(Integer::compare).orElse(0);
     }
index 987e6c7..c7bc598 100644 (file)
@@ -47,6 +47,7 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
 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.MigrationState;
 import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDefinition;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
@@ -198,7 +199,7 @@ public final class AcmUtils {
      * @return the result of validation
      */
     public static BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
-            ToscaServiceTemplate serviceTemplate, String toscaCompositionName) {
+            ToscaServiceTemplate serviceTemplate, String toscaCompositionName, boolean migrateOperation) {
         var result = new BeanValidationResult(ENTRY + automationComposition.getName(), automationComposition);
 
         var map = getMapToscaNodeTemplates(serviceTemplate);
@@ -221,13 +222,14 @@ public final class AcmUtils {
                     .collect(Collectors.toMap(ToscaConceptIdentifier::getName, UnaryOperator.identity()));
             // @formatter:on
 
-            if (definitions.size() != automationComposition.getElements().size()) {
+            if (definitions.size() != automationComposition.getElements().size()
+                    && !migrateOperation) {
                 result.setResult(ValidationStatus.INVALID,
-                        "Elements of the instance not matching with the elements of the composition");
+                            "Elements of the instance not matching with the elements of the composition");
             }
 
             for (var element : automationComposition.getElements().values()) {
-                result.addResult(validateDefinition(definitions, element.getDefinition()));
+                result.addResult(validateDefinition(definitions, element.getDefinition(), element.getMigrationState()));
             }
         }
 
@@ -236,12 +238,13 @@ public final class AcmUtils {
     }
 
     private static ValidationResult validateDefinition(Map<String, ToscaConceptIdentifier> definitions,
-            ToscaConceptIdentifier definition) {
+                                                       ToscaConceptIdentifier definition,
+                                                       MigrationState migrationState) {
         var result = new BeanValidationResult(ENTRY + definition.getName(), definition);
         var identifier = definitions.get(definition.getName());
-        if (identifier == null) {
+        if (identifier == null && MigrationState.DEFAULT.equals(migrationState)) {
             result.setResult(ValidationStatus.INVALID, "Not found");
-        } else if (!identifier.equals(definition)) {
+        } else if (! definition.equals(identifier) && MigrationState.DEFAULT.equals(migrationState)) {
             result.setResult(ValidationStatus.INVALID, "Version not matching");
         }
         return (result.isClean() ? null : result);
@@ -435,7 +438,7 @@ public final class AcmUtils {
      * @param deployOrder the DeployOrder
      */
     public static List<ParticipantDeploy> createParticipantDeployList(AutomationComposition automationComposition,
-            DeployOrder deployOrder, List<AutomationCompositionElement> removedElements) {
+            DeployOrder deployOrder) {
         Map<UUID, List<AcElementDeploy>> map = new HashMap<>();
         for (var element : automationComposition.getElements().values()) {
             var acElementDeploy = createAcElementDeploy(element, deployOrder);
@@ -449,16 +452,6 @@ public final class AcmUtils {
             participantDeploy.setAcElementList(entry.getValue());
             participantDeploys.add(participantDeploy);
         }
-        // Include the participantIds for the removed elements
-        for (var element : removedElements) {
-            if (map.get(element.getParticipantId()) == null) {
-                var participantDeploy = new ParticipantDeploy();
-                participantDeploy.setParticipantId(element.getParticipantId());
-                participantDeploys.add(participantDeploy);
-                map.put(element.getParticipantId(), new ArrayList<>());
-            }
-
-        }
         return participantDeploys;
     }
 
index ea5c850..0fe5bae 100644 (file)
@@ -41,7 +41,6 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition
 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.LockState;
-import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
 import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.document.concepts.DocToscaServiceTemplate;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
@@ -135,13 +134,13 @@ class AcmUtilsTest {
         var doc = new DocToscaServiceTemplate(CommonTestData.getToscaServiceTemplate(TOSCA_TEMPLATE_YAML));
         var automationComposition = CommonTestData.getJsonObject(AC_INSTANTIATION_JSON, AutomationComposition.class);
         var result = AcmUtils.validateAutomationComposition(automationComposition, doc.toAuthorative(),
-                AUTOMATION_COMPOSITION_NODE_TYPE);
+                AUTOMATION_COMPOSITION_NODE_TYPE, false);
         assertTrue(result.isValid());
 
         var element = automationComposition.getElements().values().iterator().next();
         automationComposition.getElements().remove(element.getId());
         result = AcmUtils.validateAutomationComposition(automationComposition, doc.toAuthorative(),
-                AUTOMATION_COMPOSITION_NODE_TYPE);
+                AUTOMATION_COMPOSITION_NODE_TYPE, false);
         assertFalse(result.isValid());
         assertThat(result.getMessage()).contains("not matching");
     }
@@ -151,7 +150,7 @@ class AcmUtilsTest {
         var automationComposition = getDummyAutomationComposition();
         var toscaServiceTemplate = getDummyToscaServiceTemplate();
         var result = AcmUtils.validateAutomationComposition(automationComposition,
-                toscaServiceTemplate, AUTOMATION_COMPOSITION_NODE_TYPE);
+                toscaServiceTemplate, AUTOMATION_COMPOSITION_NODE_TYPE, false);
         assertNotNull(result);
         assertFalse(result.isValid());
 
@@ -161,12 +160,12 @@ class AcmUtilsTest {
         nodeTemplates.put("org.onap.dcae.acm.DCAEMicroserviceAutomationCompositionParticipant", nodeTemplate);
         toscaServiceTemplate.getToscaTopologyTemplate().setNodeTemplates(nodeTemplates);
         result = AcmUtils.validateAutomationComposition(automationComposition, toscaServiceTemplate,
-                AUTOMATION_COMPOSITION_NODE_TYPE);
+                AUTOMATION_COMPOSITION_NODE_TYPE, false);
         assertFalse(result.isValid());
 
         var doc = new DocToscaServiceTemplate(CommonTestData.getToscaServiceTemplate(TOSCA_TEMPLATE_YAML));
         result = AcmUtils.validateAutomationComposition(automationComposition, doc.toAuthorative(),
-                AUTOMATION_COMPOSITION_NODE_TYPE);
+                AUTOMATION_COMPOSITION_NODE_TYPE, false);
         assertFalse(result.isValid());
     }
 
@@ -225,7 +224,7 @@ class AcmUtilsTest {
     @Test
     void testCreateAcElementDeployList() {
         var automationComposition = getDummyAutomationComposition();
-        var result = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.DEPLOY, List.of());
+        var result = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.DEPLOY);
         assertThat(result).hasSameSizeAs(automationComposition.getElements().values());
         for (var participantDeploy : result) {
             for (var element : participantDeploy.getAcElementList()) {
@@ -234,26 +233,6 @@ class AcmUtilsTest {
         }
     }
 
-    @Test
-    void testAcDeployListWithRemovedElements() {
-        var removedElement1 = CommonTestData.getJsonObject(
-                "src/test/resources/json/AutomationCompositionElementNoOrderedState.json",
-                AutomationCompositionElement.class);
-        var participantId1 = UUID.randomUUID();
-        var participantId2 = UUID.randomUUID();
-        assert removedElement1 != null;
-        removedElement1.setParticipantId(participantId1);
-        var removedElement2 = new AutomationCompositionElement(removedElement1);
-        removedElement2.setParticipantId(participantId2);
-
-        var automationComposition = getDummyAutomationComposition();
-        var result = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.DEPLOY,
-                List.of(removedElement1, removedElement2));
-        assertThat(result).hasSize(automationComposition.getElements().values().size() + 2);
-        var participantIds = result.stream().map(ParticipantDeploy::getParticipantId).toList();
-        assertThat(participantIds).containsAll(List.of(participantId1, participantId2));
-    }
-
     @Test
     void testCreateAcElementRestart() {
         var element = getDummyAutomationComposition().getElements().values().iterator().next();
index d4743cf..69ab0db 100644 (file)
@@ -296,7 +296,7 @@ public class SimulatorService {
                     DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Migration - Deleted");
         } else {
             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
-                    DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Migration - Delete failed!");
+                    DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration - Delete failed!");
         }
     }
 
index 29d0f76..49b0195 100644 (file)
@@ -184,7 +184,7 @@ class SimulatorServiceTest {
         simulatorService.getConfig().setMigrateSuccess(false);
         simulatorService.deleteInMigration(instanceId, elementId);
         verify(intermediaryApi).updateAutomationCompositionElementState(instanceId, elementId,
-                DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Migration - Delete failed!");
+                DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration - Delete failed!");
     }
 
 
index e0f8cd3..c4ebfbf 100644 (file)
@@ -151,7 +151,7 @@ public class AcDefinitionHandler {
     }
 
     private void checkComposition(ParticipantSync participantSyncMsg) {
-        // edge case scenario in migration whit remove/add elements,
+        // edge case scenario in migration with remove/add elements,
         // when composition or target composition doesn't contain elements from this participant
         for (var msg : cacheProvider.getMessagesOnHold().values()) {
             if (participantSyncMsg.getCompositionId().equals(msg.getCompositionTargetId())) {
index f00c87b..20b2a24 100644 (file)
@@ -59,16 +59,27 @@ public class AcSubStateHandler {
         if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
             return;
         }
-
+        var acTargetDefinition = cacheProvider.getAcElementsDefinitions().get(migrationMsg.getCompositionTargetId());
         var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
         if (automationComposition == null) {
-            LOGGER.debug("Automation composition {} does not use this participant",
+            if (acTargetDefinition == null) {
+                LOGGER.warn("Automation composition {} does not use this participant",
+                        migrationMsg.getAutomationCompositionId());
+                return;
+            }
+        } else {
+            LOGGER.info("Migration Precheck invoked on an existing participant for the instance {}",
                     migrationMsg.getAutomationCompositionId());
-            return;
         }
-        automationComposition.setSubState(SubState.MIGRATION_PRECHECKING);
         for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
+                if (automationComposition == null) { // New element with new participant
+                    automationComposition = cacheProvider.createAcInstance(migrationMsg.getCompositionId(),
+                            migrationMsg.getCompositionTargetId(), migrationMsg.getAutomationCompositionId(),
+                            participantDeploy, DeployState.MIGRATING, SubState.MIGRATION_PRECHECKING,
+                            migrationMsg.getRevisionIdInstance());
+                    LOGGER.info("New participant with new element type added for Migration Precheck");
+                }
 
                 callParticipantMigratePrecheck(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
                     automationComposition, migrationMsg.getCompositionTargetId());
@@ -153,7 +164,7 @@ public class AcSubStateHandler {
         if (acPrepareMsg.isPreDeploy()) {
             for (var participantPrepare : acPrepareMsg.getParticipantList()) {
                 if (cacheProvider.getParticipantId().equals(participantPrepare.getParticipantId())) {
-                    cacheProvider.initializeAutomationComposition(acPrepareMsg.getCompositionId(),
+                    cacheProvider.initializeAutomationComposition(acPrepareMsg.getCompositionId(), null,
                         acPrepareMsg.getAutomationCompositionId(), participantPrepare, DeployState.UNDEPLOYED,
                         SubState.PREPARING, acPrepareMsg.getRevisionIdInstance());
                     callParticipanPrepare(acPrepareMsg.getMessageId(), participantPrepare.getAcElementList(),
index 0b076e5..5b2f74f 100644 (file)
 
 package org.onap.policy.clamp.acm.participant.intermediary.handler;
 
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 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.acm.participant.intermediary.handler.cache.AcDefinition;
 import org.onap.policy.clamp.acm.participant.intermediary.handler.cache.CacheProvider;
 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.LockState;
+import org.onap.policy.clamp.models.acm.concepts.MigrationState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
@@ -59,6 +61,7 @@ import org.springframework.stereotype.Component;
 @RequiredArgsConstructor
 public class AutomationCompositionHandler {
     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
+    private static final String AC_NOT_USED = "Automation composition {} does not use this participant";
 
     private final CacheProvider cacheProvider;
     private final ParticipantMessagePublisher publisher;
@@ -85,8 +88,7 @@ public class AutomationCompositionHandler {
                 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
                 publisher.sendAutomationCompositionAck(automationCompositionAck);
             } else {
-                LOGGER.debug("Automation composition {} does not use this participant",
-                        stateChangeMsg.getAutomationCompositionId());
+                LOGGER.warn(AC_NOT_USED, stateChangeMsg.getAutomationCompositionId());
             }
             return;
         }
@@ -184,39 +186,57 @@ public class AutomationCompositionHandler {
         }
     }
 
-    private void migrateExistingElementsOnThisParticipant(UUID instanceId, UUID compositionTargetId,
-            ParticipantDeploy participantDeploy, int stage) {
-        var automationComposition = cacheProvider.getAutomationComposition(instanceId);
-        var acElementList = automationComposition.getElements();
+    private void migrateExistingElementsOnThisParticipant(AutomationComposition automationComposition,
+                                                          UUID compositionTargetId, ParticipantDeploy participantDeploy,
+                                                          int stage, boolean newParticipant) {
         for (var element : participantDeploy.getAcElementList()) {
+            UUID compIdForCommonProperties = null;
+            if (MigrationState.REMOVED.equals(element.getMigrationState())) {
+                compIdForCommonProperties = automationComposition.getCompositionId();
+            } else {
+                compIdForCommonProperties = compositionTargetId;
+            }
             var compositionInProperties =
-                    cacheProvider.getCommonProperties(compositionTargetId, element.getDefinition());
+                    cacheProvider.getCommonProperties(compIdForCommonProperties, element.getDefinition());
             var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties);
+            if (MigrationState.REMOVED.equals(element.getMigrationState())) {
+                stageSet = Set.of(0);
+            }
             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 {
-                    AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
-                    acElement.setDeployState(DeployState.MIGRATING);
-                    acElement.setStage(stage);
-                    acElement.setDefinition(element.getDefinition());
-                }
+                migrateElement(element, automationComposition, compositionTargetId, stage, newParticipant,
+                        participantDeploy);
             }
         }
-        // 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 migrateElement(AcElementDeploy element, AutomationComposition automationComposition,
+                                UUID compositionTargetId, int stage, boolean newParticipant,
+                                ParticipantDeploy participantDeploy) {
+        var acElementList = automationComposition.getElements();
+        automationComposition.setCompositionTargetId(compositionTargetId);
+        automationComposition.setDeployState(DeployState.MIGRATING);
+        var acElement = acElementList.get(element.getId());
+        if (acElement == null) {  // NEW element with existing participant
+            var newElement = CacheProvider.createAutomationCompositionElement(element);
+            newElement.setParticipantId(participantDeploy.getParticipantId());
+            newElement.setDeployState(DeployState.MIGRATING);
+            newElement.setLockState(LockState.LOCKED);
+            newElement.setStage(stage);
+            newElement.setMigrationState(MigrationState.NEW);
+
+            acElementList.put(element.getId(), newElement);
+            LOGGER.info("New Ac Element with id {} is added in Migration", element.getId());
+        } else {
+            acElement.setStage(stage);
+            acElement.setMigrationState(element.getMigrationState());
+            if (! newParticipant) { //DEFAULT element
+                AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
+                acElement.setDeployState(DeployState.MIGRATING);
+                acElement.setDefinition(element.getDefinition());
+            }
+            LOGGER.info("Cache updated for the migration of element with id {}", element.getId());
         }
+
     }
 
     private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy) {
@@ -230,12 +250,6 @@ public class AutomationCompositionHandler {
         }
     }
 
-    private List<UUID> findElementsToRemove(List<AcElementDeploy> acElementDeployList,
-            Map<UUID, AutomationCompositionElement> acElementList) {
-        var acElementDeploySet = acElementDeployList.stream().map(AcElementDeploy::getId).collect(Collectors.toSet());
-        return acElementList.keySet().stream().filter(id -> !acElementDeploySet.contains(id)).toList();
-    }
-
     /**
      * Method to handle when the new state from participant is UNINITIALISED state.
      *
@@ -245,16 +259,24 @@ public class AutomationCompositionHandler {
      */
     private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
             Integer startPhaseMsg) {
-        automationComposition.setCompositionTargetId(null);
         automationComposition.setDeployState(DeployState.UNDEPLOYING);
         for (var element : automationComposition.getElements().values()) {
-            var compositionInProperties = cacheProvider.getCommonProperties(automationComposition.getCompositionId(),
-                    element.getDefinition());
+            UUID compositionId = null;
+            if (MigrationState.NEW.equals(element.getMigrationState())) {
+                compositionId = automationComposition.getCompositionTargetId();
+            } else {
+                compositionId = automationComposition.getCompositionId();
+            }
+            var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
+            if (MigrationState.NEW.equals(element.getMigrationState())) {
+                // Undeploy newly added element on a Failed Migration
+                startPhase = 0;
+            }
             if (startPhaseMsg.equals(startPhase)) {
                 element.setDeployState(DeployState.UNDEPLOYING);
                 var compositionElement =
-                        cacheProvider.createCompositionElementDto(automationComposition.getCompositionId(), element);
+                        cacheProvider.createCompositionElementDto(compositionId, element);
                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
                         element.getProperties(), element.getOutProperties());
                 listener.undeploy(messageId, compositionElement, instanceElement);
@@ -288,76 +310,131 @@ public class AutomationCompositionHandler {
      */
     public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
         var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
+        var acTargetDefinition = cacheProvider.getAcElementsDefinitions().get(migrationMsg.getCompositionTargetId());
+        if (Boolean.FALSE.equals(migrationMsg.getRollback())) {
+            handleMigration(automationComposition, acTargetDefinition, migrationMsg);
+        } else {
+            handleRollback(automationComposition, migrationMsg);
+        }
+    }
+
+    private void handleRollback(AutomationComposition automationComposition,
+                                AutomationCompositionMigration migrationMsg) {
+        AutomationComposition acCopy = null;
         if (automationComposition == null) {
-            LOGGER.debug("Automation composition {} does not use this participant",
-                    migrationMsg.getAutomationCompositionId());
+            LOGGER.warn(AC_NOT_USED, migrationMsg.getAutomationCompositionId());
             return;
+        } else {
+            LOGGER.info("Rollback operation invoked for the instance {}", migrationMsg.getAutomationCompositionId());
+            acCopy = new AutomationComposition(automationComposition);
+            automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
+            automationComposition.setDeployState(DeployState.MIGRATION_REVERTING);
         }
-        var acCopy = new AutomationComposition(automationComposition);
-        automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
-        automationComposition.setDeployState(DeployState.MIGRATING);
         for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
+                migrateExistingElementsOnThisParticipant(automationComposition, migrationMsg.getCompositionTargetId(),
+                        participantDeploy, migrationMsg.getStage(), false);
 
-                migrateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(),
-                        migrationMsg.getCompositionTargetId(), participantDeploy, migrationMsg.getStage());
-
-                callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy,
-                        migrationMsg.getCompositionTargetId(), migrationMsg.getStage(),
-                        Boolean.TRUE.equals(migrationMsg.getRollback()));
+                callParticipantMigrate(migrationMsg, participantDeploy.getAcElementList(), acCopy);
             }
+
         }
     }
 
-    private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements, AutomationComposition acCopy,
-            UUID compositionTargetId, int stage, boolean rollback) {
-        var compositionElementMap = cacheProvider.getCompositionElementDtoMap(acCopy);
-        var instanceElementMap = cacheProvider.getInstanceElementDtoMap(acCopy);
-        var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
-        var compositionElementTargetMap =
-                cacheProvider.getCompositionElementDtoMap(automationComposition, compositionTargetId);
-        var instanceElementMigrateMap = cacheProvider.getInstanceElementDtoMap(automationComposition);
+    private void handleMigration(AutomationComposition automationComposition, AcDefinition acTargetDefinition,
+                                 AutomationCompositionMigration migrationMsg) {
+        AutomationComposition acCopy = null;
+        if (automationComposition == null) {
+            if (acTargetDefinition == null) {
+                LOGGER.warn(AC_NOT_USED, migrationMsg.getAutomationCompositionId());
+                return;
+            }
+        } else {
+            LOGGER.info("Migration invoked on an existing participant for the instance {}",
+                    migrationMsg.getAutomationCompositionId());
+            acCopy = new AutomationComposition(automationComposition);
+        }
+        var newParticipant = false;
+        for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
+            if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
+                if (automationComposition == null) {
+                    // New element with new participant added in Migration
+                    LOGGER.info("Participant newly added in Migration for the instance {}",
+                            migrationMsg.getAutomationCompositionId());
+                    newParticipant = true;
+                    cacheProvider.initializeAutomationComposition(migrationMsg.getCompositionId(),
+                            migrationMsg.getCompositionTargetId(), migrationMsg.getAutomationCompositionId(),
+                            participantDeploy, DeployState.MIGRATING, SubState.NONE,
+                            migrationMsg.getRevisionIdInstance());
+                    automationComposition = cacheProvider
+                            .getAutomationComposition(migrationMsg.getAutomationCompositionId());
+                }
+                migrateExistingElementsOnThisParticipant(automationComposition, migrationMsg.getCompositionTargetId(),
+                        participantDeploy, migrationMsg.getStage(), newParticipant);
+
+                callParticipantMigrate(migrationMsg, participantDeploy.getAcElementList(), acCopy);
+            }
+        }
+    }
 
-        // Call migrate for newly added and updated elements
+    private void callParticipantMigrate(AutomationCompositionMigration migrationMsg, List<AcElementDeploy> acElements,
+                                        AutomationComposition formerAcInstance) {
+        var latestAcFromCache = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
+        var instanceElementTargetMap = cacheProvider.getInstanceElementDtoMap(latestAcFromCache);
+        var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(latestAcFromCache,
+                migrationMsg.getCompositionTargetId());
+        Map<UUID, CompositionElementDto> compositionElementMap = new HashMap<>();
+        Map<UUID, InstanceElementDto> instanceElementMap = new HashMap<>();
+        if (formerAcInstance != null) { //Existing participant
+            compositionElementMap = cacheProvider.getCompositionElementDtoMap(formerAcInstance);
+            instanceElementMap = cacheProvider.getInstanceElementDtoMap(formerAcInstance);
+        }
+        // Call migrate for new and existing elements
         for (var acElement : acElements) {
+            UUID compIdForCommonProperties = null;
+            if (MigrationState.REMOVED.equals(acElement.getMigrationState())) {
+                compIdForCommonProperties = latestAcFromCache.getCompositionId();
+            } else {
+                compIdForCommonProperties = migrationMsg.getCompositionTargetId();
+            }
             var compositionInProperties =
-                    cacheProvider.getCommonProperties(compositionTargetId, acElement.getDefinition());
+                    cacheProvider.getCommonProperties(compIdForCommonProperties, acElement.getDefinition());
             var stageSet = ParticipantUtils.findStageSetMigrate(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(), Map.of(), Map.of(),
-                                    ElementState.NOT_PRESENT);
+            if (MigrationState.REMOVED.equals(acElement.getMigrationState())) {
+                stageSet = Set.of(0);
+            }
+            var rollback = Boolean.TRUE.equals(migrationMsg.getRollback());
+            if (stageSet.contains(migrationMsg.getStage())) {
+                if (MigrationState.NEW.equals(acElement.getMigrationState())) {
+                    var compositionElementDto = new CompositionElementDto(migrationMsg.getCompositionId(),
+                            acElement.getDefinition(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
+                    var instanceElementDto = new InstanceElementDto(migrationMsg.getAutomationCompositionId(),
+                            acElement.getId(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
                     var compositionElementTargetDto =
                             CacheProvider.changeStateToNew(compositionElementTargetMap.get(acElement.getId()));
-                    var instanceElementMigrateDto =
-                            CacheProvider.changeStateToNew(instanceElementMigrateMap.get(acElement.getId()));
+                    var instanceElementTargetDto =
+                            CacheProvider.changeStateToNew(instanceElementTargetMap.get(acElement.getId()));
+
+                    listenerMigrate(migrationMsg.getMessageId(), compositionElementDto, compositionElementTargetDto,
+                            instanceElementDto, instanceElementTargetDto, migrationMsg.getStage(), rollback);
 
-                    listenerMigrate(messageId, compositionElementDto, compositionElementTargetDto, instanceElementDto,
-                            instanceElementMigrateDto, stage, rollback);
-                } else {
-                    listenerMigrate(messageId, compositionElementMap.get(acElement.getId()),
+                } else if (MigrationState.REMOVED.equals(acElement.getMigrationState())) {
+                    var compositionDtoTarget = new CompositionElementDto(migrationMsg.getCompositionTargetId(),
+                            acElement.getDefinition(), Map.of(), Map.of(), ElementState.REMOVED);
+                    var instanceElementDtoTarget = new InstanceElementDto(migrationMsg.getAutomationCompositionId(),
+                            acElement.getId(), Map.of(), Map.of(), ElementState.REMOVED);
+                    listenerMigrate(migrationMsg.getMessageId(), compositionElementMap.get(acElement.getId()),
+                            compositionDtoTarget, instanceElementMap.get(acElement.getId()), instanceElementDtoTarget,
+                            migrationMsg.getStage(), rollback);
+
+                } else { // DEFAULT case
+                    listenerMigrate(migrationMsg.getMessageId(), compositionElementMap.get(acElement.getId()),
                             compositionElementTargetMap.get(acElement.getId()),
-                            instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()),
-                            stage, rollback);
+                            instanceElementMap.get(acElement.getId()), instanceElementTargetMap.get(acElement.getId()),
+                            migrationMsg.getStage(), rollback);
                 }
             }
         }
-        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, Map.of(), Map.of(),
-                        ElementState.REMOVED);
-                listenerMigrate(messageId, compositionElementMap.get(elementId), compositionDtoTarget,
-                        instanceElementMap.get(elementId), instanceDtoTarget, 0, rollback);
-            }
-        }
     }
 
     private void listenerMigrate(UUID messageId, CompositionElementDto compositionElement,
@@ -367,6 +444,7 @@ public class AutomationCompositionHandler {
             listener.rollback(messageId, compositionElement, compositionElementTarget, instanceElement,
                     instanceElementMigrate, stage);
         } else {
+            LOGGER.info("Invoking migration of element on the participant for {}", instanceElement.elementId());
             listener.migrate(messageId, compositionElement, compositionElementTarget, instanceElement,
                     instanceElementMigrate, stage);
         }
index 2da33a9..cead60a 100644 (file)
@@ -87,7 +87,7 @@ public class MsgExecutor {
                 message.setCompositionId(null);
                 message.setRevisionIdComposition(null);
             } else {
-                LOGGER.debug("Composition {} missing or outdated", message.getCompositionId());
+                LOGGER.info("Composition {} missing or outdated", message.getCompositionId());
                 result = false;
             }
         }
@@ -98,7 +98,7 @@ public class MsgExecutor {
                 message.setCompositionTargetId(null);
                 message.setRevisionIdCompositionTarget(null);
             } else {
-                LOGGER.debug("Composition Target {} missing or outdated", message.getCompositionTargetId());
+                LOGGER.info("Composition Target {} missing or outdated", message.getCompositionTargetId());
                 result = false;
             }
         }
@@ -108,7 +108,7 @@ public class MsgExecutor {
                 message.setInstanceId(null);
                 message.setRevisionIdInstance(null);
             } else {
-                LOGGER.debug("Instance {} missing or outdated", message.getInstanceId());
+                LOGGER.info("Instance {} missing or outdated", message.getInstanceId());
                 result = false;
             }
         }
index 73a3291..ce69b0b 100644 (file)
@@ -258,7 +258,6 @@ public class ParticipantHandler {
             LOGGER.debug("Ignore ParticipantSync message {}", participantSyncMsg.getMessageId());
             return;
         }
-        LOGGER.debug("ParticipantSync message received for participantId {}", participantSyncMsg.getParticipantId());
         acDefinitionHandler.handleParticipantSync(participantSyncMsg);
         msgExecutor.check();
     }
index 1c17708..6e9febc 100644 (file)
@@ -87,6 +87,7 @@ public class ThreadHandler implements Closeable {
         cleanExecution(instanceElement.elementId(), messageId);
         var result = executor.submit(() -> this.deployProcess(compositionElement, instanceElement));
         executionMap.put(instanceElement.elementId(), result);
+        LOGGER.info("Deploy process successfully started on the participant for {}", instanceElement.elementId());
     }
 
     private void deployProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) {
@@ -103,7 +104,7 @@ public class ThreadHandler implements Closeable {
     }
 
     /**
-     * Handle an udeploy on a automation composition element.
+     * Handle an undeploy on a automation composition element.
      *
      * @param messageId the messageId
      * @param compositionElement the information of the Automation Composition Definition Element
@@ -113,6 +114,7 @@ public class ThreadHandler implements Closeable {
         cleanExecution(instanceElement.elementId(), messageId);
         var result = executor.submit(() -> this.undeployProcess(compositionElement, instanceElement));
         executionMap.put(instanceElement.elementId(), result);
+        LOGGER.info("Undeploy process successfully started on the participant for {}", instanceElement.elementId());
     }
 
     private void undeployProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) {
@@ -191,6 +193,7 @@ public class ThreadHandler implements Closeable {
         cleanExecution(instanceElement.elementId(), messageId);
         var result = executor.submit(() -> this.deleteProcess(compositionElement, instanceElement));
         executionMap.put(instanceElement.elementId(), result);
+        LOGGER.info("Delete process successfully started on the participant for {}", instanceElement.elementId());
     }
 
     private void deleteProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) {
@@ -220,6 +223,7 @@ public class ThreadHandler implements Closeable {
         var result = executor.submit(() ->
             this.updateProcess(compositionElement, instanceElement, instanceElementUpdated));
         executionMap.put(instanceElement.elementId(), result);
+        LOGGER.info("Update process successfully started on the participant for {}", instanceElement.elementId());
     }
 
     private void updateProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
@@ -265,6 +269,7 @@ public class ThreadHandler implements Closeable {
         cleanExecution(composition.compositionId(), messageId);
         var result = executor.submit(() -> this.primeProcess(composition));
         executionMap.put(composition.compositionId(), result);
+        LOGGER.info("Priming process successfully started on the participant for {}", composition.compositionId());
     }
 
     private void primeProcess(CompositionDto composition) {
@@ -272,9 +277,9 @@ public class ThreadHandler implements Closeable {
             listener.prime(composition);
             executionMap.remove(composition.compositionId());
         } catch (Exception e) {
-            LOGGER.error("Composition Defintion prime failed {} {}", composition.compositionId(), e.getMessage());
+            LOGGER.error("Composition Definition prime failed {} {}", composition.compositionId(), e.getMessage());
             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
-                StateChangeResult.FAILED, "Composition Defintion prime failed");
+                StateChangeResult.FAILED, "Composition Definition prime failed");
         }
     }
 
@@ -288,6 +293,7 @@ public class ThreadHandler implements Closeable {
         cleanExecution(composition.compositionId(), messageId);
         var result = executor.submit(() -> this.deprimeProcess(composition));
         executionMap.put(composition.compositionId(), result);
+        LOGGER.info("De-priming process successfully started on the participant for {}", composition.compositionId());
     }
 
     private void deprimeProcess(CompositionDto composition) {
@@ -295,9 +301,9 @@ public class ThreadHandler implements Closeable {
             listener.deprime(composition);
             executionMap.remove(composition.compositionId());
         } catch (Exception e) {
-            LOGGER.error("Composition Defintion deprime failed {} {}", composition.compositionId(), e.getMessage());
+            LOGGER.error("Composition Definition deprime failed {} {}", composition.compositionId(), e.getMessage());
             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
-                StateChangeResult.FAILED, "Composition Defintion deprime failed");
+                StateChangeResult.FAILED, "Composition Definition deprime failed");
         }
     }
 
@@ -331,6 +337,7 @@ public class ThreadHandler implements Closeable {
             this.migrateProcess(compositionElement, compositionElementTarget,
                 instanceElement, instanceElementMigrate, stage));
         executionMap.put(instanceElement.elementId(), result);
+        LOGGER.info("Migration process successfully started on the participant for {}", instanceElement.elementId());
     }
 
     private void migrateProcess(CompositionElementDto compositionElement,
@@ -366,6 +373,8 @@ public class ThreadHandler implements Closeable {
             this.migratePrecheckProcess(compositionElement, compositionElementTarget, instanceElement,
                 instanceElementMigrate));
         executionMap.put(instanceElement.elementId(), result);
+        LOGGER.info("Migration Precheck process successfully started on the participant for {}",
+                instanceElement.elementId());
     }
 
     private void migratePrecheckProcess(CompositionElementDto compositionElement,
@@ -396,6 +405,7 @@ public class ThreadHandler implements Closeable {
         cleanExecution(instanceElement.elementId(), messageId);
         var result = executor.submit(() -> this.reviewProcess(compositionElement, instanceElement));
         executionMap.put(instanceElement.elementId(), result);
+        LOGGER.info("Review process successfully started on the participant for {}", instanceElement.elementId());
     }
 
     private void reviewProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) {
@@ -424,6 +434,7 @@ public class ThreadHandler implements Closeable {
         cleanExecution(instanceElement.elementId(), messageId);
         var result = executor.submit(() -> this.prepareProcess(compositionElement, instanceElement, stage));
         executionMap.put(instanceElement.elementId(), result);
+        LOGGER.info("Prepare process successfully started on the participant for {}", instanceElement.elementId());
     }
 
     private void prepareProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
@@ -457,6 +468,7 @@ public class ThreadHandler implements Closeable {
                 this.rollbackProcess(compositionElement, compositionElementRollback, instanceElement,
                         instanceElementRollback, stage));
         executionMap.put(instanceElement.elementId(), result);
+        LOGGER.info("Rollback process successfully started on the participant for {}", instanceElement.elementId());
     }
 
     private void rollbackProcess(CompositionElementDto compositionElement,
index 19f66ab..240cbe8 100644 (file)
@@ -46,11 +46,15 @@ import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.models.base.PfUtils;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 @Component
 public class CacheProvider {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(CacheProvider.class);
+
     @Getter
     private final UUID participantId;
 
@@ -131,6 +135,7 @@ public class CacheProvider {
             acDefinition.getElements().put(acElementDefinition.getAcElementDefinitionId(), acElementDefinition);
         }
         acElementsDefinitions.put(compositionId, acDefinition);
+        LOGGER.info("Updated cache for the composition id {}", compositionId);
     }
 
     public void removeElementDefinition(@NonNull UUID compositionId) {
@@ -177,8 +182,9 @@ public class CacheProvider {
      */
     public void initializeAutomationComposition(@NonNull UUID compositionId, @NonNull UUID instanceId,
             ParticipantDeploy participantDeploy, UUID revisionId) {
-        initializeAutomationComposition(compositionId, instanceId, participantDeploy,
+        initializeAutomationComposition(compositionId, null, instanceId, participantDeploy,
             DeployState.DEPLOYING, SubState.NONE, revisionId);
+
     }
 
     /**
@@ -191,31 +197,16 @@ public class CacheProvider {
      * @param subState the SubState
      * @param revisionId the identification of the last update
      */
-    public void initializeAutomationComposition(@NonNull UUID compositionId, @NonNull UUID instanceId,
+    public void initializeAutomationComposition(@NonNull UUID compositionId, UUID compositionTargetId,
+                                                @NonNull UUID instanceId,
             ParticipantDeploy participantDeploy, DeployState deployState, SubState subState, UUID revisionId) {
-        var acLast = automationCompositions.get(instanceId);
-        Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
-        for (var element : participantDeploy.getAcElementList()) {
-            var acElement = createAutomationCompositionElement(element);
-            acElement.setParticipantId(getParticipantId());
-            acElement.setDeployState(deployState);
-            acElement.setSubState(subState);
-            var acElementLast = acLast != null ? acLast.getElements().get(element.getId()) : null;
-            if (acElementLast != null) {
-                acElement.setOutProperties(acElementLast.getOutProperties());
-                acElement.setOperationalState(acElementLast.getOperationalState());
-                acElement.setUseState(acElementLast.getUseState());
-            }
-            acElementMap.put(element.getId(), acElement);
-        }
-        var automationComposition = new AutomationComposition();
-        automationComposition.setCompositionId(compositionId);
-        automationComposition.setInstanceId(instanceId);
-        automationComposition.setElements(acElementMap);
-        automationComposition.setDeployState(deployState);
-        automationComposition.setSubState(subState);
-        automationComposition.setRevisionId(revisionId);
+
+        var automationComposition = createAcInstance(compositionId, compositionTargetId, instanceId, participantDeploy,
+                deployState, subState, revisionId);
+
         automationCompositions.put(instanceId, automationComposition);
+        LOGGER.info("Initialized participant cache for the {} operation of the instance {}", deployState, instanceId);
+
     }
 
     /**
@@ -242,9 +233,9 @@ public class CacheProvider {
             acElement.setUseState(element.getUseState());
             acElement.setProperties(element.getProperties());
             acElement.setOutProperties(element.getOutProperties());
+            acElement.setMigrationState(element.getMigrationState());
             acElementMap.put(element.getId(), acElement);
         }
-
         var automationComposition = new AutomationComposition();
         automationComposition.setCompositionId(compositionId);
         automationComposition.setCompositionTargetId(participantRestartAc.getCompositionTargetId());
@@ -255,6 +246,55 @@ public class CacheProvider {
         automationComposition.setStateChangeResult(participantRestartAc.getStateChangeResult());
         automationComposition.setRevisionId(participantRestartAc.getRevisionId());
         automationCompositions.put(automationComposition.getInstanceId(), automationComposition);
+        LOGGER.info("Updated participant cache for the instance id {}",
+                participantRestartAc.getAutomationCompositionId());
+    }
+
+    /**
+     *  Create an AutomationComposition.
+     * @param compositionId compositionId
+     * @param compositionTargetId compositionTargetId
+     * @param instanceId instanceId
+     * @param participantDeploy participantDeploy
+     * @param deployState deployState
+     * @param subState subState
+     * @param revisionId revisionId
+     * @return AutomationComposition
+     */
+    public AutomationComposition createAcInstance(@NonNull UUID compositionId, UUID compositionTargetId,
+                                                  @NonNull UUID instanceId, ParticipantDeploy participantDeploy,
+                                                  DeployState deployState, SubState subState, UUID revisionId) {
+        var acLast = automationCompositions.get(instanceId);
+        Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
+        for (var element : participantDeploy.getAcElementList()) {
+            var acElement = createAutomationCompositionElement(element);
+            acElement.setParticipantId(getParticipantId());
+            acElement.setDeployState(deployState);
+            acElement.setSubState(subState);
+            var acElementLast = acLast != null ? acLast.getElements().get(element.getId()) : null;
+            if (acElementLast != null) {
+                acElement.setOutProperties(acElementLast.getOutProperties());
+                acElement.setOperationalState(acElementLast.getOperationalState());
+                acElement.setUseState(acElementLast.getUseState());
+            }
+            acElementMap.put(element.getId(), acElement);
+        }
+        var automationComposition = acLast != null ? acLast : new AutomationComposition();
+        automationComposition.setCompositionId(compositionId);
+        automationComposition.setInstanceId(instanceId);
+        if (acLast != null) {
+            automationComposition.getElements().putAll(acElementMap);
+        } else {
+            automationComposition.setElements(acElementMap);
+        }
+        automationComposition.setDeployState(deployState);
+        automationComposition.setSubState(subState);
+        automationComposition.setRevisionId(revisionId);
+        if (compositionTargetId != null) {
+            automationComposition.setCompositionTargetId(compositionTargetId);
+        }
+
+        return automationComposition;
     }
 
     /**
@@ -270,6 +310,7 @@ public class CacheProvider {
         acElement.setProperties(element.getProperties());
         acElement.setSubState(SubState.NONE);
         acElement.setLockState(LockState.LOCKED);
+        acElement.setMigrationState(element.getMigrationState());
         return acElement;
     }
 
@@ -303,7 +344,8 @@ public class CacheProvider {
         var acDefinition = acElementsDefinitions.get(compositionId);
         Map<UUID, CompositionElementDto> map = new HashMap<>();
         for (var element : automationComposition.getElements().values()) {
-            var acDefinitionElement = acDefinition.getElements().get(element.getDefinition());
+            var acDefinitionElement = (acDefinition != null) ? acDefinition.getElements().get(element.getDefinition()) :
+                    null;
             var compositionElement = (acDefinitionElement != null)
                     ? new CompositionElementDto(compositionId, element.getDefinition(),
                     acDefinitionElement.getAutomationCompositionElementToscaNodeTemplate().getProperties(),
index e7d6e36..1553d30 100644 (file)
@@ -40,8 +40,10 @@ import org.onap.policy.clamp.acm.participant.intermediary.handler.cache.CachePro
 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.AutomationCompositionElement;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
 import org.onap.policy.clamp.models.acm.concepts.DeployState;
+import org.onap.policy.clamp.models.acm.concepts.MigrationState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
@@ -272,7 +274,8 @@ class AutomationCompositionHandlerTest {
     void handleAutomationCompositionMigrationTest() {
         var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
         automationComposition.setCompositionId(UUID.randomUUID());
-        automationComposition.setInstanceId(UUID.randomUUID());
+        var instanceId = UUID.randomUUID();
+        automationComposition.setInstanceId(instanceId);
         automationComposition.setCompositionTargetId(UUID.randomUUID());
         var definitions =
                 CommonTestData.createAutomationCompositionElementDefinitionList(automationComposition);
@@ -291,6 +294,14 @@ class AutomationCompositionHandlerTest {
                 automationComposition.getElements().size(), false);
         testMigration(cacheProviderRollback, automationComposition, 0,
                 automationComposition.getElements().size(), true);
+
+        // New participant with new elements added in Migration
+        cacheProvider.removeAutomationComposition(instanceId);
+        for (var element : automationComposition.getElements().entrySet()) {
+            element.getValue().setMigrationState(MigrationState.NEW);
+        }
+        testMigration(cacheProvider, automationComposition, 0,
+                automationComposition.getElements().size(), false);
     }
 
     @Test
@@ -302,10 +313,17 @@ class AutomationCompositionHandlerTest {
         var acMigrate = new AutomationComposition(automationComposition);
         acMigrate.setCompositionTargetId(UUID.randomUUID());
 
-        // replacing first element with new one
+        // remove element
         var element = acMigrate.getElements().values().iterator().next();
-        element.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.0.0"));
-        element.setId(UUID.randomUUID());
+        element.setMigrationState(MigrationState.REMOVED);
+
+        //Add element
+        var newElement = new AutomationCompositionElement(element);
+        newElement.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.2.4"));
+        newElement.setId(UUID.randomUUID());
+        newElement.setMigrationState(MigrationState.NEW);
+
+        acMigrate.getElements().put(newElement.getId(), newElement);
 
         var migrateDefinitions =
                 CommonTestData.createAutomationCompositionElementDefinitionList(acMigrate);
@@ -321,10 +339,11 @@ class AutomationCompositionHandlerTest {
                 automationComposition.getInstanceId(), definitions,
                 acMigrate.getCompositionTargetId(), migrateDefinitions);
 
-        testMigration(cacheProvider, acMigrate, 0, acMigrate.getElements().size() + 1, false);
-        testMigration(cacheProviderRollback, acMigrate, 0, acMigrate.getElements().size() + 1, true);
+        testMigration(cacheProvider, acMigrate, 0, acMigrate.getElements().size(), false);
+        testMigration(cacheProviderRollback, acMigrate, 0, acMigrate.getElements().size(), true);
     }
 
+
     @Test
     void handleAcMigrationStageTest() {
         var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
@@ -334,10 +353,18 @@ class AutomationCompositionHandlerTest {
         var acMigrate = new AutomationComposition(automationComposition);
         acMigrate.setCompositionTargetId(UUID.randomUUID());
 
-        // replacing first element with new one
+        // remove element
         var element = acMigrate.getElements().values().iterator().next();
-        element.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.2.4"));
-        element.setId(UUID.randomUUID());
+        element.setMigrationState(MigrationState.REMOVED);
+
+        //Add element
+        var newElement = new AutomationCompositionElement(element);
+        newElement.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.2.4"));
+        newElement.setId(UUID.randomUUID());
+        newElement.setMigrationState(MigrationState.NEW);
+
+        acMigrate.getElements().put(newElement.getId(), newElement);
+
 
         // replacing definition version
         acMigrate.getElements().values().forEach(el -> el.setDefinition(
index 4ac0b43..f795095 100644 (file)
@@ -86,13 +86,13 @@ class ThreadHandlerTest {
             var messageId = UUID.randomUUID();
             threadHandler.prime(messageId, composition);
             verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.COMMISSIONED,
-                StateChangeResult.FAILED, "Composition Defintion prime failed");
+                StateChangeResult.FAILED, "Composition Definition prime failed");
 
             clearInvocations(listener);
             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).deprime(composition);
             threadHandler.deprime(messageId, composition);
             verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.PRIMED,
-                StateChangeResult.FAILED, "Composition Defintion deprime failed");
+                StateChangeResult.FAILED, "Composition Definition deprime failed");
         }
     }
 
index badc33e..39d1b55 100644 (file)
@@ -67,10 +67,10 @@ class CacheProviderTest {
         var deployState = DeployState.DEPLOYED;
         var subState = SubState.NONE;
 
-        assertThatThrownBy(() -> cacheProvider.initializeAutomationComposition(null, instanceId, participantDeploy,
-                deployState, subState, null)).isInstanceOf(NullPointerException.class);
-        assertThatThrownBy(() -> cacheProvider.initializeAutomationComposition(instanceId, null, participantDeploy,
-                deployState, subState, null)).isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> cacheProvider.initializeAutomationComposition(null, instanceId, null,
+                participantDeploy, deployState, subState, null)).isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> cacheProvider.initializeAutomationComposition(instanceId, null, null,
+                participantDeploy, deployState, subState, null)).isInstanceOf(NullPointerException.class);
     }
 
     @Test
index bcde4c5..edcb515 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.
@@ -281,6 +281,7 @@ public class CommonTestData {
             acElement.setId(element.getId());
             acElement.setDefinition(element.getDefinition());
             acElement.setProperties(element.getProperties());
+            acElement.setMigrationState(element.getMigrationState());
             participantDeploy.getAcElementList().add(acElement);
         }
         return participantDeploy;
index d79a84f..83af86d 100644 (file)
@@ -39,6 +39,7 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
 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.MigrationState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.clamp.models.acm.concepts.SubState;
@@ -97,9 +98,11 @@ public class AutomationCompositionInstantiationProvider {
 
         var acDefinition = acDefinitionProvider.getAcDefinition(compositionId);
         AcDefinitionProvider.checkPrimedComposition(acDefinition);
-        var validationResult = validateAutomationComposition(automationComposition, acDefinition);
+        var validationResult = validateAutomationComposition(automationComposition, acDefinition, false);
         if (!validationResult.isValid()) {
             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
+        } else {
+            associateParticipantId(automationComposition, acDefinition, null);
         }
         encryptInstanceProperties(automationComposition, compositionId);
         automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
@@ -124,23 +127,25 @@ public class AutomationCompositionInstantiationProvider {
     public InstantiationResponse updateAutomationComposition(UUID compositionId,
                                                              AutomationComposition automationComposition) {
         var instanceId = automationComposition.getInstanceId();
-        var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
-        AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acToUpdate);
+        var acFromDb = automationCompositionProvider.getAutomationComposition(instanceId);
+        AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acFromDb);
         var acDefinition = acDefinitionProvider.getAcDefinition(compositionId);
         AcDefinitionProvider.checkPrimedComposition(acDefinition);
-        if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
+        if (DeployState.UNDEPLOYED.equals(acFromDb.getDeployState())) {
             LOGGER.info("Updating undeployed instance with id {}", instanceId);
-            acToUpdate.setElements(automationComposition.getElements());
-            acToUpdate.setName(automationComposition.getName());
-            acToUpdate.setVersion(automationComposition.getVersion());
-            acToUpdate.setDescription(automationComposition.getDescription());
-            acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
-            var validationResult = validateAutomationComposition(acToUpdate, acDefinition);
+            acFromDb.setElements(automationComposition.getElements());
+            acFromDb.setName(automationComposition.getName());
+            acFromDb.setVersion(automationComposition.getVersion());
+            acFromDb.setDescription(automationComposition.getDescription());
+            acFromDb.setDerivedFrom(automationComposition.getDerivedFrom());
+            var validationResult = validateAutomationComposition(acFromDb, acDefinition, false);
             if (!validationResult.isValid()) {
                 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
+            } else {
+                associateParticipantId(acFromDb, acDefinition, null);
             }
-            encryptInstanceProperties(acToUpdate, compositionId);
-            automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
+            encryptInstanceProperties(acFromDb, compositionId);
+            automationComposition = automationCompositionProvider.updateAutomationComposition(acFromDb);
             return createInstantiationResponse(automationComposition);
 
         }
@@ -158,17 +163,17 @@ public class AutomationCompositionInstantiationProvider {
             }
         }
         var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder,
-            acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(),
-            acToUpdate.getStateChangeResult());
+            acFromDb.getDeployState(), acFromDb.getLockState(), acFromDb.getSubState(),
+            acFromDb.getStateChangeResult());
         return switch (result) {
-            case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate, acDefinition);
+            case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acFromDb, acDefinition);
 
-            case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate, acDefinition);
+            case "MIGRATE" -> migrateAutomationComposition(automationComposition, acFromDb, acDefinition);
 
-            case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate, acDefinition);
+            case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acFromDb, acDefinition);
 
             default -> throw new PfModelRuntimeException(Status.BAD_REQUEST,
-                "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState());
+                "Not allowed to " + deployOrder + " in the state " + acFromDb.getDeployState());
         };
     }
 
@@ -196,9 +201,11 @@ public class AutomationCompositionInstantiationProvider {
             AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
         }
 
-        var validationResult = validateAutomationComposition(acToBeUpdated, acDefinition);
+        var validationResult = validateAutomationComposition(acToBeUpdated, acDefinition, false);
         if (!validationResult.isValid()) {
             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
+        } else {
+            associateParticipantId(acToBeUpdated, acDefinition, null);
         }
         updateAcForProperties(acToBeUpdated);
 
@@ -212,43 +219,39 @@ public class AutomationCompositionInstantiationProvider {
     }
 
     private InstantiationResponse migrateAutomationComposition(
-            AutomationComposition automationComposition, AutomationComposition acToBeUpdated,
+            AutomationComposition automationComposition, AutomationComposition acFromDb,
             AutomationCompositionDefinition acDefinition) {
         LOGGER.info("Migrating instance with id {}", automationComposition.getInstanceId());
-        if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
+        if (!DeployState.DEPLOYED.equals(acFromDb.getDeployState())) {
             throw new PfModelRuntimeException(Status.BAD_REQUEST,
-                "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
+                "Not allowed to migrate in the state " + acFromDb.getDeployState());
         }
         // make copy for rollback
-        automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated);
+        automationCompositionProvider.copyAcElementsBeforeUpdate(acFromDb);
 
         var acDefinitionTarget = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
         AcDefinitionProvider.checkPrimedComposition(acDefinitionTarget);
         // Iterate and update the element property values
-        var elementsRemoved = updateElementsProperties(automationComposition, acToBeUpdated, acDefinitionTarget);
+        updateElementsProperties(automationComposition, acFromDb, acDefinitionTarget, acDefinition);
 
-        updateAcForMigration(acToBeUpdated, acDefinitionTarget, DeployState.MIGRATING);
+        updateAcForMigration(acFromDb, acDefinitionTarget, DeployState.MIGRATING);
 
-        var acToPublish = new AutomationComposition(acToBeUpdated);
-        encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
+        var acToPublish = new AutomationComposition(acFromDb);
+        encryptInstanceProperties(acFromDb, acFromDb.getCompositionTargetId());
 
-        var ac = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
-        for (var element : elementsRemoved) {
-            automationCompositionProvider.deleteAutomationCompositionElement(element.getId());
-        }
+        var ac = automationCompositionProvider.updateAutomationComposition(acFromDb);
 
         // Publish migrate event to the participants
-        supervisionAcHandler.migrate(acToPublish, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId(),
-                elementsRemoved);
+        supervisionAcHandler.migrate(acToPublish, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
         return createInstantiationResponse(ac);
     }
 
-    private void updateAcForMigration(AutomationComposition acToBeUpdated,
+    private void updateAcForMigration(AutomationComposition acFromDb,
                                       AutomationCompositionDefinition acDefinition, DeployState deployState) {
-        AcmUtils.setCascadedState(acToBeUpdated, deployState, LockState.LOCKED);
-        acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
-        var stage = ParticipantUtils.getFirstStage(acToBeUpdated, acDefinition.getServiceTemplate());
-        acToBeUpdated.setPhase(stage);
+        AcmUtils.setCascadedState(acFromDb, deployState, LockState.LOCKED);
+        acFromDb.setStateChangeResult(StateChangeResult.NO_ERROR);
+        var stage = ParticipantUtils.getFirstStage(acFromDb, acDefinition.getServiceTemplate());
+        acFromDb.setPhase(stage);
     }
 
     private void updateAcForProperties(AutomationComposition acToBeUpdated) {
@@ -273,11 +276,10 @@ public class AutomationCompositionInstantiationProvider {
         var acDefinitionTarget = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
         AcDefinitionProvider.checkPrimedComposition(acDefinitionTarget);
         // Iterate and update the element property values
-        var removedElements = updateElementsProperties(automationComposition, copyAc, acDefinitionTarget);
+        updateElementsProperties(automationComposition, copyAc, acDefinitionTarget, acDefinition);
 
         // Publish migrate event to the participants
-        supervisionAcHandler.migratePrecheck(copyAc, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId(),
-                removedElements);
+        supervisionAcHandler.migratePrecheck(copyAc, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
 
         AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
             SubState.MIGRATION_PRECHECKING);
@@ -294,23 +296,15 @@ public class AutomationCompositionInstantiationProvider {
      * @return the result of validation
      */
     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
-            AutomationCompositionDefinition acDefinition) {
+            AutomationCompositionDefinition acDefinition, boolean migrateOperation) {
         var result = new BeanValidationResult("AutomationComposition", automationComposition);
         participantProvider.checkRegisteredParticipant(acDefinition);
         result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
                 acDefinition.getServiceTemplate(),
-            acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
+            acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName(), migrateOperation));
 
         result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
 
-        if (result.isValid()) {
-            for (var element : automationComposition.getElements().values()) {
-                var name = element.getDefinition().getName();
-                var participantId = acDefinition.getElementStateMap().get(name).getParticipantId();
-                element.setParticipantId(participantId);
-            }
-        }
-
         return result;
     }
 
@@ -443,49 +437,54 @@ public class AutomationCompositionInstantiationProvider {
     public void rollback(UUID compositionId, UUID instanceId) {
         LOGGER.info("Rollback automation composition request received for CompositionID: {} and InstanceID: {}",
                 compositionId, instanceId);
-        var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
-        AutomationCompositionProvider.validateInstanceEndpoint(compositionId, automationComposition);
+        var acFromDb = automationCompositionProvider.getAutomationComposition(instanceId);
+        AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acFromDb);
 
         if (!DeployOrder.MIGRATION_REVERT.name().equals(acInstanceStateResolver.resolve(
                 DeployOrder.MIGRATION_REVERT, LockOrder.NONE,
-                SubOrder.NONE, automationComposition.getDeployState(), automationComposition.getLockState(),
-                automationComposition.getSubState(), automationComposition.getStateChangeResult()))) {
+                SubOrder.NONE, acFromDb.getDeployState(), acFromDb.getLockState(),
+                acFromDb.getSubState(), acFromDb.getStateChangeResult()))) {
             throw new PfModelRuntimeException(Status.BAD_REQUEST, "Invalid state for rollback");
         }
 
         var automationCompositionToRollback =
             automationCompositionProvider.getAutomationCompositionRollback(instanceId);
-        var acToBeUpdated = new AutomationComposition(automationComposition);
-        acToBeUpdated.setCompositionTargetId(automationCompositionToRollback.getCompositionId());
-        acToBeUpdated.setElements(automationCompositionToRollback.getElements().values().stream()
+        var acFromDbCopy = new AutomationComposition(acFromDb);
+        acFromDbCopy.setCompositionTargetId(automationCompositionToRollback.getCompositionId());
+        acFromDbCopy.setElements(automationCompositionToRollback.getElements().values().stream()
                 .collect(Collectors.toMap(AutomationCompositionElement::getId, AutomationCompositionElement::new)));
 
-        var acDefinitionTarget = acDefinitionProvider.getAcDefinition(acToBeUpdated.getCompositionTargetId());
-        var validationResult = validateAutomationComposition(acToBeUpdated, acDefinitionTarget);
+        var acDefinitionTarget = acDefinitionProvider.getAcDefinition(acFromDbCopy.getCompositionTargetId());
+        var validationResult = validateAutomationComposition(acFromDbCopy, acDefinitionTarget, true);
         if (!validationResult.isValid()) {
             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
         }
-
-        updateAcForMigration(acToBeUpdated, acDefinitionTarget, DeployState.MIGRATION_REVERTING);
-        var elementsRemoved = getElementRemoved(automationComposition, acToBeUpdated);
-        automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
-        for (var element : elementsRemoved) {
-            automationCompositionProvider.deleteAutomationCompositionElement(element.getId());
+        // Include new elements from migration for the participant undeploy
+        for (var element : acFromDb.getElements().values()) {
+            if (MigrationState.NEW.equals(element.getMigrationState())) {
+                acFromDbCopy.getElements().put(element.getId(), element);
+            }
+            if (MigrationState.REMOVED.equals(element.getMigrationState())) {
+                acFromDbCopy.getElements().get(element.getId()).setMigrationState(MigrationState.REMOVED);
+            }
         }
-        var acDefinition = acDefinitionProvider.getAcDefinition(acToBeUpdated.getCompositionId());
-        supervisionAcHandler.migrate(acToBeUpdated, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId(),
-                elementsRemoved);
+
+        updateAcForMigration(acFromDbCopy, acDefinitionTarget, DeployState.MIGRATION_REVERTING);
+        automationCompositionProvider.updateAutomationComposition(acFromDbCopy);
+        var acDefinition = acDefinitionProvider.getAcDefinition(acFromDbCopy.getCompositionId());
+        supervisionAcHandler.migrate(acFromDbCopy, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
     }
 
-    private List<AutomationCompositionElement> updateElementsProperties(AutomationComposition automationComposition,
-            AutomationComposition acToBeUpdated, AutomationCompositionDefinition acDefinitionTarget) {
+    private void updateElementsProperties(AutomationComposition automationComposition,
+            AutomationComposition acFromDb, AutomationCompositionDefinition acDefinitionTarget,
+                                          AutomationCompositionDefinition acDefinition) {
         for (var element : automationComposition.getElements().entrySet()) {
             var elementId = element.getKey();
-            var dbAcElement = acToBeUpdated.getElements().get(elementId);
-            // Add additional elements if present for migration
+            var dbAcElement = acFromDb.getElements().get(elementId);
             if (dbAcElement == null) {
                 LOGGER.info("New Ac element {} added in Migration", elementId);
-                acToBeUpdated.getElements().put(elementId, element.getValue());
+                element.getValue().setMigrationState(MigrationState.NEW);
+                acFromDb.getElements().put(elementId, element.getValue());
             } else {
                 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
                 var newDefinition = element.getValue().getDefinition().asConceptKey();
@@ -495,16 +494,34 @@ public class AutomationCompositionInstantiationProvider {
                 dbAcElement.setDefinition(element.getValue().getDefinition());
             }
         }
-        // Remove elements which are not present in the new Ac instance
-        var elementsRemoved = getElementRemoved(acToBeUpdated, automationComposition);
-        elementsRemoved.forEach(element -> acToBeUpdated.getElements().remove(element.getId()));
+        // Update migrationState for the removed elements
+        var elementsRemoved = getElementRemoved(acFromDb, automationComposition);
+        elementsRemoved.forEach(element -> acFromDb.getElements().get(element.getId())
+                .setMigrationState(MigrationState.REMOVED));
 
-        var validationResult = validateAutomationComposition(acToBeUpdated, acDefinitionTarget);
+        var validationResult = validateAutomationComposition(acFromDb, acDefinitionTarget, true);
         if (!validationResult.isValid()) {
             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
+        } else {
+            associateParticipantId(acFromDb, acDefinitionTarget, acDefinition);
+        }
+        acFromDb.setCompositionTargetId(automationComposition.getCompositionTargetId());
+    }
+
+    private void associateParticipantId(AutomationComposition acFromDb,
+                                        AutomationCompositionDefinition acDefinitionTarget,
+                                        AutomationCompositionDefinition oldAcDefinition) {
+        for (var element : acFromDb.getElements().values()) {
+            var name = element.getDefinition().getName();
+            var migrationState = element.getMigrationState();
+            if (MigrationState.REMOVED.equals(migrationState) && oldAcDefinition != null) {
+                var participantId = oldAcDefinition.getElementStateMap().get(name).getParticipantId();
+                element.setParticipantId(participantId);
+            } else {
+                var participantId = acDefinitionTarget.getElementStateMap().get(name).getParticipantId();
+                element.setParticipantId(participantId);
+            }
         }
-        acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
-        return elementsRemoved;
     }
 
     private AutomationCompositionDefinition getAcDefinition(UUID compositionId,
index a7defea..a979bf8 100644 (file)
@@ -24,7 +24,6 @@ import io.micrometer.core.annotation.Timed;
 import io.opentelemetry.context.Context;
 import jakarta.ws.rs.core.Response;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
@@ -40,7 +39,6 @@ import org.onap.policy.clamp.common.acm.utils.AcmThreadFactory;
 import org.onap.policy.clamp.models.acm.concepts.AcElementDeployAck;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition;
-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.LockState;
 import org.onap.policy.clamp.models.acm.concepts.MigrationState;
@@ -147,7 +145,6 @@ public class SupervisionAcHandler {
             AcmUtils.setCascadedState(automationComposition, DeployState.UNDEPLOYING, LockState.NONE);
         }
         automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
-        automationComposition.setCompositionTargetId(null);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
         automationComposition.setPhase(startPhase);
         automationCompositionProvider.updateAutomationComposition(automationComposition);
@@ -368,11 +365,11 @@ public class SupervisionAcHandler {
      * @param revisionIdCompositionTarget the last Update from Composition Target
      */
     public void migrate(AutomationComposition automationComposition, UUID revisionIdComposition,
-                        UUID revisionIdCompositionTarget, List<AutomationCompositionElement> removedElements) {
+                        UUID revisionIdCompositionTarget) {
         executor.execute(() -> {
             encryptionUtils.decryptInstanceProperties(automationComposition);
             acCompositionMigrationPublisher.send(automationComposition, automationComposition.getPhase(),
-                    revisionIdComposition, revisionIdCompositionTarget, removedElements);
+                    revisionIdComposition, revisionIdCompositionTarget);
         });
     }
 
@@ -384,8 +381,8 @@ public class SupervisionAcHandler {
      * @param revisionIdCompositionTarget the last Update from Composition Target
      */
     public void migratePrecheck(AutomationComposition automationComposition, UUID revisionIdComposition,
-            UUID revisionIdCompositionTarget, List<AutomationCompositionElement> removedElements) {
+            UUID revisionIdCompositionTarget) {
         executor.execute(() -> acCompositionMigrationPublisher.send(automationComposition, 0,
-                revisionIdComposition, revisionIdCompositionTarget, removedElements));
+                revisionIdComposition, revisionIdCompositionTarget));
     }
 }
index 3f7704f..68a70fa 100644 (file)
@@ -22,7 +22,6 @@ package org.onap.policy.clamp.acm.runtime.supervision.comm;
 
 import io.micrometer.core.annotation.Timed;
 import java.time.Instant;
-import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import lombok.AllArgsConstructor;
@@ -59,8 +58,7 @@ public class AcElementPropertiesPublisher extends AbstractParticipantPublisher<P
         propertiesUpdate.setTimestamp(Instant.now());
         propertiesUpdate.setRevisionIdInstance(automationComposition.getRevisionId());
         propertiesUpdate.setRevisionIdComposition(revisionIdComposition);
-        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.UPDATE,
-                List.of());
+        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.UPDATE);
         propertiesUpdate.setParticipantUpdatesList(participantUpdatesList);
         propertiesUpdate.setParticipantIdList(participantUpdatesList.stream()
                 .map(ParticipantDeploy::getParticipantId).collect(Collectors.toSet()));
index a1ebbf3..2f1eaf7 100644 (file)
@@ -22,7 +22,6 @@ package org.onap.policy.clamp.acm.runtime.supervision.comm;
 
 import io.micrometer.core.annotation.Timed;
 import java.time.Instant;
-import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import lombok.AllArgsConstructor;
@@ -54,8 +53,7 @@ public class AcPreparePublisher extends AbstractParticipantPublisher<AutomationC
         var acPrepare = createAutomationCompositionPrepare(automationComposition.getCompositionId(),
             automationComposition.getInstanceId());
         acPrepare.setStage(stage);
-        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.NONE,
-                List.of());
+        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.NONE);
         acPrepare.setParticipantList(participantUpdatesList);
         acPrepare.setParticipantIdList(participantUpdatesList.stream()
                 .map(ParticipantDeploy::getParticipantId).collect(Collectors.toSet()));
index 4917813..df55b08 100644 (file)
 package org.onap.policy.clamp.acm.runtime.supervision.comm;
 
 import io.micrometer.core.annotation.Timed;
-import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
 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.ParticipantDeploy;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
@@ -49,7 +47,7 @@ public class AutomationCompositionMigrationPublisher
             value = "publisher.automation_composition_migration",
             description = "AUTOMATION_COMPOSITION_MIGRATION messages published")
     public void send(AutomationComposition automationComposition, int stage, UUID revisionIdComposition,
-                     UUID revisionIdCompositionTarget, List<AutomationCompositionElement> removedElements) {
+                     UUID revisionIdCompositionTarget) {
         var acMigration = new AutomationCompositionMigration();
         acMigration.setRollback(DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState()));
         acMigration.setPrecheck(Boolean.TRUE.equals(automationComposition.getPrecheck()));
@@ -61,8 +59,7 @@ public class AutomationCompositionMigrationPublisher
         acMigration.setRevisionIdInstance(automationComposition.getRevisionId());
         acMigration.setRevisionIdComposition(revisionIdComposition);
         acMigration.setRevisionIdCompositionTarget(revisionIdCompositionTarget);
-        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.MIGRATE,
-                removedElements);
+        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.MIGRATE);
         acMigration.setParticipantUpdatesList(participantUpdatesList);
         acMigration.setParticipantIdList(participantUpdatesList.stream()
                 .map(ParticipantDeploy::getParticipantId).collect(Collectors.toSet()));
index 27a05f3..5ad8ea9 100644 (file)
@@ -26,6 +26,7 @@ import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils;
 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.MigrationState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.clamp.models.acm.concepts.SubState;
@@ -65,6 +66,12 @@ public abstract class AbstractScanner {
             // migration scenario
             automationComposition.setCompositionId(automationComposition.getCompositionTargetId());
             automationComposition.setCompositionTargetId(null);
+
+            for (var acElement : automationComposition.getElements().values()) {
+                if (MigrationState.NEW.equals(acElement.getMigrationState())) {
+                    acElement.setMigrationState(MigrationState.DEFAULT);
+                }
+            }
         }
         automationComposition.setDeployState(AcmUtils.deployCompleted(deployState));
         automationComposition.setLockState(AcmUtils.lockCompleted(deployState, automationComposition.getLockState()));
index 8785b59..226d39a 100644 (file)
@@ -75,15 +75,8 @@ public class SimpleScanner extends AbstractScanner {
     private UpdateSync handleAcStateChange(AutomationComposition automationComposition, DocMessage message) {
         var result = new UpdateSync();
         var element = automationComposition.getElements().get(message.getInstanceElementId());
-        if (element == null && isMigration(automationComposition)
-                && StateChangeResult.FAILED.equals(message.getStateChangeResult())) {
-            // fail delete element during migration
-            automationComposition.setStateChangeResult(StateChangeResult.FAILED);
-            result.setUpdated(true);
-            result.setToBeSync(true);
-            return result;
-        }
-        if (element == null || !validateStateMessage(automationComposition, message)) {
+        if (element == null) {
+            LOGGER.warn("Not a valid message, element is null for the element id {}", message.getInstanceElementId());
             return result;
         }
         result.setUpdated(true);
@@ -101,16 +94,6 @@ public class SimpleScanner extends AbstractScanner {
         return result;
     }
 
-    private boolean isMigration(AutomationComposition automationComposition) {
-        return DeployState.MIGRATING.equals(automationComposition.getDeployState())
-                || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState());
-    }
-
-    private boolean validateStateMessage(AutomationComposition automationComposition, DocMessage message) {
-        return !DeployState.DELETED.equals(message.getDeployState())
-                || (DeployState.DELETING.equals(automationComposition.getDeployState()));
-    }
-
     private UpdateSync handleOutProperties(AutomationComposition automationComposition, DocMessage message) {
         var element = automationComposition.getElements().get(message.getInstanceElementId());
         var result = new UpdateSync();
index 777130f..9b79c1b 100644 (file)
 
 package org.onap.policy.clamp.acm.runtime.supervision.scanner;
 
+import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
 import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils;
@@ -30,8 +33,11 @@ import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionM
 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.AutomationCompositionDefinition;
+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.MigrationState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
+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.AutomationCompositionProvider;
 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
@@ -76,39 +82,69 @@ public class StageScanner extends AbstractScanner {
             AutomationCompositionDefinition acDefinition, UpdateSync updateSync, UUID revisionIdComposition) {
         var completed = true;
         var minStageNotCompleted = 1000; // min stage not completed
+        List<UUID> elementsDeleted = new ArrayList<>();
         for (var element : automationComposition.getElements().values()) {
             if (AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState(),
                     element.getSubState())) {
-                var toscaNodeTemplate = acDefinition.getServiceTemplate().getToscaTopologyTemplate().getNodeTemplates()
-                        .get(element.getDefinition().getName());
-                var stageSet = DeployState.MIGRATING.equals(automationComposition.getDeployState())
-                            || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState())
-                        ? ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties())
-                        : ParticipantUtils.findStageSetPrepare(toscaNodeTemplate.getProperties());
-                var minStage = stageSet.stream().min(Comparator.comparing(Integer::valueOf)).orElse(0);
+                var minStage = calculateMinStage(element, automationComposition, acDefinition);
                 int stage = element.getStage() != null ? element.getStage() : minStage;
                 minStageNotCompleted = Math.min(minStageNotCompleted, stage);
                 completed = false;
+            } else if (DeployState.DELETED.equals(element.getDeployState())
+                    && automationComposition.getStateChangeResult().equals(StateChangeResult.NO_ERROR)) {
+                // Migration with successful removal of element
+                elementsDeleted.add(element.getId());
             }
         }
-
+        removeDeletedElements(automationComposition, elementsDeleted, updateSync);
         if (completed) {
             complete(automationComposition, updateSync);
         } else {
-            LOGGER.debug("automation composition scan: transition from state {} to {} not completed",
-                    automationComposition.getDeployState(), automationComposition.getLockState());
+            processNextStage(automationComposition, updateSync, minStageNotCompleted, revisionIdComposition,
+                    acDefinition);
+        }
+    }
 
-            if (minStageNotCompleted != automationComposition.getPhase()) {
-                savePhase(automationComposition, minStageNotCompleted);
-                updateSync.setUpdated(true);
-                saveAndSync(automationComposition, updateSync);
-                LOGGER.debug("retry message AutomationCompositionMigration");
-                var acToSend = new AutomationComposition(automationComposition);
-                decryptInstanceProperties(acToSend);
-                sendNextStage(acToSend, minStageNotCompleted, revisionIdComposition, acDefinition);
-            } else {
-                handleTimeout(automationComposition, updateSync);
-            }
+    private void processNextStage(AutomationComposition automationComposition, UpdateSync updateSync,
+                                  int minStageNotCompleted, UUID revisionIdComposition,
+                                  AutomationCompositionDefinition acDefinition) {
+        LOGGER.debug("automation composition scan: transition from state {} to {} not completed",
+                automationComposition.getDeployState(), automationComposition.getLockState());
+
+        if (minStageNotCompleted != automationComposition.getPhase()) {
+            savePhase(automationComposition, minStageNotCompleted);
+            updateSync.setUpdated(true);
+            saveAndSync(automationComposition, updateSync);
+            LOGGER.debug("retry message AutomationCompositionMigration");
+            var acToSend = new AutomationComposition(automationComposition);
+            decryptInstanceProperties(acToSend);
+            sendNextStage(acToSend, minStageNotCompleted, revisionIdComposition, acDefinition);
+        } else {
+            handleTimeout(automationComposition, updateSync);
+        }
+    }
+
+    private Integer calculateMinStage(AutomationCompositionElement element, AutomationComposition automationComposition,
+                                      AutomationCompositionDefinition acDefinition) {
+        Set<Integer> stageSet = new HashSet<>();
+        if (! MigrationState.REMOVED.equals(element.getMigrationState())) {
+            var toscaNodeTemplate = acDefinition.getServiceTemplate().getToscaTopologyTemplate().getNodeTemplates()
+                    .get(element.getDefinition().getName());
+            stageSet = DeployState.MIGRATING.equals(automationComposition.getDeployState())
+                    || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState())
+                    ? ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties())
+                    : ParticipantUtils.findStageSetPrepare(toscaNodeTemplate.getProperties());
+        }
+        return stageSet.stream().min(Comparator.comparing(Integer::valueOf)).orElse(0);
+    }
+
+    private void removeDeletedElements(AutomationComposition automationComposition, List<UUID> elementsDeleted,
+                                       UpdateSync updateSync) {
+        for (var elementId : elementsDeleted) {
+            LOGGER.info("Deleting element {} in Migration ", elementId);
+            automationComposition.getElements().remove(elementId);
+            acProvider.deleteAutomationCompositionElement(elementId);
+            updateSync.setUpdated(true);
         }
     }
 
@@ -119,7 +155,7 @@ public class StageScanner extends AbstractScanner {
             LOGGER.debug("retry message AutomationCompositionMigration");
             // acDefinition for migration is the Composition target
             acMigrationPublisher.send(automationComposition, minStageNotCompleted, revisionIdComposition,
-                    acDefinition.getRevisionId(), List.of());
+                    acDefinition.getRevisionId());
         }
         if (SubState.PREPARING.equals(automationComposition.getSubState())) {
             LOGGER.debug("retry message AutomationCompositionPrepare");
index 6179cfb..ff0f9b0 100644 (file)
@@ -311,7 +311,7 @@ class AutomationCompositionInstantiationProviderTest {
         automationCompositionTarget.setPrecheck(true);
         var preCheckResponse = instantiationProvider.updateAutomationComposition(compositionId,
                 automationCompositionTarget);
-        verify(supervisionAcHandler).migratePrecheck(any(), any(), any(), any());
+        verify(supervisionAcHandler).migratePrecheck(any(), any(), any());
         InstantiationUtils.assertInstantiationResponse(preCheckResponse, automationCompositionTarget);
 
         automationCompositionTarget.setPrecheck(false);
@@ -320,7 +320,7 @@ class AutomationCompositionInstantiationProviderTest {
         var instantiationResponse = instantiationProvider.updateAutomationComposition(compositionId,
                 automationCompositionTarget);
 
-        verify(supervisionAcHandler).migrate(any(), any(), any(), any());
+        verify(supervisionAcHandler).migrate(any(), any(), any());
         InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationCompositionTarget);
     }
 
@@ -366,7 +366,7 @@ class AutomationCompositionInstantiationProviderTest {
         var instantiationResponse = instantiationProvider
                 .updateAutomationComposition(automationComposition.getCompositionId(), automationComposition);
 
-        verify(supervisionAcHandler).migrate(any(), any(), any(), any());
+        verify(supervisionAcHandler).migrate(any(), any(), any());
         verify(acProvider).updateAutomationComposition(automationComposition);
         InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationComposition);
     }
@@ -405,7 +405,7 @@ class AutomationCompositionInstantiationProviderTest {
         var instantiationResponse = instantiationProvider
                 .updateAutomationComposition(automationComposition.getCompositionId(), automationComposition);
 
-        verify(supervisionAcHandler).migratePrecheck(any(), any(), any(), any());
+        verify(supervisionAcHandler).migratePrecheck(any(), any(), any());
         verify(acProvider).updateAutomationComposition(automationComposition);
         InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationComposition);
 
index 564723b..8fa9498 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.
@@ -25,8 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.fail;
 
 import java.io.File;
-import java.util.ArrayList;
-import java.util.UUID;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
 import org.onap.policy.common.utils.coder.Coder;
@@ -57,13 +55,6 @@ public class InstantiationUtils {
 
             // add suffix to name
             automationComposition.setName(automationComposition.getName() + suffix);
-            var elements = new ArrayList<>(automationComposition.getElements().values());
-            automationComposition.getElements().clear();
-            for (var element : elements) {
-                // set unique UUID to the element
-                element.setId(UUID.randomUUID());
-                automationComposition.getElements().put(element.getId(), element);
-            }
             return automationComposition;
         } catch (CoderException e) {
             fail("Cannot read or decode " + path);
index 3f01464..b9c5f31 100644 (file)
@@ -33,7 +33,6 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.onap.policy.clamp.acm.runtime.util.CommonTestData.TOSCA_SERVICE_TEMPLATE_YAML;
 
-import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.UUID;
@@ -539,9 +538,9 @@ class SupervisionAcHandlerTest {
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Migrate");
         assert automationComposition != null;
         automationComposition.setPhase(0);
-        handler.migrate(automationComposition, UUID.randomUUID(), UUID.randomUUID(), List.of());
+        handler.migrate(automationComposition, UUID.randomUUID(), UUID.randomUUID());
         verify(acCompositionMigrationPublisher, timeout(1000))
-                .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class), any());
+                .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class));
     }
 
     @Test
@@ -554,9 +553,9 @@ class SupervisionAcHandlerTest {
                 mock(AcPreparePublisher.class), mock(MessageProvider.class), mock(EncryptionUtils.class));
         var automationComposition =
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Migrate");
-        handler.migratePrecheck(automationComposition, UUID.randomUUID(), UUID.randomUUID(), List.of());
+        handler.migratePrecheck(automationComposition, UUID.randomUUID(), UUID.randomUUID());
         verify(acCompositionMigrationPublisher, timeout(1000))
-                .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class), any());
+                .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class));
     }
 
     @Test
index ca65a63..4c3473d 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.policy.clamp.acm.runtime.supervision;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -28,6 +29,8 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.onap.policy.clamp.acm.runtime.util.CommonTestData.TOSCA_SERVICE_TEMPLATE_YAML;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -37,17 +40,23 @@ import java.util.Set;
 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.supervision.scanner.AcDefinitionScanner;
 import org.onap.policy.clamp.acm.runtime.supervision.scanner.MonitoringScanner;
 import org.onap.policy.clamp.acm.runtime.supervision.scanner.PhaseScanner;
 import org.onap.policy.clamp.acm.runtime.supervision.scanner.SimpleScanner;
 import org.onap.policy.clamp.acm.runtime.supervision.scanner.StageScanner;
 import org.onap.policy.clamp.acm.runtime.supervision.scanner.UpdateSync;
+import org.onap.policy.clamp.acm.runtime.util.CommonTestData;
 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition;
 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.MigrationState;
 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;
@@ -123,6 +132,76 @@ class SupervisionScannerTest {
         verify(messageProvider).removeJob(JOB_ID);
     }
 
+    @Test
+    void testAcInstanceForMigrationSuccess() {
+        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+        automationComposition.setDeployState(DeployState.MIGRATING);
+        automationComposition.setInstanceId(INSTANCE_ID);
+        automationComposition.setCompositionId(COMPOSITION_ID);
+        var compositionTargetId = UUID.randomUUID();
+        automationComposition.setCompositionTargetId(compositionTargetId);
+        automationComposition.setLockState(LockState.LOCKED);
+        automationComposition.setLastMsg(TimestampHelper.now());
+        automationComposition.setPhase(0);
+        List<UUID> elementIds = new ArrayList<>(automationComposition.getElements().keySet());
+
+        Map<UUID, MigrationState> migrationStateMap = new HashMap<>();
+        List<MigrationState> states = List.of(MigrationState.REMOVED, MigrationState.NEW, MigrationState.DEFAULT);
+
+        for (int i = 0; i < elementIds.size(); i++) {
+            migrationStateMap.put(elementIds.get(i), states.get(i));
+        }
+
+        for (var entry : automationComposition.getElements().entrySet()) {
+            entry.getValue().setMigrationState(migrationStateMap.get(entry.getKey()));
+        }
+
+        for (var element : automationComposition.getElements().values()) {
+            if (MigrationState.REMOVED.equals(migrationStateMap.get(element.getId()))) {
+                element.setDeployState(DeployState.DELETED);
+                element.setLockState(LockState.LOCKED);
+            } else {
+                element.setDeployState(DeployState.DEPLOYED);
+                element.setLockState(LockState.LOCKED);
+            }
+        }
+
+        var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+        Set<UUID> set = new HashSet<>();
+        set.add(automationComposition.getInstanceId());
+        when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(set);
+        when(automationCompositionProvider.findAutomationComposition(automationComposition.getInstanceId()))
+                .thenReturn(Optional.of(automationComposition));
+
+        var acDefinitionTarget = createAutomationCompositionDefinition(AcTypeState.PRIMED);
+        acDefinitionTarget.setCompositionId(compositionTargetId);
+        var acDefinitionProvider = createAcDefinitionProvider(AcTypeState.PRIMED);
+        when(acDefinitionProvider.getAcDefinition(compositionTargetId)).thenReturn(acDefinitionTarget);
+        var acDefinition = new AutomationCompositionDefinition();
+        acDefinition.setCompositionId(COMPOSITION_ID);
+        when(acDefinitionProvider.getAcDefinition(COMPOSITION_ID)).thenReturn(acDefinition);
+        var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+        var stageScanner = new StageScanner(automationCompositionProvider, mock(ParticipantSyncPublisher.class),
+                mock(AutomationCompositionMigrationPublisher.class), mock(AcPreparePublisher.class),
+                acRuntimeParameterGroup, mock(EncryptionUtils.class));
+
+        var messageProvider = mock(MessageProvider.class);
+        when(messageProvider.createJob(automationComposition.getInstanceId())).thenReturn(Optional.of(JOB_ID));
+        var monitoringScanner = new MonitoringScanner(automationCompositionProvider, acDefinitionProvider,
+                mock(AcDefinitionScanner.class), stageScanner, mock(SimpleScanner.class), mock(PhaseScanner.class),
+                messageProvider);
+        var supervisionScanner = new SupervisionScanner(automationCompositionProvider, acDefinitionProvider,
+                messageProvider, monitoringScanner);
+
+        supervisionScanner.run();
+        assertEquals(2, automationComposition.getElements().size());
+        assertEquals(DeployState.DEPLOYED, automationComposition.getDeployState());
+        for (var entry : automationComposition.getElements().entrySet()) {
+            assertEquals(MigrationState.DEFAULT, entry.getValue().getMigrationState());
+        }
+        verify(messageProvider).removeJob(JOB_ID);
+    }
+
     @Test
     void testAcDefinitionJobExist() {
         var acDefinitionProvider = createAcDefinitionProvider(AcTypeState.PRIMING);
index 54c3ded..84b80ae 100644 (file)
@@ -235,7 +235,7 @@ class SupervisionMessagesTest {
         publisher.active(topicSink);
         var automationComposition =
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_UPDATE_JSON, "Crud");
-        publisher.send(automationComposition, 0, UUID.randomUUID(), UUID.randomUUID(), List.of());
+        publisher.send(automationComposition, 0, UUID.randomUUID(), UUID.randomUUID());
         verify(topicSink).send(anyString());
     }
 
index e8a0254..8e55579 100644 (file)
@@ -119,12 +119,12 @@ class SimpleScannerTest {
         assertFalse(result.isUpdated());
         assertFalse(result.isToBeSync());
 
-        // wrong Delete State
+        // Delete response from participant
         docMessage.setMessageType(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
         docMessage.setInstanceElementId(elementId);
         docMessage.setDeployState(DeployState.DELETED);
         result = simpleScanner.scanMessage(automationComposition, docMessage);
-        assertFalse(result.isUpdated());
+        assertTrue(result.isUpdated());
         assertFalse(result.isToBeSync());
     }
 
@@ -270,7 +270,7 @@ class SimpleScannerTest {
     @Test
     void testScanMessageMigrationFail() {
         var automationComposition = createAutomationComposition(DeployState.MIGRATING, LockState.LOCKED);
-        var elementId = UUID.randomUUID();
+        var elementId = automationComposition.getElements().entrySet().iterator().next().getKey();
         var docMessage = new DocMessage();
         docMessage.setMessageType(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
         docMessage.setStateChangeResult(StateChangeResult.FAILED);