c9da1279d82139e5595eba655b40bfe565ec1854
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.clamp.controlloop.participant.intermediary.handler;
22
23 import java.util.ArrayList;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.UUID;
28 import lombok.Getter;
29 import lombok.NoArgsConstructor;
30 import org.apache.commons.collections4.CollectionUtils;
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.ControlLoopElementDefinition;
35 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState;
36 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
37 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops;
38 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopStateChange;
39 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopUpdate;
40 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantResponseDetails;
41 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantResponseStatus;
42 import org.onap.policy.clamp.controlloop.participant.intermediary.api.ControlLoopElementListener;
43 import org.onap.policy.clamp.controlloop.participant.intermediary.comm.MessageSender;
44 import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantIntermediaryParameters;
45 import org.onap.policy.models.base.PfModelException;
46 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 /*
51  * This class is responsible for managing the state of all control loops in the participant.
52  */
53 @NoArgsConstructor
54 public class ControlLoopHandler {
55     private static final Logger LOGGER = LoggerFactory.getLogger(ControlLoopHandler.class);
56
57     private ToscaConceptIdentifier participantType = null;
58     private ToscaConceptIdentifier participantId = null;
59     private MessageSender messageSender = null;
60
61     private final Map<ToscaConceptIdentifier, ControlLoop> controlLoopMap = new LinkedHashMap<>();
62
63     private final Map<UUID, ControlLoopElement> elementsOnThisParticipant = new LinkedHashMap<>();
64
65     @Getter
66     private List<ControlLoopElementListener> listeners = new ArrayList<>();
67
68     /**
69      * Constructor, set the participant ID and messageSender.
70      *
71      * @param parameters the parameters of the participant
72      * @param messageSender the messageSender for sending responses to messages
73      */
74     public ControlLoopHandler(ParticipantIntermediaryParameters parameters, MessageSender messageSender) {
75         this.participantType = parameters.getParticipantType();
76         this.participantId = parameters.getParticipantId();
77         this.messageSender = messageSender;
78     }
79
80     public void registerControlLoopElementListener(ControlLoopElementListener listener) {
81         listeners.add(listener);
82     }
83
84     /**
85      * Handle a control loop element state change message.
86      *
87      * @param id controlloop element id
88      * @param orderedState the current state
89      * @param newState the ordered state
90      * @return controlLoopElement the updated controlloop element
91      */
92     public ControlLoopElement updateControlLoopElementState(UUID id, ControlLoopOrderedState orderedState,
93             ControlLoopState newState) {
94
95         if (id == null) {
96             return null;
97         }
98
99         ControlLoopElement clElement = elementsOnThisParticipant.get(id);
100         if (clElement != null) {
101             clElement.setOrderedState(orderedState);
102             clElement.setState(newState);
103             LOGGER.debug("Control loop element {} state changed to {}", id, newState);
104             var response = new ParticipantResponseDetails();
105             response.setResponseStatus(ParticipantResponseStatus.SUCCESS);
106             response.setResponseMessage("ControlLoopElement state changed to {} " + newState);
107             messageSender.sendResponse(response);
108             return clElement;
109         }
110
111         return null;
112     }
113
114     /**
115      * Handle a control loop element statistics.
116      *
117      * @param id controlloop element id
118      * @param elementStatistics control loop element Statistics
119      */
120     public void updateControlLoopElementStatistics(UUID id, ClElementStatistics elementStatistics) {
121         ControlLoopElement clElement = elementsOnThisParticipant.get(id);
122         if (clElement != null) {
123             elementStatistics.setParticipantId(participantId);
124             elementStatistics.setId(id);
125             clElement.setClElementStatistics(elementStatistics);
126         }
127     }
128
129     /**
130      * Handle a control loop state change message.
131      *
132      * @param stateChangeMsg the state change message
133      */
134     public void handleControlLoopStateChange(ControlLoopStateChange stateChangeMsg) {
135         if (stateChangeMsg.getControlLoopId() == null) {
136             return;
137         }
138
139         var controlLoop = controlLoopMap.get(stateChangeMsg.getControlLoopId());
140
141         if (controlLoop == null) {
142             LOGGER.debug("Control loop {} does not use this participant", stateChangeMsg.getControlLoopId());
143             return;
144         }
145
146         var response = new ParticipantResponseDetails(stateChangeMsg);
147         handleState(controlLoop, response, stateChangeMsg.getOrderedState());
148         messageSender.sendResponse(response);
149     }
150
151     /**
152      * Method to handle state changes.
153      *
154      * @param controlLoop participant response
155      * @param response participant response
156      * @param orderedState controlloop ordered state
157      */
158     private void handleState(final ControlLoop controlLoop, final ParticipantResponseDetails response,
159             ControlLoopOrderedState orderedState) {
160         switch (orderedState) {
161             case UNINITIALISED:
162                 handleUninitialisedState(controlLoop, orderedState, response);
163                 break;
164             case PASSIVE:
165                 handlePassiveState(controlLoop, orderedState, response);
166                 break;
167             case RUNNING:
168                 handleRunningState(controlLoop, orderedState, response);
169                 break;
170             default:
171                 LOGGER.debug("StateChange message has no state, state is null {}", controlLoop.getDefinition());
172                 break;
173         }
174     }
175
176     /**
177      * Handle a control loop update message.
178      *
179      * @param updateMsg the update message
180      */
181     public void handleControlLoopUpdate(ControlLoopUpdate updateMsg,
182                 Map<UUID, ControlLoopElementDefinition> clElementDefinitions) {
183
184         if (!updateMsg.appliesTo(participantType, participantId)) {
185             return;
186         }
187
188         var controlLoop = controlLoopMap.get(updateMsg.getControlLoopId());
189
190         var response = new ParticipantResponseDetails(updateMsg);
191
192         // TODO: Updates to existing ControlLoops are not supported yet (Addition/Removal of ControlLoop
193         // elements to existing ControlLoop has to be supported).
194         if (controlLoop != null) {
195             response.setResponseStatus(ParticipantResponseStatus.FAIL);
196             response.setResponseMessage("Control loop " + updateMsg.getControlLoopId()
197                     + " already defined on participant " + participantId);
198
199             messageSender.sendResponse(response);
200             return;
201         }
202
203         controlLoop = updateMsg.getControlLoop();
204         controlLoop.getElements().values().removeIf(element -> !participantType.equals(element.getParticipantType()));
205
206         controlLoopMap.put(updateMsg.getControlLoopId(), controlLoop);
207         for (ControlLoopElement element : updateMsg.getControlLoop().getElements().values()) {
208             element.setState(element.getOrderedState().asState());
209             element.setParticipantId(participantId);
210             elementsOnThisParticipant.put(element.getId(), element);
211         }
212
213         for (ControlLoopElementListener clElementListener : listeners) {
214             try {
215                 for (ControlLoopElement element : updateMsg.getControlLoop().getElements().values()) {
216                     clElementListener.controlLoopElementUpdate(element,
217                         clElementDefinitions.get(element.getId()).getControlLoopElementToscaServiceTemplate());
218                 }
219             } catch (PfModelException e) {
220                 LOGGER.debug("Control loop element update failed {}", updateMsg.getControlLoopId());
221             }
222         }
223
224         response.setResponseStatus(ParticipantResponseStatus.SUCCESS);
225         response.setResponseMessage(
226                 "Control loop " + updateMsg.getControlLoopId() + " defined on participant " + participantId);
227
228         messageSender.sendResponse(response);
229     }
230
231     /**
232      * Method to handle when the new state from participant is UNINITIALISED state.
233      *
234      * @param controlLoop participant response
235      * @param orderedState orderedState
236      * @param response participant response
237      */
238     private void handleUninitialisedState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
239             final ParticipantResponseDetails response) {
240         handleStateChange(controlLoop, orderedState, ControlLoopState.UNINITIALISED, response);
241         controlLoopMap.remove(controlLoop.getKey().asIdentifier());
242
243         for (ControlLoopElementListener clElementListener : listeners) {
244             try {
245                 for (ControlLoopElement element : controlLoop.getElements().values()) {
246                     clElementListener.controlLoopElementStateChange(element.getId(), element.getState(), orderedState);
247                 }
248             } catch (PfModelException e) {
249                 LOGGER.debug("Control loop element update failed {}", controlLoop.getDefinition());
250             }
251         }
252     }
253
254     /**
255      * Method to handle when the new state from participant is PASSIVE state.
256      *
257      * @param controlLoop participant response
258      * @param orderedState orderedState
259      * @param response participant response
260      */
261     private void handlePassiveState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
262             final ParticipantResponseDetails response) {
263         handleStateChange(controlLoop, orderedState, ControlLoopState.PASSIVE, response);
264     }
265
266     /**
267      * Method to handle when the new state from participant is RUNNING state.
268      *
269      * @param controlLoop participant response
270      * @param orderedState orderedState
271      * @param response participant response
272      */
273     private void handleRunningState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
274             final ParticipantResponseDetails response) {
275         handleStateChange(controlLoop, orderedState, ControlLoopState.RUNNING, response);
276     }
277
278     /**
279      * Method to update the state of control loop elements.
280      *
281      * @param controlLoop participant status in memory
282      * @param orderedState orderedState the new ordered state the participant should have
283      * @param newState new state of the control loop elements
284      * @param response the response to the state change request
285      */
286     private void handleStateChange(ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
287             ControlLoopState newState, ParticipantResponseDetails response) {
288
289         if (orderedState.equals(controlLoop.getOrderedState())) {
290             response.setResponseStatus(ParticipantResponseStatus.SUCCESS);
291             response.setResponseMessage("Control loop is already in state " + orderedState);
292             return;
293         }
294
295         if (!CollectionUtils.isEmpty(controlLoop.getElements().values())) {
296             controlLoop.getElements().values().forEach(element -> {
297                 element.setState(newState);
298                 element.setOrderedState(orderedState);
299             });
300         }
301
302         response.setResponseStatus(ParticipantResponseStatus.SUCCESS);
303         response.setResponseMessage(
304                 "ControlLoop state changed from " + controlLoop.getOrderedState() + " to " + orderedState);
305         controlLoop.setOrderedState(orderedState);
306     }
307
308     /**
309      * Get control loops as a {@link ConrolLoops} class.
310      *
311      * @return the control loops
312      */
313     public ControlLoops getControlLoops() {
314         var controlLoops = new ControlLoops();
315         controlLoops.setControlLoopList(new ArrayList<>(controlLoopMap.values()));
316         return controlLoops;
317     }
318 }