282d81d1bcb13ddb5b455cda0f6ac80892f5d8b7
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.clamp.acm.participant.sim.main.handler;
22
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.UUID;
27 import java.util.concurrent.locks.LockSupport;
28 import lombok.Getter;
29 import lombok.RequiredArgsConstructor;
30 import lombok.Setter;
31 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
32 import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
33 import org.onap.policy.clamp.acm.participant.sim.model.InternalData;
34 import org.onap.policy.clamp.acm.participant.sim.model.InternalDatas;
35 import org.onap.policy.clamp.acm.participant.sim.model.SimConfig;
36 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
39 import org.onap.policy.clamp.models.acm.concepts.DeployState;
40 import org.onap.policy.clamp.models.acm.concepts.LockState;
41 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
42 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
43 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46 import org.springframework.stereotype.Service;
47
48 /**
49  * This class handles implementation of Simulator Service.
50  */
51 @Service
52 @RequiredArgsConstructor
53 public class SimulatorService {
54
55     private final ParticipantIntermediaryApi intermediaryApi;
56
57     private static final Logger LOGGER = LoggerFactory.getLogger(SimulatorService.class);
58     private static final String INTERNAL_STATE = "InternalState";
59     private static final String MIGRATION_PROPERTY = "stage";
60     private static final String PREPARE_PROPERTY = "prepareStage";
61
62     @Getter
63     @Setter
64     private SimConfig config = new SimConfig();
65
66     /**
67      * Get AutomationComposition.
68      *
69      * @return the AutomationCompositions
70      */
71     public AutomationCompositions getAutomationCompositions() {
72         var result = new AutomationCompositions();
73         result.setAutomationCompositionList(new ArrayList<>(intermediaryApi.getAutomationCompositions().values()));
74         return result;
75     }
76
77     public AutomationComposition getAutomationComposition(UUID instanceId) {
78         return intermediaryApi.getAutomationComposition(instanceId);
79     }
80
81     /**
82      * Set OutProperties.
83      *
84      * @param instanceId       the automationComposition Id
85      * @param elementId        the automationComposition Element Id
86      * @param useState         the useState
87      * @param operationalState the operationalState
88      * @param outProperties    the outProperties
89      */
90     public void setOutProperties(UUID instanceId, UUID elementId, String useState, String operationalState,
91                                  Map<String, Object> outProperties) {
92         intermediaryApi.sendAcElementInfo(instanceId, elementId, useState, operationalState,
93             outProperties);
94     }
95
96     /**
97      * Get Instance Data List.
98      *
99      * @return the InternalDatas
100      */
101     public InternalDatas getDataList() {
102         var result = new InternalDatas();
103         var map = intermediaryApi.getAutomationCompositions();
104         for (var instance : map.values()) {
105             for (var element : instance.getElements().values()) {
106                 var data = new InternalData();
107                 data.setCompositionId(instance.getCompositionId());
108                 data.setAutomationCompositionId(instance.getInstanceId());
109                 data.setAutomationCompositionElementId(element.getId());
110                 data.setIntProperties(element.getProperties());
111                 data.setOperationalState(element.getOperationalState());
112                 data.setUseState(element.getUseState());
113                 data.setOutProperties(element.getOutProperties());
114                 result.getList().add(data);
115             }
116         }
117         return result;
118     }
119
120     /**
121      * Get Composition Data List.
122      *
123      * @return the InternalDatas
124      */
125     public InternalDatas getCompositionDataList() {
126         var acElementsDefinitions = intermediaryApi.getAcElementsDefinitions();
127         var internalDatas = new InternalDatas();
128         for (var entry : acElementsDefinitions.entrySet()) {
129             for (var acElementsDefinition : entry.getValue().values()) {
130                 var internalData = new InternalData();
131                 internalData.setCompositionId(entry.getKey());
132                 internalData.setCompositionDefinitionElementId(acElementsDefinition.getAcElementDefinitionId());
133                 internalData.setIntProperties(
134                     acElementsDefinition.getAutomationCompositionElementToscaNodeTemplate().getProperties());
135                 internalData.setOutProperties(acElementsDefinition.getOutProperties());
136                 internalDatas.getList().add(internalData);
137             }
138         }
139         return internalDatas;
140     }
141
142     public void setCompositionOutProperties(UUID compositionId, ToscaConceptIdentifier compositionDefinitionElementId,
143                                             Map<String, Object> outProperties) {
144         intermediaryApi.sendAcDefinitionInfo(compositionId, compositionDefinitionElementId, outProperties);
145
146     }
147
148     protected boolean isInterrupted(int timeMs, String msg, UUID elementId) {
149         long endTime = System.nanoTime() + (timeMs * 1_000_000L);
150         while (System.nanoTime() < endTime) {
151             if (Thread.interrupted()) {
152                 LOGGER.debug(msg, elementId);
153                 return true;
154             }
155             LockSupport.parkNanos(10_000_000L);
156         }
157         return false;
158     }
159
160     private void sendAcInternalState(UUID instanceId, UUID elementId, Map<String, Object> outProperties,
161             DeployState deployState) {
162         outProperties.put(INTERNAL_STATE, deployState.name());
163         intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, outProperties);
164     }
165
166     /**
167      * Handle deploying an automation composition element.
168      *
169      * @param instanceId    the instanceId
170      * @param elementId     the elementId
171      * @param outProperties the outProperties
172      */
173     public void deploy(UUID instanceId, UUID elementId, Map<String, Object> outProperties) {
174         sendAcInternalState(instanceId, elementId, outProperties, DeployState.DEPLOYING);
175
176         if (isInterrupted(getConfig().getDeployTimerMs(),
177             "Current Thread deploy is Interrupted during execution {}", elementId)) {
178             return;
179         }
180
181         if (getConfig().isDeploySuccess()) {
182             sendAcInternalState(instanceId, elementId, outProperties, DeployState.DEPLOYED);
183
184             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
185                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Deployed");
186         } else {
187             sendAcInternalState(instanceId, elementId, outProperties, DeployState.UNDEPLOYED);
188
189             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
190                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Deploy failed!");
191         }
192     }
193
194     /**
195      * Handle undeploying an automation composition element.
196      *
197      * @param instanceId    the instanceId
198      * @param elementId     the elementId
199      * @param outProperties the outProperties
200      */
201     public void undeploy(UUID instanceId, UUID elementId, Map<String, Object> outProperties) {
202         sendAcInternalState(instanceId, elementId, outProperties, DeployState.UNDEPLOYING);
203
204         if (isInterrupted(getConfig().getUndeployTimerMs(),
205             "Current Thread undeploy is Interrupted during execution {}", elementId)) {
206             return;
207         }
208
209         if (getConfig().isUndeploySuccess()) {
210             sendAcInternalState(instanceId, elementId, outProperties, DeployState.UNDEPLOYED);
211
212             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
213                 DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Undeployed");
214         } else {
215             sendAcInternalState(instanceId, elementId, outProperties, DeployState.DEPLOYED);
216
217             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
218                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Undeploy failed!");
219         }
220     }
221
222     /**
223      * Handle locking an automation composition element.
224      *
225      * @param instanceId the instanceId
226      * @param elementId  the elementId
227      */
228     public void lock(UUID instanceId, UUID elementId) {
229         if (isInterrupted(getConfig().getLockTimerMs(),
230             "Current Thread lock is Interrupted during execution {}", elementId)) {
231             return;
232         }
233
234         if (getConfig().isLockSuccess()) {
235             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
236                 null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");
237         } else {
238             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
239                 null, LockState.UNLOCKED, StateChangeResult.FAILED, "Lock failed!");
240         }
241     }
242
243     /**
244      * Handle unlocking an automation composition element.
245      *
246      * @param instanceId the instanceId
247      * @param elementId  the elementId
248      */
249     public void unlock(UUID instanceId, UUID elementId) {
250         if (isInterrupted(getConfig().getUnlockTimerMs(),
251             "Current Thread unlock is Interrupted during execution {}", elementId)) {
252             return;
253         }
254
255         if (getConfig().isUnlockSuccess()) {
256             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
257                 null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");
258         } else {
259             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
260                 null, LockState.LOCKED, StateChangeResult.FAILED, "Unlock failed!");
261         }
262     }
263
264     /**
265      * Handle deleting an automation composition element.
266      *
267      * @param instanceId the instanceId
268      * @param elementId  the elementId
269      */
270     public void delete(UUID instanceId, UUID elementId) {
271         if (isInterrupted(getConfig().getDeleteTimerMs(),
272             "Current Thread delete is Interrupted during execution {}", elementId)) {
273             return;
274         }
275
276         if (getConfig().isDeleteSuccess()) {
277             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
278                 DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
279         } else {
280             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
281                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Delete failed!");
282         }
283     }
284
285     /**
286      * Handle an update on an automation composition element.
287      *
288      * @param instanceId the instanceId
289      * @param elementId  the elementId
290      */
291     public void update(UUID instanceId, UUID elementId) {
292         if (isInterrupted(getConfig().getUpdateTimerMs(),
293             "Current Thread update is Interrupted during execution {}", elementId)) {
294             return;
295         }
296
297         if (getConfig().isUpdateSuccess()) {
298             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
299                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Updated");
300         } else {
301             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
302                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Update failed!");
303         }
304     }
305
306     /**
307      * Handle a prime on an automation composition definition.
308      *
309      * @param composition the information of the Automation Composition Definition
310      */
311     public void prime(CompositionDto composition) {
312         if (isInterrupted(getConfig().getPrimeTimerMs(),
313             "Current Thread prime is Interrupted during execution {}", composition.compositionId())) {
314             return;
315         }
316
317         if (getConfig().isPrimeSuccess()) {
318             sendOutProperties(composition, AcTypeState.PRIMED.name());
319             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
320                 StateChangeResult.NO_ERROR, "Primed");
321         } else {
322             sendOutProperties(composition, AcTypeState.COMMISSIONED.name());
323             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
324                 StateChangeResult.FAILED, "Prime failed!");
325         }
326     }
327
328     private void sendOutProperties(CompositionDto composition, String data) {
329         for (var elementEntry : composition.outPropertiesMap().entrySet()) {
330             elementEntry.getValue().put(INTERNAL_STATE, data);
331             intermediaryApi.sendAcDefinitionInfo(
332                 composition.compositionId(), elementEntry.getKey(), elementEntry.getValue());
333         }
334     }
335
336     /**
337      * Handle a deprime on an automation composition definition.
338      *
339      * @param composition the information of the Automation Composition Definition
340      */
341     public void deprime(CompositionDto composition) {
342         if (isInterrupted(getConfig().getDeprimeTimerMs(),
343             "Current Thread deprime is Interrupted during execution {}", composition.compositionId())) {
344             return;
345         }
346
347         if (getConfig().isDeprimeSuccess()) {
348             sendOutProperties(composition, AcTypeState.COMMISSIONED.name());
349             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
350                 StateChangeResult.NO_ERROR, "Deprimed");
351         } else {
352             sendOutProperties(composition, AcTypeState.PRIMED.name());
353             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
354                 StateChangeResult.FAILED, "Deprime failed!");
355         }
356     }
357
358     /**
359      * Handle a migration on an automation composition element.
360      *
361      * @param instanceId              the instanceId
362      * @param elementId               the elementId
363      * @param stage                   the stage
364      * @param compositionInProperties in Properties from composition definition element
365      * @param instanceOutProperties   in Properties from instance element
366      */
367     public void migrate(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
368                         Map<String, Object> instanceOutProperties) {
369         if (isInterrupted(getConfig().getMigrateTimerMs(),
370             "Current Thread migrate is Interrupted during execution {}", elementId)) {
371             return;
372         }
373
374         if (config.isMigrateSuccess()) {
375             var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties);
376             var nextStage = 1000;
377             for (var s : stageSet) {
378                 if (s > stage) {
379                     nextStage = Math.min(s, nextStage);
380                 }
381             }
382             instanceOutProperties.putIfAbsent(MIGRATION_PROPERTY, new ArrayList<>());
383             @SuppressWarnings("unchecked")
384             var stageList = (List<Integer>) instanceOutProperties.get(MIGRATION_PROPERTY);
385             stageList.add(stage);
386             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
387             if (nextStage == 1000) {
388                 intermediaryApi.updateAutomationCompositionElementState(
389                     instanceId, elementId,
390                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
391             } else {
392                 intermediaryApi.updateAutomationCompositionElementStage(
393                     instanceId, elementId,
394                     StateChangeResult.NO_ERROR, nextStage, "stage " + stage + " Migrated");
395             }
396         } else {
397             intermediaryApi.updateAutomationCompositionElementState(
398                 instanceId, elementId,
399                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migrate failed!");
400         }
401     }
402
403     /**
404      * Handle a Migrate Precheck on an automation composition element.
405      *
406      * @param instanceId the instanceId
407      * @param elementId  the elementId
408      */
409     public void migratePrecheck(UUID instanceId, UUID elementId) {
410         if (isInterrupted(config.getMigratePrecheckTimerMs(),
411             "Current Thread migrate precheck is Interrupted during execution {}", elementId)) {
412             return;
413         }
414
415         if (config.isMigratePrecheck()) {
416             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
417                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migration precheck completed");
418         } else {
419             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
420                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration precheck failed");
421         }
422     }
423
424     /**
425      * Handle a Prepare on an automation composition element.
426      *
427      * @param instanceId              the instanceId
428      * @param elementId               the elementId
429      * @param stage                   the stage
430      * @param compositionInProperties in Properties from composition definition element
431      * @param instanceOutProperties   in Properties from instance element
432      */
433     public void prepare(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
434                         Map<String, Object> instanceOutProperties) {
435         if (isInterrupted(config.getPrepareTimerMs(),
436             "Current Thread prepare is Interrupted during execution {}", elementId)) {
437             return;
438         }
439
440         if (config.isPrepare()) {
441             var stageSet = ParticipantUtils.findStageSetPrepare(compositionInProperties);
442             var nextStage = 1000;
443             for (var s : stageSet) {
444                 if (s > stage) {
445                     nextStage = Math.min(s, nextStage);
446                 }
447             }
448             instanceOutProperties.putIfAbsent(PREPARE_PROPERTY, new ArrayList<>());
449             @SuppressWarnings("unchecked")
450             var stageList = (List<Integer>) instanceOutProperties.get(PREPARE_PROPERTY);
451             stageList.add(stage);
452             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
453             if (nextStage == 1000) {
454                 intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
455                     DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Prepare completed");
456             } else {
457                 intermediaryApi.updateAutomationCompositionElementStage(
458                     instanceId, elementId,
459                     StateChangeResult.NO_ERROR, nextStage, "stage " + stage + " Prepared");
460             }
461         } else {
462             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
463                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Prepare failed");
464         }
465     }
466
467     /**
468      * Handle a Review on an automation composition element.
469      *
470      * @param instanceId the instanceId
471      * @param elementId  the elementId
472      */
473     public void review(UUID instanceId, UUID elementId) {
474         if (isInterrupted(config.getReviewTimerMs(),
475             "Current Thread review is Interrupted during execution {}", elementId)) {
476             return;
477         }
478
479         if (config.isReview()) {
480             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
481                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Review completed");
482         } else {
483             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
484                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Review failed");
485         }
486     }
487
488     /**
489      * Handle rollback of an automation composition.
490      *
491      * @param instanceId AC instance ID
492      * @param elementId  AC element ID
493      */
494     public void rollback(UUID instanceId, UUID elementId) {
495         if (isInterrupted(getConfig().getRollbackTimerMs(),
496             "Current Thread for rollback was Interrupted during execution {}", instanceId)) {
497             LOGGER.debug("Rollback interrupted");
498             return;
499         }
500
501         if (config.isRollback()) {
502             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId, DeployState.DEPLOYED, null,
503                 StateChangeResult.NO_ERROR, "Migration rollback done");
504         } else {
505             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId, DeployState.DEPLOYED, null,
506                 StateChangeResult.FAILED, "Migration rollback failed");
507         }
508     }
509 }