48e06010dd1fce67606df5fd20fa323dddf03ab3
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2021 Nordix Foundation.
4  * ================================================================================
5  * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * SPDX-License-Identifier: Apache-2.0
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.clamp.controlloop.runtime.supervision;
24
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import org.apache.commons.lang3.tuple.Pair;
29 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
30 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
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.concepts.ParticipantHealthStatus;
34 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantUtils;
35 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
36 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantProvider;
37 import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup;
38 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopStateChangePublisher;
39 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopUpdatePublisher;
40 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantStatusReqPublisher;
41 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantUpdatePublisher;
42 import org.onap.policy.models.base.PfModelException;
43 import org.onap.policy.models.provider.PolicyModelsProvider;
44 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
45 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
46 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import org.springframework.stereotype.Component;
50
51 /**
52  * This class is used to scan the control loops in the database and check if they are in the correct state.
53  */
54 @Component
55 public class SupervisionScanner {
56     private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionScanner.class);
57
58     private final HandleCounter<ToscaConceptIdentifier> controlLoopCounter = new HandleCounter<>();
59     private final HandleCounter<ToscaConceptIdentifier> participantStatusCounter = new HandleCounter<>();
60     private final HandleCounter<Pair<ToscaConceptIdentifier, ToscaConceptIdentifier>> participantUpdateCounter =
61             new HandleCounter<>();
62
63     private final Map<ToscaConceptIdentifier, Integer> phaseMap = new HashMap<>();
64
65     private final ControlLoopProvider controlLoopProvider;
66     private final PolicyModelsProvider modelsProvider;
67     private final ControlLoopStateChangePublisher controlLoopStateChangePublisher;
68     private final ControlLoopUpdatePublisher controlLoopUpdatePublisher;
69     private final ParticipantProvider participantProvider;
70     private final ParticipantStatusReqPublisher participantStatusReqPublisher;
71     private final ParticipantUpdatePublisher participantUpdatePublisher;
72
73     /**
74      * Constructor for instantiating SupervisionScanner.
75      *
76      * @param controlLoopProvider the provider to use to read control loops from the database
77      * @param modelsProvider the Policy Models Provider
78      * @param controlLoopStateChangePublisher the ControlLoop StateChange Publisher
79      * @param controlLoopUpdatePublisher the ControlLoopUpdate Publisher
80      * @param participantProvider the Participant Provider
81      * @param participantStatusReqPublisher the Participant StatusReq Publisher
82      * @param participantUpdatePublisher the Participant Update Publisher
83      * @param clRuntimeParameterGroup the parameters for the control loop runtime
84      */
85     public SupervisionScanner(final ControlLoopProvider controlLoopProvider, PolicyModelsProvider modelsProvider,
86             final ControlLoopStateChangePublisher controlLoopStateChangePublisher,
87             ControlLoopUpdatePublisher controlLoopUpdatePublisher, ParticipantProvider participantProvider,
88             ParticipantStatusReqPublisher participantStatusReqPublisher,
89             ParticipantUpdatePublisher participantUpdatePublisher,
90             final ClRuntimeParameterGroup clRuntimeParameterGroup) {
91         this.controlLoopProvider = controlLoopProvider;
92         this.modelsProvider = modelsProvider;
93         this.controlLoopStateChangePublisher = controlLoopStateChangePublisher;
94         this.controlLoopUpdatePublisher = controlLoopUpdatePublisher;
95         this.participantProvider = participantProvider;
96         this.participantStatusReqPublisher = participantStatusReqPublisher;
97         this.participantUpdatePublisher = participantUpdatePublisher;
98
99         controlLoopCounter.setMaxRetryCount(
100                 clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount());
101         controlLoopCounter.setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs());
102
103         participantUpdateCounter.setMaxRetryCount(
104                 clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount());
105         participantUpdateCounter
106                 .setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxWaitMs());
107
108         participantStatusCounter.setMaxRetryCount(
109                 clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount());
110         participantStatusCounter.setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs());
111     }
112
113     /**
114      * Run Scanning.
115      *
116      * @param counterCheck if true activate counter and retry
117      */
118     public void run(boolean counterCheck) {
119         LOGGER.debug("Scanning control loops in the database . . .");
120
121         if (counterCheck) {
122             try {
123                 for (Participant participant : participantProvider.getParticipants(null, null)) {
124                     scanParticipantStatus(participant);
125                 }
126             } catch (PfModelException pfme) {
127                 LOGGER.warn("error reading participant from database", pfme);
128                 return;
129             }
130         }
131
132         try {
133             var list = modelsProvider.getServiceTemplateList(null, null);
134             if (list != null && !list.isEmpty()) {
135                 ToscaServiceTemplate toscaServiceTemplate = list.get(0);
136
137                 for (ControlLoop controlLoop : controlLoopProvider.getControlLoops(null, null)) {
138                     scanControlLoop(controlLoop, toscaServiceTemplate, counterCheck);
139                 }
140             }
141         } catch (PfModelException pfme) {
142             LOGGER.warn("error reading control loops from database", pfme);
143         }
144
145         if (counterCheck) {
146             scanParticipantUpdate();
147         }
148
149         LOGGER.debug("Control loop scan complete . . .");
150     }
151
152     private void scanParticipantUpdate() {
153         LOGGER.debug("Scanning participants to update . . .");
154
155         for (var id : participantUpdateCounter.keySet()) {
156             if (participantUpdateCounter.isFault(id)) {
157                 LOGGER.debug("report Participant Update fault");
158
159             } else if (participantUpdateCounter.getDuration(id) > participantUpdateCounter.getMaxWaitMs()) {
160
161                 if (participantUpdateCounter.count(id)) {
162                     LOGGER.debug("retry message ParticipantUpdate");
163                     participantUpdatePublisher.sendCommissioning(null, null, id.getLeft(), id.getRight());
164                 } else {
165                     LOGGER.debug("report Participant Update fault");
166                     participantUpdateCounter.setFault(id);
167                 }
168             }
169         }
170
171         LOGGER.debug("Participants to update scan complete . . .");
172     }
173
174     private void scanParticipantStatus(Participant participant) throws PfModelException {
175         ToscaConceptIdentifier id = participant.getKey().asIdentifier();
176         if (participantStatusCounter.isFault(id)) {
177             LOGGER.debug("report Participant fault");
178             return;
179         }
180         if (participantStatusCounter.getDuration(id) > participantStatusCounter.getMaxWaitMs()) {
181             if (participantStatusCounter.count(id)) {
182                 LOGGER.debug("retry message ParticipantStatusReq");
183                 participantStatusReqPublisher.send(id);
184                 participant.setHealthStatus(ParticipantHealthStatus.NOT_HEALTHY);
185             } else {
186                 LOGGER.debug("report Participant fault");
187                 participantStatusCounter.setFault(id);
188                 participant.setHealthStatus(ParticipantHealthStatus.OFF_LINE);
189             }
190             participantProvider.updateParticipants(List.of(participant));
191         }
192     }
193
194     /**
195      * handle participant Status message.
196      */
197     public void handleParticipantStatus(ToscaConceptIdentifier id) {
198         participantStatusCounter.clear(id);
199     }
200
201     public void handleParticipantRegister(Pair<ToscaConceptIdentifier, ToscaConceptIdentifier> id) {
202         participantUpdateCounter.clear(id);
203     }
204
205     public void handleParticipantUpdateAck(Pair<ToscaConceptIdentifier, ToscaConceptIdentifier> id) {
206         participantUpdateCounter.remove(id);
207     }
208
209     private void scanControlLoop(final ControlLoop controlLoop, ToscaServiceTemplate toscaServiceTemplate,
210             boolean counterCheck) throws PfModelException {
211         LOGGER.debug("scanning control loop {} . . .", controlLoop.getKey().asIdentifier());
212
213         if (controlLoop.getState().equals(controlLoop.getOrderedState().asState())) {
214             LOGGER.debug("control loop {} scanned, OK", controlLoop.getKey().asIdentifier());
215
216             // Clear missed report counter on Control Loop
217             clearFaultAndCounter(controlLoop);
218             return;
219         }
220
221         var completed = true;
222         var minSpNotCompleted = 1000; // min startPhase not completed
223         var maxSpNotCompleted = 0; // max startPhase not completed
224         var defaultMin = 1000; // min startPhase
225         var defaultMax = 0; // max startPhase
226         for (ControlLoopElement element : controlLoop.getElements().values()) {
227             ToscaNodeTemplate toscaNodeTemplate = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates()
228                     .get(element.getDefinition().getName());
229             int startPhase = ParticipantUtils.findStartPhase(toscaNodeTemplate.getProperties());
230             defaultMin = Math.min(defaultMin, startPhase);
231             defaultMax = Math.max(defaultMax, startPhase);
232             if (!element.getState().equals(element.getOrderedState().asState())) {
233                 completed = false;
234                 minSpNotCompleted = Math.min(minSpNotCompleted, startPhase);
235                 maxSpNotCompleted = Math.max(maxSpNotCompleted, startPhase);
236             }
237         }
238
239         if (completed) {
240             LOGGER.debug("control loop scan: transition from state {} to {} completed", controlLoop.getState(),
241                     controlLoop.getOrderedState());
242
243             controlLoop.setState(controlLoop.getOrderedState().asState());
244             controlLoopProvider.updateControlLoop(controlLoop);
245
246             // Clear missed report counter on Control Loop
247             clearFaultAndCounter(controlLoop);
248         } else {
249             LOGGER.debug("control loop scan: transition from state {} to {} not completed", controlLoop.getState(),
250                     controlLoop.getOrderedState());
251
252             var nextSpNotCompleted = ControlLoopState.UNINITIALISED2PASSIVE.equals(controlLoop.getState())
253                     || ControlLoopState.PASSIVE2RUNNING.equals(controlLoop.getState()) ? minSpNotCompleted
254                             : maxSpNotCompleted;
255
256             var firstStartPhase = ControlLoopState.UNINITIALISED2PASSIVE.equals(controlLoop.getState())
257                     || ControlLoopState.PASSIVE2RUNNING.equals(controlLoop.getState()) ? defaultMin
258                             : defaultMax;
259
260             if (nextSpNotCompleted != phaseMap.getOrDefault(controlLoop.getKey().asIdentifier(), firstStartPhase)) {
261                 phaseMap.put(controlLoop.getKey().asIdentifier(), nextSpNotCompleted);
262                 sendControlLoopMsg(controlLoop, nextSpNotCompleted);
263             } else if (counterCheck) {
264                 phaseMap.put(controlLoop.getKey().asIdentifier(), nextSpNotCompleted);
265                 handleCounter(controlLoop, nextSpNotCompleted);
266             }
267         }
268     }
269
270     private void clearFaultAndCounter(ControlLoop controlLoop) {
271         controlLoopCounter.clear(controlLoop.getKey().asIdentifier());
272         phaseMap.clear();
273     }
274
275     private void handleCounter(ControlLoop controlLoop, int startPhase) {
276         ToscaConceptIdentifier id = controlLoop.getKey().asIdentifier();
277         if (controlLoopCounter.isFault(id)) {
278             LOGGER.debug("report ControlLoop fault");
279             return;
280         }
281
282         if (controlLoopCounter.getDuration(id) > controlLoopCounter.getMaxWaitMs()) {
283             if (controlLoopCounter.count(id)) {
284                 phaseMap.put(id, startPhase);
285                 sendControlLoopMsg(controlLoop, startPhase);
286             } else {
287                 LOGGER.debug("report ControlLoop fault");
288                 controlLoopCounter.setFault(id);
289             }
290         }
291     }
292
293     private void sendControlLoopMsg(ControlLoop controlLoop, int startPhase) {
294         if (ControlLoopState.UNINITIALISED2PASSIVE.equals(controlLoop.getState())) {
295             LOGGER.debug("retry message ControlLoopUpdate");
296             controlLoopUpdatePublisher.send(controlLoop, startPhase);
297         } else {
298             LOGGER.debug("retry message ControlLoopStateChange");
299             controlLoopStateChangePublisher.send(controlLoop, startPhase);
300         }
301     }
302 }