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