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