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