push addional code
[sdc.git] / openecomp-be / backend / openecomp-sdc-vendor-software-product-manager / src / main / java / org / openecomp / sdc / vendorsoftwareproduct / services / CompositionDataExtractor.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.sdc.vendorsoftwareproduct.services;
22
23 import org.apache.commons.collections4.CollectionUtils;
24 import org.apache.commons.collections4.MapUtils;
25 import org.openecomp.sdc.common.errors.CoreException;
26 import org.openecomp.sdc.tosca.datatypes.ToscaCapabilityType;
27 import org.openecomp.sdc.tosca.datatypes.ToscaFunctions;
28 import org.openecomp.sdc.tosca.datatypes.ToscaNodeType;
29 import org.openecomp.sdc.tosca.datatypes.ToscaRelationshipType;
30 import org.openecomp.sdc.tosca.datatypes.ToscaServiceModel;
31 import org.openecomp.sdc.tosca.datatypes.model.NodeTemplate;
32 import org.openecomp.sdc.tosca.datatypes.model.ParameterDefinition;
33 import org.openecomp.sdc.tosca.datatypes.model.RequirementAssignment;
34 import org.openecomp.sdc.tosca.datatypes.model.ServiceTemplate;
35 import org.openecomp.sdc.tosca.errors.ToscaInvalidEntryNotFoundErrorBuilder;
36 import org.openecomp.sdc.tosca.errors.ToscaInvalidSubstituteNodeTemplateErrorBuilder;
37 import org.openecomp.sdc.tosca.errors.ToscaMissingSubstitutionMappingForReqCapErrorBuilder;
38 import org.openecomp.sdc.tosca.services.ToscaAnalyzerService;
39 import org.openecomp.sdc.tosca.services.ToscaConstants;
40 import org.openecomp.sdc.tosca.services.impl.ToscaAnalyzerServiceImpl;
41 import org.openecomp.sdc.tosca.services.yamlutil.ToscaExtensionYamlUtil;
42 import org.openecomp.sdc.vendorsoftwareproduct.types.ExtractCompositionDataContext;
43 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Component;
44 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.ComponentData;
45 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.CompositionData;
46 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Network;
47 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Nic;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import java.util.ArrayList;
52 import java.util.HashMap;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Optional;
56
57 /**
58  * The type Composition data extractor.
59  */
60 public class CompositionDataExtractor {
61
62   /**
63    * The constant logger.
64    */
65   protected static Logger logger;
66   private static ToscaAnalyzerService toscaAnalyzerService;
67
68   static {
69     logger = LoggerFactory.getLogger(CompositionDataExtractor.class);
70     toscaAnalyzerService = new ToscaAnalyzerServiceImpl();
71   }
72
73   /**
74    * Extract service composition data composition data.
75    *
76    * @param toscaServiceModel the tosca service model
77    * @return the composition data
78    */
79   public static CompositionData extractServiceCompositionData(ToscaServiceModel toscaServiceModel) {
80     ExtractCompositionDataContext context = new ExtractCompositionDataContext();
81     String entryDefinitionServiceTemplateFileName =
82         toscaServiceModel.getEntryDefinitionServiceTemplate();
83     ServiceTemplate entryDefinitionServiceTemplate =
84         toscaServiceModel.getServiceTemplates().get(entryDefinitionServiceTemplateFileName);
85     extractServiceCompositionData(entryDefinitionServiceTemplateFileName,
86         entryDefinitionServiceTemplate, toscaServiceModel, context);
87
88     CompositionData compositionData = new CompositionData();
89     compositionData.setNetworks(context.getNetworks());
90     compositionData.setComponents(context.getComponents());
91     return compositionData;
92   }
93
94   private static void extractServiceCompositionData(String serviceTemplateFileName,
95                                                     ServiceTemplate serviceTemplate,
96                                                     ToscaServiceModel toscaServiceModel,
97                                                     ExtractCompositionDataContext context) {
98     if (context.getHandledServiceTemplates().contains(serviceTemplateFileName)) {
99       return;
100     }
101     context.addNetworks(extractNetworks(serviceTemplate, toscaServiceModel));
102     extractComponents(serviceTemplate, toscaServiceModel, context);
103     handleSubstitution(serviceTemplate, toscaServiceModel, context);
104     context.addHandledServiceTemplates(serviceTemplateFileName);
105   }
106
107   private static void handleSubstitution(ServiceTemplate serviceTemplate,
108                                          ToscaServiceModel toscaServiceModel,
109                                          ExtractCompositionDataContext context) {
110     Map<String, NodeTemplate> substitutableNodeTemplates =
111         toscaAnalyzerService.getSubstitutableNodeTemplates(serviceTemplate);
112
113     if (substitutableNodeTemplates != null) {
114       for (String substitutableNodeTemplateId : substitutableNodeTemplates.keySet()) {
115         handleSubstitutableNodeTemplate(serviceTemplate, toscaServiceModel,
116             substitutableNodeTemplateId,
117             substitutableNodeTemplates.get(substitutableNodeTemplateId), context);
118       }
119     }
120   }
121
122   private static void handleSubstitutableNodeTemplate(ServiceTemplate serviceTemplate,
123                                                       ToscaServiceModel toscaServiceModel,
124                                                       String substitutableNodeTemplateId,
125                                                       NodeTemplate substitutableNodeTemplate,
126                                                       ExtractCompositionDataContext context) {
127     ToscaExtensionYamlUtil toscaExtensionYamlUtil = new ToscaExtensionYamlUtil();
128     Optional<String> substituteServiceTemplateFileName = toscaAnalyzerService
129         .getSubstituteServiceTemplateName(substitutableNodeTemplateId, substitutableNodeTemplate);
130     if (!substituteServiceTemplateFileName.isPresent()) {
131       throw new CoreException(
132           new ToscaInvalidSubstituteNodeTemplateErrorBuilder(substitutableNodeTemplateId).build());
133     }
134     if (context.getHandledServiceTemplates().contains(substituteServiceTemplateFileName.get())) {
135       return;
136     }
137
138     ServiceTemplate substituteServiceTemplate =
139         toscaServiceModel.getServiceTemplates().get(substituteServiceTemplateFileName.get());
140     extractServiceCompositionData(substituteServiceTemplateFileName.get(),
141         substituteServiceTemplate, toscaServiceModel, context);
142
143     List<Map<String, RequirementAssignment>> substitutableRequirements =
144         substitutableNodeTemplate.getRequirements();
145
146     if (CollectionUtils.isEmpty(substitutableRequirements)) {
147       return;
148     }
149
150     for (Map<String, RequirementAssignment> substitutableReq : substitutableRequirements) {
151       substitutableReq.keySet().stream().filter(reqId -> {
152         RequirementAssignment reqAssignment = toscaExtensionYamlUtil
153             .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
154                 RequirementAssignment.class);
155         return isLinkToNetworkRequirementAssignment(reqAssignment);
156       }).forEach(reqId -> {
157         RequirementAssignment linkToNetworkRequirement = toscaExtensionYamlUtil
158             .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
159                 RequirementAssignment.class);
160         String connectedNodeId = linkToNetworkRequirement.getNode();
161         Optional<NodeTemplate> connectedNodeTemplate =
162             toscaAnalyzerService.getNodeTemplateById(serviceTemplate, connectedNodeId);
163
164         if (connectedNodeTemplate.isPresent() && toscaAnalyzerService
165             .isTypeOf(connectedNodeTemplate.get(), ToscaNodeType.NETWORK.getDisplayName(),
166                 serviceTemplate, toscaServiceModel)) {
167           Optional<Map.Entry<String, NodeTemplate>> mappedNodeTemplate = toscaAnalyzerService
168               .getSubstitutionMappedNodeTemplateByExposedReq(
169                   substituteServiceTemplateFileName.get(), substituteServiceTemplate, reqId);
170           if (!mappedNodeTemplate.isPresent()) {
171             throw new CoreException(new ToscaMissingSubstitutionMappingForReqCapErrorBuilder(
172                 ToscaMissingSubstitutionMappingForReqCapErrorBuilder.MappingExposedEntry
173                     .REQUIREMENT, connectedNodeId).build());
174           }
175
176           if (toscaAnalyzerService.isTypeOf(mappedNodeTemplate.get().getValue(),
177               ToscaNodeType.NETWORK_PORT.getDisplayName(), serviceTemplate, toscaServiceModel)) {
178             Nic port = context.getNics().get(mappedNodeTemplate.get().getKey());
179             if (port != null) {
180               port.setNetworkName(connectedNodeId);
181             } else {
182               logger.warn(
183                   "Different ports define for the same component which is used in different "
184                       + "substitution service templates.");
185             }
186           }
187         } else if (!connectedNodeTemplate.isPresent()) {
188           throw new CoreException(
189               new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", connectedNodeId).build());
190         }
191       });
192     }
193   }
194
195   private static boolean isLinkToNetworkRequirementAssignment(RequirementAssignment requirement) {
196     return toscaAnalyzerService.isDesiredRequirementAssignment(requirement,
197         ToscaCapabilityType.NETWORK_LINKABLE.getDisplayName(), null,
198         ToscaRelationshipType.NETWORK_LINK_TO.getDisplayName());
199   }
200
201
202   private static void connectPortToNetwork(Nic port, NodeTemplate portNodeTemplate) {
203     List<RequirementAssignment> linkRequirementsToNetwork =
204         toscaAnalyzerService.getRequirements(portNodeTemplate, ToscaConstants.LINK_REQUIREMENT_ID);
205
206     //port is connected to one network
207     for (RequirementAssignment linkRequirementToNetwork : linkRequirementsToNetwork) {
208       port.setNetworkName(linkRequirementToNetwork.getNode());
209     }
210
211   }
212
213   /*
214   return Map with key - compute node template id, value - list of connected port node template id
215    */
216   private static Map<String, List<String>> getComputeToPortsConnection(
217       Map<String, NodeTemplate> portNodeTemplates) {
218     Map<String, List<String>> computeToPortConnection = new HashMap<>();
219     if (MapUtils.isEmpty(portNodeTemplates)) {
220       return computeToPortConnection;
221     }
222     for (String portId : portNodeTemplates.keySet()) {
223       List<RequirementAssignment> bindingRequirementsToCompute = toscaAnalyzerService
224           .getRequirements(portNodeTemplates.get(portId), ToscaConstants.BINDING_REQUIREMENT_ID);
225       for (RequirementAssignment bindingRequirementToCompute : bindingRequirementsToCompute) {
226         computeToPortConnection
227             .putIfAbsent(bindingRequirementToCompute.getNode(), new ArrayList<>());
228         computeToPortConnection.get(bindingRequirementToCompute.getNode()).add(portId);
229       }
230
231     }
232
233     return computeToPortConnection;
234   }
235
236   private static void extractComponents(ServiceTemplate serviceTemplate,
237                                         ToscaServiceModel toscaServiceModel,
238                                         ExtractCompositionDataContext context) {
239     Map<String, NodeTemplate> computeNodeTemplates = toscaAnalyzerService
240         .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.COMPUTE.getDisplayName(),
241             toscaServiceModel);
242     if (MapUtils.isEmpty(computeNodeTemplates)) {
243       return;
244     }
245     Map<String, NodeTemplate> portNodeTemplates = toscaAnalyzerService
246         .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NETWORK_PORT.getDisplayName(),
247             toscaServiceModel);
248     Map<String, List<String>> computeToPortsConnection =
249         getComputeToPortsConnection(portNodeTemplates);
250     Map<String, List<String>> computesGroupedByType =
251         getNodeTemplatesGroupedByType(computeNodeTemplates);
252
253     computesGroupedByType.keySet()
254         .stream()
255         .filter(nodeType ->
256             !context.getCreatedComponents().contains(nodeType))
257         .forEach(nodeType -> extractComponent(serviceTemplate, computeToPortsConnection,
258             computesGroupedByType, nodeType, context));
259   }
260
261   private static void extractComponent(ServiceTemplate serviceTemplate,
262                                        Map<String, List<String>> computeToPortsConnection,
263                                        Map<String, List<String>> computesGroupedByType,
264                                        String computeNodeType,
265                                        ExtractCompositionDataContext context) {
266     ComponentData component = new ComponentData();
267     component.setName(computeNodeType);
268     component.setDisplayName(getComponentDisplayName(component.getName()));
269     Component componentModel = new Component();
270     componentModel.setData(component);
271
272     String computeId = computesGroupedByType.get(computeNodeType).get(0);
273     List<String> connectedPortIds = computeToPortsConnection.get(computeId);
274
275     if (connectedPortIds != null) {
276       componentModel.setNics(new ArrayList<>());
277       for (String portId : connectedPortIds) {
278         Nic port = extractPort(serviceTemplate, portId);
279         componentModel.getNics().add(port);
280         context.addNic(portId, port);
281       }
282     }
283     context.addComponent(componentModel);
284     context.getCreatedComponents().add(computeNodeType);
285   }
286
287   private static Nic extractPort(ServiceTemplate serviceTemplate, String portNodeTemplateId) {
288     Optional<NodeTemplate> portNodeTemplate =
289         toscaAnalyzerService.getNodeTemplateById(serviceTemplate, portNodeTemplateId);
290     if (portNodeTemplate.isPresent()) {
291       Nic port = new Nic();
292       port.setName(portNodeTemplateId);
293       connectPortToNetwork(port, portNodeTemplate.get());
294       return port;
295     } else {
296       throw new CoreException(
297           new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", portNodeTemplateId).build());
298     }
299   }
300
301
302   private static Map<String, List<String>> getNodeTemplatesGroupedByType(
303       Map<String, NodeTemplate> nodeTemplates) {
304     Map<String, List<String>> nodeTemplatesGrouped =
305         new HashMap<>();   //key - node type, value - list of node ids with this type
306     for (String nodeId : nodeTemplates.keySet()) {
307       String nodeType = nodeTemplates.get(nodeId).getType();
308       nodeTemplatesGrouped.putIfAbsent(nodeType, new ArrayList<>());
309       nodeTemplatesGrouped.get(nodeType).add(nodeId);
310     }
311     return nodeTemplatesGrouped;
312   }
313
314   private static List<Network> extractNetworks(ServiceTemplate serviceTemplate,
315                                                ToscaServiceModel toscaServiceModel) {
316     List<Network> networks = new ArrayList<>();
317     Map<String, NodeTemplate> networkNodeTemplates = toscaAnalyzerService
318         .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NETWORK.getDisplayName(),
319             toscaServiceModel);
320     if (MapUtils.isEmpty(networkNodeTemplates)) {
321       return networks;
322     }
323     for (String networkId : networkNodeTemplates.keySet()) {
324       Network network = new Network();
325       network.setName(networkId);
326       Optional<Boolean> networkDhcpValue =
327           getNetworkDhcpValue(serviceTemplate, networkNodeTemplates.get(networkId));
328       network.setDhcp(networkDhcpValue.isPresent() ? networkDhcpValue.get() : true);
329       networks.add(network);
330     }
331
332     return networks;
333   }
334
335   //dhcp default value is true
336   private static Optional<Boolean> getNetworkDhcpValue(ServiceTemplate serviceTemplate,
337                                                        NodeTemplate networkNodeTemplate) {
338     if (networkNodeTemplate == null) {
339       return Optional.empty();
340     }
341     if (networkNodeTemplate.getProperties() == null
342         || networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME)
343         == null) {
344       return Optional.of(true);
345     }
346
347     Object dhcp =
348         networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME);
349     if (dhcp instanceof String) {
350       return Optional.of(Boolean.valueOf((String) dhcp));
351     } else if (dhcp instanceof Boolean) {
352       return Optional.of((Boolean) dhcp);
353     } else if (dhcp instanceof Map) {
354       String inputParameterName =
355           (String) ((Map) dhcp).get(ToscaFunctions.GET_INPUT.getDisplayName());
356       if (inputParameterName != null) {
357         ParameterDefinition inputParameterDefinition =
358             serviceTemplate.getTopology_template().getInputs().get(inputParameterName);
359         if (inputParameterDefinition != null) {
360           if (inputParameterDefinition.get_default() != null) {
361             return Optional.of(Boolean.valueOf(inputParameterDefinition.get_default().toString()));
362           }
363         } else {
364           throw new CoreException(
365               new ToscaInvalidEntryNotFoundErrorBuilder("Input Parameter", inputParameterName)
366                   .build());
367         }
368       }
369     }
370
371     return Optional.of(true);
372   }
373
374   private static String getComponentDisplayName(String componentName) {
375     if (componentName == null) {
376       return null;
377     }
378     String delimiterChar = ".";
379     if (componentName.contains(delimiterChar)) {
380       return componentName.substring(componentName.lastIndexOf(delimiterChar) + 1);
381     }
382     return componentName;
383
384   }
385
386 }