fd82c37ba9b91b9372ca64e15e010ca4cd7efe32
[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.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.UUID;
29 import java.util.stream.Collectors;
30 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
31 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
32 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
33 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
34 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
35 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
39 import org.onap.policy.clamp.models.acm.concepts.DeployState;
40 import org.onap.policy.clamp.models.acm.concepts.LockState;
41 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
42 import org.onap.policy.clamp.models.acm.concepts.ParticipantRestartAc;
43 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
44 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
45 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy;
46 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
47 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
48 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange;
49 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
50 import org.onap.policy.clamp.models.acm.messages.kafka.participant.PropertiesUpdate;
51 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
52 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
53 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import org.springframework.stereotype.Component;
57
58 /*
59  * This class is responsible for managing the state of all automation compositions in the participant.
60  */
61 @Component
62 public class AutomationCompositionHandler {
63     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
64
65     private final CacheProvider cacheProvider;
66     private final ParticipantMessagePublisher publisher;
67     private final ThreadHandler listener;
68     private final AcInstanceStateResolver acInstanceStateResolver;
69
70     /**
71      * Constructor, set the participant ID and messageSender.
72      *
73      * @param cacheProvider the Cache Provider
74      * @param publisher the ParticipantMessage Publisher
75      * @param listener the ThreadHandler Listener
76      */
77     public AutomationCompositionHandler(CacheProvider cacheProvider, ParticipantMessagePublisher publisher,
78             ThreadHandler listener) {
79         this.cacheProvider = cacheProvider;
80         this.publisher = publisher;
81         this.listener = listener;
82         this.acInstanceStateResolver = new AcInstanceStateResolver();
83     }
84
85     /**
86      * Handle a automation composition state change message.
87      *
88      * @param stateChangeMsg the state change message
89      */
90     public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
91         if (stateChangeMsg.getAutomationCompositionId() == null) {
92             return;
93         }
94
95         var automationComposition = cacheProvider.getAutomationComposition(stateChangeMsg.getAutomationCompositionId());
96
97         if (automationComposition == null) {
98             if (DeployOrder.DELETE.equals(stateChangeMsg.getDeployOrderedState())) {
99                 var automationCompositionAck = new AutomationCompositionDeployAck(
100                         ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
101                 automationCompositionAck.setParticipantId(cacheProvider.getParticipantId());
102                 automationCompositionAck.setMessage("Already deleted or never used");
103                 automationCompositionAck.setResult(true);
104                 automationCompositionAck.setStateChangeResult(StateChangeResult.NO_ERROR);
105                 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
106                 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
107                 publisher.sendAutomationCompositionAck(automationCompositionAck);
108             } else {
109                 LOGGER.debug("Automation composition {} does not use this participant",
110                         stateChangeMsg.getAutomationCompositionId());
111             }
112             return;
113         }
114
115         if (!checkConsistantOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
116                 stateChangeMsg.getLockOrderedState())) {
117             LOGGER.warn("Not Consistant OrderState Automation composition {}",
118                     stateChangeMsg.getAutomationCompositionId());
119             return;
120         }
121
122         if (DeployOrder.NONE.equals(stateChangeMsg.getDeployOrderedState())) {
123             handleLockOrderState(stateChangeMsg.getMessageId(), automationComposition,
124                     stateChangeMsg.getLockOrderedState(), stateChangeMsg.getStartPhase());
125         } else {
126             handleDeployOrderState(stateChangeMsg.getMessageId(), automationComposition,
127                     stateChangeMsg.getDeployOrderedState(), stateChangeMsg.getStartPhase());
128         }
129     }
130
131     private boolean checkConsistantOrderState(AutomationComposition automationComposition, DeployOrder deployOrder,
132             LockOrder lockOrder) {
133         if (DeployOrder.UPDATE.equals(deployOrder)) {
134             return true;
135         }
136         return acInstanceStateResolver.resolve(deployOrder, lockOrder, automationComposition.getDeployState(),
137                 automationComposition.getLockState(), automationComposition.getStateChangeResult()) != null;
138     }
139
140     /**
141      * Method to handle state changes.
142      *
143      * @param messageId the messageId
144      * @param automationComposition participant response
145      * @param orderedState automation composition ordered state
146      * @param startPhaseMsg startPhase from message
147      */
148     private void handleDeployOrderState(UUID messageId, final AutomationComposition automationComposition,
149             DeployOrder orderedState, Integer startPhaseMsg) {
150
151         switch (orderedState) {
152             case UNDEPLOY:
153                 handleUndeployState(messageId, automationComposition, startPhaseMsg);
154                 break;
155             case DELETE:
156                 handleDeleteState(messageId, automationComposition, startPhaseMsg);
157                 break;
158
159             default:
160                 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
161                 break;
162         }
163     }
164
165     /**
166      * Method to handle state changes.
167      *
168      * @param messageId the messageId
169      * @param automationComposition participant response
170      * @param orderedState automation composition ordered state
171      * @param startPhaseMsg startPhase from message
172      */
173     private void handleLockOrderState(UUID messageId, final AutomationComposition automationComposition,
174             LockOrder orderedState, Integer startPhaseMsg) {
175
176         switch (orderedState) {
177             case LOCK:
178                 handleLockState(messageId, automationComposition, startPhaseMsg);
179                 break;
180             case UNLOCK:
181                 handleUnlockState(messageId, automationComposition, startPhaseMsg);
182                 break;
183             default:
184                 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
185                 break;
186         }
187     }
188
189     /**
190      * Handle a automation composition properties update message.
191      *
192      * @param updateMsg the properties update message
193      */
194     public void handleAcPropertyUpdate(PropertiesUpdate updateMsg) {
195
196         if (updateMsg.getParticipantUpdatesList().isEmpty()) {
197             LOGGER.warn("No AutomationCompositionElement updates in message {}",
198                     updateMsg.getAutomationCompositionId());
199             return;
200         }
201
202         for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
203             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
204
205                 var acCopy = new AutomationComposition(cacheProvider.getAutomationComposition(
206                     updateMsg.getAutomationCompositionId()));
207                 updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy,
208                         DeployState.UPDATING);
209
210                 callParticipantUpdateProperty(updateMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy);
211             }
212         }
213     }
214
215     /**
216      * Handle a automation composition Deploy message.
217      *
218      * @param deployMsg the Deploy message
219      */
220     public void handleAutomationCompositionDeploy(AutomationCompositionDeploy deployMsg) {
221
222         if (deployMsg.getParticipantUpdatesList().isEmpty()) {
223             LOGGER.warn("No AutomationCompositionElement deploy in message {}", deployMsg.getAutomationCompositionId());
224             return;
225         }
226
227         for (var participantDeploy : deployMsg.getParticipantUpdatesList()) {
228             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
229                 if (deployMsg.isFirstStartPhase()) {
230                     cacheProvider.initializeAutomationComposition(deployMsg.getCompositionId(),
231                             deployMsg.getAutomationCompositionId(), participantDeploy);
232                 }
233                 callParticipanDeploy(deployMsg.getMessageId(), participantDeploy.getAcElementList(),
234                         deployMsg.getStartPhase(), deployMsg.getAutomationCompositionId());
235             }
236         }
237     }
238
239     private void callParticipanDeploy(UUID messageId, List<AcElementDeploy> acElementDeployList,
240             Integer startPhaseMsg, UUID instanceId) {
241         var automationComposition = cacheProvider.getAutomationComposition(instanceId);
242         for (var elementDeploy : acElementDeployList) {
243             var element = automationComposition.getElements().get(elementDeploy.getId());
244             var compositionInProperties = cacheProvider
245                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
246             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
247             if (startPhaseMsg.equals(startPhase)) {
248                 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
249                     element, compositionInProperties);
250                 var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
251                     elementDeploy.getToscaServiceTemplateFragment(),
252                     elementDeploy.getProperties(), element.getOutProperties());
253                 listener.deploy(messageId, compositionElement, instanceElement);
254             }
255         }
256     }
257
258     private CompositionElementDto createCompositionElementDto(UUID compositionId, AutomationCompositionElement element,
259         Map<String, Object> compositionInProperties) {
260         var compositionOutProperties = cacheProvider.getAcElementsDefinitions()
261             .get(compositionId).get(element.getDefinition()).getOutProperties();
262         return new CompositionElementDto(compositionId,
263             element.getDefinition(), compositionInProperties, compositionOutProperties);
264     }
265
266     private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition,
267         UUID compositionId) {
268         Map<UUID, CompositionElementDto> map = new HashMap<>();
269         for (var element : automationComposition.getElements().values()) {
270             var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
271             var compositionElement = createCompositionElementDto(compositionId, element, compositionInProperties);
272             map.put(element.getId(), compositionElement);
273         }
274         return map;
275     }
276
277     private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) {
278         return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId());
279     }
280
281     private Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) {
282         Map<UUID, InstanceElementDto> map = new HashMap<>();
283         for (var element : automationComposition.getElements().values()) {
284             var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
285                 null, element.getProperties(), element.getOutProperties());
286             map.put(element.getId(), instanceElement);
287         }
288         return map;
289     }
290
291     private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
292         AutomationComposition acCopy) {
293         var instanceElementDtoMap = getInstanceElementDtoMap(acCopy);
294         var instanceElementDtoMapUpdated = getInstanceElementDtoMap(
295             cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
296         var compositionElementDtoMap = getCompositionElementDtoMap(acCopy);
297         for (var acElement : acElements) {
298             listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
299                 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
300         }
301     }
302
303     private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy,
304         DeployState deployState) {
305         var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
306         for (var element : participantDeploy.getAcElementList()) {
307             var acElement = acElementList.get(element.getId());
308             acElement.getProperties().putAll(element.getProperties());
309             acElement.setDeployState(deployState);
310             acElement.setDefinition(element.getDefinition());
311         }
312     }
313
314     /**
315      * Method to handle when the new state from participant is UNINITIALISED state.
316      *
317      * @param messageId the messageId
318      * @param automationComposition participant response
319      * @param startPhaseMsg startPhase from message
320      */
321     private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
322             Integer startPhaseMsg) {
323         automationComposition.setCompositionTargetId(null);
324         for (var element : automationComposition.getElements().values()) {
325             var compositionInProperties = cacheProvider
326                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
327             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
328             if (startPhaseMsg.equals(startPhase)) {
329                 element.setDeployState(DeployState.UNDEPLOYING);
330                 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
331                     element, compositionInProperties);
332                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
333                     null, element.getProperties(), element.getOutProperties());
334                 listener.undeploy(messageId, compositionElement, instanceElement);
335             }
336         }
337     }
338
339     private void handleDeleteState(UUID messageId, final AutomationComposition automationComposition,
340             Integer startPhaseMsg) {
341         for (var element : automationComposition.getElements().values()) {
342             var compositionInProperties = cacheProvider
343                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
344             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
345             if (startPhaseMsg.equals(startPhase)) {
346                 element.setDeployState(DeployState.DELETING);
347                 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
348                     element, compositionInProperties);
349                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
350                     null, element.getProperties(), element.getOutProperties());
351                 listener.delete(messageId, compositionElement, instanceElement);
352             }
353         }
354     }
355
356     /**
357      * Method to handle when the new state from participant is PASSIVE state.
358      *
359      * @param messageId the messageId
360      * @param automationComposition participant response
361      * @param startPhaseMsg startPhase from message
362      */
363     private void handleLockState(UUID messageId, final AutomationComposition automationComposition,
364             Integer startPhaseMsg) {
365         for (var element : automationComposition.getElements().values()) {
366             var compositionInProperties = cacheProvider
367                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
368             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
369             if (startPhaseMsg.equals(startPhase)) {
370                 element.setLockState(LockState.LOCKING);
371                 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
372                     element, compositionInProperties);
373                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
374                     null, element.getProperties(), element.getOutProperties());
375                 listener.lock(messageId, compositionElement, instanceElement);
376             }
377         }
378     }
379
380     /**
381      * Method to handle when the new state from participant is RUNNING state.
382      *
383      * @param messageId the messageId
384      * @param automationComposition participant response
385      * @param startPhaseMsg startPhase from message
386      */
387     private void handleUnlockState(UUID messageId, final AutomationComposition automationComposition,
388             Integer startPhaseMsg) {
389         for (var element : automationComposition.getElements().values()) {
390             var compositionInProperties = cacheProvider
391                 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
392             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
393             if (startPhaseMsg.equals(startPhase)) {
394                 element.setLockState(LockState.UNLOCKING);
395                 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
396                     element, compositionInProperties);
397                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
398                     null, element.getProperties(), element.getOutProperties());
399                 listener.unlock(messageId, compositionElement, instanceElement);
400             }
401         }
402     }
403
404     /**
405      * Handles prime a Composition Definition.
406      *
407      * @param messageId the messageId
408      * @param compositionId the compositionId
409      * @param list the list of AutomationCompositionElementDefinition
410      */
411     public void prime(UUID messageId, UUID compositionId, List<AutomationCompositionElementDefinition> list) {
412         var inPropertiesMap = list.stream().collect(Collectors.toMap(
413             AutomationCompositionElementDefinition::getAcElementDefinitionId,
414             el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
415         var outPropertiesMap = list.stream().collect(Collectors.toMap(
416             AutomationCompositionElementDefinition::getAcElementDefinitionId,
417             AutomationCompositionElementDefinition::getOutProperties));
418         listener.prime(messageId, new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap));
419     }
420
421     /**
422      * Handles deprime a Composition Definition.
423      *
424      * @param messageId the messageId
425      * @param compositionId the compositionId
426      */
427     public void deprime(UUID messageId, UUID compositionId) {
428         var acElementsDefinitions = cacheProvider.getAcElementsDefinitions().get(compositionId);
429         if (acElementsDefinitions == null) {
430             // this participant does not handle this composition
431             return;
432         }
433         var list = new ArrayList<>(acElementsDefinitions.values());
434         var inPropertiesMap = list.stream().collect(Collectors.toMap(
435             AutomationCompositionElementDefinition::getAcElementDefinitionId,
436             el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
437         var outPropertiesMap = list.stream().collect(Collectors.toMap(
438             AutomationCompositionElementDefinition::getAcElementDefinitionId,
439             AutomationCompositionElementDefinition::getOutProperties));
440         listener.deprime(messageId, new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap));
441     }
442
443     /**
444      * Handles restarted scenario.
445      *
446      * @param messageId the messageId
447      * @param compositionId the compositionId
448      * @param list the list of AutomationCompositionElementDefinition
449      * @param state the state of the composition
450      * @param automationCompositionList list of ParticipantRestartAc
451      */
452     public void restarted(UUID messageId, UUID compositionId, List<AutomationCompositionElementDefinition> list,
453             AcTypeState state, List<ParticipantRestartAc> automationCompositionList) {
454
455         for (var automationcomposition : automationCompositionList) {
456             cacheProvider.initializeAutomationComposition(compositionId, automationcomposition);
457         }
458         var inPropertiesMap = list.stream().collect(Collectors.toMap(
459             AutomationCompositionElementDefinition::getAcElementDefinitionId,
460             el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
461         var outPropertiesMap = list.stream().collect(Collectors.toMap(
462             AutomationCompositionElementDefinition::getAcElementDefinitionId,
463             AutomationCompositionElementDefinition::getOutProperties));
464         var composition = new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap);
465         listener.restarted(messageId, composition, state, automationCompositionList);
466     }
467
468     /**
469      * Handles AutomationComposition Migration.
470      *
471      * @param migrationMsg the AutomationCompositionMigration
472      */
473     public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
474         if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
475             return;
476         }
477
478         var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
479         if (automationComposition == null) {
480             LOGGER.debug("Automation composition {} does not use this participant",
481                     migrationMsg.getAutomationCompositionId());
482             return;
483         }
484         var acCopy = new AutomationComposition(automationComposition);
485         automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
486         for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
487             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
488
489                 updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
490                         DeployState.MIGRATING);
491
492                 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
493                     acCopy, migrationMsg.getCompositionTargetId());
494             }
495         }
496     }
497
498     private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
499             AutomationComposition acCopy, UUID compositionTargetId) {
500         var compositionElementMap = getCompositionElementDtoMap(acCopy);
501         var instanceElementMap = getInstanceElementDtoMap(acCopy);
502         var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
503         var compositionElementTargetMap = getCompositionElementDtoMap(automationComposition, compositionTargetId);
504         var instanceElementMigrateMap = getInstanceElementDtoMap(automationComposition);
505
506         for (var acElement : acElements) {
507             listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
508                 compositionElementTargetMap.get(acElement.getId()),
509                 instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
510         }
511     }
512 }