diff options
author | 2025-02-14 11:35:17 +0000 | |
---|---|---|
committer | 2025-02-14 15:24:04 +0000 | |
commit | c3c94eef2854ec8d552ac3059e693cbc352737a5 (patch) | |
tree | e175dc5026748d2f1187e357ba774ff0aaf3e7fa /runtime-acm | |
parent | e3c94609eba45f2c4e10ab52024709bd9a216aca (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')
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()); + } +} |