684711677d073a5cfe1771a42af910767aea35fa
[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.List;
26 import org.apache.commons.lang3.tuple.Pair;
27 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
28 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
29 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
30 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant;
31 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantHealthStatus;
32 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantUtils;
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.runtime.main.parameters.ClRuntimeParameterGroup;
36 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopStateChangePublisher;
37 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopUpdatePublisher;
38 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantStatusReqPublisher;
39 import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantUpdatePublisher;
40 import org.onap.policy.models.base.PfModelException;
41 import org.onap.policy.models.provider.PolicyModelsProvider;
42 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
43 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
44 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.springframework.stereotype.Component;
48
49 /**
50  * This class is used to scan the control loops in the database and check if they are in the correct state.
51  */
52 @Component
53 public class SupervisionScanner {
54     private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionScanner.class);
55
56     private HandleCounter<ToscaConceptIdentifier> controlLoopCounter = new HandleCounter<>();
57     private HandleCounter<ToscaConceptIdentifier> participantStatusCounter = new HandleCounter<>();
58     private HandleCounter<Pair<ToscaConceptIdentifier, ToscaConceptIdentifier>> participantUpdateCounter =
59             new HandleCounter<>();
60
61     private final ControlLoopProvider controlLoopProvider;
62     private final PolicyModelsProvider modelsProvider;
63     private final ControlLoopStateChangePublisher controlLoopStateChangePublisher;
64     private final ControlLoopUpdatePublisher controlLoopUpdatePublisher;
65     private final ParticipantProvider participantProvider;
66     private final ParticipantStatusReqPublisher participantStatusReqPublisher;
67     private final ParticipantUpdatePublisher participantUpdatePublisher;
68
69     /**
70      * Constructor for instantiating SupervisionScanner.
71      *
72      * @param controlLoopProvider the provider to use to read control loops from the database
73      * @param modelsProvider the Policy Models Provider
74      * @param controlLoopStateChangePublisher the ControlLoop StateChange Publisher
75      * @param controlLoopUpdatePublisher the ControlLoopUpdate Publisher
76      * @param participantProvider the Participant Provider
77      * @param participantStatusReqPublisher the Participant StatusReq Publisher
78      * @param participantUpdatePublisher the Participant Update Publisher
79      * @param clRuntimeParameterGroup the parameters for the control loop runtime
80      */
81     public SupervisionScanner(final ControlLoopProvider controlLoopProvider, PolicyModelsProvider modelsProvider,
82             final ControlLoopStateChangePublisher controlLoopStateChangePublisher,
83             ControlLoopUpdatePublisher controlLoopUpdatePublisher, ParticipantProvider participantProvider,
84             ParticipantStatusReqPublisher participantStatusReqPublisher,
85             ParticipantUpdatePublisher participantUpdatePublisher,
86             final ClRuntimeParameterGroup clRuntimeParameterGroup) {
87         this.controlLoopProvider = controlLoopProvider;
88         this.modelsProvider = modelsProvider;
89         this.controlLoopStateChangePublisher = controlLoopStateChangePublisher;
90         this.controlLoopUpdatePublisher = controlLoopUpdatePublisher;
91         this.participantProvider = participantProvider;
92         this.participantStatusReqPublisher = participantStatusReqPublisher;
93         this.participantUpdatePublisher = participantUpdatePublisher;
94
95         controlLoopCounter.setMaxRetryCount(
96                 clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount());
97         controlLoopCounter.setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs());
98
99         participantUpdateCounter.setMaxRetryCount(
100                 clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount());
101         participantUpdateCounter
102                 .setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxWaitMs());
103
104         participantStatusCounter.setMaxRetryCount(
105                 clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount());
106         participantStatusCounter.setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs());
107     }
108
109     /**
110      * Run Scanning.
111      *
112      * @param counterCheck if true activate counter and retry
113      */
114     public void run(boolean counterCheck) {
115         LOGGER.debug("Scanning control loops in the database . . .");
116
117         if (counterCheck) {
118             try {
119                 for (Participant participant : participantProvider.getParticipants(null, null)) {
120                     scanParticipantStatus(participant);
121                 }
122             } catch (PfModelException pfme) {
123                 LOGGER.warn("error reading participant from database", pfme);
124                 return;
125             }
126         }
127
128         try {
129             var list = modelsProvider.getServiceTemplateList(null, null);
130             if (list != null && !list.isEmpty()) {
131                 ToscaServiceTemplate toscaServiceTemplate = list.get(0);
132
133                 for (ControlLoop controlLoop : controlLoopProvider.getControlLoops(null, null)) {
134                     scanControlLoop(controlLoop, toscaServiceTemplate, counterCheck);
135                 }
136             }
137         } catch (PfModelException pfme) {
138             LOGGER.warn("error reading control loops from database", pfme);
139         }
140
141         if (counterCheck) {
142             scanParticipantUpdate();
143         }
144
145         LOGGER.debug("Control loop scan complete . . .");
146     }
147
148     private void scanParticipantUpdate() {
149         LOGGER.debug("Scanning participants to update . . .");
150
151         for (var id : participantUpdateCounter.keySet()) {
152             if (participantUpdateCounter.isFault(id)) {
153                 LOGGER.debug("report Participant Update fault");
154
155             } else if (participantUpdateCounter.getDuration(id) > participantUpdateCounter.getMaxWaitMs()) {
156
157                 if (participantUpdateCounter.count(id)) {
158                     LOGGER.debug("retry message ParticipantUpdate");
159                     participantUpdatePublisher.send(null, true);
160                 } else {
161                     LOGGER.debug("report Participant Update fault");
162                     participantUpdateCounter.setFault(id);
163                 }
164             }
165         }
166
167         LOGGER.debug("Participants to update scan complete . . .");
168     }
169
170     private void scanParticipantStatus(Participant participant) throws PfModelException {
171         ToscaConceptIdentifier id = participant.getKey().asIdentifier();
172         if (participantStatusCounter.isFault(id)) {
173             LOGGER.debug("report Participant fault");
174             return;
175         }
176         if (participantStatusCounter.getDuration(id) > participantStatusCounter.getMaxWaitMs()) {
177             if (participantStatusCounter.count(id)) {
178                 LOGGER.debug("retry message ParticipantStatusReq");
179                 participantStatusReqPublisher.send(id);
180                 participant.setHealthStatus(ParticipantHealthStatus.NOT_HEALTHY);
181             } else {
182                 LOGGER.debug("report Participant fault");
183                 participantStatusCounter.setFault(id);
184                 participant.setHealthStatus(ParticipantHealthStatus.OFF_LINE);
185             }
186             participantProvider.updateParticipants(List.of(participant));
187         }
188     }
189
190     /**
191      * handle participant Status message.
192      */
193     public void handleParticipantStatus(ToscaConceptIdentifier id) {
194         participantStatusCounter.clear(id);
195     }
196
197     public void handleParticipantRegister(Pair<ToscaConceptIdentifier, ToscaConceptIdentifier> id) {
198         participantUpdateCounter.clear(id);
199     }
200
201     public void handleParticipantUpdateAck(Pair<ToscaConceptIdentifier, ToscaConceptIdentifier> id) {
202         participantUpdateCounter.remove(id);
203     }
204
205     private void scanControlLoop(final ControlLoop controlLoop, ToscaServiceTemplate toscaServiceTemplate,
206             boolean counterCheck) throws PfModelException {
207         LOGGER.debug("scanning control loop {} . . .", controlLoop.getKey().asIdentifier());
208
209         if (controlLoop.getState().equals(controlLoop.getOrderedState().asState())) {
210             LOGGER.debug("control loop {} scanned, OK", controlLoop.getKey().asIdentifier());
211
212             // Clear missed report counter on Control Loop
213             clearFaultAndCounter(controlLoop);
214             return;
215         }
216
217         var completed = true;
218         var minSpNotCompleted = 1000; // min startPhase not completed
219         for (ControlLoopElement element : controlLoop.getElements().values()) {
220             if (!element.getState().equals(element.getOrderedState().asState())) {
221                 completed = false;
222                 ToscaNodeTemplate toscaNodeTemplate = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates()
223                         .get(element.getDefinition().getName());
224                 int startPhase = ParticipantUtils.findStartPhase(toscaNodeTemplate.getProperties());
225                 minSpNotCompleted = Math.min(minSpNotCompleted, startPhase);
226             }
227
228         }
229
230         if (completed) {
231             LOGGER.debug("control loop scan: transition from state {} to {} completed", controlLoop.getState(),
232                     controlLoop.getOrderedState());
233
234             controlLoop.setState(controlLoop.getOrderedState().asState());
235             controlLoopProvider.updateControlLoop(controlLoop);
236
237             // Clear missed report counter on Control Loop
238             clearFaultAndCounter(controlLoop);
239         } else {
240             LOGGER.debug("control loop scan: transition from state {} to {} not completed", controlLoop.getState(),
241                     controlLoop.getOrderedState());
242             if (counterCheck) {
243                 handleCounter(controlLoop, minSpNotCompleted);
244             }
245         }
246     }
247
248     private void clearFaultAndCounter(ControlLoop controlLoop) {
249         controlLoopCounter.clear(controlLoop.getKey().asIdentifier());
250     }
251
252     private void handleCounter(ControlLoop controlLoop, int startPhase) {
253         ToscaConceptIdentifier id = controlLoop.getKey().asIdentifier();
254         if (controlLoopCounter.isFault(id)) {
255             LOGGER.debug("report ControlLoop fault");
256             return;
257         }
258
259         if (controlLoopCounter.getDuration(id) > controlLoopCounter.getMaxWaitMs()) {
260             if (controlLoopCounter.count(id)) {
261                 if (ControlLoopState.UNINITIALISED2PASSIVE.equals(controlLoop.getState())) {
262                     LOGGER.debug("retry message ControlLoopUpdate");
263                     controlLoopUpdatePublisher.send(controlLoop, startPhase);
264                 } else {
265                     LOGGER.debug("retry message ControlLoopStateChange");
266                     controlLoopStateChangePublisher.send(controlLoop);
267                 }
268             } else {
269                 LOGGER.debug("report ControlLoop fault");
270                 controlLoopCounter.setFault(id);
271             }
272         }
273     }
274 }