d2918684421f3d68008e3806cc0c36b2e7504d4d
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021-2022 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.acm.participant.intermediary.handler;
24
25 import io.micrometer.core.annotation.Timed;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Objects;
31 import java.util.UUID;
32 import lombok.Getter;
33 import lombok.Setter;
34 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
35 import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionInfo;
38 import org.onap.policy.clamp.models.acm.concepts.Participant;
39 import org.onap.policy.clamp.models.acm.concepts.ParticipantDefinition;
40 import org.onap.policy.clamp.models.acm.concepts.ParticipantHealthStatus;
41 import org.onap.policy.clamp.models.acm.concepts.ParticipantState;
42 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange;
43 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionUpdate;
44 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantAckMessage;
45 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantDeregister;
46 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantDeregisterAck;
47 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessage;
48 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantRegister;
49 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantRegisterAck;
50 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantStatus;
51 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantStatusReq;
52 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantUpdate;
53 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantUpdateAck;
54 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
55 import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58 import org.springframework.stereotype.Component;
59
60 /**
61  * This class is responsible for managing the state of a participant.
62  */
63 @Component
64 public class ParticipantHandler {
65     private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantHandler.class);
66
67     @Getter
68     private final ToscaConceptIdentifier participantType;
69
70     @Getter
71     private final ToscaConceptIdentifier participantId;
72
73     private final AutomationCompositionHandler automationCompositionHandler;
74     private final ParticipantMessagePublisher publisher;
75
76     @Setter
77     private ParticipantState state = ParticipantState.UNKNOWN;
78
79     @Setter
80     private ParticipantHealthStatus healthStatus = ParticipantHealthStatus.UNKNOWN;
81
82     private final List<AutomationCompositionElementDefinition> acElementDefsOnThisParticipant = new ArrayList<>();
83
84     /**
85      * Constructor, set the participant ID and sender.
86      *
87      * @param parameters the parameters of the participant
88      * @param publisher the publisher for sending responses to messages
89      */
90     public ParticipantHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher,
91         AutomationCompositionHandler automationCompositionHandler) {
92         this.participantType = parameters.getIntermediaryParameters().getParticipantType();
93         this.participantId = parameters.getIntermediaryParameters().getParticipantId();
94         this.publisher = publisher;
95         this.automationCompositionHandler = automationCompositionHandler;
96     }
97
98     /**
99      * Method which handles a participant health check event from clamp.
100      *
101      * @param participantStatusReqMsg participant participantStatusReq message
102      */
103     @Timed(value = "listener.participant_status_req",
104             description = "PARTICIPANT_STATUS_REQ messages received")
105     public void handleParticipantStatusReq(final ParticipantStatusReq participantStatusReqMsg) {
106         var participantStatus = makeHeartbeat(true);
107         publisher.sendParticipantStatus(participantStatus);
108     }
109
110     /**
111      * Handle a automation composition update message.
112      *
113      * @param updateMsg the update message
114      */
115     @Timed(value = "listener.automation_composition_update",
116             description = "AUTOMATION_COMPOSITION_UPDATE messages received")
117     public void handleAutomationCompositionUpdate(AutomationCompositionUpdate updateMsg) {
118         automationCompositionHandler.handleAutomationCompositionUpdate(updateMsg, acElementDefsOnThisParticipant);
119     }
120
121     /**
122      * Handle a automation composition state change message.
123      *
124      * @param stateChangeMsg the state change message
125      */
126     @Timed(value = "listener.automation_composition_state_change",
127             description = "AUTOMATION_COMPOSITION_STATE_CHANGE messages received")
128     public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
129         automationCompositionHandler.handleAutomationCompositionStateChange(stateChangeMsg,
130             acElementDefsOnThisParticipant);
131     }
132
133     private void handleStateChange(ParticipantState newParticipantState, ParticipantUpdateAck response) {
134         if (state.equals(newParticipantState)) {
135             response.setResult(false);
136             response.setMessage("Participant already in state " + newParticipantState);
137         } else {
138             response.setResult(true);
139             response.setMessage("Participant state changed from " + state + " to " + newParticipantState);
140             state = newParticipantState;
141         }
142     }
143
144     /**
145      * Method to update participant state.
146      *
147      * @param definition participant definition
148      * @param participantState participant state
149      * @return the participant
150      */
151     public Participant updateParticipantState(ToscaConceptIdentifier definition, ParticipantState participantState) {
152         if (!Objects.equals(definition, participantId)) {
153             LOGGER.debug("No participant with this ID {}", definition.getName());
154             return null;
155         }
156
157         var participantUpdateAck = new ParticipantUpdateAck();
158         handleStateChange(participantState, participantUpdateAck);
159         publisher.sendParticipantUpdateAck(participantUpdateAck);
160         return getParticipant(definition.getName(), definition.getVersion());
161     }
162
163     /**
164      * Get participants as a {@link Participant} class.
165      *
166      * @param name the participant name to get
167      * @param version the version of the participant to get
168      * @return the participant
169      */
170     public Participant getParticipant(String name, String version) {
171         if (participantId.getName().equals(name)) {
172             var participant = new Participant();
173             participant.setDefinition(participantId);
174             participant.setParticipantState(state);
175             participant.setHealthStatus(healthStatus);
176             return participant;
177         }
178         return null;
179     }
180
181     /**
182      * Get common properties of a automation composition element.
183      *
184      * @param acElementDef the automation composition element definition
185      * @return the common properties
186      */
187     public Map<String, ToscaProperty> getAcElementDefinitionCommonProperties(ToscaConceptIdentifier acElementDef) {
188         Map<String, ToscaProperty> commonPropertiesMap = new HashMap<>();
189         acElementDefsOnThisParticipant.stream().forEach(definition -> {
190             if (definition.getAcElementDefinitionId().equals(acElementDef)) {
191                 commonPropertiesMap.putAll(definition.getCommonPropertiesMap());
192             }
193         });
194         return commonPropertiesMap;
195     }
196
197     /**
198      * Check if a participant message applies to this participant handler.
199      *
200      * @param participantMsg the message to check
201      * @return true if it applies, false otherwise
202      */
203     public boolean appliesTo(ParticipantMessage participantMsg) {
204         return participantMsg.appliesTo(participantType, participantId);
205     }
206
207     /**
208      * Check if a participant message applies to this participant handler.
209      *
210      * @param participantMsg the message to check
211      * @return true if it applies, false otherwise
212      */
213     public boolean appliesTo(ParticipantAckMessage participantMsg) {
214         return participantMsg.appliesTo(participantType, participantId);
215     }
216
217     /**
218      * Method to send ParticipantRegister message to automation composition runtime.
219      */
220     public void sendParticipantRegister() {
221         var participantRegister = new ParticipantRegister();
222         participantRegister.setParticipantId(participantId);
223         participantRegister.setParticipantType(participantType);
224
225         publisher.sendParticipantRegister(participantRegister);
226     }
227
228     /**
229      * Handle a participantRegister Ack message.
230      *
231      * @param participantRegisterAckMsg the participantRegisterAck message
232      */
233     @Timed(value = "listener.participant_register_ack",
234             description = "PARTICIPANT_REGISTER_ACK messages received")
235     public void handleParticipantRegisterAck(ParticipantRegisterAck participantRegisterAckMsg) {
236         LOGGER.debug("ParticipantRegisterAck message received as responseTo {}",
237             participantRegisterAckMsg.getResponseTo());
238         statusToPassive();
239         publisher.sendParticipantStatus(makeHeartbeat(false));
240     }
241
242     private void statusToPassive() {
243         if (ParticipantHealthStatus.UNKNOWN.equals(this.healthStatus)) {
244             this.healthStatus = ParticipantHealthStatus.HEALTHY;
245         }
246
247         if (ParticipantState.UNKNOWN.equals(this.state) || ParticipantState.TERMINATED.equals(this.state)) {
248             this.state = ParticipantState.PASSIVE;
249         }
250
251     }
252
253     /**
254      * Method to send ParticipantDeregister message to automation composition runtime.
255      */
256     public void sendParticipantDeregister() {
257         var participantDeregister = new ParticipantDeregister();
258         participantDeregister.setParticipantId(participantId);
259         participantDeregister.setParticipantType(participantType);
260
261         publisher.sendParticipantDeregister(participantDeregister);
262     }
263
264     /**
265      * Handle a participantDeregister Ack message.
266      *
267      * @param participantDeregisterAckMsg the participantDeregisterAck message
268      */
269     @Timed(value = "listener.participant_deregister_ack",
270             description = "PARTICIPANT_DEREGISTER_ACK messages received")
271     public void handleParticipantDeregisterAck(ParticipantDeregisterAck participantDeregisterAckMsg) {
272         LOGGER.debug("ParticipantDeregisterAck message received as responseTo {}",
273             participantDeregisterAckMsg.getResponseTo());
274     }
275
276     /**
277      * Handle a ParticipantUpdate message.
278      *
279      * @param participantUpdateMsg the ParticipantUpdate message
280      */
281     @Timed(value = "listener.participant_update",
282             description = "PARTICIPANT_UPDATE messages received")
283     public void handleParticipantUpdate(ParticipantUpdate participantUpdateMsg) {
284         LOGGER.debug("ParticipantUpdate message received for participantId {}",
285             participantUpdateMsg.getParticipantId());
286
287         if (!participantUpdateMsg.getParticipantDefinitionUpdates().isEmpty()) {
288             statusToPassive();
289             // This message is to commission the automation composition
290             for (ParticipantDefinition participantDefinition : participantUpdateMsg.getParticipantDefinitionUpdates()) {
291                 if (participantDefinition.getParticipantType().equals(participantType)) {
292                     acElementDefsOnThisParticipant
293                         .addAll(participantDefinition.getAutomationCompositionElementDefinitionList());
294                     break;
295                 }
296             }
297         } else {
298             // This message is to decommission the automation composition
299             acElementDefsOnThisParticipant.clear();
300             this.state = ParticipantState.TERMINATED;
301         }
302         sendParticipantUpdateAck(participantUpdateMsg.getMessageId());
303     }
304
305     /**
306      * Method to send ParticipantUpdateAck message to automation composition runtime.
307      */
308     public void sendParticipantUpdateAck(UUID messageId) {
309         var participantUpdateAck = new ParticipantUpdateAck();
310         participantUpdateAck.setResponseTo(messageId);
311         participantUpdateAck.setMessage("Participant Update Ack message");
312         participantUpdateAck.setResult(true);
313         participantUpdateAck.setParticipantId(participantId);
314         participantUpdateAck.setParticipantType(participantType);
315         participantUpdateAck.setState(state);
316         publisher.sendParticipantUpdateAck(participantUpdateAck);
317     }
318
319     /**
320      * Dispatch a heartbeat for this participant.
321      */
322     public void sendHeartbeat() {
323         publisher.sendHeartbeat(makeHeartbeat(false));
324     }
325
326     /**
327      * Method to send heartbeat to automation composition runtime.
328      */
329     public ParticipantStatus makeHeartbeat(boolean responseToParticipantStatusReq) {
330         var heartbeat = new ParticipantStatus();
331         heartbeat.setParticipantId(participantId);
332         heartbeat.setParticipantType(participantType);
333         heartbeat.setHealthStatus(healthStatus);
334         heartbeat.setState(state);
335         heartbeat.setAutomationCompositionInfoList(getAutomationCompositionInfoList());
336
337         if (responseToParticipantStatusReq) {
338             ParticipantDefinition participantDefinition = new ParticipantDefinition();
339             participantDefinition.setParticipantId(participantId);
340             participantDefinition.setParticipantType(participantType);
341             participantDefinition.setAutomationCompositionElementDefinitionList(acElementDefsOnThisParticipant);
342             heartbeat.setParticipantDefinitionUpdates(List.of(participantDefinition));
343         }
344
345         return heartbeat;
346     }
347
348     private List<AutomationCompositionInfo> getAutomationCompositionInfoList() {
349         List<AutomationCompositionInfo> automationCompositionInfoList = new ArrayList<>();
350         for (var entry : automationCompositionHandler.getAutomationCompositionMap().entrySet()) {
351             var acInfo = new AutomationCompositionInfo();
352             acInfo.setAutomationCompositionId(entry.getKey());
353             acInfo.setState(entry.getValue().getState());
354             automationCompositionInfoList.add(acInfo);
355         }
356         return automationCompositionInfoList;
357     }
358 }