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
 
   9  *      http://www.apache.org/licenses/LICENSE-2.0
 
  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.
 
  17  * SPDX-License-Identifier: Apache-2.0
 
  18  * ============LICENSE_END=========================================================
 
  21 package org.onap.policy.clamp.acm.participant.sim.main.handler;
 
  23 import java.util.ArrayList;
 
  24 import java.util.List;
 
  26 import java.util.UUID;
 
  27 import java.util.concurrent.locks.LockSupport;
 
  29 import lombok.RequiredArgsConstructor;
 
  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;
 
  49  * This class handles implementation of Simulator Service.
 
  52 @RequiredArgsConstructor
 
  53 public class SimulatorService {
 
  55     private final ParticipantIntermediaryApi intermediaryApi;
 
  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";
 
  66     private SimConfig config = new SimConfig();
 
  69      * Get AutomationComposition.
 
  71      * @return the AutomationCompositions
 
  73     public AutomationCompositions getAutomationCompositions() {
 
  74         var result = new AutomationCompositions();
 
  75         result.setAutomationCompositionList(new ArrayList<>(intermediaryApi.getAutomationCompositions().values()));
 
  79     public AutomationComposition getAutomationComposition(UUID instanceId) {
 
  80         return intermediaryApi.getAutomationComposition(instanceId);
 
  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
 
  92     public void setOutProperties(UUID instanceId, UUID elementId, String useState, String operationalState,
 
  93                                  Map<String, Object> outProperties) {
 
  94         intermediaryApi.sendAcElementInfo(instanceId, elementId, useState, operationalState,
 
  99      * Get Instance Data List.
 
 101      * @return the InternalDatas
 
 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);
 
 123      * Get Composition Data List.
 
 125      * @return the InternalDatas
 
 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);
 
 141         return internalDatas;
 
 144     public void setCompositionOutProperties(UUID compositionId, ToscaConceptIdentifier compositionDefinitionElementId,
 
 145                                             Map<String, Object> outProperties) {
 
 146         intermediaryApi.sendAcDefinitionInfo(compositionId, compositionDefinitionElementId, outProperties);
 
 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);
 
 157             LockSupport.parkNanos(10_000_000L);
 
 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);
 
 169      * Handle deploying an automation composition element.
 
 171      * @param instanceId    the instanceId
 
 172      * @param elementId     the elementId
 
 173      * @param outProperties the outProperties
 
 175     public void deploy(UUID instanceId, UUID elementId, Map<String, Object> outProperties) {
 
 176         sendAcInternalState(instanceId, elementId, outProperties, DeployState.DEPLOYING);
 
 178         if (isInterrupted(getConfig().getDeployTimerMs(),
 
 179             "Current Thread deploy is Interrupted during execution {}", elementId)) {
 
 183         if (getConfig().isDeploySuccess()) {
 
 184             sendAcInternalState(instanceId, elementId, outProperties, DeployState.DEPLOYED);
 
 186             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 187                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Deployed");
 
 189             sendAcInternalState(instanceId, elementId, outProperties, DeployState.UNDEPLOYED);
 
 191             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 192                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Deploy failed!");
 
 197      * Handle undeploying an automation composition element.
 
 199      * @param instanceId    the instanceId
 
 200      * @param elementId     the elementId
 
 201      * @param outProperties the outProperties
 
 203     public void undeploy(UUID instanceId, UUID elementId, Map<String, Object> outProperties) {
 
 204         sendAcInternalState(instanceId, elementId, outProperties, DeployState.UNDEPLOYING);
 
 206         if (isInterrupted(getConfig().getUndeployTimerMs(),
 
 207             "Current Thread undeploy is Interrupted during execution {}", elementId)) {
 
 211         if (getConfig().isUndeploySuccess()) {
 
 212             sendAcInternalState(instanceId, elementId, outProperties, DeployState.UNDEPLOYED);
 
 214             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 215                 DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Undeployed");
 
 217             sendAcInternalState(instanceId, elementId, outProperties, DeployState.DEPLOYED);
 
 219             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 220                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Undeploy failed!");
 
 225      * Handle locking an automation composition element.
 
 227      * @param instanceId the instanceId
 
 228      * @param elementId  the elementId
 
 230     public void lock(UUID instanceId, UUID elementId) {
 
 231         if (isInterrupted(getConfig().getLockTimerMs(),
 
 232             "Current Thread lock is Interrupted during execution {}", elementId)) {
 
 236         if (getConfig().isLockSuccess()) {
 
 237             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 238                 null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");
 
 240             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 241                 null, LockState.UNLOCKED, StateChangeResult.FAILED, "Lock failed!");
 
 246      * Handle unlocking an automation composition element.
 
 248      * @param instanceId the instanceId
 
 249      * @param elementId  the elementId
 
 251     public void unlock(UUID instanceId, UUID elementId) {
 
 252         if (isInterrupted(getConfig().getUnlockTimerMs(),
 
 253             "Current Thread unlock is Interrupted during execution {}", elementId)) {
 
 257         if (getConfig().isUnlockSuccess()) {
 
 258             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 259                 null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");
 
 261             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 262                 null, LockState.LOCKED, StateChangeResult.FAILED, "Unlock failed!");
 
 267      * Handle deleting an automation composition element.
 
 269      * @param instanceId the instanceId
 
 270      * @param elementId  the elementId
 
 272     public void delete(UUID instanceId, UUID elementId) {
 
 273         if (isInterrupted(getConfig().getDeleteTimerMs(),
 
 274             "Current Thread delete is Interrupted during execution {}", elementId)) {
 
 278         if (getConfig().isDeleteSuccess()) {
 
 279             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 280                 DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
 
 282             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 283                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Delete failed!");
 
 288      * Handle an update on an automation composition element.
 
 290      * @param instanceId the instanceId
 
 291      * @param elementId  the elementId
 
 293     public void update(UUID instanceId, UUID elementId) {
 
 294         if (isInterrupted(getConfig().getUpdateTimerMs(),
 
 295             "Current Thread update is Interrupted during execution {}", elementId)) {
 
 299         if (getConfig().isUpdateSuccess()) {
 
 300             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 301                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Updated");
 
 303             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 304                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Update failed!");
 
 309      * Handle a prime on an automation composition definition.
 
 311      * @param composition the information of the Automation Composition Definition
 
 313     public void prime(CompositionDto composition) {
 
 314         if (isInterrupted(getConfig().getPrimeTimerMs(),
 
 315             "Current Thread prime is Interrupted during execution {}", composition.compositionId())) {
 
 319         if (getConfig().isPrimeSuccess()) {
 
 320             sendOutProperties(composition, AcTypeState.PRIMED.name());
 
 321             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
 
 322                 StateChangeResult.NO_ERROR, "Primed");
 
 324             sendOutProperties(composition, AcTypeState.COMMISSIONED.name());
 
 325             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
 
 326                 StateChangeResult.FAILED, "Prime failed!");
 
 330     private void sendOutProperties(CompositionDto composition, String data) {
 
 331         for (var elementEntry : composition.outPropertiesMap().entrySet()) {
 
 332             elementEntry.getValue().put(INTERNAL_STATE, data);
 
 333             intermediaryApi.sendAcDefinitionInfo(
 
 334                 composition.compositionId(), elementEntry.getKey(), elementEntry.getValue());
 
 339      * Handle a deprime on an automation composition definition.
 
 341      * @param composition the information of the Automation Composition Definition
 
 343     public void deprime(CompositionDto composition) {
 
 344         if (isInterrupted(getConfig().getDeprimeTimerMs(),
 
 345             "Current Thread deprime is Interrupted during execution {}", composition.compositionId())) {
 
 349         if (getConfig().isDeprimeSuccess()) {
 
 350             sendOutProperties(composition, AcTypeState.COMMISSIONED.name());
 
 351             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
 
 352                 StateChangeResult.NO_ERROR, "Deprimed");
 
 354             sendOutProperties(composition, AcTypeState.PRIMED.name());
 
 355             intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
 
 356                 StateChangeResult.FAILED, "Deprime failed!");
 
 361      * Handle a migration on an automation composition element.
 
 363      * @param instanceId              the instanceId
 
 364      * @param elementId               the elementId
 
 365      * @param stage                   the stage
 
 366      * @param compositionInProperties in Properties from composition definition element
 
 367      * @param instanceOutProperties   in Properties from instance element
 
 369     public void migrate(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
 
 370                         Map<String, Object> instanceOutProperties) {
 
 371         if (isInterrupted(getConfig().getMigrateTimerMs(),
 
 372             "Current Thread migrate is Interrupted during execution {}", elementId)) {
 
 376         if (config.isMigrateSuccess()) {
 
 377             var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties);
 
 378             var nextStage = 1000;
 
 379             for (var s : stageSet) {
 
 381                     nextStage = Math.min(s, nextStage);
 
 384             instanceOutProperties.putIfAbsent(MIGRATION_PROPERTY, new ArrayList<>());
 
 385             @SuppressWarnings("unchecked")
 
 386             var stageList = (List<Integer>) instanceOutProperties.get(MIGRATION_PROPERTY);
 
 387             stageList.add(stage);
 
 388             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
 
 389             if (nextStage == 1000) {
 
 390                 intermediaryApi.updateAutomationCompositionElementState(
 
 391                     instanceId, elementId,
 
 392                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
 
 394                 intermediaryApi.updateAutomationCompositionElementStage(
 
 395                     instanceId, elementId,
 
 396                     StateChangeResult.NO_ERROR, nextStage, String.format(STAGE_MSG, stage, "Migrated"));
 
 399             intermediaryApi.updateAutomationCompositionElementState(
 
 400                 instanceId, elementId,
 
 401                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migrate failed!");
 
 406      * Handle a Migrate Precheck on an automation composition element.
 
 408      * @param instanceId the instanceId
 
 409      * @param elementId  the elementId
 
 411     public void migratePrecheck(UUID instanceId, UUID elementId) {
 
 412         if (isInterrupted(config.getMigratePrecheckTimerMs(),
 
 413             "Current Thread migrate precheck is Interrupted during execution {}", elementId)) {
 
 417         if (config.isMigratePrecheck()) {
 
 418             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 419                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migration precheck completed");
 
 421             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 422                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration precheck failed");
 
 427      * Handle a Prepare on an automation composition element.
 
 429      * @param instanceId              the instanceId
 
 430      * @param elementId               the elementId
 
 431      * @param stage                   the stage
 
 432      * @param compositionInProperties in Properties from composition definition element
 
 433      * @param instanceOutProperties   in Properties from instance element
 
 435     public void prepare(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
 
 436                         Map<String, Object> instanceOutProperties) {
 
 437         if (isInterrupted(config.getPrepareTimerMs(),
 
 438             "Current Thread prepare is Interrupted during execution {}", elementId)) {
 
 442         if (config.isPrepare()) {
 
 443             var stageSet = ParticipantUtils.findStageSetPrepare(compositionInProperties);
 
 444             var nextStage = 1000;
 
 445             for (var s : stageSet) {
 
 447                     nextStage = Math.min(s, nextStage);
 
 450             instanceOutProperties.putIfAbsent(PREPARE_PROPERTY, new ArrayList<>());
 
 451             @SuppressWarnings("unchecked")
 
 452             var stageList = (List<Integer>) instanceOutProperties.get(PREPARE_PROPERTY);
 
 453             stageList.add(stage);
 
 454             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
 
 455             if (nextStage == 1000) {
 
 456                 intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 457                     DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Prepare completed");
 
 459                 intermediaryApi.updateAutomationCompositionElementStage(
 
 460                     instanceId, elementId,
 
 461                     StateChangeResult.NO_ERROR, nextStage, String.format(STAGE_MSG, stage, "Prepared"));
 
 464             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 465                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Prepare failed");
 
 470      * Handle a Review on an automation composition element.
 
 472      * @param instanceId the instanceId
 
 473      * @param elementId  the elementId
 
 475     public void review(UUID instanceId, UUID elementId) {
 
 476         if (isInterrupted(config.getReviewTimerMs(),
 
 477             "Current Thread review is Interrupted during execution {}", elementId)) {
 
 481         if (config.isReview()) {
 
 482             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 483                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Review completed");
 
 485             intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
 
 486                 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Review failed");
 
 491      * Handle rollback of an automation composition.
 
 493      * @param instanceId              the instanceId
 
 494      * @param elementId               the elementId
 
 495      * @param stage                   the stage
 
 496      * @param compositionInProperties in Properties from composition definition element
 
 497      * @param instanceOutProperties   in Properties from instance element
 
 499     public void rollback(UUID instanceId, UUID elementId, int stage, Map<String, Object> compositionInProperties,
 
 500             Map<String, Object> instanceOutProperties) {
 
 501         if (isInterrupted(getConfig().getRollbackTimerMs(),
 
 502             "Current Thread for rollback was Interrupted during execution {}", instanceId)) {
 
 503             LOGGER.debug("Rollback interrupted");
 
 507         if (config.isRollback()) {
 
 508             var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties);
 
 509             var nextStage = 1000;
 
 510             for (var s : stageSet) {
 
 512                     nextStage = Math.min(s, nextStage);
 
 515             instanceOutProperties.putIfAbsent(ROLLBACK_PROPERTY, new ArrayList<>());
 
 516             @SuppressWarnings("unchecked")
 
 517             var stageList = (List<Integer>) instanceOutProperties.get(ROLLBACK_PROPERTY);
 
 518             stageList.add(stage);
 
 519             intermediaryApi.sendAcElementInfo(instanceId, elementId, null, null, instanceOutProperties);
 
 520             if (nextStage == 1000) {
 
 521                 intermediaryApi.updateAutomationCompositionElementState(
 
 522                         instanceId, elementId,
 
 523                         DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migration rollback done");
 
 525                 intermediaryApi.updateAutomationCompositionElementStage(
 
 526                         instanceId, elementId,
 
 527                         StateChangeResult.NO_ERROR, nextStage, String.format(STAGE_MSG, stage, "Migration rollback"));
 
 530             intermediaryApi.updateAutomationCompositionElementState(
 
 531                     instanceId, elementId,
 
 532                     DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration rollback failed");