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