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