69f8febb862400c65cba87004c534738250929bf
[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.participant.intermediary.handler;
24
25 import java.time.Instant;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.UUID;
31 import java.util.stream.Collectors;
32 import lombok.Getter;
33 import lombok.Setter;
34 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatisticsList;
35 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
36 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
37 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementDefinition;
38 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopInfo;
39 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopStatistics;
40 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops;
41 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant;
42 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantDefinition;
43 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantHealthStatus;
44 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState;
45 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatistics;
46 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopStateChange;
47 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopUpdate;
48 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantAckMessage;
49 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregister;
50 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregisterAck;
51 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessage;
52 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegister;
53 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegisterAck;
54 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus;
55 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatusReq;
56 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdate;
57 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdateAck;
58 import org.onap.policy.clamp.controlloop.participant.intermediary.api.ControlLoopElementListener;
59 import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ParticipantMessagePublisher;
60 import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantParameters;
61 import org.onap.policy.models.base.PfModelException;
62 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65 import org.springframework.stereotype.Component;
66
67 /**
68  * This class is responsible for managing the state of a participant.
69  */
70 @Component
71 public class ParticipantHandler {
72     private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantHandler.class);
73
74     @Getter
75     private final ToscaConceptIdentifier participantType;
76
77     @Getter
78     private final ToscaConceptIdentifier participantId;
79
80     private final ControlLoopHandler controlLoopHandler;
81     private final ParticipantStatistics participantStatistics;
82     private final ParticipantMessagePublisher publisher;
83
84     @Setter
85     private ParticipantState state = ParticipantState.UNKNOWN;
86
87     @Setter
88     private ParticipantHealthStatus healthStatus = ParticipantHealthStatus.UNKNOWN;
89
90     private final List<ControlLoopElementDefinition> clElementDefsOnThisParticipant = new ArrayList<>();
91
92     /**
93      * Constructor, set the participant ID and sender.
94      *
95      * @param parameters the parameters of the participant
96      * @param publisher the publisher for sending responses to messages
97      */
98     public ParticipantHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher,
99             ControlLoopHandler controlLoopHandler) {
100         this.participantType = parameters.getIntermediaryParameters().getParticipantType();
101         this.participantId = parameters.getIntermediaryParameters().getParticipantId();
102         this.publisher = publisher;
103         this.controlLoopHandler = controlLoopHandler;
104         this.participantStatistics = new ParticipantStatistics();
105         this.participantStatistics.setParticipantId(participantId);
106         this.participantStatistics.setState(state);
107         this.participantStatistics.setHealthStatus(healthStatus);
108         this.participantStatistics.setTimeStamp(Instant.now());
109     }
110
111     /**
112      * Method which handles a participant health check event from clamp.
113      *
114      * @param participantStatusReqMsg participant participantStatusReq message
115      */
116     public void handleParticipantStatusReq(final ParticipantStatusReq participantStatusReqMsg) {
117         var participantStatus = makeHeartbeat(true);
118         publisher.sendParticipantStatus(participantStatus);
119     }
120
121     /**
122      * Update ControlLoopElement statistics. The control loop elements listening will be
123      * notified to retrieve statistics from respective controlloop elements, and controlloopelements
124      * data on the handler will be updated.
125      *
126      * @param controlLoops the control loops
127      * @param clElementListener control loop element listener
128      */
129     private void updateClElementStatistics(ControlLoops controlLoops, ControlLoopElementListener clElementListener) {
130         for (ControlLoop controlLoop : controlLoops.getControlLoopList()) {
131             for (ControlLoopElement element : controlLoop.getElements().values()) {
132                 try {
133                     clElementListener.handleStatistics(element.getId());
134                 } catch (PfModelException e) {
135                     LOGGER.debug("Getting statistics for Control loop element failed for element ID {}",
136                             element.getId(), e);
137                 }
138             }
139         }
140     }
141
142     /**
143      * Handle a control loop update message.
144      *
145      * @param updateMsg the update message
146      */
147     public void handleControlLoopUpdate(ControlLoopUpdate updateMsg) {
148         controlLoopHandler.handleControlLoopUpdate(updateMsg, clElementDefsOnThisParticipant);
149     }
150
151     /**
152      * Handle a control loop state change message.
153      *
154      * @param stateChangeMsg the state change message
155      */
156     public void handleControlLoopStateChange(ControlLoopStateChange stateChangeMsg) {
157         controlLoopHandler.handleControlLoopStateChange(stateChangeMsg);
158     }
159
160     private void handleStateChange(ParticipantState newParticipantState, ParticipantUpdateAck response) {
161         if (state.equals(newParticipantState)) {
162             response.setResult(false);
163             response.setMessage("Participant already in state " + newParticipantState);
164         } else {
165             response.setResult(true);
166             response.setMessage("Participant state changed from " + state + " to " + newParticipantState);
167             state = newParticipantState;
168         }
169     }
170
171     /**
172      * Method to update participant state.
173      *
174      * @param definition participant definition
175      * @param participantState participant state
176      * @return the participant
177      */
178     public Participant updateParticipantState(ToscaConceptIdentifier definition, ParticipantState participantState) {
179         if (!Objects.equals(definition, participantId)) {
180             LOGGER.debug("No participant with this ID {}", definition.getName());
181             return null;
182         }
183
184         var participantUpdateAck = new ParticipantUpdateAck();
185         handleStateChange(participantState, participantUpdateAck);
186         publisher.sendParticipantUpdateAck(participantUpdateAck);
187         return getParticipant(definition.getName(), definition.getVersion());
188     }
189
190     /**
191      * Get participants as a {@link Participant} class.
192      *
193      * @param name the participant name to get
194      * @param version the version of the participant to get
195      * @return the participant
196      */
197     public Participant getParticipant(String name, String version) {
198         if (participantId.getName().equals(name)) {
199             var participant = new Participant();
200             participant.setDefinition(participantId);
201             participant.setParticipantState(state);
202             participant.setHealthStatus(healthStatus);
203             return participant;
204         }
205         return null;
206     }
207
208     /**
209      * Check if a participant message applies to this participant handler.
210      *
211      * @param participantMsg the message to check
212      * @return true if it applies, false otherwise
213      */
214     public boolean appliesTo(ParticipantMessage participantMsg) {
215         return participantMsg.appliesTo(participantType, participantId);
216     }
217
218     /**
219      * Check if a participant message applies to this participant handler.
220      *
221      * @param participantMsg the message to check
222      * @return true if it applies, false otherwise
223      */
224     public boolean appliesTo(ParticipantAckMessage participantMsg) {
225         return participantMsg.appliesTo(participantType, participantId);
226     }
227
228     /**
229      * Method to send ParticipantRegister message to controlloop runtime.
230      */
231     public void sendParticipantRegister() {
232         var participantRegister = new ParticipantRegister();
233         participantRegister.setParticipantId(participantId);
234         participantRegister.setParticipantType(participantType);
235
236         publisher.sendParticipantRegister(participantRegister);
237     }
238
239     /**
240      * Handle a participantRegister Ack message.
241      *
242      * @param participantRegisterAckMsg the participantRegisterAck message
243      */
244     public void handleParticipantRegisterAck(ParticipantRegisterAck participantRegisterAckMsg) {
245         LOGGER.debug("ParticipantRegisterAck message received as responseTo {}",
246                 participantRegisterAckMsg.getResponseTo());
247         if (ParticipantHealthStatus.UNKNOWN.equals(this.healthStatus)) {
248             this.healthStatus = ParticipantHealthStatus.HEALTHY;
249         }
250
251         if (ParticipantState.UNKNOWN.equals(this.state)) {
252             this.state = ParticipantState.PASSIVE;
253         }
254         publisher.sendParticipantStatus(makeHeartbeat(false));
255     }
256
257     /**
258      * Method to send ParticipantDeregister message to controlloop runtime.
259      */
260     public void sendParticipantDeregister() {
261         var participantDeregister = new ParticipantDeregister();
262         participantDeregister.setParticipantId(participantId);
263         participantDeregister.setParticipantType(participantType);
264
265         publisher.sendParticipantDeregister(participantDeregister);
266     }
267
268     /**
269      * Handle a participantDeregister Ack message.
270      *
271      * @param participantDeregisterAckMsg the participantDeregisterAck message
272      */
273     public void handleParticipantDeregisterAck(ParticipantDeregisterAck participantDeregisterAckMsg) {
274         LOGGER.debug("ParticipantDeregisterAck message received as responseTo {}",
275                 participantDeregisterAckMsg.getResponseTo());
276     }
277
278     /**
279      * Handle a ParticipantUpdate message.
280      *
281      * @param participantUpdateMsg the ParticipantUpdate message
282      */
283     public void handleParticipantUpdate(ParticipantUpdate participantUpdateMsg) {
284         LOGGER.debug("ParticipantUpdate message received for participantId {}",
285                 participantUpdateMsg.getParticipantId());
286
287         if (!participantUpdateMsg.appliesTo(participantType, participantId)) {
288             return;
289         }
290
291         if (!participantUpdateMsg.getParticipantDefinitionUpdates().isEmpty()) {
292             // This message is to commission the controlloop
293             for (ParticipantDefinition participantDefinition : participantUpdateMsg.getParticipantDefinitionUpdates()) {
294                 if (participantDefinition.getParticipantType().equals(participantType)) {
295                     clElementDefsOnThisParticipant.clear();
296                     clElementDefsOnThisParticipant.addAll(participantDefinition.getControlLoopElementDefinitionList());
297                     break;
298                 }
299             }
300         } else {
301             // This message is to decommision the controlloop
302             clElementDefsOnThisParticipant.clear();
303             this.state = ParticipantState.TERMINATED;
304         }
305         sendParticipantUpdateAck(participantUpdateMsg.getMessageId());
306     }
307
308     /**
309      * Method to send ParticipantUpdateAck message to controlloop runtime.
310      */
311     public void sendParticipantUpdateAck(UUID messageId) {
312         var participantUpdateAck = new ParticipantUpdateAck();
313         participantUpdateAck.setResponseTo(messageId);
314         participantUpdateAck.setMessage("Participant Update Ack message");
315         participantUpdateAck.setResult(true);
316         participantUpdateAck.setParticipantId(participantId);
317         participantUpdateAck.setParticipantType(participantType);
318         participantUpdateAck.setState(state);
319         publisher.sendParticipantUpdateAck(participantUpdateAck);
320     }
321
322     /**
323      * Dispatch a heartbeat for this participant.
324      */
325     public void sendHeartbeat() {
326         publisher.sendHeartbeat(makeHeartbeat(false));
327     }
328
329     /**
330      * Method to send heartbeat to controlloop runtime.
331      */
332     public ParticipantStatus makeHeartbeat(boolean responseToParticipantStatusReq) {
333         if (!responseToParticipantStatusReq) {
334             var controlLoops = controlLoopHandler.getControlLoops();
335             for (var clElementListener : controlLoopHandler.getListeners()) {
336                 updateClElementStatistics(controlLoops, clElementListener);
337             }
338         }
339         this.participantStatistics.setState(state);
340         this.participantStatistics.setHealthStatus(healthStatus);
341         this.participantStatistics.setTimeStamp(Instant.now());
342
343         var heartbeat = new ParticipantStatus();
344         heartbeat.setParticipantId(participantId);
345         heartbeat.setParticipantStatistics(participantStatistics);
346         heartbeat.setParticipantType(participantType);
347         heartbeat.setHealthStatus(healthStatus);
348         heartbeat.setState(state);
349         heartbeat.setControlLoopInfoList(getControlLoopInfoList());
350
351         if (responseToParticipantStatusReq) {
352             ParticipantDefinition participantDefinition = new ParticipantDefinition();
353             participantDefinition.setParticipantId(participantId);
354             participantDefinition.setParticipantType(participantType);
355             participantDefinition.setControlLoopElementDefinitionList(clElementDefsOnThisParticipant);
356             heartbeat.setParticipantDefinitionUpdates(List.of(participantDefinition));
357         }
358
359         return heartbeat;
360     }
361
362     private List<ControlLoopInfo> getControlLoopInfoList() {
363         List<ControlLoopInfo> controlLoopInfoList = new ArrayList<>();
364         for (Map.Entry<ToscaConceptIdentifier, ControlLoop> entry : controlLoopHandler.getControlLoopMap().entrySet()) {
365             ControlLoopInfo clInfo = new ControlLoopInfo();
366             clInfo.setControlLoopId(entry.getKey());
367             ControlLoopStatistics clStatitistics = new ControlLoopStatistics();
368             clStatitistics.setControlLoopId(entry.getKey());
369             ClElementStatisticsList clElementStatisticsList = new ClElementStatisticsList();
370             clElementStatisticsList
371                     .setClElementStatistics(entry.getValue().getElements().values()
372                             .stream()
373                             .map(ControlLoopElement::getClElementStatistics)
374                             .filter(Objects::nonNull)
375                             .collect(Collectors.toList()));
376             clStatitistics.setClElementStatisticsList(clElementStatisticsList);
377             clInfo.setControlLoopStatistics(clStatitistics);
378             clInfo.setState(entry.getValue().getState());
379             controlLoopInfoList.add(clInfo);
380         }
381         return controlLoopInfoList;
382     }
383 }