49b16d23dd029c66ab32148fb74a9bc398d1012d
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021-2022 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 java.util.stream.Collectors;
31 import lombok.Getter;
32 import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
33 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
34 import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementAck;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
39 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionOrderedState;
40 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionState;
41 import org.onap.policy.clamp.models.acm.concepts.ParticipantUpdates;
42 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
43 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionAck;
44 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange;
45 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionUpdate;
46 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType;
47 import org.onap.policy.models.base.PfModelException;
48 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
49 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.springframework.stereotype.Component;
53
54 /*
55  * This class is responsible for managing the state of all automation compositions in the participant.
56  */
57 @Component
58 public class AutomationCompositionHandler {
59     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
60
61     private final ToscaConceptIdentifier participantType;
62     private final UUID participantId;
63     private final ParticipantMessagePublisher publisher;
64
65     @Getter
66     private final Map<UUID, AutomationComposition> automationCompositionMap = new LinkedHashMap<>();
67
68     @Getter
69     private final Map<UUID, AutomationCompositionElement> elementsOnThisParticipant = new LinkedHashMap<>();
70
71     @Getter
72     private List<AutomationCompositionElementListener> listeners = new ArrayList<>();
73
74     /**
75      * Constructor, set the participant ID and messageSender.
76      *
77      * @param parameters the parameters of the participant
78      * @param publisher the ParticipantMessage Publisher
79      */
80     public AutomationCompositionHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
81         this.participantType = parameters.getIntermediaryParameters().getParticipantType();
82         this.participantId = parameters.getIntermediaryParameters().getParticipantId();
83         this.publisher = publisher;
84     }
85
86     public void registerAutomationCompositionElementListener(AutomationCompositionElementListener listener) {
87         listeners.add(listener);
88     }
89
90     /**
91      * Handle a automation composition element state change message.
92      *
93      * @param automationCompositionId the automationComposition Id
94      * @param id the automationComposition UUID
95      * @param orderedState the current state
96      * @param newState the ordered state
97      * @return automationCompositionElement the updated automation composition element
98      */
99     public AutomationCompositionElement updateAutomationCompositionElementState(UUID automationCompositionId, UUID id,
100             AutomationCompositionOrderedState orderedState, AutomationCompositionState newState) {
101
102         if (id == null) {
103             LOGGER.warn("Cannot update Automation composition element state, id is null");
104             return null;
105         }
106
107         // Update states of AutomationCompositionElement in automationCompositionMap
108         for (var automationComposition : automationCompositionMap.values()) {
109             var element = automationComposition.getElements().get(id);
110             if (element != null) {
111                 element.setOrderedState(orderedState);
112                 element.setState(newState);
113             }
114             var checkOpt = automationComposition.getElements().values().stream()
115                     .filter(acElement -> !newState.equals(acElement.getState())).findAny();
116             if (checkOpt.isEmpty()) {
117                 automationComposition.setState(newState);
118                 automationComposition.setOrderedState(orderedState);
119             }
120         }
121
122         // Update states of AutomationCompositionElement in elementsOnThisParticipant
123         var acElement = elementsOnThisParticipant.get(id);
124         if (acElement != null) {
125             var automationCompositionStateChangeAck =
126                     new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
127             automationCompositionStateChangeAck.setParticipantId(participantId);
128             automationCompositionStateChangeAck.setParticipantType(participantType);
129             automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
130             acElement.setOrderedState(orderedState);
131             acElement.setState(newState);
132             automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
133                     new AutomationCompositionElementAck(newState, true,
134                             "Automation composition element {} state changed to {}\", id, newState)"));
135             LOGGER.debug("Automation composition element {} state changed to {}", id, newState);
136             automationCompositionStateChangeAck
137                     .setMessage("AutomationCompositionElement state changed to {} " + newState);
138             automationCompositionStateChangeAck.setResult(true);
139             publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
140             return acElement;
141         }
142         return null;
143     }
144
145     /**
146      * Handle a automation composition state change message.
147      *
148      * @param stateChangeMsg the state change message
149      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
150      */
151     public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
152             List<AutomationCompositionElementDefinition> acElementDefinitions) {
153         if (stateChangeMsg.getAutomationCompositionId() == null) {
154             return;
155         }
156
157         var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
158
159         if (automationComposition == null) {
160             var automationCompositionAck =
161                     new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
162             automationCompositionAck.setParticipantId(participantId);
163             automationCompositionAck.setParticipantType(participantType);
164             automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
165                     + " does not use this participant " + participantId);
166             automationCompositionAck.setResult(false);
167             automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
168             automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
169             publisher.sendAutomationCompositionAck(automationCompositionAck);
170             LOGGER.debug("Automation composition {} does not use this participant",
171                     stateChangeMsg.getAutomationCompositionId());
172             return;
173         }
174
175         handleState(automationComposition, stateChangeMsg.getOrderedState(), stateChangeMsg.getStartPhase(),
176                 acElementDefinitions);
177     }
178
179     /**
180      * Method to handle state changes.
181      *
182      * @param automationComposition participant response
183      * @param orderedState automation composition ordered state
184      * @param startPhaseMsg startPhase from message
185      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
186      */
187     private void handleState(final AutomationComposition automationComposition,
188             AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
189             List<AutomationCompositionElementDefinition> acElementDefinitions) {
190         switch (orderedState) {
191             case UNINITIALISED:
192                 handleUninitialisedState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
193                 break;
194             case PASSIVE:
195                 handlePassiveState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
196                 break;
197             case RUNNING:
198                 handleRunningState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
199                 break;
200             default:
201                 LOGGER.debug("StateChange message has no state, state is null {}", automationComposition.getKey());
202                 break;
203         }
204     }
205
206     /**
207      * Handle a automation composition update message.
208      *
209      * @param updateMsg the update message
210      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
211      */
212     public void handleAutomationCompositionUpdate(AutomationCompositionUpdate updateMsg,
213             List<AutomationCompositionElementDefinition> acElementDefinitions) {
214
215         if (!updateMsg.appliesTo(participantType, participantId)) {
216             return;
217         }
218
219         if (0 == updateMsg.getStartPhase()) {
220             handleAcUpdatePhase0(updateMsg, acElementDefinitions);
221         } else {
222             handleAcUpdatePhaseN(updateMsg, acElementDefinitions);
223         }
224     }
225
226     private void handleAcUpdatePhase0(AutomationCompositionUpdate updateMsg,
227             List<AutomationCompositionElementDefinition> acElementDefinitions) {
228         var automationComposition = automationCompositionMap.get(updateMsg.getAutomationCompositionId());
229
230         // TODO: Updates to existing AutomationCompositions are not supported yet (Addition/Removal of
231         // AutomationComposition
232         // elements to existing AutomationComposition has to be supported).
233         if (automationComposition != null) {
234             var automationCompositionUpdateAck =
235                     new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_UPDATE_ACK);
236             automationCompositionUpdateAck.setParticipantId(participantId);
237             automationCompositionUpdateAck.setParticipantType(participantType);
238
239             automationCompositionUpdateAck.setMessage("Automation composition " + updateMsg.getAutomationCompositionId()
240                     + " already defined on participant " + participantId);
241             automationCompositionUpdateAck.setResult(false);
242             automationCompositionUpdateAck.setResponseTo(updateMsg.getMessageId());
243             automationCompositionUpdateAck.setAutomationCompositionId(updateMsg.getAutomationCompositionId());
244             publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
245             return;
246         }
247
248         if (updateMsg.getParticipantUpdatesList().isEmpty()) {
249             LOGGER.warn("No AutomationCompositionElement updates in message {}",
250                     updateMsg.getAutomationCompositionId());
251             return;
252         }
253
254         automationComposition = new AutomationComposition();
255         automationComposition.setInstanceId(updateMsg.getAutomationCompositionId());
256         var acElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
257         var acElementMap = prepareAcElementMap(acElements);
258         automationComposition.setElements(acElementMap);
259         automationCompositionMap.put(updateMsg.getAutomationCompositionId(), automationComposition);
260
261         handleAutomationCompositionElementUpdate(acElements, acElementDefinitions, updateMsg.getStartPhase(),
262                 updateMsg.getAutomationCompositionId());
263     }
264
265     private void handleAcUpdatePhaseN(AutomationCompositionUpdate updateMsg,
266             List<AutomationCompositionElementDefinition> acElementDefinitions) {
267
268         var acElementList = updateMsg.getParticipantUpdatesList().stream()
269                 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
270                 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
271
272         handleAutomationCompositionElementUpdate(acElementList, acElementDefinitions, updateMsg.getStartPhase(),
273                 updateMsg.getAutomationCompositionId());
274     }
275
276     private void handleAutomationCompositionElementUpdate(List<AutomationCompositionElement> acElements,
277             List<AutomationCompositionElementDefinition> acElementDefinitions, Integer startPhaseMsg,
278             UUID automationCompositionId) {
279         try {
280             for (var element : acElements) {
281                 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition());
282                 if (acElementNodeTemplate != null) {
283                     int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
284                     if (startPhaseMsg.equals(startPhase)) {
285                         for (var acElementListener : listeners) {
286                             var map = new HashMap<>(acElementNodeTemplate.getProperties());
287                             map.putAll(element.getProperties());
288                             acElementListener.automationCompositionElementUpdate(automationCompositionId, element, map);
289                         }
290                     }
291                 }
292             }
293         } catch (PfModelException e) {
294             LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
295         }
296
297     }
298
299     private ToscaNodeTemplate getAcElementNodeTemplate(
300             List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
301
302         for (var acElementDefinition : acElementDefinitions) {
303             if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
304                 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
305             }
306         }
307         return null;
308     }
309
310     private List<AutomationCompositionElement> storeElementsOnThisParticipant(
311             List<ParticipantUpdates> participantUpdates) {
312         var acElementList = participantUpdates.stream()
313                 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
314                 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
315
316         for (var element : acElementList) {
317             elementsOnThisParticipant.put(element.getId(), element);
318         }
319         return acElementList;
320     }
321
322     private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
323         Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
324         for (var element : acElements) {
325             acElementMap.put(element.getId(), element);
326         }
327         return acElementMap;
328     }
329
330     /**
331      * Method to handle when the new state from participant is UNINITIALISED state.
332      *
333      * @param automationComposition participant response
334      * @param orderedState orderedState
335      * @param startPhaseMsg startPhase from message
336      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
337      */
338     private void handleUninitialisedState(final AutomationComposition automationComposition,
339             final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
340             List<AutomationCompositionElementDefinition> acElementDefinitions) {
341         handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
342         boolean isAllUninitialised = automationComposition.getElements().values().stream()
343                 .filter(element -> !AutomationCompositionState.UNINITIALISED.equals(element.getState())).findAny()
344                 .isEmpty();
345         if (isAllUninitialised) {
346             automationCompositionMap.remove(automationComposition.getInstanceId());
347             automationComposition.getElements().values()
348                     .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
349         }
350     }
351
352     /**
353      * Method to handle when the new state from participant is PASSIVE state.
354      *
355      * @param automationComposition participant response
356      * @param orderedState orderedState
357      * @param startPhaseMsg startPhase from message
358      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
359      */
360     private void handlePassiveState(final AutomationComposition automationComposition,
361             final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
362             List<AutomationCompositionElementDefinition> acElementDefinitions) {
363         handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
364     }
365
366     /**
367      * Method to handle when the new state from participant is RUNNING state.
368      *
369      * @param automationComposition participant response
370      * @param orderedState orderedState
371      * @param startPhaseMsg startPhase from message
372      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
373      */
374     private void handleRunningState(final AutomationComposition automationComposition,
375             final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
376             List<AutomationCompositionElementDefinition> acElementDefinitions) {
377         handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
378     }
379
380     /**
381      * Method to update the state of automation composition elements.
382      *
383      * @param automationComposition participant status in memory
384      * @param orderedState orderedState the new ordered state the participant should have
385      * @param startPhaseMsg startPhase from message
386      * @param acElementDefinitions the list of AutomationCompositionElementDefinition
387      */
388     private void handleStateChange(AutomationComposition automationComposition,
389             final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
390             List<AutomationCompositionElementDefinition> acElementDefinitions) {
391
392         if (orderedState.equals(automationComposition.getOrderedState())) {
393             var automationCompositionAck =
394                     new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
395             automationCompositionAck.setParticipantId(participantId);
396             automationCompositionAck.setParticipantType(participantType);
397             automationCompositionAck.setMessage("Automation composition is already in state " + orderedState);
398             automationCompositionAck.setResult(false);
399             automationCompositionAck.setAutomationCompositionId(automationComposition.getInstanceId());
400             publisher.sendAutomationCompositionAck(automationCompositionAck);
401             return;
402         }
403
404         automationComposition.getElements().values().stream()
405                 .forEach(acElement -> automationCompositionElementStateChange(automationComposition, orderedState,
406                         acElement, startPhaseMsg, acElementDefinitions));
407     }
408
409     private void automationCompositionElementStateChange(AutomationComposition automationComposition,
410             AutomationCompositionOrderedState orderedState, 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.automationCompositionElementStateChange(automationComposition.getInstanceId(),
419                                 acElement.getId(), acElement.getState(), orderedState);
420                     } catch (PfModelException e) {
421                         LOGGER.debug("Automation composition element update failed {}",
422                                 automationComposition.getInstanceId());
423                     }
424                 }
425             }
426         }
427     }
428 }