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