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