9180cedb36ccdb927aca7c0ec210ce3cfbf8952d
[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 java.util.Map;
25 import javax.ws.rs.core.Response;
26 import lombok.AllArgsConstructor;
27 import org.apache.commons.collections4.CollectionUtils;
28 import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
29 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
30 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopInfo;
31 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
32 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant;
33 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
34 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantProvider;
35 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregister;
36 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegister;
37 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus;
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());
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      * Supervise a control loop, performing whatever actions need to be performed on the control loop.
170      *
171      * @param controlLoop the control loop to supervises
172      * @throws PfModelException on accessing models in the database
173      * @throws ControlLoopException on supervision errors
174      */
175     private void superviseControlLoop(ControlLoop controlLoop) throws ControlLoopException, PfModelException {
176         switch (controlLoop.getOrderedState()) {
177             case UNINITIALISED:
178                 superviseControlLoopUninitialization(controlLoop);
179                 break;
180
181             case PASSIVE:
182                 superviseControlLoopPassivation(controlLoop);
183                 break;
184
185             case RUNNING:
186                 superviseControlLoopActivation(controlLoop);
187                 break;
188
189             default:
190                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
191                         "A control loop cannot be commanded to go into state " + controlLoop.getOrderedState().name());
192         }
193     }
194
195     /**
196      * Supervise a control loop uninitialisation, performing whatever actions need to be performed on the control loop,
197      * control loop ordered state is UNINITIALIZED.
198      *
199      * @param controlLoop the control loop to supervises
200      * @throws ControlLoopException on supervision errors
201      */
202     private void superviseControlLoopUninitialization(ControlLoop controlLoop) throws ControlLoopException {
203         switch (controlLoop.getState()) {
204             case UNINITIALISED:
205                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
206                         CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
207                 break;
208
209             case UNINITIALISED2PASSIVE:
210             case PASSIVE:
211                 controlLoop.setState(ControlLoopState.PASSIVE2UNINITIALISED);
212                 controlLoopStateChangePublisher.send(controlLoop);
213                 break;
214
215             case PASSIVE2UNINITIALISED:
216                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
217                         + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
218                 break;
219
220             default:
221                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
222                         + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
223                 break;
224         }
225     }
226
227     private void superviseControlLoopPassivation(ControlLoop controlLoop)
228             throws ControlLoopException, PfModelException {
229         switch (controlLoop.getState()) {
230             case PASSIVE:
231                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
232                         CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
233                 break;
234             case UNINITIALISED:
235                 controlLoop.setState(ControlLoopState.UNINITIALISED2PASSIVE);
236                 controlLoopUpdatePublisher.send(controlLoop);
237                 break;
238
239             case UNINITIALISED2PASSIVE:
240             case RUNNING2PASSIVE:
241                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
242                         + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
243                 break;
244
245             case RUNNING:
246                 controlLoop.setState(ControlLoopState.RUNNING2PASSIVE);
247                 controlLoopStateChangePublisher.send(controlLoop);
248                 break;
249
250             default:
251                 exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
252                         + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
253                 break;
254         }
255     }
256
257     private void superviseControlLoopActivation(ControlLoop controlLoop) throws ControlLoopException {
258         switch (controlLoop.getState()) {
259             case RUNNING:
260                 exceptionOccured(Response.Status.NOT_ACCEPTABLE,
261                         CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
262                 break;
263
264             case PASSIVE2RUNNING:
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 PASSIVE:
270                 controlLoop.setState(ControlLoopState.PASSIVE2RUNNING);
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 superviseParticipant(ParticipantStatus participantStatusMessage)
282             throws PfModelException, ControlLoopException {
283         if (participantStatusMessage.getParticipantId() == null) {
284             exceptionOccured(Response.Status.NOT_FOUND, "Participant ID on PARTICIPANT_STATUS message is null");
285         }
286
287         List<Participant> participantList =
288                 participantProvider.getParticipants(participantStatusMessage.getParticipantId().getName(),
289                         participantStatusMessage.getParticipantId().getVersion());
290
291         if (CollectionUtils.isEmpty(participantList)) {
292             var participant = new Participant();
293             participant.setName(participantStatusMessage.getParticipantId().getName());
294             participant.setVersion(participantStatusMessage.getParticipantId().getVersion());
295             participant.setDefinition(new ToscaConceptIdentifier("unknown", "0.0.0"));
296             participant.setParticipantState(participantStatusMessage.getState());
297             participant.setHealthStatus(participantStatusMessage.getHealthStatus());
298
299             participantList.add(participant);
300             participantProvider.createParticipants(participantList);
301         } else {
302             for (Participant participant : participantList) {
303                 participant.setParticipantState(participantStatusMessage.getState());
304                 participant.setHealthStatus(participantStatusMessage.getHealthStatus());
305             }
306             participantProvider.updateParticipants(participantList);
307         }
308
309         monitoringProvider.createParticipantStatistics(List.of(participantStatusMessage.getParticipantStatistics()));
310     }
311
312     private void superviseControlLoops(ParticipantStatus participantStatusMessage)
313             throws PfModelException, ControlLoopException {
314         if (participantStatusMessage.getControlLoopInfoMap() != null) {
315             for (Map.Entry<String, ControlLoopInfo> clEntry : participantStatusMessage.getControlLoopInfoMap()
316                     .entrySet()) {
317                 String[] key = clEntry.getKey().split(" ");
318                 var dbControlLoop = controlLoopProvider.getControlLoop(
319                         new ToscaConceptIdentifier(key[0], key[1]));
320                 if (dbControlLoop == null) {
321                     exceptionOccured(Response.Status.NOT_FOUND,
322                             "PARTICIPANT_STATUS control loop not found in database: " + clEntry.getKey());
323                 }
324                 dbControlLoop.setState(clEntry.getValue().getState());
325                 monitoringProvider.createClElementStatistics(clEntry.getValue().getControlLoopStatistics()
326                         .getClElementStatisticsList().getClElementStatistics());
327             }
328         }
329     }
330
331     private void exceptionOccured(Response.Status status, String reason) throws ControlLoopException {
332         throw new ControlLoopException(status, reason);
333     }
334 }