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