2 * Copyright © 2016-2018 European Support Limited
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.openecomp.sdc.vendorsoftwareproduct.services.impl.composition;
19 import com.fasterxml.jackson.databind.ObjectMapper;
20 import org.apache.commons.collections4.CollectionUtils;
21 import org.apache.commons.collections4.MapUtils;
22 import org.onap.sdc.tosca.datatypes.model.NodeTemplate;
23 import org.onap.sdc.tosca.datatypes.model.ParameterDefinition;
24 import org.onap.sdc.tosca.datatypes.model.RequirementAssignment;
25 import org.onap.sdc.tosca.datatypes.model.ServiceTemplate;
26 import org.onap.sdc.tosca.services.ToscaExtensionYamlUtil;
27 import org.openecomp.sdc.common.errors.CoreException;
28 import org.openecomp.sdc.logging.api.Logger;
29 import org.openecomp.sdc.logging.api.LoggerFactory;
30 import org.openecomp.sdc.tosca.datatypes.*;
31 import org.openecomp.sdc.tosca.errors.ToscaInvalidEntryNotFoundErrorBuilder;
32 import org.openecomp.sdc.tosca.errors.ToscaInvalidSubstituteNodeTemplateErrorBuilder;
33 import org.openecomp.sdc.tosca.errors.ToscaMissingSubstitutionMappingForReqCapErrorBuilder;
34 import org.openecomp.sdc.tosca.services.ToscaAnalyzerService;
35 import org.openecomp.sdc.tosca.services.ToscaConstants;
36 import org.openecomp.sdc.tosca.services.impl.ToscaAnalyzerServiceImpl;
37 import org.openecomp.sdc.vendorsoftwareproduct.services.composition.CompositionDataExtractor;
38 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.*;
41 import java.util.stream.Collectors;
43 public class CompositionDataExtractorImpl implements CompositionDataExtractor {
45 protected static Logger logger;
46 private static ToscaAnalyzerService toscaAnalyzerService;
48 logger = LoggerFactory.getLogger(CompositionDataExtractorImpl.class);
49 toscaAnalyzerService = new ToscaAnalyzerServiceImpl();
53 * Extract service composition data composition data.
55 * @param toscaServiceModel the tosca service model
56 * @return the composition data
58 public CompositionData extractServiceCompositionData(ToscaServiceModel toscaServiceModel) {
59 ExtractCompositionDataContext context = new ExtractCompositionDataContext();
60 String entryDefinitionServiceTemplateFileName =
61 toscaServiceModel.getEntryDefinitionServiceTemplate();
62 ServiceTemplate entryDefinitionServiceTemplate =
63 toscaServiceModel.getServiceTemplates().get(entryDefinitionServiceTemplateFileName);
64 extractServiceCompositionData(entryDefinitionServiceTemplateFileName,
65 entryDefinitionServiceTemplate, toscaServiceModel, context);
67 CompositionData compositionData = new CompositionData();
68 compositionData.setNetworks(context.getNetworks());
69 compositionData.setComponents(context.getComponents());
70 return compositionData;
73 private void extractServiceCompositionData(String serviceTemplateFileName,
74 ServiceTemplate serviceTemplate,
75 ToscaServiceModel toscaServiceModel,
76 ExtractCompositionDataContext context) {
77 if (context.getHandledServiceTemplates().contains(serviceTemplateFileName)) {
80 context.addNetworks(extractNetworks(serviceTemplate, toscaServiceModel));
81 extractComponents(serviceTemplate, toscaServiceModel, context);
82 handleSubstitution(serviceTemplate, toscaServiceModel, context);
83 context.addHandledServiceTemplates(serviceTemplateFileName);
86 private void handleSubstitution(ServiceTemplate serviceTemplate,
87 ToscaServiceModel toscaServiceModel,
88 ExtractCompositionDataContext context) {
89 Map<String, NodeTemplate> substitutableNodeTemplates =
90 toscaAnalyzerService.getSubstitutableNodeTemplates(serviceTemplate);
92 if (substitutableNodeTemplates != null) {
93 for (String substitutableNodeTemplateId : substitutableNodeTemplates.keySet()) {
94 handleSubstitutableNodeTemplate(serviceTemplate, toscaServiceModel,
95 substitutableNodeTemplateId,
96 substitutableNodeTemplates.get(substitutableNodeTemplateId), context);
101 private void handleSubstitutableNodeTemplate(ServiceTemplate serviceTemplate,
102 ToscaServiceModel toscaServiceModel,
103 String substitutableNodeTemplateId,
104 NodeTemplate substitutableNodeTemplate,
105 ExtractCompositionDataContext context) {
106 ToscaExtensionYamlUtil toscaExtensionYamlUtil = new ToscaExtensionYamlUtil();
107 Optional<String> substituteServiceTemplateFileName = toscaAnalyzerService
108 .getSubstituteServiceTemplateName(substitutableNodeTemplateId, substitutableNodeTemplate);
109 if (!substituteServiceTemplateFileName.isPresent()) {
110 throw new CoreException(
111 new ToscaInvalidSubstituteNodeTemplateErrorBuilder(substitutableNodeTemplateId).build());
113 if (context.getHandledServiceTemplates().contains(substituteServiceTemplateFileName.get())) {
114 //each substitution is should be handled once, and will get the connection to the upper
115 // service level according to the first one which was processed
119 ServiceTemplate substituteServiceTemplate =
120 toscaServiceModel.getServiceTemplates().get(substituteServiceTemplateFileName.get());
121 extractServiceCompositionData(substituteServiceTemplateFileName.get(),
122 substituteServiceTemplate, toscaServiceModel, context);
124 List<Map<String, RequirementAssignment>> substitutableRequirements =
125 substitutableNodeTemplate.getRequirements();
127 if (CollectionUtils.isEmpty(substitutableRequirements)) {
131 for (Map<String, RequirementAssignment> substitutableReq : substitutableRequirements) {
132 substitutableReq.keySet().stream().filter(reqId -> {
133 RequirementAssignment reqAssignment = toscaExtensionYamlUtil
134 .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
135 RequirementAssignment.class);
136 return isLinkToNetworkRequirementAssignment(reqAssignment);
137 }).forEach(reqId -> {
138 RequirementAssignment linkToNetworkRequirement = toscaExtensionYamlUtil
139 .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
140 RequirementAssignment.class);
141 String connectedNodeId = linkToNetworkRequirement.getNode();
142 Optional<NodeTemplate> connectedNodeTemplate =
143 toscaAnalyzerService.getNodeTemplateById(serviceTemplate, connectedNodeId);
145 if (connectedNodeTemplate.isPresent() && toscaAnalyzerService
146 .isTypeOf(connectedNodeTemplate.get(), ToscaNodeType.NATIVE_NETWORK,
147 serviceTemplate, toscaServiceModel)) {
148 Optional<Map.Entry<String, NodeTemplate>> mappedNodeTemplate = toscaAnalyzerService
149 .getSubstitutionMappedNodeTemplateByExposedReq(
150 substituteServiceTemplateFileName.get(), substituteServiceTemplate, reqId);
151 if (!mappedNodeTemplate.isPresent()) {
152 throw new CoreException(new ToscaMissingSubstitutionMappingForReqCapErrorBuilder(
153 ToscaMissingSubstitutionMappingForReqCapErrorBuilder.MappingExposedEntry
154 .REQUIREMENT, connectedNodeId).build());
157 if (toscaAnalyzerService.isTypeOf(mappedNodeTemplate.get().getValue(),
158 ToscaNodeType.NATIVE_NETWORK_PORT, serviceTemplate,
159 toscaServiceModel)) {
160 Nic port = context.getNics().get(mappedNodeTemplate.get().getKey());
162 port.setNetworkName(connectedNodeId);
165 "Different ports define for the same component which is used in different "
166 + "substitution service templates.");
169 } else if (!connectedNodeTemplate.isPresent()) {
170 throw new CoreException(
171 new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", connectedNodeId).build());
177 private boolean isLinkToNetworkRequirementAssignment(RequirementAssignment requirement) {
178 return toscaAnalyzerService.isDesiredRequirementAssignment(requirement,
179 ToscaCapabilityType.NATIVE_NETWORK_LINKABLE, null,
180 ToscaRelationshipType.NATIVE_NETWORK_LINK_TO);
184 private void connectPortToNetwork(Nic port, NodeTemplate portNodeTemplate) {
185 List<RequirementAssignment> linkRequirementsToNetwork =
186 toscaAnalyzerService.getRequirements(portNodeTemplate, ToscaConstants.LINK_REQUIREMENT_ID);
188 //port is connected to one network
189 for (RequirementAssignment linkRequirementToNetwork : linkRequirementsToNetwork) {
190 port.setNetworkName(linkRequirementToNetwork.getNode());
195 return Map with key - compute node template id, value - list of connected port node template id
197 private Map<String, List<String>> getComputeToPortsConnection(
198 Map<String, NodeTemplate> portNodeTemplates) {
199 Map<String, List<String>> computeToPortConnection = new HashMap<>();
200 if (MapUtils.isEmpty(portNodeTemplates)) {
201 return computeToPortConnection;
203 for (String portId : portNodeTemplates.keySet()) {
204 List<RequirementAssignment> bindingRequirementsToCompute = toscaAnalyzerService
205 .getRequirements(portNodeTemplates.get(portId), ToscaConstants.BINDING_REQUIREMENT_ID);
206 for (RequirementAssignment bindingRequirementToCompute : bindingRequirementsToCompute) {
207 computeToPortConnection
208 .putIfAbsent(bindingRequirementToCompute.getNode(), new ArrayList<>());
209 computeToPortConnection.get(bindingRequirementToCompute.getNode()).add(portId);
212 return computeToPortConnection;
215 private void extractComponents(ServiceTemplate serviceTemplate,
216 ToscaServiceModel toscaServiceModel,
217 ExtractCompositionDataContext context) {
218 Map<String, NodeTemplate> computeNodeTemplates = toscaAnalyzerService
219 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_COMPUTE,
221 if (MapUtils.isEmpty(computeNodeTemplates)) {
224 Map<String, List<String>> imageNodeTemplates = getComponentImages(computeNodeTemplates,
226 Map<String, List<String>> computeFlavorNodeTemplates =
227 getComponentComputeFlavor(computeNodeTemplates, toscaServiceModel);
228 Map<String, NodeTemplate> portNodeTemplates = toscaAnalyzerService
229 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_NETWORK_PORT,
231 Map<String, List<String>> computeToPortsConnection =
232 getComputeToPortsConnection(portNodeTemplates);
233 Map<String, List<String>> computesGroupedByType =
234 getNodeTemplatesGroupedByType(computeNodeTemplates);
236 computesGroupedByType.keySet()
239 !context.getCreatedComponents().contains(nodeType))
240 .forEach(nodeType -> extractComponent(serviceTemplate, computeToPortsConnection,
241 computesGroupedByType, imageNodeTemplates, computeFlavorNodeTemplates, nodeType,
245 private Map<String,List<String>> getComponentImages(Map<String, NodeTemplate>
246 computeNodeTemplates,
247 ToscaServiceModel toscaServiceModel) {
248 return getComponentProperty(ToscaConstants.COMPUTE_IMAGE, computeNodeTemplates, toscaServiceModel);
251 private Map<String,List<String>> getComponentComputeFlavor(Map<String, NodeTemplate>
252 computeNodeTemplates,
253 ToscaServiceModel toscaServiceModel) {
254 return getComponentProperty(ToscaConstants.COMPUTE_FLAVOR, computeNodeTemplates, toscaServiceModel);
257 private Map<String, List<String>> getComponentProperty(String propertyName,
258 Map<String, NodeTemplate> computeNodeTemplates,
259 ToscaServiceModel toscaServiceModel) {
260 Map<String,List<String>> componentPropertyValues = new HashMap<>();
261 for (String component : computeNodeTemplates.keySet()) {
262 List<String> computes = new ArrayList<>();
263 Map<String,Object> properties = computeNodeTemplates.get(component).getProperties();
265 if(MapUtils.isEmpty(properties)){
269 List<Object> computesList = properties.entrySet()
271 .filter(map -> map.getKey().equals(propertyName))
272 .map(Map.Entry::getValue)
273 .collect(Collectors.toList());
274 for (Object obj : computesList) {
275 if (obj instanceof String) {
276 computes.add((String) obj);
278 Map<String, String> objMap = new ObjectMapper().convertValue(obj, Map.class);
279 computes.add(getInputs(toscaServiceModel, objMap.get("get_input")));
282 componentPropertyValues.put(component,computes);
284 return componentPropertyValues;
287 private String getInputs(ToscaServiceModel toscaServiceModel, String inputValue) {
288 String mainTemplate = toscaServiceModel.getEntryDefinitionServiceTemplate();
289 List<ServiceTemplate> toscaServiceTemplates = toscaServiceModel.getServiceTemplates().entrySet()
291 .filter(map -> map.getKey().equals(mainTemplate))
292 .map(Map.Entry::getValue)
293 .collect(Collectors.toList());
294 ServiceTemplate serviceTemplate = toscaServiceTemplates.get(0);
296 if (Objects.nonNull(serviceTemplate.getTopology_template())
297 && MapUtils.isNotEmpty(serviceTemplate.getTopology_template().getInputs())) {
298 for (Map.Entry<String, ParameterDefinition> inputEntry : serviceTemplate
299 .getTopology_template().getInputs().entrySet()) {
300 if (inputEntry.getKey().equals(inputValue)) {
303 value= (String) inputEntry.getValue().get_default();
304 } catch (Exception e) {
305 logger.debug(e.getMessage(), e);
306 value = inputEntry.getValue().get_default().toString();
315 private void extractComponent(ServiceTemplate serviceTemplate,
316 Map<String, List<String>> computeToPortsConnection,
317 Map<String, List<String>> computesGroupedByType,
318 Map<String, List<String>> imageList,
319 Map<String, List<String>> computeFlavorNodeTemplates,
320 String computeNodeType,
321 ExtractCompositionDataContext context) {
322 ComponentData component = new ComponentData();
323 component.setName(computeNodeType);
324 component.setDisplayName(getComponentDisplayName(component.getName()));
325 Component componentModel = new Component();
326 componentModel.setData(component);
328 String computeId = computesGroupedByType.get(computeNodeType).get(0);
329 List<String> connectedPortIds = computeToPortsConnection.get(computeId);
330 List<String> images = imageList.get(computeId);
331 List<String> computeFlavors = computeFlavorNodeTemplates.get(computeId);
333 if (CollectionUtils.isNotEmpty(connectedPortIds)) {
334 componentModel.setNics(new ArrayList<>());
335 componentModel.setImages(new ArrayList<>());
336 componentModel.setCompute(new ArrayList<>());
338 connectedPortIds.forEach(portId -> {
339 Nic port = extractPort(serviceTemplate, portId);
340 componentModel.getNics().add(port);
341 context.addNic(portId, port);
344 if (CollectionUtils.isNotEmpty(images)) {
345 images.forEach(image -> {
346 Image img = new Image(image);
347 componentModel.getImages().add(img);
348 context.addImage(image, img);
352 if (CollectionUtils.isNotEmpty(computeFlavors)) {
353 computeFlavors.forEach(flavor -> {
354 ComputeData computeFlavor = new ComputeData(flavor);
355 componentModel.getCompute().add(computeFlavor);
356 context.addCompute(flavor, computeFlavor);
360 context.addComponent(componentModel);
361 context.getCreatedComponents().add(computeNodeType);
364 private Nic extractPort(ServiceTemplate serviceTemplate, String portNodeTemplateId) {
365 Optional<NodeTemplate> portNodeTemplate =
366 toscaAnalyzerService.getNodeTemplateById(serviceTemplate, portNodeTemplateId);
367 if (portNodeTemplate.isPresent()) {
368 Nic port = new Nic();
369 port.setName(portNodeTemplateId);
370 connectPortToNetwork(port, portNodeTemplate.get());
373 throw new CoreException(
374 new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", portNodeTemplateId).build());
378 private Map<String, List<String>> getNodeTemplatesGroupedByType(
379 Map<String, NodeTemplate> nodeTemplates) {
380 Map<String, List<String>> nodeTemplatesGrouped =
381 new HashMap<>(); //key - node type, value - list of node ids with this type
382 for (String nodeId : nodeTemplates.keySet()) {
383 String nodeType = nodeTemplates.get(nodeId).getType();
384 nodeTemplatesGrouped.putIfAbsent(nodeType, new ArrayList<>());
385 nodeTemplatesGrouped.get(nodeType).add(nodeId);
387 return nodeTemplatesGrouped;
390 private List<Network> extractNetworks(ServiceTemplate serviceTemplate,
391 ToscaServiceModel toscaServiceModel) {
392 List<Network> networks = new ArrayList<>();
393 Map<String, NodeTemplate> networkNodeTemplates = toscaAnalyzerService
394 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_NETWORK,
396 if (MapUtils.isEmpty(networkNodeTemplates)) {
399 for (String networkId : networkNodeTemplates.keySet()) {
400 Network network = new Network();
401 network.setName(networkId);
402 Optional<Boolean> networkDhcpValue =
403 getNetworkDhcpValue(serviceTemplate, networkNodeTemplates.get(networkId));
404 network.setDhcp(networkDhcpValue.orElse(true));
405 networks.add(network);
410 //dhcp default value is true
411 private Optional<Boolean> getNetworkDhcpValue(ServiceTemplate serviceTemplate,
412 NodeTemplate networkNodeTemplate) {
413 if (networkNodeTemplate == null) {
414 return Optional.empty();
416 if (networkNodeTemplate.getProperties() == null
417 || networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME)
419 return Optional.of(true);
423 networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME);
424 if (dhcp instanceof String) {
425 return Optional.of(Boolean.valueOf((String) dhcp));
426 } else if (dhcp instanceof Boolean) {
427 return Optional.of((Boolean) dhcp);
428 } else if (dhcp instanceof Map) {
429 String inputParameterName =
430 (String) ((Map) dhcp).get(ToscaFunctions.GET_INPUT.getFunctionName());
431 if (inputParameterName != null) {
432 ParameterDefinition inputParameterDefinition =
433 serviceTemplate.getTopology_template().getInputs().get(inputParameterName);
434 if (inputParameterDefinition != null) {
435 if (inputParameterDefinition.get_default() != null) {
436 return Optional.of(Boolean.valueOf(inputParameterDefinition.get_default().toString()));
439 throw new CoreException(
440 new ToscaInvalidEntryNotFoundErrorBuilder("Input Parameter", inputParameterName)
445 return Optional.of(true);
449 public String getComponentDisplayName(String componentName) {
450 if (componentName == null) {
453 String delimiterChar = ".";
454 if (componentName.contains(delimiterChar)) {
455 return componentName.substring(componentName.lastIndexOf(delimiterChar) + 1);
457 return componentName;