2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.openecomp.sdc.vendorsoftwareproduct.services.impl.composition;
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.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.ToscaExtensionYamlUtil;
50 import org.openecomp.sdc.tosca.services.impl.ToscaAnalyzerServiceImpl;
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.ComputeData;
56 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.ExtractCompositionDataContext;
57 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Image;
58 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Network;
59 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Nic;
61 import java.util.ArrayList;
62 import java.util.HashMap;
63 import java.util.List;
65 import java.util.Objects;
66 import java.util.Optional;
67 import java.util.stream.Collectors;
69 public class CompositionDataExtractorImpl implements CompositionDataExtractor {
71 protected static Logger logger;
72 private static ToscaAnalyzerService toscaAnalyzerService;
74 logger = LoggerFactory.getLogger(CompositionDataExtractorImpl.class);
75 toscaAnalyzerService = new ToscaAnalyzerServiceImpl();
79 * Extract service composition data composition data.
81 * @param toscaServiceModel the tosca service model
82 * @return the composition data
84 public CompositionData extractServiceCompositionData(ToscaServiceModel toscaServiceModel) {
85 ExtractCompositionDataContext context = new ExtractCompositionDataContext();
86 String entryDefinitionServiceTemplateFileName =
87 toscaServiceModel.getEntryDefinitionServiceTemplate();
88 ServiceTemplate entryDefinitionServiceTemplate =
89 toscaServiceModel.getServiceTemplates().get(entryDefinitionServiceTemplateFileName);
90 extractServiceCompositionData(entryDefinitionServiceTemplateFileName,
91 entryDefinitionServiceTemplate, toscaServiceModel, context);
93 CompositionData compositionData = new CompositionData();
94 compositionData.setNetworks(context.getNetworks());
95 compositionData.setComponents(context.getComponents());
96 return compositionData;
99 private void extractServiceCompositionData(String serviceTemplateFileName,
100 ServiceTemplate serviceTemplate,
101 ToscaServiceModel toscaServiceModel,
102 ExtractCompositionDataContext context) {
103 if (context.getHandledServiceTemplates().contains(serviceTemplateFileName)) {
106 context.addNetworks(extractNetworks(serviceTemplate, toscaServiceModel));
107 extractComponents(serviceTemplate, toscaServiceModel, context);
108 handleSubstitution(serviceTemplate, toscaServiceModel, context);
109 context.addHandledServiceTemplates(serviceTemplateFileName);
112 private void handleSubstitution(ServiceTemplate serviceTemplate,
113 ToscaServiceModel toscaServiceModel,
114 ExtractCompositionDataContext context) {
115 Map<String, NodeTemplate> substitutableNodeTemplates =
116 toscaAnalyzerService.getSubstitutableNodeTemplates(serviceTemplate);
118 if (substitutableNodeTemplates != null) {
119 for (String substitutableNodeTemplateId : substitutableNodeTemplates.keySet()) {
120 handleSubstitutableNodeTemplate(serviceTemplate, toscaServiceModel,
121 substitutableNodeTemplateId,
122 substitutableNodeTemplates.get(substitutableNodeTemplateId), context);
127 private void handleSubstitutableNodeTemplate(ServiceTemplate serviceTemplate,
128 ToscaServiceModel toscaServiceModel,
129 String substitutableNodeTemplateId,
130 NodeTemplate substitutableNodeTemplate,
131 ExtractCompositionDataContext context) {
132 ToscaExtensionYamlUtil toscaExtensionYamlUtil = new ToscaExtensionYamlUtil();
133 Optional<String> substituteServiceTemplateFileName = toscaAnalyzerService
134 .getSubstituteServiceTemplateName(substitutableNodeTemplateId, substitutableNodeTemplate);
135 if (!substituteServiceTemplateFileName.isPresent()) {
136 MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
137 LoggerTragetServiceName.EXTRACT_COMPOSITION_DATA, ErrorLevel.ERROR.name(),
138 LoggerErrorCode.DATA_ERROR.getErrorCode(),
139 LoggerErrorDescription.EXTRACT_COMPOSITION_DATA);
140 throw new CoreException(
141 new ToscaInvalidSubstituteNodeTemplateErrorBuilder(substitutableNodeTemplateId).build());
143 if (context.getHandledServiceTemplates().contains(substituteServiceTemplateFileName.get())) {
144 //each substitution is should be handled once, and will get the connection to the upper
145 // service level according to the first one which was processed
149 ServiceTemplate substituteServiceTemplate =
150 toscaServiceModel.getServiceTemplates().get(substituteServiceTemplateFileName.get());
151 extractServiceCompositionData(substituteServiceTemplateFileName.get(),
152 substituteServiceTemplate, toscaServiceModel, context);
154 List<Map<String, RequirementAssignment>> substitutableRequirements =
155 substitutableNodeTemplate.getRequirements();
157 if (CollectionUtils.isEmpty(substitutableRequirements)) {
161 for (Map<String, RequirementAssignment> substitutableReq : substitutableRequirements) {
162 substitutableReq.keySet().stream().filter(reqId -> {
163 RequirementAssignment reqAssignment = toscaExtensionYamlUtil
164 .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
165 RequirementAssignment.class);
166 return isLinkToNetworkRequirementAssignment(reqAssignment);
167 }).forEach(reqId -> {
168 RequirementAssignment linkToNetworkRequirement = toscaExtensionYamlUtil
169 .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
170 RequirementAssignment.class);
171 String connectedNodeId = linkToNetworkRequirement.getNode();
172 Optional<NodeTemplate> connectedNodeTemplate =
173 toscaAnalyzerService.getNodeTemplateById(serviceTemplate, connectedNodeId);
175 if (connectedNodeTemplate.isPresent() && toscaAnalyzerService
176 .isTypeOf(connectedNodeTemplate.get(), ToscaNodeType.NATIVE_NETWORK,
177 serviceTemplate, toscaServiceModel)) {
178 Optional<Map.Entry<String, NodeTemplate>> mappedNodeTemplate = toscaAnalyzerService
179 .getSubstitutionMappedNodeTemplateByExposedReq(
180 substituteServiceTemplateFileName.get(), substituteServiceTemplate, reqId);
181 if (!mappedNodeTemplate.isPresent()) {
182 MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
183 LoggerTragetServiceName.EXTRACT_COMPOSITION_DATA, ErrorLevel.ERROR.name(),
184 LoggerErrorCode.DATA_ERROR.getErrorCode(),
185 LoggerErrorDescription.EXTRACT_COMPOSITION_DATA);
186 throw new CoreException(new ToscaMissingSubstitutionMappingForReqCapErrorBuilder(
187 ToscaMissingSubstitutionMappingForReqCapErrorBuilder.MappingExposedEntry
188 .REQUIREMENT, connectedNodeId).build());
191 if (toscaAnalyzerService.isTypeOf(mappedNodeTemplate.get().getValue(),
192 ToscaNodeType.NATIVE_NETWORK_PORT, serviceTemplate,
193 toscaServiceModel)) {
194 Nic port = context.getNics().get(mappedNodeTemplate.get().getKey());
196 port.setNetworkName(connectedNodeId);
199 "Different ports define for the same component which is used in different "
200 + "substitution service templates.");
203 } else if (!connectedNodeTemplate.isPresent()) {
204 MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
205 LoggerTragetServiceName.EXTRACT_COMPOSITION_DATA, ErrorLevel.ERROR.name(),
206 LoggerErrorCode.DATA_ERROR.getErrorCode(),
207 LoggerErrorDescription.EXTRACT_COMPOSITION_DATA);
208 throw new CoreException(
209 new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", connectedNodeId).build());
215 private boolean isLinkToNetworkRequirementAssignment(RequirementAssignment requirement) {
216 return toscaAnalyzerService.isDesiredRequirementAssignment(requirement,
217 ToscaCapabilityType.NATIVE_NETWORK_LINKABLE, null,
218 ToscaRelationshipType.NATIVE_NETWORK_LINK_TO);
222 private void connectPortToNetwork(Nic port, NodeTemplate portNodeTemplate) {
223 List<RequirementAssignment> linkRequirementsToNetwork =
224 toscaAnalyzerService.getRequirements(portNodeTemplate, ToscaConstants.LINK_REQUIREMENT_ID);
226 //port is connected to one network
227 for (RequirementAssignment linkRequirementToNetwork : linkRequirementsToNetwork) {
228 port.setNetworkName(linkRequirementToNetwork.getNode());
233 return Map with key - compute node template id, value - list of connected port node template id
235 private Map<String, List<String>> getComputeToPortsConnection(
236 Map<String, NodeTemplate> portNodeTemplates) {
237 Map<String, List<String>> computeToPortConnection = new HashMap<>();
238 if (MapUtils.isEmpty(portNodeTemplates)) {
239 return computeToPortConnection;
241 for (String portId : portNodeTemplates.keySet()) {
242 List<RequirementAssignment> bindingRequirementsToCompute = toscaAnalyzerService
243 .getRequirements(portNodeTemplates.get(portId), ToscaConstants.BINDING_REQUIREMENT_ID);
244 for (RequirementAssignment bindingRequirementToCompute : bindingRequirementsToCompute) {
245 computeToPortConnection
246 .putIfAbsent(bindingRequirementToCompute.getNode(), new ArrayList<>());
247 computeToPortConnection.get(bindingRequirementToCompute.getNode()).add(portId);
250 return computeToPortConnection;
253 private void extractComponents(ServiceTemplate serviceTemplate,
254 ToscaServiceModel toscaServiceModel,
255 ExtractCompositionDataContext context) {
256 Map<String, NodeTemplate> computeNodeTemplates = toscaAnalyzerService
257 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_COMPUTE,
259 if (MapUtils.isEmpty(computeNodeTemplates)) {
262 Map<String, List<String>> imageNodeTemplates = getComponentImages(computeNodeTemplates,
264 Map<String, List<String>> computeFlavorNodeTemplates =
265 getComponentComputeFlavor(computeNodeTemplates, toscaServiceModel);
266 Map<String, NodeTemplate> portNodeTemplates = toscaAnalyzerService
267 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_NETWORK_PORT,
269 Map<String, List<String>> computeToPortsConnection =
270 getComputeToPortsConnection(portNodeTemplates);
271 Map<String, List<String>> computesGroupedByType =
272 getNodeTemplatesGroupedByType(computeNodeTemplates);
274 computesGroupedByType.keySet()
277 !context.getCreatedComponents().contains(nodeType))
278 .forEach(nodeType -> extractComponent(serviceTemplate, computeToPortsConnection,
279 computesGroupedByType, imageNodeTemplates, computeFlavorNodeTemplates, nodeType,
283 private Map<String,List<String>> getComponentImages(Map<String, NodeTemplate>
284 computeNodeTemplates,
285 ToscaServiceModel toscaServiceModel) {
286 return getComponentProperty(ToscaConstants.COMPUTE_IMAGE, computeNodeTemplates, toscaServiceModel);
289 private Map<String,List<String>> getComponentComputeFlavor(Map<String, NodeTemplate>
290 computeNodeTemplates,
291 ToscaServiceModel toscaServiceModel) {
292 return getComponentProperty(ToscaConstants.COMPUTE_FLAVOR, computeNodeTemplates, toscaServiceModel);
295 private Map<String, List<String>> getComponentProperty(String propertyName,
296 Map<String, NodeTemplate> computeNodeTemplates,
297 ToscaServiceModel toscaServiceModel) {
298 Map<String,List<String>> componentPropertyValues = new HashMap<>();
299 for (String component : computeNodeTemplates.keySet()) {
300 List<String> computes = new ArrayList<>();
301 Map<String,Object> properties = computeNodeTemplates.get(component).getProperties();
303 if(MapUtils.isEmpty(properties)){
307 List<Object> computesList = properties.entrySet()
309 .filter(map -> map.getKey().equals(propertyName))
310 .map(Map.Entry::getValue)
311 .collect(Collectors.toList());
312 for (Object obj : computesList) {
313 if (obj instanceof String) {
314 computes.add((String) obj);
316 Map<String, String> objMap = new ObjectMapper().convertValue(obj, Map.class);
317 computes.add(getInputs(toscaServiceModel, objMap.get("get_input")));
320 componentPropertyValues.put(component,computes);
322 return componentPropertyValues;
325 private String getInputs(ToscaServiceModel toscaServiceModel, String inputValue) {
326 String mainTemplate = toscaServiceModel.getEntryDefinitionServiceTemplate();
327 List<ServiceTemplate> toscaServiceTemplates = toscaServiceModel.getServiceTemplates().entrySet()
329 .filter(map -> map.getKey().equals(mainTemplate))
330 .map(map -> map.getValue())
331 .collect(Collectors.toList());
332 ServiceTemplate serviceTemplate = toscaServiceTemplates.get(0);
334 if (Objects.nonNull(serviceTemplate.getTopology_template())
335 && MapUtils.isNotEmpty(serviceTemplate.getTopology_template().getInputs())) {
336 for (Map.Entry<String, ParameterDefinition> inputEntry : serviceTemplate
337 .getTopology_template().getInputs().entrySet()) {
338 if (inputEntry.getKey().equals(inputValue)) {
341 value= (String) inputEntry.getValue().get_default();
342 } catch (Exception e) {
343 logger.debug(e.getMessage(), e);
344 value = inputEntry.getValue().get_default().toString();
353 private void extractComponent(ServiceTemplate serviceTemplate,
354 Map<String, List<String>> computeToPortsConnection,
355 Map<String, List<String>> computesGroupedByType,
356 Map<String, List<String>> imageList,
357 Map<String, List<String>> computeFlavorNodeTemplates,
358 String computeNodeType,
359 ExtractCompositionDataContext context) {
360 ComponentData component = new ComponentData();
361 component.setName(computeNodeType);
362 component.setDisplayName(getComponentDisplayName(component.getName()));
363 component.setVfcCode(component.getDisplayName());
364 Component componentModel = new Component();
365 componentModel.setData(component);
367 String computeId = computesGroupedByType.get(computeNodeType).get(0);
368 List<String> connectedPortIds = computeToPortsConnection.get(computeId);
369 List<String> images = imageList.get(computeId);
370 List<String> computeFlavors = computeFlavorNodeTemplates.get(computeId);
372 if (connectedPortIds != null) {
373 componentModel.setNics(new ArrayList<>());
374 componentModel.setImages(new ArrayList<>());
375 componentModel.setCompute(new ArrayList<>());
376 for (String portId : connectedPortIds) {
377 Nic port = extractPort(serviceTemplate, portId);
378 componentModel.getNics().add(port);
379 context.addNic(portId, port);
381 for (String image : images) {
382 Image img = new Image(image);
383 componentModel.getImages().add(img);
384 context.addImage(image, img);
386 for (String flavor : computeFlavors) {
387 ComputeData computeFlavor = new ComputeData(flavor);
388 componentModel.getCompute().add(computeFlavor);
389 context.addCompute(flavor,computeFlavor);
392 context.addComponent(componentModel);
393 context.getCreatedComponents().add(computeNodeType);
396 private Nic extractPort(ServiceTemplate serviceTemplate, String portNodeTemplateId) {
397 Optional<NodeTemplate> portNodeTemplate =
398 toscaAnalyzerService.getNodeTemplateById(serviceTemplate, portNodeTemplateId);
399 if (portNodeTemplate.isPresent()) {
400 Nic port = new Nic();
401 port.setName(portNodeTemplateId);
402 connectPortToNetwork(port, portNodeTemplate.get());
405 MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
406 LoggerTragetServiceName.EXTRACT_COMPOSITION_DATA, ErrorLevel.ERROR.name(),
407 LoggerErrorCode.DATA_ERROR.getErrorCode(),
408 LoggerErrorDescription.EXTRACT_COMPOSITION_DATA);
409 throw new CoreException(
410 new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", portNodeTemplateId).build());
414 private Map<String, List<String>> getNodeTemplatesGroupedByType(
415 Map<String, NodeTemplate> nodeTemplates) {
416 Map<String, List<String>> nodeTemplatesGrouped =
417 new HashMap<>(); //key - node type, value - list of node ids with this type
418 for (String nodeId : nodeTemplates.keySet()) {
419 String nodeType = nodeTemplates.get(nodeId).getType();
420 nodeTemplatesGrouped.putIfAbsent(nodeType, new ArrayList<>());
421 nodeTemplatesGrouped.get(nodeType).add(nodeId);
423 return nodeTemplatesGrouped;
426 private List<Network> extractNetworks(ServiceTemplate serviceTemplate,
427 ToscaServiceModel toscaServiceModel) {
428 List<Network> networks = new ArrayList<>();
429 Map<String, NodeTemplate> networkNodeTemplates = toscaAnalyzerService
430 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_NETWORK,
432 if (MapUtils.isEmpty(networkNodeTemplates)) {
435 for (String networkId : networkNodeTemplates.keySet()) {
436 Network network = new Network();
437 network.setName(networkId);
438 Optional<Boolean> networkDhcpValue =
439 getNetworkDhcpValue(serviceTemplate, networkNodeTemplates.get(networkId));
440 network.setDhcp(networkDhcpValue.isPresent() ? networkDhcpValue.get() : true);
441 networks.add(network);
446 //dhcp default value is true
447 private Optional<Boolean> getNetworkDhcpValue(ServiceTemplate serviceTemplate,
448 NodeTemplate networkNodeTemplate) {
449 if (networkNodeTemplate == null) {
450 return Optional.empty();
452 if (networkNodeTemplate.getProperties() == null
453 || networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME)
455 return Optional.of(true);
459 networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME);
460 if (dhcp instanceof String) {
461 return Optional.of(Boolean.valueOf((String) dhcp));
462 } else if (dhcp instanceof Boolean) {
463 return Optional.of((Boolean) dhcp);
464 } else if (dhcp instanceof Map) {
465 String inputParameterName =
466 (String) ((Map) dhcp).get(ToscaFunctions.GET_INPUT.getDisplayName());
467 if (inputParameterName != null) {
468 ParameterDefinition inputParameterDefinition =
469 serviceTemplate.getTopology_template().getInputs().get(inputParameterName);
470 if (inputParameterDefinition != null) {
471 if (inputParameterDefinition.get_default() != null) {
472 return Optional.of(Boolean.valueOf(inputParameterDefinition.get_default().toString()));
475 MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
476 LoggerTragetServiceName.EXTRACT_COMPOSITION_DATA, ErrorLevel.ERROR.name(),
477 LoggerErrorCode.DATA_ERROR.getErrorCode(),
478 LoggerErrorDescription.EXTRACT_COMPOSITION_DATA);
479 throw new CoreException(
480 new ToscaInvalidEntryNotFoundErrorBuilder("Input Parameter", inputParameterName)
485 return Optional.of(true);
489 public String getComponentDisplayName(String componentName) {
490 if (componentName == null) {
493 String delimiterChar = ".";
494 if (componentName.contains(delimiterChar)) {
495 return componentName.substring(componentName.lastIndexOf(delimiterChar) + 1);
497 return componentName;