74b5394f4eb14dc69c5bafafa52ac7063d7bede0
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2021 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.clamp.controlloop.runtime.commissioning;
22
23 import com.fasterxml.jackson.core.JsonProcessingException;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.fasterxml.jackson.databind.PropertyNamingStrategies;
26 import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.stream.Collectors;
33 import javax.ws.rs.core.Response.Status;
34 import org.apache.commons.collections4.CollectionUtils;
35 import org.apache.commons.collections4.MapUtils;
36 import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
37 import org.onap.policy.clamp.controlloop.models.controlloop.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
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, ControlLoopException {
101
102         if (verifyIfInstancePropertiesExists()) {
103             throw new ControlLoopException(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,
111                     null);
112
113             if (participantList != null) {
114                 for (Participant participant: participantList) {
115                     var participantType = new ToscaConceptIdentifier();
116                     participantType.setName(participant.getType());
117                     participantType.setVersion(participant.getTypeVersion());
118
119                     var participantUpdate = new ParticipantUpdate();
120                     participantUpdate.setParticipantId(participant.getDefinition());
121                     participantUpdate.setParticipantType(participantType);
122
123                     this.supervisionHandler.handleSendCommissionMessage(participantUpdate);
124                 }
125             }
126
127         }
128
129         var response = new CommissioningResponse();
130         // @formatter:off
131         response.setAffectedControlLoopDefinitions(serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
132                 .values()
133                 .stream()
134                 .map(template -> template.getKey().asIdentifier())
135                 .collect(Collectors.toList()));
136         // @formatter:on
137
138         return response;
139     }
140
141     /**
142      * Delete the control loop definition with the given name and version.
143      *
144      * @param name the name of the control loop definition to delete
145      * @param version the version of the control loop to delete
146      * @return the result of the deletion
147      * @throws PfModelException on deletion errors
148      */
149     public CommissioningResponse deleteControlLoopDefinition(String name, String version)
150         throws PfModelException, ControlLoopException {
151
152         if (verifyIfInstancePropertiesExists()) {
153             throw new ControlLoopException(Status.BAD_REQUEST,
154                 "Delete instances, to commission control loop definitions");
155         }
156
157         synchronized (lockit) {
158             List<Participant> participantList =
159                 participantProvider.getParticipants(null,
160                     null);
161
162             if (participantList != null) {
163                 for (Participant participant : participantList) {
164                     var participantType = new ToscaConceptIdentifier();
165                     participantType.setName(participant.getType());
166                     participantType.setVersion(participant.getTypeVersion());
167
168                     var participantUpdate = new ParticipantUpdate();
169                     participantUpdate.setParticipantId(participant.getDefinition());
170                     participantUpdate.setParticipantType(participantType);
171
172                     this.supervisionHandler.handleSendDeCommissionMessage(participantUpdate);
173                 }
174             }
175
176             modelsProvider.deleteServiceTemplate(name, version);
177         }
178
179         var response = new CommissioningResponse();
180         response.setAffectedControlLoopDefinitions(
181                 Collections.singletonList(new ToscaConceptIdentifier(name, version)));
182
183         return response;
184     }
185
186     /**
187      * Get control loop node templates.
188      *
189      * @param clName the name of the control loop, null for all
190      * @param clVersion the version of the control loop, null for all
191      * @return list of control loop node templates
192      * @throws PfModelException on errors getting control loop definitions
193      */
194     public List<ToscaNodeTemplate> getControlLoopDefinitions(String clName, String clVersion) throws PfModelException {
195
196         // @formatter:off
197         ToscaTypedEntityFilter<ToscaNodeTemplate> nodeTemplateFilter = ToscaTypedEntityFilter
198                 .<ToscaNodeTemplate>builder()
199                 .name(clName)
200                 .version(clVersion)
201                 .type(CONTROL_LOOP_NODE_TYPE)
202                 .build();
203         // @formatter:on
204
205         return clProvider.getFilteredNodeTemplates(nodeTemplateFilter);
206     }
207
208     /**
209      * Get the control loop elements from a control loop node template.
210      *
211      * @param controlLoopNodeTemplate the control loop node template
212      * @return a list of the control loop element node templates in a control loop node template
213      * @throws PfModelException on errors get control loop element node templates
214      */
215     public List<ToscaNodeTemplate> getControlLoopElementDefinitions(ToscaNodeTemplate controlLoopNodeTemplate)
216             throws PfModelException {
217         if (!CONTROL_LOOP_NODE_TYPE.equals(controlLoopNodeTemplate.getType())) {
218             return Collections.emptyList();
219         }
220
221         if (MapUtils.isEmpty(controlLoopNodeTemplate.getProperties())) {
222             return Collections.emptyList();
223         }
224
225         @SuppressWarnings("unchecked")
226         List<Map<String, String>> controlLoopElements =
227                 (List<Map<String, String>>) controlLoopNodeTemplate.getProperties().get("elements");
228
229         if (CollectionUtils.isEmpty(controlLoopElements)) {
230             return Collections.emptyList();
231         }
232
233         List<ToscaNodeTemplate> controlLoopElementList = new ArrayList<>();
234         // @formatter:off
235         controlLoopElementList.addAll(
236                 controlLoopElements
237                         .stream()
238                         .map(elementMap -> clProvider.getNodeTemplates(elementMap.get("name"),
239                                 elementMap.get("version")))
240                         .flatMap(List::stream)
241                         .collect(Collectors.toList())
242         );
243         // @formatter:on
244
245         return controlLoopElementList;
246     }
247
248     /**
249      * Get the initial node types with common or instance properties.
250      *
251      * @param fullNodeTypes map of all the node types in the specified template
252      * @param common boolean to indicate whether common or instance properties are required
253      * @return node types map that only has common properties
254      * @throws PfModelException on errors getting node type with common properties
255      */
256     private Map<String, ToscaNodeType> getInitialNodeTypesMap(Map<String, ToscaNodeType> fullNodeTypes,
257             boolean common) {
258
259         var tempNodeTypesMap = new HashMap<String, ToscaNodeType>();
260
261         fullNodeTypes.forEach((key, nodeType) -> {
262             var tempToscaNodeType = new ToscaNodeType();
263             tempToscaNodeType.setName(key);
264
265             var resultantPropertyMap = findCommonOrInstancePropsInNodeTypes(nodeType, common);
266
267             if (!resultantPropertyMap.isEmpty()) {
268                 tempToscaNodeType.setProperties(resultantPropertyMap);
269                 tempNodeTypesMap.put(key, tempToscaNodeType);
270             }
271         });
272         return tempNodeTypesMap;
273     }
274
275     private Map<String, ToscaProperty> findCommonOrInstancePropsInNodeTypes(ToscaNodeType nodeType, boolean common) {
276
277         var tempCommonPropertyMap = new HashMap<String, ToscaProperty>();
278         var tempInstancePropertyMap = new HashMap<String, ToscaProperty>();
279
280         nodeType.getProperties().forEach((propKey, prop) -> {
281
282             if (prop.getMetadata() != null) {
283                 prop.getMetadata().forEach((k, v) -> {
284                     if (k.equals("common") && v.equals("true") && common) {
285                         tempCommonPropertyMap.put(propKey, prop);
286                     } else if (k.equals("common") && v.equals("false") && !common) {
287                         tempInstancePropertyMap.put(propKey, prop);
288                     }
289
290                 });
291             } else {
292                 tempInstancePropertyMap.put(propKey, prop);
293             }
294         });
295
296         if (tempCommonPropertyMap.isEmpty() && !common) {
297             return tempInstancePropertyMap;
298         } else {
299             return tempCommonPropertyMap;
300         }
301     }
302
303     /**
304      * Get the node types derived from those that have common properties.
305      *
306      * @param initialNodeTypes map of all the node types in the specified template
307      * @param filteredNodeTypes map of all the node types that have common or instance properties
308      * @return all node types that have common properties including their children
309      * @throws PfModelException on errors getting node type with common properties
310      */
311     private Map<String, ToscaNodeType> getFinalNodeTypesMap(Map<String, ToscaNodeType> initialNodeTypes,
312             Map<String, ToscaNodeType> filteredNodeTypes) {
313         for (var i = 0; i < initialNodeTypes.size(); i++) {
314             initialNodeTypes.forEach((key, nodeType) -> {
315                 var tempToscaNodeType = new ToscaNodeType();
316                 tempToscaNodeType.setName(key);
317
318                 if (filteredNodeTypes.get(nodeType.getDerivedFrom()) != null) {
319                     tempToscaNodeType.setName(key);
320
321                     var finalProps = new HashMap<String, ToscaProperty>(
322                             filteredNodeTypes.get(nodeType.getDerivedFrom()).getProperties());
323
324                     tempToscaNodeType.setProperties(finalProps);
325                 } else {
326                     return;
327                 }
328                 filteredNodeTypes.putIfAbsent(key, tempToscaNodeType);
329
330             });
331         }
332         return filteredNodeTypes;
333     }
334
335     /**
336      * Get the requested node types with common or instance properties.
337      *
338      * @param common boolean indicating common or instance properties
339      * @param name the name of the definition to get, null for all definitions
340      * @param version the version of the definition to get, null for all definitions
341      * @return the node types with common or instance properties
342      * @throws PfModelException on errors getting node type properties
343      */
344     private Map<String, ToscaNodeType> getCommonOrInstancePropertiesFromNodeTypes(boolean common, String name,
345             String version) throws PfModelException {
346         var serviceTemplates = new ToscaServiceTemplates();
347         serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version));
348         var tempNodeTypesMap =
349                 this.getInitialNodeTypesMap(serviceTemplates.getServiceTemplates().get(0).getNodeTypes(), common);
350
351         return this.getFinalNodeTypesMap(serviceTemplates.getServiceTemplates().get(0).getNodeTypes(),
352                 tempNodeTypesMap);
353
354     }
355
356     /**
357      * Get node templates with appropriate common or instance properties added.
358      *
359      * @param initialNodeTemplates map of all the node templates in the specified template
360      * @param nodeTypeProps map of all the node types that have common or instance properties including children
361      * @return all node templates with appropriate common or instance properties added
362      * @throws PfModelException on errors getting map of node templates with common or instance properties added
363      */
364     private Map<String, ToscaNodeTemplate> getDerivedCommonOrInstanceNodeTemplates(
365             Map<String, ToscaNodeTemplate> initialNodeTemplates, Map<String, ToscaNodeType> nodeTypeProps) {
366
367         var finalNodeTemplatesMap = new HashMap<String, ToscaNodeTemplate>();
368
369         initialNodeTemplates.forEach((templateKey, template) -> {
370             if (nodeTypeProps.containsKey(template.getType())) {
371                 var finalMergedProps = new HashMap<String, Object>();
372
373                 nodeTypeProps.get(template.getType()).getProperties().forEach(finalMergedProps::putIfAbsent);
374
375                 template.setProperties(finalMergedProps);
376
377                 finalNodeTemplatesMap.put(templateKey, template);
378             } else {
379                 return;
380             }
381         });
382         return finalNodeTemplatesMap;
383     }
384
385     /**
386      * Get node templates with common properties added.
387      *
388      * @param common boolean indicating common or instance properties to be used
389      * @param name the name of the definition to use, null for all definitions
390      * @param version the version of the definition to use, null for all definitions
391      * @return the nodes templates with common or instance properties
392      * @throws PfModelException on errors getting common or instance properties from node_templates
393      */
394     public Map<String, ToscaNodeTemplate> getNodeTemplatesWithCommonOrInstanceProperties(boolean common, String name,
395             String version) throws PfModelException {
396
397         var commonOrInstanceNodeTypeProps = this.getCommonOrInstancePropertiesFromNodeTypes(common, name, version);
398
399         var serviceTemplates = new ToscaServiceTemplates();
400         serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version));
401
402         return this.getDerivedCommonOrInstanceNodeTemplates(
403                 serviceTemplates.getServiceTemplates().get(0).getToscaTopologyTemplate().getNodeTemplates(),
404                 commonOrInstanceNodeTypeProps);
405     }
406
407     /**
408      * Get the requested control loop definitions.
409      *
410      * @param name the name of the definition to get, null for all definitions
411      * @param version the version of the definition to get, null for all definitions
412      * @return the control loop definitions
413      * @throws PfModelException on errors getting control loop definitions
414      */
415     public ToscaServiceTemplate getToscaServiceTemplate(String name, String version) throws PfModelException {
416         var serviceTemplates = new ToscaServiceTemplates();
417         serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version));
418         return serviceTemplates.getServiceTemplates().get(0);
419     }
420
421     /**
422      * Get the tosca service template with only required sections.
423      *
424      * @param name the name of the template to get, null for all definitions
425      * @param version the version of the template to get, null for all definitions
426      * @return the tosca service template
427      * @throws PfModelException on errors getting tosca service template
428      */
429     public String getToscaServiceTemplateReduced(String name, String version)
430         throws PfModelException {
431
432         var serviceTemplates = new ToscaServiceTemplates();
433         serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version));
434
435         ToscaServiceTemplate fullTemplate = serviceTemplates.getServiceTemplates().get(0);
436
437         var template = new HashMap<String, Object>();
438         template.put("tosca_definitions_version", fullTemplate.getToscaDefinitionsVersion());
439         template.put("data_types", fullTemplate.getDataTypes());
440         template.put("policy_types", fullTemplate.getPolicyTypes());
441         template.put("node_types", fullTemplate.getNodeTypes());
442         template.put("topology_template", fullTemplate.getToscaTopologyTemplate());
443
444         try {
445             return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(template);
446
447         } catch (JsonProcessingException e) {
448             throw new PfModelException(Status.BAD_REQUEST, "Converion to Json Schema failed", e);
449         }
450     }
451
452     /**
453      * Get the requested json schema.
454      *
455      * @param section section of the tosca service template to get schema for
456      * @return the specified tosca service template or section Json Schema
457      * @throws PfModelException on errors with retrieving the classes
458      */
459     public String getToscaServiceTemplateSchema(String section) throws PfModelException {
460         var visitor = new SchemaFactoryWrapper();
461
462         try {
463             switch (section) {
464                 case "data_types":
465                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaDataType.class), visitor);
466                     break;
467                 case "capability_types":
468                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaCapabilityType.class), visitor);
469                     break;
470                 case "node_types":
471                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaNodeType.class), visitor);
472                     break;
473                 case "relationship_types":
474                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaRelationshipType.class), visitor);
475                     break;
476                 case "policy_types":
477                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaPolicyType.class), visitor);
478                     break;
479                 case "topology_template":
480                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaTopologyTemplate.class), visitor);
481                     break;
482                 case "node_templates":
483                     mapper.acceptJsonFormatVisitor(
484                             mapper.getTypeFactory().constructCollectionType(List.class, ToscaNodeTemplate.class),
485                             visitor);
486                     break;
487                 default:
488                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaServiceTemplate.class), visitor);
489             }
490
491             var jsonSchema = visitor.finalSchema();
492             return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema);
493         } catch (JsonProcessingException e) {
494             throw new PfModelException(Status.BAD_REQUEST, "Converion to Json Schema failed", e);
495         }
496     }
497
498     /**
499      * Validates to see if there is any instance properties saved.
500      *
501      * @return true if exists instance properties
502      */
503     private Boolean verifyIfInstancePropertiesExists() {
504         return clProvider.getNodeTemplates(null, null).stream()
505             .anyMatch(nodeTemplate -> nodeTemplate.getKey().getName().contains("_Instance"));
506
507     }
508 }