d4743cf8aa398e68a0d75c733cde35bccffc95f1
[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 ROLLBACK_PROPERTY = "rollbackStage";
61     private static final String PREPARE_PROPERTY = "prepareStage";
62     private static final String STAGE_MSG = "stage %d %s";
63
64     @Getter
65     @Setter
66     private SimConfig config = new SimConfig();
67
68     /**
69      * Get AutomationComposition.
70      *
71      * @return the AutomationCompositions
72      */
73     public AutomationCompositions getAutomationCompositions() {
74         var result = new AutomationCompositions();
75         result.setAutomationCompositionList(new ArrayList<>(intermediaryApi.getAutomationCompositions().values()));
76         return result;
77     }
78
79     public AutomationComposition getAutomationComposition(UUID instanceId) {
80         return intermediaryApi.getAutomationComposition(instanceId);
81     }
82
83     /**
84      * Set OutProperties.
85      *
86      * @param instanceId       the automationComposition Id
87      * @param elementId        the automationComposition Element Id
88      * @param useState         the useState
89      * @param operationalState the operationalState
90      * @param outProperties    the outProperties
91      */
92     public void setOutProperties(UUID instanceId, UUID elementId, String useState, String operationalState,
93                                  Map<String, Object> outProperties) {
94         intermediaryApi.sendAcElementInfo(instanceId, elementId, useState, operationalState,
95             outProperties);
96     }
97
98     /**
99      * Get Instance Data List.
100      *
101      * @return the InternalDatas
102      */
103     public InternalDatas getDataList() {
104         var result = new InternalDatas();
105         var map = intermediaryApi.getAutomationCompositions();
106         for (var instance : map.values()) {
107             for (var element : instance.getElements().values()) {
108                 var data = new InternalData();
109                 data.setCompositionId(instance.getCompositionId());
110                 data.setAutomationCompositionId(instance.getInstanceId());
111                 data.setAutomationCompositionElementId(element.getId());
112                 data.setIntProperties(element.getProperties());
113                 data.setOperationalState(element.getOperationalState());
114                 data.setUseState(element.getUseState());
115                 data.setOutProperties(element.getOutProperties());
116                 result.getList().add(data);
117             }
118         }
119         return result;
120     }
121
122     /**
123      * Get Composition Data List.
124      *
125      * @return the InternalDatas
126      */
127     public InternalDatas getCompositionDataList() {
128         var acElementsDefinitions = intermediaryApi.getAcElementsDefinitions();
129         var internalDatas = new InternalDatas();
130         for (var entry : acElementsDefinitions.entrySet()) {
131             for (var acElementsDefinition : entry.getValue().values()) {
132                 var internalData = new InternalData();
133                 internalData.setCompositionId(entry.getKey());
134                 internalData.setCompositionDefinitionElementId(acElementsDefinition.getAcElementDefinitionId());
135                 internalData.setIntProperties(
136                     acElementsDefinition.getAutomationCompositionElementToscaNodeTemplate().getProperties());
137                 internalData.setOutProperties(acElementsDefinition.getOutProperties());
138                 internalDatas.getList().add(internalData);
139             }
140         }
141         return internalDatas;
142     }
143
144     public void setCompositionOutProperties(UUID compositionId, ToscaConceptIdentifier compositionDefinitionElementId,
145                                             Map<String, Object> outProperties) {
146         intermediaryApi.sendAcDefinitionInfo(compositionId, compositionDefinitionElementId, outProperties);
147
148     }
149
150     protected boolean isInterrupted(int timeMs, String msg, UUID elementId) {
151         long endTime = System.nanoTime() + (timeMs * 1_000_000L);
152         while (System.nanoTime() < endTime) {
153             if (Thread.interrupted()) {
154                 LOGGER.debug(msg, elementId);
155                 return true;
156             }
157             LockSupport.parkNanos(10_000_000L);
158         }
159         return false;
160     }
161
162     private void sendAcInternalState(UUID instanceId, UUID elementId, Map<String, Object> outProperties,
163             DeployState deployState) {
164         outProperties.put(INTERNAL_STATE, deployState.name());
165         intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, outProperties);
166     }
167
168     /**
169      * Handle deploying an automation composition element.
170      *
171      * @param instanceId    the instanceId
172      * @param elementId     the elementId
173      * @param outProperties the outProperties
174      */
175     public void deploy(UUID instanceId, UUID elementId, Map<String, Object> outProperties) {
176         sendAcInternalState(instanceId, elementId, outProperties, DeployState.DEPLOYING);
177
178         if (isInterrupted(getConfig().getDeployTimerMs(),
179             "Current Thread deploy is Interrupted during execution {}", elementId)) {
180             return;
181         }
182
183         if (getConfig().isDeploySuccess()) {
184             sendAcInternalState(instanceId, elementId, outProperties, DeployState.DEPLOYED);
185
186             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
187                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Deployed");
188         } else {
189             sendAcInternalState(instanceId, elementId, outProperties, DeployState.UNDEPLOYED);
190
191             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
192                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Deploy failed!");
193         }
194     }
195
196     /**
197      * Handle undeploying an automation composition element.
198      *
199      * @param instanceId    the instanceId
200      * @param elementId     the elementId
201      * @param outProperties the outProperties
202      */
203     public void undeploy(UUID instanceId, UUID elementId, Map<String, Object> outProperties) {
204         sendAcInternalState(instanceId, elementId, outProperties, DeployState.UNDEPLOYING);
205
206         if (isInterrupted(getConfig().getUndeployTimerMs(),
207             "Current Thread undeploy is Interrupted during execution {}", elementId)) {
208             return;
209         }
210
211         if (getConfig().isUndeploySuccess()) {
212             sendAcInternalState(instanceId, elementId, outProperties, DeployState.UNDEPLOYED);
213
214             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
215                 DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Undeployed");
216         } else {
217             sendAcInternalState(instanceId, elementId, outProperties, DeployState.DEPLOYED);
218
219             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
220                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Undeploy failed!");
221         }
222     }
223
224     /**
225      * Handle locking an automation composition element.
226      *
227      * @param instanceId the instanceId
228      * @param elementId  the elementId
229      */
230     public void lock(UUID instanceId, UUID elementId) {
231         if (isInterrupted(getConfig().getLockTimerMs(),
232             "Current Thread lock is Interrupted during execution {}", elementId)) {
233             return;
234         }
235
236         if (getConfig().isLockSuccess()) {
237             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
238                 null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");
239         } else {
240             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
241                 null, LockState.UNLOCKED, StateChangeResult.FAILED, "Lock failed!");
242         }
243     }
244
245     /**
246      * Handle unlocking an automation composition element.
247      *
248      * @param instanceId the instanceId
249      * @param elementId  the elementId
250      */
251     public void unlock(UUID instanceId, UUID elementId) {
252         if (isInterrupted(getConfig().getUnlockTimerMs(),
253             "Current Thread unlock is Interrupted during execution {}", elementId)) {
254             return;
255         }
256
257         if (getConfig().isUnlockSuccess()) {
258             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
259                 null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");
260         } else {
261             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
262                 null, LockState.LOCKED, StateChangeResult.FAILED, "Unlock failed!");
263         }
264     }
265
266     /**
267      * Handle deleting an automation composition element.
268      *
269      * @param instanceId the instanceId
270      * @param elementId  the elementId
271      */
272     public void delete(UUID instanceId, UUID elementId) {
273         if (isInterrupted(getConfig().getDeleteTimerMs(),
274             "Current Thread delete is Interrupted during execution {}", elementId)) {
275             return;
276         }
277
278         if (getConfig().isDeleteSuccess()) {
279             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
280                 DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
281         } else {
282             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
283                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Delete failed!");
284         }
285     }
286
287     /**
288      * Handle deleting an automation composition element in migration.
289      *
290      * @param instanceId the instanceId
291      * @param elementId  the elementId
292      */
293     public void deleteInMigration(UUID instanceId, UUID elementId) {
294         if (getConfig().isMigrateSuccess()) {
295             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
296                     DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Migration - Deleted");
297         } else {
298             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
299                     DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Migration - Delete failed!");
300         }
301     }
302
303     /**
304      * Handle deleting an automation composition element in rollback.
305      *
306      * @param instanceId the instanceId
307      * @param elementId  the elementId
308      */
309     public void deleteInRollback(UUID instanceId, UUID elementId) {
310         if (getConfig().isRollback()) {
311             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
312                     DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Rollback - Deleted");
313         } else {
314             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
315                     DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Rollback - Delete failed!");
316         }
317     }
318
319     /**
320      * Handle an update on an automation composition element.
321      *
322      * @param instanceId the instanceId
323      * @param elementId  the elementId
324      */
325     public void update(UUID instanceId, UUID elementId) {
326         if (isInterrupted(getConfig().getUpdateTimerMs(),
327             "Current Thread update is Interrupted during execution {}", elementId)) {
328             return;
329         }
330
331         if (getConfig().isUpdateSuccess()) {
332             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
333                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Updated");
334         } else {
335             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
336                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Update failed!");
337         }
338     }
339
340     /**
341      * Handle a prime on an automation composition definition.
342      *
343      * @param composition the information of the Automation Composition Definition
344      */
345     public void prime(CompositionDto composition) {
346         if (isInterrupted(getConfig().getPrimeTimerMs(),
347             "Current Thread prime is Interrupted during execution {}", composition.compositionId())) {
348             return;
349         }
350
351         if (getConfig().isPrimeSuccess()) {
352             sendOutProperties(composition, AcTypeState.PRIMED.name());
353             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
354                 StateChangeResult.NO_ERROR, "Primed");
355         } else {
356             sendOutProperties(composition, AcTypeState.COMMISSIONED.name());
357             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
358                 StateChangeResult.FAILED, "Prime failed!");
359         }
360     }
361
362     private void sendOutProperties(CompositionDto composition, String data) {
363         for (var elementEntry : composition.outPropertiesMap().entrySet()) {
364             elementEntry.getValue().put(INTERNAL_STATE, data);
365             intermediaryApi.sendAcDefinitionInfo(
366                 composition.compositionId(), elementEntry.getKey(), elementEntry.getValue());
367         }
368     }
369
370     /**
371      * Handle a deprime on an automation composition definition.
372      *
373      * @param composition the information of the Automation Composition Definition
374      */
375     public void deprime(CompositionDto composition) {
376         if (isInterrupted(getConfig().getDeprimeTimerMs(),
377             "Current Thread deprime is Interrupted during execution {}", composition.compositionId())) {
378             return;
379         }
380
381         if (getConfig().isDeprimeSuccess()) {
382             sendOutProperties(composition, AcTypeState.COMMISSIONED.name());
383             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
384                 StateChangeResult.NO_ERROR, "Deprimed");
385         } else {
386             sendOutProperties(composition, AcTypeState.PRIMED.name());
387             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
388                 StateChangeResult.FAILED, "Deprime failed!");
389         }
390     }
391
392     /**
393      * Handle a migration on an automation composition element.
394      *
395      * @param instanceId              the instanceId
396      * @param elementId               the elementId
397      * @param stage                   the stage
398      * @param compositionInProperties in Properties from composition definition element
399      * @param instanceOutProperties   in Properties from instance element
400      */
401     public void migrate(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
402                         Map<String, Object> instanceOutProperties) {
403         if (isInterrupted(getConfig().getMigrateTimerMs(),
404             "Current Thread migrate is Interrupted during execution {}", elementId)) {
405             return;
406         }
407
408         if (config.isMigrateSuccess()) {
409             var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties);
410             var nextStage = 1000;
411             for (var s : stageSet) {
412                 if (s > stage) {
413                     nextStage = Math.min(s, nextStage);
414                 }
415             }
416             instanceOutProperties.putIfAbsent(MIGRATION_PROPERTY, new ArrayList<>());
417             @SuppressWarnings("unchecked")
418             var stageList = (List<Integer>) instanceOutProperties.get(MIGRATION_PROPERTY);
419             stageList.add(stage);
420             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
421             if (nextStage == 1000) {
422                 intermediaryApi.updateAutomationCompositionElementState(
423                     instanceId, elementId,
424                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
425             } else {
426                 intermediaryApi.updateAutomationCompositionElementStage(
427                     instanceId, elementId,
428                     StateChangeResult.NO_ERROR, nextStage, String.format(STAGE_MSG, stage, "Migrated"));
429             }
430         } else {
431             intermediaryApi.updateAutomationCompositionElementState(
432                 instanceId, elementId,
433                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migrate failed!");
434         }
435     }
436
437     /**
438      * Handle a Migrate Precheck on an automation composition element.
439      *
440      * @param instanceId the instanceId
441      * @param elementId  the elementId
442      */
443     public void migratePrecheck(UUID instanceId, UUID elementId) {
444         if (isInterrupted(config.getMigratePrecheckTimerMs(),
445             "Current Thread migrate precheck is Interrupted during execution {}", elementId)) {
446             return;
447         }
448
449         if (config.isMigratePrecheck()) {
450             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
451                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migration precheck completed");
452         } else {
453             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
454                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration precheck failed");
455         }
456     }
457
458     /**
459      * Handle a Prepare on an automation composition element.
460      *
461      * @param instanceId              the instanceId
462      * @param elementId               the elementId
463      * @param stage                   the stage
464      * @param compositionInProperties in Properties from composition definition element
465      * @param instanceOutProperties   in Properties from instance element
466      */
467     public void prepare(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
468                         Map<String, Object> instanceOutProperties) {
469         if (isInterrupted(config.getPrepareTimerMs(),
470             "Current Thread prepare is Interrupted during execution {}", elementId)) {
471             return;
472         }
473
474         if (config.isPrepare()) {
475             var stageSet = ParticipantUtils.findStageSetPrepare(compositionInProperties);
476             var nextStage = 1000;
477             for (var s : stageSet) {
478                 if (s > stage) {
479                     nextStage = Math.min(s, nextStage);
480                 }
481             }
482             instanceOutProperties.putIfAbsent(PREPARE_PROPERTY, new ArrayList<>());
483             @SuppressWarnings("unchecked")
484             var stageList = (List<Integer>) instanceOutProperties.get(PREPARE_PROPERTY);
485             stageList.add(stage);
486             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
487             if (nextStage == 1000) {
488                 intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
489                     DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Prepare completed");
490             } else {
491                 intermediaryApi.updateAutomationCompositionElementStage(
492                     instanceId, elementId,
493                     StateChangeResult.NO_ERROR, nextStage, String.format(STAGE_MSG, stage, "Prepared"));
494             }
495         } else {
496             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
497                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Prepare failed");
498         }
499     }
500
501     /**
502      * Handle a Review on an automation composition element.
503      *
504      * @param instanceId the instanceId
505      * @param elementId  the elementId
506      */
507     public void review(UUID instanceId, UUID elementId) {
508         if (isInterrupted(config.getReviewTimerMs(),
509             "Current Thread review is Interrupted during execution {}", elementId)) {
510             return;
511         }
512
513         if (config.isReview()) {
514             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
515                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Review completed");
516         } else {
517             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
518                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Review failed");
519         }
520     }
521
522     /**
523      * Handle rollback of an automation composition.
524      *
525      * @param instanceId              the instanceId
526      * @param elementId               the elementId
527      * @param stage                   the stage
528      * @param compositionInProperties in Properties from composition definition element
529      * @param instanceOutProperties   in Properties from instance element
530      */
531     public void rollback(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
532             Map<String, Object> instanceOutProperties) {
533         if (isInterrupted(getConfig().getRollbackTimerMs(),
534             "Current Thread for rollback was Interrupted during execution {}", instanceId)) {
535             LOGGER.debug("Rollback interrupted");
536             return;
537         }
538
539         if (config.isRollback()) {
540             var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties);
541             var nextStage = 1000;
542             for (var s : stageSet) {
543                 if (s > stage) {
544                     nextStage = Math.min(s, nextStage);
545                 }
546             }
547             instanceOutProperties.putIfAbsent(ROLLBACK_PROPERTY, new ArrayList<>());
548             @SuppressWarnings("unchecked")
549             var stageList = (List<Integer>) instanceOutProperties.get(ROLLBACK_PROPERTY);
550             stageList.add(stage);
551             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
552             if (nextStage == 1000) {
553                 intermediaryApi.updateAutomationCompositionElementState(
554                         instanceId, elementId,
555                         DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migration rollback done");
556             } else {
557                 intermediaryApi.updateAutomationCompositionElementStage(
558                         instanceId, elementId,
559                         StateChangeResult.NO_ERROR, nextStage, String.format(STAGE_MSG, stage, "Migration rollback"));
560             }
561         } else {
562             intermediaryApi.updateAutomationCompositionElementState(
563                     instanceId, elementId,
564                     DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration rollback failed");
565         }
566     }
567 }