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