382823b19db80604288523d4bf5b06c368074529
[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.controlloop.persistence.provider.ServiceTemplateProvider;
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.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.ToscaRelationshipType;
51 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplates;
53 import org.onap.policy.models.tosca.authorative.concepts.ToscaTopologyTemplate;
54 import org.onap.policy.models.tosca.authorative.concepts.ToscaTypedEntityFilter;
55 import org.springframework.stereotype.Service;
56 import org.springframework.transaction.annotation.Transactional;
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 @Service
63 @Transactional
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 ServiceTemplateProvider serviceTemplateProvider;
69     private final ControlLoopProvider clProvider;
70     private final ObjectMapper mapper = new ObjectMapper();
71     private final ParticipantProvider participantProvider;
72     private final SupervisionHandler supervisionHandler;
73
74     /**
75      * Create a commissioning provider.
76      *
77      * @param serviceTemplateProvider the ServiceTemplate Provider
78      * @param clProvider the ControlLoop Provider
79      * @param supervisionHandler the Supervision Handler
80      * @param participantProvider the Participant Provider
81      */
82     public CommissioningProvider(ServiceTemplateProvider serviceTemplateProvider, ControlLoopProvider clProvider,
83             SupervisionHandler supervisionHandler, ParticipantProvider participantProvider) {
84         this.serviceTemplateProvider = serviceTemplateProvider;
85         this.clProvider = clProvider;
86         this.supervisionHandler = supervisionHandler;
87         this.participantProvider = participantProvider;
88         mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
89     }
90
91     /**
92      * Create control loops from a service template.
93      *
94      * @param serviceTemplate the service template
95      * @return the result of the commissioning operation
96      * @throws PfModelException on creation errors
97      */
98     public CommissioningResponse createControlLoopDefinitions(ToscaServiceTemplate serviceTemplate)
99             throws PfModelException {
100
101         if (verifyIfInstancePropertiesExists()) {
102             throw new PfModelException(Status.BAD_REQUEST, "Delete instances, to commission control loop definitions");
103         }
104         serviceTemplate = serviceTemplateProvider.createServiceTemplate(serviceTemplate);
105         List<Participant> participantList = participantProvider.getParticipants();
106         if (!participantList.isEmpty()) {
107             supervisionHandler.handleSendCommissionMessage(serviceTemplate.getName(), serviceTemplate.getVersion());
108         }
109         var response = new CommissioningResponse();
110         // @formatter:off
111         response.setAffectedControlLoopDefinitions(serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
112                 .values()
113                 .stream()
114                 .map(template -> template.getKey().asIdentifier())
115                 .collect(Collectors.toList()));
116         // @formatter:on
117
118         return response;
119     }
120
121     /**
122      * Delete the control loop definition with the given name and version.
123      *
124      * @param name the name of the control loop definition to delete
125      * @param version the version of the control loop to delete
126      * @return the result of the deletion
127      * @throws PfModelException on deletion errors
128      */
129     public CommissioningResponse deleteControlLoopDefinition(String name, String version) throws PfModelException {
130
131         if (verifyIfInstancePropertiesExists()) {
132             throw new PfModelException(Status.BAD_REQUEST, "Delete instances, to commission control loop definitions");
133         }
134         List<Participant> participantList = participantProvider.getParticipants();
135         if (!participantList.isEmpty()) {
136             supervisionHandler.handleSendDeCommissionMessage();
137         }
138         serviceTemplateProvider.deleteServiceTemplate(name, version);
139         var response = new CommissioningResponse();
140         response.setAffectedControlLoopDefinitions(List.of(new ToscaConceptIdentifier(name, version)));
141
142         return response;
143     }
144
145     /**
146      * Get control loop node templates.
147      *
148      * @param clName the name of the control loop, null for all
149      * @param clVersion the version of the control loop, null for all
150      * @return list of control loop node templates
151      * @throws PfModelException on errors getting control loop definitions
152      */
153     @Transactional(readOnly = true)
154     public List<ToscaNodeTemplate> getControlLoopDefinitions(String clName, String clVersion) throws PfModelException {
155
156         // @formatter:off
157         ToscaTypedEntityFilter<ToscaNodeTemplate> nodeTemplateFilter = ToscaTypedEntityFilter
158                 .<ToscaNodeTemplate>builder()
159                 .name(clName)
160                 .version(clVersion)
161                 .type(CONTROL_LOOP_NODE_TYPE)
162                 .build();
163         // @formatter:on
164
165         return clProvider.getFilteredNodeTemplates(nodeTemplateFilter);
166     }
167
168     /**
169      * Get the control loop elements from a control loop node template.
170      *
171      * @param controlLoopNodeTemplate the control loop node template
172      * @return a list of the control loop element node templates in a control loop node template
173      * @throws PfModelException on errors get control loop element node templates
174      */
175     @Transactional(readOnly = true)
176     public List<ToscaNodeTemplate> getControlLoopElementDefinitions(ToscaNodeTemplate controlLoopNodeTemplate)
177             throws PfModelException {
178         if (!CONTROL_LOOP_NODE_TYPE.equals(controlLoopNodeTemplate.getType())) {
179             return Collections.emptyList();
180         }
181
182         if (MapUtils.isEmpty(controlLoopNodeTemplate.getProperties())) {
183             return Collections.emptyList();
184         }
185
186         @SuppressWarnings("unchecked")
187         List<Map<String, String>> controlLoopElements =
188                 (List<Map<String, String>>) controlLoopNodeTemplate.getProperties().get("elements");
189
190         if (CollectionUtils.isEmpty(controlLoopElements)) {
191             return Collections.emptyList();
192         }
193
194         List<ToscaNodeTemplate> controlLoopElementList = new ArrayList<>();
195         // @formatter:off
196         controlLoopElementList.addAll(
197                 controlLoopElements
198                         .stream()
199                         .map(elementMap -> clProvider.getNodeTemplates(elementMap.get("name"),
200                                 elementMap.get("version")))
201                         .flatMap(List::stream)
202                         .collect(Collectors.toList())
203         );
204         // @formatter:on
205
206         return controlLoopElementList;
207     }
208
209     /**
210      * Get node templates with common properties added.
211      *
212      * @param common boolean indicating common or instance properties to be used
213      * @param name the name of the definition to use, null for all definitions
214      * @param version the version of the definition to use, null for all definitions
215      * @return the nodes templates with common or instance properties
216      * @throws PfModelException on errors getting common or instance properties from node_templates
217      */
218     @Transactional(readOnly = true)
219     public Map<String, ToscaNodeTemplate> getNodeTemplatesWithCommonOrInstanceProperties(boolean common, String name,
220             String version) throws PfModelException {
221
222         if (common && verifyIfInstancePropertiesExists()) {
223             throw new PfModelException(Status.BAD_REQUEST,
224                     "Cannot create or edit common properties, delete all the instantiations first");
225         }
226
227         var serviceTemplateList = serviceTemplateProvider.getServiceTemplateList(name, version);
228         var commonOrInstanceNodeTypeProps =
229                 serviceTemplateProvider.getCommonOrInstancePropertiesFromNodeTypes(common, serviceTemplateList.get(0));
230
231         var serviceTemplates = new ToscaServiceTemplates();
232         serviceTemplates.setServiceTemplates(filterToscaNodeTemplateInstance(serviceTemplateList));
233
234         return serviceTemplateProvider.getDerivedCommonOrInstanceNodeTemplates(
235                 serviceTemplates.getServiceTemplates().get(0).getToscaTopologyTemplate().getNodeTemplates(),
236                 commonOrInstanceNodeTypeProps);
237     }
238
239     /**
240      * Get the requested control loop definitions.
241      *
242      * @param name the name of the definition to get, null for all definitions
243      * @param version the version of the definition to get, null for all definitions
244      * @return the control loop definitions
245      * @throws PfModelException on errors getting control loop definitions
246      */
247     @Transactional(readOnly = true)
248     public ToscaServiceTemplate getToscaServiceTemplate(String name, String version) throws PfModelException {
249         return serviceTemplateProvider.getToscaServiceTemplate(name, version);
250     }
251
252     /**
253      * Get All the requested control loop definitions.
254      *
255      * @return the control loop definitions
256      * @throws PfModelException on errors getting control loop definitions
257      */
258     @Transactional(readOnly = true)
259     public List<ToscaServiceTemplate> getAllToscaServiceTemplate() throws PfModelException {
260         return serviceTemplateProvider.getAllServiceTemplates();
261     }
262
263     /**
264      * Get the tosca service template with only required sections.
265      *
266      * @param name the name of the template to get, null for all definitions
267      * @param version the version of the template to get, null for all definitions
268      * @return the tosca service template
269      * @throws PfModelException on errors getting tosca service template
270      */
271     @Transactional(readOnly = true)
272     public String getToscaServiceTemplateReduced(String name, String version) throws PfModelException {
273         var serviceTemplateList = serviceTemplateProvider.getServiceTemplateList(name, version);
274
275         List<ToscaServiceTemplate> filteredServiceTemplateList = filterToscaNodeTemplateInstance(serviceTemplateList);
276
277         if (filteredServiceTemplateList.isEmpty()) {
278             throw new PfModelException(Status.BAD_REQUEST, "Invalid Service Template");
279         }
280
281         ToscaServiceTemplate fullTemplate = filteredServiceTemplateList.get(0);
282
283         var template = new HashMap<String, Object>();
284         template.put("tosca_definitions_version", fullTemplate.getToscaDefinitionsVersion());
285         template.put("data_types", fullTemplate.getDataTypes());
286         template.put("policy_types", fullTemplate.getPolicyTypes());
287         template.put("node_types", fullTemplate.getNodeTypes());
288         template.put("topology_template", fullTemplate.getToscaTopologyTemplate());
289
290         try {
291             return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(template);
292
293         } catch (JsonProcessingException e) {
294             throw new PfModelException(Status.BAD_REQUEST, "Converion to Json Schema failed", e);
295         }
296     }
297
298     /**
299      * Get the requested json schema.
300      *
301      * @param section section of the tosca service template to get schema for
302      * @return the specified tosca service template or section Json Schema
303      * @throws PfModelException on errors with retrieving the classes
304      */
305     @Transactional(readOnly = true)
306     public String getToscaServiceTemplateSchema(String section) throws PfModelException {
307         var visitor = new SchemaFactoryWrapper();
308
309         try {
310             switch (section) {
311                 case "data_types":
312                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaDataType.class), visitor);
313                     break;
314                 case "capability_types":
315                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaCapabilityType.class), visitor);
316                     break;
317                 case "node_types":
318                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaNodeType.class), visitor);
319                     break;
320                 case "relationship_types":
321                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaRelationshipType.class), visitor);
322                     break;
323                 case "policy_types":
324                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaPolicyType.class), visitor);
325                     break;
326                 case "topology_template":
327                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaTopologyTemplate.class), visitor);
328                     break;
329                 case "node_templates":
330                     mapper.acceptJsonFormatVisitor(
331                             mapper.getTypeFactory().constructCollectionType(List.class, ToscaNodeTemplate.class),
332                             visitor);
333                     break;
334                 default:
335                     mapper.acceptJsonFormatVisitor(mapper.constructType(ToscaServiceTemplate.class), visitor);
336             }
337
338             var jsonSchema = visitor.finalSchema();
339             return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema);
340         } catch (JsonProcessingException e) {
341             throw new PfModelException(Status.BAD_REQUEST, "Converion to Json Schema failed", e);
342         }
343     }
344
345     private List<ToscaServiceTemplate> filterToscaNodeTemplateInstance(List<ToscaServiceTemplate> serviceTemplates) {
346
347         List<ToscaServiceTemplate> toscaServiceTemplates = new ArrayList<>();
348
349         serviceTemplates.stream().forEach(serviceTemplate -> {
350
351             Map<String, ToscaNodeTemplate> toscaNodeTemplates = new HashMap<>();
352
353             serviceTemplate.getToscaTopologyTemplate().getNodeTemplates().forEach((key, nodeTemplate) -> {
354                 if (!nodeTemplate.getName().contains(INSTANCE_TEXT)) {
355                     toscaNodeTemplates.put(key, nodeTemplate);
356                 }
357             });
358
359             serviceTemplate.getToscaTopologyTemplate().getNodeTemplates().clear();
360             serviceTemplate.getToscaTopologyTemplate().setNodeTemplates(toscaNodeTemplates);
361
362             toscaServiceTemplates.add(serviceTemplate);
363         });
364
365         return toscaServiceTemplates;
366     }
367
368     /**
369      * Validates to see if there is any instance properties saved.
370      *
371      * @return true if exists instance properties
372      */
373     private boolean verifyIfInstancePropertiesExists() {
374         return clProvider.getAllNodeTemplates().stream()
375                 .anyMatch(nodeTemplate -> nodeTemplate.getKey().getName().contains(INSTANCE_TEXT));
376
377     }
378 }