aboutsummaryrefslogtreecommitdiffstats
path: root/runtime-acm
diff options
context:
space:
mode:
authorFrancescoFioraEst <francesco.fiora@est.tech>2025-02-14 11:35:17 +0000
committerFrancescoFioraEst <francesco.fiora@est.tech>2025-02-14 15:24:04 +0000
commitc3c94eef2854ec8d552ac3059e693cbc352737a5 (patch)
treee175dc5026748d2f1187e357ba774ff0aaf3e7fa /runtime-acm
parente3c94609eba45f2c4e10ab52024709bd9a216aca (diff)
Add monitoring implementation for synchronization events in ACM-r
Issue-ID: POLICY-5277 Change-Id: Ie4c8a11b27a110b02972f6dd5a81c0be1ba8de3a Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
Diffstat (limited to 'runtime-acm')
-rw-r--r--runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java304
-rw-r--r--runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AbstractScanner.java124
-rw-r--r--runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AcDefinitionScanner.java166
-rw-r--r--runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/PhaseScanner.java124
-rw-r--r--runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.java123
-rw-r--r--runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java96
-rw-r--r--runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/UpdateSync.java41
-rw-r--r--runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java434
-rw-r--r--runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AcDefinitionScannerTest.java242
-rw-r--r--runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/PhaseScannerTest.java254
-rw-r--r--runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScannerTest.java246
-rw-r--r--runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScannerTest.java100
12 files changed, 1620 insertions, 634 deletions
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java
index 9b030eae1..3b17565ef 100644
--- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java
@@ -1,6 +1,6 @@
/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2024 Nordix Foundation.
+ * Copyright (C) 2021-2025 Nordix Foundation.
* ================================================================================
* Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
* ================================================================================
@@ -22,25 +22,23 @@
package org.onap.policy.clamp.acm.runtime.supervision;
-import java.util.Comparator;
import java.util.HashMap;
+import java.util.Map;
import java.util.UUID;
-import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
-import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionDeployPublisher;
-import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionMigrationPublisher;
-import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionStateChangePublisher;
-import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
-import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
+import lombok.RequiredArgsConstructor;
+import org.onap.policy.clamp.acm.runtime.supervision.scanner.AcDefinitionScanner;
+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.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.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.AcDefinitionProvider;
import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
import org.onap.policy.clamp.models.acm.utils.AcmUtils;
-import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -50,42 +48,16 @@ import org.springframework.stereotype.Component;
* This class is used to scan the automation compositions in the database and check if they are in the correct state.
*/
@Component
+@RequiredArgsConstructor
public class SupervisionScanner {
private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionScanner.class);
- private final long maxStatusWaitMs;
-
private final AutomationCompositionProvider automationCompositionProvider;
private final AcDefinitionProvider acDefinitionProvider;
- private final AutomationCompositionStateChangePublisher automationCompositionStateChangePublisher;
- private final AutomationCompositionDeployPublisher automationCompositionDeployPublisher;
- private final ParticipantSyncPublisher participantSyncPublisher;
- private final AutomationCompositionMigrationPublisher automationCompositionMigrationPublisher;
-
- /**
- * Constructor for instantiating SupervisionScanner.
- *
- * @param automationCompositionProvider the provider to use to read automation compositions from the database
- * @param acDefinitionProvider the Policy Models Provider
- * @param automationCompositionStateChangePublisher the AutomationComposition StateChange Publisher
- * @param automationCompositionDeployPublisher the AutomationCompositionUpdate Publisher
- * @param acRuntimeParameterGroup the parameters for the automation composition runtime
- */
- public SupervisionScanner(final AutomationCompositionProvider automationCompositionProvider,
- final AcDefinitionProvider acDefinitionProvider,
- final AutomationCompositionStateChangePublisher automationCompositionStateChangePublisher,
- final AutomationCompositionDeployPublisher automationCompositionDeployPublisher,
- final ParticipantSyncPublisher participantSyncPublisher,
- final AutomationCompositionMigrationPublisher automationCompositionMigrationPublisher,
- final AcRuntimeParameterGroup acRuntimeParameterGroup) {
- this.automationCompositionProvider = automationCompositionProvider;
- this.acDefinitionProvider = acDefinitionProvider;
- this.automationCompositionStateChangePublisher = automationCompositionStateChangePublisher;
- this.automationCompositionDeployPublisher = automationCompositionDeployPublisher;
- this.participantSyncPublisher = participantSyncPublisher;
- this.automationCompositionMigrationPublisher = automationCompositionMigrationPublisher;
- this.maxStatusWaitMs = acRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs();
- }
+ private final AcDefinitionScanner acDefinitionScanner;
+ private final StageScanner stageScanner;
+ private final SimpleScanner simpleScanner;
+ private final PhaseScanner phaseScanner;
/**
* Run Scanning.
@@ -93,251 +65,59 @@ public class SupervisionScanner {
public void run() {
LOGGER.debug("Scanning automation compositions in the database . . .");
- var acDefinitionList = acDefinitionProvider.getAllAcDefinitionsInTransition();
- for (var acDefinition : acDefinitionList) {
- scanAutomationCompositionDefinition(acDefinition);
+ var acDefinitions = acDefinitionProvider.getAllAcDefinitionsInTransition();
+ for (var acDefinition : acDefinitions) {
+ scanAcDefinition(acDefinition.getCompositionId());
}
- var acList = automationCompositionProvider.getAcInstancesInTransition();
- HashMap<UUID, AutomationCompositionDefinition> acDefinitionMap = new HashMap<>();
- for (var automationComposition : acList) {
- var compositionId = automationComposition.getCompositionTargetId() != null
- ? automationComposition.getCompositionTargetId() : automationComposition.getCompositionId();
- var acDefinition = acDefinitionMap.computeIfAbsent(compositionId, acDefinitionProvider::getAcDefinition);
- scanAutomationComposition(automationComposition, acDefinition.getServiceTemplate());
+ var instances = automationCompositionProvider.getAcInstancesInTransition();
+ Map<UUID, AutomationCompositionDefinition> acDefinitionMap = new HashMap<>();
+ for (var instance : instances) {
+ scanAutomationComposition(instance.getInstanceId(), acDefinitionMap);
}
LOGGER.debug("Automation composition scan complete . . .");
}
- private void scanAutomationCompositionDefinition(AutomationCompositionDefinition acDefinition) {
- if (StateChangeResult.FAILED.equals(acDefinition.getStateChangeResult())) {
- LOGGER.debug("automation definition {} scanned, OK", acDefinition.getCompositionId());
- return;
- }
+ private void scanAcDefinition(UUID compositionId) {
+ var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
+ var updateSync = new UpdateSync();
+ acDefinitionOpt.ifPresent(acDefinition ->
+ acDefinitionScanner.scanAutomationCompositionDefinition(acDefinition, updateSync));
+ }
- boolean completed = true;
- var finalState = AcTypeState.PRIMING.equals(acDefinition.getState())
- || AcTypeState.PRIMED.equals(acDefinition.getState()) ? AcTypeState.PRIMED : AcTypeState.COMMISSIONED;
- for (var element : acDefinition.getElementStateMap().values()) {
- if (!finalState.equals(element.getState())) {
- completed = false;
- break;
- }
- }
- if (completed) {
- acDefinitionProvider.updateAcDefinitionState(acDefinition.getCompositionId(), finalState,
- StateChangeResult.NO_ERROR);
- participantSyncPublisher.sendSync(acDefinition, null);
- } else {
- handleTimeout(acDefinition);
+ private void scanAutomationComposition(UUID instanceId,
+ Map<UUID, AutomationCompositionDefinition> acDefinitionMap) {
+ var automationCompositionOpt = automationCompositionProvider.findAutomationComposition(instanceId);
+ var updateSync = new UpdateSync();
+ if (automationCompositionOpt.isPresent()) {
+ var automationComposition = automationCompositionOpt.get();
+ var compositionId = automationComposition.getCompositionTargetId() != null
+ ? automationComposition.getCompositionTargetId() : automationComposition.getCompositionId();
+ var acDefinition = acDefinitionMap.computeIfAbsent(compositionId, acDefinitionProvider::getAcDefinition);
+ scanAutomationComposition(automationComposition, acDefinition.getServiceTemplate(), updateSync);
}
}
private void scanAutomationComposition(final AutomationComposition automationComposition,
- ToscaServiceTemplate serviceTemplate) {
+ ToscaServiceTemplate serviceTemplate, UpdateSync updateSync) {
LOGGER.debug("scanning automation composition {} . . .", automationComposition.getInstanceId());
if (!AcmUtils.isInTransitionalState(automationComposition.getDeployState(),
automationComposition.getLockState(), automationComposition.getSubState())
|| StateChangeResult.FAILED.equals(automationComposition.getStateChangeResult())) {
LOGGER.debug("automation composition {} scanned, OK", automationComposition.getInstanceId());
-
- return;
+ simpleScanner.saveAndSync(automationComposition, updateSync);
}
if (DeployState.MIGRATING.equals(automationComposition.getDeployState())) {
- scanStage(automationComposition, serviceTemplate);
+ stageScanner.scanStage(automationComposition, serviceTemplate, updateSync);
} else if (DeployState.UPDATING.equals(automationComposition.getDeployState())
- || SubState.PREPARING.equals(automationComposition.getSubState())
|| SubState.REVIEWING.equals(automationComposition.getSubState())
+ || SubState.PREPARING.equals(automationComposition.getSubState())
|| SubState.MIGRATION_PRECHECKING.equals(automationComposition.getSubState())) {
- simpleScan(automationComposition);
- } else {
- scanWithPhase(automationComposition, serviceTemplate);
- }
- }
-
- /**
- * Scan with startPhase: DEPLOY, UNDEPLOY, LOCK and UNLOCK.
- *
- * @param automationComposition the AutomationComposition
- * @param serviceTemplate the ToscaServiceTemplate
- */
- private void scanWithPhase(final AutomationComposition automationComposition,
- ToscaServiceTemplate serviceTemplate) {
- var completed = true;
- var minSpNotCompleted = 1000; // min startPhase not completed
- var maxSpNotCompleted = 0; // max startPhase not completed
- var defaultMin = 1000; // min startPhase
- var defaultMax = 0; // max startPhase
- for (var element : automationComposition.getElements().values()) {
- var toscaNodeTemplate = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
- .get(element.getDefinition().getName());
- int startPhase = toscaNodeTemplate != null
- && element.getDefinition().getVersion().equals(toscaNodeTemplate.getVersion())
- ? ParticipantUtils.findStartPhase(toscaNodeTemplate.getProperties()) : 0;
- defaultMin = Math.min(defaultMin, startPhase);
- defaultMax = Math.max(defaultMax, startPhase);
- if (AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState(),
- element.getSubState())) {
- completed = false;
- minSpNotCompleted = Math.min(minSpNotCompleted, startPhase);
- maxSpNotCompleted = Math.max(maxSpNotCompleted, startPhase);
- }
- }
-
- if (completed) {
- complete(automationComposition);
- } else {
- LOGGER.debug("automation composition scan: transition state {} {} not completed",
- automationComposition.getDeployState(), automationComposition.getLockState());
-
- var isForward =
- AcmUtils.isForward(automationComposition.getDeployState(), automationComposition.getLockState());
-
- var nextSpNotCompleted = isForward ? minSpNotCompleted : maxSpNotCompleted;
-
- if (nextSpNotCompleted != automationComposition.getPhase()) {
- sendAutomationCompositionMsg(automationComposition, nextSpNotCompleted);
- } else {
- handleTimeout(automationComposition);
- }
- }
- }
-
- /**
- * Simple scan: UPDATE, PREPARE, REVIEW, MIGRATE_PRECHECKING.
- *
- * @param automationComposition the AutomationComposition
- */
- private void simpleScan(final AutomationComposition automationComposition) {
- var completed = automationComposition.getElements().values().stream()
- .filter(element -> AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState(),
- element.getSubState())).findFirst().isEmpty();
-
- if (completed) {
- complete(automationComposition);
- } else {
- handleTimeout(automationComposition);
- }
- }
-
- /**
- * Scan with stage: MIGRATE.
- *
- * @param automationComposition the AutomationComposition
- * @param serviceTemplate the ToscaServiceTemplate
- */
- private void scanStage(final AutomationComposition automationComposition, ToscaServiceTemplate serviceTemplate) {
- var completed = true;
- var minStageNotCompleted = 1000; // min stage not completed
- for (var element : automationComposition.getElements().values()) {
- if (AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState(),
- element.getSubState())) {
- var toscaNodeTemplate = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
- .get(element.getDefinition().getName());
- var stageSet = ParticipantUtils.findStageSet(toscaNodeTemplate.getProperties());
- var minStage = stageSet.stream().min(Comparator.comparing(Integer::valueOf)).orElse(0);
- int stage = element.getStage() != null ? element.getStage() : minStage;
- minStageNotCompleted = Math.min(minStageNotCompleted, stage);
- completed = false;
- }
- }
-
- if (completed) {
- complete(automationComposition);
- } else {
- LOGGER.debug("automation composition scan: transition from state {} to {} not completed",
- automationComposition.getDeployState(), automationComposition.getLockState());
-
- if (minStageNotCompleted != automationComposition.getPhase()) {
- savePhase(automationComposition, minStageNotCompleted);
- LOGGER.debug("retry message AutomationCompositionMigration");
- automationCompositionMigrationPublisher.send(automationComposition, minStageNotCompleted);
- } else {
- handleTimeout(automationComposition);
- }
- }
- }
-
- private void complete(final AutomationComposition automationComposition) {
- LOGGER.debug("automation composition scan: transition state {} {} {} completed",
- automationComposition.getDeployState(), automationComposition.getLockState(),
- automationComposition.getSubState());
-
- var deployState = automationComposition.getDeployState();
- if (DeployState.MIGRATING.equals(automationComposition.getDeployState())) {
- // migration scenario
- automationComposition.setCompositionId(automationComposition.getCompositionTargetId());
- automationComposition.setCompositionTargetId(null);
- }
- automationComposition.setDeployState(AcmUtils.deployCompleted(deployState));
- automationComposition.setLockState(AcmUtils.lockCompleted(deployState, automationComposition.getLockState()));
- automationComposition.setPhase(null);
- automationComposition.setSubState(SubState.NONE);
- automationComposition.setPrecheck(null);
- if (StateChangeResult.TIMEOUT.equals(automationComposition.getStateChangeResult())) {
- automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
- }
- var acToUpdate = automationComposition;
- if (DeployState.DELETED.equals(automationComposition.getDeployState())) {
- automationCompositionProvider.deleteAutomationComposition(automationComposition.getInstanceId());
- } else {
- acToUpdate = automationCompositionProvider.updateAcState(acToUpdate);
- }
- participantSyncPublisher.sendSync(acToUpdate);
- }
-
- private void handleTimeout(AutomationCompositionDefinition acDefinition) {
- if (StateChangeResult.TIMEOUT.equals(acDefinition.getStateChangeResult())) {
- LOGGER.debug("The ac definition is in timeout {}", acDefinition.getCompositionId());
- return;
- }
- var now = TimestampHelper.nowEpochMilli();
- var lastMsg = TimestampHelper.toEpochMilli(acDefinition.getLastMsg());
- if ((now - lastMsg) > maxStatusWaitMs) {
- LOGGER.debug("Report timeout for the ac definition {}", acDefinition.getCompositionId());
- acDefinition.setStateChangeResult(StateChangeResult.TIMEOUT);
- acDefinitionProvider.updateAcDefinitionState(acDefinition.getCompositionId(),
- acDefinition.getState(), acDefinition.getStateChangeResult());
- participantSyncPublisher.sendSync(acDefinition, null);
- }
- }
-
- private void handleTimeout(AutomationComposition automationComposition) {
- LOGGER.debug("automation composition scan: transition from state {} to {} {} not completed",
- automationComposition.getDeployState(), automationComposition.getLockState(),
- automationComposition.getSubState());
-
- if (StateChangeResult.TIMEOUT.equals(automationComposition.getStateChangeResult())) {
- LOGGER.debug("The ac instance is in timeout {}", automationComposition.getInstanceId());
- return;
- }
- var now = TimestampHelper.nowEpochMilli();
- var lastMsg = TimestampHelper.toEpochMilli(automationComposition.getLastMsg());
- if ((now - lastMsg) > maxStatusWaitMs) {
- LOGGER.debug("Report timeout for the ac instance {}", automationComposition.getInstanceId());
- automationComposition.setStateChangeResult(StateChangeResult.TIMEOUT);
- automationCompositionProvider.updateAcState(automationComposition);
- participantSyncPublisher.sendSync(automationComposition);
- }
- }
-
- private void savePhase(AutomationComposition automationComposition, int startPhase) {
- automationComposition.setLastMsg(TimestampHelper.now());
- automationComposition.setPhase(startPhase);
- automationCompositionProvider.updateAcState(automationComposition);
- }
-
- private void sendAutomationCompositionMsg(AutomationComposition automationComposition, int startPhase) {
- savePhase(automationComposition, startPhase);
-
- if (DeployState.DEPLOYING.equals(automationComposition.getDeployState())) {
- LOGGER.debug("retry message AutomationCompositionDeploy");
- automationCompositionDeployPublisher.send(automationComposition, startPhase, false);
+ simpleScanner.simpleScan(automationComposition, updateSync);
} else {
- LOGGER.debug("retry message AutomationCompositionStateChange");
- automationCompositionStateChangePublisher.send(automationComposition, startPhase, false);
+ phaseScanner.scanWithPhase(automationComposition, serviceTemplate, updateSync);
}
}
}
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AbstractScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AbstractScanner.java
new file mode 100644
index 000000000..136276e55
--- /dev/null
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AbstractScanner.java
@@ -0,0 +1,124 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
+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.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;
+import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractScanner {
+
+ protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractScanner.class);
+
+ protected final long maxStatusWaitMs;
+
+ protected final AutomationCompositionProvider acProvider;
+ private final ParticipantSyncPublisher participantSyncPublisher;
+
+ protected AbstractScanner(final AutomationCompositionProvider acProvider,
+ final ParticipantSyncPublisher participantSyncPublisher,
+ final AcRuntimeParameterGroup acRuntimeParameterGroup) {
+ this.acProvider = acProvider;
+ this.participantSyncPublisher = participantSyncPublisher;
+ this.maxStatusWaitMs = acRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs();
+ }
+
+ protected void complete(final AutomationComposition automationComposition, UpdateSync updateSync) {
+ LOGGER.debug("automation composition scan: transition state {} {} {} completed",
+ automationComposition.getDeployState(), automationComposition.getLockState(),
+ automationComposition.getSubState());
+
+ var deployState = automationComposition.getDeployState();
+ if (DeployState.MIGRATING.equals(automationComposition.getDeployState())) {
+ // migration scenario
+ automationComposition.setCompositionId(automationComposition.getCompositionTargetId());
+ automationComposition.setCompositionTargetId(null);
+ }
+ automationComposition.setDeployState(AcmUtils.deployCompleted(deployState));
+ automationComposition.setLockState(AcmUtils.lockCompleted(deployState, automationComposition.getLockState()));
+ automationComposition.setPhase(null);
+ automationComposition.setSubState(SubState.NONE);
+ automationComposition.setPrecheck(null);
+ if (StateChangeResult.TIMEOUT.equals(automationComposition.getStateChangeResult())) {
+ automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
+ }
+ if (DeployState.DELETED.equals(automationComposition.getDeployState())) {
+ updateSync.setToBeDelete(true);
+ updateSync.setUpdated(false);
+ } else {
+ updateSync.setUpdated(true);
+ }
+ updateSync.setToBeSync(true);
+ saveAndSync(automationComposition, updateSync);
+ }
+
+ protected void savePhase(AutomationComposition automationComposition, int startPhase) {
+ automationComposition.setLastMsg(TimestampHelper.now());
+ automationComposition.setPhase(startPhase);
+ }
+
+ protected void handleTimeout(AutomationComposition automationComposition, UpdateSync updateSync) {
+ LOGGER.debug("automation composition scan: transition from state {} to {} {} not completed",
+ automationComposition.getDeployState(), automationComposition.getLockState(),
+ automationComposition.getSubState());
+
+ if (StateChangeResult.TIMEOUT.equals(automationComposition.getStateChangeResult())) {
+ LOGGER.debug("The ac instance is in timeout {}", automationComposition.getInstanceId());
+ saveAndSync(automationComposition, updateSync);
+ return;
+ }
+ var now = TimestampHelper.nowEpochMilli();
+ var lastMsg = TimestampHelper.toEpochMilli(automationComposition.getLastMsg());
+ if ((now - lastMsg) > maxStatusWaitMs) {
+ LOGGER.debug("Report timeout for the ac instance {}", automationComposition.getInstanceId());
+ automationComposition.setStateChangeResult(StateChangeResult.TIMEOUT);
+ updateSync.setUpdated(true);
+ updateSync.setToBeSync(true);
+ }
+ saveAndSync(automationComposition, updateSync);
+ }
+
+ /**
+ * Save AutomationComposition and Sync.
+ *
+ * @param automationComposition the AutomationComposition
+ * @param updateSync the update/sync information
+ */
+ public void saveAndSync(AutomationComposition automationComposition, UpdateSync updateSync) {
+ if (updateSync.isUpdated()) {
+ acProvider.updateAutomationComposition(automationComposition);
+ }
+ if (updateSync.isToBeDelete()) {
+ acProvider.deleteAutomationComposition(automationComposition.getInstanceId());
+ }
+ if (updateSync.isToBeSync()) {
+ participantSyncPublisher.sendSync(automationComposition);
+ }
+ }
+}
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AcDefinitionScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AcDefinitionScanner.java
new file mode 100644
index 000000000..d3f97269c
--- /dev/null
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AcDefinitionScanner.java
@@ -0,0 +1,166 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
+import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
+import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition;
+import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
+import org.onap.policy.clamp.models.acm.document.concepts.DocMessage;
+import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
+import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AcDefinitionScanner {
+ private static final Logger LOGGER = LoggerFactory.getLogger(AcDefinitionScanner.class);
+
+ private final long maxStatusWaitMs;
+
+ private final AcDefinitionProvider acDefinitionProvider;
+ private final ParticipantSyncPublisher participantSyncPublisher;
+
+ /**
+ * Constructor for instantiating AcDefinitionScanner.
+ *
+ * @param acDefinitionProvider the Policy Models Provider
+ * @param participantSyncPublisher the Participant Sync Publisher
+ * @param acRuntimeParameterGroup the parameters for the automation composition runtime
+ */
+ public AcDefinitionScanner(final AcDefinitionProvider acDefinitionProvider,
+ final ParticipantSyncPublisher participantSyncPublisher,
+ final AcRuntimeParameterGroup acRuntimeParameterGroup) {
+ this.acDefinitionProvider = acDefinitionProvider;
+ this.participantSyncPublisher = participantSyncPublisher;
+ this.maxStatusWaitMs = acRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs();
+ }
+
+ private UpdateSync handlePrimeAckElement(DocMessage message, AutomationCompositionDefinition acDefinition) {
+ var result = new UpdateSync();
+ if (StateChangeResult.FAILED.equals(message.getStateChangeResult())) {
+ acDefinition.setStateChangeResult(StateChangeResult.FAILED);
+ result.setUpdated(true);
+ result.setToBeSync(true);
+ }
+ for (var element : acDefinition.getElementStateMap().values()) {
+ if (message.getParticipantId().equals(element.getParticipantId())) {
+ element.setMessage(message.getMessage());
+ element.setState(message.getCompositionState());
+ result.setUpdated(true);
+ }
+ }
+ return result;
+ }
+
+ private UpdateSync handleOutProperties(DocMessage message, AutomationCompositionDefinition acDefinition) {
+ var elementOpt = acDefinition.getElementStateMap().values().stream()
+ .filter(element -> element.getNodeTemplateId().equals(message.getAcElementDefinitionId())).findFirst();
+
+ var result = new UpdateSync();
+ if (elementOpt.isPresent()) {
+ elementOpt.get().setOutProperties(message.getOutProperties());
+ result.setUpdated(true);
+ result.setToBeSync(true);
+ }
+ return result;
+ }
+
+ /**
+ * Scan Message.
+ *
+ * @param acDefinition the AutomationComposition Definition
+ * @param message the message
+ */
+ public UpdateSync scanMessage(AutomationCompositionDefinition acDefinition, DocMessage message) {
+ return switch (message.getMessageType()) {
+ case PARTICIPANT_STATUS -> handleOutProperties(message, acDefinition);
+ case PARTICIPANT_PRIME_ACK -> handlePrimeAckElement(message, acDefinition);
+ default -> {
+ LOGGER.debug("Not valid MessageType {}", message.getMessageType());
+ yield new UpdateSync();
+ }
+ };
+ }
+
+ /**
+ * Scan an AutomationComposition Definition.
+ *
+ * @param acDefinition the AutomationComposition Definition
+ * @param updateSync defines if true if the composition has to be saved or sync
+ */
+ public void scanAutomationCompositionDefinition(AutomationCompositionDefinition acDefinition,
+ UpdateSync updateSync) {
+ if (StateChangeResult.FAILED.equals(acDefinition.getStateChangeResult())) {
+ LOGGER.debug("automation definition {} scanned, OK", acDefinition.getCompositionId());
+ updateAcDefinitionState(acDefinition, updateSync);
+ return;
+ }
+
+ boolean completed = true;
+ var finalState = AcTypeState.PRIMING.equals(acDefinition.getState())
+ || AcTypeState.PRIMED.equals(acDefinition.getState()) ? AcTypeState.PRIMED : AcTypeState.COMMISSIONED;
+ for (var element : acDefinition.getElementStateMap().values()) {
+ if (!finalState.equals(element.getState())) {
+ completed = false;
+ break;
+ }
+ }
+ if (completed) {
+ acDefinition.setState(finalState);
+ acDefinition.setStateChangeResult(StateChangeResult.NO_ERROR);
+ updateSync.setUpdated(true);
+ updateSync.setToBeSync(true);
+ } else {
+ updateSync.or(handleTimeout(acDefinition));
+ }
+ updateAcDefinitionState(acDefinition, updateSync);
+ }
+
+ private UpdateSync handleTimeout(AutomationCompositionDefinition acDefinition) {
+ var result = new UpdateSync();
+ if (StateChangeResult.TIMEOUT.equals(acDefinition.getStateChangeResult())) {
+ LOGGER.debug("The ac definition is in timeout {}", acDefinition.getCompositionId());
+ return result;
+ }
+ var now = TimestampHelper.nowEpochMilli();
+ var lastMsg = TimestampHelper.toEpochMilli(acDefinition.getLastMsg());
+ if ((now - lastMsg) > maxStatusWaitMs) {
+ LOGGER.debug("Report timeout for the ac definition {}", acDefinition.getCompositionId());
+ acDefinition.setStateChangeResult(StateChangeResult.TIMEOUT);
+ result.setUpdated(true);
+ result.setToBeSync(true);
+ }
+ return result;
+ }
+
+ private void updateAcDefinitionState(AutomationCompositionDefinition acDefinition,
+ UpdateSync updateSync) {
+ if (updateSync.isUpdated()) {
+ acDefinitionProvider.updateAcDefinitionState(acDefinition);
+ }
+ if (updateSync.isToBeSync()) {
+ participantSyncPublisher.sendSync(acDefinition, null);
+ }
+ }
+}
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/PhaseScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/PhaseScanner.java
new file mode 100644
index 000000000..de56040cd
--- /dev/null
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/PhaseScanner.java
@@ -0,0 +1,124 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionDeployPublisher;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionStateChangePublisher;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
+import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
+import org.onap.policy.clamp.models.acm.concepts.DeployState;
+import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
+import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
+import org.onap.policy.clamp.models.acm.utils.AcmUtils;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class PhaseScanner extends AbstractScanner {
+
+ private final AutomationCompositionStateChangePublisher acStateChangePublisher;
+ private final AutomationCompositionDeployPublisher acDeployPublisher;
+
+
+ /**
+ * Constructor for instantiating PhaseScanner.
+ *
+ * @param acProvider the provider to use to read automation compositions from the database
+ * @param participantSyncPublisher the Participant Sync Publisher
+ * @param acStateChangePublisher the automation composition StateChange Publisher
+ * @param acDeployPublisher the automation composition Deploy Publisher
+ * @param acRuntimeParameterGroup the parameters for the automation composition runtime
+ */
+ public PhaseScanner(final AutomationCompositionProvider acProvider,
+ final ParticipantSyncPublisher participantSyncPublisher,
+ final AutomationCompositionStateChangePublisher acStateChangePublisher,
+ final AutomationCompositionDeployPublisher acDeployPublisher,
+ final AcRuntimeParameterGroup acRuntimeParameterGroup) {
+ super(acProvider, participantSyncPublisher, acRuntimeParameterGroup);
+ this.acStateChangePublisher = acStateChangePublisher;
+ this.acDeployPublisher = acDeployPublisher;
+ }
+
+ /**
+ * Scan with startPhase: DEPLOY, UNDEPLOY, LOCK and UNLOCK.
+ *
+ * @param automationComposition the AutomationComposition
+ * @param serviceTemplate the ToscaServiceTemplate
+ * @param updateSync the update/sync information
+ */
+ public void scanWithPhase(final AutomationComposition automationComposition,
+ ToscaServiceTemplate serviceTemplate, UpdateSync updateSync) {
+ var completed = true;
+ var minSpNotCompleted = 1000; // min startPhase not completed
+ var maxSpNotCompleted = 0; // max startPhase not completed
+ var defaultMin = 1000; // min startPhase
+ var defaultMax = 0; // max startPhase
+ for (var element : automationComposition.getElements().values()) {
+ var toscaNodeTemplate = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
+ .get(element.getDefinition().getName());
+ int startPhase = toscaNodeTemplate != null
+ && element.getDefinition().getVersion().equals(toscaNodeTemplate.getVersion())
+ ? ParticipantUtils.findStartPhase(toscaNodeTemplate.getProperties()) : 0;
+ defaultMin = Math.min(defaultMin, startPhase);
+ defaultMax = Math.max(defaultMax, startPhase);
+ if (AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState(),
+ element.getSubState())) {
+ completed = false;
+ minSpNotCompleted = Math.min(minSpNotCompleted, startPhase);
+ maxSpNotCompleted = Math.max(maxSpNotCompleted, startPhase);
+ }
+ }
+
+ if (completed) {
+ complete(automationComposition, updateSync);
+ } else {
+ LOGGER.debug("automation composition scan: transition state {} {} not completed",
+ automationComposition.getDeployState(), automationComposition.getLockState());
+
+ var isForward =
+ AcmUtils.isForward(automationComposition.getDeployState(), automationComposition.getLockState());
+
+ var nextSpNotCompleted = isForward ? minSpNotCompleted : maxSpNotCompleted;
+
+ if (nextSpNotCompleted != automationComposition.getPhase()) {
+ sendAutomationCompositionMsg(automationComposition, nextSpNotCompleted, updateSync);
+ } else {
+ handleTimeout(automationComposition, updateSync);
+ }
+ }
+ }
+
+ private void sendAutomationCompositionMsg(AutomationComposition automationComposition, int startPhase,
+ UpdateSync updateSync) {
+ savePhase(automationComposition, startPhase);
+ updateSync.setUpdated(true);
+ saveAndSync(automationComposition, updateSync);
+
+ if (DeployState.DEPLOYING.equals(automationComposition.getDeployState())) {
+ LOGGER.debug("retry message AutomationCompositionDeploy");
+ acDeployPublisher.send(automationComposition, startPhase, false);
+ } else {
+ LOGGER.debug("retry message AutomationCompositionStateChange");
+ acStateChangePublisher.send(automationComposition, startPhase, false);
+ }
+ }
+}
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.java
new file mode 100644
index 000000000..e35d5f03a
--- /dev/null
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.java
@@ -0,0 +1,123 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
+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.StateChangeResult;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
+import org.onap.policy.clamp.models.acm.document.concepts.DocMessage;
+import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
+import org.onap.policy.clamp.models.acm.utils.AcmUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SimpleScanner extends AbstractScanner {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SimpleScanner.class);
+
+ /**
+ * Constructor for instantiating SimpleScanner.
+ *
+ * @param acProvider the provider to use to read automation compositions from the database
+ * @param participantSyncPublisher the Participant Sync Publisher
+ * @param acRuntimeParameterGroup the parameters for the automation composition runtime
+ */
+ public SimpleScanner(final AutomationCompositionProvider acProvider,
+ final ParticipantSyncPublisher participantSyncPublisher,
+ final AcRuntimeParameterGroup acRuntimeParameterGroup) {
+ super(acProvider, participantSyncPublisher, acRuntimeParameterGroup);
+ }
+
+ /**
+ * Scan Message.
+ *
+ * @param automationComposition the AutomationComposition
+ * @param message the message
+ * @return the update/sync information
+ */
+ public UpdateSync scanMessage(AutomationComposition automationComposition, DocMessage message) {
+ return switch (message.getMessageType()) {
+ case PARTICIPANT_STATUS -> handleOutProperties(automationComposition, message);
+ case AUTOMATION_COMPOSITION_DEPLOY_ACK, AUTOMATION_COMPOSITION_STATECHANGE_ACK
+ -> handleAcStateChange(automationComposition, message);
+ default -> {
+ LOGGER.debug("Not valid MessageType {}", message.getMessageType());
+ yield new UpdateSync();
+ }
+ };
+ }
+
+ private UpdateSync handleAcStateChange(AutomationComposition automationComposition, DocMessage message) {
+ var result = new UpdateSync();
+ if (StateChangeResult.FAILED.equals(message.getStateChangeResult())) {
+ automationComposition.setStateChangeResult(StateChangeResult.FAILED);
+ result.setUpdated(true);
+ result.setToBeSync(true);
+ }
+ var element = automationComposition.getElements().get(message.getInstanceElementId());
+ if (element != null) {
+ element.setDeployState(message.getDeployState());
+ element.setLockState(message.getLockState());
+ if (element.getStage() == null) {
+ element.setSubState(SubState.NONE);
+ }
+ element.setStage(element.getStage());
+ element.setMessage(message.getMessage());
+ result.setUpdated(true);
+ }
+ return result;
+ }
+
+ private UpdateSync handleOutProperties(AutomationComposition automationComposition, DocMessage message) {
+ var element = automationComposition.getElements().get(message.getInstanceElementId());
+ var result = new UpdateSync();
+ if (element != null) {
+ element.setOutProperties(message.getOutProperties());
+ element.setOperationalState(message.getOperationalState());
+ element.setUseState(message.getUseState());
+ result.setUpdated(true);
+ result.setToBeSync(true);
+ }
+ return result;
+ }
+
+ /**
+ * Simple scan: UPDATE, PREPARE, REVIEW, MIGRATE_PRECHECKING.
+ *
+ * @param automationComposition the AutomationComposition
+ * @param updateSync the update/sync information
+ */
+ public void simpleScan(final AutomationComposition automationComposition, UpdateSync updateSync) {
+ var completed = automationComposition.getElements().values().stream()
+ .filter(element -> AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState(),
+ element.getSubState())).findFirst().isEmpty();
+
+ if (completed) {
+ complete(automationComposition, updateSync);
+ } else {
+ handleTimeout(automationComposition, updateSync);
+ }
+ }
+}
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java
new file mode 100644
index 000000000..18717cc2b
--- /dev/null
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java
@@ -0,0 +1,96 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+import java.util.Comparator;
+import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionMigrationPublisher;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
+import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
+import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
+import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
+import org.onap.policy.clamp.models.acm.utils.AcmUtils;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class StageScanner extends AbstractScanner {
+
+ private final AutomationCompositionMigrationPublisher acMigrationPublisher;
+
+ /**
+ * Constructor for instantiating StageScanner.
+ *
+ * @param acProvider the provider to use to read automation compositions from the database
+ * @param participantSyncPublisher the Participant Sync Publisher
+ * @param acMigrationPublisher the AutomationComposition Migration Publisher
+ * @param acRuntimeParameterGroup the parameters for the automation composition runtime
+ */
+ public StageScanner(final AutomationCompositionProvider acProvider,
+ final ParticipantSyncPublisher participantSyncPublisher,
+ final AutomationCompositionMigrationPublisher acMigrationPublisher,
+ final AcRuntimeParameterGroup acRuntimeParameterGroup) {
+ super(acProvider, participantSyncPublisher, acRuntimeParameterGroup);
+ this.acMigrationPublisher = acMigrationPublisher;
+ }
+
+ /**
+ * Scan with stage: MIGRATE.
+ *
+ * @param automationComposition the AutomationComposition
+ * @param serviceTemplate the ToscaServiceTemplate
+ * @param updateSync the update/sync information
+ */
+ public void scanStage(final AutomationComposition automationComposition, ToscaServiceTemplate serviceTemplate,
+ UpdateSync updateSync) {
+ var completed = true;
+ var minStageNotCompleted = 1000; // min stage not completed
+ for (var element : automationComposition.getElements().values()) {
+ if (AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState(),
+ element.getSubState())) {
+ var toscaNodeTemplate = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
+ .get(element.getDefinition().getName());
+ var stageSet = ParticipantUtils.findStageSet(toscaNodeTemplate.getProperties());
+ var minStage = stageSet.stream().min(Comparator.comparing(Integer::valueOf)).orElse(0);
+ int stage = element.getStage() != null ? element.getStage() : minStage;
+ minStageNotCompleted = Math.min(minStageNotCompleted, stage);
+ completed = false;
+ }
+ }
+
+ if (completed) {
+ complete(automationComposition, updateSync);
+ } else {
+ 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");
+ acMigrationPublisher.send(automationComposition, minStageNotCompleted);
+ } else {
+ handleTimeout(automationComposition, updateSync);
+ }
+ }
+ }
+}
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/UpdateSync.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/UpdateSync.java
new file mode 100644
index 000000000..e6daa0ca9
--- /dev/null
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/UpdateSync.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+import lombok.Data;
+
+@Data
+public class UpdateSync {
+ private boolean toBeDelete = false;
+ private boolean updated = false;
+ private boolean toBeSync = false;
+
+ /**
+ * Or operator with other update/sync information.
+ *
+ * @param updateSync the update/sync information
+ */
+ public void or(UpdateSync updateSync) {
+ this.updated |= updateSync.updated;
+ this.toBeSync |= updateSync.toBeSync;
+ this.toBeDelete |= updateSync.toBeDelete;
+ }
+}
diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java
index ddb8a3332..a555d82c6 100644
--- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java
+++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java
@@ -1,6 +1,6 @@
/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2024 Nordix Foundation.
+ * Copyright (C) 2021-2025 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,10 +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.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -34,18 +31,18 @@ import static org.onap.policy.clamp.acm.runtime.util.CommonTestData.TOSCA_SERVIC
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.UUID;
-import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.onap.policy.clamp.acm.runtime.instantiation.InstantiationUtils;
-import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionDeployPublisher;
-import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionStateChangePublisher;
-import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
-import org.onap.policy.clamp.acm.runtime.util.CommonTestData;
+import org.onap.policy.clamp.acm.runtime.supervision.scanner.AcDefinitionScanner;
+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.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.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.NodeTemplateState;
@@ -58,8 +55,6 @@ import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
class SupervisionScannerTest {
private static final String AC_JSON = "src/test/resources/rest/acm/AutomationCompositionSmoke.json";
- private static final String ELEMENT_NAME =
- "org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement";
private static final UUID compositionId = UUID.randomUUID();
@@ -71,7 +66,7 @@ class SupervisionScannerTest {
acDefinition.setStateChangeResult(stateChangeResult);
acDefinition.setCompositionId(compositionId);
acDefinition.setLastMsg(TimestampHelper.now());
- acDefinition.setServiceTemplate(serviceTemplate);
+ acDefinition.setServiceTemplate(Objects.requireNonNull(serviceTemplate));
var node = new NodeTemplateState();
node.setState(AcTypeState.PRIMING);
node.setNodeTemplateStateId(UUID.randomUUID());
@@ -83,8 +78,11 @@ class SupervisionScannerTest {
var acDefinitionProvider = mock(AcDefinitionProvider.class);
var acTypeState = acDefinition.getState();
if (AcTypeState.PRIMING.equals(acTypeState) || AcTypeState.DEPRIMING.equals(acTypeState)) {
- when(acDefinitionProvider.getAllAcDefinitionsInTransition())
- .thenReturn(List.of(Objects.requireNonNull(acDefinition)));
+ when(acDefinitionProvider.getAllAcDefinitionsInTransition()).thenReturn(List.of(acDefinition));
+ when(acDefinitionProvider.getAcDefinition(acDefinition.getCompositionId()))
+ .thenReturn(Objects.requireNonNull(acDefinition));
+ when(acDefinitionProvider.findAcDefinition(acDefinition.getCompositionId()))
+ .thenReturn(Optional.of(Objects.requireNonNull(acDefinition)));
}
when(acDefinitionProvider.getAcDefinition(compositionId)).thenReturn(acDefinition);
return acDefinitionProvider;
@@ -100,276 +98,65 @@ class SupervisionScannerTest {
}
@Test
- void testAcDefinitionPrimeFailed() {
- var acDefinitionProvider = createAcDefinitionProvider(AcTypeState.PRIMING, StateChangeResult.FAILED);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ void testAcDefinition() {
+ var acDefinitionProvider = createAcDefinitionProvider(AcTypeState.PRIMING, StateChangeResult.NO_ERROR);
+ var acDefinitionScanner = mock(AcDefinitionScanner.class);
var supervisionScanner = new SupervisionScanner(mock(AutomationCompositionProvider.class), acDefinitionProvider,
- mock(AutomationCompositionStateChangePublisher.class), mock(AutomationCompositionDeployPublisher.class),
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
+ acDefinitionScanner, mock(StageScanner.class), mock(SimpleScanner.class), mock(PhaseScanner.class));
supervisionScanner.run();
- verify(acDefinitionProvider, times(0)).updateAcDefinitionState(any(), any(), any());
- }
-
- @Test
- void testAcDefinitionPrimeTimeout() {
- var acDefinition = createAutomationCompositionDefinition(AcTypeState.PRIMING, StateChangeResult.NO_ERROR);
- var acDefinitionProvider = createAcDefinitionProvider(acDefinition);
- var participantSyncPublisher = mock(ParticipantSyncPublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
- var supervisionScanner = new SupervisionScanner(mock(AutomationCompositionProvider.class), acDefinitionProvider,
- mock(AutomationCompositionStateChangePublisher.class), mock(AutomationCompositionDeployPublisher.class),
- participantSyncPublisher, null, acRuntimeParameterGroup);
- supervisionScanner.run();
- // Ac Definition in Priming state
- verify(acDefinitionProvider, times(0)).updateAcDefinitionState(any(), any(), any());
-
- acRuntimeParameterGroup.getParticipantParameters().setMaxStatusWaitMs(-1);
- supervisionScanner = new SupervisionScanner(mock(AutomationCompositionProvider.class), acDefinitionProvider,
- mock(AutomationCompositionStateChangePublisher.class), mock(AutomationCompositionDeployPublisher.class),
- participantSyncPublisher, null, acRuntimeParameterGroup);
- supervisionScanner.run();
- // set Timeout
- verify(acDefinitionProvider).updateAcDefinitionState(acDefinition.getCompositionId(), acDefinition.getState(),
- StateChangeResult.TIMEOUT);
- verify(participantSyncPublisher).sendSync(any(AutomationCompositionDefinition.class), any());
-
- clearInvocations(acDefinitionProvider);
- clearInvocations(participantSyncPublisher);
- acDefinition.setStateChangeResult(StateChangeResult.TIMEOUT);
- supervisionScanner.run();
- // already in Timeout
- verify(acDefinitionProvider, times(0)).updateAcDefinitionState(any(), any(), any());
- verify(participantSyncPublisher, times(0)).sendSync(acDefinition, null);
-
- clearInvocations(acDefinitionProvider);
- clearInvocations(participantSyncPublisher);
- // retry by the user
- acDefinition.setStateChangeResult(StateChangeResult.NO_ERROR);
- supervisionScanner.run();
- // set Timeout
- verify(acDefinitionProvider).updateAcDefinitionState(acDefinition.getCompositionId(), acDefinition.getState(),
- StateChangeResult.TIMEOUT);
- verify(participantSyncPublisher).sendSync(any(AutomationCompositionDefinition.class), any());
-
- clearInvocations(acDefinitionProvider);
- for (var element : acDefinition.getElementStateMap().values()) {
- element.setState(AcTypeState.PRIMED);
- }
- supervisionScanner.run();
- // completed
- verify(acDefinitionProvider).updateAcDefinitionState(acDefinition.getCompositionId(), AcTypeState.PRIMED,
- StateChangeResult.NO_ERROR);
+ verify(acDefinitionScanner).scanAutomationCompositionDefinition(any(), any());
}
@Test
void testAcNotInTransitionOrFailed() {
var automationCompositionProvider = mock(AutomationCompositionProvider.class);
- var automationCompositionStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
- var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
- automationComposition.setCompositionId(compositionId);
+ automationComposition.setCompositionId(Objects.requireNonNull(compositionId));
when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
+ var stageScanner = mock(StageScanner.class);
+ var simpleScanner = mock(SimpleScanner.class);
+ var phaseScanner = mock(PhaseScanner.class);
var supervisionScanner = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
- automationCompositionStateChangePublisher, automationCompositionDeployPublisher,
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
+ mock(AcDefinitionScanner.class), stageScanner, simpleScanner, phaseScanner);
// not in transition
supervisionScanner.run();
- verify(automationCompositionProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
+ verify(stageScanner, times(0)).scanStage(any(), any(), any());
+ verify(simpleScanner, times(0)).simpleScan(any(), any());
+ verify(phaseScanner, times(0)).scanWithPhase(any(), any(), any());
automationComposition.setDeployState(DeployState.DEPLOYING);
automationComposition.setStateChangeResult(StateChangeResult.FAILED);
supervisionScanner.run();
// failed
- verify(automationCompositionProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
- }
-
- @Test
- void testAcUndeployCompleted() {
- var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
- automationComposition.setDeployState(DeployState.UNDEPLOYING);
- automationComposition.setLockState(LockState.NONE);
- automationComposition.setCompositionId(compositionId);
- var automationCompositionProvider = mock(AutomationCompositionProvider.class);
- when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
- when(automationCompositionProvider.updateAcState(any())).thenReturn(automationComposition);
-
- var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
- var automationCompositionStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
-
- var supervisionScanner = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
- automationCompositionStateChangePublisher, automationCompositionDeployPublisher,
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
- supervisionScanner.run();
-
- verify(automationCompositionProvider).updateAcState(any(AutomationComposition.class));
- }
-
- @Test
- void testAcDeleted() {
- var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
- automationComposition.setDeployState(DeployState.DELETING);
- automationComposition.setLockState(LockState.NONE);
- automationComposition.setCompositionId(compositionId);
- var automationCompositionProvider = mock(AutomationCompositionProvider.class);
- when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
-
- var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
- var automationCompositionStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
-
- var supervisionScanner = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
- automationCompositionStateChangePublisher, automationCompositionDeployPublisher,
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
- supervisionScanner.run();
-
- verify(automationCompositionProvider).deleteAutomationComposition(automationComposition.getInstanceId());
+ verify(stageScanner, times(0)).scanStage(any(), any(), any());
+ verify(simpleScanner, times(0)).simpleScan(any(), any());
+ verify(phaseScanner, times(0)).scanWithPhase(any(), any(), any());
}
@Test
void testScanner() {
- var automationCompositionProvider = mock(AutomationCompositionProvider.class);
var automationComposition = new AutomationComposition();
automationComposition.setCompositionId(compositionId);
- when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
-
- var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
- var automationCompositionStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
-
- var supervisionScanner = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
- automationCompositionStateChangePublisher, automationCompositionDeployPublisher,
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
-
- supervisionScanner.run();
- verify(automationCompositionProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
- }
-
- @Test
- void testScannerForTimeout() {
- var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
automationComposition.setDeployState(DeployState.DEPLOYING);
- automationComposition.setLockState(LockState.NONE);
- automationComposition.setPhase(0);
- automationComposition.setCompositionId(compositionId);
- for (var entry : automationComposition.getElements().entrySet()) {
- entry.getValue().setDeployState(DeployState.DEPLOYING);
- }
- // the first element is already completed
- automationComposition.getElements().entrySet().iterator().next().getValue()
- .setDeployState(DeployState.DEPLOYED);
-
var automationCompositionProvider = mock(AutomationCompositionProvider.class);
when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
- when(automationCompositionProvider.updateAcState(any())).thenReturn(automationComposition);
- var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
- var automationCompositionStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
- var participantSyncPublisher = mock(ParticipantSyncPublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
- acRuntimeParameterGroup.getParticipantParameters().setMaxStatusWaitMs(-1);
+ when(automationCompositionProvider.findAutomationComposition(automationComposition.getInstanceId()))
+ .thenReturn(Optional.of(automationComposition));
- // verify timeout scenario
- var scannerObj2 = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
- automationCompositionStateChangePublisher, automationCompositionDeployPublisher,
- participantSyncPublisher, null, acRuntimeParameterGroup);
-
- automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
- automationComposition.setLastMsg(TimestampHelper.now());
- scannerObj2.run();
- verify(automationCompositionProvider).updateAcState(any(AutomationComposition.class));
- verify(participantSyncPublisher).sendSync(any(AutomationComposition.class));
- assertEquals(StateChangeResult.TIMEOUT, automationComposition.getStateChangeResult());
-
- //already in TIMEOUT
- clearInvocations(automationCompositionProvider);
- clearInvocations(participantSyncPublisher);
- scannerObj2.run();
- verify(automationCompositionProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
- verify(participantSyncPublisher, times(0))
- .sendSync(any(AutomationComposition.class));
-
- clearInvocations(automationCompositionProvider);
- clearInvocations(participantSyncPublisher);
- for (Map.Entry<UUID, AutomationCompositionElement> entry : automationComposition.getElements().entrySet()) {
- entry.getValue().setDeployState(DeployState.DEPLOYED);
- }
- scannerObj2.run();
- verify(automationCompositionProvider).updateAcState(any(AutomationComposition.class));
- verify(participantSyncPublisher).sendSync(any(AutomationComposition.class));
- assertEquals(StateChangeResult.NO_ERROR, automationComposition.getStateChangeResult());
- }
-
- @Test
- void testSendAutomationCompositionMsgStartPhase() {
- var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
- automationComposition.setDeployState(DeployState.DEPLOYING);
- automationComposition.setLockState(LockState.NONE);
- automationComposition.setPhase(0);
- automationComposition.setCompositionId(compositionId);
- for (var element : automationComposition.getElements().values()) {
- if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
- element.setDeployState(DeployState.DEPLOYING);
- element.setLockState(LockState.NONE);
- } else {
- element.setDeployState(DeployState.DEPLOYED);
- element.setLockState(LockState.LOCKED);
- }
- }
-
- var automationCompositionProvider = mock(AutomationCompositionProvider.class);
- when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
-
- var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
- var automationCompositionStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var stageScanner = mock(StageScanner.class);
+ var simpleScanner = mock(SimpleScanner.class);
+ var phaseScanner = mock(PhaseScanner.class);
var supervisionScanner = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
- automationCompositionStateChangePublisher, automationCompositionDeployPublisher,
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
+ mock(AcDefinitionScanner.class), stageScanner, simpleScanner, phaseScanner);
supervisionScanner.run();
-
- verify(automationCompositionDeployPublisher).send(any(AutomationComposition.class), anyInt(), anyBoolean());
- }
-
- @Test
- void testStartPhaseWithNull() {
- var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
- automationComposition.setDeployState(DeployState.DEPLOYING);
- automationComposition.setLockState(LockState.NONE);
- automationComposition.setPhase(0);
- automationComposition.setLastMsg(TimestampHelper.now());
- automationComposition.setCompositionId(compositionId);
- for (var element : automationComposition.getElements().values()) {
- if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
- element.setDeployState(DeployState.DEPLOYING);
- element.getDefinition().setName("NotExistElement");
- element.setLockState(LockState.NONE);
- } else {
- element.setDeployState(DeployState.DEPLOYING);
- element.getDefinition().setVersion("0.0.0");
- element.setLockState(LockState.NONE);
- }
- }
-
- var automationCompositionProvider = mock(AutomationCompositionProvider.class);
- when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
-
- var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
-
- var supervisionScanner = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
- mock(AutomationCompositionStateChangePublisher.class), automationCompositionDeployPublisher,
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
-
- supervisionScanner.run();
-
- verify(automationCompositionDeployPublisher, times(0))
- .send(any(AutomationComposition.class), anyInt(), anyBoolean());
+ verify(stageScanner, times(0)).scanStage(any(), any(), any());
+ verify(simpleScanner, times(0)).simpleScan(any(), any());
+ verify(phaseScanner).scanWithPhase(any(), any(), any());
}
@Test
@@ -386,159 +173,62 @@ class SupervisionScannerTest {
element.setDeployState(DeployState.DEPLOYED);
element.setLockState(LockState.LOCKED);
}
- // first element is not migrated yet
- automationComposition.getElements().entrySet().iterator().next().getValue()
- .setDeployState(DeployState.MIGRATING);
var automationCompositionProvider = mock(AutomationCompositionProvider.class);
when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
- when(automationCompositionProvider.updateAcState(any())).thenReturn(automationComposition);
-
- var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
- var automationCompositionStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ when(automationCompositionProvider.findAutomationComposition(automationComposition.getInstanceId()))
+ .thenReturn(Optional.of(automationComposition));
var definitionTarget = createAutomationCompositionDefinition(AcTypeState.PRIMED, StateChangeResult.NO_ERROR);
definitionTarget.setCompositionId(compositionTargetId);
var acDefinitionProvider = createAcDefinitionProvider();
when(acDefinitionProvider.getAcDefinition(compositionTargetId)).thenReturn(definitionTarget);
+ var stageScanner = mock(StageScanner.class);
var supervisionScanner = new SupervisionScanner(automationCompositionProvider, acDefinitionProvider,
- automationCompositionStateChangePublisher, automationCompositionDeployPublisher,
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
+ mock(AcDefinitionScanner.class), stageScanner, mock(SimpleScanner.class), mock(PhaseScanner.class));
supervisionScanner.run();
- verify(automationCompositionProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
- assertEquals(DeployState.MIGRATING, automationComposition.getDeployState());
-
- // first element is migrated
- automationComposition.getElements().entrySet().iterator().next().getValue()
- .setDeployState(DeployState.DEPLOYED);
- supervisionScanner.run();
- verify(automationCompositionProvider, times(1)).updateAcState(any(AutomationComposition.class));
-
- assertEquals(DeployState.DEPLOYED, automationComposition.getDeployState());
- assertEquals(compositionTargetId, automationComposition.getCompositionId());
+ verify(stageScanner).scanStage(automationComposition, definitionTarget.getServiceTemplate(),
+ new UpdateSync());
}
@Test
- void testSendAutomationCompositionUpdate() {
- var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
- automationComposition.setLockState(LockState.LOCKED);
- automationComposition.setDeployState(DeployState.UPDATING);
- for (var element : automationComposition.getElements().values()) {
- element.setSubState(SubState.NONE);
- element.setLockState(LockState.LOCKED);
- if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
- element.setDeployState(DeployState.UPDATING);
- } else {
- element.setDeployState(DeployState.DEPLOYED);
- }
- }
- testSimpleScan(automationComposition, element -> element.setDeployState(DeployState.DEPLOYED));
- }
-
- @Test
- void testSendAutomationCompositionMigratingPrecheck() {
+ void testSendAutomationCompositionSimpleScan() {
var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
automationComposition.setLockState(LockState.LOCKED);
automationComposition.setDeployState(DeployState.DEPLOYED);
automationComposition.setSubState(SubState.MIGRATION_PRECHECKING);
- for (var element : automationComposition.getElements().values()) {
- element.setDeployState(DeployState.DEPLOYED);
- element.setSubState(SubState.NONE);
- element.setLockState(LockState.LOCKED);
- if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
- element.setSubState(SubState.MIGRATION_PRECHECKING);
- }
- }
- testSimpleScan(automationComposition, element -> element.setSubState(SubState.NONE));
- }
-
- @Test
- void testSendAutomationCompositionPrepare() {
- var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
- automationComposition.setLockState(LockState.NONE);
- automationComposition.setDeployState(DeployState.UNDEPLOYED);
- automationComposition.setSubState(SubState.PREPARING);
- for (var element : automationComposition.getElements().values()) {
- element.setDeployState(DeployState.UNDEPLOYED);
- element.setSubState(SubState.NONE);
- element.setLockState(LockState.NONE);
- if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
- element.setSubState(SubState.PREPARING);
- }
- }
- testSimpleScan(automationComposition, element -> element.setSubState(SubState.NONE));
- }
-
- @Test
- void testSendAutomationCompositionReview() {
- var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
- automationComposition.setLockState(LockState.LOCKED);
- automationComposition.setDeployState(DeployState.DEPLOYED);
- automationComposition.setSubState(SubState.REVIEWING);
- for (var element : automationComposition.getElements().values()) {
- element.setDeployState(DeployState.DEPLOYED);
- element.setSubState(SubState.NONE);
- element.setLockState(LockState.LOCKED);
- if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
- element.setSubState(SubState.REVIEWING);
- }
- }
- testSimpleScan(automationComposition, element -> element.setSubState(SubState.NONE));
- }
-
- private void testSimpleScan(AutomationComposition automationComposition, Consumer<AutomationCompositionElement> c) {
automationComposition.setLockState(LockState.NONE);
automationComposition.setCompositionId(compositionId);
automationComposition.setLastMsg(TimestampHelper.now());
var automationCompositionProvider = mock(AutomationCompositionProvider.class);
when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
+ when(automationCompositionProvider.findAutomationComposition(automationComposition.getInstanceId()))
+ .thenReturn(Optional.of(automationComposition));
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var simpleScanner = mock(SimpleScanner.class);
var supervisionScanner = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
- null, null,
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
+ mock(AcDefinitionScanner.class), mock(StageScanner.class), simpleScanner, mock(PhaseScanner.class));
supervisionScanner.run();
- verify(automationCompositionProvider, times(0)).updateAcState(any());
+ verify(simpleScanner).simpleScan(automationComposition, new UpdateSync());
- automationComposition.getElements().values().forEach(c);
+ clearInvocations(simpleScanner);
+ automationComposition.setDeployState(DeployState.UNDEPLOYED);
+ automationComposition.setSubState(SubState.PREPARING);
supervisionScanner.run();
- verify(automationCompositionProvider).updateAcState(any());
- }
+ verify(simpleScanner).simpleScan(automationComposition, new UpdateSync());
- @Test
- void testSendAutomationCompositionMsgUnlocking() {
- var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ clearInvocations(simpleScanner);
automationComposition.setDeployState(DeployState.DEPLOYED);
- automationComposition.setLockState(LockState.UNLOCKING);
- automationComposition.setCompositionId(compositionId);
- automationComposition.setPhase(0);
- for (var element : automationComposition.getElements().values()) {
- if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
- element.setDeployState(DeployState.DEPLOYED);
- element.setLockState(LockState.UNLOCKING);
- } else {
- element.setDeployState(DeployState.DEPLOYED);
- element.setLockState(LockState.UNLOCKED);
- }
- }
-
- var automationCompositionProvider = mock(AutomationCompositionProvider.class);
- when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
-
- var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
- var automationCompositionStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
- var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
-
- var supervisionScanner = new SupervisionScanner(automationCompositionProvider, createAcDefinitionProvider(),
- automationCompositionStateChangePublisher, automationCompositionDeployPublisher,
- mock(ParticipantSyncPublisher.class), null, acRuntimeParameterGroup);
-
+ automationComposition.setSubState(SubState.REVIEWING);
supervisionScanner.run();
+ verify(simpleScanner).simpleScan(automationComposition, new UpdateSync());
- verify(automationCompositionStateChangePublisher).send(any(AutomationComposition.class), anyInt(),
- anyBoolean());
+ clearInvocations(simpleScanner);
+ automationComposition.setDeployState(DeployState.UPDATING);
+ automationComposition.setSubState(SubState.NONE);
+ supervisionScanner.run();
+ verify(simpleScanner).simpleScan(automationComposition, new UpdateSync());
}
}
diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AcDefinitionScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AcDefinitionScannerTest.java
new file mode 100644
index 000000000..2e6d28222
--- /dev/null
+++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AcDefinitionScannerTest.java
@@ -0,0 +1,242 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.onap.policy.clamp.acm.runtime.util.CommonTestData.TOSCA_SERVICE_TEMPLATE_YAML;
+
+import java.util.Map;
+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.supervision.comm.ParticipantSyncPublisher;
+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.AutomationCompositionDefinition;
+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.document.concepts.DocMessage;
+import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
+import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
+import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+class AcDefinitionScannerTest {
+
+ private static final UUID COMPOSITION_ID = UUID.randomUUID();
+ private static final Map<String, Object> OUT_PROPERTIES = Map.of("key", "value");
+
+ @Test
+ void testFailScenario() {
+ var acDefinitionProvider = mock(AcDefinitionProvider.class);
+ var participantSyncPublisher = mock(ParticipantSyncPublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acDefinitionScanner = new AcDefinitionScanner(acDefinitionProvider,
+ participantSyncPublisher, acRuntimeParameterGroup);
+ var acDefinition = createAutomationCompositionDefinition(AcTypeState.PRIMING, StateChangeResult.NO_ERROR);
+ var element = acDefinition.getElementStateMap().values().iterator().next();
+ var docMessage = new DocMessage();
+ docMessage.setCompositionId(COMPOSITION_ID);
+ docMessage.setMessageType(ParticipantMessageType.PARTICIPANT_PRIME_ACK);
+ docMessage.setStateChangeResult(StateChangeResult.FAILED);
+ docMessage.setCompositionState(AcTypeState.COMMISSIONED);
+ docMessage.setParticipantId(element.getParticipantId());
+ var result = acDefinitionScanner.scanMessage(acDefinition, docMessage);
+ assertTrue(result.isUpdated());
+ assertTrue(result.isToBeSync());
+ assertEquals(docMessage.getCompositionState(),
+ acDefinition.getElementStateMap().get(element.getNodeTemplateStateId().toString()).getState());
+ assertEquals(docMessage.getStateChangeResult(), acDefinition.getStateChangeResult());
+
+ }
+
+ @Test
+ void testWithWrongData() {
+ var acDefinitionProvider = mock(AcDefinitionProvider.class);
+ var participantSyncPublisher = mock(ParticipantSyncPublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acDefinitionScanner = new AcDefinitionScanner(acDefinitionProvider,
+ participantSyncPublisher, acRuntimeParameterGroup);
+ var acDefinition = createAutomationCompositionDefinition(AcTypeState.DEPRIMING, StateChangeResult.NO_ERROR);
+ var element = acDefinition.getElementStateMap().values().iterator().next();
+ var docMessage = new DocMessage();
+ docMessage.setCompositionId(COMPOSITION_ID);
+ docMessage.setStateChangeResult(StateChangeResult.NO_ERROR);
+ docMessage.setCompositionState(AcTypeState.COMMISSIONED);
+ docMessage.setParticipantId(element.getParticipantId());
+
+ // wrong MessageType
+ docMessage.setMessageType(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
+ var result = acDefinitionScanner.scanMessage(acDefinition, docMessage);
+ assertFalse(result.isUpdated());
+ assertFalse(result.isToBeSync());
+
+ // wrong elementId in outProperties
+ docMessage.setMessageType(ParticipantMessageType.PARTICIPANT_STATUS);
+ docMessage.setOutProperties(OUT_PROPERTIES);
+ docMessage.setAcElementDefinitionId(new ToscaConceptIdentifier("wrong", "1.0.1"));
+ result = acDefinitionScanner.scanMessage(acDefinition, docMessage);
+ assertFalse(result.isUpdated());
+ assertFalse(result.isToBeSync());
+
+ // wrong participantId in StateChange
+ docMessage.setMessageType(ParticipantMessageType.PARTICIPANT_PRIME_ACK);
+ docMessage.setParticipantId(UUID.randomUUID());
+ result = acDefinitionScanner.scanMessage(acDefinition, docMessage);
+ assertFalse(result.isUpdated());
+ assertFalse(result.isToBeSync());
+ }
+
+ @Test
+ void testScanMessageStateChange() {
+ var acDefinitionProvider = mock(AcDefinitionProvider.class);
+ var participantSyncPublisher = mock(ParticipantSyncPublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acDefinitionScanner = new AcDefinitionScanner(acDefinitionProvider,
+ participantSyncPublisher, acRuntimeParameterGroup);
+ var acDefinition = createAutomationCompositionDefinition(AcTypeState.DEPRIMING, StateChangeResult.NO_ERROR);
+ var element = acDefinition.getElementStateMap().values().iterator().next();
+ var docMessage = new DocMessage();
+ docMessage.setCompositionId(COMPOSITION_ID);
+ docMessage.setMessageType(ParticipantMessageType.PARTICIPANT_PRIME_ACK);
+ docMessage.setStateChangeResult(StateChangeResult.NO_ERROR);
+ docMessage.setCompositionState(AcTypeState.COMMISSIONED);
+ docMessage.setParticipantId(element.getParticipantId());
+ var result = acDefinitionScanner.scanMessage(acDefinition, docMessage);
+ assertTrue(result.isUpdated());
+ assertFalse(result.isToBeSync());
+ assertEquals(docMessage.getCompositionState(),
+ acDefinition.getElementStateMap().get(element.getNodeTemplateStateId().toString()).getState());
+ }
+
+ @Test
+ void testScanMessageOutProperties() {
+ var acDefinitionProvider = mock(AcDefinitionProvider.class);
+ var participantSyncPublisher = mock(ParticipantSyncPublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acDefinitionScanner = new AcDefinitionScanner(acDefinitionProvider,
+ participantSyncPublisher, acRuntimeParameterGroup);
+ var acDefinition = createAutomationCompositionDefinition(AcTypeState.DEPRIMING, StateChangeResult.NO_ERROR);
+ var element = acDefinition.getElementStateMap().values().iterator().next();
+ var docMessage = new DocMessage();
+ docMessage.setCompositionId(COMPOSITION_ID);
+ docMessage.setMessageType(ParticipantMessageType.PARTICIPANT_STATUS);
+ docMessage.setOutProperties(OUT_PROPERTIES);
+ docMessage.setAcElementDefinitionId(element.getNodeTemplateId());
+ var result = acDefinitionScanner.scanMessage(acDefinition, docMessage);
+ assertTrue(result.isUpdated());
+ assertTrue(result.isToBeSync());
+ assertEquals(docMessage.getOutProperties(),
+ acDefinition.getElementStateMap().get(element.getNodeTemplateStateId().toString()).getOutProperties());
+ }
+
+ private AutomationCompositionDefinition createAutomationCompositionDefinition(AcTypeState acTypeState,
+ StateChangeResult stateChangeResult) {
+ var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+ var acDefinition = new AutomationCompositionDefinition();
+ acDefinition.setState(acTypeState);
+ acDefinition.setStateChangeResult(stateChangeResult);
+ acDefinition.setCompositionId(COMPOSITION_ID);
+ acDefinition.setLastMsg(TimestampHelper.now());
+ acDefinition.setServiceTemplate(serviceTemplate);
+ var node = new NodeTemplateState();
+ node.setState(acTypeState);
+ node.setNodeTemplateStateId(UUID.randomUUID());
+ node.setParticipantId(UUID.randomUUID());
+ node.setNodeTemplateId(new ToscaConceptIdentifier("name", "1.0.0"));
+ acDefinition.setElementStateMap(Map.of(node.getNodeTemplateStateId().toString(), node));
+ return acDefinition;
+ }
+
+ @Test
+ void testAcDefinitionPrimeFailed() {
+ var acDefinitionProvider = mock(AcDefinitionProvider.class);
+ var participantSyncPublisher = mock(ParticipantSyncPublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acDefinitionScanner = new AcDefinitionScanner(acDefinitionProvider,
+ participantSyncPublisher, acRuntimeParameterGroup);
+ var acDefinition = createAutomationCompositionDefinition(AcTypeState.PRIMING, StateChangeResult.FAILED);
+ acDefinitionScanner.scanAutomationCompositionDefinition(acDefinition, new UpdateSync());
+ verify(acDefinitionProvider, times(0)).updateAcDefinitionState(any());
+ verify(participantSyncPublisher, times(0)).sendSync(any(), any());
+ }
+
+ @Test
+ void testAcDefinitionPrimeTimeout() {
+ var acDefinitionProvider = mock(AcDefinitionProvider.class);
+ var participantSyncPublisher = mock(ParticipantSyncPublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acDefinitionScanner = new AcDefinitionScanner(acDefinitionProvider, participantSyncPublisher,
+ acRuntimeParameterGroup);
+ var acDefinition = createAutomationCompositionDefinition(AcTypeState.DEPRIMING, StateChangeResult.NO_ERROR);
+ acDefinitionScanner.scanAutomationCompositionDefinition(acDefinition, new UpdateSync());
+ // Ac Definition in Depriming state
+ verify(acDefinitionProvider, times(0)).updateAcDefinitionState(any());
+ verify(participantSyncPublisher, times(0)).sendSync(any(), any());
+
+ acDefinition.setState(AcTypeState.PRIMING);
+ acDefinitionScanner.scanAutomationCompositionDefinition(acDefinition, new UpdateSync());
+ // Ac Definition in Priming state
+ verify(acDefinitionProvider, times(0)).updateAcDefinitionState(any());
+ verify(participantSyncPublisher, times(0)).sendSync(any(), any());
+
+ acRuntimeParameterGroup.getParticipantParameters().setMaxStatusWaitMs(-1);
+ acDefinitionScanner = new AcDefinitionScanner(acDefinitionProvider, participantSyncPublisher,
+ acRuntimeParameterGroup);
+ acDefinition = createAutomationCompositionDefinition(AcTypeState.PRIMING, StateChangeResult.NO_ERROR);
+ acDefinitionScanner.scanAutomationCompositionDefinition(acDefinition, new UpdateSync());
+ // set Timeout
+ verify(acDefinitionProvider).updateAcDefinitionState(acDefinition);
+ verify(participantSyncPublisher).sendSync(any(AutomationCompositionDefinition.class), any());
+
+ clearInvocations(acDefinitionProvider);
+ clearInvocations(participantSyncPublisher);
+ acDefinition = createAutomationCompositionDefinition(AcTypeState.PRIMING, StateChangeResult.TIMEOUT);
+ acDefinitionScanner.scanAutomationCompositionDefinition(acDefinition, new UpdateSync());
+ // already in Timeout
+ verify(acDefinitionProvider, times(0)).updateAcDefinitionState(any());
+ verify(participantSyncPublisher, times(0)).sendSync(any(), any());
+
+ clearInvocations(acDefinitionProvider);
+ clearInvocations(participantSyncPublisher);
+ // retry by the user
+ acDefinition.setStateChangeResult(StateChangeResult.NO_ERROR);
+ acDefinitionScanner.scanAutomationCompositionDefinition(acDefinition, new UpdateSync());
+ // set Timeout
+ verify(acDefinitionProvider).updateAcDefinitionState(acDefinition);
+ verify(participantSyncPublisher).sendSync(any(AutomationCompositionDefinition.class), any());
+
+ clearInvocations(acDefinitionProvider);
+ for (var element : acDefinition.getElementStateMap().values()) {
+ element.setState(AcTypeState.PRIMED);
+ }
+ acDefinitionScanner.scanAutomationCompositionDefinition(acDefinition, new UpdateSync());
+ // completed
+ verify(acDefinitionProvider).updateAcDefinitionState(acDefinition);
+ }
+}
diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/PhaseScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/PhaseScannerTest.java
new file mode 100644
index 000000000..6958fe410
--- /dev/null
+++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/PhaseScannerTest.java
@@ -0,0 +1,254 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.onap.policy.clamp.acm.runtime.util.CommonTestData.TOSCA_SERVICE_TEMPLATE_YAML;
+
+import java.util.Map;
+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.supervision.comm.AutomationCompositionDeployPublisher;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionStateChangePublisher;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
+import org.onap.policy.clamp.acm.runtime.util.CommonTestData;
+import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
+import org.onap.policy.clamp.models.acm.concepts.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.StateChangeResult;
+import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
+import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
+
+class PhaseScannerTest {
+
+ private static final String AC_JSON = "src/test/resources/rest/acm/AutomationCompositionSmoke.json";
+ private static final UUID COMPOSITION_ID = UUID.randomUUID();
+ private static final UUID INSTANCE_ID = UUID.randomUUID();
+ private static final String ELEMENT_NAME =
+ "org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement";
+
+ @Test
+ void testAcUndeployCompleted() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setInstanceId(INSTANCE_ID);
+ automationComposition.setDeployState(DeployState.UNDEPLOYING);
+ automationComposition.setLockState(LockState.NONE);
+ automationComposition.setCompositionId(COMPOSITION_ID);
+ var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+ when(automationCompositionProvider.updateAutomationComposition(any())).thenReturn(automationComposition);
+
+ var acDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
+ var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+
+ var phaseScanner = new PhaseScanner(automationCompositionProvider, mock(ParticipantSyncPublisher.class),
+ acStateChangePublisher, acDeployPublisher, acRuntimeParameterGroup);
+ var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+ phaseScanner.scanWithPhase(automationComposition, serviceTemplate, new UpdateSync());
+
+ verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
+ }
+
+ @Test
+ void testAcDeleted() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setInstanceId(INSTANCE_ID);
+ automationComposition.setDeployState(DeployState.DELETING);
+ automationComposition.setLockState(LockState.NONE);
+ automationComposition.setCompositionId(COMPOSITION_ID);
+ var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+ var acDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
+ var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+
+ var phaseScanner = new PhaseScanner(automationCompositionProvider, mock(ParticipantSyncPublisher.class),
+ acStateChangePublisher, acDeployPublisher, acRuntimeParameterGroup);
+ var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+ phaseScanner.scanWithPhase(automationComposition, serviceTemplate, new UpdateSync());
+
+ verify(automationCompositionProvider).deleteAutomationComposition(automationComposition.getInstanceId());
+ }
+
+ @Test
+ void testScannerForTimeout() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setInstanceId(INSTANCE_ID);
+ automationComposition.setDeployState(DeployState.DEPLOYING);
+ automationComposition.setLockState(LockState.NONE);
+ automationComposition.setPhase(0);
+ automationComposition.setCompositionId(COMPOSITION_ID);
+ for (var entry : automationComposition.getElements().entrySet()) {
+ entry.getValue().setDeployState(DeployState.DEPLOYING);
+ }
+ // the first element is already completed
+ automationComposition.getElements().entrySet().iterator().next().getValue()
+ .setDeployState(DeployState.DEPLOYED);
+
+ var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+ when(automationCompositionProvider.updateAutomationComposition(any())).thenReturn(automationComposition);
+ var acDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
+ var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
+ var participantSyncPublisher = mock(ParticipantSyncPublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ acRuntimeParameterGroup.getParticipantParameters().setMaxStatusWaitMs(-1);
+
+ // verify timeout scenario
+ var phaseScanner = new PhaseScanner(automationCompositionProvider, participantSyncPublisher,
+ acStateChangePublisher, acDeployPublisher, acRuntimeParameterGroup);
+
+ automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
+ automationComposition.setLastMsg(TimestampHelper.now());
+ var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+ phaseScanner.scanWithPhase(automationComposition, serviceTemplate, new UpdateSync());
+ verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
+ verify(participantSyncPublisher).sendSync(any(AutomationComposition.class));
+ assertEquals(StateChangeResult.TIMEOUT, automationComposition.getStateChangeResult());
+
+ //already in TIMEOUT
+ clearInvocations(automationCompositionProvider);
+ clearInvocations(participantSyncPublisher);
+ phaseScanner.scanWithPhase(automationComposition, serviceTemplate, new UpdateSync());
+ verify(automationCompositionProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
+ verify(participantSyncPublisher, times(0))
+ .sendSync(any(AutomationComposition.class));
+
+ clearInvocations(automationCompositionProvider);
+ clearInvocations(participantSyncPublisher);
+ for (Map.Entry<UUID, AutomationCompositionElement> entry : automationComposition.getElements().entrySet()) {
+ entry.getValue().setDeployState(DeployState.DEPLOYED);
+ }
+ phaseScanner.scanWithPhase(automationComposition, serviceTemplate, new UpdateSync());
+ verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
+ verify(participantSyncPublisher).sendSync(any(AutomationComposition.class));
+ assertEquals(StateChangeResult.NO_ERROR, automationComposition.getStateChangeResult());
+ }
+
+ @Test
+ void testSendAutomationCompositionMsgStartPhase() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setInstanceId(INSTANCE_ID);
+ automationComposition.setDeployState(DeployState.DEPLOYING);
+ automationComposition.setLockState(LockState.NONE);
+ automationComposition.setPhase(0);
+ automationComposition.setCompositionId(COMPOSITION_ID);
+ for (var element : automationComposition.getElements().values()) {
+ if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+ element.setDeployState(DeployState.DEPLOYING);
+ element.setLockState(LockState.NONE);
+ } else {
+ element.setDeployState(DeployState.DEPLOYED);
+ element.setLockState(LockState.LOCKED);
+ }
+ }
+
+ var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+ var acDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
+ var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+
+ var phaseScanner = new PhaseScanner(automationCompositionProvider, mock(ParticipantSyncPublisher.class),
+ acStateChangePublisher, acDeployPublisher, acRuntimeParameterGroup);
+
+ var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+ phaseScanner.scanWithPhase(automationComposition, serviceTemplate, new UpdateSync());
+
+ verify(acDeployPublisher).send(any(AutomationComposition.class), anyInt(), anyBoolean());
+ }
+
+ @Test
+ void testStartPhaseWithNull() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setInstanceId(INSTANCE_ID);
+ automationComposition.setDeployState(DeployState.DEPLOYING);
+ automationComposition.setLockState(LockState.NONE);
+ automationComposition.setPhase(0);
+ automationComposition.setLastMsg(TimestampHelper.now());
+ automationComposition.setCompositionId(COMPOSITION_ID);
+ for (var element : automationComposition.getElements().values()) {
+ if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+ element.setDeployState(DeployState.DEPLOYING);
+ element.getDefinition().setName("NotExistElement");
+ element.setLockState(LockState.NONE);
+ } else {
+ element.setDeployState(DeployState.DEPLOYING);
+ element.getDefinition().setVersion("0.0.0");
+ element.setLockState(LockState.NONE);
+ }
+ }
+
+ var acProvider = mock(AutomationCompositionProvider.class);
+ var acDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+
+ var phaseScanner = new PhaseScanner(acProvider, mock(ParticipantSyncPublisher.class),
+ mock(AutomationCompositionStateChangePublisher.class), acDeployPublisher,
+ acRuntimeParameterGroup);
+
+ var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+ phaseScanner.scanWithPhase(automationComposition, serviceTemplate, new UpdateSync());
+
+ verify(acDeployPublisher, times(0))
+ .send(any(AutomationComposition.class), anyInt(), anyBoolean());
+ }
+
+ @Test
+ void testSendAutomationCompositionMsgUnlocking() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setInstanceId(INSTANCE_ID);
+ automationComposition.setDeployState(DeployState.DEPLOYED);
+ automationComposition.setLockState(LockState.UNLOCKING);
+ automationComposition.setCompositionId(COMPOSITION_ID);
+ automationComposition.setPhase(0);
+ for (var element : automationComposition.getElements().values()) {
+ if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+ element.setDeployState(DeployState.DEPLOYED);
+ element.setLockState(LockState.UNLOCKING);
+ } else {
+ element.setDeployState(DeployState.DEPLOYED);
+ element.setLockState(LockState.UNLOCKED);
+ }
+ }
+
+ var acProvider = mock(AutomationCompositionProvider.class);
+ var acDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
+ var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+
+ var phaseScanner = new PhaseScanner(acProvider, mock(ParticipantSyncPublisher.class),
+ acStateChangePublisher, acDeployPublisher, acRuntimeParameterGroup);
+
+ var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+ phaseScanner.scanWithPhase(automationComposition, serviceTemplate, new UpdateSync());
+
+ verify(acStateChangePublisher).send(any(AutomationComposition.class), anyInt(),
+ anyBoolean());
+ }
+}
diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScannerTest.java
new file mode 100644
index 000000000..b422f13d3
--- /dev/null
+++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScannerTest.java
@@ -0,0 +1,246 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Consumer;
+import org.junit.jupiter.api.Test;
+import org.onap.policy.clamp.acm.runtime.instantiation.InstantiationUtils;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
+import org.onap.policy.clamp.acm.runtime.util.CommonTestData;
+import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
+import org.onap.policy.clamp.models.acm.concepts.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.StateChangeResult;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
+import org.onap.policy.clamp.models.acm.document.concepts.DocMessage;
+import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
+import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
+import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
+
+class SimpleScannerTest {
+
+ private static final String AC_JSON = "src/test/resources/rest/acm/AutomationCompositionSmoke.json";
+ private static final String ELEMENT_NAME =
+ "org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement";
+
+ private static final UUID COMPOSITION_ID = UUID.randomUUID();
+ private static final UUID INSTANCE_ID = UUID.randomUUID();
+ private static final Map<String, Object> OUT_PROPERTIES = Map.of("key", "value");
+
+ @Test
+ void testFailScenario() {
+ var automationComposition = createDeploying();
+ var elementId = automationComposition.getElements().values().iterator().next().getId();
+ var docMessage = new DocMessage();
+ docMessage.setMessageType(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY_ACK);
+ docMessage.setInstanceId(INSTANCE_ID);
+ docMessage.setInstanceElementId(elementId);
+ docMessage.setDeployState(DeployState.UNDEPLOYED);
+ docMessage.setLockState(LockState.NONE);
+ docMessage.setStateChangeResult(StateChangeResult.FAILED);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acProvider = mock(AutomationCompositionProvider.class);
+ var simpleScanner = new SimpleScanner(acProvider, mock(ParticipantSyncPublisher.class),
+ acRuntimeParameterGroup);
+ var result = simpleScanner.scanMessage(automationComposition, docMessage);
+ assertTrue(result.isUpdated());
+ assertTrue(result.isToBeSync());
+ assertEquals(docMessage.getDeployState(),
+ automationComposition.getElements().get(elementId).getDeployState());
+ assertEquals(docMessage.getLockState(),
+ automationComposition.getElements().get(elementId).getLockState());
+ assertEquals(docMessage.getStateChangeResult(), automationComposition.getStateChangeResult());
+ }
+
+ @Test
+ void testWithWrongData() {
+ var automationComposition = createDeploying();
+ var elementId = automationComposition.getElements().values().iterator().next().getId();
+ var docMessage = new DocMessage();
+ docMessage.setInstanceId(INSTANCE_ID);
+ docMessage.setInstanceElementId(elementId);
+ docMessage.setStateChangeResult(StateChangeResult.NO_ERROR);
+ docMessage.setDeployState(DeployState.DEPLOYED);
+ docMessage.setLockState(LockState.LOCKED);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acProvider = mock(AutomationCompositionProvider.class);
+ var simpleScanner = new SimpleScanner(acProvider, mock(ParticipantSyncPublisher.class),
+ acRuntimeParameterGroup);
+
+ // wrong MessageType
+ docMessage.setMessageType(ParticipantMessageType.PARTICIPANT_PRIME_ACK);
+ var result = simpleScanner.scanMessage(automationComposition, docMessage);
+ assertFalse(result.isUpdated());
+ assertFalse(result.isToBeSync());
+
+ // wrong elementId in outProperties
+ docMessage.setMessageType(ParticipantMessageType.PARTICIPANT_STATUS);
+ docMessage.setInstanceElementId(UUID.randomUUID());
+ docMessage.setOutProperties(OUT_PROPERTIES);
+ result = simpleScanner.scanMessage(automationComposition, docMessage);
+ assertFalse(result.isUpdated());
+ assertFalse(result.isToBeSync());
+
+ // wrong elementId in StateChange
+ docMessage.setMessageType(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
+ result = simpleScanner.scanMessage(automationComposition, docMessage);
+ assertFalse(result.isUpdated());
+ assertFalse(result.isToBeSync());
+ }
+
+ @Test
+ void testScanMessageOutProperties() {
+ var automationComposition = createDeploying();
+ var elementId = automationComposition.getElements().values().iterator().next().getId();
+ var docMessage = new DocMessage();
+ docMessage.setMessageType(ParticipantMessageType.PARTICIPANT_STATUS);
+ docMessage.setInstanceId(INSTANCE_ID);
+ docMessage.setInstanceElementId(elementId);
+ docMessage.setOutProperties(Map.of("key", "value"));
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acProvider = mock(AutomationCompositionProvider.class);
+ var simpleScanner = new SimpleScanner(acProvider, mock(ParticipantSyncPublisher.class),
+ acRuntimeParameterGroup);
+ var result = simpleScanner.scanMessage(automationComposition, docMessage);
+ assertTrue(result.isUpdated());
+ assertTrue(result.isToBeSync());
+ assertEquals(docMessage.getOutProperties(),
+ automationComposition.getElements().get(elementId).getOutProperties());
+ }
+
+ @Test
+ void testScanMessageStateChange() {
+ var automationComposition = createDeploying();
+ var elementId = automationComposition.getElements().values().iterator().next().getId();
+ var docMessage = new DocMessage();
+ docMessage.setMessageType(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY_ACK);
+ docMessage.setStateChangeResult(StateChangeResult.NO_ERROR);
+ docMessage.setInstanceId(INSTANCE_ID);
+ docMessage.setInstanceElementId(elementId);
+ docMessage.setDeployState(DeployState.DEPLOYED);
+ docMessage.setLockState(LockState.LOCKED);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var acProvider = mock(AutomationCompositionProvider.class);
+ var simpleScanner = new SimpleScanner(acProvider, mock(ParticipantSyncPublisher.class),
+ acRuntimeParameterGroup);
+ var result = simpleScanner.scanMessage(automationComposition, docMessage);
+ assertTrue(result.isUpdated());
+ assertFalse(result.isToBeSync());
+ assertEquals(docMessage.getDeployState(),
+ automationComposition.getElements().get(elementId).getDeployState());
+ assertEquals(docMessage.getLockState(),
+ automationComposition.getElements().get(elementId).getLockState());
+ }
+
+ private AutomationComposition createDeploying() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setInstanceId(INSTANCE_ID);
+ automationComposition.setDeployState(DeployState.DEPLOYING);
+ automationComposition.setLockState(LockState.NONE);
+ automationComposition.setPhase(0);
+ automationComposition.setLastMsg(TimestampHelper.now());
+ automationComposition.setCompositionId(COMPOSITION_ID);
+ for (var element : automationComposition.getElements().values()) {
+ element.setDeployState(DeployState.DEPLOYING);
+ element.setLockState(LockState.NONE);
+ }
+ return automationComposition;
+ }
+
+ @Test
+ void testSendAutomationCompositionMigratingPrecheck() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setLockState(LockState.LOCKED);
+ automationComposition.setDeployState(DeployState.DEPLOYED);
+ automationComposition.setSubState(SubState.MIGRATION_PRECHECKING);
+ for (var element : automationComposition.getElements().values()) {
+ element.setDeployState(DeployState.DEPLOYED);
+ element.setSubState(SubState.NONE);
+ element.setLockState(LockState.LOCKED);
+ if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+ element.setSubState(SubState.MIGRATION_PRECHECKING);
+ }
+ }
+ testSimpleScan(automationComposition, element -> element.setSubState(SubState.NONE));
+ }
+
+ @Test
+ void testSendAutomationCompositionPrepare() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setLockState(LockState.NONE);
+ automationComposition.setDeployState(DeployState.UNDEPLOYED);
+ automationComposition.setSubState(SubState.PREPARING);
+ for (var element : automationComposition.getElements().values()) {
+ element.setDeployState(DeployState.UNDEPLOYED);
+ element.setSubState(SubState.NONE);
+ element.setLockState(LockState.NONE);
+ if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+ element.setSubState(SubState.PREPARING);
+ }
+ }
+ testSimpleScan(automationComposition, element -> element.setSubState(SubState.NONE));
+ }
+
+ @Test
+ void testSendAutomationCompositionUpdate() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setLockState(LockState.LOCKED);
+ automationComposition.setDeployState(DeployState.UPDATING);
+ for (var element : automationComposition.getElements().values()) {
+ element.setSubState(SubState.NONE);
+ element.setLockState(LockState.LOCKED);
+ if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
+ element.setDeployState(DeployState.UPDATING);
+ } else {
+ element.setDeployState(DeployState.DEPLOYED);
+ }
+ }
+ testSimpleScan(automationComposition, element -> element.setDeployState(DeployState.DEPLOYED));
+ }
+
+ private void testSimpleScan(AutomationComposition automationComposition, Consumer<AutomationCompositionElement> c) {
+ automationComposition.setInstanceId(INSTANCE_ID);
+ automationComposition.setLockState(LockState.NONE);
+ automationComposition.setCompositionId(COMPOSITION_ID);
+ automationComposition.setLastMsg(TimestampHelper.now());
+ var acProvider = mock(AutomationCompositionProvider.class);
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var simpleScanner = new SimpleScanner(acProvider, mock(ParticipantSyncPublisher.class),
+ acRuntimeParameterGroup);
+ simpleScanner.simpleScan(automationComposition, new UpdateSync());
+ verify(acProvider, times(0)).updateAutomationComposition(any());
+
+ automationComposition.getElements().values().forEach(c);
+ simpleScanner.simpleScan(automationComposition, new UpdateSync());
+ verify(acProvider).updateAutomationComposition(any());
+ }
+}
diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScannerTest.java
new file mode 100644
index 000000000..1cff25fe6
--- /dev/null
+++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScannerTest.java
@@ -0,0 +1,100 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.runtime.supervision.scanner;
+
+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;
+import static org.mockito.Mockito.times;
+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.UUID;
+import org.junit.jupiter.api.Test;
+import org.onap.policy.clamp.acm.runtime.instantiation.InstantiationUtils;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionMigrationPublisher;
+import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher;
+import org.onap.policy.clamp.acm.runtime.util.CommonTestData;
+import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
+import org.onap.policy.clamp.models.acm.concepts.DeployState;
+import org.onap.policy.clamp.models.acm.concepts.LockState;
+import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
+import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
+
+class StageScannerTest {
+ private static final String AC_JSON = "src/test/resources/rest/acm/AutomationCompositionSmoke.json";
+ private static final UUID COMPOSITION_ID = UUID.randomUUID();
+
+ @Test
+ void testSendAutomationCompositionMigrate() {
+ var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+ automationComposition.setInstanceId(UUID.randomUUID());
+ automationComposition.setDeployState(DeployState.MIGRATING);
+ automationComposition.setCompositionId(COMPOSITION_ID);
+ var compositionTargetId = UUID.randomUUID();
+ automationComposition.setCompositionTargetId(compositionTargetId);
+ automationComposition.setLockState(LockState.LOCKED);
+ automationComposition.setLastMsg(TimestampHelper.now());
+ automationComposition.setPhase(0);
+ for (var element : automationComposition.getElements().values()) {
+ element.setDeployState(DeployState.DEPLOYED);
+ element.setLockState(LockState.LOCKED);
+ }
+ // first element is not migrated yet
+ var element = automationComposition.getElements().entrySet().iterator().next().getValue();
+ element.setDeployState(DeployState.MIGRATING);
+
+ var acProvider = mock(AutomationCompositionProvider.class);
+ when(acProvider.updateAutomationComposition(any())).thenReturn(automationComposition);
+
+ var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+ var supervisionScanner = new StageScanner(acProvider, mock(ParticipantSyncPublisher.class),
+ mock(AutomationCompositionMigrationPublisher.class), acRuntimeParameterGroup);
+
+ var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+ supervisionScanner.scanStage(automationComposition, serviceTemplate, new UpdateSync());
+ verify(acProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
+ assertEquals(DeployState.MIGRATING, automationComposition.getDeployState());
+
+ // send message for next stage
+ clearInvocations(acProvider);
+ var toscaNodeTemplate = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
+ .get(element.getDefinition().getName());
+ toscaNodeTemplate.setProperties(Map.of("stage", List.of(1)));
+
+ supervisionScanner.scanStage(automationComposition, serviceTemplate, new UpdateSync());
+ verify(acProvider).updateAutomationComposition(any(AutomationComposition.class));
+ assertEquals(DeployState.MIGRATING, automationComposition.getDeployState());
+
+ // first element is migrated
+ clearInvocations(acProvider);
+ element.setDeployState(DeployState.DEPLOYED);
+ supervisionScanner.scanStage(automationComposition, serviceTemplate, new UpdateSync());
+ verify(acProvider).updateAutomationComposition(any(AutomationComposition.class));
+
+ assertEquals(DeployState.DEPLOYED, automationComposition.getDeployState());
+ assertEquals(compositionTargetId, automationComposition.getCompositionId());
+ }
+}