dfced7cf47a7189ac8cad155190385227f49c209
[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     /**
161      * Handle deploying an automation composition element.
162      *
163      * @param instanceId    the instanceId
164      * @param elementId     the elementId
165      * @param outProperties the outProperties
166      */
167     public void deploy(UUID instanceId, UUID elementId, Map<String, Object> outProperties) {
168         if (isInterrupted(getConfig().getDeployTimerMs(),
169             "Current Thread deploy is Interrupted during execution {}", elementId)) {
170             return;
171         }
172
173         if (getConfig().isDeploySuccess()) {
174             outProperties.put(INTERNAL_STATE, DeployState.DEPLOYED.name());
175             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, outProperties);
176
177             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
178                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Deployed");
179         } else {
180             outProperties.put(INTERNAL_STATE, DeployState.UNDEPLOYED.name());
181             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, outProperties);
182
183             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
184                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Deploy failed!");
185         }
186     }
187
188     /**
189      * Handle undeploying an automation composition element.
190      *
191      * @param instanceId    the instanceId
192      * @param elementId     the elementId
193      * @param outProperties the outProperties
194      */
195     public void undeploy(UUID instanceId, UUID elementId, Map<String, Object> outProperties) {
196         if (isInterrupted(getConfig().getUndeployTimerMs(),
197             "Current Thread undeploy is Interrupted during execution {}", elementId)) {
198             return;
199         }
200
201         if (getConfig().isUndeploySuccess()) {
202             outProperties.put(INTERNAL_STATE, DeployState.UNDEPLOYED.name());
203             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, outProperties);
204
205             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
206                 DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Undeployed");
207         } else {
208             outProperties.put(INTERNAL_STATE, DeployState.DEPLOYED.name());
209             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, outProperties);
210
211             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
212                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Undeploy failed!");
213         }
214     }
215
216     /**
217      * Handle locking an automation composition element.
218      *
219      * @param instanceId the instanceId
220      * @param elementId  the elementId
221      */
222     public void lock(UUID instanceId, UUID elementId) {
223         if (isInterrupted(getConfig().getLockTimerMs(),
224             "Current Thread lock is Interrupted during execution {}", elementId)) {
225             return;
226         }
227
228         if (getConfig().isLockSuccess()) {
229             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
230                 null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");
231         } else {
232             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
233                 null, LockState.UNLOCKED, StateChangeResult.FAILED, "Lock failed!");
234         }
235     }
236
237     /**
238      * Handle unlocking an automation composition element.
239      *
240      * @param instanceId the instanceId
241      * @param elementId  the elementId
242      */
243     public void unlock(UUID instanceId, UUID elementId) {
244         if (isInterrupted(getConfig().getUnlockTimerMs(),
245             "Current Thread unlock is Interrupted during execution {}", elementId)) {
246             return;
247         }
248
249         if (getConfig().isUnlockSuccess()) {
250             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
251                 null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");
252         } else {
253             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
254                 null, LockState.LOCKED, StateChangeResult.FAILED, "Unlock failed!");
255         }
256     }
257
258     /**
259      * Handle deleting an automation composition element.
260      *
261      * @param instanceId the instanceId
262      * @param elementId  the elementId
263      */
264     public void delete(UUID instanceId, UUID elementId) {
265         if (isInterrupted(getConfig().getDeleteTimerMs(),
266             "Current Thread delete is Interrupted during execution {}", elementId)) {
267             return;
268         }
269
270         if (getConfig().isDeleteSuccess()) {
271             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
272                 DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
273         } else {
274             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
275                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Delete failed!");
276         }
277     }
278
279     /**
280      * Handle an update on an automation composition element.
281      *
282      * @param instanceId the instanceId
283      * @param elementId  the elementId
284      */
285     public void update(UUID instanceId, UUID elementId) {
286         if (isInterrupted(getConfig().getUpdateTimerMs(),
287             "Current Thread update is Interrupted during execution {}", elementId)) {
288             return;
289         }
290
291         if (getConfig().isUpdateSuccess()) {
292             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
293                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Updated");
294         } else {
295             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
296                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Update failed!");
297         }
298     }
299
300     /**
301      * Handle a prime on an automation composition definition.
302      *
303      * @param composition the information of the Automation Composition Definition
304      */
305     public void prime(CompositionDto composition) {
306         if (isInterrupted(getConfig().getPrimeTimerMs(),
307             "Current Thread prime is Interrupted during execution {}", composition.compositionId())) {
308             return;
309         }
310
311         if (getConfig().isPrimeSuccess()) {
312             sendOutProperties(composition, AcTypeState.PRIMED.name());
313             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
314                 StateChangeResult.NO_ERROR, "Primed");
315         } else {
316             sendOutProperties(composition, AcTypeState.COMMISSIONED.name());
317             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
318                 StateChangeResult.FAILED, "Prime failed!");
319         }
320     }
321
322     private void sendOutProperties(CompositionDto composition, String data) {
323         for (var elementEntry : composition.outPropertiesMap().entrySet()) {
324             elementEntry.getValue().put(INTERNAL_STATE, data);
325             intermediaryApi.sendAcDefinitionInfo(
326                 composition.compositionId(), elementEntry.getKey(), elementEntry.getValue());
327         }
328     }
329
330     /**
331      * Handle a deprime on an automation composition definition.
332      *
333      * @param composition the information of the Automation Composition Definition
334      */
335     public void deprime(CompositionDto composition) {
336         if (isInterrupted(getConfig().getDeprimeTimerMs(),
337             "Current Thread deprime is Interrupted during execution {}", composition.compositionId())) {
338             return;
339         }
340
341         if (getConfig().isDeprimeSuccess()) {
342             sendOutProperties(composition, AcTypeState.COMMISSIONED.name());
343             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
344                 StateChangeResult.NO_ERROR, "Deprimed");
345         } else {
346             sendOutProperties(composition, AcTypeState.PRIMED.name());
347             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
348                 StateChangeResult.FAILED, "Deprime failed!");
349         }
350     }
351
352     /**
353      * Handle a migration on an automation composition element.
354      *
355      * @param instanceId              the instanceId
356      * @param elementId               the elementId
357      * @param stage                   the stage
358      * @param compositionInProperties in Properties from composition definition element
359      * @param instanceOutProperties   in Properties from instance element
360      */
361     public void migrate(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
362                         Map<String, Object> instanceOutProperties) {
363         if (isInterrupted(getConfig().getMigrateTimerMs(),
364             "Current Thread migrate is Interrupted during execution {}", elementId)) {
365             return;
366         }
367
368         if (config.isMigrateSuccess()) {
369             var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties);
370             var nextStage = 1000;
371             for (var s : stageSet) {
372                 if (s > stage) {
373                     nextStage = Math.min(s, nextStage);
374                 }
375             }
376             instanceOutProperties.putIfAbsent(MIGRATION_PROPERTY, new ArrayList<>());
377             @SuppressWarnings("unchecked")
378             var stageList = (List<Integer>) instanceOutProperties.get(MIGRATION_PROPERTY);
379             stageList.add(stage);
380             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
381             if (nextStage == 1000) {
382                 intermediaryApi.updateAutomationCompositionElementState(
383                     instanceId, elementId,
384                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
385             } else {
386                 intermediaryApi.updateAutomationCompositionElementStage(
387                     instanceId, elementId,
388                     StateChangeResult.NO_ERROR, nextStage, "stage " + stage + " Migrated");
389             }
390         } else {
391             intermediaryApi.updateAutomationCompositionElementState(
392                 instanceId, elementId,
393                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migrate failed!");
394         }
395     }
396
397     /**
398      * Handle a Migrate Precheck on an automation composition element.
399      *
400      * @param instanceId the instanceId
401      * @param elementId  the elementId
402      */
403     public void migratePrecheck(UUID instanceId, UUID elementId) {
404         if (isInterrupted(config.getMigratePrecheckTimerMs(),
405             "Current Thread migrate precheck is Interrupted during execution {}", elementId)) {
406             return;
407         }
408
409         if (config.isMigratePrecheck()) {
410             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
411                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migration precheck completed");
412         } else {
413             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
414                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration precheck failed");
415         }
416     }
417
418     /**
419      * Handle a Prepare on an automation composition element.
420      *
421      * @param instanceId              the instanceId
422      * @param elementId               the elementId
423      * @param stage                   the stage
424      * @param compositionInProperties in Properties from composition definition element
425      * @param instanceOutProperties   in Properties from instance element
426      */
427     public void prepare(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
428                         Map<String, Object> instanceOutProperties) {
429         if (isInterrupted(config.getPrepareTimerMs(),
430             "Current Thread prepare is Interrupted during execution {}", elementId)) {
431             return;
432         }
433
434         if (config.isPrepare()) {
435             var stageSet = ParticipantUtils.findStageSetPrepare(compositionInProperties);
436             var nextStage = 1000;
437             for (var s : stageSet) {
438                 if (s > stage) {
439                     nextStage = Math.min(s, nextStage);
440                 }
441             }
442             instanceOutProperties.putIfAbsent(PREPARE_PROPERTY, new ArrayList<>());
443             @SuppressWarnings("unchecked")
444             var stageList = (List<Integer>) instanceOutProperties.get(PREPARE_PROPERTY);
445             stageList.add(stage);
446             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
447             if (nextStage == 1000) {
448                 intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
449                     DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Prepare completed");
450             } else {
451                 intermediaryApi.updateAutomationCompositionElementStage(
452                     instanceId, elementId,
453                     StateChangeResult.NO_ERROR, nextStage, "stage " + stage + " Prepared");
454             }
455         } else {
456             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
457                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Prepare failed");
458         }
459     }
460
461     /**
462      * Handle a Review on an automation composition element.
463      *
464      * @param instanceId the instanceId
465      * @param elementId  the elementId
466      */
467     public void review(UUID instanceId, UUID elementId) {
468         if (isInterrupted(config.getReviewTimerMs(),
469             "Current Thread review is Interrupted during execution {}", elementId)) {
470             return;
471         }
472
473         if (config.isReview()) {
474             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
475                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Review completed");
476         } else {
477             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
478                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Review failed");
479         }
480     }
481
482     /**
483      * Handle rollback of an automation composition.
484      *
485      * @param instanceId AC instance ID
486      * @param elementId  AC element ID
487      */
488     public void rollback(UUID instanceId, UUID elementId) {
489         if (isInterrupted(getConfig().getRollbackTimerMs(),
490             "Current Thread for rollback was Interrupted during execution {}", instanceId)) {
491             LOGGER.debug("Rollback interrupted");
492             return;
493         }
494
495         if (config.isRollback()) {
496             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId, DeployState.DEPLOYED, null,
497                 StateChangeResult.NO_ERROR, "Migration rollback done");
498         } else {
499             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId, DeployState.DEPLOYED, null,
500                 StateChangeResult.FAILED, "Migration rollback failed");
501         }
502     }
503 }