af4e844d5d6b9ab479e867a09829d0da752b7a74
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2021-2023 Nordix Foundation.
4  * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.clamp.acm.runtime.instantiation;
23
24 import jakarta.validation.Valid;
25 import jakarta.ws.rs.core.Response;
26 import jakarta.ws.rs.core.Response.Status;
27 import java.util.UUID;
28 import java.util.stream.Collectors;
29 import lombok.AllArgsConstructor;
30 import lombok.NonNull;
31 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
32 import org.onap.policy.clamp.acm.runtime.participants.AcmParticipantProvider;
33 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionAcHandler;
34 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
37 import org.onap.policy.clamp.models.acm.concepts.DeployState;
38 import org.onap.policy.clamp.models.acm.concepts.LockState;
39 import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
40 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
41 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
42 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
43 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
44 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
45 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
46 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
47 import org.onap.policy.common.parameters.BeanValidationResult;
48 import org.onap.policy.common.parameters.ObjectValidationResult;
49 import org.onap.policy.common.parameters.ValidationStatus;
50 import org.onap.policy.models.base.PfModelRuntimeException;
51 import org.springframework.stereotype.Service;
52 import org.springframework.transaction.annotation.Transactional;
53
54 /**
55  * This class is dedicated to the Instantiation of Commissioned automation composition.
56  */
57 @Service
58 @Transactional
59 @AllArgsConstructor
60 public class AutomationCompositionInstantiationProvider {
61     private static final String DO_NOT_MATCH = " do not match with ";
62
63     private final AutomationCompositionProvider automationCompositionProvider;
64     private final AcDefinitionProvider acDefinitionProvider;
65     private final AcInstanceStateResolver acInstanceStateResolver;
66     private final SupervisionAcHandler supervisionAcHandler;
67     private final AcmParticipantProvider acmParticipantProvider;
68     private final AcRuntimeParameterGroup acRuntimeParameterGroup;
69
70     /**
71      * Create automation composition.
72      *
73      * @param compositionId The UUID of the automation composition definition
74      * @param automationComposition the automation composition
75      * @return the result of the instantiation operation
76      */
77     public InstantiationResponse createAutomationComposition(UUID compositionId,
78             AutomationComposition automationComposition) {
79         if (!compositionId.equals(automationComposition.getCompositionId())) {
80             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
81                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
82         }
83         var checkAutomationCompositionOpt =
84                 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
85         if (checkAutomationCompositionOpt.isPresent()) {
86             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
87                     automationComposition.getKey().asIdentifier() + " already defined");
88         }
89
90         var validationResult = validateAutomationComposition(automationComposition);
91         if (!validationResult.isValid()) {
92             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
93         }
94         automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
95
96         var response = new InstantiationResponse();
97         response.setInstanceId(automationComposition.getInstanceId());
98         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
99
100         return response;
101     }
102
103     /**
104      * Update automation composition.
105      *
106      * @param compositionId The UUID of the automation composition definition
107      * @param automationComposition the automation composition
108      * @return the result of the update
109      */
110     public InstantiationResponse updateAutomationComposition(UUID compositionId,
111             AutomationComposition automationComposition) {
112         var response = new InstantiationResponse();
113         var instanceId = automationComposition.getInstanceId();
114         var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
115         if (!compositionId.equals(acToUpdate.getCompositionId())) {
116             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
117                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
118         }
119         if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
120             acToUpdate.setElements(automationComposition.getElements());
121             acToUpdate.setName(automationComposition.getName());
122             acToUpdate.setVersion(automationComposition.getVersion());
123             acToUpdate.setDescription(automationComposition.getDescription());
124             acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
125             var validationResult = validateAutomationComposition(acToUpdate);
126             if (!validationResult.isValid()) {
127                 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
128             }
129             automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
130             response.setInstanceId(instanceId);
131             response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
132             return response;
133
134         } else if ((DeployState.DEPLOYED.equals(acToUpdate.getDeployState())
135                 || DeployState.UPDATING.equals(acToUpdate.getDeployState()))
136                 && LockState.LOCKED.equals(acToUpdate.getLockState())) {
137             return updateDeployedAutomationComposition(compositionId, automationComposition, acToUpdate);
138         }
139         throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
140                 "Not allowed to update in the state " + acToUpdate.getDeployState());
141     }
142
143     /**
144      * Update deployed AC Element properties.
145      *
146      * @param compositionId The UUID of the automation composition definition
147      * @param automationComposition the automation composition
148      * @param acToBeUpdated the composition to be updated
149      * @return the result of the update
150      */
151     public InstantiationResponse updateDeployedAutomationComposition(UUID compositionId,
152             AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
153
154         if (automationComposition.getCompositionTargetId() != null
155                 && !DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
156             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
157                     "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
158         }
159
160         // Iterate and update the element property values
161         for (var dbAcElement : acToBeUpdated.getElements().entrySet()) {
162             var elementId = dbAcElement.getKey();
163             if (automationComposition.getElements().containsKey(elementId)) {
164                 dbAcElement.getValue().getProperties()
165                         .putAll(automationComposition.getElements().get(elementId).getProperties());
166             }
167         }
168         if (automationComposition.getRestarting() != null) {
169             throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Update not allowed");
170         }
171
172         if (automationComposition.getCompositionTargetId() != null) {
173             var validationResult =
174                     validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
175             if (!validationResult.isValid()) {
176                 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
177             }
178             acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
179
180             // Publish migrate event to the participants
181             supervisionAcHandler.migrate(acToBeUpdated, automationComposition.getCompositionTargetId());
182         } else {
183             var validationResult = validateAutomationComposition(acToBeUpdated);
184             if (!validationResult.isValid()) {
185                 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
186             }
187             // Publish property update event to the participants
188             supervisionAcHandler.update(acToBeUpdated);
189         }
190
191         automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
192         var response = new InstantiationResponse();
193         var instanceId = automationComposition.getInstanceId();
194         response.setInstanceId(instanceId);
195         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
196         return response;
197     }
198
199     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
200         return validateAutomationComposition(automationComposition, automationComposition.getCompositionId());
201     }
202
203     /**
204      * Validate AutomationComposition.
205      *
206      * @param automationComposition AutomationComposition to validate
207      * @param compositionId the composition id
208      * @return the result of validation
209      */
210     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
211             UUID compositionId) {
212
213         var result = new BeanValidationResult("AutomationComposition", automationComposition);
214         var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
215         if (acDefinitionOpt.isEmpty()) {
216             result.addResult(new ObjectValidationResult("ServiceTemplate", compositionId, ValidationStatus.INVALID,
217                     "Commissioned automation composition definition not found"));
218             return result;
219         }
220         if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
221             result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
222                     ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
223             return result;
224         }
225         if (acDefinitionOpt.get().getRestarting() != null) {
226             result.addResult(
227                     new ObjectValidationResult("ServiceTemplate.restarting", acDefinitionOpt.get().getRestarting(),
228                             ValidationStatus.INVALID, "There is a restarting process in composition"));
229             return result;
230         }
231         var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
232                 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
233
234         acmParticipantProvider.verifyParticipantState(participantIds);
235
236         result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
237                 acDefinitionOpt.get().getServiceTemplate(),
238                 acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
239
240         if (result.isValid()) {
241             for (var element : automationComposition.getElements().values()) {
242                 var name = element.getDefinition().getName();
243                 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
244                 element.setParticipantId(participantId);
245             }
246         }
247
248         return result;
249     }
250
251     /**
252      * Get Automation Composition.
253      *
254      * @param compositionId The UUID of the automation composition definition
255      * @param instanceId The UUID of the automation composition instance
256      * @return the Automation Composition
257      */
258     @Transactional(readOnly = true)
259     public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
260         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
261         if (!compositionId.equals(automationComposition.getCompositionId())
262                         && !compositionId.equals(automationComposition.getCompositionTargetId())) {
263             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
264                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
265         }
266         return automationComposition;
267     }
268
269     /**
270      * Delete the automation composition with the given name and version.
271      *
272      * @param compositionId The UUID of the automation composition definition
273      * @param instanceId The UUID of the automation composition instance
274      * @return the result of the deletion
275      */
276     public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
277         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
278         if (!compositionId.equals(automationComposition.getCompositionId())) {
279             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
280                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
281         }
282         if (!DeployState.UNDEPLOYED.equals(automationComposition.getDeployState())
283                 && !DeployState.DELETING.equals(automationComposition.getDeployState())) {
284             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
285                     "Automation composition state is still " + automationComposition.getDeployState());
286         }
287         if (DeployState.DELETING.equals(automationComposition.getDeployState())
288                 && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) {
289             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
290                     "Automation composition state is still " + automationComposition.getDeployState());
291         }
292         if (automationComposition.getRestarting() != null) {
293             throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Delete not allowed");
294         }
295         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
296         var participantIds = acDefinition.getElementStateMap().values().stream()
297             .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
298         acmParticipantProvider.verifyParticipantState(participantIds);
299         supervisionAcHandler.delete(automationComposition, acDefinition);
300         var response = new InstantiationResponse();
301         response.setInstanceId(automationComposition.getInstanceId());
302         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
303         return response;
304     }
305
306     /**
307      * Get the requested automation compositions.
308      *
309      * @param name the name of the automation composition to get, null for all automation compositions
310      * @param version the version of the automation composition to get, null for all automation compositions
311      * @return the automation compositions
312      */
313     @Transactional(readOnly = true)
314     public AutomationCompositions getAutomationCompositions(UUID compositionId, String name, String version) {
315         var automationCompositions = new AutomationCompositions();
316         automationCompositions.setAutomationCompositionList(
317                 automationCompositionProvider.getAutomationCompositions(compositionId, name, version));
318
319         return automationCompositions;
320     }
321
322     /**
323      * Handle Composition Instance State.
324      *
325      * @param compositionId the compositionId
326      * @param instanceId the instanceId
327      * @param acInstanceStateUpdate the AcInstanceStateUpdate
328      */
329     public void compositionInstanceState(UUID compositionId, UUID instanceId,
330             @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
331         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
332         if (!compositionId.equals(automationComposition.getCompositionId())) {
333             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
334                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
335         }
336         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
337
338         var participantIds = acDefinition.getElementStateMap().values().stream()
339                 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
340
341         acmParticipantProvider.verifyParticipantState(participantIds);
342         var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
343                 acInstanceStateUpdate.getLockOrder(), automationComposition.getDeployState(),
344                 automationComposition.getLockState(), automationComposition.getStateChangeResult());
345         switch (result) {
346             case "DEPLOY":
347                 supervisionAcHandler.deploy(automationComposition, acDefinition);
348                 break;
349
350             case "UNDEPLOY":
351                 supervisionAcHandler.undeploy(automationComposition, acDefinition);
352                 break;
353
354             case "LOCK":
355                 supervisionAcHandler.lock(automationComposition, acDefinition);
356                 break;
357
358             case "UNLOCK":
359                 supervisionAcHandler.unlock(automationComposition, acDefinition);
360                 break;
361
362             default:
363                 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Not valid " + acInstanceStateUpdate);
364         }
365     }
366 }