From 461bbdb67733552e78037bd96ca7efdfd3015e25 Mon Sep 17 00:00:00 2001 From: rameshiyer27 Date: Thu, 18 May 2023 16:47:10 +0100 Subject: Update AC Element properties on the DEPLOYED state User can update the instance property values on DEPLOYED state. The runtime sends PROPERTY_UPDATE event to the participants and the updated values are stored on the runtime database. Participants can implement the update method to perform the required action. Issue-ID: POLICY-4682 Signed-off-by: zrrmmua Change-Id: Ic35fba669b5ffcf2c75d6deaa8c309d58e4026f0 --- .../clamp/models/acm/concepts/DeployState.java | 3 +- .../dmaap/participant/ParticipantMessageType.java | 13 ++- .../dmaap/participant/PropertiesUpdate.java | 61 ++++++++++++++ .../messages/rest/instantiation/DeployOrder.java | 3 +- .../api/AutomationCompositionElementListener.java | 5 ++ .../comm/AcPropertyUpdateListener.java | 49 +++++++++++ .../handler/AutomationCompositionHandler.java | 95 ++++++++++++++++++---- .../intermediary/handler/ParticipantHandler.java | 14 ++++ ...AutomationCompositionInstantiationProvider.java | 65 ++++++++++++--- .../runtime/supervision/SupervisionAcHandler.java | 12 +++ .../comm/AcElementPropertiesPublisher.java | 89 ++++++++++++++++++++ ...mationCompositionInstantiationProviderTest.java | 16 ---- .../supervision/SupervisionAcHandlerTest.java | 13 +-- 13 files changed, 389 insertions(+), 49 deletions(-) create mode 100644 models/src/main/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/PropertiesUpdate.java create mode 100644 participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPropertyUpdateListener.java create mode 100644 runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcElementPropertiesPublisher.java diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/DeployState.java b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/DeployState.java index 94aa07097..9dde703b6 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/DeployState.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/DeployState.java @@ -26,5 +26,6 @@ public enum DeployState { UNDEPLOYED, UNDEPLOYING, DELETING, - DELETED + DELETED, + UPDATING } diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/ParticipantMessageType.java b/models/src/main/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/ParticipantMessageType.java index 474bcd36f..e60a13202 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/ParticipantMessageType.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/ParticipantMessageType.java @@ -100,5 +100,16 @@ public enum ParticipantMessageType { /** * Used by automation composition runtime to request for PARTICIPANT_STATUS message immediately. */ - PARTICIPANT_STATUS_REQ + PARTICIPANT_STATUS_REQ, + + /** + * Used by automation composition runtime to send the element properties update to participant. + */ + PROPERTIES_UPDATE, + + /** + * Used by participant to acknowledge the receipt of PROPERTIES_UPDATE message + * from automation composition runtime. + */ + PROPERTIES_UPDATE_ACK } diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/PropertiesUpdate.java b/models/src/main/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/PropertiesUpdate.java new file mode 100644 index 000000000..43573cc08 --- /dev/null +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/PropertiesUpdate.java @@ -0,0 +1,61 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.models.acm.messages.dmaap.participant; + +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy; +import org.onap.policy.models.base.PfUtils; + + +/** + * Class to represent the PROPERTIES_UPDATE message that the ACM runtime sends to a participant. + * The ACM Runtime sends updated automation composition element property values to Participants. + */ +@Getter +@Setter +@ToString(callSuper = true) +public class PropertiesUpdate extends ParticipantMessage { + + // A list of updates to AC element properties + private List participantUpdatesList = new ArrayList<>(); + + /** + * Constructor for instantiating properties update class with message name. + * + */ + public PropertiesUpdate() { + super(ParticipantMessageType.PROPERTIES_UPDATE); + } + + /** + * Constructs the object, making a deep copy. + * + * @param source source from which to copy + */ + public PropertiesUpdate(PropertiesUpdate source) { + super(source); + this.participantUpdatesList = PfUtils.mapList(source.participantUpdatesList, ParticipantDeploy::new); + } +} diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/messages/rest/instantiation/DeployOrder.java b/models/src/main/java/org/onap/policy/clamp/models/acm/messages/rest/instantiation/DeployOrder.java index f4b57f1d1..318b36a19 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/messages/rest/instantiation/DeployOrder.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/messages/rest/instantiation/DeployOrder.java @@ -24,5 +24,6 @@ public enum DeployOrder { NONE, UNDEPLOY, DEPLOY, - DELETE + DELETE, + UPDATE } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/AutomationCompositionElementListener.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/AutomationCompositionElementListener.java index d6ef80fbe..c8fd91d69 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/AutomationCompositionElementListener.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/AutomationCompositionElementListener.java @@ -62,4 +62,9 @@ public interface AutomationCompositionElementListener { throws PfModelException { // default Delete Operation } + + public default void update(UUID automationCompositionId, AcElementDeploy element, Map properties) + throws PfModelException { + // default update Operation + } } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPropertyUpdateListener.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPropertyUpdateListener.java new file mode 100644 index 000000000..d6f1970d7 --- /dev/null +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPropertyUpdateListener.java @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.dmaap.participant.AutomationCompositionDeploy; +import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType; +import org.onap.policy.clamp.models.acm.messages.dmaap.participant.PropertiesUpdate; +import org.springframework.stereotype.Component; + +/** + * Listener for AC Element property update messages sent by ACM. + */ +@Component +public class AcPropertyUpdateListener extends ParticipantListener { + + /** + * Constructs the object. + * + * @param participantHandler the handler for managing the state of the participant + */ + public AcPropertyUpdateListener(final ParticipantHandler participantHandler) { + super(PropertiesUpdate.class, participantHandler, + participantHandler::handleAcPropertyUpdate); + } + + @Override + public String getType() { + return ParticipantMessageType.PROPERTIES_UPDATE.name(); + } +} 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 d33498518..9087054fe 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 @@ -49,6 +49,7 @@ import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCom import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange; import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType; import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantStatus; +import org.onap.policy.clamp.models.acm.messages.dmaap.participant.PropertiesUpdate; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder; import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver; @@ -261,6 +262,33 @@ public class AutomationCompositionHandler { } } + /** + * Handle a automation composition properties update message. + * + * @param updateMsg the properties update message + * @param acElementDefinitions the list of AutomationCompositionElementDefinition + */ + public void handleAcPropertyUpdate(PropertiesUpdate updateMsg, + List acElementDefinitions) { + + if (updateMsg.getParticipantUpdatesList().isEmpty()) { + LOGGER.warn("No AutomationCompositionElement updates in message {}", + updateMsg.getAutomationCompositionId()); + return; + } + + for (var participantDeploy : updateMsg.getParticipantUpdatesList()) { + if (participantId.equals(participantDeploy.getParticipantId())) { + + initializeDeploy(updateMsg.getMessageId(), updateMsg.getAutomationCompositionId(), + participantDeploy, DeployState.UPDATING); + + callParticipantUpdateProperty(participantDeploy.getAcElementList(), acElementDefinitions, + updateMsg.getAutomationCompositionId()); + } + } + } + /** * Handle a automation composition Deploy message. * @@ -280,28 +308,34 @@ public class AutomationCompositionHandler { if (participantId.equals(participantDeploy.getParticipantId())) { if (updateMsg.isFirstStartPhase()) { initializeDeploy(updateMsg.getMessageId(), updateMsg.getAutomationCompositionId(), - participantDeploy); + participantDeploy, DeployState.DEPLOYING); } - callParticipanDeploy(participantDeploy.getAcElementList(), acElementDefinitions, + callParticipantDeploy(participantDeploy.getAcElementList(), acElementDefinitions, updateMsg.getStartPhase(), updateMsg.getAutomationCompositionId()); } } } - private void initializeDeploy(UUID messageId, UUID instanceId, ParticipantDeploy participantDeploy) { - var automationComposition = new AutomationComposition(); - automationComposition.setInstanceId(instanceId); - var acElements = storeElementsOnThisParticipant(participantDeploy); - automationComposition.setElements(prepareAcElementMap(acElements)); - automationCompositionMap.put(instanceId, automationComposition); + private void initializeDeploy(UUID messageId, UUID instanceId, ParticipantDeploy participantDeploy, + DeployState deployState) { + if (automationCompositionMap.containsKey(instanceId)) { + updateExistingElementsOnThisParticipant(instanceId, participantDeploy, deployState); + } else { + var automationComposition = new AutomationComposition(); + automationComposition.setInstanceId(instanceId); + var acElements = storeElementsOnThisParticipant(participantDeploy, deployState); + automationComposition.setElements(prepareAcElementMap(acElements)); + automationCompositionMap.put(instanceId, automationComposition); + } } - private void callParticipanDeploy(List acElements, - List acElementDefinitions, Integer startPhaseMsg, - UUID automationCompositionId) { + private void callParticipantDeploy(List acElements, + List acElementDefinitions, + Integer startPhaseMsg, UUID automationCompositionId) { try { for (var element : acElements) { - var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition()); + var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, + element.getDefinition()); if (acElementNodeTemplate != null) { int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties()); if (startPhaseMsg.equals(startPhase)) { @@ -319,6 +353,27 @@ public class AutomationCompositionHandler { } + private void callParticipantUpdateProperty(List acElements, + List acElementDefinitions, + UUID automationCompositionId) { + try { + for (var element : acElements) { + var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, + element.getDefinition()); + if (acElementNodeTemplate != null) { + for (var acElementListener : listeners) { + var map = new HashMap<>(acElementNodeTemplate.getProperties()); + map.putAll(element.getProperties()); + acElementListener.update(automationCompositionId, element, map); + } + } + } + } catch (PfModelException e) { + LOGGER.error("Automation composition element update failed for {} {}", automationCompositionId, e); + } + + } + private ToscaNodeTemplate getAcElementNodeTemplate( List acElementDefinitions, ToscaConceptIdentifier acElementDefId) { @@ -330,20 +385,32 @@ public class AutomationCompositionHandler { return null; } - private List storeElementsOnThisParticipant(ParticipantDeploy participantDeploy) { + private List storeElementsOnThisParticipant(ParticipantDeploy participantDeploy, + DeployState deployState) { List acElementList = new ArrayList<>(); for (var element : participantDeploy.getAcElementList()) { var acElement = new AutomationCompositionElement(); acElement.setId(element.getId()); acElement.setParticipantId(participantDeploy.getParticipantId()); acElement.setDefinition(element.getDefinition()); - acElement.setDeployState(DeployState.DEPLOYING); + acElement.setDeployState(deployState); acElement.setLockState(LockState.NONE); acElementList.add(acElement); } return acElementList; } + private void updateExistingElementsOnThisParticipant( + UUID instanceId, ParticipantDeploy participantDeploy, DeployState deployState) { + + Map elementList = automationCompositionMap.get(instanceId).getElements(); + for (var element : participantDeploy.getAcElementList()) { + automationCompositionMap.get(instanceId).getElements().get(element.getId()).getProperties() + .putAll(element.getProperties()); + automationCompositionMap.get(instanceId).getElements().get(element.getId()).setDeployState(deployState); + } + } + private Map prepareAcElementMap(List acElements) { Map acElementMap = new LinkedHashMap<>(); for (var element : acElements) { 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 44a988a27..9e1216cec 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 @@ -47,6 +47,7 @@ import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantRe import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantRegisterAck; import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantStatus; import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantStatusReq; +import org.onap.policy.clamp.models.acm.messages.dmaap.participant.PropertiesUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -120,6 +121,19 @@ public class ParticipantHandler { acElementDefsMap.get(stateChangeMsg.getCompositionId())); } + /** + * Handle a automation composition property update message. + * + * @param propertyUpdateMsg the property update message + */ + @Timed( + value = "listener.properties_update", + description = "PROPERTIES_UPDATE message received") + public void handleAcPropertyUpdate(PropertiesUpdate propertyUpdateMsg) { + automationCompositionHandler.handleAcPropertyUpdate(propertyUpdateMsg, + acElementDefsMap.get(propertyUpdateMsg.getCompositionId())); + } + /** * Check if a participant message applies to this participant handler. * diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java index 4665fec37..787547860 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java @@ -21,6 +21,8 @@ package org.onap.policy.clamp.acm.runtime.instantiation; + +import java.util.Map; import java.util.UUID; import javax.validation.Valid; import javax.ws.rs.core.Response; @@ -29,8 +31,10 @@ import lombok.AllArgsConstructor; import org.onap.policy.clamp.acm.runtime.supervision.SupervisionAcHandler; 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.AutomationCompositionElement; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions; import org.onap.policy.clamp.models.acm.concepts.DeployState; +import org.onap.policy.clamp.models.acm.concepts.LockState; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse; import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider; @@ -100,33 +104,72 @@ public class AutomationCompositionInstantiationProvider { */ public InstantiationResponse updateAutomationComposition(UUID compositionId, AutomationComposition automationComposition) { + var response = new InstantiationResponse(); var instanceId = automationComposition.getInstanceId(); var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId); if (!compositionId.equals(acToUpdate.getCompositionId())) { throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId); } - if (!DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) { - throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, - "Not allow to update for state in " + acToUpdate.getDeployState()); + if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) { + acToUpdate.setElements(automationComposition.getElements()); + acToUpdate.setName(automationComposition.getName()); + acToUpdate.setVersion(automationComposition.getVersion()); + acToUpdate.setDescription(automationComposition.getDescription()); + acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom()); + var validationResult = validateAutomationComposition(acToUpdate); + if (!validationResult.isValid()) { + throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult()); + } + automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate); + response.setInstanceId(instanceId); + response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier()); + return response; + + } else if (DeployState.DEPLOYED.equals(acToUpdate.getDeployState()) + && LockState.LOCKED.equals(acToUpdate.getLockState())) { + return updateDeployedAutomationComposition(compositionId, automationComposition, acToUpdate); + } + throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, + "Not allowed to update in the state " + acToUpdate.getDeployState()); + } + + /** + * Update deployed AC Element properties. + * + * @param compositionId The UUID of the automation composition definition + * @param automationComposition the automation composition + * @param acToBeUpdated the composition to be updated + * @return the result of the update + */ + public InstantiationResponse updateDeployedAutomationComposition(UUID compositionId, + AutomationComposition automationComposition, + AutomationComposition acToBeUpdated) { + + // Iterate and update the element property values + for (Map.Entry dbAcElement : acToBeUpdated.getElements().entrySet()) { + var elementId = dbAcElement.getKey(); + if (automationComposition.getElements().containsKey(elementId)) { + dbAcElement.getValue().getProperties().putAll(automationComposition.getElements().get(elementId) + .getProperties()); + } } - acToUpdate.setElements(automationComposition.getElements()); - acToUpdate.setName(automationComposition.getName()); - acToUpdate.setVersion(automationComposition.getVersion()); - acToUpdate.setDescription(automationComposition.getDescription()); - acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom()); - var validationResult = validateAutomationComposition(acToUpdate); + // Publish property update event to the participants + supervisionAcHandler.update(acToBeUpdated); + + var validationResult = validateAutomationComposition(acToBeUpdated); if (!validationResult.isValid()) { throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult()); } - automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate); - + automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated); var response = new InstantiationResponse(); + var instanceId = automationComposition.getInstanceId(); response.setInstanceId(instanceId); response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier()); return response; } + /** * Validate AutomationComposition. * diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java index 654c31194..da3cbc399 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import lombok.AllArgsConstructor; +import org.onap.policy.clamp.acm.runtime.supervision.comm.AcElementPropertiesPublisher; 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.models.acm.concepts.AcElementDeployAck; @@ -55,6 +56,8 @@ public class SupervisionAcHandler { private final AutomationCompositionDeployPublisher automationCompositionDeployPublisher; private final AutomationCompositionStateChangePublisher automationCompositionStateChangePublisher; + private final AcElementPropertiesPublisher acElementPropertiesPublisher; + /** * Handle Deploy an AutomationComposition instance. * @@ -108,6 +111,15 @@ public class SupervisionAcHandler { automationCompositionStateChangePublisher.send(automationComposition, startPhase, true); } + /** + * Handle Element property update on a deployed instance. + * @param automationComposition the AutomationComposition + */ + public void update(AutomationComposition automationComposition) { + AcmUtils.setCascadedState(automationComposition, DeployState.UPDATING, LockState.NONE); + acElementPropertiesPublisher.send(automationComposition); + } + /** * Handle Delete an AutomationComposition instance. * diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcElementPropertiesPublisher.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcElementPropertiesPublisher.java new file mode 100644 index 000000000..c5f33ad97 --- /dev/null +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcElementPropertiesPublisher.java @@ -0,0 +1,89 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.comm; + +import io.micrometer.core.annotation.Timed; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.UnaryOperator; +import lombok.AllArgsConstructor; +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.ParticipantDeploy; +import org.onap.policy.clamp.models.acm.messages.dmaap.participant.PropertiesUpdate; +import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder; +import org.onap.policy.models.base.PfUtils; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * This class is used to send PropertiesUpdate messages to participants. + */ +@Component +@AllArgsConstructor +public class AcElementPropertiesPublisher extends AbstractParticipantPublisher { + + private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionDeployPublisher.class); + + /** + * Send ACElementPropertiesUpdate to Participant. + * + * @param automationComposition the AutomationComposition + */ + @Timed(value = "publisher.properties_update", + description = "AC Element Properties Update published") + public void send(AutomationComposition automationComposition) { + Map> map = new HashMap<>(); + for (var element : automationComposition.getElements().values()) { + var acElementDeploy = new AcElementDeploy(); + acElementDeploy.setId(element.getId()); + acElementDeploy.setDefinition(new ToscaConceptIdentifier(element.getDefinition())); + acElementDeploy.setOrderedState(DeployOrder.UPDATE); + acElementDeploy.setProperties(PfUtils.mapMap(element.getProperties(), UnaryOperator.identity())); + + map.putIfAbsent(element.getParticipantId(), new ArrayList<>()); + map.get(element.getParticipantId()).add(acElementDeploy); + } + List participantDeploys = new ArrayList<>(); + for (var entry : map.entrySet()) { + var participantDeploy = new ParticipantDeploy(); + participantDeploy.setParticipantId(entry.getKey()); + participantDeploy.setAcElementList(entry.getValue()); + participantDeploys.add(participantDeploy); + } + + var propertiesUpdate = new PropertiesUpdate(); + propertiesUpdate.setCompositionId(automationComposition.getCompositionId()); + propertiesUpdate.setAutomationCompositionId(automationComposition.getInstanceId()); + propertiesUpdate.setMessageId(UUID.randomUUID()); + propertiesUpdate.setTimestamp(Instant.now()); + propertiesUpdate.setParticipantUpdatesList(participantDeploys); + + LOGGER.debug("AC Element properties update sent {}", propertiesUpdate); + super.send(propertiesUpdate); + } +} diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java index faf356c22..54a0c9613 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java @@ -312,22 +312,6 @@ class AutomationCompositionInstantiationProviderTest { + " Commissioned automation composition definition not primed\n"); } - @Test - void testUpdateBadRequest() { - var automationComposition = InstantiationUtils - .getAutomationCompositionFromResource(AC_INSTANTIATION_AC_DEFINITION_NOT_FOUND_JSON, "AcNotFound"); - - var acProvider = mock(AutomationCompositionProvider.class); - automationComposition.setDeployState(DeployState.DEPLOYED); - when(acProvider.getAutomationComposition(automationComposition.getInstanceId())) - .thenReturn(automationComposition); - var provider = new AutomationCompositionInstantiationProvider(acProvider, - mock(AcDefinitionProvider.class), null, null); - - assertThatThrownBy(() -> provider.updateAutomationComposition(automationComposition.getCompositionId(), - automationComposition)).hasMessageMatching("Not allow to update for state in DEPLOYED"); - } - @Test void testCompositionInstanceState() { var acDefinitionProvider = mock(AcDefinitionProvider.class); diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java index c61984b01..e763127cc 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java @@ -33,6 +33,7 @@ import java.util.Optional; 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.AcElementPropertiesPublisher; 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.util.CommonTestData; @@ -59,7 +60,8 @@ class SupervisionAcHandlerTest { var handler = new SupervisionAcHandler(automationCompositionProvider, mock(AutomationCompositionDeployPublisher.class), - mock(AutomationCompositionStateChangePublisher.class)); + mock(AutomationCompositionStateChangePublisher.class), + mock(AcElementPropertiesPublisher.class)); var automationCompositionAckMessage = new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK); @@ -97,7 +99,8 @@ class SupervisionAcHandlerTest { var handler = new SupervisionAcHandler(automationCompositionProvider, mock(AutomationCompositionDeployPublisher.class), - mock(AutomationCompositionStateChangePublisher.class)); + mock(AutomationCompositionStateChangePublisher.class), + mock(AcElementPropertiesPublisher.class)); handler.handleAutomationCompositionUpdateAckMessage(automationCompositionAckMessage); @@ -110,7 +113,7 @@ class SupervisionAcHandlerTest { var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class); var handler = new SupervisionAcHandler(automationCompositionProvider, mock(AutomationCompositionDeployPublisher.class), - acStateChangePublisher); + acStateChangePublisher, mock(AcElementPropertiesPublisher.class)); var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML); var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED); var automationComposition = @@ -127,7 +130,7 @@ class SupervisionAcHandlerTest { var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class); var handler = new SupervisionAcHandler(automationCompositionProvider, mock(AutomationCompositionDeployPublisher.class), - acStateChangePublisher); + acStateChangePublisher, mock(AcElementPropertiesPublisher.class)); var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML); var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED); var automationComposition = @@ -144,7 +147,7 @@ class SupervisionAcHandlerTest { var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class); var handler = new SupervisionAcHandler(automationCompositionProvider, mock(AutomationCompositionDeployPublisher.class), - acStateChangePublisher); + acStateChangePublisher, mock(AcElementPropertiesPublisher.class)); var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML); var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED); var automationComposition = -- cgit 1.2.3-korg