da85b0a8343cdc4a4d4b69265bf5afc4ed3c7d79
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2021 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.clamp.controlloop.runtime.instantiation;
22
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.function.UnaryOperator;
28 import java.util.stream.Collectors;
29 import javax.ws.rs.core.Response;
30 import javax.ws.rs.core.Response.Status;
31 import lombok.AllArgsConstructor;
32 import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
33 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
34 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
35 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
36 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops;
37 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
38 import org.onap.policy.clamp.controlloop.models.messages.rest.GenericNameVersion;
39 import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.ControlLoopOrderStateResponse;
40 import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstancePropertiesResponse;
41 import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationCommand;
42 import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationResponse;
43 import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningProvider;
44 import org.onap.policy.clamp.controlloop.runtime.supervision.SupervisionHandler;
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.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
53 import org.springframework.stereotype.Component;
54
55 /**
56  * This class is dedicated to the Instantiation of Commissioned control loop.
57  */
58 @Component
59 @AllArgsConstructor
60 public class ControlLoopInstantiationProvider {
61     private final ControlLoopProvider controlLoopProvider;
62     private final CommissioningProvider commissioningProvider;
63     private final SupervisionHandler supervisionHandler;
64
65     private static final Object lockit = new Object();
66
67     private static final String CL_ELEMENT_NAME = "name";
68
69     /**
70      * Create Instance Properties.
71      *
72      * @param serviceTemplate the service template
73      * @return the result of the instantiation operation
74      * @throws PfModelException on creation errors
75      */
76     public InstancePropertiesResponse saveInstanceProperties(ToscaServiceTemplate serviceTemplate) {
77
78         String instanceName = generateSequentialInstanceName();
79
80         Map<String, ToscaNodeTemplate> nodeTemplates = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates();
81
82         nodeTemplates.forEach((key, template) -> {
83             String name = key + instanceName;
84             String description = template.getDescription() + instanceName;
85             template.setName(name);
86             template.setDescription(description);
87
88             changeInstanceElementsName(template, instanceName);
89
90         });
91
92         Map<String, ToscaNodeTemplate> toscaSavedNodeTemplate = controlLoopProvider
93             .saveInstanceProperties(serviceTemplate);
94
95         var response = new InstancePropertiesResponse();
96
97         // @formatter:off
98         response.setAffectedInstanceProperties(toscaSavedNodeTemplate.values().stream().map(template ->
99             template.getKey().asIdentifier()).collect(Collectors.toList()));
100         // @formatter:on
101
102         return response;
103     }
104
105     /**
106      * Create control loops.
107      *
108      * @param controlLoops the control loop
109      * @return the result of the instantiation operation
110      * @throws PfModelException on creation errors
111      */
112     public InstantiationResponse createControlLoops(ControlLoops controlLoops) throws PfModelException {
113
114         synchronized (lockit) {
115             for (ControlLoop controlLoop : controlLoops.getControlLoopList()) {
116                 var checkControlLoop = controlLoopProvider.getControlLoop(controlLoop.getKey().asIdentifier());
117                 if (checkControlLoop != null) {
118                     throw new PfModelException(Response.Status.BAD_REQUEST,
119                             controlLoop.getKey().asIdentifier() + " already defined");
120                 }
121             }
122             BeanValidationResult validationResult = validateControlLoops(controlLoops);
123             if (!validationResult.isValid()) {
124                 throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
125             }
126             controlLoopProvider.createControlLoops(controlLoops.getControlLoopList());
127         }
128
129         var response = new InstantiationResponse();
130         response.setAffectedControlLoops(controlLoops.getControlLoopList().stream()
131                 .map(cl -> cl.getKey().asIdentifier()).collect(Collectors.toList()));
132
133         return response;
134     }
135
136     /**
137      * Update control loops.
138      *
139      * @param controlLoops the control loop
140      * @return the result of the instantiation operation
141      * @throws PfModelException on update errors
142      */
143     public InstantiationResponse updateControlLoops(ControlLoops controlLoops) throws PfModelException {
144         synchronized (lockit) {
145             BeanValidationResult validationResult = validateControlLoops(controlLoops);
146             if (!validationResult.isValid()) {
147                 throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
148             }
149             controlLoopProvider.updateControlLoops(controlLoops.getControlLoopList());
150         }
151
152         var response = new InstantiationResponse();
153         response.setAffectedControlLoops(controlLoops.getControlLoopList().stream()
154                 .map(cl -> cl.getKey().asIdentifier()).collect(Collectors.toList()));
155
156         return response;
157     }
158
159     /**
160      * Validate ControlLoops.
161      *
162      * @param controlLoops ControlLoops to validate
163      * @return the result of validation
164      * @throws PfModelException if controlLoops is not valid
165      */
166     private BeanValidationResult validateControlLoops(ControlLoops controlLoops) throws PfModelException {
167
168         var result = new BeanValidationResult("ControlLoops", controlLoops);
169
170         for (ControlLoop controlLoop : controlLoops.getControlLoopList()) {
171             var subResult = new BeanValidationResult("entry " + controlLoop.getDefinition().getName(), controlLoop);
172
173             List<ToscaNodeTemplate> toscaNodeTemplates = commissioningProvider.getControlLoopDefinitions(
174                     controlLoop.getDefinition().getName(), controlLoop.getDefinition().getVersion());
175
176             if (toscaNodeTemplates.isEmpty()) {
177                 subResult.addResult(new ObjectValidationResult("ControlLoop", controlLoop.getDefinition().getName(),
178                         ValidationStatus.INVALID, "Commissioned control loop definition not FOUND"));
179             } else if (toscaNodeTemplates.size() > 1) {
180                 subResult.addResult(new ObjectValidationResult("ControlLoop", controlLoop.getDefinition().getName(),
181                         ValidationStatus.INVALID, "Commissioned control loop definition not VALID"));
182             } else {
183
184                 List<ToscaNodeTemplate> clElementDefinitions =
185                         commissioningProvider.getControlLoopElementDefinitions(toscaNodeTemplates.get(0));
186
187                 // @formatter:off
188                 Map<String, ToscaConceptIdentifier> definitions = clElementDefinitions
189                         .stream()
190                         .map(nodeTemplate -> nodeTemplate.getKey().asIdentifier())
191                         .collect(Collectors.toMap(ToscaConceptIdentifier::getName, UnaryOperator.identity()));
192                 // @formatter:on
193
194                 for (ControlLoopElement element : controlLoop.getElements().values()) {
195                     subResult.addResult(validateDefinition(definitions, element.getDefinition()));
196                 }
197             }
198             result.addResult(subResult);
199         }
200         return result;
201     }
202
203     /**
204      * Validate ToscaConceptIdentifier, checking if exist in ToscaConceptIdentifiers map.
205      *
206      * @param definitions map of all ToscaConceptIdentifiers
207      * @param definition ToscaConceptIdentifier to validate
208      * @return the validation result
209      */
210     private ValidationResult validateDefinition(Map<String, ToscaConceptIdentifier> definitions,
211             ToscaConceptIdentifier definition) {
212         var result = new BeanValidationResult("entry " + definition.getName(), definition);
213         ToscaConceptIdentifier identifier = definitions.get(definition.getName());
214         if (identifier == null) {
215             result.setResult(ValidationStatus.INVALID, "Not FOUND");
216         } else if (!identifier.equals(definition)) {
217             result.setResult(ValidationStatus.INVALID, "Version not matching");
218         }
219         return (result.isClean() ? null : result);
220     }
221
222     /**
223      * Delete the control loop with the given name and version.
224      *
225      * @param name the name of the control loop to delete
226      * @param version the version of the control loop to delete
227      * @return the result of the deletion
228      * @throws PfModelException on deletion errors
229      */
230     public InstantiationResponse deleteControlLoop(String name, String version) throws PfModelException {
231         var response = new InstantiationResponse();
232         synchronized (lockit) {
233             List<ControlLoop> controlLoops = controlLoopProvider.getControlLoops(name, version);
234             if (controlLoops.isEmpty()) {
235                 throw new PfModelException(Response.Status.NOT_FOUND, "Control Loop not found");
236             }
237             for (ControlLoop controlLoop : controlLoops) {
238                 if (!ControlLoopState.UNINITIALISED.equals(controlLoop.getState())) {
239                     throw new PfModelException(Response.Status.BAD_REQUEST,
240                             "Control Loop State is still " + controlLoop.getState());
241                 }
242             }
243
244             response.setAffectedControlLoops(Collections
245                     .singletonList(controlLoopProvider.deleteControlLoop(name, version).getKey().asIdentifier()));
246         }
247         return response;
248     }
249
250     /**
251      * Get the requested control loops.
252      *
253      * @param name the name of the control loop to get, null for all control loops
254      * @param version the version of the control loop to get, null for all control loops
255      * @return the control loops
256      * @throws PfModelException on errors getting control loops
257      */
258     public ControlLoops getControlLoops(String name, String version) throws PfModelException {
259         var controlLoops = new ControlLoops();
260         controlLoops.setControlLoopList(controlLoopProvider.getControlLoops(name, version));
261
262         return controlLoops;
263     }
264
265     /**
266      * Issue a command to control loops, setting their ordered state.
267      *
268      * @param command the command to issue to control loops
269      * @return the result of the initiation command
270      * @throws PfModelException on errors setting the ordered state on the control loops
271      * @throws ControlLoopException on ordered state invalid
272      */
273     public InstantiationResponse issueControlLoopCommand(InstantiationCommand command)
274             throws ControlLoopException, PfModelException {
275
276         if (command.getOrderedState() == null) {
277             throw new ControlLoopException(Status.BAD_REQUEST, "ordered state invalid or not specified on command");
278         }
279
280         synchronized (lockit) {
281             List<ControlLoop> controlLoops = new ArrayList<>(command.getControlLoopIdentifierList().size());
282             for (ToscaConceptIdentifier id : command.getControlLoopIdentifierList()) {
283                 var controlLoop = controlLoopProvider.getControlLoop(id);
284                 controlLoop.setCascadedOrderedState(command.getOrderedState());
285                 controlLoops.add(controlLoop);
286             }
287             controlLoopProvider.updateControlLoops(controlLoops);
288         }
289
290         supervisionHandler.triggerControlLoopSupervision(command.getControlLoopIdentifierList());
291         var response = new InstantiationResponse();
292         response.setAffectedControlLoops(command.getControlLoopIdentifierList());
293
294         return response;
295     }
296
297     /**
298      * Gets a list of control loops with it's ordered state.
299      *
300      * @param name the name of the control loop to get, null for all control loops
301      * @param version the version of the control loop to get, null for all control loops
302      * @return a list of Instantiation Command
303      * @throws PfModelException on errors getting control loops
304      */
305     public ControlLoopOrderStateResponse getInstantiationOrderState(String name, String version)
306         throws PfModelException {
307
308         List<ControlLoop> controlLoops = controlLoopProvider.getControlLoops(name, version);
309
310         var response = new ControlLoopOrderStateResponse();
311
312         controlLoops.forEach(controlLoop -> {
313             var genericNameVersion = new GenericNameVersion();
314             genericNameVersion.setName(controlLoop.getName());
315             genericNameVersion.setVersion(controlLoop.getVersion());
316             response.getControlLoopIdentifierList().add(genericNameVersion);
317         });
318
319         return response;
320     }
321
322     /**
323      * Creates instance element name.
324      *
325      * @param serviceTemplate the service serviceTemplate
326      * @param instanceName    to amend to the element name
327      */
328     private void changeInstanceElementsName(ToscaNodeTemplate serviceTemplate, String instanceName) {
329
330         @SuppressWarnings("unchecked")
331         List<Map<String, String>> controlLoopElements = (List<Map<String, String>>) serviceTemplate.getProperties()
332             .get("elements");
333
334         if (controlLoopElements != null) {
335             controlLoopElements.forEach(clElement -> {
336                 String name = clElement.get(CL_ELEMENT_NAME) + instanceName;
337                 clElement.replace(CL_ELEMENT_NAME, name);
338             });
339         }
340     }
341
342
343     /**
344      * Generates Instance Name in sequential order and return it to append to the Node Template Name.
345      *
346      * @return instanceName
347      */
348     private String generateSequentialInstanceName() {
349         List<ToscaNodeTemplate> nodeTemplates = controlLoopProvider.getNodeTemplates(null, null);
350
351         int instanceNumber =
352             nodeTemplates.stream().map(ToscaNodeTemplate::getName)
353                 .filter(name -> name.contains("_Instance")).map(n -> {
354                     String[] defNameArr = n.split("_Instance");
355
356                     return Integer.parseInt(defNameArr[1]);
357                 }).reduce(0, Math::max);
358
359         return "_Instance" + (instanceNumber + 1);
360     }
361 }