ba632f9cce70d4a128bc0eae1610e35558b4d67f
[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.commissioning;
22
23 import com.fasterxml.jackson.core.JsonProcessingException;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.fasterxml.jackson.databind.PropertyNamingStrategies;
26 import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.stream.Collectors;
33 import javax.ws.rs.core.Response.Status;
34 import org.apache.commons.collections4.CollectionUtils;
35 import org.apache.commons.collections4.MapUtils;
36 import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
37 import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
38 import org.onap.policy.clamp.controlloop.models.messages.rest.commissioning.CommissioningResponse;
39 import org.onap.policy.models.base.PfModelException;
40 import org.onap.policy.models.provider.PolicyModelsProvider;
41 import org.onap.policy.models.tosca.authorative.concepts.ToscaCapabilityType;
42 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
43 import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType;
44 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
45 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeType;
46 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType;
47 import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
48 import org.onap.policy.models.tosca.authorative.concepts.ToscaRelationshipType;
49 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplates;
51 import org.onap.policy.models.tosca.authorative.concepts.ToscaTopologyTemplate;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaTypedEntityFilter;
53 import org.springframework.stereotype.Component;
54
55 /**
56  * This class provides the create, read and delete actions on Commissioning of Control Loop concepts in the database to
57  * the callers.
58  */
59 @Component
60 public class CommissioningProvider {
61     public static final String CONTROL_LOOP_NODE_TYPE = "org.onap.policy.clamp.controlloop.ControlLoop";
62
63     private final PolicyModelsProvider modelsProvider;
64     private final ControlLoopProvider clProvider;
65     private final ObjectMapper mapper = new ObjectMapper();
66
67     private static final Object lockit = new Object();
68
69     /**
70      * Create a commissioning provider.
71      *
72      * @param modelsProvider the PolicyModelsProvider
73      * @param clProvider the ControlLoopProvider
74      */
75     public CommissioningProvider(PolicyModelsProvider modelsProvider, ControlLoopProvider clProvider) {
76         this.modelsProvider = modelsProvider;
77         this.clProvider = clProvider;
78         mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
79     }
80
81     /**
82      * Create control loops from a service template.
83      *
84      * @param serviceTemplate the service template
85      * @return the result of the commissioning operation
86      * @throws PfModelException on creation errors
87      */
88     public CommissioningResponse createControlLoopDefinitions(ToscaServiceTemplate serviceTemplate)
89         throws PfModelException, ControlLoopException {
90
91         if (verifyIfInstancePropertiesExists()) {
92             throw new ControlLoopException(Status.BAD_REQUEST,
93                 "Delete instances, to commission control loop definitions");
94         }
95
96         synchronized (lockit) {
97             modelsProvider.createServiceTemplate(serviceTemplate);
98         }
99
100         var response = new CommissioningResponse();
101         // @formatter:off
102         response.setAffectedControlLoopDefinitions(serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
103                 .values()
104                 .stream()
105                 .map(template -> template.getKey().asIdentifier())
106                 .collect(Collectors.toList()));
107         // @formatter:on
108
109         return response;
110     }
111
112     /**
113      * Delete the control loop definition with the given name and version.
114      *
115      * @param name the name of the control loop definition to delete
116      * @param version the version of the control loop to delete
117      * @return the result of the deletion
118      * @throws PfModelException on deletion errors
119      */
120     public CommissioningResponse deleteControlLoopDefinition(String name, String version)
121         throws PfModelException, ControlLoopException {
122
123         if (verifyIfInstancePropertiesExists()) {
124             throw new ControlLoopException(Status.BAD_REQUEST,
125                 "Delete instances, to commission control loop definitions");
126         }
127
128         synchronized (lockit) {
129             modelsProvider.deleteServiceTemplate(name, version);
130         }
131
132         var response = new CommissioningResponse();
133         response.setAffectedControlLoopDefinitions(
134                 Collections.singletonList(new ToscaConceptIdentifier(name, version)));
135
136         return response;
137     }
138
139     /**
140      * Get control loop node templates.
141      *
142      * @param clName the name of the control loop, null for all
143      * @param clVersion the version of the control loop, null for all
144      * @return list of control loop node templates
145      * @throws PfModelException on errors getting control loop definitions
146      */
147     public List<ToscaNodeTemplate> getControlLoopDefinitions(String clName, String clVersion) throws PfModelException {
148
149         // @formatter:off
150         ToscaTypedEntityFilter<ToscaNodeTemplate> nodeTemplateFilter = ToscaTypedEntityFilter
151                 .<ToscaNodeTemplate>builder()
152                 .name(clName)
153                 .version(clVersion)
154                 .type(CONTROL_LOOP_NODE_TYPE)
155                 .build();
156         // @formatter:on
157
158         return clProvider.getFilteredNodeTemplates(nodeTemplateFilter);
159     }
160
161     /**
162      * Get the control loop elements from a control loop node template.
163      *
164      * @param controlLoopNodeTemplate the control loop node template
165      * @return a list of the control loop element node templates in a control loop node template
166      * @throws PfModelException on errors get control loop element node templates
167      */
168     public List<ToscaNodeTemplate> getControlLoopElementDefinitions(ToscaNodeTemplate controlLoopNodeTemplate)
169             throws PfModelException {
170         if (!CONTROL_LOOP_NODE_TYPE.equals(controlLoopNodeTemplate.getType())) {
171             return Collections.emptyList();
172         }
173
174         if (MapUtils.isEmpty(controlLoopNodeTemplate.getProperties())) {
175             return Collections.emptyList();
176         }
177
178         @SuppressWarnings("unchecked")
179         List<Map<String, String>> controlLoopElements =
180                 (List<Map<String, String>>) controlLoopNodeTemplate.getProperties().get("elements");
181
182         if (CollectionUtils.isEmpty(controlLoopElements)) {
183             return Collections.emptyList();
184         }
185
186         List<ToscaNodeTemplate> controlLoopElementList = new ArrayList<>();
187         // @formatter:off
188         controlLoopElementList.addAll(
189                 controlLoopElements
190                         .stream()
191                         .map(elementMap -> clProvider.getNodeTemplates(elementMap.get("name"),
192                                 elementMap.get("version")))
193                         .flatMap(List::stream)
194                         .collect(Collectors.toList())
195         );
196         // @formatter:on
197
198         return controlLoopElementList;
199     }
200
201     /**
202      * Get the initial node types with common or instance properties.
203      *
204      * @param fullNodeTypes map of all the node types in the specified template
205      * @param common boolean to indicate whether common or instance properties are required
206      * @return node types map that only has common properties
207      * @throws PfModelException on errors getting node type with common properties
208      */
209     private Map<String, ToscaNodeType> getInitialNodeTypesMap(Map<String, ToscaNodeType> fullNodeTypes,
210             boolean common) {
211
212         var tempNodeTypesMap = new HashMap<String, ToscaNodeType>();
213
214         fullNodeTypes.forEach((key, nodeType) -> {
215             var tempToscaNodeType = new ToscaNodeType();
216             tempToscaNodeType.setName(key);
217
218             var resultantPropertyMap = findCommonOrInstancePropsInNodeTypes(nodeType, common);
219
220             if (!resultantPropertyMap.isEmpty()) {
221                 tempToscaNodeType.setProperties(resultantPropertyMap);
222                 tempNodeTypesMap.put(key, tempToscaNodeType);
223             }
224         });
225         return tempNodeTypesMap;
226     }
227
228     private Map<String, ToscaProperty> findCommonOrInstancePropsInNodeTypes(ToscaNodeType nodeType, boolean common) {
229
230         var tempCommonPropertyMap = new HashMap<String, ToscaProperty>();
231         var tempInstancePropertyMap = new HashMap<String, ToscaProperty>();
232
233         nodeType.getProperties().forEach((propKey, prop) -> {
234
235             if (prop.getMetadata() != null) {
236                 prop.getMetadata().forEach((k, v) -> {
237                     if (k.equals("common") && v.equals("true") && common) {
238                         tempCommonPropertyMap.put(propKey, prop);
239                     } else if (k.equals("common") && v.equals("false") && !common) {
240                         tempInstancePropertyMap.put(propKey, prop);
241                     }
242
243                 });
244             } else {
245                 tempInstancePropertyMap.put(propKey, prop);
246             }
247         });
248
249         if (tempCommonPropertyMap.isEmpty() && !common) {
250             return tempInstancePropertyMap;
251         } else {
252             return tempCommonPropertyMap;
253         }
254     }
255
256     /**
257      * Get the node types derived from those that have common properties.
258      *
259      * @param initialNodeTypes map of all the node types in the specified template
260      * @param filteredNodeTypes map of all the node types that have common or instance properties
261      * @return all node types that have common properties including their children
262      * @throws PfModelException on errors getting node type with common properties
263      */
264     private Map<String, ToscaNodeType> getFinalNodeTypesMap(Map<String, ToscaNodeType> initialNodeTypes,
265             Map<String, ToscaNodeType> filteredNodeTypes) {
266         for (var i = 0; i < initialNodeTypes.size(); i++) {
267             initialNodeTypes.forEach((key, nodeType) -> {
268                 var tempToscaNodeType = new ToscaNodeType();
269                 tempToscaNodeType.setName(key);
270
271                 if (filteredNodeTypes.get(nodeType.getDerivedFrom()) != null) {
272                     tempToscaNodeType.setName(key);
273
274                     var finalProps = new HashMap<String, ToscaProperty>(
275                             filteredNodeTypes.get(nodeType.getDerivedFrom()).getProperties());
276
277                     tempToscaNodeType.setProperties(finalProps);
278                 } else {
279                     return;
280                 }
281                 filteredNodeTypes.putIfAbsent(key, tempToscaNodeType);
282
283             });
284         }
285         return filteredNodeTypes;
286     }
287
288     /**
289      * Get the requested node types with common or instance properties.
290      *
291      * @param common boolean indicating common or instance properties
292      * @param name the name of the definition to get, null for all definitions
293      * @param version the version of the definition to get, null for all definitions
294      * @return the node types with common or instance properties
295      * @throws PfModelException on errors getting node type properties
296      */
297     private Map<String, ToscaNodeType> getCommonOrInstancePropertiesFromNodeTypes(boolean common, String name,
298             String version) throws PfModelException {
299         var serviceTemplates = new ToscaServiceTemplates();
300         serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version));
301         var tempNodeTypesMap =
302                 this.getInitialNodeTypesMap(serviceTemplates.getServiceTemplates().get(0).getNodeTypes(), common);
303
304         return this.getFinalNodeTypesMap(serviceTemplates.getServiceTemplates().get(0).getNodeTypes(),
305                 tempNodeTypesMap);
306
307     }
308
309     /**
310      * Get node templates with appropriate common or instance properties added.
311      *
312      * @param initialNodeTemplates map of all the node templates in the specified template
313      * @param nodeTypeProps map of all the node types that have common or instance properties including children
314      * @return all node templates with appropriate common or instance properties added
315      * @throws PfModelException on errors getting map of node templates with common or instance properties added
316      */
317     private Map<String, ToscaNodeTemplate> getDerivedCommonOrInstanceNodeTemplates(
318             Map<String, ToscaNodeTemplate> initialNodeTemplates, Map<String, ToscaNodeType> nodeTypeProps) {
319
320         var finalNodeTemplatesMap = new HashMap<String, ToscaNodeTemplate>();
321
322         initialNodeTemplates.forEach((templateKey, template) -> {
323             if (nodeTypeProps.containsKey(template.getType())) {
324                 var finalMergedProps = new HashMap<String, Object>();
325
326                 nodeTypeProps.get(template.getType()).getProperties().forEach(finalMergedProps::putIfAbsent);
327
328                 template.setProperties(finalMergedProps);
329
330                 finalNodeTemplatesMap.put(templateKey, template);
331             } else {
332                 return;
333             }
334         });
335         return finalNodeTemplatesMap;
336     }
337
338     /**
339      * Get node templates with common properties added.
340      *
341      * @param common boolean indicating common or instance properties to be used
342      * @param name the name of the definition to use, null for all definitions
343      * @param version the version of the definition to use, null for all definitions
344      * @return the nodes templates with common or instance properties
345      * @throws PfModelException on errors getting common or instance properties from node_templates
346      */
347     public Map<String, ToscaNodeTemplate> getNodeTemplatesWithCommonOrInstanceProperties(boolean common, String name,
348             String version) throws PfModelException {
349
350         var commonOrInstanceNodeTypeProps = this.getCommonOrInstancePropertiesFromNodeTypes(common, name, version);
351
352         var serviceTemplates = new ToscaServiceTemplates();
353         serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version));
354
355         return this.getDerivedCommonOrInstanceNodeTemplates(
356                 serviceTemplates.getServiceTemplates().get(0).getToscaTopologyTemplate().getNodeTemplates(),
357                 commonOrInstanceNodeTypeProps);
358     }
359
360     /**
361      * Get the requested control loop definitions.
362      *
363      * @param name the name of the definition to get, null for all definitions
364      * @param version the version of the definition to get, null for all definitions
365      * @return the control loop definitions
366      * @throws PfModelException on errors getting control loop definitions
367      */
368     public ToscaServiceTemplate getToscaServiceTemplate(String name, String version) throws PfModelException {
369         var serviceTemplates = new ToscaServiceTemplates();
370         serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version));
371         return serviceTemplates.getServiceTemplates().get(0);
372     }
373
374     /**
375      * Get the tosca service template with only required sections.
376      *
377      * @param name the name of the template to get, null for all definitions
378      * @param version the version of the template to get, null for all definitions
379      * @return the tosca service template
380      * @throws PfModelException on errors getting tosca service template
381      */
382     public String getToscaServiceTemplateReduced(String name, String version)
383         throws PfModelException {
384
385         var serviceTemplates = new ToscaServiceTemplates();
386         serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version));
387
388         ToscaServiceTemplate fullTemplate = serviceTemplates.getServiceTemplates().get(0);
389
390         var template = new HashMap<String, Object>();
391         template.put("tosca_definitions_version", fullTemplate.getToscaDefinitionsVersion());
392         template.put("data_types", fullTemplate.getDataTypes());
393         template.put("policy_types", fullTemplate.getPolicyTypes());
394         template.put("node_types", fullTemplate.getNodeTypes());
395         template.put("topology_template", fullTemplate.getToscaTopologyTemplate());
396
397         try {
398             return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(template);
399
400         } catch (JsonProcessingException e) {
401             throw new PfModelException(Status.BAD_REQUEST, "Converion to Json Schema failed", e);
402         }
403     }
404
405     /**
406      * Get the requested json schema.
407      *
408      * @param section section of the tosca service template to get schema for
409      * @return the specified tosca service template or section Json Schema
410      * @throws PfModelException on errors with retrieving the classes
411      */
412     public String getToscaServiceTemplateSchema(String section) throws PfModelException {
413         var visitor = new SchemaFactoryWrapper();
414
415         try {
416             switch (section) {
417                 case "data_types":
418                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaDataType.class), visitor);
419                     break;
420                 case "capability_types":
421                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaCapabilityType.class), visitor);
422                     break;
423                 case "node_types":
424                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaNodeType.class), visitor);
425                     break;
426                 case "relationship_types":
427                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaRelationshipType.class), visitor);
428                     break;
429                 case "policy_types":
430                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaPolicyType.class), visitor);
431                     break;
432                 case "topology_template":
433                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaTopologyTemplate.class), visitor);
434                     break;
435                 case "node_templates":
436                     mapper.acceptJsonFormatVisitor(
437                             mapper.getTypeFactory().constructCollectionType(List.class, ToscaNodeTemplate.class),
438                             visitor);
439                     break;
440                 default:
441                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaServiceTemplate.class), visitor);
442             }
443
444             var jsonSchema = visitor.finalSchema();
445             return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema);
446         } catch (JsonProcessingException e) {
447             throw new PfModelException(Status.BAD_REQUEST, "Converion to Json Schema failed", e);
448         }
449     }
450
451     /**
452      * Validates to see if there is any instance properties saved.
453      *
454      * @return true if exists instance properties
455      */
456     private Boolean verifyIfInstancePropertiesExists() {
457         return clProvider.getNodeTemplates(null, null).stream()
458             .anyMatch(nodeTemplate -> nodeTemplate.getKey().getName().contains("_Instance"));
459
460     }
461 }