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