1eaf63dcc489633a5fa96b6bfc06d34bb896299f
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021-2023 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.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.UUID;
30 import lombok.Getter;
31 import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
32 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
33 import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters;
34 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
35 import org.onap.policy.clamp.models.acm.concepts.AcElementDeployAck;
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.ParticipantUtils;
43 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionDeploy;
44 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionDeployAck;
45 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange;
46 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType;
47 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
48 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
49 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
50 import org.onap.policy.models.base.PfModelException;
51 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.stereotype.Component;
56
57 /*
58  * This class is responsible for managing the state of all automation compositions in the participant.
59  */
60 @Component
61 public class AutomationCompositionHandler {
62     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
63
64     private final UUID participantId;
65     private final ParticipantMessagePublisher publisher;
66     private final AcInstanceStateResolver acInstanceStateResolver;
67
68     @Getter
69     private final Map<UUID, AutomationComposition> automationCompositionMap = new LinkedHashMap<>();
70
71     @Getter
72     private final Map<UUID, AutomationCompositionElement> elementsOnThisParticipant = new LinkedHashMap<>();
73
74     @Getter
75     private final List<AutomationCompositionElementListener> listeners = new ArrayList<>();
76
77     /**
78      * Constructor, set the participant ID and messageSender.
79      *
80      * @param parameters the parameters of the participant
81      * @param publisher the ParticipantMessage Publisher
82      */
83     public AutomationCompositionHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
84         this.participantId = parameters.getIntermediaryParameters().getParticipantId();
85         this.publisher = publisher;
86         this.acInstanceStateResolver = new AcInstanceStateResolver();
87     }
88
89     public void registerAutomationCompositionElementListener(AutomationCompositionElementListener listener) {
90         listeners.add(listener);
91     }
92
93     /**
94      * Handle a automation composition element state change message.
95      *
96      * @param automationCompositionId the automationComposition Id
97      * @param id the automationComposition UUID
98      * @param deployState the DeployState state
99      */
100     public void updateAutomationCompositionElementState(UUID automationCompositionId, UUID id, DeployState deployState,
101             LockState lockState) {
102
103         if (id == null) {
104             LOGGER.warn("Cannot update Automation composition element state, id is null");
105             return;
106         }
107
108         // Update states of AutomationCompositionElement in automationCompositionMap
109         for (var automationComposition : automationCompositionMap.values()) {
110             var element = automationComposition.getElements().get(id);
111             if (element != null) {
112                 element.setDeployState(deployState);
113                 element.setLockState(lockState);
114                 element.setUseState(getUseState(automationCompositionId, id));
115                 element.setOperationalState(getOperationalState(automationCompositionId, id));
116             }
117             var checkOpt = automationComposition.getElements().values().stream()
118                     .filter(acElement -> !deployState.equals(acElement.getDeployState())).findAny();
119             if (checkOpt.isEmpty()) {
120                 automationComposition.setDeployState(deployState);
121             }
122             checkOpt = automationComposition.getElements().values().stream()
123                     .filter(acElement -> !lockState.equals(acElement.getLockState())).findAny();
124             if (checkOpt.isEmpty()) {
125                 automationComposition.setLockState(lockState);
126             }
127         }
128
129         // Update states of AutomationCompositionElement in elementsOnThisParticipant
130         var acElement = elementsOnThisParticipant.get(id);
131         if (acElement != null) {
132             var automationCompositionStateChangeAck =
133                     new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
134             automationCompositionStateChangeAck.setParticipantId(participantId);
135             automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
136             acElement.setDeployState(deployState);
137             acElement.setLockState(lockState);
138             acElement.setUseState(getUseState(automationCompositionId, id));
139             acElement.setOperationalState(getOperationalState(automationCompositionId, id));
140             automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
141                     new AcElementDeployAck(deployState, lockState,
142                             acElement.getOperationalState(), acElement.getUseState(), true,
143                             "Automation composition element {} state changed to {}\", id, newState)"));
144             LOGGER.debug("Automation composition element {} state changed to {}", id, deployState);
145             automationCompositionStateChangeAck
146                     .setMessage("AutomationCompositionElement state changed to {} " + deployState);
147             automationCompositionStateChangeAck.setResult(true);
148             publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
149         }
150     }
151
152     /**
153      * Handle a automation composition state change message.
154      *
155      * @param stateChangeMsg the state change message
156      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
157      */
158     public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
159             List<AutomationCompositionElementDefinition> acElementDefinitions) {
160         if (stateChangeMsg.getAutomationCompositionId() == null) {
161             return;
162         }
163
164         var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
165
166         if (automationComposition == null) {
167             var automationCompositionAck =
168                     new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
169             automationCompositionAck.setParticipantId(participantId);
170             automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
171                     + " does not use this participant " + participantId);
172             automationCompositionAck.setResult(false);
173             automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
174             automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
175             publisher.sendAutomationCompositionAck(automationCompositionAck);
176             LOGGER.debug("Automation composition {} does not use this participant",
177                     stateChangeMsg.getAutomationCompositionId());
178             return;
179         }
180
181         if (!checkConsistantOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
182                 stateChangeMsg.getLockOrderedState())) {
183             var automationCompositionAck =
184                     new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
185             automationCompositionAck.setParticipantId(participantId);
186             automationCompositionAck.setMessage("Automation composition is already in state "
187                     + stateChangeMsg.getDeployOrderedState() + " and " + stateChangeMsg.getLockOrderedState());
188             automationCompositionAck.setResult(false);
189             automationCompositionAck.setAutomationCompositionId(automationComposition.getInstanceId());
190             publisher.sendAutomationCompositionAck(automationCompositionAck);
191             return;
192         }
193
194         if (DeployOrder.NONE.equals(stateChangeMsg.getDeployOrderedState())) {
195             handleLockOrderState(automationComposition, stateChangeMsg.getLockOrderedState(),
196                     stateChangeMsg.getStartPhase(), acElementDefinitions);
197         } else {
198             handleDeployOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
199                     stateChangeMsg.getStartPhase(), acElementDefinitions);
200         }
201     }
202
203     private boolean checkConsistantOrderState(AutomationComposition automationComposition, DeployOrder deployOrder,
204             LockOrder lockOrder) {
205         return acInstanceStateResolver.resolve(deployOrder, lockOrder, automationComposition.getDeployState(),
206                 automationComposition.getLockState()) != null;
207     }
208
209     /**
210      * Method to handle state changes.
211      *
212      * @param automationComposition participant response
213      * @param orderedState automation composition ordered state
214      * @param startPhaseMsg startPhase from message
215      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
216      */
217     private void handleDeployOrderState(final AutomationComposition automationComposition, DeployOrder orderedState,
218             Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
219
220         if (DeployOrder.UNDEPLOY.equals(orderedState)) {
221             handleUndeployState(automationComposition, startPhaseMsg, acElementDefinitions);
222         } else {
223             LOGGER.debug("StateChange message has no state, state is null {}", automationComposition.getKey());
224         }
225     }
226
227     /**
228      * Method to handle state changes.
229      *
230      * @param automationComposition participant response
231      * @param orderedState automation composition ordered state
232      * @param startPhaseMsg startPhase from message
233      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
234      */
235     private void handleLockOrderState(final AutomationComposition automationComposition, LockOrder orderedState,
236             Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
237
238         switch (orderedState) {
239             case LOCK:
240                 handleLockState(automationComposition, startPhaseMsg, acElementDefinitions);
241                 break;
242             case UNLOCK:
243                 handleUnlockState(automationComposition, startPhaseMsg, acElementDefinitions);
244                 break;
245             default:
246                 LOGGER.debug("StateChange message has no state, state is null {}", automationComposition.getKey());
247                 break;
248         }
249     }
250
251     /**
252      * Handle a automation composition Deploy message.
253      *
254      * @param updateMsg the Deploy message
255      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
256      */
257     public void handleAutomationCompositionDeploy(AutomationCompositionDeploy updateMsg,
258             List<AutomationCompositionElementDefinition> acElementDefinitions) {
259
260         if (updateMsg.getParticipantUpdatesList().isEmpty()) {
261             LOGGER.warn("No AutomationCompositionElement updates in message {}",
262                     updateMsg.getAutomationCompositionId());
263             return;
264         }
265
266         for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
267             if (participantId.equals(participantDeploy.getParticipantId())) {
268                 if (updateMsg.isFirstStartPhase()) {
269                     initializeDeploy(updateMsg.getMessageId(), updateMsg.getAutomationCompositionId(),
270                             participantDeploy);
271                 }
272                 callParticipanDeploy(participantDeploy.getAcElementList(), acElementDefinitions,
273                         updateMsg.getStartPhase(), updateMsg.getAutomationCompositionId());
274             }
275         }
276     }
277
278     private void initializeDeploy(UUID messageId, UUID instanceId, ParticipantDeploy participantDeploy) {
279         var automationComposition = automationCompositionMap.get(instanceId);
280
281         if (automationComposition != null) {
282             var automationCompositionUpdateAck =
283                     new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY_ACK);
284             automationCompositionUpdateAck.setParticipantId(participantId);
285
286             automationCompositionUpdateAck.setMessage(
287                     "Automation composition " + instanceId + " already defined on participant " + participantId);
288             automationCompositionUpdateAck.setResult(false);
289             automationCompositionUpdateAck.setResponseTo(messageId);
290             automationCompositionUpdateAck.setAutomationCompositionId(instanceId);
291             publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
292             return;
293         }
294
295         automationComposition = new AutomationComposition();
296         automationComposition.setInstanceId(instanceId);
297         var acElements = storeElementsOnThisParticipant(participantDeploy);
298         automationComposition.setElements(prepareAcElementMap(acElements));
299         automationCompositionMap.put(instanceId, automationComposition);
300     }
301
302     private void callParticipanDeploy(List<AcElementDeploy> acElements,
303             List<AutomationCompositionElementDefinition> acElementDefinitions, Integer startPhaseMsg,
304             UUID automationCompositionId) {
305         try {
306             for (var element : acElements) {
307                 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition());
308                 if (acElementNodeTemplate != null) {
309                     int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
310                     if (startPhaseMsg.equals(startPhase)) {
311                         for (var acElementListener : listeners) {
312                             var map = new HashMap<>(acElementNodeTemplate.getProperties());
313                             map.putAll(element.getProperties());
314                             acElementListener.deploy(automationCompositionId, element, map);
315                         }
316                     }
317                 }
318             }
319         } catch (PfModelException e) {
320             LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
321         }
322
323     }
324
325     private ToscaNodeTemplate getAcElementNodeTemplate(
326             List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
327
328         for (var acElementDefinition : acElementDefinitions) {
329             if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
330                 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
331             }
332         }
333         return null;
334     }
335
336     private List<AutomationCompositionElement> storeElementsOnThisParticipant(ParticipantDeploy participantDeploy) {
337         List<AutomationCompositionElement> acElementList = new ArrayList<>();
338         for (var element : participantDeploy.getAcElementList()) {
339             var acElement = new AutomationCompositionElement();
340             acElement.setId(element.getId());
341             acElement.setParticipantId(participantDeploy.getParticipantId());
342             acElement.setDefinition(element.getDefinition());
343             acElement.setDeployState(DeployState.DEPLOYING);
344             acElement.setLockState(LockState.NONE);
345             elementsOnThisParticipant.put(element.getId(), acElement);
346             acElementList.add(acElement);
347         }
348         return acElementList;
349     }
350
351     private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
352         Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
353         for (var element : acElements) {
354             acElementMap.put(element.getId(), element);
355         }
356         return acElementMap;
357     }
358
359     /**
360      * Method to handle when the new state from participant is UNINITIALISED state.
361      *
362      * @param automationComposition participant response
363      * @param startPhaseMsg startPhase from message
364      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
365      */
366     private void handleUndeployState(final AutomationComposition automationComposition, Integer startPhaseMsg,
367             List<AutomationCompositionElementDefinition> acElementDefinitions) {
368
369         automationComposition.getElements().values().stream()
370                 .forEach(acElement -> automationCompositionElementUndeploy(automationComposition.getInstanceId(),
371                         acElement, startPhaseMsg, acElementDefinitions));
372
373         boolean isAllUninitialised = automationComposition.getElements().values().stream()
374                 .filter(element -> !DeployState.UNDEPLOYED.equals(element.getDeployState())).findAny().isEmpty();
375         if (isAllUninitialised) {
376             automationCompositionMap.remove(automationComposition.getInstanceId());
377             automationComposition.getElements().values()
378                     .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
379         }
380     }
381
382     /**
383      * Method to handle when the new state from participant is PASSIVE state.
384      *
385      * @param automationComposition participant response
386      * @param startPhaseMsg startPhase from message
387      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
388      */
389     private void handleLockState(final AutomationComposition automationComposition, Integer startPhaseMsg,
390             List<AutomationCompositionElementDefinition> acElementDefinitions) {
391         automationComposition.getElements().values().stream()
392                 .forEach(acElement -> automationCompositionElementLock(automationComposition.getInstanceId(), acElement,
393                         startPhaseMsg, acElementDefinitions));
394     }
395
396     /**
397      * Method to handle when the new state from participant is RUNNING state.
398      *
399      * @param automationComposition participant response
400      * @param startPhaseMsg startPhase from message
401      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
402      */
403     private void handleUnlockState(final AutomationComposition automationComposition, Integer startPhaseMsg,
404             List<AutomationCompositionElementDefinition> acElementDefinitions) {
405         automationComposition.getElements().values().stream()
406                 .forEach(acElement -> automationCompositionElementUnlock(automationComposition.getInstanceId(),
407                         acElement, startPhaseMsg, acElementDefinitions));
408     }
409
410     private void automationCompositionElementLock(UUID instanceId, AutomationCompositionElement acElement,
411             Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
412         var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
413         if (acElementNodeTemplate != null) {
414             int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
415             if (startPhaseMsg.equals(startPhase)) {
416                 for (var acElementListener : listeners) {
417                     try {
418                         acElementListener.lock(instanceId, acElement.getId());
419                         updateAutomationCompositionElementState(instanceId, acElement.getId(), DeployState.DEPLOYED,
420                                 LockState.LOCKED);
421                     } catch (PfModelException e) {
422                         LOGGER.error("Automation composition element lock failed {}", instanceId);
423                     }
424                 }
425             }
426         }
427     }
428
429     private void automationCompositionElementUnlock(UUID instanceId, AutomationCompositionElement acElement,
430             Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
431         var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
432         if (acElementNodeTemplate != null) {
433             int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
434             if (startPhaseMsg.equals(startPhase)) {
435                 for (var acElementListener : listeners) {
436                     try {
437                         acElementListener.unlock(instanceId, acElement.getId());
438                         updateAutomationCompositionElementState(instanceId, acElement.getId(), DeployState.DEPLOYED,
439                                 LockState.UNLOCKED);
440                     } catch (PfModelException e) {
441                         LOGGER.error("Automation composition element unlock failed {}", instanceId);
442                     }
443                 }
444             }
445         }
446     }
447
448     private void automationCompositionElementUndeploy(UUID instanceId, AutomationCompositionElement acElement,
449             Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
450         var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
451         if (acElementNodeTemplate != null) {
452             int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
453             if (startPhaseMsg.equals(startPhase)) {
454                 for (var acElementListener : listeners) {
455                     try {
456                         acElementListener.undeploy(instanceId, acElement.getId());
457                     } catch (PfModelException e) {
458                         LOGGER.error("Automation composition element update failed {}", instanceId);
459                     }
460                 }
461             }
462         }
463     }
464
465     /**
466      * Get UseState.
467      *
468      * @param instanceId the instance Id
469      * @param acElementId the Automation Composition Element Id
470      * @return the UseState of the Automation Composition Element
471      */
472     public String getUseState(UUID instanceId, UUID acElementId) {
473         for (var acElementListener : listeners) {
474             try {
475                 return acElementListener.getUseState(instanceId, acElementId);
476             } catch (PfModelException e) {
477                 LOGGER.error("Automation composition element get Use State failed {}", acElementId);
478             }
479         }
480         return null;
481     }
482
483     /**
484      * Get OperationalState.
485      *
486      * @param instanceId the instance Id
487      * @param acElementId the Automation Composition Element Id
488      * @return the OperationalState of the Automation Composition Element
489      */
490     public String getOperationalState(UUID instanceId, UUID acElementId) {
491         for (var acElementListener : listeners) {
492             try {
493                 return acElementListener.getOperationalState(instanceId, acElementId);
494             } catch (PfModelException e) {
495                 LOGGER.error("Automation composition element get Use State failed {}", acElementId);
496             }
497         }
498         return null;
499     }
500 }