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