9e2484c7d84a265726c2fce3ee51d25df50004ea
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021-2023 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.UUID;
31 import lombok.Getter;
32 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
33 import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters;
34 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementInfo;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionInfo;
38 import org.onap.policy.clamp.models.acm.concepts.ParticipantDefinition;
39 import org.onap.policy.clamp.models.acm.concepts.ParticipantState;
40 import org.onap.policy.clamp.models.acm.concepts.ParticipantSupportedElementType;
41 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionDeploy;
42 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange;
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.ParticipantPrime;
48 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantPrimeAck;
49 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantRegister;
50 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantRegisterAck;
51 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantStatus;
52 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantStatusReq;
53 import org.onap.policy.models.base.PfModelException;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import org.springframework.stereotype.Component;
57
58 /**
59  * This class is responsible for managing the state of a participant.
60  */
61 @Component
62 public class ParticipantHandler {
63     private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantHandler.class);
64
65     @Getter
66     private final UUID participantId;
67
68     private final AutomationCompositionHandler automationCompositionHandler;
69     private final ParticipantMessagePublisher publisher;
70
71     private final Map<UUID, List<AutomationCompositionElementDefinition>> acElementDefsMap = new HashMap<>();
72
73     private final List<ParticipantSupportedElementType> supportedAcElementTypes;
74
75     /**
76      * Constructor, set the participant ID and sender.
77      *
78      * @param parameters the parameters of the participant
79      * @param publisher the publisher for sending responses to messages
80      */
81     public ParticipantHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher,
82             AutomationCompositionHandler automationCompositionHandler) {
83         this.participantId = parameters.getIntermediaryParameters().getParticipantId();
84         this.publisher = publisher;
85         this.automationCompositionHandler = automationCompositionHandler;
86         this.supportedAcElementTypes = parameters.getIntermediaryParameters().getParticipantSupportedElementTypes();
87     }
88
89     /**
90      * Method which handles a participant health check event from clamp.
91      *
92      * @param participantStatusReqMsg participant participantStatusReq message
93      */
94     @Timed(value = "listener.participant_status_req", description = "PARTICIPANT_STATUS_REQ messages received")
95     public void handleParticipantStatusReq(final ParticipantStatusReq participantStatusReqMsg) {
96         var participantStatus = makeHeartbeat(true);
97         publisher.sendParticipantStatus(participantStatus);
98     }
99
100     /**
101      * Handle a automation composition update message.
102      *
103      * @param updateMsg the update message
104      */
105     @Timed(
106             value = "listener.automation_composition_update",
107             description = "AUTOMATION_COMPOSITION_UPDATE messages received")
108     public void handleAutomationCompositionDeploy(AutomationCompositionDeploy updateMsg) {
109         automationCompositionHandler.handleAutomationCompositionDeploy(updateMsg,
110                 acElementDefsMap.get(updateMsg.getCompositionId()));
111     }
112
113     /**
114      * Handle a automation composition state change message.
115      *
116      * @param stateChangeMsg the state change message
117      */
118     @Timed(
119             value = "listener.automation_composition_state_change",
120             description = "AUTOMATION_COMPOSITION_STATE_CHANGE messages received")
121     public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
122         automationCompositionHandler.handleAutomationCompositionStateChange(stateChangeMsg,
123                 acElementDefsMap.get(stateChangeMsg.getCompositionId()));
124     }
125
126     /**
127      * Check if a participant message applies to this participant handler.
128      *
129      * @param participantMsg the message to check
130      * @return true if it applies, false otherwise
131      */
132     public boolean appliesTo(ParticipantMessage participantMsg) {
133         return participantMsg.appliesTo(participantId);
134     }
135
136     /**
137      * Check if a participant message applies to this participant handler.
138      *
139      * @param participantMsg the message to check
140      * @return true if it applies, false otherwise
141      */
142     public boolean appliesTo(ParticipantAckMessage participantMsg) {
143         return participantMsg.appliesTo(participantId);
144     }
145
146     /**
147      * Method to send ParticipantRegister message to automation composition runtime.
148      */
149     public void sendParticipantRegister() {
150         var participantRegister = new ParticipantRegister();
151         participantRegister.setParticipantId(participantId);
152         participantRegister.setParticipantSupportedElementType(supportedAcElementTypes);
153
154         publisher.sendParticipantRegister(participantRegister);
155     }
156
157     /**
158      * Handle a participantRegister Ack message.
159      *
160      * @param participantRegisterAckMsg the participantRegisterAck message
161      */
162     @Timed(value = "listener.participant_register_ack", description = "PARTICIPANT_REGISTER_ACK messages received")
163     public void handleParticipantRegisterAck(ParticipantRegisterAck participantRegisterAckMsg) {
164         LOGGER.debug("ParticipantRegisterAck message received as responseTo {}",
165                 participantRegisterAckMsg.getResponseTo());
166         publisher.sendParticipantStatus(makeHeartbeat(false));
167     }
168
169     /**
170      * Method to send ParticipantDeregister message to automation composition runtime.
171      */
172     public void sendParticipantDeregister() {
173         var participantDeregister = new ParticipantDeregister();
174         participantDeregister.setParticipantId(participantId);
175         publisher.sendParticipantDeregister(participantDeregister);
176         undeployInstancesOnParticipant();
177     }
178
179     private void undeployInstancesOnParticipant() {
180         automationCompositionHandler.getAutomationCompositionMap().values().forEach(ac ->
181             undeployInstanceOnParticipant(ac)
182         );
183     }
184
185     private void undeployInstanceOnParticipant(AutomationComposition automationComposition) {
186         automationComposition.getElements().values().forEach(element -> {
187             if (element.getParticipantId().equals(participantId)) {
188                 undeployInstanceElementsOnParticipant(automationComposition.getInstanceId(), element.getId());
189             }
190         });
191     }
192
193     private void undeployInstanceElementsOnParticipant(UUID instanceId, UUID elementId) {
194         var acElementListeners = automationCompositionHandler.getListeners();
195         for (var acElementListener : acElementListeners) {
196             try {
197                 acElementListener.undeploy(instanceId, elementId);
198             } catch (PfModelException e) {
199                 LOGGER.debug("Automation composition element update failed {}", instanceId);
200             }
201         }
202     }
203
204     /**
205      * Handle a participantDeregister Ack message.
206      *
207      * @param participantDeregisterAckMsg the participantDeregisterAck message
208      */
209     @Timed(value = "listener.participant_deregister_ack", description = "PARTICIPANT_DEREGISTER_ACK messages received")
210     public void handleParticipantDeregisterAck(ParticipantDeregisterAck participantDeregisterAckMsg) {
211         LOGGER.debug("ParticipantDeregisterAck message received as responseTo {}",
212                 participantDeregisterAckMsg.getResponseTo());
213     }
214
215     /**
216      * Handle a ParticipantPrime message.
217      *
218      * @param participantPrimeMsg the ParticipantPrime message
219      */
220     @Timed(value = "listener.participant_prime", description = "PARTICIPANT_PRIME messages received")
221     public void handleParticipantPrime(ParticipantPrime participantPrimeMsg) {
222         LOGGER.debug("ParticipantPrime message received for participantId {}",
223                 participantPrimeMsg.getParticipantId());
224
225         acElementDefsMap.putIfAbsent(participantPrimeMsg.getCompositionId(), new ArrayList<>());
226         if (!participantPrimeMsg.getParticipantDefinitionUpdates().isEmpty()) {
227             // This message is to commission the automation composition
228             for (var participantDefinition : participantPrimeMsg.getParticipantDefinitionUpdates()) {
229                 if (participantDefinition.getParticipantId().equals(participantId)) {
230                     acElementDefsMap.get(participantPrimeMsg.getCompositionId())
231                             .addAll(participantDefinition.getAutomationCompositionElementDefinitionList());
232                     break;
233                 }
234             }
235         } else {
236             // This message is to decommission the automation composition
237             acElementDefsMap.get(participantPrimeMsg.getCompositionId()).clear();
238         }
239         sendParticipantPrimeAck(participantPrimeMsg.getMessageId(), participantPrimeMsg.getCompositionId());
240     }
241
242     /**
243      * Method to send ParticipantPrimeAck message to automation composition runtime.
244      */
245     public void sendParticipantPrimeAck(UUID messageId, UUID compositionId) {
246         var participantPrimeAck = new ParticipantPrimeAck();
247         participantPrimeAck.setResponseTo(messageId);
248         participantPrimeAck.setCompositionId(compositionId);
249         participantPrimeAck.setMessage("Participant Prime Ack message");
250         participantPrimeAck.setResult(true);
251         participantPrimeAck.setParticipantId(participantId);
252         participantPrimeAck.setState(ParticipantState.ON_LINE);
253         publisher.sendParticipantPrimeAck(participantPrimeAck);
254     }
255
256     /**
257      * Dispatch a heartbeat for this participant.
258      */
259     public void sendHeartbeat() {
260         publisher.sendHeartbeat(makeHeartbeat(false));
261     }
262
263     /**
264      * Method to send heartbeat to automation composition runtime.
265      */
266     public ParticipantStatus makeHeartbeat(boolean responseToParticipantStatusReq) {
267         var heartbeat = new ParticipantStatus();
268         heartbeat.setParticipantId(participantId);
269         heartbeat.setState(ParticipantState.ON_LINE);
270         heartbeat.setAutomationCompositionInfoList(getAutomationCompositionInfoList());
271         heartbeat.setParticipantSupportedElementType(new ArrayList<>(this.supportedAcElementTypes));
272
273         if (responseToParticipantStatusReq) {
274             List<ParticipantDefinition> participantDefinitionList = new ArrayList<>(acElementDefsMap.size());
275             for (var acElementDefsOnThisParticipant : acElementDefsMap.values()) {
276                 var participantDefinition = new ParticipantDefinition();
277                 participantDefinition.setParticipantId(participantId);
278                 participantDefinition.setAutomationCompositionElementDefinitionList(acElementDefsOnThisParticipant);
279                 participantDefinitionList.add(participantDefinition);
280             }
281             heartbeat.setParticipantDefinitionUpdates(participantDefinitionList);
282         }
283
284         return heartbeat;
285     }
286
287     private List<AutomationCompositionInfo> getAutomationCompositionInfoList() {
288         List<AutomationCompositionInfo> automationCompositionInfoList = new ArrayList<>();
289         for (var entry : automationCompositionHandler.getAutomationCompositionMap().entrySet()) {
290             var acInfo = new AutomationCompositionInfo();
291             acInfo.setAutomationCompositionId(entry.getKey());
292             acInfo.setDeployState(entry.getValue().getDeployState());
293             acInfo.setLockState(entry.getValue().getLockState());
294             for (var element : entry.getValue().getElements().values()) {
295                 var elementInfo = new AutomationCompositionElementInfo();
296                 elementInfo.setAutomationCompositionElementId(element.getId());
297                 elementInfo.setDeployState(element.getDeployState());
298                 elementInfo.setLockState(element.getLockState());
299                 elementInfo.setOperationalState(
300                         automationCompositionHandler.getOperationalState(entry.getKey(), element.getId()));
301                 elementInfo.setUseState(automationCompositionHandler.getUseState(entry.getKey(), element.getId()));
302                 acInfo.getElements().add(elementInfo);
303             }
304             automationCompositionInfoList.add(acInfo);
305         }
306         return automationCompositionInfoList;
307     }
308 }