dadfe0de25884395916e19bd167139fef19de884
[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.runtime.supervision;
22
23 import java.util.List;
24 import javax.ws.rs.core.Response;
25 import lombok.AllArgsConstructor;
26 import org.apache.commons.collections4.CollectionUtils;
27 import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
28 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
29 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
30 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
31 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant;
32 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
33 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantProvider;
34 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregister;
35 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegister;
36 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus;
37 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdateAck;
38 import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringProvider;
39 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopStateChangePublisher;
40 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopUpdatePublisher;
41 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantDeregisterAckPublisher;
42 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantRegisterAckPublisher;
43 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantUpdatePublisher;
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 import org.springframework.stereotype.Component;
49
50 /**
51  * This class handles supervision of control loop instances, so only one object of this type should be built at a time.
52  *
53  * <p/>
54  * It is effectively a singleton that is started at system start.
55  */
56 @Component
57 @AllArgsConstructor
58 public class SupervisionHandler {
59     private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionHandler.class);
60
61     private static final String CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE = "Control loop can't transition from state ";
62     private static final String CONTROL_LOOP_IS_ALREADY_IN_STATE = "Control loop is already in state ";
63     private static final String TO_STATE = " to state ";
64     private static final String AND_TRANSITIONING_TO_STATE = " and transitioning to state ";
65
66     private final ControlLoopProvider controlLoopProvider;
67     private final ParticipantProvider participantProvider;
68     private final MonitoringProvider monitoringProvider;
69
70     // Publishers for participant communication
71     private final ControlLoopUpdatePublisher controlLoopUpdatePublisher;
72     private final ControlLoopStateChangePublisher controlLoopStateChangePublisher;
73     private final ParticipantRegisterAckPublisher participantRegisterAckPublisher;
74     private final ParticipantDeregisterAckPublisher participantDeregisterAckPublisher;
75     private final ParticipantUpdatePublisher participantUpdatePublisher;
76
77     /**
78      * Supervision trigger called when a command is issued on control loops.
79      *
80      * <p/>
81      * Causes supervision to start or continue supervision on the control loops in question.
82      *
83      * @param controlLoopIdentifierList the control loops for which the supervision command has been issued
84      * @throws ControlLoopException on supervision triggering exceptions
85      */
86     public void triggerControlLoopSupervision(List<ToscaConceptIdentifier> controlLoopIdentifierList)
87             throws ControlLoopException {
88
89         LOGGER.debug("triggering control loop supervision on control loops {}", controlLoopIdentifierList);
90
91         if (CollectionUtils.isEmpty(controlLoopIdentifierList)) {
92             // This is just to force throwing of the exception in certain circumstances.
93             exceptionOccured(Response.Status.NOT_ACCEPTABLE, "The list of control loops for supervision is empty");
94         }
95
96         for (ToscaConceptIdentifier controlLoopId : controlLoopIdentifierList) {
97             try {
98                 var controlLoop = controlLoopProvider.getControlLoop(controlLoopId);
99
100                 superviseControlLoop(controlLoop);
101
102                 controlLoopProvider.updateControlLoop(controlLoop);
103             } catch (PfModelException pfme) {
104                 throw new ControlLoopException(pfme.getErrorResponse().getResponseCode(), pfme.getMessage(), pfme);
105             }
106         }
107     }
108
109     /**
110      * Handle a ParticipantStatus message from a participant.
111      *
112      * @param participantStatusMessage the ParticipantStatus message received from a participant
113      */
114     @MessageIntercept
115     public void handleParticipantMessage(ParticipantStatus participantStatusMessage) {
116         LOGGER.debug("Participant Status received {}", participantStatusMessage);
117         try {
118             superviseParticipant(participantStatusMessage);
119         } catch (PfModelException | ControlLoopException svExc) {
120             LOGGER.warn("error supervising participant {}", participantStatusMessage.getParticipantId(), svExc);
121             return;
122         }
123
124         try {
125             superviseControlLoops(participantStatusMessage);
126         } catch (PfModelException | ControlLoopException svExc) {
127             LOGGER.warn("error supervising participant {}", participantStatusMessage.getParticipantId(), svExc);
128         }
129     }
130
131     /**
132      * Handle a ParticipantRegister message from a participant.
133      *
134      * @param participantRegisterMessage the ParticipantRegister message received from a participant
135      */
136     @MessageIntercept
137     public void handleParticipantMessage(ParticipantRegister participantRegisterMessage) {
138         LOGGER.debug("Participant Register received {}", participantRegisterMessage);
139
140         participantRegisterAckPublisher.send(participantRegisterMessage.getMessageId());
141
142         participantUpdatePublisher.send(participantRegisterMessage.getParticipantId(),
143                 participantRegisterMessage.getParticipantType());
144     }
145
146     /**
147      * Handle a ParticipantDeregister message from a participant.
148      *
149      * @param participantDeregisterMessage the ParticipantDeregister message received from a participant
150      */
151     @MessageIntercept
152     public void handleParticipantMessage(ParticipantDeregister participantDeregisterMessage) {
153         LOGGER.debug("Participant Deregister received {}", participantDeregisterMessage);
154         participantDeregisterAckPublisher.send(participantDeregisterMessage.getMessageId());
155     }
156
157     /**
158      * Handle a ParticipantUpdateAck message from a participant.
159      *
160      * @param participantUpdateAckMessage the ParticipantUpdateAck message received from a participant
161      */
162     @MessageIntercept
163     public void handleParticipantMessage(ParticipantUpdateAck participantUpdateAckMessage) {
164         LOGGER.debug("Participant Update Ack received {}", participantUpdateAckMessage);
165     }
166
167     /**
168      * Supervise a control loop, performing whatever actions need to be performed on the control loop.
169      *
170      * @param controlLoop the control loop to supervises
171      * @throws PfModelException on accessing models in the database
172      * @throws ControlLoopException on supervision errors
173      */
174     private void superviseControlLoop(ControlLoop controlLoop) throws ControlLoopException, PfModelException {
175         switch (controlLoop.getOrderedState()) {
176             case UNINITIALISED:
177                 superviseControlLoopUninitialization(controlLoop);
178                 break;
179
180             case PASSIVE:
181                 superviseControlLoopPassivation(controlLoop);
182                 break;
183
184             case RUNNING:
185                 superviseControlLoopActivation(controlLoop);
186                 break;
187
188             default:
189                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
190                         "A control loop cannot be commanded to go into state " + controlLoop.getOrderedState().name());
191         }
192     }
193
194     /**
195      * Supervise a control loop uninitialisation, performing whatever actions need to be performed on the control loop,
196      * control loop ordered state is UNINITIALIZED.
197      *
198      * @param controlLoop the control loop to supervises
199      * @throws ControlLoopException on supervision errors
200      */
201     private void superviseControlLoopUninitialization(ControlLoop controlLoop) throws ControlLoopException {
202         switch (controlLoop.getState()) {
203             case UNINITIALISED:
204                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
205                         CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
206                 break;
207
208             case UNINITIALISED2PASSIVE:
209             case PASSIVE:
210                 controlLoop.setState(ControlLoopState.PASSIVE2UNINITIALISED);
211                 controlLoopStateChangePublisher.send(controlLoop);
212                 break;
213
214             case PASSIVE2UNINITIALISED:
215                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
216                         + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
217                 break;
218
219             default:
220                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
221                         + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
222                 break;
223         }
224     }
225
226     private void superviseControlLoopPassivation(ControlLoop controlLoop)
227             throws ControlLoopException, PfModelException {
228         switch (controlLoop.getState()) {
229             case PASSIVE:
230                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
231                         CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
232                 break;
233             case UNINITIALISED:
234                 controlLoop.setState(ControlLoopState.UNINITIALISED2PASSIVE);
235                 controlLoopUpdatePublisher.send(controlLoop);
236                 break;
237
238             case UNINITIALISED2PASSIVE:
239             case RUNNING2PASSIVE:
240                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
241                         + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
242                 break;
243
244             case RUNNING:
245                 controlLoop.setState(ControlLoopState.RUNNING2PASSIVE);
246                 controlLoopStateChangePublisher.send(controlLoop);
247                 break;
248
249             default:
250                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
251                         + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
252                 break;
253         }
254     }
255
256     private void superviseControlLoopActivation(ControlLoop controlLoop) throws ControlLoopException {
257         switch (controlLoop.getState()) {
258             case RUNNING:
259                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
260                         CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
261                 break;
262
263             case PASSIVE2RUNNING:
264                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
265                         + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
266                 break;
267
268             case PASSIVE:
269                 controlLoop.setState(ControlLoopState.PASSIVE2RUNNING);
270                 controlLoopStateChangePublisher.send(controlLoop);
271                 break;
272
273             default:
274                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
275                         + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
276                 break;
277         }
278     }
279
280     private void superviseParticipant(ParticipantStatus participantStatusMessage)
281             throws PfModelException, ControlLoopException {
282         if (participantStatusMessage.getParticipantId() == null) {
283             exceptionOccured(Response.Status.NOT_FOUND, "Participant ID on PARTICIPANT_STATUS message is null");
284         }
285
286         List<Participant> participantList =
287                 participantProvider.getParticipants(participantStatusMessage.getParticipantId().getName(),
288                         participantStatusMessage.getParticipantId().getVersion());
289
290         if (CollectionUtils.isEmpty(participantList)) {
291             var participant = new Participant();
292             participant.setName(participantStatusMessage.getParticipantId().getName());
293             participant.setVersion(participantStatusMessage.getParticipantId().getVersion());
294             participant.setDefinition(new ToscaConceptIdentifier("unknown", "0.0.0"));
295             participant.setParticipantState(participantStatusMessage.getState());
296             participant.setHealthStatus(participantStatusMessage.getHealthStatus());
297
298             participantList.add(participant);
299             participantProvider.createParticipants(participantList);
300         } else {
301             for (Participant participant : participantList) {
302                 participant.setParticipantState(participantStatusMessage.getState());
303                 participant.setHealthStatus(participantStatusMessage.getHealthStatus());
304             }
305             participantProvider.updateParticipants(participantList);
306         }
307
308         monitoringProvider.createParticipantStatistics(List.of(participantStatusMessage.getParticipantStatistics()));
309     }
310
311     private void superviseControlLoops(ParticipantStatus participantStatusMessage)
312             throws PfModelException, ControlLoopException {
313         if (CollectionUtils.isEmpty(participantStatusMessage.getControlLoops().getControlLoopList())) {
314             return;
315         }
316
317         for (ControlLoop controlLoop : participantStatusMessage.getControlLoops().getControlLoopList()) {
318             if (controlLoop == null) {
319                 exceptionOccured(Response.Status.NOT_FOUND,
320                         "PARTICIPANT_STATUS message references unknown control loop: " + controlLoop);
321             }
322
323             var dbControlLoop = controlLoopProvider
324                     .getControlLoop(new ToscaConceptIdentifier(controlLoop.getName(), controlLoop.getVersion()));
325             if (dbControlLoop == null) {
326                 exceptionOccured(Response.Status.NOT_FOUND,
327                         "PARTICIPANT_STATUS control loop not found in database: " + controlLoop);
328             }
329
330             for (ControlLoopElement element : controlLoop.getElements().values()) {
331                 ControlLoopElement dbElement = dbControlLoop.getElements().get(element.getId());
332
333                 if (dbElement == null) {
334                     exceptionOccured(Response.Status.NOT_FOUND,
335                             "PARTICIPANT_STATUS message references unknown control loop element: " + element);
336                 }
337
338                 // Replace element entry in the database
339                 dbControlLoop.getElements().put(element.getId(), element);
340             }
341             controlLoopProvider.updateControlLoop(dbControlLoop);
342         }
343
344         for (ControlLoop controlLoop : participantStatusMessage.getControlLoops().getControlLoopList()) {
345             monitoringProvider.createClElementStatistics(controlLoop.getControlLoopElementStatisticsList(controlLoop));
346         }
347     }
348
349     private void exceptionOccured(Response.Status status, String reason) throws ControlLoopException {
350         throw new ControlLoopException(status, reason);
351     }
352 }