4949c66129cf48326be42c85367b175e9f697654
[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.List;
25 import java.util.UUID;
26 import java.util.function.Function;
27 import java.util.stream.Collectors;
28 import javax.ws.rs.core.Response;
29 import javax.ws.rs.core.Response.Status;
30 import lombok.AllArgsConstructor;
31 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionHandler;
32 import org.onap.policy.clamp.common.acm.exception.AutomationCompositionException;
33 import org.onap.policy.clamp.common.acm.exception.AutomationCompositionRuntimeException;
34 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionState;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
37 import org.onap.policy.clamp.models.acm.concepts.Participant;
38 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationCommand;
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.AutomationCompositionProvider;
42 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
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 AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE = "AutomationCompositionElement";
59     private static final String DO_NOT_MATCH = " do not match with ";
60
61     private final AutomationCompositionProvider automationCompositionProvider;
62     private final SupervisionHandler supervisionHandler;
63     private final ParticipantProvider participantProvider;
64     private final AcDefinitionProvider acDefinitionProvider;
65     private static final String ENTRY = "entry ";
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 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         acToUpdate.setElements(automationComposition.getElements());
116         acToUpdate.setName(automationComposition.getName());
117         acToUpdate.setVersion(automationComposition.getVersion());
118         acToUpdate.setDescription(automationComposition.getDescription());
119         acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
120         var validationResult = validateAutomationComposition(acToUpdate);
121         if (!validationResult.isValid()) {
122             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
123         }
124         automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
125
126         var response = new InstantiationResponse();
127         response.setInstanceId(instanceId);
128         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
129         return response;
130     }
131
132     /**
133      * Validate AutomationComposition.
134      *
135      * @param automationComposition AutomationComposition to validate
136      * @return the result of validation
137      */
138     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
139
140         var result = new BeanValidationResult("AutomationComposition", automationComposition);
141         var acDefinitionOpt = acDefinitionProvider.findAcDefinition(automationComposition.getCompositionId());
142         if (acDefinitionOpt.isEmpty()) {
143             result.addResult(new ObjectValidationResult("ServiceTemplate", "", ValidationStatus.INVALID,
144                     "Commissioned automation composition definition not found"));
145         } else {
146             result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
147                     acDefinitionOpt.get().getServiceTemplate()));
148         }
149         return result;
150     }
151
152     /**
153      * Get Automation Composition.
154      *
155      * @param compositionId The UUID of the automation composition definition
156      * @param instanceId The UUID of the automation composition instance
157      * @return the Automation Composition
158      */
159     @Transactional(readOnly = true)
160     public AutomationComposition getAutomationComposition(UUID compositionId, UUID instanceId) {
161         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
162         if (!automationComposition.getCompositionId().equals(compositionId)) {
163             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
164                     "Composition Id " + compositionId + DO_NOT_MATCH + automationComposition.getCompositionId());
165         }
166         return automationComposition;
167     }
168
169     /**
170      * Delete the automation composition with the given name and version.
171      *
172      * @param compositionId The UUID of the automation composition definition
173      * @param instanceId The UUID of the automation composition instance
174      * @return the result of the deletion
175      */
176     public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
177         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
178         if (!compositionId.equals(automationComposition.getCompositionId())) {
179             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
180                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
181         }
182         if (!AutomationCompositionState.UNINITIALISED.equals(automationComposition.getState())) {
183             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
184                     "Automation composition state is still " + automationComposition.getState());
185         }
186         var response = new InstantiationResponse();
187         automationComposition =
188                 automationCompositionProvider.deleteAutomationComposition(automationComposition.getInstanceId());
189         response.setInstanceId(automationComposition.getInstanceId());
190         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
191         return response;
192     }
193
194     /**
195      * Get the requested automation compositions.
196      *
197      * @param name the name of the automation composition to get, null for all automation compositions
198      * @param version the version of the automation composition to get, null for all automation compositions
199      * @return the automation compositions
200      */
201     @Transactional(readOnly = true)
202     public AutomationCompositions getAutomationCompositions(UUID compositionId, String name, String version) {
203         var automationCompositions = new AutomationCompositions();
204         automationCompositions.setAutomationCompositionList(
205                 automationCompositionProvider.getAutomationCompositions(compositionId, name, version));
206
207         return automationCompositions;
208     }
209
210     /**
211      * Issue a command to automation compositions, setting their ordered state.
212      *
213      * @param automationComposition the AutomationComposition
214      * @param command the command to issue to automation compositions
215      */
216     public void issueAutomationCompositionCommand(AutomationComposition automationComposition,
217             InstantiationCommand command) {
218
219         if (command.getOrderedState() == null) {
220             throw new AutomationCompositionRuntimeException(Status.BAD_REQUEST,
221                     "ordered state invalid or not specified on command");
222         }
223
224         var participants = participantProvider.getParticipants();
225         if (participants.isEmpty()) {
226             throw new AutomationCompositionRuntimeException(Status.BAD_REQUEST, "No participants registered");
227         }
228         var validationResult = validateIssueAutomationComposition(automationComposition, participants);
229         if (!validationResult.isValid()) {
230             throw new AutomationCompositionRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
231         }
232
233         automationComposition.setCascadedOrderedState(command.getOrderedState());
234         try {
235             supervisionHandler.triggerAutomationCompositionSupervision(automationComposition);
236         } catch (AutomationCompositionException e) {
237             throw new AutomationCompositionRuntimeException(Response.Status.BAD_REQUEST, e.getMessage());
238         }
239         automationCompositionProvider.updateAutomationComposition(automationComposition);
240     }
241
242     private BeanValidationResult validateIssueAutomationComposition(AutomationComposition automationComposition,
243             List<Participant> participants) {
244         var result = new BeanValidationResult("AutomationComposition", automationComposition);
245
246         var participantMap = participants.stream()
247                 .collect(Collectors.toMap(participant -> participant.getParticipantId(), Function.identity()));
248
249         for (var element : automationComposition.getElements().values()) {
250
251             var subResult = new BeanValidationResult(ENTRY + element.getDefinition().getName(), element);
252             var p = participantMap.get(element.getParticipantId());
253             if (p == null) {
254                 subResult.addResult(new ObjectValidationResult(AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE,
255                         element.getDefinition().getName(), ValidationStatus.INVALID,
256                         "Participant with ID " + element.getParticipantId() + " is not registered"));
257             } else if (!p.getParticipantId().equals(element.getParticipantId())) {
258                 subResult.addResult(new ObjectValidationResult(AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE,
259                         element.getDefinition().getName(), ValidationStatus.INVALID,
260                         "Participant with ID " + " - " + element.getParticipantId()
261                                 + " is not registered"));
262             }
263             result.addResult(subResult);
264         }
265
266         return result;
267     }
268 }