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