726fcba5c08ced1744a81619da2136fadce03075
[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.acm.runtime.commissioning;
23
24 import com.fasterxml.jackson.core.JsonProcessingException;
25 import com.fasterxml.jackson.databind.JavaType;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import com.fasterxml.jackson.databind.PropertyNamingStrategies;
28 import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.stream.Collectors;
35 import javax.ws.rs.core.Response.Status;
36 import org.apache.commons.collections4.CollectionUtils;
37 import org.apache.commons.collections4.MapUtils;
38 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionHandler;
39 import org.onap.policy.clamp.models.acm.concepts.Participant;
40 import org.onap.policy.clamp.models.acm.messages.rest.commissioning.CommissioningResponse;
41 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
42 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
43 import org.onap.policy.clamp.models.acm.persistence.provider.ServiceTemplateProvider;
44 import org.onap.policy.models.base.PfModelException;
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.ToscaRelationshipType;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
53 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplates;
54 import org.onap.policy.models.tosca.authorative.concepts.ToscaTopologyTemplate;
55 import org.onap.policy.models.tosca.authorative.concepts.ToscaTypedEntityFilter;
56 import org.springframework.boot.context.event.ApplicationReadyEvent;
57 import org.springframework.context.event.EventListener;
58 import org.springframework.stereotype.Service;
59 import org.springframework.transaction.annotation.Transactional;
60
61 /**
62  * This class provides the create, read and delete actions on Commissioning of automation composition concepts in the
63  * database to the callers.
64  */
65 @Service
66 @Transactional
67 public class CommissioningProvider {
68     public static final String AUTOMATION_COMPOSITION_NODE_TYPE = "org.onap.policy.clamp.acm.AutomationComposition";
69     private static final String HYPHEN = "-";
70
71     private final ServiceTemplateProvider serviceTemplateProvider;
72     private final AutomationCompositionProvider acProvider;
73     private final ObjectMapper mapper = new ObjectMapper();
74     private final ParticipantProvider participantProvider;
75     private final SupervisionHandler supervisionHandler;
76
77     private static final Map<String, JavaType> sections = new HashMap<>();
78
79     /**
80      * Create a commissioning provider.
81      *
82      * @param serviceTemplateProvider the ServiceTemplate Provider
83      * @param acProvider the AutomationComposition Provider
84      * @param supervisionHandler the Supervision Handler
85      * @param participantProvider the Participant Provider
86      */
87     public CommissioningProvider(ServiceTemplateProvider serviceTemplateProvider,
88             AutomationCompositionProvider acProvider, SupervisionHandler supervisionHandler,
89             ParticipantProvider participantProvider) {
90         this.serviceTemplateProvider = serviceTemplateProvider;
91         this.acProvider = acProvider;
92         this.supervisionHandler = supervisionHandler;
93         this.participantProvider = participantProvider;
94         mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
95     }
96
97     /**
98      * Event listener initiilize function called at ApplicationReadyEvent.
99      *
100      */
101     @EventListener(ApplicationReadyEvent.class)
102     public void initialize() {
103         sections.put("data_types", mapper.constructType(ToscaDataType.class));
104         sections.put("capability_types", mapper.constructType(ToscaCapabilityType.class));
105         sections.put("node_types", mapper.constructType(ToscaNodeType.class));
106         sections.put("relationship_types", mapper.constructType(ToscaRelationshipType.class));
107         sections.put("policy_types", mapper.constructType(ToscaPolicyType.class));
108         sections.put("topology_template", mapper.constructType(ToscaTopologyTemplate.class));
109         sections.put("node_templates",
110                 mapper.getTypeFactory().constructCollectionType(List.class, ToscaNodeTemplate.class));
111         sections.put("all", mapper.constructType(ToscaServiceTemplate.class));
112     }
113
114     /**
115      * Create automation compositions from a service template.
116      *
117      * @param serviceTemplate the service template
118      * @return the result of the commissioning operation
119      * @throws PfModelException on creation errors
120      */
121     public CommissioningResponse createAutomationCompositionDefinitions(ToscaServiceTemplate serviceTemplate)
122             throws PfModelException {
123
124         if (verifyIfInstancePropertiesExists()) {
125             throw new PfModelException(Status.BAD_REQUEST,
126                     "Delete instances, to commission automation composition definitions");
127         }
128         serviceTemplate = serviceTemplateProvider.createServiceTemplate(serviceTemplate);
129         List<Participant> participantList = participantProvider.getParticipants();
130         if (!participantList.isEmpty()) {
131             supervisionHandler.handleSendCommissionMessage(serviceTemplate.getName(), serviceTemplate.getVersion());
132         }
133         var response = new CommissioningResponse();
134         // @formatter:off
135         response.setAffectedAutomationCompositionDefinitions(
136             serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
137                 .values()
138                 .stream()
139                 .map(template -> template.getKey().asIdentifier())
140                 .collect(Collectors.toList()));
141         // @formatter:on
142
143         return response;
144     }
145
146     /**
147      * Delete the automation composition definition with the given name and version.
148      *
149      * @param name the name of the automation composition definition to delete
150      * @param version the version of the automation composition to delete
151      * @return the result of the deletion
152      * @throws PfModelException on deletion errors
153      */
154     public CommissioningResponse deleteAutomationCompositionDefinition(String name, String version)
155             throws PfModelException {
156
157         if (verifyIfInstancePropertiesExists()) {
158             throw new PfModelException(Status.BAD_REQUEST,
159                     "Delete instances, to commission automation composition definitions");
160         }
161         List<Participant> participantList = participantProvider.getParticipants();
162         if (!participantList.isEmpty()) {
163             supervisionHandler.handleSendDeCommissionMessage();
164         }
165         serviceTemplateProvider.deleteServiceTemplate(name, version);
166         var response = new CommissioningResponse();
167         response.setAffectedAutomationCompositionDefinitions(List.of(new ToscaConceptIdentifier(name, version)));
168
169         return response;
170     }
171
172     /**
173      * Get automation composition node templates.
174      *
175      * @param acName the name of the automation composition, null for all
176      * @param acVersion the version of the automation composition, null for all
177      * @return list of automation composition node templates
178      * @throws PfModelException on errors getting automation composition definitions
179      */
180     @Transactional(readOnly = true)
181     public List<ToscaNodeTemplate> getAutomationCompositionDefinitions(String acName, String acVersion)
182             throws PfModelException {
183
184         // @formatter:off
185         ToscaTypedEntityFilter<ToscaNodeTemplate> nodeTemplateFilter = ToscaTypedEntityFilter
186                 .<ToscaNodeTemplate>builder()
187                 .name(acName)
188                 .version(acVersion)
189                 .type(AUTOMATION_COMPOSITION_NODE_TYPE)
190                 .build();
191         // @formatter:on
192
193         return acProvider.getFilteredNodeTemplates(nodeTemplateFilter);
194     }
195
196     /**
197      * Get the automation composition elements from a automation composition node template.
198      *
199      * @param automationCompositionNodeTemplate the automation composition node template
200      * @return a list of the automation composition element node templates in a automation composition node template
201      * @throws PfModelException on errors get automation composition element node templates
202      */
203     @Transactional(readOnly = true)
204     public List<ToscaNodeTemplate> getAutomationCompositionElementDefinitions(
205             ToscaNodeTemplate automationCompositionNodeTemplate) throws PfModelException {
206         if (!AUTOMATION_COMPOSITION_NODE_TYPE.equals(automationCompositionNodeTemplate.getType())) {
207             return Collections.emptyList();
208         }
209
210         if (MapUtils.isEmpty(automationCompositionNodeTemplate.getProperties())) {
211             return Collections.emptyList();
212         }
213
214         @SuppressWarnings("unchecked")
215         List<Map<String, String>> automationCompositionElements =
216                 (List<Map<String, String>>) automationCompositionNodeTemplate.getProperties().get("elements");
217
218         if (CollectionUtils.isEmpty(automationCompositionElements)) {
219             return Collections.emptyList();
220         }
221
222         List<ToscaNodeTemplate> automationCompositionElementList = new ArrayList<>();
223         // @formatter:off
224         automationCompositionElementList.addAll(
225                 automationCompositionElements
226                         .stream()
227                         .map(elementMap -> acProvider.getNodeTemplates(elementMap.get("name"),
228                                 elementMap.get("version")))
229                         .flatMap(List::stream)
230                         .collect(Collectors.toList())
231         );
232         // @formatter:on
233
234         return automationCompositionElementList;
235     }
236
237     /**
238      * Get node templates with common properties added.
239      *
240      * @param common boolean indicating common or instance properties to be used
241      * @param name the name of the definition to use, null for all definitions
242      * @param version the version of the definition to use, null for all definitions
243      * @return the nodes templates with common or instance properties
244      * @throws PfModelException on errors getting common or instance properties from node_templates
245      */
246     @Transactional(readOnly = true)
247     public Map<String, ToscaNodeTemplate> getNodeTemplatesWithCommonOrInstanceProperties(boolean common, String name,
248             String version) throws PfModelException {
249
250         if (common && verifyIfInstancePropertiesExists()) {
251             throw new PfModelException(Status.BAD_REQUEST,
252                     "Cannot create or edit common properties, delete all the instantiations first");
253         }
254
255         var serviceTemplateList = serviceTemplateProvider.getServiceTemplateList(name, version);
256         var commonOrInstanceNodeTypeProps =
257                 serviceTemplateProvider.getCommonOrInstancePropertiesFromNodeTypes(common, serviceTemplateList.get(0));
258
259         var serviceTemplates = new ToscaServiceTemplates();
260         serviceTemplates.setServiceTemplates(filterToscaNodeTemplateInstance(serviceTemplateList));
261
262         return serviceTemplateProvider.getDerivedCommonOrInstanceNodeTemplates(
263                 serviceTemplates.getServiceTemplates().get(0).getToscaTopologyTemplate().getNodeTemplates(),
264                 commonOrInstanceNodeTypeProps);
265     }
266
267     /**
268      * Get the requested automation composition definitions.
269      *
270      * @param name the name of the definition to get, null for all definitions
271      * @param version the version of the definition to get, null for all definitions
272      * @return the automation composition definitions
273      * @throws PfModelException on errors getting automation composition definitions
274      */
275     @Transactional(readOnly = true)
276     public ToscaServiceTemplate getToscaServiceTemplate(String name, String version) throws PfModelException {
277         return serviceTemplateProvider.getToscaServiceTemplate(name, version);
278     }
279
280     /**
281      * Get All the requested automation composition definitions.
282      *
283      * @return the automation composition definitions
284      * @throws PfModelException on errors getting automation composition definitions
285      */
286     @Transactional(readOnly = true)
287     public List<ToscaServiceTemplate> getAllToscaServiceTemplate() throws PfModelException {
288         return serviceTemplateProvider.getAllServiceTemplates();
289     }
290
291     /**
292      * Get the tosca service template with only required sections.
293      *
294      * @param name the name of the template to get, null for all definitions
295      * @param version the version of the template to get, null for all definitions
296      * @return the tosca service template
297      * @throws PfModelException on errors getting tosca service template
298      */
299     @Transactional(readOnly = true)
300     public String getToscaServiceTemplateReduced(String name, String version) throws PfModelException {
301         var serviceTemplateList = serviceTemplateProvider.getServiceTemplateList(name, version);
302
303         List<ToscaServiceTemplate> filteredServiceTemplateList = filterToscaNodeTemplateInstance(serviceTemplateList);
304
305         if (filteredServiceTemplateList.isEmpty()) {
306             throw new PfModelException(Status.BAD_REQUEST, "Invalid Service Template");
307         }
308
309         ToscaServiceTemplate fullTemplate = filteredServiceTemplateList.get(0);
310
311         var template = new HashMap<String, Object>();
312         template.put("tosca_definitions_version", fullTemplate.getToscaDefinitionsVersion());
313         template.put("data_types", fullTemplate.getDataTypes());
314         template.put("policy_types", fullTemplate.getPolicyTypes());
315         template.put("node_types", fullTemplate.getNodeTypes());
316         template.put("topology_template", fullTemplate.getToscaTopologyTemplate());
317
318         try {
319             return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(template);
320
321         } catch (JsonProcessingException e) {
322             throw new PfModelException(Status.BAD_REQUEST, "Converion to Json Schema failed", e);
323         }
324     }
325
326     /**
327      * Get the requested json schema.
328      *
329      * @param section section of the tosca service template to get schema for
330      * @return the specified tosca service template or section Json Schema
331      * @throws PfModelException on errors with retrieving the classes
332      */
333     public String getToscaServiceTemplateSchema(String section) throws PfModelException {
334         var visitor = new SchemaFactoryWrapper();
335         var sectionMapper = sections.getOrDefault(section, sections.get("all"));
336         try {
337             mapper.acceptJsonFormatVisitor(sectionMapper, visitor);
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(HYPHEN)) {
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 acProvider.getAllNodeTemplates().stream()
375                 .anyMatch(nodeTemplate -> nodeTemplate.getKey().getName().contains(HYPHEN));
376
377     }
378 }