diff options
author | FrancescoFioraEst <francesco.fiora@est.tech> | 2024-01-19 14:29:25 +0000 |
---|---|---|
committer | FrancescoFioraEst <francesco.fiora@est.tech> | 2024-07-24 16:19:31 +0100 |
commit | 37195c69c99b5987f037a018859b4421cbcbadf2 (patch) | |
tree | 66c18541aca4cb1598701ee2a289d1e390cb6553 | |
parent | a2f6493f89eff78a78d8ba2aa34f6080d6ab8830 (diff) |
Add support for Prepare, Review and Migrate pre-check in intermediary
Add support for Prepare, Review and Migrate pre-check
in ACM intermediary.
Issue-ID: POLICY-5080
Change-Id: I08045a8eb01dcea6492aac12b7a8021a47ae19e8
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
13 files changed, 818 insertions, 102 deletions
diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPrepareListener.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPrepareListener.java new file mode 100644 index 000000000..0f6d2c09f --- /dev/null +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPrepareListener.java @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.acm.participant.intermediary.comm; + +import org.onap.policy.clamp.acm.participant.intermediary.handler.ParticipantHandler; +import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare; +import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType; +import org.springframework.stereotype.Component; + +@Component +public class AcPrepareListener extends ParticipantListener<AutomationCompositionPrepare> { + + /** + * Constructs the object. + * + * @param participantHandler the handler for managing the state of the participant + */ + public AcPrepareListener(final ParticipantHandler participantHandler) { + super(AutomationCompositionPrepare.class, participantHandler, + participantHandler::handleAutomationCompositionPrepare); + } + + + @Override + public String getType() { + return ParticipantMessageType.AUTOMATION_COMPOSITION_PREPARE.name(); + } +} diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcLockHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcLockHandler.java index 89daa5fd2..95613cc9e 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcLockHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcLockHandler.java @@ -27,6 +27,7 @@ import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; import org.onap.policy.clamp.models.acm.concepts.LockState; import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils; +import org.onap.policy.clamp.models.acm.concepts.SubState; import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,6 +80,7 @@ public class AcLockHandler { int startPhase = ParticipantUtils.findStartPhase(compositionInProperties); if (startPhaseMsg.equals(startPhase)) { element.setLockState(LockState.LOCKING); + element.setSubState(SubState.NONE); var compositionElement = cacheProvider.createCompositionElementDto( automationComposition.getCompositionId(), element, compositionInProperties); var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(), @@ -99,6 +101,7 @@ public class AcLockHandler { int startPhase = ParticipantUtils.findStartPhase(compositionInProperties); if (startPhaseMsg.equals(startPhase)) { element.setLockState(LockState.UNLOCKING); + element.setSubState(SubState.NONE); var compositionElement = cacheProvider.createCompositionElementDto( automationComposition.getCompositionId(), element, compositionInProperties); var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(), diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java new file mode 100644 index 000000000..3044080af --- /dev/null +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java @@ -0,0 +1,152 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.acm.participant.intermediary.handler; + +import java.util.List; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto; +import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy; +import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; +import org.onap.policy.clamp.models.acm.concepts.DeployState; +import org.onap.policy.clamp.models.acm.concepts.SubState; +import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration; +import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare; +import org.onap.policy.clamp.models.acm.utils.AcmUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class AcSubStateHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AcSubStateHandler.class); + + private final CacheProvider cacheProvider; + private final ThreadHandler listener; + + /** + * Handles AutomationComposition Migration Precheck. + * + * @param migrationMsg the AutomationCompositionMigration + */ + public void handleAcMigrationPrecheck(AutomationCompositionMigration migrationMsg) { + if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) { + return; + } + + var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId()); + if (automationComposition == null) { + LOGGER.debug("Automation composition {} does not use this participant", + migrationMsg.getAutomationCompositionId()); + return; + } + automationComposition.setSubState(SubState.MIGRATION_PRECHECKING); + for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) { + if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) { + + callParticipantMigratePrecheck(migrationMsg.getMessageId(), participantDeploy.getAcElementList(), + automationComposition, migrationMsg.getCompositionTargetId()); + } + } + } + + private void callParticipantMigratePrecheck(UUID messageId, List<AcElementDeploy> acElements, + AutomationComposition automationComposition, UUID compositionTargetId) { + var compositionElementMap = cacheProvider.getCompositionElementDtoMap(automationComposition); + var instanceElementMap = cacheProvider.getInstanceElementDtoMap(automationComposition); + var acElementList = automationComposition.getElements(); + for (var acElement : acElements) { + var element = acElementList.get(acElement.getId()); + element.setSubState(SubState.MIGRATION_PRECHECKING); + } + var acCopyMigrateTo = new AutomationComposition(automationComposition); + acElementList = acCopyMigrateTo.getElements(); + for (var acElement : acElements) { + var element = acElementList.get(acElement.getId()); + AcmUtils.recursiveMerge(element.getProperties(), acElement.getProperties()); + element.setDefinition(acElement.getDefinition()); + } + + var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(acCopyMigrateTo, + compositionTargetId); + var instanceElementMigrateMap = cacheProvider.getInstanceElementDtoMap(acCopyMigrateTo); + + for (var acElement : acElements) { + listener.migratePrecheck(messageId, compositionElementMap.get(acElement.getId()), + compositionElementTargetMap.get(acElement.getId()), + instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId())); + } + } + + /** + * Handle AutomationComposition Prepare message. + * + * @param acPrepareMsg the AutomationCompositionPrepare message + */ + public void handleAcPrepare(AutomationCompositionPrepare acPrepareMsg) { + if (acPrepareMsg.isPreDeploy()) { + for (var participantPrepare : acPrepareMsg.getParticipantList()) { + if (cacheProvider.getParticipantId().equals(participantPrepare.getParticipantId())) { + cacheProvider.initializeAutomationComposition(acPrepareMsg.getCompositionId(), + acPrepareMsg.getAutomationCompositionId(), participantPrepare, DeployState.UNDEPLOYED, + SubState.PREPARING); + callParticipanPrepare(acPrepareMsg.getMessageId(), participantPrepare.getAcElementList(), + acPrepareMsg.getAutomationCompositionId()); + } + } + } else { + var automationComposition = + cacheProvider.getAutomationComposition(acPrepareMsg.getAutomationCompositionId()); + automationComposition.setSubState(SubState.REVIEWING); + callParticipanReview(acPrepareMsg.getMessageId(), automationComposition); + } + } + + private void callParticipanPrepare(UUID messageId, List<AcElementDeploy> acElementList, UUID instanceId) { + var automationComposition = cacheProvider.getAutomationComposition(instanceId); + for (var elementDeploy : acElementList) { + var element = automationComposition.getElements().get(elementDeploy.getId()); + var compositionInProperties = cacheProvider + .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition()); + var compositionElement = cacheProvider.createCompositionElementDto(automationComposition.getCompositionId(), + element, compositionInProperties); + var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(), + elementDeploy.getToscaServiceTemplateFragment(), + elementDeploy.getProperties(), element.getOutProperties()); + listener.prepare(messageId, compositionElement, instanceElement); + } + } + + private void callParticipanReview(UUID messageId, AutomationComposition automationComposition) { + for (var element : automationComposition.getElements().values()) { + var compositionInProperties = cacheProvider + .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition()); + element.setSubState(SubState.REVIEWING); + var compositionElement = cacheProvider.createCompositionElementDto(automationComposition.getCompositionId(), + element, compositionInProperties); + var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(), + null, element.getProperties(), element.getOutProperties()); + listener.review(messageId, compositionElement, instanceElement); + } + } +} diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java index b60d6372c..0f230c0a4 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java @@ -21,12 +21,9 @@ package org.onap.policy.clamp.acm.participant.intermediary.handler; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto; import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto; import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher; import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy; @@ -35,6 +32,7 @@ import org.onap.policy.clamp.models.acm.concepts.DeployState; import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy; import org.onap.policy.clamp.models.acm.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.messages.kafka.participant.AutomationCompositionDeploy; import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck; import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration; @@ -171,40 +169,12 @@ public class AutomationCompositionHandler { } } - private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition, - UUID compositionId) { - Map<UUID, CompositionElementDto> map = new HashMap<>(); - for (var element : automationComposition.getElements().values()) { - var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition()); - var compositionElement = cacheProvider - .createCompositionElementDto(compositionId, element, compositionInProperties); - map.put(element.getId(), compositionElement); - } - return map; - } - - private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) { - return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId()); - } - - private Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) { - Map<UUID, InstanceElementDto> map = new HashMap<>(); - var serviceTemplateFragment = cacheProvider - .getServiceTemplateFragmentMap().get(automationComposition.getCompositionId()); - for (var element : automationComposition.getElements().values()) { - var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(), - serviceTemplateFragment, element.getProperties(), element.getOutProperties()); - map.put(element.getId(), instanceElement); - } - return map; - } - private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements, AutomationComposition acCopy) { - var instanceElementDtoMap = getInstanceElementDtoMap(acCopy); - var instanceElementDtoMapUpdated = getInstanceElementDtoMap( + var instanceElementDtoMap = cacheProvider.getInstanceElementDtoMap(acCopy); + var instanceElementDtoMapUpdated = cacheProvider.getInstanceElementDtoMap( cacheProvider.getAutomationComposition(acCopy.getInstanceId())); - var compositionElementDtoMap = getCompositionElementDtoMap(acCopy); + var compositionElementDtoMap = cacheProvider.getCompositionElementDtoMap(acCopy); for (var acElement : acElements) { listener.update(messageId, compositionElementDtoMap.get(acElement.getId()), instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId())); @@ -218,6 +188,7 @@ public class AutomationCompositionHandler { var acElement = acElementList.get(element.getId()); AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties()); acElement.setDeployState(deployState); + acElement.setSubState(SubState.NONE); acElement.setDefinition(element.getDefinition()); } } @@ -261,6 +232,7 @@ public class AutomationCompositionHandler { int startPhase = ParticipantUtils.findStartPhase(compositionInProperties); if (startPhaseMsg.equals(startPhase)) { element.setDeployState(DeployState.DELETING); + element.setSubState(SubState.NONE); var compositionElement = cacheProvider.createCompositionElementDto( automationComposition.getCompositionId(), element, compositionInProperties); var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(), @@ -293,7 +265,7 @@ public class AutomationCompositionHandler { if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) { updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy, - DeployState.MIGRATING); + DeployState.MIGRATING); callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy, migrationMsg.getCompositionTargetId()); @@ -303,11 +275,12 @@ public class AutomationCompositionHandler { private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements, AutomationComposition acCopy, UUID compositionTargetId) { - var compositionElementMap = getCompositionElementDtoMap(acCopy); - var instanceElementMap = getInstanceElementDtoMap(acCopy); + var compositionElementMap = cacheProvider.getCompositionElementDtoMap(acCopy); + var instanceElementMap = cacheProvider.getInstanceElementDtoMap(acCopy); var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId()); - var compositionElementTargetMap = getCompositionElementDtoMap(automationComposition, compositionTargetId); - var instanceElementMigrateMap = getInstanceElementDtoMap(automationComposition); + var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(automationComposition, + compositionTargetId); + var instanceElementMigrateMap = cacheProvider.getInstanceElementDtoMap(automationComposition); for (var acElement : acElements) { listener.migrate(messageId, compositionElementMap.get(acElement.getId()), diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandler.java index 1f4c036e7..06b11bbca 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandler.java @@ -37,6 +37,7 @@ import org.onap.policy.clamp.models.acm.concepts.LockState; import org.onap.policy.clamp.models.acm.concepts.ParticipantDefinition; import org.onap.policy.clamp.models.acm.concepts.ParticipantState; 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.messages.kafka.participant.AutomationCompositionDeployAck; import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType; import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantPrimeAck; @@ -93,7 +94,13 @@ public class AutomationCompositionOutHandler { } element.setRestarting(null); - if (deployState != null) { + if (deployState != null && !SubState.NONE.equals(element.getSubState())) { + handleSubState(automationComposition, element); + if (!StateChangeResult.NO_ERROR.equals(stateChangeResult)) { + stateChangeResult = StateChangeResult.NO_ERROR; + LOGGER.warn("SubState has always NO_ERROR result!"); + } + } else if (deployState != null) { handleDeployState(automationComposition, element, deployState); } if (lockState != null) { @@ -132,6 +139,7 @@ public class AutomationCompositionOutHandler { } automationComposition.setDeployState(deployState); automationComposition.setLockState(element.getLockState()); + automationComposition.setSubState(SubState.NONE); if (DeployState.DELETED.equals(deployState)) { cacheProvider.removeAutomationComposition(automationComposition.getInstanceId()); @@ -146,6 +154,16 @@ public class AutomationCompositionOutHandler { .filter(acElement -> !lockState.equals(acElement.getLockState())).findAny(); if (checkOpt.isEmpty()) { automationComposition.setLockState(lockState); + automationComposition.setSubState(SubState.NONE); + } + } + + private void handleSubState(AutomationComposition automationComposition, AutomationCompositionElement element) { + element.setSubState(SubState.NONE); + var checkOpt = automationComposition.getElements().values().stream() + .filter(acElement -> !SubState.NONE.equals(acElement.getSubState())).findAny(); + if (checkOpt.isEmpty()) { + automationComposition.setSubState(SubState.NONE); } } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProvider.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProvider.java index 29b77fcb6..f56fabf3f 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProvider.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProvider.java @@ -30,6 +30,7 @@ import lombok.Getter; import lombok.NonNull; import lombok.Setter; import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto; +import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto; import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters; import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; @@ -39,6 +40,7 @@ import org.onap.policy.clamp.models.acm.concepts.LockState; import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy; import org.onap.policy.clamp.models.acm.concepts.ParticipantRestartAc; import org.onap.policy.clamp.models.acm.concepts.ParticipantSupportedElementType; +import org.onap.policy.clamp.models.acm.concepts.SubState; import org.onap.policy.models.base.PfUtils; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; @@ -161,6 +163,21 @@ public class CacheProvider { */ public void initializeAutomationComposition(@NonNull UUID compositionId, @NonNull UUID instanceId, ParticipantDeploy participantDeploy) { + initializeAutomationComposition(compositionId, instanceId, participantDeploy, + DeployState.DEPLOYING, SubState.NONE); + } + + /** + * Initialize an AutomationComposition from a ParticipantDeploy. + * + * @param compositionId the composition Id + * @param instanceId the Automation Composition Id + * @param participantDeploy the ParticipantDeploy + * @param deployState the DeployState + * @param subState the SubState + */ + public void initializeAutomationComposition(@NonNull UUID compositionId, @NonNull UUID instanceId, + ParticipantDeploy participantDeploy, DeployState deployState, SubState subState) { var acLast = automationCompositions.get(instanceId); Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>(); for (var element : participantDeploy.getAcElementList()) { @@ -168,8 +185,9 @@ public class CacheProvider { acElement.setId(element.getId()); acElement.setParticipantId(getParticipantId()); acElement.setDefinition(element.getDefinition()); - acElement.setDeployState(DeployState.DEPLOYING); + acElement.setDeployState(deployState); acElement.setLockState(LockState.NONE); + acElement.setSubState(subState); acElement.setProperties(element.getProperties()); var acElementLast = acLast != null ? acLast.getElements().get(element.getId()) : null; if (acElementLast != null) { @@ -186,6 +204,8 @@ public class CacheProvider { automationComposition.setCompositionId(compositionId); automationComposition.setInstanceId(instanceId); automationComposition.setElements(acElementMap); + automationComposition.setDeployState(deployState); + automationComposition.setSubState(subState); automationCompositions.put(instanceId, automationComposition); } @@ -208,6 +228,7 @@ public class CacheProvider { acElement.setDefinition(element.getDefinition()); acElement.setDeployState(element.getDeployState()); acElement.setLockState(element.getLockState()); + acElement.setSubState(SubState.NONE); acElement.setOperationalState(element.getOperationalState()); acElement.setUseState(element.getUseState()); acElement.setProperties(element.getProperties()); @@ -236,10 +257,49 @@ public class CacheProvider { * @return the CompositionElementDto */ public CompositionElementDto createCompositionElementDto(UUID compositionId, AutomationCompositionElement element, - Map<String, Object> compositionInProperties) { + Map<String, Object> compositionInProperties) { var compositionOutProperties = getAcElementsDefinitions() .get(compositionId).get(element.getDefinition()).getOutProperties(); return new CompositionElementDto(compositionId, element.getDefinition(), compositionInProperties, compositionOutProperties); } + + /** + * Get a Map of CompositionElementDto by elementId from the elements of an AutomationComposition. + * + * @param automationComposition the AutomationComposition + * @param compositionId the compositionId + * @return the Map of CompositionElementDto + */ + public Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition, + UUID compositionId) { + Map<UUID, CompositionElementDto> map = new HashMap<>(); + for (var element : automationComposition.getElements().values()) { + var compositionInProperties = getCommonProperties(compositionId, element.getDefinition()); + var compositionElement = createCompositionElementDto(compositionId, element, compositionInProperties); + map.put(element.getId(), compositionElement); + } + return map; + } + + public Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) { + return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId()); + } + + /** + * Get a Map of InstanceElementDto by elementId from the elements of an AutomationComposition. + * + * @param automationComposition the AutomationComposition + * @return the Map of InstanceElementDto + */ + public Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) { + Map<UUID, InstanceElementDto> map = new HashMap<>(); + var serviceTemplateFragment = serviceTemplateFragmentMap.get(automationComposition.getCompositionId()); + for (var element : automationComposition.getElements().values()) { + var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(), + serviceTemplateFragment, element.getProperties(), element.getOutProperties()); + map.put(element.getId(), instanceElement); + } + return map; + } } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java index 5ae8f0422..6615ae102 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java @@ -28,6 +28,7 @@ import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessag import org.onap.policy.clamp.models.acm.concepts.ParticipantState; import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy; import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration; +import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare; import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange; import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantAckMessage; import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantDeregister; @@ -55,6 +56,7 @@ public class ParticipantHandler { private final AutomationCompositionHandler automationCompositionHandler; private final AcLockHandler acLockHandler; + private final AcSubStateHandler acSubStateHandler; private final AcDefinitionHandler acDefinitionHandler; private final ParticipantMessagePublisher publisher; private final CacheProvider cacheProvider; @@ -106,7 +108,11 @@ public class ParticipantHandler { value = "listener.automation_composition_migration", description = "AUTOMATION_COMPOSITION_MIGRATION messages received") public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) { - automationCompositionHandler.handleAutomationCompositionMigration(migrationMsg); + if (Boolean.TRUE.equals(migrationMsg.getPrecheck())) { + acSubStateHandler.handleAcMigrationPrecheck(migrationMsg); + } else { + automationCompositionHandler.handleAutomationCompositionMigration(migrationMsg); + } } /** @@ -119,6 +125,11 @@ public class ParticipantHandler { automationCompositionHandler.handleAcPropertyUpdate(propertyUpdateMsg); } + @Timed(value = "listener.prepare", description = "AUTOMATION_COMPOSITION_PREPARE message received") + public void handleAutomationCompositionPrepare(AutomationCompositionPrepare acPrepareMsg) { + acSubStateHandler.handleAcPrepare(acPrepareMsg); + } + /** * Check if a participant message applies to this participant handler. * diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java index 00e0044b4..e6c2d379c 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java @@ -319,4 +319,93 @@ public class ThreadHandler implements Closeable { } executionMap.remove(instanceElement.elementId()); } + + /** + * Handles AutomationComposition Migration Precheck. + * + * @param messageId the messageId + * @param compositionElement the information of the Automation Composition Definition Element + * @param compositionElementTarget the information of the Automation Composition Definition Element Target + * @param instanceElement the information of the Automation Composition Instance Element + * @param instanceElementMigrate the information of the Automation Composition Instance Element updated + */ + public void migratePrecheck(UUID messageId, CompositionElementDto compositionElement, + CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement, + InstanceElementDto instanceElementMigrate) { + cleanExecution(instanceElement.elementId(), messageId); + var result = executor.submit(() -> + this.migratePrecheckProcess(compositionElement, compositionElementTarget, instanceElement, + instanceElementMigrate)); + executionMap.put(instanceElement.elementId(), result); + } + + private void migratePrecheckProcess(CompositionElementDto compositionElement, + CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement, + InstanceElementDto instanceElementMigrate) { + try { + listener.migratePrecheck(compositionElement, compositionElementTarget, instanceElement, + instanceElementMigrate); + } catch (PfModelException e) { + LOGGER.error("Automation composition element migrate precheck failed {} {}", + instanceElement.elementId(), e.getMessage()); + intermediaryApi.updateAutomationCompositionElementState( + instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED, + null, StateChangeResult.FAILED, "Automation composition element migrate precheck failed"); + } + executionMap.remove(instanceElement.elementId()); + } + + /** + * Handles AutomationComposition Prepare Post Deploy. + * + * @param messageId the messageId + * @param compositionElement the information of the Automation Composition Definition Element + * @param instanceElement the information of the Automation Composition Instance Element + */ + public void review(UUID messageId, CompositionElementDto compositionElement, + InstanceElementDto instanceElement) { + cleanExecution(instanceElement.elementId(), messageId); + var result = executor.submit(() -> this.reviewProcess(compositionElement, instanceElement)); + executionMap.put(instanceElement.elementId(), result); + } + + private void reviewProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) { + try { + listener.review(compositionElement, instanceElement); + } catch (PfModelException e) { + LOGGER.error("Automation composition element Review failed {} {}", + instanceElement.elementId(), e.getMessage()); + intermediaryApi.updateAutomationCompositionElementState( + instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED, + null, StateChangeResult.FAILED, "Automation composition element Review failed"); + } + executionMap.remove(instanceElement.elementId()); + } + + /** + * Handles AutomationComposition Prepare Pre Deploy. + * + * @param messageId the messageId + * @param compositionElement the information of the Automation Composition Definition Element + * @param instanceElement the information of the Automation Composition Instance Element + */ + public void prepare(UUID messageId, CompositionElementDto compositionElement, + InstanceElementDto instanceElement) { + cleanExecution(instanceElement.elementId(), messageId); + var result = executor.submit(() -> this.prepareProcess(compositionElement, instanceElement)); + executionMap.put(instanceElement.elementId(), result); + } + + private void prepareProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) { + try { + listener.prepare(compositionElement, instanceElement); + } catch (PfModelException e) { + LOGGER.error("Automation composition element prepare Pre Deploy failed {} {}", + instanceElement.elementId(), e.getMessage()); + intermediaryApi.updateAutomationCompositionElementState( + instanceElement.instanceId(), instanceElement.elementId(), DeployState.UNDEPLOYED, + null, StateChangeResult.FAILED, "Automation composition element prepare Pre Deploy failed"); + } + executionMap.remove(instanceElement.elementId()); + } } diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandlerTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandlerTest.java new file mode 100644 index 000000000..293a4415b --- /dev/null +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandlerTest.java @@ -0,0 +1,164 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.acm.participant.intermediary.handler; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +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 static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.onap.policy.clamp.acm.participant.intermediary.main.parameters.CommonTestData; +import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition; +import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy; +import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration; +import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +class AcSubStateHandlerTest { + + @Test + void handleAcStateChangeNullTest() { + var cacheProvider = mock(CacheProvider.class); + var ach = new AcSubStateHandler(cacheProvider, mock(ThreadHandler.class)); + + var acMigration = new AutomationCompositionMigration(); + acMigration.setPrecheck(true); + assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(acMigration)); + + var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next(); + acMigration.setAutomationCompositionId(automationComposition.getInstanceId()); + acMigration.setCompositionTargetId(UUID.randomUUID()); + assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(acMigration)); + + var acPrepare = new AutomationCompositionPrepare(); + assertDoesNotThrow(() -> ach.handleAcPrepare(acPrepare)); + + acPrepare.setAutomationCompositionId(automationComposition.getInstanceId()); + assertDoesNotThrow(() -> ach.handleAcPrepare(acPrepare)); + } + + @Test + void handleAcMigrationPrecheckTest() { + var listener = mock(ThreadHandler.class); + var cacheProvider = mock(CacheProvider.class); + var ach = new AcSubStateHandler(cacheProvider, listener); + var migrationMsg = new AutomationCompositionMigration(); + migrationMsg.setPrecheck(true); + assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(migrationMsg)); + var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next(); + migrationMsg.setCompositionTargetId(UUID.randomUUID()); + migrationMsg.setAutomationCompositionId(automationComposition.getInstanceId()); + assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(migrationMsg)); + when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(automationComposition); + var participantDeploy = new ParticipantDeploy(); + participantDeploy.setParticipantId(CommonTestData.getParticipantId()); + when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId()); + migrationMsg.getParticipantUpdatesList().add(participantDeploy); + Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>(); + for (var element : automationComposition.getElements().values()) { + var acElementDeploy = new AcElementDeploy(); + acElementDeploy.setProperties(Map.of()); + acElementDeploy.setId(element.getId()); + acElementDeploy.setDefinition(element.getDefinition()); + participantDeploy.getAcElementList().add(acElementDeploy); + map.put(element.getDefinition(), new AutomationCompositionElementDefinition()); + } + when(cacheProvider.getAcElementsDefinitions()) + .thenReturn(Map.of(automationComposition.getCompositionId(), map, + migrationMsg.getCompositionTargetId(), map)); + + ach.handleAcMigrationPrecheck(migrationMsg); + verify(listener, times(automationComposition.getElements().size())) + .migratePrecheck(any(), any(), any(), any(), any()); + } + + @Test + void handlePrepareTest() { + var listener = mock(ThreadHandler.class); + var cacheProvider = mock(CacheProvider.class); + var ach = new AcSubStateHandler(cacheProvider, listener); + + var acPrepareMsg = new AutomationCompositionPrepare(); + acPrepareMsg.setPreDeploy(true); + assertDoesNotThrow(() -> ach.handleAcPrepare(acPrepareMsg)); + + acPrepareMsg.setParticipantId(CommonTestData.getParticipantId()); + when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId()); + var participantDeploy = new ParticipantDeploy(); + participantDeploy.setParticipantId(CommonTestData.getParticipantId()); + acPrepareMsg.getParticipantList().add(participantDeploy); + + var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next(); + acPrepareMsg.setAutomationCompositionId(automationComposition.getInstanceId()); + when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(automationComposition); + Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>(); + for (var element : automationComposition.getElements().values()) { + var acElementDeploy = new AcElementDeploy(); + acElementDeploy.setProperties(Map.of()); + acElementDeploy.setId(element.getId()); + participantDeploy.getAcElementList().add(acElementDeploy); + map.put(element.getDefinition(), new AutomationCompositionElementDefinition()); + } + when(cacheProvider.getAcElementsDefinitions()) + .thenReturn(Map.of(automationComposition.getCompositionId(), map)); + + ach.handleAcPrepare(acPrepareMsg); + verify(listener, times(automationComposition.getElements().size())).prepare(any(), any(), any()); + } + + @Test + void handleReviewTest() { + var cacheProvider = mock(CacheProvider.class); + when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId()); + + var acPrepareMsg = new AutomationCompositionPrepare(); + acPrepareMsg.setPreDeploy(false); + acPrepareMsg.setParticipantId(CommonTestData.getParticipantId()); + + var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next(); + acPrepareMsg.setAutomationCompositionId(automationComposition.getInstanceId()); + when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(automationComposition); + Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>(); + for (var element : automationComposition.getElements().values()) { + var acElementDeploy = new AcElementDeploy(); + acElementDeploy.setProperties(Map.of()); + acElementDeploy.setId(element.getId()); + map.put(element.getDefinition(), new AutomationCompositionElementDefinition()); + } + when(cacheProvider.getAcElementsDefinitions()) + .thenReturn(Map.of(automationComposition.getCompositionId(), map)); + + var listener = mock(ThreadHandler.class); + var ach = new AcSubStateHandler(cacheProvider, listener); + ach.handleAcPrepare(acPrepareMsg); + verify(listener, times(automationComposition.getElements().size())).review(any(), any(), any()); + } +} diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandlerTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandlerTest.java index eed5319f8..27813988d 100644 --- a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandlerTest.java +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandlerTest.java @@ -39,6 +39,7 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDef 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.messages.kafka.participant.AutomationCompositionDeployAck; import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantPrimeAck; import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantStatus; @@ -87,6 +88,23 @@ class AutomationCompositionOutHandlerTest { } @Test + void updateAutomationCompositionElementStatePrepareTest() { + var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next(); + automationComposition.setSubState(SubState.PREPARING); + var cacheProvider = mock(CacheProvider.class); + when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(automationComposition); + var element = automationComposition.getElements().values().iterator().next(); + element.setSubState(SubState.PREPARING); + var elementId = element.getId(); + var publisher = mock(ParticipantMessagePublisher.class); + var acOutHandler = new AutomationCompositionOutHandler(publisher, cacheProvider); + acOutHandler.updateAutomationCompositionElementState(automationComposition.getInstanceId(), elementId, + DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Prepare completed"); + verify(publisher).sendAutomationCompositionAck(any(AutomationCompositionDeployAck.class)); + } + + @Test void updateAutomationCompositionElementStateLockTest() { var publisher = mock(ParticipantMessagePublisher.class); var cacheProvider = mock(CacheProvider.class); diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProviderTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProviderTest.java index 9451f0138..ced2d81e8 100644 --- a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProviderTest.java +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProviderTest.java @@ -186,4 +186,36 @@ class CacheProviderTest { assertEquals(element.getDefinition(), result.elementDefinitionId()); } } + + @Test + void testGetCompositionElementDtoMap() { + var parameter = CommonTestData.getParticipantParameters(); + var cacheProvider = new CacheProvider(parameter); + var compositionId = UUID.randomUUID(); + var automationComposition = + CommonTestData.getTestAutomationCompositions().getAutomationCompositionList().get(0); + automationComposition.setCompositionId(compositionId); + cacheProvider.addElementDefinition(compositionId, + CommonTestData.createAutomationCompositionElementDefinitionList(automationComposition)); + var result = cacheProvider.getCompositionElementDtoMap(automationComposition); + for (var element : automationComposition.getElements().values()) { + var compositionElementDto = result.get(element.getId()); + assertEquals(element.getDefinition(), compositionElementDto.elementDefinitionId()); + } + } + + @Test + void testGetInstanceElementDtoMap() { + var parameter = CommonTestData.getParticipantParameters(); + var cacheProvider = new CacheProvider(parameter); + var compositionId = UUID.randomUUID(); + var automationComposition = + CommonTestData.getTestAutomationCompositions().getAutomationCompositionList().get(0); + automationComposition.setCompositionId(compositionId); + var result = cacheProvider.getInstanceElementDtoMap(automationComposition); + for (var element : automationComposition.getElements().values()) { + var compositionElementDto = result.get(element.getId()); + assertEquals(element.getId(), compositionElementDto.elementId()); + } + } } diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandlerTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandlerTest.java index 8c2b2473c..1fb72812b 100644 --- a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandlerTest.java +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandlerTest.java @@ -62,7 +62,8 @@ class ParticipantHandlerTest { when(publisher.isActive()).thenReturn(true); var cacheProvider = mock(CacheProvider.class); var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class), - mock(AcLockHandler.class), mock(AcDefinitionHandler.class), publisher, cacheProvider); + mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), + publisher, cacheProvider); participantHandler.handleParticipantStatusReq(new ParticipantStatusReq()); verify(publisher).sendParticipantRegister(any(ParticipantRegister.class)); @@ -76,7 +77,8 @@ class ParticipantHandlerTest { void handleAutomationCompositionDeployTest() { var acHandler = mock(AutomationCompositionHandler.class); var participantHandler = new ParticipantHandler(acHandler, mock(AcLockHandler.class), - mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), mock(CacheProvider.class)); + mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), + mock(CacheProvider.class)); var automationCompositionDeploy = new AutomationCompositionDeploy(); participantHandler.handleAutomationCompositionDeploy(automationCompositionDeploy); verify(acHandler).handleAutomationCompositionDeploy(automationCompositionDeploy); @@ -86,8 +88,8 @@ class ParticipantHandlerTest { void handleAutomationCompositionStateChangeTest() { var acHandler = mock(AutomationCompositionHandler.class); var acLockHandler = mock(AcLockHandler.class); - var participantHandler = new ParticipantHandler(acHandler, acLockHandler, mock(AcDefinitionHandler.class), - mock(ParticipantMessagePublisher.class), mock(CacheProvider.class)); + var participantHandler = new ParticipantHandler(acHandler, acLockHandler, mock(AcSubStateHandler.class), + mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), mock(CacheProvider.class)); var acStateChange = new AutomationCompositionStateChange(); acStateChange.setDeployOrderedState(DeployOrder.DEPLOY); @@ -104,18 +106,25 @@ class ParticipantHandlerTest { @Test void handleAutomationCompositionMigrationTest() { var acHandler = mock(AutomationCompositionHandler.class); + var acSubStateHandler = mock(AcSubStateHandler.class); var participantHandler = new ParticipantHandler(acHandler, mock(AcLockHandler.class), - mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), mock(CacheProvider.class)); + acSubStateHandler, mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), + mock(CacheProvider.class)); var migrationMsg = new AutomationCompositionMigration(); participantHandler.handleAutomationCompositionMigration(migrationMsg); verify(acHandler).handleAutomationCompositionMigration(migrationMsg); + + migrationMsg.setPrecheck(true); + participantHandler.handleAutomationCompositionMigration(migrationMsg); + verify(acSubStateHandler).handleAcMigrationPrecheck(migrationMsg); } @Test void handleAcPropertyUpdateTest() { var acHandler = mock(AutomationCompositionHandler.class); var participantHandler = new ParticipantHandler(acHandler, mock(AcLockHandler.class), - mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), mock(CacheProvider.class)); + mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), + mock(CacheProvider.class)); var propertyUpdateMsg = new PropertiesUpdate(); participantHandler.handleAcPropertyUpdate(propertyUpdateMsg); verify(acHandler).handleAcPropertyUpdate(propertyUpdateMsg); @@ -127,8 +136,8 @@ class ParticipantHandlerTest { when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId()); when(cacheProvider.getReplicaId()).thenReturn(CommonTestData.getReplicaId()); var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class), - mock(AcLockHandler.class), mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), - cacheProvider); + mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), + mock(ParticipantMessagePublisher.class), cacheProvider); var participantAckMsg = new ParticipantAckMessage(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY); assertTrue(participantHandler.appliesTo(participantAckMsg)); @@ -147,7 +156,8 @@ class ParticipantHandlerTest { when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId()); when(cacheProvider.getSupportedAcElementTypes()).thenReturn(List.of(new ParticipantSupportedElementType())); var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class), - mock(AcLockHandler.class), mock(AcDefinitionHandler.class), publisher, cacheProvider); + mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), publisher, + cacheProvider); participantHandler.sendParticipantRegister(); verify(publisher).sendParticipantRegister(any(ParticipantRegister.class)); @@ -159,7 +169,8 @@ class ParticipantHandlerTest { var cacheProvider = mock(CacheProvider.class); when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId()); var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class), - mock(AcLockHandler.class), mock(AcDefinitionHandler.class), publisher, cacheProvider); + mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), publisher, + cacheProvider); participantHandler.handleParticipantRegisterAck(new ParticipantRegisterAck()); verify(publisher).sendParticipantStatus(any(ParticipantStatus.class)); @@ -171,7 +182,8 @@ class ParticipantHandlerTest { var cacheProvider = mock(CacheProvider.class); when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId()); var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class), - mock(AcLockHandler.class), mock(AcDefinitionHandler.class), publisher, cacheProvider); + mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), publisher, + cacheProvider); participantHandler.sendParticipantDeregister(); verify(publisher).sendParticipantDeregister(any(ParticipantDeregister.class)); @@ -180,8 +192,8 @@ class ParticipantHandlerTest { @Test void handleParticipantDeregisterAckTest() { var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class), - mock(AcLockHandler.class), mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), - mock(CacheProvider.class)); + mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), + mock(ParticipantMessagePublisher.class), mock(CacheProvider.class)); var participantDeregisterAck = new ParticipantDeregisterAck(); assertDoesNotThrow(() -> participantHandler.handleParticipantDeregisterAck(participantDeregisterAck)); } @@ -194,8 +206,8 @@ class ParticipantHandlerTest { var acHandler = mock(AcDefinitionHandler.class); var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class), - mock(AcLockHandler.class), acHandler, mock(ParticipantMessagePublisher.class), - mock(CacheProvider.class)); + mock(AcLockHandler.class), mock(AcSubStateHandler.class), acHandler, + mock(ParticipantMessagePublisher.class), mock(CacheProvider.class)); participantHandler.handleParticipantPrime(participantPrime); verify(acHandler).handlePrime(participantPrime); @@ -213,7 +225,7 @@ class ParticipantHandlerTest { var publisher = mock(ParticipantMessagePublisher.class); var acHandler = mock(AcDefinitionHandler.class); var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class), - mock(AcLockHandler.class), acHandler, publisher, cacheProvider); + mock(AcLockHandler.class), mock(AcSubStateHandler.class), acHandler, publisher, cacheProvider); participantHandler.handleParticipantSync(participantSyncMsg); verify(acHandler).handleParticipantSync(participantSyncMsg); @@ -229,7 +241,7 @@ class ParticipantHandlerTest { when(publisher.isActive()).thenReturn(true); var acHandler = mock(AcDefinitionHandler.class); var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class), - mock(AcLockHandler.class), acHandler, publisher, cacheProvider); + mock(AcLockHandler.class), mock(AcSubStateHandler.class), acHandler, publisher, cacheProvider); participantHandler.sendHeartbeat(); verify(publisher).sendParticipantRegister(any(ParticipantRegister.class)); diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java index f3471e6ee..57b065978 100644 --- a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java @@ -49,7 +49,7 @@ class ThreadHandlerTest { private static final int TIMEOUT = 400; @Test - void test() throws PfModelException, IOException { + void testPrime() throws PfModelException, IOException { var listener = mock(AutomationCompositionElementListener.class); var intermediaryApi = mock(ParticipantIntermediaryApi.class); try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) { @@ -61,11 +61,45 @@ class ThreadHandlerTest { verify(listener, timeout(TIMEOUT)).prime(composition); clearInvocations(listener); + threadHandler.deprime(messageId, composition); + verify(listener, timeout(TIMEOUT)).deprime(composition); + } + } + + @Test + void testPrimeException() throws PfModelException, IOException { + var listener = mock(AutomationCompositionElementListener.class); + var intermediaryApi = mock(ParticipantIntermediaryApi.class); + try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) { + + var compositionId = UUID.randomUUID(); + var composition = new CompositionDto(compositionId, Map.of(), Map.of()); + doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).prime(composition); + var messageId = UUID.randomUUID(); + threadHandler.prime(messageId, composition); + verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.COMMISSIONED, + StateChangeResult.FAILED, "Composition Defintion prime failed"); + + clearInvocations(listener); + doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).deprime(composition); + threadHandler.deprime(messageId, composition); + verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.PRIMED, + StateChangeResult.FAILED, "Composition Defintion deprime failed"); + } + } + + @Test + void testDeploy() throws PfModelException, IOException { + var listener = mock(AutomationCompositionElementListener.class); + var intermediaryApi = mock(ParticipantIntermediaryApi.class); + try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) { + Map<String, Object> properties = Map.of("key", "value"); var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(), properties, properties); var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(), - null, properties, properties); + null, properties, properties); + var messageId = UUID.randomUUID(); threadHandler.deploy(messageId, compositionElement, instanceElement); verify(listener, timeout(TIMEOUT)).deploy(compositionElement, instanceElement); @@ -88,43 +122,21 @@ class ThreadHandlerTest { instanceElement, instanceElementUpdated); clearInvocations(listener); - threadHandler.lock(messageId, compositionElement, instanceElement); - verify(listener, timeout(TIMEOUT)).lock(compositionElement, instanceElement); - - clearInvocations(listener); - threadHandler.unlock(messageId, compositionElement, instanceElement); - verify(listener, timeout(TIMEOUT)).unlock(compositionElement, instanceElement); - - clearInvocations(listener); threadHandler.undeploy(messageId, compositionElement, instanceElement); verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement); clearInvocations(listener); threadHandler.delete(messageId, compositionElement, instanceElement); verify(listener, timeout(TIMEOUT)).delete(compositionElement, instanceElement); - - clearInvocations(listener); - threadHandler.deprime(messageId, composition); - verify(listener, timeout(TIMEOUT)).deprime(composition); } } @Test - void testException() throws PfModelException, IOException { + void testDeployException() throws PfModelException, IOException { var listener = mock(AutomationCompositionElementListener.class); var intermediaryApi = mock(ParticipantIntermediaryApi.class); try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) { - var compositionId = UUID.randomUUID(); - var composition = new CompositionDto(compositionId, Map.of(), Map.of()); - doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) - .prime(composition); - var messageId = UUID.randomUUID(); - threadHandler.prime(messageId, composition); - verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.COMMISSIONED, - StateChangeResult.FAILED, "Composition Defintion prime failed"); - - clearInvocations(intermediaryApi); Map<String, Object> properties = Map.of("key", "value"); var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(), properties, properties); @@ -135,9 +147,10 @@ class ThreadHandlerTest { element.setId(elementId); doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) .deploy(compositionElement, instanceElement); + var messageId = UUID.randomUUID(); threadHandler.deploy(messageId, compositionElement, instanceElement); verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, - DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, + DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Automation composition element deploy failed"); clearInvocations(listener); @@ -147,44 +160,169 @@ class ThreadHandlerTest { .update(compositionElement, instanceElement, instanceElementUpdated); threadHandler.update(messageId, compositionElement, instanceElement, instanceElementUpdated); verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, - DeployState.DEPLOYED, null, StateChangeResult.FAILED, - "Automation composition element update failed"); + DeployState.DEPLOYED, null, StateChangeResult.FAILED, + "Automation composition element update failed"); + + clearInvocations(listener); + var compositionTargetId = UUID.randomUUID(); + var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(), + properties, properties); + doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) + .migrate(compositionElement, compositionElementTarget, instanceElement, instanceElementUpdated); + threadHandler.migrate(messageId, compositionElement, compositionElementTarget, + instanceElement, instanceElementUpdated); + verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, + DeployState.DEPLOYED, null, StateChangeResult.FAILED, + "Automation composition element migrate failed"); clearInvocations(listener); doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) + .undeploy(compositionElement, instanceElement); + threadHandler.undeploy(messageId, compositionElement, instanceElement); + verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, + DeployState.DEPLOYED, null, StateChangeResult.FAILED, + "Automation composition element undeploy failed"); + + clearInvocations(listener); + doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) + .delete(compositionElement, instanceElement); + threadHandler.delete(messageId, compositionElement, instanceElement); + verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, + DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, + "Automation composition element delete failed"); + } + } + + @Test + void testLock() throws PfModelException, IOException { + var listener = mock(AutomationCompositionElementListener.class); + var intermediaryApi = mock(ParticipantIntermediaryApi.class); + try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) { + + Map<String, Object> properties = Map.of("key", "value"); + var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(), + properties, properties); + var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(), + null, properties, properties); + var messageId = UUID.randomUUID(); + threadHandler.lock(messageId, compositionElement, instanceElement); + verify(listener, timeout(TIMEOUT)).lock(compositionElement, instanceElement); + + clearInvocations(listener); + threadHandler.unlock(messageId, compositionElement, instanceElement); + verify(listener, timeout(TIMEOUT)).unlock(compositionElement, instanceElement); + + clearInvocations(listener); + threadHandler.undeploy(messageId, compositionElement, instanceElement); + verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement); + } + } + + @Test + void testLockException() throws PfModelException, IOException { + var listener = mock(AutomationCompositionElementListener.class); + var intermediaryApi = mock(ParticipantIntermediaryApi.class); + try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) { + + Map<String, Object> properties = Map.of("key", "value"); + var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(), + properties, properties); + var instanceId = UUID.randomUUID(); + var elementId = UUID.randomUUID(); + var instanceElement = new InstanceElementDto(instanceId, elementId, null, properties, properties); + var element = new AcElementDeploy(); + element.setId(elementId); + var messageId = UUID.randomUUID(); + doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) .lock(compositionElement, instanceElement); threadHandler.lock(messageId, compositionElement, instanceElement); verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, - null, LockState.UNLOCKED, StateChangeResult.FAILED, "Automation composition element lock failed"); + null, LockState.UNLOCKED, StateChangeResult.FAILED, "Automation composition element lock failed"); clearInvocations(listener); doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) .unlock(compositionElement, instanceElement); threadHandler.unlock(messageId, compositionElement, instanceElement); verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, - null, LockState.LOCKED, StateChangeResult.FAILED, "Automation composition element unlock failed"); + null, LockState.LOCKED, StateChangeResult.FAILED, "Automation composition element unlock failed"); + } + } + + @Test + void testSubState() throws PfModelException, IOException { + var listener = mock(AutomationCompositionElementListener.class); + var intermediaryApi = mock(ParticipantIntermediaryApi.class); + try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) { + + Map<String, Object> properties = Map.of("key", "value"); + var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(), + properties, properties); + var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(), + null, properties, properties); + var messageId = UUID.randomUUID(); + threadHandler.prepare(messageId, compositionElement, instanceElement); + verify(listener, timeout(TIMEOUT)).prepare(compositionElement, instanceElement); clearInvocations(listener); + threadHandler.review(messageId, compositionElement, instanceElement); + verify(listener, timeout(TIMEOUT)).review(compositionElement, instanceElement); + + clearInvocations(listener); + var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(), + instanceElement.elementId(), null, properties, properties); + var compositionTargetId = UUID.randomUUID(); + var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(), + properties, properties); + threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget, + instanceElement, instanceElementMigrate); + verify(listener, timeout(TIMEOUT)).migratePrecheck(compositionElement, compositionElementTarget, + instanceElement, instanceElementMigrate); + } + } + + @Test + void testSubStateException() throws PfModelException, IOException { + var listener = mock(AutomationCompositionElementListener.class); + var intermediaryApi = mock(ParticipantIntermediaryApi.class); + try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) { + + Map<String, Object> properties = Map.of("key", "value"); + var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(), + properties, properties); + var instanceId = UUID.randomUUID(); + var elementId = UUID.randomUUID(); + var instanceElement = new InstanceElementDto(instanceId, elementId, null, properties, properties); + var element = new AcElementDeploy(); + element.setId(elementId); doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) - .undeploy(compositionElement, instanceElement); - threadHandler.undeploy(messageId, compositionElement, instanceElement); + .prepare(compositionElement, instanceElement); + var messageId = UUID.randomUUID(); + threadHandler.prepare(messageId, compositionElement, instanceElement); verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, - DeployState.DEPLOYED, null, StateChangeResult.FAILED, - "Automation composition element undeploy failed"); + DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, + "Automation composition element prepare Pre Deploy failed"); clearInvocations(listener); doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) - .delete(compositionElement, instanceElement); - threadHandler.delete(messageId, compositionElement, instanceElement); + .review(compositionElement, instanceElement); + threadHandler.review(messageId, compositionElement, instanceElement); verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, - DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, - "Automation composition element delete failed"); + DeployState.DEPLOYED, null, StateChangeResult.FAILED, + "Automation composition element Review failed"); clearInvocations(listener); - doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).deprime(composition); - threadHandler.deprime(messageId, composition); - verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.PRIMED, - StateChangeResult.FAILED, "Composition Defintion deprime failed"); + var compositionTargetId = UUID.randomUUID(); + var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(), + properties, properties); + var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(), + instanceElement.elementId(), null, properties, properties); + doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener) + .migratePrecheck(compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate); + threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget, + instanceElement, instanceElementMigrate); + verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId, + DeployState.DEPLOYED, null, StateChangeResult.FAILED, + "Automation composition element migrate precheck failed"); } } } |