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