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