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