f2421141a8506d82ea73cac5af9b394787fe9cfd
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.clamp.controlloop.participant.intermediary.handler;
22
23 import java.util.ArrayList;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.UUID;
28 import lombok.Getter;
29 import lombok.NoArgsConstructor;
30 import org.apache.commons.collections4.CollectionUtils;
31 import org.apache.commons.lang3.tuple.Pair;
32 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatistics;
33 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
34 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
35 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementDefinition;
36 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState;
37 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
38 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops;
39 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantUpdates;
40 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopAck;
41 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopStateChange;
42 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopUpdate;
43 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessageType;
44 import org.onap.policy.clamp.controlloop.participant.intermediary.api.ControlLoopElementListener;
45 import org.onap.policy.clamp.controlloop.participant.intermediary.comm.MessageSender;
46 import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantIntermediaryParameters;
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
53 /*
54  * This class is responsible for managing the state of all control loops in the participant.
55  */
56 @NoArgsConstructor
57 public class ControlLoopHandler {
58     private static final Logger LOGGER = LoggerFactory.getLogger(ControlLoopHandler.class);
59
60     private ToscaConceptIdentifier participantType = null;
61     private ToscaConceptIdentifier participantId = null;
62     private MessageSender messageSender = null;
63
64     private final Map<ToscaConceptIdentifier, ControlLoop> controlLoopMap = new LinkedHashMap<>();
65
66     @Getter
67     private final Map<UUID, ControlLoopElement> elementsOnThisParticipant = new LinkedHashMap<>();
68
69     @Getter
70     private List<ControlLoopElementListener> listeners = new ArrayList<>();
71
72     /**
73      * Constructor, set the participant ID and messageSender.
74      *
75      * @param parameters the parameters of the participant
76      * @param messageSender the messageSender for sending responses to messages
77      */
78     public ControlLoopHandler(ParticipantIntermediaryParameters parameters, MessageSender messageSender) {
79         this.participantType = parameters.getParticipantType();
80         this.participantId = parameters.getParticipantId();
81         this.messageSender = messageSender;
82     }
83
84     public void registerControlLoopElementListener(ControlLoopElementListener listener) {
85         listeners.add(listener);
86     }
87
88     /**
89      * Handle a control loop element state change message.
90      *
91      * @param id controlloop element id
92      * @param orderedState the current state
93      * @param newState the ordered state
94      * @return controlLoopElement the updated controlloop element
95      */
96     public ControlLoopElement updateControlLoopElementState(UUID id, ControlLoopOrderedState orderedState,
97             ControlLoopState newState) {
98
99         if (id == null) {
100             LOGGER.warn("Cannot update Control loop element state, id is null");
101         }
102
103         ControlLoopAck controlLoopStateChangeAck =
104                 new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_STATECHANGE_ACK);
105         ControlLoopElement clElement = elementsOnThisParticipant.get(id);
106         if (clElement != null) {
107             clElement.setOrderedState(orderedState);
108             clElement.setState(newState);
109             controlLoopStateChangeAck.getControlLoopResultMap().put(clElement.getId(),
110                 Pair.of(true, "Control loop element {} state changed to {}\", id, newState)"));
111             LOGGER.debug("Control loop element {} state changed to {}", id, newState);
112             controlLoopStateChangeAck.setMessage("ControlLoopElement state changed to {} " + newState);
113             controlLoopStateChangeAck.setResult(true);
114             messageSender.sendAckResponse(controlLoopStateChangeAck);
115             return clElement;
116         }
117         return null;
118     }
119
120     /**
121      * Handle a control loop element statistics.
122      *
123      * @param id controlloop element id
124      * @param elementStatistics control loop element Statistics
125      */
126     public void updateControlLoopElementStatistics(UUID id, ClElementStatistics elementStatistics) {
127         ControlLoopElement clElement = elementsOnThisParticipant.get(id);
128         if (clElement != null) {
129             elementStatistics.setParticipantId(participantId);
130             elementStatistics.setId(id);
131             clElement.setClElementStatistics(elementStatistics);
132         }
133     }
134
135     /**
136      * Handle a control loop state change message.
137      *
138      * @param stateChangeMsg the state change message
139      */
140     public void handleControlLoopStateChange(ControlLoopStateChange stateChangeMsg) {
141         if (stateChangeMsg.getControlLoopId() == null) {
142             return;
143         }
144
145         var controlLoop = controlLoopMap.get(stateChangeMsg.getControlLoopId());
146
147         if (controlLoop == null) {
148             LOGGER.debug("Control loop {} does not use this participant", stateChangeMsg.getControlLoopId());
149             return;
150         }
151
152         var controlLoopStateChangeAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_STATECHANGE_ACK);
153         controlLoopStateChangeAck.setResponseTo(stateChangeMsg.getMessageId());
154         controlLoopStateChangeAck.setControlLoopId(stateChangeMsg.getControlLoopId());
155         handleState(controlLoop, controlLoopStateChangeAck, stateChangeMsg.getOrderedState());
156         messageSender.sendAckResponse(controlLoopStateChangeAck);
157     }
158
159     /**
160      * Method to handle state changes.
161      *
162      * @param controlLoop participant response
163      * @param response participant response
164      * @param orderedState controlloop ordered state
165      */
166     private void handleState(final ControlLoop controlLoop, final ControlLoopAck response,
167             ControlLoopOrderedState orderedState) {
168         switch (orderedState) {
169             case UNINITIALISED:
170                 handleUninitialisedState(controlLoop, orderedState, response);
171                 break;
172             case PASSIVE:
173                 handlePassiveState(controlLoop, orderedState, response);
174                 break;
175             case RUNNING:
176                 handleRunningState(controlLoop, orderedState, response);
177                 break;
178             default:
179                 LOGGER.debug("StateChange message has no state, state is null {}", controlLoop.getDefinition());
180                 break;
181         }
182     }
183
184     /**
185      * Handle a control loop update message.
186      *
187      * @param updateMsg the update message
188      */
189     public void handleControlLoopUpdate(ControlLoopUpdate updateMsg,
190                 List<ControlLoopElementDefinition> clElementDefinitions) {
191
192         if (!updateMsg.appliesTo(participantType, participantId)) {
193             return;
194         }
195
196         var controlLoop = controlLoopMap.get(updateMsg.getControlLoopId());
197
198         var controlLoopUpdateAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_UPDATE_ACK);
199
200         // TODO: Updates to existing ControlLoops are not supported yet (Addition/Removal of ControlLoop
201         // elements to existing ControlLoop has to be supported).
202         if (controlLoop != null) {
203             controlLoopUpdateAck.setResponseTo(updateMsg.getMessageId());
204             controlLoopUpdateAck.setControlLoopId(updateMsg.getControlLoopId());
205             controlLoopUpdateAck.setMessage("Control loop " + updateMsg.getControlLoopId()
206                 + " already defined on participant " + participantId);
207             controlLoopUpdateAck.setResult(false);
208             messageSender.sendAckResponse(controlLoopUpdateAck);
209             return;
210         }
211
212         List<ControlLoopElement> clElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
213
214         try {
215             for (ControlLoopElement element : clElements) {
216                 ToscaNodeTemplate clElementNodeTemplate = getClElementNodeTemplate(
217                         clElementDefinitions, element.getDefinition());
218                 for (ControlLoopElementListener clElementListener : listeners) {
219                     clElementListener.controlLoopElementUpdate(element, clElementNodeTemplate);
220                 }
221             }
222         } catch (PfModelException e) {
223             LOGGER.debug("Control loop element update failed {}", updateMsg.getControlLoopId());
224         }
225
226         Map<UUID, ControlLoopElement> clElementMap = prepareClElementMap(clElements);
227         controlLoop = new ControlLoop();
228         controlLoop.setDefinition(updateMsg.getControlLoopId());
229         controlLoop.setElements(clElementMap);
230         controlLoopMap.put(updateMsg.getControlLoopId(), controlLoop);
231
232         controlLoopUpdateAck.setResponseTo(updateMsg.getMessageId());
233         controlLoopUpdateAck.setControlLoopId(updateMsg.getControlLoopId());
234         controlLoopUpdateAck.setMessage("Control loop " + updateMsg.getControlLoopId()
235                 + " defined on participant " + participantId);
236         controlLoopUpdateAck.setResult(true);
237         messageSender.sendAckResponse(controlLoopUpdateAck);
238     }
239
240     private ToscaNodeTemplate getClElementNodeTemplate(List<ControlLoopElementDefinition> clElementDefinitions,
241                 ToscaConceptIdentifier clElementDefId) {
242         for (ControlLoopElementDefinition clElementDefinition : clElementDefinitions) {
243             if (clElementDefinition.getClElementDefinitionId().equals(clElementDefId)) {
244                 return clElementDefinition.getControlLoopElementToscaNodeTemplate();
245             }
246         }
247         return null;
248     }
249
250     private List<ControlLoopElement> storeElementsOnThisParticipant(List<ParticipantUpdates> participantUpdates) {
251         List<ControlLoopElement> clElementMap = new ArrayList<>();
252         for (ParticipantUpdates participantUpdate : participantUpdates) {
253             if (participantUpdate.getParticipantId().equals(participantType)) {
254                 clElementMap = participantUpdate.getControlLoopElementList();
255             }
256         }
257         for (ControlLoopElement element : clElementMap) {
258             elementsOnThisParticipant.put(element.getId(), element);
259         }
260         return clElementMap;
261     }
262
263     private Map<UUID, ControlLoopElement> prepareClElementMap(List<ControlLoopElement> clElements) {
264         Map<UUID, ControlLoopElement> clElementMap = new LinkedHashMap<>();
265         for (ControlLoopElement element : clElements) {
266             clElementMap.put(element.getId(), element);
267         }
268         return clElementMap;
269     }
270
271     /**
272      * Method to handle when the new state from participant is UNINITIALISED state.
273      *
274      * @param controlLoop participant response
275      * @param orderedState orderedState
276      * @param response participant response
277      */
278     private void handleUninitialisedState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
279             final ControlLoopAck response) {
280         handleStateChange(controlLoop, orderedState, ControlLoopState.UNINITIALISED, response);
281         controlLoopMap.remove(controlLoop.getKey().asIdentifier());
282
283         for (ControlLoopElementListener clElementListener : listeners) {
284             try {
285                 for (ControlLoopElement element : controlLoop.getElements().values()) {
286                     clElementListener.controlLoopElementStateChange(element.getId(), element.getState(), orderedState);
287                 }
288             } catch (PfModelException e) {
289                 LOGGER.debug("Control loop element update failed {}", controlLoop.getDefinition());
290             }
291         }
292     }
293
294     /**
295      * Method to handle when the new state from participant is PASSIVE state.
296      *
297      * @param controlLoop participant response
298      * @param orderedState orderedState
299      * @param response participant response
300      */
301     private void handlePassiveState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
302             final ControlLoopAck response) {
303         handleStateChange(controlLoop, orderedState, ControlLoopState.PASSIVE, response);
304     }
305
306     /**
307      * Method to handle when the new state from participant is RUNNING state.
308      *
309      * @param controlLoop participant response
310      * @param orderedState orderedState
311      * @param response participant response
312      */
313     private void handleRunningState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
314             final ControlLoopAck response) {
315         handleStateChange(controlLoop, orderedState, ControlLoopState.RUNNING, response);
316     }
317
318     /**
319      * Method to update the state of control loop elements.
320      *
321      * @param controlLoop participant status in memory
322      * @param orderedState orderedState the new ordered state the participant should have
323      * @param newState new state of the control loop elements
324      * @param response the response to the state change request
325      */
326     private void handleStateChange(ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
327             ControlLoopState newState, ControlLoopAck response) {
328
329         if (orderedState.equals(controlLoop.getOrderedState())) {
330             response.setMessage("Control loop is already in state " + orderedState);
331             response.setResult(false);
332             return;
333         }
334
335         if (!CollectionUtils.isEmpty(controlLoop.getElements().values())) {
336             controlLoop.getElements().values().forEach(element -> {
337                 element.setState(newState);
338                 element.setOrderedState(orderedState);
339             });
340         }
341
342         response.setMessage("ControlLoop state changed from " + controlLoop.getOrderedState() + " to " + orderedState);
343         response.setResult(true);
344         controlLoop.setOrderedState(orderedState);
345     }
346
347     /**
348      * Get control loops as a {@link ConrolLoops} class.
349      *
350      * @return the control loops
351      */
352     public ControlLoops getControlLoops() {
353         var controlLoops = new ControlLoops();
354         controlLoops.setControlLoopList(new ArrayList<>(controlLoopMap.values()));
355         return controlLoops;
356     }
357 }