a3eafd844aee1ca72ac1f6b61e478bd8fb157683
[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.setReplicaId(cacheProvider.getReplicaId());
81                 automationCompositionAck.setMessage("Already deleted or never used");
82                 automationCompositionAck.setResult(true);
83                 automationCompositionAck.setStateChangeResult(StateChangeResult.NO_ERROR);
84                 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
85                 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
86                 publisher.sendAutomationCompositionAck(automationCompositionAck);
87             } else {
88                 LOGGER.debug("Automation composition {} does not use this participant",
89                         stateChangeMsg.getAutomationCompositionId());
90             }
91             return;
92         }
93
94         switch (stateChangeMsg.getDeployOrderedState()) {
95             case UNDEPLOY -> handleUndeployState(stateChangeMsg.getMessageId(), automationComposition,
96                     stateChangeMsg.getStartPhase());
97             case DELETE -> handleDeleteState(stateChangeMsg.getMessageId(), automationComposition,
98                     stateChangeMsg.getStartPhase());
99             default ->
100                     LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
101         }
102     }
103
104     /**
105      * Handle a automation composition properties update message.
106      *
107      * @param updateMsg the properties update message
108      */
109     public void handleAcPropertyUpdate(PropertiesUpdate updateMsg) {
110
111         if (updateMsg.getParticipantUpdatesList().isEmpty()) {
112             LOGGER.warn("No AutomationCompositionElement updates in message {}",
113                     updateMsg.getAutomationCompositionId());
114             return;
115         }
116
117         for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
118             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
119
120                 var acCopy = new AutomationComposition(cacheProvider.getAutomationComposition(
121                     updateMsg.getAutomationCompositionId()));
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         for (var elementDeploy : acElementDeployList) {
158             var element = automationComposition.getElements().get(elementDeploy.getId());
159             var compositionInProperties = cacheProvider
160                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
161             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
162             if (startPhaseMsg.equals(startPhase)) {
163                 var compositionElement = cacheProvider.createCompositionElementDto(
164                         automationComposition.getCompositionId(), element, compositionInProperties);
165                 var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
166                     elementDeploy.getToscaServiceTemplateFragment(),
167                     elementDeploy.getProperties(), element.getOutProperties());
168                 listener.deploy(messageId, compositionElement, instanceElement);
169             }
170         }
171     }
172
173     private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition,
174         UUID compositionId) {
175         Map<UUID, CompositionElementDto> map = new HashMap<>();
176         for (var element : automationComposition.getElements().values()) {
177             var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
178             var compositionElement = cacheProvider
179                     .createCompositionElementDto(compositionId, element, compositionInProperties);
180             map.put(element.getId(), compositionElement);
181         }
182         return map;
183     }
184
185     private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) {
186         return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId());
187     }
188
189     private Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) {
190         Map<UUID, InstanceElementDto> map = new HashMap<>();
191         for (var element : automationComposition.getElements().values()) {
192             var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
193                 null, element.getProperties(), element.getOutProperties());
194             map.put(element.getId(), instanceElement);
195         }
196         return map;
197     }
198
199     private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
200         AutomationComposition acCopy) {
201         var instanceElementDtoMap = getInstanceElementDtoMap(acCopy);
202         var instanceElementDtoMapUpdated = getInstanceElementDtoMap(
203             cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
204         var compositionElementDtoMap = getCompositionElementDtoMap(acCopy);
205         for (var acElement : acElements) {
206             listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
207                 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
208         }
209     }
210
211     private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy,
212         DeployState deployState) {
213         var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
214         for (var element : participantDeploy.getAcElementList()) {
215             var acElement = acElementList.get(element.getId());
216             AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
217             acElement.setDeployState(deployState);
218             acElement.setDefinition(element.getDefinition());
219         }
220     }
221
222     /**
223      * Method to handle when the new state from participant is UNINITIALISED state.
224      *
225      * @param messageId the messageId
226      * @param automationComposition participant response
227      * @param startPhaseMsg startPhase from message
228      */
229     private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
230             Integer startPhaseMsg) {
231         automationComposition.setCompositionTargetId(null);
232         for (var element : automationComposition.getElements().values()) {
233             var compositionInProperties = cacheProvider
234                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
235             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
236             if (startPhaseMsg.equals(startPhase)) {
237                 element.setDeployState(DeployState.UNDEPLOYING);
238                 var compositionElement = cacheProvider.createCompositionElementDto(
239                         automationComposition.getCompositionId(), element, compositionInProperties);
240                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
241                     null, element.getProperties(), element.getOutProperties());
242                 listener.undeploy(messageId, compositionElement, instanceElement);
243             }
244         }
245     }
246
247     private void handleDeleteState(UUID messageId, final AutomationComposition automationComposition,
248             Integer startPhaseMsg) {
249         for (var element : automationComposition.getElements().values()) {
250             var compositionInProperties = cacheProvider
251                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
252             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
253             if (startPhaseMsg.equals(startPhase)) {
254                 element.setDeployState(DeployState.DELETING);
255                 var compositionElement = cacheProvider.createCompositionElementDto(
256                         automationComposition.getCompositionId(), element, compositionInProperties);
257                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
258                     null, element.getProperties(), element.getOutProperties());
259                 listener.delete(messageId, compositionElement, instanceElement);
260             }
261         }
262     }
263
264     /**
265      * Handles AutomationComposition Migration.
266      *
267      * @param migrationMsg the AutomationCompositionMigration
268      */
269     public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
270         if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
271             return;
272         }
273
274         var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
275         if (automationComposition == null) {
276             LOGGER.debug("Automation composition {} does not use this participant",
277                     migrationMsg.getAutomationCompositionId());
278             return;
279         }
280         var acCopy = new AutomationComposition(automationComposition);
281         automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
282         for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
283             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
284
285                 updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
286                         DeployState.MIGRATING);
287
288                 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
289                     acCopy, migrationMsg.getCompositionTargetId());
290             }
291         }
292     }
293
294     private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
295             AutomationComposition acCopy, UUID compositionTargetId) {
296         var compositionElementMap = getCompositionElementDtoMap(acCopy);
297         var instanceElementMap = getInstanceElementDtoMap(acCopy);
298         var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
299         var compositionElementTargetMap = getCompositionElementDtoMap(automationComposition, compositionTargetId);
300         var instanceElementMigrateMap = getInstanceElementDtoMap(automationComposition);
301
302         for (var acElement : acElements) {
303             listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
304                 compositionElementTargetMap.get(acElement.getId()),
305                 instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
306         }
307     }
308 }