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