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