b60d6372ccce7e550b9d707d14d4c93ae0dddbc4
[policy/clamp.git] /
1 /*-
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.clamp.acm.participant.intermediary.handler;
23
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.UUID;
28 import lombok.RequiredArgsConstructor;
29 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
30 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
31 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
32 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
33 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
34 import org.onap.policy.clamp.models.acm.concepts.DeployState;
35 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
36 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
37 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
38 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy;
39 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
40 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
41 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange;
42 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
43 import org.onap.policy.clamp.models.acm.messages.kafka.participant.PropertiesUpdate;
44 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
45 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.stereotype.Component;
49
50 /*
51  * This class is responsible for managing the state of all automation compositions in the participant.
52  */
53 @Component
54 @RequiredArgsConstructor
55 public class AutomationCompositionHandler {
56     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
57
58     private final CacheProvider cacheProvider;
59     private final ParticipantMessagePublisher publisher;
60     private final ThreadHandler listener;
61
62     /**
63      * Handle a automation composition state change message.
64      *
65      * @param stateChangeMsg the state change message
66      */
67     public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
68         if (stateChangeMsg.getAutomationCompositionId() == null) {
69             return;
70         }
71
72         var automationComposition = cacheProvider.getAutomationComposition(stateChangeMsg.getAutomationCompositionId());
73
74         if (automationComposition == null) {
75             if (DeployOrder.DELETE.equals(stateChangeMsg.getDeployOrderedState())) {
76                 var automationCompositionAck = new AutomationCompositionDeployAck(
77                         ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
78                 automationCompositionAck.setParticipantId(cacheProvider.getParticipantId());
79                 automationCompositionAck.setReplicaId(cacheProvider.getReplicaId());
80                 automationCompositionAck.setMessage("Already deleted or never used");
81                 automationCompositionAck.setResult(true);
82                 automationCompositionAck.setStateChangeResult(StateChangeResult.NO_ERROR);
83                 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
84                 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
85                 publisher.sendAutomationCompositionAck(automationCompositionAck);
86             } else {
87                 LOGGER.debug("Automation composition {} does not use this participant",
88                         stateChangeMsg.getAutomationCompositionId());
89             }
90             return;
91         }
92
93         switch (stateChangeMsg.getDeployOrderedState()) {
94             case UNDEPLOY -> handleUndeployState(stateChangeMsg.getMessageId(), automationComposition,
95                     stateChangeMsg.getStartPhase());
96             case DELETE -> handleDeleteState(stateChangeMsg.getMessageId(), automationComposition,
97                     stateChangeMsg.getStartPhase());
98             default ->
99                     LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
100         }
101     }
102
103     /**
104      * Handle a automation composition properties update message.
105      *
106      * @param updateMsg the properties update message
107      */
108     public void handleAcPropertyUpdate(PropertiesUpdate updateMsg) {
109
110         if (updateMsg.getParticipantUpdatesList().isEmpty()) {
111             LOGGER.warn("No AutomationCompositionElement updates in message {}",
112                     updateMsg.getAutomationCompositionId());
113             return;
114         }
115
116         for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
117             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
118                 var automationComposition = cacheProvider.getAutomationComposition(
119                         updateMsg.getAutomationCompositionId());
120                 automationComposition.setDeployState(DeployState.UPDATING);
121                 var acCopy = new AutomationComposition(automationComposition);
122                 updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy,
123                         DeployState.UPDATING);
124
125                 callParticipantUpdateProperty(updateMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy);
126             }
127         }
128     }
129
130     /**
131      * Handle a automation composition Deploy message.
132      *
133      * @param deployMsg the Deploy message
134      */
135     public void handleAutomationCompositionDeploy(AutomationCompositionDeploy deployMsg) {
136
137         if (deployMsg.getParticipantUpdatesList().isEmpty()) {
138             LOGGER.warn("No AutomationCompositionElement deploy in message {}", deployMsg.getAutomationCompositionId());
139             return;
140         }
141
142         for (var participantDeploy : deployMsg.getParticipantUpdatesList()) {
143             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
144                 if (deployMsg.isFirstStartPhase()) {
145                     cacheProvider.initializeAutomationComposition(deployMsg.getCompositionId(),
146                             deployMsg.getAutomationCompositionId(), participantDeploy);
147                 }
148                 callParticipanDeploy(deployMsg.getMessageId(), participantDeploy.getAcElementList(),
149                         deployMsg.getStartPhase(), deployMsg.getAutomationCompositionId());
150             }
151         }
152     }
153
154     private void callParticipanDeploy(UUID messageId, List<AcElementDeploy> acElementDeployList,
155             Integer startPhaseMsg, UUID instanceId) {
156         var automationComposition = cacheProvider.getAutomationComposition(instanceId);
157         automationComposition.setDeployState(DeployState.DEPLOYING);
158         for (var elementDeploy : acElementDeployList) {
159             var element = automationComposition.getElements().get(elementDeploy.getId());
160             var compositionInProperties = cacheProvider
161                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
162             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
163             if (startPhaseMsg.equals(startPhase)) {
164                 var compositionElement = cacheProvider.createCompositionElementDto(
165                         automationComposition.getCompositionId(), element, compositionInProperties);
166                 var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
167                     elementDeploy.getToscaServiceTemplateFragment(),
168                     elementDeploy.getProperties(), element.getOutProperties());
169                 listener.deploy(messageId, compositionElement, instanceElement);
170             }
171         }
172     }
173
174     private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition,
175         UUID compositionId) {
176         Map<UUID, CompositionElementDto> map = new HashMap<>();
177         for (var element : automationComposition.getElements().values()) {
178             var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
179             var compositionElement = cacheProvider
180                     .createCompositionElementDto(compositionId, element, compositionInProperties);
181             map.put(element.getId(), compositionElement);
182         }
183         return map;
184     }
185
186     private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) {
187         return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId());
188     }
189
190     private Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) {
191         Map<UUID, InstanceElementDto> map = new HashMap<>();
192         var serviceTemplateFragment = cacheProvider
193                 .getServiceTemplateFragmentMap().get(automationComposition.getCompositionId());
194         for (var element : automationComposition.getElements().values()) {
195             var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
196                     serviceTemplateFragment, element.getProperties(), element.getOutProperties());
197             map.put(element.getId(), instanceElement);
198         }
199         return map;
200     }
201
202     private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
203         AutomationComposition acCopy) {
204         var instanceElementDtoMap = getInstanceElementDtoMap(acCopy);
205         var instanceElementDtoMapUpdated = getInstanceElementDtoMap(
206             cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
207         var compositionElementDtoMap = getCompositionElementDtoMap(acCopy);
208         for (var acElement : acElements) {
209             listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
210                 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
211         }
212     }
213
214     private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy,
215         DeployState deployState) {
216         var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
217         for (var element : participantDeploy.getAcElementList()) {
218             var acElement = acElementList.get(element.getId());
219             AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
220             acElement.setDeployState(deployState);
221             acElement.setDefinition(element.getDefinition());
222         }
223     }
224
225     /**
226      * Method to handle when the new state from participant is UNINITIALISED state.
227      *
228      * @param messageId the messageId
229      * @param automationComposition participant response
230      * @param startPhaseMsg startPhase from message
231      */
232     private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
233             Integer startPhaseMsg) {
234         automationComposition.setCompositionTargetId(null);
235         automationComposition.setDeployState(DeployState.UNDEPLOYING);
236         var serviceTemplateFragment = cacheProvider
237                 .getServiceTemplateFragmentMap().get(automationComposition.getCompositionId());
238         for (var element : automationComposition.getElements().values()) {
239             var compositionInProperties = cacheProvider
240                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
241             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
242             if (startPhaseMsg.equals(startPhase)) {
243                 element.setDeployState(DeployState.UNDEPLOYING);
244                 var compositionElement = cacheProvider.createCompositionElementDto(
245                         automationComposition.getCompositionId(), element, compositionInProperties);
246                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
247                         serviceTemplateFragment, element.getProperties(), element.getOutProperties());
248                 listener.undeploy(messageId, compositionElement, instanceElement);
249             }
250         }
251     }
252
253     private void handleDeleteState(UUID messageId, final AutomationComposition automationComposition,
254             Integer startPhaseMsg) {
255         automationComposition.setDeployState(DeployState.DELETING);
256         var serviceTemplateFragment = cacheProvider
257                 .getServiceTemplateFragmentMap().get(automationComposition.getCompositionId());
258         for (var element : automationComposition.getElements().values()) {
259             var compositionInProperties = cacheProvider
260                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
261             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
262             if (startPhaseMsg.equals(startPhase)) {
263                 element.setDeployState(DeployState.DELETING);
264                 var compositionElement = cacheProvider.createCompositionElementDto(
265                         automationComposition.getCompositionId(), element, compositionInProperties);
266                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
267                         serviceTemplateFragment, element.getProperties(), element.getOutProperties());
268                 listener.delete(messageId, compositionElement, instanceElement);
269             }
270         }
271     }
272
273     /**
274      * Handles AutomationComposition Migration.
275      *
276      * @param migrationMsg the AutomationCompositionMigration
277      */
278     public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
279         if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
280             return;
281         }
282
283         var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
284         if (automationComposition == null) {
285             LOGGER.debug("Automation composition {} does not use this participant",
286                     migrationMsg.getAutomationCompositionId());
287             return;
288         }
289         var acCopy = new AutomationComposition(automationComposition);
290         automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
291         automationComposition.setDeployState(DeployState.MIGRATING);
292         for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
293             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
294
295                 updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
296                         DeployState.MIGRATING);
297
298                 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
299                     acCopy, migrationMsg.getCompositionTargetId());
300             }
301         }
302     }
303
304     private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
305             AutomationComposition acCopy, UUID compositionTargetId) {
306         var compositionElementMap = getCompositionElementDtoMap(acCopy);
307         var instanceElementMap = getInstanceElementDtoMap(acCopy);
308         var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
309         var compositionElementTargetMap = getCompositionElementDtoMap(automationComposition, compositionTargetId);
310         var instanceElementMigrateMap = getInstanceElementDtoMap(automationComposition);
311
312         for (var acElement : acElements) {
313             listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
314                 compositionElementTargetMap.get(acElement.getId()),
315                 instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
316         }
317     }
318 }