900a117bde0270f95b35538edb845a0e9285e6c0
[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.models.controlloop.persistence.provider.ServiceTemplateProvider;
38 import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup;
39 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopStateChangePublisher;
40 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopUpdatePublisher;
41 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantStatusReqPublisher;
42 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantUpdatePublisher;
43 import org.onap.policy.models.base.PfModelException;
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 ServiceTemplateProvider serviceTemplateProvider;
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 serviceTemplateProvider 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,
86             ServiceTemplateProvider serviceTemplateProvider,
87             final ControlLoopStateChangePublisher controlLoopStateChangePublisher,
88             ControlLoopUpdatePublisher controlLoopUpdatePublisher, ParticipantProvider participantProvider,
89             ParticipantStatusReqPublisher participantStatusReqPublisher,
90             ParticipantUpdatePublisher participantUpdatePublisher,
91             final ClRuntimeParameterGroup clRuntimeParameterGroup) {
92         this.controlLoopProvider = controlLoopProvider;
93         this.serviceTemplateProvider = serviceTemplateProvider;
94         this.controlLoopStateChangePublisher = controlLoopStateChangePublisher;
95         this.controlLoopUpdatePublisher = controlLoopUpdatePublisher;
96         this.participantProvider = participantProvider;
97         this.participantStatusReqPublisher = participantStatusReqPublisher;
98         this.participantUpdatePublisher = participantUpdatePublisher;
99
100         controlLoopCounter.setMaxRetryCount(
101                 clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount());
102         controlLoopCounter.setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs());
103
104         participantUpdateCounter.setMaxRetryCount(
105                 clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount());
106         participantUpdateCounter
107                 .setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxWaitMs());
108
109         participantStatusCounter.setMaxRetryCount(
110                 clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount());
111         participantStatusCounter.setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs());
112     }
113
114     /**
115      * Run Scanning.
116      *
117      * @param counterCheck if true activate counter and retry
118      */
119     public void run(boolean counterCheck) {
120         LOGGER.debug("Scanning control loops in the database . . .");
121
122         if (counterCheck) {
123             try {
124                 for (Participant participant : participantProvider.getParticipants(null, null)) {
125                     scanParticipantStatus(participant);
126                 }
127             } catch (PfModelException pfme) {
128                 LOGGER.warn("error reading participant from database", pfme);
129                 return;
130             }
131         }
132
133         try {
134             var list = serviceTemplateProvider.getServiceTemplateList(null, null);
135             if (list != null && !list.isEmpty()) {
136                 ToscaServiceTemplate toscaServiceTemplate = list.get(0);
137
138                 for (ControlLoop controlLoop : controlLoopProvider.getControlLoops(null, null)) {
139                     scanControlLoop(controlLoop, toscaServiceTemplate, counterCheck);
140                 }
141             }
142         } catch (PfModelException pfme) {
143             LOGGER.warn("error reading control loops from database", pfme);
144         }
145
146         if (counterCheck) {
147             scanParticipantUpdate();
148         }
149
150         LOGGER.debug("Control loop scan complete . . .");
151     }
152
153     private void scanParticipantUpdate() {
154         LOGGER.debug("Scanning participants to update . . .");
155
156         for (var id : participantUpdateCounter.keySet()) {
157             if (participantUpdateCounter.isFault(id)) {
158                 LOGGER.debug("report Participant Update fault");
159
160             } else if (participantUpdateCounter.getDuration(id) > participantUpdateCounter.getMaxWaitMs()) {
161
162                 if (participantUpdateCounter.count(id)) {
163                     LOGGER.debug("retry message ParticipantUpdate");
164                     participantUpdatePublisher.sendCommissioning(null, null, id.getLeft(), id.getRight());
165                 } else {
166                     LOGGER.debug("report Participant Update fault");
167                     participantUpdateCounter.setFault(id);
168                 }
169             }
170         }
171
172         LOGGER.debug("Participants to update scan complete . . .");
173     }
174
175     private void scanParticipantStatus(Participant participant) throws PfModelException {
176         ToscaConceptIdentifier id = participant.getKey().asIdentifier();
177         if (participantStatusCounter.isFault(id)) {
178             LOGGER.debug("report Participant fault");
179             return;
180         }
181         if (participantStatusCounter.getDuration(id) > participantStatusCounter.getMaxWaitMs()) {
182             if (participantStatusCounter.count(id)) {
183                 LOGGER.debug("retry message ParticipantStatusReq");
184                 participantStatusReqPublisher.send(id);
185                 participant.setHealthStatus(ParticipantHealthStatus.NOT_HEALTHY);
186             } else {
187                 LOGGER.debug("report Participant fault");
188                 participantStatusCounter.setFault(id);
189                 participant.setHealthStatus(ParticipantHealthStatus.OFF_LINE);
190             }
191             participantProvider.updateParticipants(List.of(participant));
192         }
193     }
194
195     /**
196      * handle participant Status message.
197      */
198     public void handleParticipantStatus(ToscaConceptIdentifier id) {
199         participantStatusCounter.clear(id);
200     }
201
202     public void handleParticipantRegister(Pair<ToscaConceptIdentifier, ToscaConceptIdentifier> id) {
203         participantUpdateCounter.clear(id);
204     }
205
206     public void handleParticipantUpdateAck(Pair<ToscaConceptIdentifier, ToscaConceptIdentifier> id) {
207         participantUpdateCounter.remove(id);
208     }
209
210     private void scanControlLoop(final ControlLoop controlLoop, ToscaServiceTemplate toscaServiceTemplate,
211             boolean counterCheck) throws PfModelException {
212         LOGGER.debug("scanning control loop {} . . .", controlLoop.getKey().asIdentifier());
213
214         if (controlLoop.getState().equals(controlLoop.getOrderedState().asState())) {
215             LOGGER.debug("control loop {} scanned, OK", controlLoop.getKey().asIdentifier());
216
217             // Clear missed report counter on Control Loop
218             clearFaultAndCounter(controlLoop);
219             return;
220         }
221
222         var completed = true;
223         var minSpNotCompleted = 1000; // min startPhase not completed
224         var maxSpNotCompleted = 0; // max startPhase not completed
225         var defaultMin = 1000; // min startPhase
226         var defaultMax = 0; // max startPhase
227         for (ControlLoopElement element : controlLoop.getElements().values()) {
228             ToscaNodeTemplate toscaNodeTemplate = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates()
229                     .get(element.getDefinition().getName());
230             int startPhase = ParticipantUtils.findStartPhase(toscaNodeTemplate.getProperties());
231             defaultMin = Math.min(defaultMin, startPhase);
232             defaultMax = Math.max(defaultMax, startPhase);
233             if (!element.getState().equals(element.getOrderedState().asState())) {
234                 completed = false;
235                 minSpNotCompleted = Math.min(minSpNotCompleted, startPhase);
236                 maxSpNotCompleted = Math.max(maxSpNotCompleted, startPhase);
237             }
238         }
239
240         if (completed) {
241             LOGGER.debug("control loop scan: transition from state {} to {} completed", controlLoop.getState(),
242                     controlLoop.getOrderedState());
243
244             controlLoop.setState(controlLoop.getOrderedState().asState());
245             controlLoopProvider.updateControlLoop(controlLoop);
246
247             // Clear missed report counter on Control Loop
248             clearFaultAndCounter(controlLoop);
249         } else {
250             LOGGER.debug("control loop scan: transition from state {} to {} not completed", controlLoop.getState(),
251                     controlLoop.getOrderedState());
252
253             var nextSpNotCompleted = ControlLoopState.UNINITIALISED2PASSIVE.equals(controlLoop.getState())
254                     || ControlLoopState.PASSIVE2RUNNING.equals(controlLoop.getState()) ? minSpNotCompleted
255                             : maxSpNotCompleted;
256
257             var firstStartPhase = ControlLoopState.UNINITIALISED2PASSIVE.equals(controlLoop.getState())
258                     || ControlLoopState.PASSIVE2RUNNING.equals(controlLoop.getState()) ? defaultMin
259                             : defaultMax;
260
261             if (nextSpNotCompleted != phaseMap.getOrDefault(controlLoop.getKey().asIdentifier(), firstStartPhase)) {
262                 phaseMap.put(controlLoop.getKey().asIdentifier(), nextSpNotCompleted);
263                 sendControlLoopMsg(controlLoop, nextSpNotCompleted);
264             } else if (counterCheck) {
265                 phaseMap.put(controlLoop.getKey().asIdentifier(), nextSpNotCompleted);
266                 handleCounter(controlLoop, nextSpNotCompleted);
267             }
268         }
269     }
270
271     private void clearFaultAndCounter(ControlLoop controlLoop) {
272         controlLoopCounter.clear(controlLoop.getKey().asIdentifier());
273         phaseMap.clear();
274     }
275
276     private void handleCounter(ControlLoop controlLoop, int startPhase) {
277         ToscaConceptIdentifier id = controlLoop.getKey().asIdentifier();
278         if (controlLoopCounter.isFault(id)) {
279             LOGGER.debug("report ControlLoop fault");
280             return;
281         }
282
283         if (controlLoopCounter.getDuration(id) > controlLoopCounter.getMaxWaitMs()) {
284             if (controlLoopCounter.count(id)) {
285                 phaseMap.put(id, startPhase);
286                 sendControlLoopMsg(controlLoop, startPhase);
287             } else {
288                 LOGGER.debug("report ControlLoop fault");
289                 controlLoopCounter.setFault(id);
290             }
291         }
292     }
293
294     private void sendControlLoopMsg(ControlLoop controlLoop, int startPhase) {
295         if (ControlLoopState.UNINITIALISED2PASSIVE.equals(controlLoop.getState())) {
296             LOGGER.debug("retry message ControlLoopUpdate");
297             controlLoopUpdatePublisher.send(controlLoop, startPhase);
298         } else {
299             LOGGER.debug("retry message ControlLoopStateChange");
300             controlLoopStateChangePublisher.send(controlLoop, startPhase);
301         }
302     }
303 }