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