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