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