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