a426cac4a16bdc41a756ae14d1bbb9586946c908
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation.
4  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.clamp.controlloop.runtime.supervision;
23
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.UUID;
28 import javax.ws.rs.core.Response;
29 import lombok.AllArgsConstructor;
30 import org.apache.commons.collections4.CollectionUtils;
31 import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
32 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
33 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementAck;
34 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopInfo;
35 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
36 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant;
37 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
38 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantProvider;
39 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopAck;
40 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregister;
41 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegister;
42 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus;
43 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdate;
44 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdateAck;
45 import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringProvider;
46 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopStateChangePublisher;
47 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopUpdatePublisher;
48 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantDeregisterAckPublisher;
49 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantRegisterAckPublisher;
50 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantUpdatePublisher;
51 import org.onap.policy.models.base.PfModelException;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.stereotype.Component;
56
57 /**
58  * This class handles supervision of control loop instances, so only one object of this type should be built at a time.
59  *
60  * <p/>
61  * It is effectively a singleton that is started at system start.
62  */
63 @Component
64 @AllArgsConstructor
65 public class SupervisionHandler {
66     private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionHandler.class);
67
68     private static final String CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE = "Control loop can't transition from state ";
69     private static final String CONTROL_LOOP_IS_ALREADY_IN_STATE = "Control loop is already in state ";
70     private static final String TO_STATE = " to state ";
71     private static final String AND_TRANSITIONING_TO_STATE = " and transitioning to state ";
72
73     private final ControlLoopProvider controlLoopProvider;
74     private final ParticipantProvider participantProvider;
75     private final MonitoringProvider monitoringProvider;
76
77     // Publishers for participant communication
78     private final ControlLoopUpdatePublisher controlLoopUpdatePublisher;
79     private final ControlLoopStateChangePublisher controlLoopStateChangePublisher;
80     private final ParticipantRegisterAckPublisher participantRegisterAckPublisher;
81     private final ParticipantDeregisterAckPublisher participantDeregisterAckPublisher;
82     private final ParticipantUpdatePublisher participantUpdatePublisher;
83
84     /**
85      * Supervision trigger called when a command is issued on control loops.
86      *
87      * <p/>
88      * Causes supervision to start or continue supervision on the control loops in question.
89      *
90      * @param controlLoopIdentifierList the control loops for which the supervision command has been issued
91      * @throws ControlLoopException on supervision triggering exceptions
92      */
93     public void triggerControlLoopSupervision(List<ToscaConceptIdentifier> controlLoopIdentifierList)
94             throws ControlLoopException {
95
96         LOGGER.debug("triggering control loop supervision on control loops {}", controlLoopIdentifierList);
97
98         if (CollectionUtils.isEmpty(controlLoopIdentifierList)) {
99             // This is just to force throwing of the exception in certain circumstances.
100             exceptionOccured(Response.Status.NOT_ACCEPTABLE, "The list of control loops for supervision is empty");
101         }
102
103         for (ToscaConceptIdentifier controlLoopId : controlLoopIdentifierList) {
104             try {
105                 var controlLoop = controlLoopProvider.getControlLoop(controlLoopId);
106
107                 superviseControlLoop(controlLoop);
108
109                 controlLoopProvider.updateControlLoop(controlLoop);
110             } catch (PfModelException pfme) {
111                 throw new ControlLoopException(pfme.getErrorResponse().getResponseCode(), pfme.getMessage(), pfme);
112             }
113         }
114     }
115
116     /**
117      * Handle a ParticipantStatus message from a participant.
118      *
119      * @param participantStatusMessage the ParticipantStatus message received from a participant
120      */
121     @MessageIntercept
122     public void handleParticipantMessage(ParticipantStatus participantStatusMessage) {
123         LOGGER.debug("Participant Status received {}", participantStatusMessage);
124         try {
125             superviseParticipant(participantStatusMessage);
126         } catch (PfModelException | ControlLoopException svExc) {
127             LOGGER.warn("error supervising participant {}", participantStatusMessage.getParticipantId(), svExc);
128             return;
129         }
130
131         try {
132             superviseControlLoops(participantStatusMessage);
133         } catch (PfModelException | ControlLoopException svExc) {
134             LOGGER.warn("error supervising participant {}", participantStatusMessage.getParticipantId(), svExc);
135         }
136     }
137
138     /**
139      * Handle a ParticipantRegister message from a participant.
140      *
141      * @param participantRegisterMessage the ParticipantRegister message received from a participant
142      */
143     @MessageIntercept
144     public void handleParticipantMessage(ParticipantRegister participantRegisterMessage) {
145         LOGGER.debug("Participant Register received {}", participantRegisterMessage);
146
147         participantRegisterAckPublisher.send(participantRegisterMessage.getMessageId(),
148                 participantRegisterMessage.getParticipantId(), participantRegisterMessage.getParticipantType());
149
150         participantUpdatePublisher.send(participantRegisterMessage.getParticipantId(),
151                 participantRegisterMessage.getParticipantType(), true);
152     }
153
154     /**
155      * Handle a ParticipantDeregister message from a participant.
156      *
157      * @param participantDeregisterMessage the ParticipantDeregister message received from a participant
158      */
159     @MessageIntercept
160     public void handleParticipantMessage(ParticipantDeregister participantDeregisterMessage) {
161         LOGGER.debug("Participant Deregister received {}", participantDeregisterMessage);
162         participantDeregisterAckPublisher.send(participantDeregisterMessage.getMessageId());
163     }
164
165     /**
166      * Handle a ParticipantUpdateAck message from a participant.
167      *
168      * @param participantUpdateAckMessage the ParticipantUpdateAck message received from a participant
169      */
170     @MessageIntercept
171     public void handleParticipantMessage(ParticipantUpdateAck participantUpdateAckMessage) {
172         LOGGER.debug("Participant Update Ack received {}", participantUpdateAckMessage);
173         try {
174             var participantList =
175                 participantProvider.getParticipants(participantUpdateAckMessage.getParticipantId().getName(),
176                     participantUpdateAckMessage.getParticipantId().getVersion());
177
178             if (participantList != null) {
179                 for (Participant participant : participantList) {
180                     participant.setParticipantState(participantUpdateAckMessage.getState());
181                 }
182                 participantProvider.updateParticipants(participantList);
183             } else {
184                 LOGGER.warn("Participant not found in database {}", participantUpdateAckMessage.getParticipantId());
185             }
186         } catch (PfModelException pfme) {
187             LOGGER.warn("Model exception occured {}", participantUpdateAckMessage.getParticipantId());
188         }
189     }
190
191     /**
192      * Send commissioning update message to dmaap.
193      *
194      * @param participantUpdateMessage the ParticipantUpdate message to send
195      */
196     public void handleSendCommissionMessage(ParticipantUpdate participantUpdateMessage) {
197         LOGGER.debug("Participant update message being sent {}", participantUpdateMessage);
198
199         participantUpdatePublisher.send(participantUpdateMessage.getParticipantId(),
200             participantUpdateMessage.getParticipantType(), true);
201     }
202
203     /**
204      * Send decommissioning update message to dmaap.
205      *
206      * @param participantUpdateMessage the ParticipantUpdate message to send
207      */
208     public void handleSendDeCommissionMessage(ParticipantUpdate participantUpdateMessage) {
209         LOGGER.debug("Participant update message being sent {}", participantUpdateMessage);
210
211         participantUpdatePublisher.send(participantUpdateMessage.getParticipantId(),
212             participantUpdateMessage.getParticipantType(), false);
213     }
214
215     /**
216      * Handle a ControlLoop update acknowledge message from a participant.
217      *
218      * @param controlLoopAckMessage the ControlLoopAck message received from a participant
219      */
220     @MessageIntercept
221     public void handleControlLoopUpdateAckMessage(ControlLoopAck controlLoopAckMessage) {
222         LOGGER.debug("ControlLoop Update Ack message received {}", controlLoopAckMessage);
223         setClElementStateInDb(controlLoopAckMessage);
224     }
225
226     /**
227      * Handle a ControlLoop statechange acknowledge message from a participant.
228      *
229      * @param controlLoopAckMessage the ControlLoopAck message received from a participant
230      */
231     @MessageIntercept
232     public void handleControlLoopStateChangeAckMessage(ControlLoopAck controlLoopAckMessage) {
233         LOGGER.debug("ControlLoop StateChange Ack message received {}", controlLoopAckMessage);
234         setClElementStateInDb(controlLoopAckMessage);
235     }
236
237     private void setClElementStateInDb(ControlLoopAck controlLoopAckMessage) {
238         if (controlLoopAckMessage.getControlLoopResultMap() != null) {
239             try {
240                 var controlLoop = controlLoopProvider.getControlLoop(controlLoopAckMessage.getControlLoopId());
241                 if (controlLoop != null) {
242                     var updated = updateState(controlLoop, controlLoopAckMessage
243                             .getControlLoopResultMap().entrySet());
244                     if (updated) {
245                         controlLoopProvider.updateControlLoop(controlLoop);
246                     }
247                 } else {
248                     LOGGER.warn("ControlLoop not found in database {}", controlLoopAckMessage.getControlLoopId());
249                 }
250             } catch (PfModelException pfme) {
251                 LOGGER.warn("Model exception occured {}", controlLoopAckMessage.getControlLoopId());
252             }
253         }
254     }
255
256     private boolean updateState(ControlLoop controlLoop, Set<Map.Entry<UUID, ControlLoopElementAck>>
257             controlLoopResultSet) {
258         var updated = false;
259         for (var clElementAck : controlLoopResultSet) {
260             var element = controlLoop.getElements().get(clElementAck.getKey());
261             if (element != null) {
262                 element.setState(clElementAck.getValue().getState());
263                 updated = true;
264             }
265         }
266         return updated;
267     }
268
269     /**
270      * Supervise a control loop, performing whatever actions need to be performed on the control loop.
271      *
272      * @param controlLoop the control loop to supervises
273      * @throws ControlLoopException on supervision errors
274      */
275     private void superviseControlLoop(ControlLoop controlLoop) throws ControlLoopException {
276         switch (controlLoop.getOrderedState()) {
277             case UNINITIALISED:
278                 superviseControlLoopUninitialization(controlLoop);
279                 break;
280
281             case PASSIVE:
282                 superviseControlLoopPassivation(controlLoop);
283                 break;
284
285             case RUNNING:
286                 superviseControlLoopActivation(controlLoop);
287                 break;
288
289             default:
290                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
291                         "A control loop cannot be commanded to go into state " + controlLoop.getOrderedState().name());
292         }
293     }
294
295     /**
296      * Supervise a control loop uninitialisation, performing whatever actions need to be performed on the control loop,
297      * control loop ordered state is UNINITIALIZED.
298      *
299      * @param controlLoop the control loop to supervises
300      * @throws ControlLoopException on supervision errors
301      */
302     private void superviseControlLoopUninitialization(ControlLoop controlLoop) throws ControlLoopException {
303         switch (controlLoop.getState()) {
304             case UNINITIALISED:
305                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
306                         CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
307                 break;
308
309             case UNINITIALISED2PASSIVE:
310             case PASSIVE:
311                 controlLoop.setState(ControlLoopState.PASSIVE2UNINITIALISED);
312                 controlLoopStateChangePublisher.send(controlLoop);
313                 break;
314
315             case PASSIVE2UNINITIALISED:
316                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
317                         + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
318                 break;
319
320             default:
321                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
322                         + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
323                 break;
324         }
325     }
326
327     private void superviseControlLoopPassivation(ControlLoop controlLoop)
328             throws ControlLoopException {
329         switch (controlLoop.getState()) {
330             case PASSIVE:
331                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
332                         CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
333                 break;
334             case UNINITIALISED:
335                 controlLoop.setState(ControlLoopState.UNINITIALISED2PASSIVE);
336                 controlLoopUpdatePublisher.send(controlLoop);
337                 break;
338
339             case UNINITIALISED2PASSIVE:
340             case RUNNING2PASSIVE:
341                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
342                         + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
343                 break;
344
345             case RUNNING:
346                 controlLoop.setState(ControlLoopState.RUNNING2PASSIVE);
347                 controlLoopStateChangePublisher.send(controlLoop);
348                 break;
349
350             default:
351                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
352                         + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
353                 break;
354         }
355     }
356
357     private void superviseControlLoopActivation(ControlLoop controlLoop) throws ControlLoopException {
358         switch (controlLoop.getState()) {
359             case RUNNING:
360                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
361                         CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
362                 break;
363
364             case PASSIVE2RUNNING:
365                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
366                         + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
367                 break;
368
369             case PASSIVE:
370                 controlLoop.setState(ControlLoopState.PASSIVE2RUNNING);
371                 controlLoopStateChangePublisher.send(controlLoop);
372                 break;
373
374             default:
375                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
376                         + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
377                 break;
378         }
379     }
380
381     private void superviseParticipant(ParticipantStatus participantStatusMessage)
382             throws PfModelException, ControlLoopException {
383         if (participantStatusMessage.getParticipantId() == null) {
384             exceptionOccured(Response.Status.NOT_FOUND, "Participant ID on PARTICIPANT_STATUS message is null");
385         }
386
387         List<Participant> participantList =
388                 participantProvider.getParticipants(participantStatusMessage.getParticipantId().getName(),
389                         participantStatusMessage.getParticipantId().getVersion());
390
391         if (CollectionUtils.isEmpty(participantList)) {
392             var participant = new Participant();
393             participant.setName(participantStatusMessage.getParticipantId().getName());
394             participant.setVersion(participantStatusMessage.getParticipantId().getVersion());
395             participant.setDefinition(new ToscaConceptIdentifier("unknown", "0.0.0"));
396             participant.setParticipantState(participantStatusMessage.getState());
397             participant.setHealthStatus(participantStatusMessage.getHealthStatus());
398
399             participantList.add(participant);
400             participantProvider.createParticipants(participantList);
401         } else {
402             for (Participant participant : participantList) {
403                 participant.setParticipantState(participantStatusMessage.getState());
404                 participant.setHealthStatus(participantStatusMessage.getHealthStatus());
405             }
406             participantProvider.updateParticipants(participantList);
407         }
408
409         monitoringProvider.createParticipantStatistics(List.of(participantStatusMessage.getParticipantStatistics()));
410     }
411
412     private void superviseControlLoops(ParticipantStatus participantStatusMessage)
413             throws PfModelException, ControlLoopException {
414         if (participantStatusMessage.getControlLoopInfoList() != null) {
415             for (ControlLoopInfo clEntry : participantStatusMessage.getControlLoopInfoList()) {
416                 var dbControlLoop =
417                         controlLoopProvider.getControlLoop(new ToscaConceptIdentifier(clEntry.getControlLoopId()));
418                 if (dbControlLoop == null) {
419                     exceptionOccured(Response.Status.NOT_FOUND,
420                             "PARTICIPANT_STATUS control loop not found in database: " + clEntry.getControlLoopId());
421                 }
422                 dbControlLoop.setState(clEntry.getState());
423                 monitoringProvider.createClElementStatistics(
424                         clEntry.getControlLoopStatistics().getClElementStatisticsList().getClElementStatistics());
425             }
426         }
427     }
428
429     private void exceptionOccured(Response.Status status, String reason) throws ControlLoopException {
430         throw new ControlLoopException(status, reason);
431     }
432 }