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