2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2024 Nordix Foundation.
4 * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.clamp.acm.participant.intermediary.handler;
25 import java.util.ArrayList;
26 import java.util.List;
28 import java.util.UUID;
29 import lombok.RequiredArgsConstructor;
30 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
31 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
32 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
33 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
34 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
36 import org.onap.policy.clamp.models.acm.concepts.DeployState;
37 import org.onap.policy.clamp.models.acm.concepts.LockState;
38 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
39 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
40 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
41 import org.onap.policy.clamp.models.acm.concepts.SubState;
42 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy;
43 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
44 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
45 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange;
46 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
47 import org.onap.policy.clamp.models.acm.messages.kafka.participant.PropertiesUpdate;
48 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
49 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53 import org.springframework.stereotype.Component;
56 * This class is responsible for managing the state of all automation compositions in the participant.
59 @RequiredArgsConstructor
60 public class AutomationCompositionHandler {
61 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
63 private final CacheProvider cacheProvider;
64 private final ParticipantMessagePublisher publisher;
65 private final ThreadHandler listener;
68 * Handle a automation composition state change message.
70 * @param stateChangeMsg the state change message
72 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
73 if (stateChangeMsg.getAutomationCompositionId() == null) {
77 var automationComposition = cacheProvider.getAutomationComposition(stateChangeMsg.getAutomationCompositionId());
79 if (automationComposition == null) {
80 if (DeployOrder.DELETE.equals(stateChangeMsg.getDeployOrderedState())) {
81 var automationCompositionAck = new AutomationCompositionDeployAck(
82 ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
83 automationCompositionAck.setParticipantId(cacheProvider.getParticipantId());
84 automationCompositionAck.setReplicaId(cacheProvider.getReplicaId());
85 automationCompositionAck.setMessage("Already deleted or never used");
86 automationCompositionAck.setResult(true);
87 automationCompositionAck.setStateChangeResult(StateChangeResult.NO_ERROR);
88 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
89 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
90 publisher.sendAutomationCompositionAck(automationCompositionAck);
92 LOGGER.debug("Automation composition {} does not use this participant",
93 stateChangeMsg.getAutomationCompositionId());
98 switch (stateChangeMsg.getDeployOrderedState()) {
99 case UNDEPLOY -> handleUndeployState(stateChangeMsg.getMessageId(), automationComposition,
100 stateChangeMsg.getStartPhase());
101 case DELETE -> handleDeleteState(stateChangeMsg.getMessageId(), automationComposition,
102 stateChangeMsg.getStartPhase());
104 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
109 * Handle a automation composition properties update message.
111 * @param updateMsg the properties update message
113 public void handleAcPropertyUpdate(PropertiesUpdate updateMsg) {
115 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
116 LOGGER.warn("No AutomationCompositionElement updates in message {}",
117 updateMsg.getAutomationCompositionId());
121 for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
122 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
123 var automationComposition = cacheProvider.getAutomationComposition(
124 updateMsg.getAutomationCompositionId());
125 automationComposition.setDeployState(DeployState.UPDATING);
126 var acCopy = new AutomationComposition(automationComposition);
127 updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy,
128 DeployState.UPDATING);
130 callParticipantUpdateProperty(updateMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy);
136 * Handle a automation composition Deploy message.
138 * @param deployMsg the Deploy message
140 public void handleAutomationCompositionDeploy(AutomationCompositionDeploy deployMsg) {
142 if (deployMsg.getParticipantUpdatesList().isEmpty()) {
143 LOGGER.warn("No AutomationCompositionElement deploy in message {}", deployMsg.getAutomationCompositionId());
147 for (var participantDeploy : deployMsg.getParticipantUpdatesList()) {
148 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
149 if (deployMsg.isFirstStartPhase()) {
150 cacheProvider.initializeAutomationComposition(deployMsg.getCompositionId(),
151 deployMsg.getAutomationCompositionId(), participantDeploy);
153 callParticipanDeploy(deployMsg.getMessageId(), participantDeploy.getAcElementList(),
154 deployMsg.getStartPhase(), deployMsg.getAutomationCompositionId());
159 private void callParticipanDeploy(UUID messageId, List<AcElementDeploy> acElementDeployList,
160 Integer startPhaseMsg, UUID instanceId) {
161 var automationComposition = cacheProvider.getAutomationComposition(instanceId);
162 automationComposition.setDeployState(DeployState.DEPLOYING);
163 for (var elementDeploy : acElementDeployList) {
164 var element = automationComposition.getElements().get(elementDeploy.getId());
165 var compositionInProperties = cacheProvider
166 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
167 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
168 if (startPhaseMsg.equals(startPhase)) {
169 var compositionElement = cacheProvider.createCompositionElementDto(
170 automationComposition.getCompositionId(), element, compositionInProperties);
171 var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
172 elementDeploy.getToscaServiceTemplateFragment(),
173 elementDeploy.getProperties(), element.getOutProperties());
174 listener.deploy(messageId, compositionElement, instanceElement);
179 private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
180 AutomationComposition acCopy) {
181 var instanceElementDtoMap = cacheProvider.getInstanceElementDtoMap(acCopy);
182 var instanceElementDtoMapUpdated = cacheProvider.getInstanceElementDtoMap(
183 cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
184 var compositionElementDtoMap = cacheProvider.getCompositionElementDtoMap(acCopy);
185 for (var acElement : acElements) {
186 listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
187 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
191 private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy,
192 DeployState deployState) {
193 var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
194 for (var element : participantDeploy.getAcElementList()) {
195 var acElement = acElementList.get(element.getId());
196 if (acElement == null && deployState.equals(DeployState.MIGRATING)) {
197 var newElement = new AutomationCompositionElement();
198 newElement.setId(element.getId());
199 newElement.setParticipantId(participantDeploy.getParticipantId());
200 newElement.setDefinition(element.getDefinition());
201 newElement.setDeployState(deployState);
202 newElement.setSubState(SubState.NONE);
203 newElement.setLockState(LockState.LOCKED);
204 newElement.setProperties(element.getProperties());
206 acElementList.put(element.getId(), newElement);
207 LOGGER.info("New Ac Element with id {} is added in Migration", element.getId());
208 } else if (acElement != null) {
209 AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
210 acElement.setDeployState(deployState);
211 acElement.setSubState(SubState.NONE);
212 acElement.setDefinition(element.getDefinition());
215 if (deployState.equals(DeployState.MIGRATING)) {
216 // Check for missing elements and remove them from cache
217 List<UUID> elementsToRemove = findElementsToRemove(participantDeploy.getAcElementList(), acElementList);
218 for (UUID key : elementsToRemove) {
219 acElementList.remove(key);
220 LOGGER.info("Element with id {} is removed in Migration", key);
225 private List<UUID> findElementsToRemove(List<AcElementDeploy> acElementDeployList, Map<UUID,
226 AutomationCompositionElement> acElementList) {
227 List<UUID> elementsToRemove = new ArrayList<>();
228 for (var elementInCache : acElementList.entrySet()) {
229 boolean found = false;
230 for (var element : acElementDeployList) {
231 if (element.getId().equals(elementInCache.getValue().getId())) {
237 elementsToRemove.add(elementInCache.getKey());
240 return elementsToRemove;
244 * Method to handle when the new state from participant is UNINITIALISED state.
246 * @param messageId the messageId
247 * @param automationComposition participant response
248 * @param startPhaseMsg startPhase from message
250 private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
251 Integer startPhaseMsg) {
252 automationComposition.setCompositionTargetId(null);
253 automationComposition.setDeployState(DeployState.UNDEPLOYING);
254 var serviceTemplateFragment = cacheProvider
255 .getServiceTemplateFragmentMap().get(automationComposition.getCompositionId());
256 for (var element : automationComposition.getElements().values()) {
257 var compositionInProperties = cacheProvider
258 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
259 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
260 if (startPhaseMsg.equals(startPhase)) {
261 element.setDeployState(DeployState.UNDEPLOYING);
262 var compositionElement = cacheProvider.createCompositionElementDto(
263 automationComposition.getCompositionId(), element, compositionInProperties);
264 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
265 serviceTemplateFragment, element.getProperties(), element.getOutProperties());
266 listener.undeploy(messageId, compositionElement, instanceElement);
271 private void handleDeleteState(UUID messageId, final AutomationComposition automationComposition,
272 Integer startPhaseMsg) {
273 automationComposition.setDeployState(DeployState.DELETING);
274 var serviceTemplateFragment = cacheProvider
275 .getServiceTemplateFragmentMap().get(automationComposition.getCompositionId());
276 for (var element : automationComposition.getElements().values()) {
277 var compositionInProperties = cacheProvider
278 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
279 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
280 if (startPhaseMsg.equals(startPhase)) {
281 element.setDeployState(DeployState.DELETING);
282 element.setSubState(SubState.NONE);
283 var compositionElement = cacheProvider.createCompositionElementDto(
284 automationComposition.getCompositionId(), element, compositionInProperties);
285 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
286 serviceTemplateFragment, element.getProperties(), element.getOutProperties());
287 listener.delete(messageId, compositionElement, instanceElement);
293 * Handles AutomationComposition Migration.
295 * @param migrationMsg the AutomationCompositionMigration
297 public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
298 if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
302 var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
303 if (automationComposition == null) {
304 LOGGER.debug("Automation composition {} does not use this participant",
305 migrationMsg.getAutomationCompositionId());
308 var acCopy = new AutomationComposition(automationComposition);
309 automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
310 automationComposition.setDeployState(DeployState.MIGRATING);
311 for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
312 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
314 updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
315 DeployState.MIGRATING);
317 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
318 acCopy, migrationMsg.getCompositionTargetId());
323 private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
324 AutomationComposition acCopy, UUID compositionTargetId) {
325 var compositionElementMap = cacheProvider.getCompositionElementDtoMap(acCopy);
326 var instanceElementMap = cacheProvider.getInstanceElementDtoMap(acCopy);
327 var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
328 var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(automationComposition,
329 compositionTargetId);
330 var instanceElementMigrateMap = cacheProvider.getInstanceElementDtoMap(automationComposition);
332 // Call migrate for newly added and updated elements
333 for (var acElement : acElements) {
334 if (instanceElementMap.get(acElement.getId()) == null) {
335 var compositionDto = new CompositionElementDto(acElement.getId(), acElement.getDefinition(),
336 Map.of(), Map.of(), true, false);
337 var instanceDto = new InstanceElementDto(acCopy.getInstanceId(), acElement.getId(),
338 new ToscaServiceTemplate(), Map.of(), Map.of(), true, false);
340 listener.migrate(messageId, compositionDto,
341 compositionElementTargetMap.get(acElement.getId()),
342 instanceDto, instanceElementMigrateMap.get(acElement.getId()));
344 listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
345 compositionElementTargetMap.get(acElement.getId()),
346 instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
349 // Call migrate for removed elements
350 List<UUID> removedElements = findElementsToRemove(acElements, acCopy.getElements());
351 for (var elementId : removedElements) {
352 var compositionDtoTarget = new CompositionElementDto(elementId, acCopy.getElements().get(elementId)
353 .getDefinition(), Map.of(), Map.of(), false, true);
354 var instanceDtoTarget = new InstanceElementDto(acCopy.getInstanceId(), elementId,
355 new ToscaServiceTemplate(), Map.of(), Map.of(), false, true);
357 listener.migrate(messageId, compositionElementMap.get(elementId),
358 compositionDtoTarget,
359 instanceElementMap.get(elementId), instanceDtoTarget);