0e76d8a01b5b800150ef8693a42216ac2d63056c
[sdc.git] /
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.openecomp.sdc.vendorsoftwareproduct.services.impl.composition;
18
19 import com.fasterxml.jackson.databind.ObjectMapper;
20 import org.apache.commons.collections4.CollectionUtils;
21 import org.apache.commons.collections4.MapUtils;
22 import org.openecomp.sdc.common.errors.CoreException;
23 import org.openecomp.sdc.logging.api.Logger;
24 import org.openecomp.sdc.logging.api.LoggerFactory;
25 import org.openecomp.sdc.tosca.datatypes.ToscaCapabilityType;
26 import org.openecomp.sdc.tosca.datatypes.ToscaFunctions;
27 import org.openecomp.sdc.tosca.datatypes.ToscaNodeType;
28 import org.openecomp.sdc.tosca.datatypes.ToscaRelationshipType;
29 import org.openecomp.sdc.tosca.datatypes.ToscaServiceModel;
30 import org.onap.sdc.tosca.datatypes.model.NodeTemplate;
31 import org.onap.sdc.tosca.datatypes.model.ParameterDefinition;
32 import org.onap.sdc.tosca.datatypes.model.RequirementAssignment;
33 import org.onap.sdc.tosca.datatypes.model.ServiceTemplate;
34 import org.openecomp.sdc.tosca.errors.ToscaInvalidEntryNotFoundErrorBuilder;
35 import org.openecomp.sdc.tosca.errors.ToscaInvalidSubstituteNodeTemplateErrorBuilder;
36 import org.openecomp.sdc.tosca.errors.ToscaMissingSubstitutionMappingForReqCapErrorBuilder;
37 import org.openecomp.sdc.tosca.services.ToscaAnalyzerService;
38 import org.openecomp.sdc.tosca.services.ToscaConstants;
39 import org.onap.sdc.tosca.services.ToscaExtensionYamlUtil;
40 import org.openecomp.sdc.tosca.services.impl.ToscaAnalyzerServiceImpl;
41 import org.openecomp.sdc.vendorsoftwareproduct.services.composition.CompositionDataExtractor;
42 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Component;
43 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.ComponentData;
44 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.CompositionData;
45 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.ComputeData;
46 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.ExtractCompositionDataContext;
47 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Image;
48 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Network;
49 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Nic;
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.Objects;
56 import java.util.Optional;
57 import java.util.stream.Collectors;
58
59 public class CompositionDataExtractorImpl implements CompositionDataExtractor {
60
61   protected static Logger logger;
62   private static ToscaAnalyzerService toscaAnalyzerService;
63   static {
64     logger = LoggerFactory.getLogger(CompositionDataExtractorImpl.class);
65     toscaAnalyzerService = new ToscaAnalyzerServiceImpl();
66   }
67
68   /**
69    * Extract service composition data composition data.
70    *
71    * @param toscaServiceModel the tosca service model
72    * @return the composition data
73    */
74   public CompositionData extractServiceCompositionData(ToscaServiceModel toscaServiceModel) {
75     ExtractCompositionDataContext context = new ExtractCompositionDataContext();
76     String entryDefinitionServiceTemplateFileName =
77         toscaServiceModel.getEntryDefinitionServiceTemplate();
78     ServiceTemplate entryDefinitionServiceTemplate =
79         toscaServiceModel.getServiceTemplates().get(entryDefinitionServiceTemplateFileName);
80     extractServiceCompositionData(entryDefinitionServiceTemplateFileName,
81         entryDefinitionServiceTemplate, toscaServiceModel, context);
82
83     CompositionData compositionData = new CompositionData();
84     compositionData.setNetworks(context.getNetworks());
85     compositionData.setComponents(context.getComponents());
86     return compositionData;
87   }
88
89   private void extractServiceCompositionData(String serviceTemplateFileName,
90                                                     ServiceTemplate serviceTemplate,
91                                                     ToscaServiceModel toscaServiceModel,
92                                                     ExtractCompositionDataContext context) {
93     if (context.getHandledServiceTemplates().contains(serviceTemplateFileName)) {
94       return;
95     }
96     context.addNetworks(extractNetworks(serviceTemplate, toscaServiceModel));
97     extractComponents(serviceTemplate, toscaServiceModel, context);
98     handleSubstitution(serviceTemplate, toscaServiceModel, context);
99     context.addHandledServiceTemplates(serviceTemplateFileName);
100   }
101
102   private void handleSubstitution(ServiceTemplate serviceTemplate,
103                                          ToscaServiceModel toscaServiceModel,
104                                          ExtractCompositionDataContext context) {
105     Map<String, NodeTemplate> substitutableNodeTemplates =
106         toscaAnalyzerService.getSubstitutableNodeTemplates(serviceTemplate);
107
108     if (substitutableNodeTemplates != null) {
109       for (String substitutableNodeTemplateId : substitutableNodeTemplates.keySet()) {
110         handleSubstitutableNodeTemplate(serviceTemplate, toscaServiceModel,
111             substitutableNodeTemplateId,
112             substitutableNodeTemplates.get(substitutableNodeTemplateId), context);
113       }
114     }
115   }
116
117   private void handleSubstitutableNodeTemplate(ServiceTemplate serviceTemplate,
118                                                       ToscaServiceModel toscaServiceModel,
119                                                       String substitutableNodeTemplateId,
120                                                       NodeTemplate substitutableNodeTemplate,
121                                                       ExtractCompositionDataContext context) {
122     ToscaExtensionYamlUtil toscaExtensionYamlUtil = new ToscaExtensionYamlUtil();
123     Optional<String> substituteServiceTemplateFileName = toscaAnalyzerService
124         .getSubstituteServiceTemplateName(substitutableNodeTemplateId, substitutableNodeTemplate);
125     if (!substituteServiceTemplateFileName.isPresent()) {
126       throw new CoreException(
127           new ToscaInvalidSubstituteNodeTemplateErrorBuilder(substitutableNodeTemplateId).build());
128     }
129     if (context.getHandledServiceTemplates().contains(substituteServiceTemplateFileName.get())) {
130       //each substitution is should be handled once, and will get the connection to the upper
131       // service level according to the first one which was processed
132       return;
133     }
134
135     ServiceTemplate substituteServiceTemplate =
136         toscaServiceModel.getServiceTemplates().get(substituteServiceTemplateFileName.get());
137     extractServiceCompositionData(substituteServiceTemplateFileName.get(),
138         substituteServiceTemplate, toscaServiceModel, context);
139
140     List<Map<String, RequirementAssignment>> substitutableRequirements =
141         substitutableNodeTemplate.getRequirements();
142
143     if (CollectionUtils.isEmpty(substitutableRequirements)) {
144       return;
145     }
146
147     for (Map<String, RequirementAssignment> substitutableReq : substitutableRequirements) {
148       substitutableReq.keySet().stream().filter(reqId -> {
149         RequirementAssignment reqAssignment = toscaExtensionYamlUtil
150             .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
151                 RequirementAssignment.class);
152         return isLinkToNetworkRequirementAssignment(reqAssignment);
153       }).forEach(reqId -> {
154         RequirementAssignment linkToNetworkRequirement = toscaExtensionYamlUtil
155             .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
156                 RequirementAssignment.class);
157         String connectedNodeId = linkToNetworkRequirement.getNode();
158         Optional<NodeTemplate> connectedNodeTemplate =
159             toscaAnalyzerService.getNodeTemplateById(serviceTemplate, connectedNodeId);
160
161         if (connectedNodeTemplate.isPresent() && toscaAnalyzerService
162             .isTypeOf(connectedNodeTemplate.get(), ToscaNodeType.NATIVE_NETWORK,
163                 serviceTemplate, toscaServiceModel)) {
164           Optional<Map.Entry<String, NodeTemplate>> mappedNodeTemplate = toscaAnalyzerService
165               .getSubstitutionMappedNodeTemplateByExposedReq(
166                   substituteServiceTemplateFileName.get(), substituteServiceTemplate, reqId);
167           if (!mappedNodeTemplate.isPresent()) {
168             throw new CoreException(new ToscaMissingSubstitutionMappingForReqCapErrorBuilder(
169                 ToscaMissingSubstitutionMappingForReqCapErrorBuilder.MappingExposedEntry
170                     .REQUIREMENT, connectedNodeId).build());
171           }
172
173           if (toscaAnalyzerService.isTypeOf(mappedNodeTemplate.get().getValue(),
174               ToscaNodeType.NATIVE_NETWORK_PORT, serviceTemplate,
175               toscaServiceModel)) {
176             Nic port = context.getNics().get(mappedNodeTemplate.get().getKey());
177             if (port != null) {
178               port.setNetworkName(connectedNodeId);
179             } else {
180               logger.warn(
181                   "Different ports define for the same component which is used in different "
182                       + "substitution service templates.");
183             }
184           }
185         } else if (!connectedNodeTemplate.isPresent()) {
186           throw new CoreException(
187               new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", connectedNodeId).build());
188         }
189       });
190     }
191   }
192
193   private boolean isLinkToNetworkRequirementAssignment(RequirementAssignment requirement) {
194     return toscaAnalyzerService.isDesiredRequirementAssignment(requirement,
195         ToscaCapabilityType.NATIVE_NETWORK_LINKABLE, null,
196         ToscaRelationshipType.NATIVE_NETWORK_LINK_TO);
197   }
198
199
200   private void connectPortToNetwork(Nic port, NodeTemplate portNodeTemplate) {
201     List<RequirementAssignment> linkRequirementsToNetwork =
202         toscaAnalyzerService.getRequirements(portNodeTemplate, ToscaConstants.LINK_REQUIREMENT_ID);
203
204     //port is connected to one network
205     for (RequirementAssignment linkRequirementToNetwork : linkRequirementsToNetwork) {
206       port.setNetworkName(linkRequirementToNetwork.getNode());
207     }
208   }
209
210   /*
211   return Map with key - compute node template id, value - list of connected port node template id
212    */
213   private Map<String, List<String>> getComputeToPortsConnection(
214       Map<String, NodeTemplate> portNodeTemplates) {
215     Map<String, List<String>> computeToPortConnection = new HashMap<>();
216     if (MapUtils.isEmpty(portNodeTemplates)) {
217       return computeToPortConnection;
218     }
219     for (String portId : portNodeTemplates.keySet()) {
220       List<RequirementAssignment> bindingRequirementsToCompute = toscaAnalyzerService
221           .getRequirements(portNodeTemplates.get(portId), ToscaConstants.BINDING_REQUIREMENT_ID);
222       for (RequirementAssignment bindingRequirementToCompute : bindingRequirementsToCompute) {
223         computeToPortConnection
224             .putIfAbsent(bindingRequirementToCompute.getNode(), new ArrayList<>());
225         computeToPortConnection.get(bindingRequirementToCompute.getNode()).add(portId);
226       }
227     }
228     return computeToPortConnection;
229   }
230
231   private void extractComponents(ServiceTemplate serviceTemplate,
232                                         ToscaServiceModel toscaServiceModel,
233                                         ExtractCompositionDataContext context) {
234     Map<String, NodeTemplate> computeNodeTemplates = toscaAnalyzerService
235         .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_COMPUTE,
236             toscaServiceModel);
237     if (MapUtils.isEmpty(computeNodeTemplates)) {
238       return;
239     }
240     Map<String, List<String>> imageNodeTemplates = getComponentImages(computeNodeTemplates,
241         toscaServiceModel);
242     Map<String, List<String>> computeFlavorNodeTemplates =
243         getComponentComputeFlavor(computeNodeTemplates, toscaServiceModel);
244     Map<String, NodeTemplate> portNodeTemplates = toscaAnalyzerService
245         .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_NETWORK_PORT,
246             toscaServiceModel);
247     Map<String, List<String>> computeToPortsConnection =
248         getComputeToPortsConnection(portNodeTemplates);
249     Map<String, List<String>> computesGroupedByType =
250         getNodeTemplatesGroupedByType(computeNodeTemplates);
251
252     computesGroupedByType.keySet()
253         .stream()
254         .filter(nodeType ->
255             !context.getCreatedComponents().contains(nodeType))
256         .forEach(nodeType -> extractComponent(serviceTemplate, computeToPortsConnection,
257             computesGroupedByType, imageNodeTemplates, computeFlavorNodeTemplates, nodeType,
258             context));
259   }
260
261   private Map<String,List<String>> getComponentImages(Map<String, NodeTemplate>
262                                                           computeNodeTemplates,
263                                                       ToscaServiceModel toscaServiceModel) {
264     return getComponentProperty(ToscaConstants.COMPUTE_IMAGE, computeNodeTemplates, toscaServiceModel);
265   }
266
267   private Map<String,List<String>> getComponentComputeFlavor(Map<String, NodeTemplate>
268                                                                  computeNodeTemplates,
269                                                              ToscaServiceModel toscaServiceModel) {
270     return getComponentProperty(ToscaConstants.COMPUTE_FLAVOR, computeNodeTemplates, toscaServiceModel);
271   }
272
273   private Map<String, List<String>> getComponentProperty(String propertyName,
274                                                          Map<String, NodeTemplate> computeNodeTemplates,
275                                                          ToscaServiceModel toscaServiceModel) {
276     Map<String,List<String>> componentPropertyValues = new HashMap<>();
277     for (String component : computeNodeTemplates.keySet()) {
278       List<String> computes = new ArrayList<>();
279       Map<String,Object> properties =  computeNodeTemplates.get(component).getProperties();
280
281       if(MapUtils.isEmpty(properties)){
282         continue;
283       }
284
285       List<Object> computesList = properties.entrySet()
286           .stream()
287           .filter(map -> map.getKey().equals(propertyName))
288           .map(Map.Entry::getValue)
289           .collect(Collectors.toList());
290       for (Object obj : computesList) {
291         if (obj instanceof String) {
292           computes.add((String) obj);
293         } else {
294           Map<String, String> objMap = new ObjectMapper().convertValue(obj, Map.class);
295           computes.add(getInputs(toscaServiceModel, objMap.get("get_input")));
296         }
297       }
298       componentPropertyValues.put(component,computes);
299     }
300     return componentPropertyValues;
301   }
302
303   private String  getInputs(ToscaServiceModel toscaServiceModel, String inputValue) {
304     String mainTemplate = toscaServiceModel.getEntryDefinitionServiceTemplate();
305     List<ServiceTemplate> toscaServiceTemplates = toscaServiceModel.getServiceTemplates().entrySet()
306         .stream()
307         .filter(map -> map.getKey().equals(mainTemplate))
308         .map(Map.Entry::getValue)
309         .collect(Collectors.toList());
310     ServiceTemplate serviceTemplate = toscaServiceTemplates.get(0);
311
312     if (Objects.nonNull(serviceTemplate.getTopology_template())
313         && MapUtils.isNotEmpty(serviceTemplate.getTopology_template().getInputs())) {
314       for (Map.Entry<String, ParameterDefinition> inputEntry : serviceTemplate
315           .getTopology_template().getInputs().entrySet()) {
316         if (inputEntry.getKey().equals(inputValue)) {
317           String value;
318           try {
319             value= (String) inputEntry.getValue().get_default();
320           } catch (Exception e) {
321             logger.debug(e.getMessage(), e);
322             value = inputEntry.getValue().get_default().toString();
323           }
324         return value;
325         }
326       }
327     }
328     return inputValue;
329   }
330
331   private void extractComponent(ServiceTemplate serviceTemplate,
332                                        Map<String, List<String>> computeToPortsConnection,
333                                        Map<String, List<String>> computesGroupedByType,
334                                        Map<String, List<String>> imageList,
335                                        Map<String, List<String>> computeFlavorNodeTemplates,
336                                        String computeNodeType,
337                                        ExtractCompositionDataContext context) {
338     ComponentData component = new ComponentData();
339     component.setName(computeNodeType);
340     component.setDisplayName(getComponentDisplayName(component.getName()));
341     Component componentModel = new Component();
342     componentModel.setData(component);
343
344     String computeId = computesGroupedByType.get(computeNodeType).get(0);
345     List<String> connectedPortIds = computeToPortsConnection.get(computeId);
346     List<String> images = imageList.get(computeId);
347     List<String> computeFlavors = computeFlavorNodeTemplates.get(computeId);
348
349     if (connectedPortIds != null) {
350       componentModel.setNics(new ArrayList<>());
351       componentModel.setImages(new ArrayList<>());
352       componentModel.setCompute(new ArrayList<>());
353       for (String portId : connectedPortIds) {
354         Nic port = extractPort(serviceTemplate, portId);
355         componentModel.getNics().add(port);
356         context.addNic(portId, port);
357       }
358       for (String image : images) {
359         Image img = new Image(image);
360         componentModel.getImages().add(img);
361         context.addImage(image, img);
362       }
363       for (String flavor : computeFlavors) {
364         ComputeData computeFlavor = new ComputeData(flavor);
365         componentModel.getCompute().add(computeFlavor);
366         context.addCompute(flavor,computeFlavor);
367       }
368     }
369     context.addComponent(componentModel);
370     context.getCreatedComponents().add(computeNodeType);
371   }
372
373   private Nic extractPort(ServiceTemplate serviceTemplate, String portNodeTemplateId) {
374     Optional<NodeTemplate> portNodeTemplate =
375         toscaAnalyzerService.getNodeTemplateById(serviceTemplate, portNodeTemplateId);
376     if (portNodeTemplate.isPresent()) {
377       Nic port = new Nic();
378       port.setName(portNodeTemplateId);
379       connectPortToNetwork(port, portNodeTemplate.get());
380       return port;
381     } else {
382       throw new CoreException(
383           new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", portNodeTemplateId).build());
384     }
385   }
386
387   private Map<String, List<String>> getNodeTemplatesGroupedByType(
388       Map<String, NodeTemplate> nodeTemplates) {
389     Map<String, List<String>> nodeTemplatesGrouped =
390         new HashMap<>();   //key - node type, value - list of node ids with this type
391     for (String nodeId : nodeTemplates.keySet()) {
392       String nodeType = nodeTemplates.get(nodeId).getType();
393       nodeTemplatesGrouped.putIfAbsent(nodeType, new ArrayList<>());
394       nodeTemplatesGrouped.get(nodeType).add(nodeId);
395     }
396     return nodeTemplatesGrouped;
397   }
398
399   private List<Network> extractNetworks(ServiceTemplate serviceTemplate,
400                                                ToscaServiceModel toscaServiceModel) {
401     List<Network> networks = new ArrayList<>();
402     Map<String, NodeTemplate> networkNodeTemplates = toscaAnalyzerService
403         .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_NETWORK,
404             toscaServiceModel);
405     if (MapUtils.isEmpty(networkNodeTemplates)) {
406       return networks;
407     }
408     for (String networkId : networkNodeTemplates.keySet()) {
409       Network network = new Network();
410       network.setName(networkId);
411       Optional<Boolean> networkDhcpValue =
412           getNetworkDhcpValue(serviceTemplate, networkNodeTemplates.get(networkId));
413       network.setDhcp(networkDhcpValue.orElse(true));
414       networks.add(network);
415     }
416     return networks;
417   }
418
419   //dhcp default value is true
420   private Optional<Boolean> getNetworkDhcpValue(ServiceTemplate serviceTemplate,
421                                                        NodeTemplate networkNodeTemplate) {
422     if (networkNodeTemplate == null) {
423       return Optional.empty();
424     }
425     if (networkNodeTemplate.getProperties() == null
426         || networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME)
427         == null) {
428       return Optional.of(true);
429     }
430
431     Object dhcp =
432         networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME);
433     if (dhcp instanceof String) {
434       return Optional.of(Boolean.valueOf((String) dhcp));
435     } else if (dhcp instanceof Boolean) {
436       return Optional.of((Boolean) dhcp);
437     } else if (dhcp instanceof Map) {
438       String inputParameterName =
439           (String) ((Map) dhcp).get(ToscaFunctions.GET_INPUT.getDisplayName());
440       if (inputParameterName != null) {
441         ParameterDefinition inputParameterDefinition =
442             serviceTemplate.getTopology_template().getInputs().get(inputParameterName);
443         if (inputParameterDefinition != null) {
444           if (inputParameterDefinition.get_default() != null) {
445             return Optional.of(Boolean.valueOf(inputParameterDefinition.get_default().toString()));
446           }
447         } else {
448           throw new CoreException(
449               new ToscaInvalidEntryNotFoundErrorBuilder("Input Parameter", inputParameterName)
450                   .build());
451         }
452       }
453     }
454     return Optional.of(true);
455   }
456
457   @Override
458   public String getComponentDisplayName(String componentName) {
459     if (componentName == null) {
460       return null;
461     }
462     String delimiterChar = ".";
463     if (componentName.contains(delimiterChar)) {
464       return componentName.substring(componentName.lastIndexOf(delimiterChar) + 1);
465     }
466     return componentName;
467
468   }
469
470 }