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