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