977708ee16aed92c70c6a803c4d505063d441616
[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 org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
31 import org.onap.policy.clamp.acm.runtime.participants.AcmParticipantProvider;
32 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionAcHandler;
33 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
34 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
36 import org.onap.policy.clamp.models.acm.concepts.DeployState;
37 import org.onap.policy.clamp.models.acm.concepts.LockState;
38 import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
39 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
40 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
41 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
42 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
43 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
44 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
45 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
46 import org.onap.policy.common.parameters.BeanValidationResult;
47 import org.onap.policy.common.parameters.ObjectValidationResult;
48 import org.onap.policy.common.parameters.ValidationStatus;
49 import org.onap.policy.models.base.PfModelRuntimeException;
50 import org.springframework.stereotype.Service;
51 import org.springframework.transaction.annotation.Transactional;
52
53 /**
54  * This class is dedicated to the Instantiation of Commissioned automation composition.
55  */
56 @Service
57 @Transactional
58 @AllArgsConstructor
59 public class AutomationCompositionInstantiationProvider {
60     private static final String DO_NOT_MATCH = " do not match with ";
61
62     private final AutomationCompositionProvider automationCompositionProvider;
63     private final AcDefinitionProvider acDefinitionProvider;
64     private final AcInstanceStateResolver acInstanceStateResolver;
65     private final SupervisionAcHandler supervisionAcHandler;
66     private final AcmParticipantProvider acmParticipantProvider;
67     private final AcRuntimeParameterGroup acRuntimeParameterGroup;
68
69     /**
70      * Create automation composition.
71      *
72      * @param compositionId The UUID of the automation composition definition
73      * @param automationComposition the automation composition
74      * @return the result of the instantiation operation
75      */
76     public InstantiationResponse createAutomationComposition(UUID compositionId,
77             AutomationComposition automationComposition) {
78         if (!compositionId.equals(automationComposition.getCompositionId())) {
79             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
80                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
81         }
82         var checkAutomationCompositionOpt =
83                 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
84         if (checkAutomationCompositionOpt.isPresent()) {
85             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
86                     automationComposition.getKey().asIdentifier() + " already defined");
87         }
88
89         var validationResult = validateAutomationComposition(automationComposition);
90         if (!validationResult.isValid()) {
91             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
92         }
93         automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
94
95         var response = new InstantiationResponse();
96         response.setInstanceId(automationComposition.getInstanceId());
97         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
98
99         return response;
100     }
101
102     /**
103      * Update automation composition.
104      *
105      * @param compositionId The UUID of the automation composition definition
106      * @param automationComposition the automation composition
107      * @return the result of the update
108      */
109     public InstantiationResponse updateAutomationComposition(UUID compositionId,
110             AutomationComposition automationComposition) {
111         var response = new InstantiationResponse();
112         var instanceId = automationComposition.getInstanceId();
113         var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
114         if (!compositionId.equals(acToUpdate.getCompositionId())) {
115             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
116                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
117         }
118         if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
119             acToUpdate.setElements(automationComposition.getElements());
120             acToUpdate.setName(automationComposition.getName());
121             acToUpdate.setVersion(automationComposition.getVersion());
122             acToUpdate.setDescription(automationComposition.getDescription());
123             acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
124             var validationResult = validateAutomationComposition(acToUpdate);
125             if (!validationResult.isValid()) {
126                 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
127             }
128             automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
129             response.setInstanceId(instanceId);
130             response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
131             return response;
132
133         } else if ((DeployState.DEPLOYED.equals(acToUpdate.getDeployState())
134                 || DeployState.UPDATING.equals(acToUpdate.getDeployState()))
135                 && LockState.LOCKED.equals(acToUpdate.getLockState())) {
136             return updateDeployedAutomationComposition(compositionId, automationComposition, acToUpdate);
137         }
138         throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
139                 "Not allowed to update in the state " + acToUpdate.getDeployState());
140     }
141
142     /**
143      * Update deployed AC Element properties.
144      *
145      * @param compositionId The UUID of the automation composition definition
146      * @param automationComposition the automation composition
147      * @param acToBeUpdated the composition to be updated
148      * @return the result of the update
149      */
150     public InstantiationResponse updateDeployedAutomationComposition(UUID compositionId,
151             AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
152
153         // Iterate and update the element property values
154         for (var dbAcElement : acToBeUpdated.getElements().entrySet()) {
155             var elementId = dbAcElement.getKey();
156             if (automationComposition.getElements().containsKey(elementId)) {
157                 dbAcElement.getValue().getProperties()
158                         .putAll(automationComposition.getElements().get(elementId).getProperties());
159             }
160         }
161         if (automationComposition.getRestarting() != null) {
162             throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Update not allowed");
163         }
164         var validationResult = validateAutomationComposition(acToBeUpdated);
165         if (!validationResult.isValid()) {
166             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
167         }
168
169         // Publish property update event to the participants
170         supervisionAcHandler.update(acToBeUpdated);
171
172         automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
173         var response = new InstantiationResponse();
174         var instanceId = automationComposition.getInstanceId();
175         response.setInstanceId(instanceId);
176         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
177         return response;
178     }
179
180     /**
181      * Validate AutomationComposition.
182      *
183      * @param automationComposition AutomationComposition to validate
184      * @return the result of validation
185      */
186     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
187
188         var result = new BeanValidationResult("AutomationComposition", automationComposition);
189         var acDefinitionOpt = acDefinitionProvider.findAcDefinition(automationComposition.getCompositionId());
190         if (acDefinitionOpt.isEmpty()) {
191             result.addResult(new ObjectValidationResult("ServiceTemplate", "", ValidationStatus.INVALID,
192                     "Commissioned automation composition definition not found"));
193             return result;
194         }
195         if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
196             result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
197                     ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
198             return result;
199         }
200         if (acDefinitionOpt.get().getRestarting() != null) {
201             result.addResult(
202                     new ObjectValidationResult("ServiceTemplate.restarting", acDefinitionOpt.get().getRestarting(),
203                             ValidationStatus.INVALID, "There is a restarting process in composition"));
204             return result;
205         }
206         var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
207                 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
208
209         acmParticipantProvider.verifyParticipantState(participantIds);
210
211         result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
212                 acDefinitionOpt.get().getServiceTemplate(),
213                 acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
214
215         if (result.isValid()) {
216             for (var element : automationComposition.getElements().values()) {
217                 var name = element.getDefinition().getName();
218                 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
219                 element.setParticipantId(participantId);
220             }
221         }
222
223         return result;
224     }
225
226     /**
227      * Get Automation Composition.
228      *
229      * @param compositionId The UUID of the automation composition definition
230      * @param instanceId The UUID of the automation composition instance
231      * @return the Automation Composition
232      */
233     @Transactional(readOnly = true)
234     public AutomationComposition getAutomationComposition(UUID compositionId, UUID instanceId) {
235         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
236         if (!automationComposition.getCompositionId().equals(compositionId)) {
237             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
238                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
239         }
240         return automationComposition;
241     }
242
243     /**
244      * Delete the automation composition with the given name and version.
245      *
246      * @param compositionId The UUID of the automation composition definition
247      * @param instanceId The UUID of the automation composition instance
248      * @return the result of the deletion
249      */
250     public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
251         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
252         if (!compositionId.equals(automationComposition.getCompositionId())) {
253             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
254                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
255         }
256         if (!DeployState.UNDEPLOYED.equals(automationComposition.getDeployState())
257                 && !DeployState.DELETING.equals(automationComposition.getDeployState())) {
258             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
259                     "Automation composition state is still " + automationComposition.getDeployState());
260         }
261         if (DeployState.DELETING.equals(automationComposition.getDeployState())
262                 && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) {
263             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
264                     "Automation composition state is still " + automationComposition.getDeployState());
265         }
266         if (automationComposition.getRestarting() != null) {
267             throw new PfModelRuntimeException(Status.BAD_REQUEST, "There is a restarting process, Delete not allowed");
268         }
269         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
270         if (acDefinition != null) {
271             var participantIds = acDefinition.getElementStateMap().values().stream()
272                     .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
273             acmParticipantProvider.verifyParticipantState(participantIds);
274         }
275         supervisionAcHandler.delete(automationComposition, acDefinition);
276         var response = new InstantiationResponse();
277         response.setInstanceId(automationComposition.getInstanceId());
278         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
279         return response;
280     }
281
282     /**
283      * Get the requested automation compositions.
284      *
285      * @param name the name of the automation composition to get, null for all automation compositions
286      * @param version the version of the automation composition to get, null for all automation compositions
287      * @return the automation compositions
288      */
289     @Transactional(readOnly = true)
290     public AutomationCompositions getAutomationCompositions(UUID compositionId, String name, String version) {
291         var automationCompositions = new AutomationCompositions();
292         automationCompositions.setAutomationCompositionList(
293                 automationCompositionProvider.getAutomationCompositions(compositionId, name, version));
294
295         return automationCompositions;
296     }
297
298     /**
299      * Handle Composition Instance State.
300      *
301      * @param compositionId the compositionId
302      * @param instanceId the instanceId
303      * @param acInstanceStateUpdate the AcInstanceStateUpdate
304      */
305     public void compositionInstanceState(UUID compositionId, UUID instanceId,
306             @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
307         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
308         if (!compositionId.equals(automationComposition.getCompositionId())) {
309             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
310                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
311         }
312         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
313
314         var participantIds = acDefinition.getElementStateMap().values().stream()
315                 .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
316
317         acmParticipantProvider.verifyParticipantState(participantIds);
318         var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
319                 acInstanceStateUpdate.getLockOrder(), automationComposition.getDeployState(),
320                 automationComposition.getLockState(), automationComposition.getStateChangeResult());
321         switch (result) {
322             case "DEPLOY":
323                 supervisionAcHandler.deploy(automationComposition, acDefinition);
324                 break;
325
326             case "UNDEPLOY":
327                 supervisionAcHandler.undeploy(automationComposition, acDefinition);
328                 break;
329
330             case "LOCK":
331                 supervisionAcHandler.lock(automationComposition, acDefinition);
332                 break;
333
334             case "UNLOCK":
335                 supervisionAcHandler.unlock(automationComposition, acDefinition);
336                 break;
337
338             default:
339                 throw new PfModelRuntimeException(Status.BAD_REQUEST, "Not valid " + acInstanceStateUpdate);
340         }
341     }
342 }