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