5c54861f7c83c4d45bf81c01485193a3c83fadc3
[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     /**
64      * Handle a automation composition state change message.
65      *
66      * @param stateChangeMsg the state change message
67      */
68     public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
69         if (stateChangeMsg.getAutomationCompositionId() == null) {
70             return;
71         }
72
73         var automationComposition = cacheProvider.getAutomationComposition(stateChangeMsg.getAutomationCompositionId());
74
75         if (automationComposition == null) {
76             if (DeployOrder.DELETE.equals(stateChangeMsg.getDeployOrderedState())) {
77                 var automationCompositionAck = new AutomationCompositionDeployAck(
78                         ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
79                 automationCompositionAck.setParticipantId(cacheProvider.getParticipantId());
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
119                 var acCopy = new AutomationComposition(cacheProvider.getAutomationComposition(
120                     updateMsg.getAutomationCompositionId()));
121                 updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy,
122                         DeployState.UPDATING);
123
124                 callParticipantUpdateProperty(updateMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy);
125             }
126         }
127     }
128
129     /**
130      * Handle a automation composition Deploy message.
131      *
132      * @param deployMsg the Deploy message
133      */
134     public void handleAutomationCompositionDeploy(AutomationCompositionDeploy deployMsg) {
135
136         if (deployMsg.getParticipantUpdatesList().isEmpty()) {
137             LOGGER.warn("No AutomationCompositionElement deploy in message {}", deployMsg.getAutomationCompositionId());
138             return;
139         }
140
141         for (var participantDeploy : deployMsg.getParticipantUpdatesList()) {
142             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
143                 if (deployMsg.isFirstStartPhase()) {
144                     cacheProvider.initializeAutomationComposition(deployMsg.getCompositionId(),
145                             deployMsg.getAutomationCompositionId(), participantDeploy);
146                 }
147                 callParticipanDeploy(deployMsg.getMessageId(), participantDeploy.getAcElementList(),
148                         deployMsg.getStartPhase(), deployMsg.getAutomationCompositionId());
149             }
150         }
151     }
152
153     private void callParticipanDeploy(UUID messageId, List<AcElementDeploy> acElementDeployList,
154             Integer startPhaseMsg, UUID instanceId) {
155         var automationComposition = cacheProvider.getAutomationComposition(instanceId);
156         for (var elementDeploy : acElementDeployList) {
157             var element = automationComposition.getElements().get(elementDeploy.getId());
158             var compositionInProperties = cacheProvider
159                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
160             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
161             if (startPhaseMsg.equals(startPhase)) {
162                 var compositionElement = cacheProvider.createCompositionElementDto(
163                         automationComposition.getCompositionId(), element, compositionInProperties);
164                 var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
165                     elementDeploy.getToscaServiceTemplateFragment(),
166                     elementDeploy.getProperties(), element.getOutProperties());
167                 listener.deploy(messageId, compositionElement, instanceElement);
168             }
169         }
170     }
171
172     private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition,
173         UUID compositionId) {
174         Map<UUID, CompositionElementDto> map = new HashMap<>();
175         for (var element : automationComposition.getElements().values()) {
176             var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
177             var compositionElement = cacheProvider
178                     .createCompositionElementDto(compositionId, element, compositionInProperties);
179             map.put(element.getId(), compositionElement);
180         }
181         return map;
182     }
183
184     private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) {
185         return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId());
186     }
187
188     private Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) {
189         Map<UUID, InstanceElementDto> map = new HashMap<>();
190         for (var element : automationComposition.getElements().values()) {
191             var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
192                 null, element.getProperties(), element.getOutProperties());
193             map.put(element.getId(), instanceElement);
194         }
195         return map;
196     }
197
198     private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
199         AutomationComposition acCopy) {
200         var instanceElementDtoMap = getInstanceElementDtoMap(acCopy);
201         var instanceElementDtoMapUpdated = getInstanceElementDtoMap(
202             cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
203         var compositionElementDtoMap = getCompositionElementDtoMap(acCopy);
204         for (var acElement : acElements) {
205             listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
206                 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
207         }
208     }
209
210     private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy,
211         DeployState deployState) {
212         var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
213         for (var element : participantDeploy.getAcElementList()) {
214             var acElement = acElementList.get(element.getId());
215             AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
216             acElement.setDeployState(deployState);
217             acElement.setDefinition(element.getDefinition());
218         }
219     }
220
221     /**
222      * Method to handle when the new state from participant is UNINITIALISED state.
223      *
224      * @param messageId the messageId
225      * @param automationComposition participant response
226      * @param startPhaseMsg startPhase from message
227      */
228     private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
229             Integer startPhaseMsg) {
230         automationComposition.setCompositionTargetId(null);
231         for (var element : automationComposition.getElements().values()) {
232             var compositionInProperties = cacheProvider
233                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
234             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
235             if (startPhaseMsg.equals(startPhase)) {
236                 element.setDeployState(DeployState.UNDEPLOYING);
237                 var compositionElement = cacheProvider.createCompositionElementDto(
238                         automationComposition.getCompositionId(), element, compositionInProperties);
239                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
240                     null, element.getProperties(), element.getOutProperties());
241                 listener.undeploy(messageId, compositionElement, instanceElement);
242             }
243         }
244     }
245
246     private void handleDeleteState(UUID messageId, final AutomationComposition automationComposition,
247             Integer startPhaseMsg) {
248         for (var element : automationComposition.getElements().values()) {
249             var compositionInProperties = cacheProvider
250                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
251             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
252             if (startPhaseMsg.equals(startPhase)) {
253                 element.setDeployState(DeployState.DELETING);
254                 var compositionElement = cacheProvider.createCompositionElementDto(
255                         automationComposition.getCompositionId(), element, compositionInProperties);
256                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
257                     null, element.getProperties(), element.getOutProperties());
258                 listener.delete(messageId, compositionElement, instanceElement);
259             }
260         }
261     }
262
263     /**
264      * Handles AutomationComposition Migration.
265      *
266      * @param migrationMsg the AutomationCompositionMigration
267      */
268     public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
269         if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
270             return;
271         }
272
273         var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
274         if (automationComposition == null) {
275             LOGGER.debug("Automation composition {} does not use this participant",
276                     migrationMsg.getAutomationCompositionId());
277             return;
278         }
279         var acCopy = new AutomationComposition(automationComposition);
280         automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
281         for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
282             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
283
284                 updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
285                         DeployState.MIGRATING);
286
287                 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
288                     acCopy, migrationMsg.getCompositionTargetId());
289             }
290         }
291     }
292
293     private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
294             AutomationComposition acCopy, UUID compositionTargetId) {
295         var compositionElementMap = getCompositionElementDtoMap(acCopy);
296         var instanceElementMap = getInstanceElementDtoMap(acCopy);
297         var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
298         var compositionElementTargetMap = getCompositionElementDtoMap(automationComposition, compositionTargetId);
299         var instanceElementMigrateMap = getInstanceElementDtoMap(automationComposition);
300
301         for (var acElement : acElements) {
302             listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
303                 compositionElementTargetMap.get(acElement.getId()),
304                 instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
305         }
306     }
307 }