18fd1f93873333f53996e6816917a38459e44836
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2021 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.ArrayList;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.function.Function;
28 import java.util.function.UnaryOperator;
29 import java.util.stream.Collectors;
30 import javax.ws.rs.core.Response;
31 import javax.ws.rs.core.Response.Status;
32 import lombok.AllArgsConstructor;
33 import org.onap.policy.clamp.acm.runtime.commissioning.CommissioningProvider;
34 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionHandler;
35 import org.onap.policy.clamp.common.acm.exception.AutomationCompositionException;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionState;
39 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
40 import org.onap.policy.clamp.models.acm.concepts.Participant;
41 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationCommand;
42 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
43 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
44 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
45 import org.onap.policy.common.parameters.BeanValidationResult;
46 import org.onap.policy.common.parameters.ObjectValidationResult;
47 import org.onap.policy.common.parameters.ValidationResult;
48 import org.onap.policy.common.parameters.ValidationStatus;
49 import org.onap.policy.models.base.PfModelException;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
51 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
52 import org.springframework.stereotype.Service;
53 import org.springframework.transaction.annotation.Transactional;
54
55 /**
56  * This class is dedicated to the Instantiation of Commissioned automation composition.
57  */
58 @Service
59 @Transactional
60 @AllArgsConstructor
61 public class AutomationCompositionInstantiationProvider {
62     private static final String AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE = "AutomationCompositionElement";
63
64     private final AutomationCompositionProvider automationCompositionProvider;
65     private final CommissioningProvider commissioningProvider;
66     private final SupervisionHandler supervisionHandler;
67     private final ParticipantProvider participantProvider;
68     private static final String ENTRY = "entry ";
69
70     /**
71      * Create automation compositions.
72      *
73      * @param automationCompositions the automation composition
74      * @return the result of the instantiation operation
75      * @throws PfModelException on creation errors
76      */
77     public InstantiationResponse createAutomationCompositions(AutomationCompositions automationCompositions)
78         throws PfModelException {
79         for (AutomationComposition automationComposition : automationCompositions.getAutomationCompositionList()) {
80             var checkAutomationCompositionOpt =
81                 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
82             if (checkAutomationCompositionOpt.isPresent()) {
83                 throw new PfModelException(Response.Status.BAD_REQUEST,
84                     automationComposition.getKey().asIdentifier() + " already defined");
85             }
86         }
87         BeanValidationResult validationResult = validateAutomationCompositions(automationCompositions);
88         if (!validationResult.isValid()) {
89             throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
90         }
91         automationCompositionProvider.saveAutomationCompositions(automationCompositions.getAutomationCompositionList());
92
93         var response = new InstantiationResponse();
94         response.setAffectedAutomationCompositions(automationCompositions.getAutomationCompositionList().stream()
95             .map(ac -> ac.getKey().asIdentifier()).collect(Collectors.toList()));
96
97         return response;
98     }
99
100     /**
101      * Update automation compositions.
102      *
103      * @param automationCompositions the automation composition
104      * @return the result of the instantiation operation
105      * @throws PfModelException on update errors
106      */
107     public InstantiationResponse updateAutomationCompositions(AutomationCompositions automationCompositions)
108         throws PfModelException {
109         BeanValidationResult validationResult = validateAutomationCompositions(automationCompositions);
110         if (!validationResult.isValid()) {
111             throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
112         }
113         automationCompositionProvider.saveAutomationCompositions(automationCompositions.getAutomationCompositionList());
114
115         var response = new InstantiationResponse();
116         response.setAffectedAutomationCompositions(automationCompositions.getAutomationCompositionList().stream()
117             .map(ac -> ac.getKey().asIdentifier()).collect(Collectors.toList()));
118
119         return response;
120     }
121
122     /**
123      * Validate AutomationCompositions.
124      *
125      * @param automationCompositions AutomationCompositions to validate
126      * @return the result of validation
127      * @throws PfModelException if automationCompositions is not valid
128      */
129     private BeanValidationResult validateAutomationCompositions(AutomationCompositions automationCompositions)
130         throws PfModelException {
131
132         var result = new BeanValidationResult("AutomationCompositions", automationCompositions);
133
134         for (AutomationComposition automationComposition : automationCompositions.getAutomationCompositionList()) {
135             var subResult = new BeanValidationResult(ENTRY + automationComposition.getDefinition().getName(),
136                 automationComposition);
137
138             List<ToscaNodeTemplate> toscaNodeTemplates = commissioningProvider.getAutomationCompositionDefinitions(
139                 automationComposition.getDefinition().getName(), automationComposition.getDefinition().getVersion());
140
141             if (toscaNodeTemplates.isEmpty()) {
142                 subResult.addResult(
143                     new ObjectValidationResult("AutomationComposition", automationComposition.getDefinition().getName(),
144                         ValidationStatus.INVALID, "Commissioned automation composition definition not found"));
145             } else if (toscaNodeTemplates.size() > 1) {
146                 subResult.addResult(
147                     new ObjectValidationResult("AutomationComposition", automationComposition.getDefinition().getName(),
148                         ValidationStatus.INVALID, "Commissioned automation composition definition not valid"));
149             } else {
150
151                 List<ToscaNodeTemplate> acElementDefinitions =
152                     commissioningProvider.getAutomationCompositionElementDefinitions(toscaNodeTemplates.get(0));
153
154                 // @formatter:off
155                 Map<String, ToscaConceptIdentifier> definitions = acElementDefinitions
156                         .stream()
157                         .map(nodeTemplate -> nodeTemplate.getKey().asIdentifier())
158                         .collect(Collectors.toMap(ToscaConceptIdentifier::getName, UnaryOperator.identity()));
159                 // @formatter:on
160
161                 for (AutomationCompositionElement element : automationComposition.getElements().values()) {
162                     subResult.addResult(validateDefinition(definitions, element.getDefinition()));
163                 }
164             }
165             result.addResult(subResult);
166         }
167         return result;
168     }
169
170     /**
171      * Validate ToscaConceptIdentifier, checking if exist in ToscaConceptIdentifiers map.
172      *
173      * @param definitions map of all ToscaConceptIdentifiers
174      * @param definition ToscaConceptIdentifier to validate
175      * @return the validation result
176      */
177     private ValidationResult validateDefinition(Map<String, ToscaConceptIdentifier> definitions,
178         ToscaConceptIdentifier definition) {
179         var result = new BeanValidationResult(ENTRY + definition.getName(), definition);
180         ToscaConceptIdentifier identifier = definitions.get(definition.getName());
181         if (identifier == null) {
182             result.setResult(ValidationStatus.INVALID, "Not found");
183         } else if (!identifier.equals(definition)) {
184             result.setResult(ValidationStatus.INVALID, "Version not matching");
185         }
186         return (result.isClean() ? null : result);
187     }
188
189     /**
190      * Delete the automation composition with the given name and version.
191      *
192      * @param name the name of the automation composition to delete
193      * @param version the version of the automation composition to delete
194      * @return the result of the deletion
195      * @throws PfModelException on deletion errors
196      */
197     public InstantiationResponse deleteAutomationComposition(String name, String version) throws PfModelException {
198         var automationCompositionOpt = automationCompositionProvider.findAutomationComposition(name, version);
199         if (automationCompositionOpt.isEmpty()) {
200             throw new PfModelException(Response.Status.NOT_FOUND, "Automation composition not found");
201         }
202         var automationComposition = automationCompositionOpt.get();
203         if (!AutomationCompositionState.UNINITIALISED.equals(automationComposition.getState())) {
204             throw new PfModelException(Response.Status.BAD_REQUEST,
205                 "Automation composition state is still " + automationComposition.getState());
206         }
207         var response = new InstantiationResponse();
208         response.setAffectedAutomationCompositions(
209             List.of(automationCompositionProvider.deleteAutomationComposition(name, version).getKey().asIdentifier()));
210         return response;
211     }
212
213     /**
214      * Get the requested automation compositions.
215      *
216      * @param name the name of the automation composition to get, null for all automation compositions
217      * @param version the version of the automation composition to get, null for all automation compositions
218      * @return the automation compositions
219      * @throws PfModelException on errors getting automation compositions
220      */
221     @Transactional(readOnly = true)
222     public AutomationCompositions getAutomationCompositions(String name, String version) throws PfModelException {
223         var automationCompositions = new AutomationCompositions();
224         automationCompositions
225             .setAutomationCompositionList(automationCompositionProvider.getAutomationCompositions(name, version));
226
227         return automationCompositions;
228     }
229
230     /**
231      * Issue a command to automation compositions, setting their ordered state.
232      *
233      * @param command the command to issue to automation compositions
234      * @return the result of the initiation command
235      * @throws PfModelException on errors setting the ordered state on the automation compositions
236      * @throws AutomationCompositionException on ordered state invalid
237      */
238     public InstantiationResponse issueAutomationCompositionCommand(InstantiationCommand command)
239         throws AutomationCompositionException, PfModelException {
240
241         if (command.getOrderedState() == null) {
242             throw new AutomationCompositionException(Status.BAD_REQUEST,
243                 "ordered state invalid or not specified on command");
244         }
245
246         var participants = participantProvider.getParticipants();
247         if (participants.isEmpty()) {
248             throw new AutomationCompositionException(Status.BAD_REQUEST, "No participants registered");
249         }
250         var validationResult = new BeanValidationResult("InstantiationCommand", command);
251         List<AutomationComposition> automationCompositions =
252             new ArrayList<>(command.getAutomationCompositionIdentifierList().size());
253         for (ToscaConceptIdentifier id : command.getAutomationCompositionIdentifierList()) {
254             var automationCompositionOpt = automationCompositionProvider.findAutomationComposition(id);
255             if (automationCompositionOpt.isEmpty()) {
256                 validationResult.addResult("ToscaConceptIdentifier", id, ValidationStatus.INVALID,
257                     "AutomationComposition with id " + id + " not found");
258             } else {
259                 var automationComposition = automationCompositionOpt.get();
260                 automationComposition.setCascadedOrderedState(command.getOrderedState());
261                 automationCompositions.add(automationComposition);
262             }
263         }
264         if (validationResult.isValid()) {
265             validationResult = validateIssueAutomationCompositions(automationCompositions, participants);
266         }
267         if (!validationResult.isValid()) {
268             throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
269         }
270         automationCompositionProvider.saveAutomationCompositions(automationCompositions);
271
272         supervisionHandler.triggerAutomationCompositionSupervision(command.getAutomationCompositionIdentifierList());
273         var response = new InstantiationResponse();
274         response.setAffectedAutomationCompositions(command.getAutomationCompositionIdentifierList());
275
276         return response;
277     }
278
279     private BeanValidationResult validateIssueAutomationCompositions(List<AutomationComposition> automationCompositions,
280         List<Participant> participants) {
281         var result = new BeanValidationResult("AutomationCompositions", automationCompositions);
282
283         Map<ToscaConceptIdentifier, Participant> participantMap = participants.stream()
284             .collect(Collectors.toMap(participant -> participant.getKey().asIdentifier(), Function.identity()));
285
286         for (AutomationComposition automationComposition : automationCompositions) {
287
288             for (var element : automationComposition.getElements().values()) {
289
290                 var subResult = new BeanValidationResult(ENTRY + element.getDefinition().getName(), element);
291                 Participant p = participantMap.get(element.getParticipantId());
292                 if (p == null) {
293                     subResult.addResult(new ObjectValidationResult(AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE,
294                         element.getDefinition().getName(), ValidationStatus.INVALID,
295                         "Participant with ID " + element.getParticipantId() + " is not registered"));
296                 } else if (!p.getParticipantType().equals(element.getParticipantType())) {
297                     subResult.addResult(new ObjectValidationResult(AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE,
298                         element.getDefinition().getName(), ValidationStatus.INVALID,
299                         "Participant with ID " + element.getParticipantType() + " - " + element.getParticipantId()
300                             + " is not registered"));
301                 }
302                 result.addResult(subResult);
303             }
304
305         }
306
307         return result;
308     }
309 }