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