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.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.onap.sdc.tosca.datatypes.model.NodeTemplate;
35 import org.onap.sdc.tosca.datatypes.model.ParameterDefinition;
36 import org.onap.sdc.tosca.datatypes.model.RequirementAssignment;
37 import org.onap.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.onap.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;
55 import java.util.ArrayList;
56 import java.util.HashMap;
57 import java.util.List;
59 import java.util.Objects;
60 import java.util.Optional;
61 import java.util.stream.Collectors;
63 public class CompositionDataExtractorImpl implements CompositionDataExtractor {
65 protected static Logger logger;
66 private static ToscaAnalyzerService toscaAnalyzerService;
68 logger = LoggerFactory.getLogger(CompositionDataExtractorImpl.class);
69 toscaAnalyzerService = new ToscaAnalyzerServiceImpl();
73 * Extract service composition data composition data.
75 * @param toscaServiceModel the tosca service model
76 * @return the composition data
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);
87 CompositionData compositionData = new CompositionData();
88 compositionData.setNetworks(context.getNetworks());
89 compositionData.setComponents(context.getComponents());
90 return compositionData;
93 private void extractServiceCompositionData(String serviceTemplateFileName,
94 ServiceTemplate serviceTemplate,
95 ToscaServiceModel toscaServiceModel,
96 ExtractCompositionDataContext context) {
97 if (context.getHandledServiceTemplates().contains(serviceTemplateFileName)) {
100 context.addNetworks(extractNetworks(serviceTemplate, toscaServiceModel));
101 extractComponents(serviceTemplate, toscaServiceModel, context);
102 handleSubstitution(serviceTemplate, toscaServiceModel, context);
103 context.addHandledServiceTemplates(serviceTemplateFileName);
106 private void handleSubstitution(ServiceTemplate serviceTemplate,
107 ToscaServiceModel toscaServiceModel,
108 ExtractCompositionDataContext context) {
109 Map<String, NodeTemplate> substitutableNodeTemplates =
110 toscaAnalyzerService.getSubstitutableNodeTemplates(serviceTemplate);
112 if (substitutableNodeTemplates != null) {
113 for (String substitutableNodeTemplateId : substitutableNodeTemplates.keySet()) {
114 handleSubstitutableNodeTemplate(serviceTemplate, toscaServiceModel,
115 substitutableNodeTemplateId,
116 substitutableNodeTemplates.get(substitutableNodeTemplateId), context);
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());
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
139 ServiceTemplate substituteServiceTemplate =
140 toscaServiceModel.getServiceTemplates().get(substituteServiceTemplateFileName.get());
141 extractServiceCompositionData(substituteServiceTemplateFileName.get(),
142 substituteServiceTemplate, toscaServiceModel, context);
144 List<Map<String, RequirementAssignment>> substitutableRequirements =
145 substitutableNodeTemplate.getRequirements();
147 if (CollectionUtils.isEmpty(substitutableRequirements)) {
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);
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());
177 if (toscaAnalyzerService.isTypeOf(mappedNodeTemplate.get().getValue(),
178 ToscaNodeType.NATIVE_NETWORK_PORT, serviceTemplate,
179 toscaServiceModel)) {
180 Nic port = context.getNics().get(mappedNodeTemplate.get().getKey());
182 port.setNetworkName(connectedNodeId);
185 "Different ports define for the same component which is used in different "
186 + "substitution service templates.");
189 } else if (!connectedNodeTemplate.isPresent()) {
190 throw new CoreException(
191 new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", connectedNodeId).build());
197 private boolean isLinkToNetworkRequirementAssignment(RequirementAssignment requirement) {
198 return toscaAnalyzerService.isDesiredRequirementAssignment(requirement,
199 ToscaCapabilityType.NATIVE_NETWORK_LINKABLE, null,
200 ToscaRelationshipType.NATIVE_NETWORK_LINK_TO);
204 private void connectPortToNetwork(Nic port, NodeTemplate portNodeTemplate) {
205 List<RequirementAssignment> linkRequirementsToNetwork =
206 toscaAnalyzerService.getRequirements(portNodeTemplate, ToscaConstants.LINK_REQUIREMENT_ID);
208 //port is connected to one network
209 for (RequirementAssignment linkRequirementToNetwork : linkRequirementsToNetwork) {
210 port.setNetworkName(linkRequirementToNetwork.getNode());
215 return Map with key - compute node template id, value - list of connected port node template id
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;
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);
232 return computeToPortConnection;
235 private void extractComponents(ServiceTemplate serviceTemplate,
236 ToscaServiceModel toscaServiceModel,
237 ExtractCompositionDataContext context) {
238 Map<String, NodeTemplate> computeNodeTemplates = toscaAnalyzerService
239 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_COMPUTE,
241 if (MapUtils.isEmpty(computeNodeTemplates)) {
244 Map<String, List<String>> imageNodeTemplates = getComponentImages(computeNodeTemplates,
246 Map<String, List<String>> computeFlavorNodeTemplates =
247 getComponentComputeFlavor(computeNodeTemplates, toscaServiceModel);
248 Map<String, NodeTemplate> portNodeTemplates = toscaAnalyzerService
249 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_NETWORK_PORT,
251 Map<String, List<String>> computeToPortsConnection =
252 getComputeToPortsConnection(portNodeTemplates);
253 Map<String, List<String>> computesGroupedByType =
254 getNodeTemplatesGroupedByType(computeNodeTemplates);
256 computesGroupedByType.keySet()
259 !context.getCreatedComponents().contains(nodeType))
260 .forEach(nodeType -> extractComponent(serviceTemplate, computeToPortsConnection,
261 computesGroupedByType, imageNodeTemplates, computeFlavorNodeTemplates, nodeType,
265 private Map<String,List<String>> getComponentImages(Map<String, NodeTemplate>
266 computeNodeTemplates,
267 ToscaServiceModel toscaServiceModel) {
268 return getComponentProperty(ToscaConstants.COMPUTE_IMAGE, computeNodeTemplates, toscaServiceModel);
271 private Map<String,List<String>> getComponentComputeFlavor(Map<String, NodeTemplate>
272 computeNodeTemplates,
273 ToscaServiceModel toscaServiceModel) {
274 return getComponentProperty(ToscaConstants.COMPUTE_FLAVOR, computeNodeTemplates, toscaServiceModel);
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();
285 if(MapUtils.isEmpty(properties)){
289 List<Object> computesList = properties.entrySet()
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);
298 Map<String, String> objMap = new ObjectMapper().convertValue(obj, Map.class);
299 computes.add(getInputs(toscaServiceModel, objMap.get("get_input")));
302 componentPropertyValues.put(component,computes);
304 return componentPropertyValues;
307 private String getInputs(ToscaServiceModel toscaServiceModel, String inputValue) {
308 String mainTemplate = toscaServiceModel.getEntryDefinitionServiceTemplate();
309 List<ServiceTemplate> toscaServiceTemplates = toscaServiceModel.getServiceTemplates().entrySet()
311 .filter(map -> map.getKey().equals(mainTemplate))
312 .map(Map.Entry::getValue)
313 .collect(Collectors.toList());
314 ServiceTemplate serviceTemplate = toscaServiceTemplates.get(0);
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)) {
323 value= (String) inputEntry.getValue().get_default();
324 } catch (Exception e) {
325 logger.debug(e.getMessage(), e);
326 value = inputEntry.getValue().get_default().toString();
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);
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);
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);
363 for (String image : images) {
364 Image img = new Image(image);
365 componentModel.getImages().add(img);
366 context.addImage(image, img);
368 for (String flavor : computeFlavors) {
369 ComputeData computeFlavor = new ComputeData(flavor);
370 componentModel.getCompute().add(computeFlavor);
371 context.addCompute(flavor,computeFlavor);
374 context.addComponent(componentModel);
375 context.getCreatedComponents().add(computeNodeType);
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());
387 throw new CoreException(
388 new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", portNodeTemplateId).build());
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);
401 return nodeTemplatesGrouped;
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,
410 if (MapUtils.isEmpty(networkNodeTemplates)) {
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);
424 //dhcp default value is true
425 private Optional<Boolean> getNetworkDhcpValue(ServiceTemplate serviceTemplate,
426 NodeTemplate networkNodeTemplate) {
427 if (networkNodeTemplate == null) {
428 return Optional.empty();
430 if (networkNodeTemplate.getProperties() == null
431 || networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME)
433 return Optional.of(true);
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()));
453 throw new CoreException(
454 new ToscaInvalidEntryNotFoundErrorBuilder("Input Parameter", inputParameterName)
459 return Optional.of(true);
463 public String getComponentDisplayName(String componentName) {
464 if (componentName == null) {
467 String delimiterChar = ".";
468 if (componentName.contains(delimiterChar)) {
469 return componentName.substring(componentName.lastIndexOf(delimiterChar) + 1);
471 return componentName;