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.openecomp.sdc.common.errors.CoreException;
23 import org.openecomp.sdc.logging.api.Logger;
24 import org.openecomp.sdc.logging.api.LoggerFactory;
25 import org.openecomp.sdc.tosca.datatypes.ToscaCapabilityType;
26 import org.openecomp.sdc.tosca.datatypes.ToscaFunctions;
27 import org.openecomp.sdc.tosca.datatypes.ToscaNodeType;
28 import org.openecomp.sdc.tosca.datatypes.ToscaRelationshipType;
29 import org.openecomp.sdc.tosca.datatypes.ToscaServiceModel;
30 import org.onap.sdc.tosca.datatypes.model.NodeTemplate;
31 import org.onap.sdc.tosca.datatypes.model.ParameterDefinition;
32 import org.onap.sdc.tosca.datatypes.model.RequirementAssignment;
33 import org.onap.sdc.tosca.datatypes.model.ServiceTemplate;
34 import org.openecomp.sdc.tosca.errors.ToscaInvalidEntryNotFoundErrorBuilder;
35 import org.openecomp.sdc.tosca.errors.ToscaInvalidSubstituteNodeTemplateErrorBuilder;
36 import org.openecomp.sdc.tosca.errors.ToscaMissingSubstitutionMappingForReqCapErrorBuilder;
37 import org.openecomp.sdc.tosca.services.ToscaAnalyzerService;
38 import org.openecomp.sdc.tosca.services.ToscaConstants;
39 import org.onap.sdc.tosca.services.ToscaExtensionYamlUtil;
40 import org.openecomp.sdc.tosca.services.impl.ToscaAnalyzerServiceImpl;
41 import org.openecomp.sdc.vendorsoftwareproduct.services.composition.CompositionDataExtractor;
42 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Component;
43 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.ComponentData;
44 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.CompositionData;
45 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.ComputeData;
46 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.ExtractCompositionDataContext;
47 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Image;
48 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Network;
49 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Nic;
51 import java.util.ArrayList;
52 import java.util.HashMap;
53 import java.util.List;
55 import java.util.Objects;
56 import java.util.Optional;
57 import java.util.stream.Collectors;
59 public class CompositionDataExtractorImpl implements CompositionDataExtractor {
61 protected static Logger logger;
62 private static ToscaAnalyzerService toscaAnalyzerService;
64 logger = LoggerFactory.getLogger(CompositionDataExtractorImpl.class);
65 toscaAnalyzerService = new ToscaAnalyzerServiceImpl();
69 * Extract service composition data composition data.
71 * @param toscaServiceModel the tosca service model
72 * @return the composition data
74 public CompositionData extractServiceCompositionData(ToscaServiceModel toscaServiceModel) {
75 ExtractCompositionDataContext context = new ExtractCompositionDataContext();
76 String entryDefinitionServiceTemplateFileName =
77 toscaServiceModel.getEntryDefinitionServiceTemplate();
78 ServiceTemplate entryDefinitionServiceTemplate =
79 toscaServiceModel.getServiceTemplates().get(entryDefinitionServiceTemplateFileName);
80 extractServiceCompositionData(entryDefinitionServiceTemplateFileName,
81 entryDefinitionServiceTemplate, toscaServiceModel, context);
83 CompositionData compositionData = new CompositionData();
84 compositionData.setNetworks(context.getNetworks());
85 compositionData.setComponents(context.getComponents());
86 return compositionData;
89 private void extractServiceCompositionData(String serviceTemplateFileName,
90 ServiceTemplate serviceTemplate,
91 ToscaServiceModel toscaServiceModel,
92 ExtractCompositionDataContext context) {
93 if (context.getHandledServiceTemplates().contains(serviceTemplateFileName)) {
96 context.addNetworks(extractNetworks(serviceTemplate, toscaServiceModel));
97 extractComponents(serviceTemplate, toscaServiceModel, context);
98 handleSubstitution(serviceTemplate, toscaServiceModel, context);
99 context.addHandledServiceTemplates(serviceTemplateFileName);
102 private void handleSubstitution(ServiceTemplate serviceTemplate,
103 ToscaServiceModel toscaServiceModel,
104 ExtractCompositionDataContext context) {
105 Map<String, NodeTemplate> substitutableNodeTemplates =
106 toscaAnalyzerService.getSubstitutableNodeTemplates(serviceTemplate);
108 if (substitutableNodeTemplates != null) {
109 for (String substitutableNodeTemplateId : substitutableNodeTemplates.keySet()) {
110 handleSubstitutableNodeTemplate(serviceTemplate, toscaServiceModel,
111 substitutableNodeTemplateId,
112 substitutableNodeTemplates.get(substitutableNodeTemplateId), context);
117 private void handleSubstitutableNodeTemplate(ServiceTemplate serviceTemplate,
118 ToscaServiceModel toscaServiceModel,
119 String substitutableNodeTemplateId,
120 NodeTemplate substitutableNodeTemplate,
121 ExtractCompositionDataContext context) {
122 ToscaExtensionYamlUtil toscaExtensionYamlUtil = new ToscaExtensionYamlUtil();
123 Optional<String> substituteServiceTemplateFileName = toscaAnalyzerService
124 .getSubstituteServiceTemplateName(substitutableNodeTemplateId, substitutableNodeTemplate);
125 if (!substituteServiceTemplateFileName.isPresent()) {
126 throw new CoreException(
127 new ToscaInvalidSubstituteNodeTemplateErrorBuilder(substitutableNodeTemplateId).build());
129 if (context.getHandledServiceTemplates().contains(substituteServiceTemplateFileName.get())) {
130 //each substitution is should be handled once, and will get the connection to the upper
131 // service level according to the first one which was processed
135 ServiceTemplate substituteServiceTemplate =
136 toscaServiceModel.getServiceTemplates().get(substituteServiceTemplateFileName.get());
137 extractServiceCompositionData(substituteServiceTemplateFileName.get(),
138 substituteServiceTemplate, toscaServiceModel, context);
140 List<Map<String, RequirementAssignment>> substitutableRequirements =
141 substitutableNodeTemplate.getRequirements();
143 if (CollectionUtils.isEmpty(substitutableRequirements)) {
147 for (Map<String, RequirementAssignment> substitutableReq : substitutableRequirements) {
148 substitutableReq.keySet().stream().filter(reqId -> {
149 RequirementAssignment reqAssignment = toscaExtensionYamlUtil
150 .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
151 RequirementAssignment.class);
152 return isLinkToNetworkRequirementAssignment(reqAssignment);
153 }).forEach(reqId -> {
154 RequirementAssignment linkToNetworkRequirement = toscaExtensionYamlUtil
155 .yamlToObject(toscaExtensionYamlUtil.objectToYaml(substitutableReq.get(reqId)),
156 RequirementAssignment.class);
157 String connectedNodeId = linkToNetworkRequirement.getNode();
158 Optional<NodeTemplate> connectedNodeTemplate =
159 toscaAnalyzerService.getNodeTemplateById(serviceTemplate, connectedNodeId);
161 if (connectedNodeTemplate.isPresent() && toscaAnalyzerService
162 .isTypeOf(connectedNodeTemplate.get(), ToscaNodeType.NATIVE_NETWORK,
163 serviceTemplate, toscaServiceModel)) {
164 Optional<Map.Entry<String, NodeTemplate>> mappedNodeTemplate = toscaAnalyzerService
165 .getSubstitutionMappedNodeTemplateByExposedReq(
166 substituteServiceTemplateFileName.get(), substituteServiceTemplate, reqId);
167 if (!mappedNodeTemplate.isPresent()) {
168 throw new CoreException(new ToscaMissingSubstitutionMappingForReqCapErrorBuilder(
169 ToscaMissingSubstitutionMappingForReqCapErrorBuilder.MappingExposedEntry
170 .REQUIREMENT, connectedNodeId).build());
173 if (toscaAnalyzerService.isTypeOf(mappedNodeTemplate.get().getValue(),
174 ToscaNodeType.NATIVE_NETWORK_PORT, serviceTemplate,
175 toscaServiceModel)) {
176 Nic port = context.getNics().get(mappedNodeTemplate.get().getKey());
178 port.setNetworkName(connectedNodeId);
181 "Different ports define for the same component which is used in different "
182 + "substitution service templates.");
185 } else if (!connectedNodeTemplate.isPresent()) {
186 throw new CoreException(
187 new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", connectedNodeId).build());
193 private boolean isLinkToNetworkRequirementAssignment(RequirementAssignment requirement) {
194 return toscaAnalyzerService.isDesiredRequirementAssignment(requirement,
195 ToscaCapabilityType.NATIVE_NETWORK_LINKABLE, null,
196 ToscaRelationshipType.NATIVE_NETWORK_LINK_TO);
200 private void connectPortToNetwork(Nic port, NodeTemplate portNodeTemplate) {
201 List<RequirementAssignment> linkRequirementsToNetwork =
202 toscaAnalyzerService.getRequirements(portNodeTemplate, ToscaConstants.LINK_REQUIREMENT_ID);
204 //port is connected to one network
205 for (RequirementAssignment linkRequirementToNetwork : linkRequirementsToNetwork) {
206 port.setNetworkName(linkRequirementToNetwork.getNode());
211 return Map with key - compute node template id, value - list of connected port node template id
213 private Map<String, List<String>> getComputeToPortsConnection(
214 Map<String, NodeTemplate> portNodeTemplates) {
215 Map<String, List<String>> computeToPortConnection = new HashMap<>();
216 if (MapUtils.isEmpty(portNodeTemplates)) {
217 return computeToPortConnection;
219 for (String portId : portNodeTemplates.keySet()) {
220 List<RequirementAssignment> bindingRequirementsToCompute = toscaAnalyzerService
221 .getRequirements(portNodeTemplates.get(portId), ToscaConstants.BINDING_REQUIREMENT_ID);
222 for (RequirementAssignment bindingRequirementToCompute : bindingRequirementsToCompute) {
223 computeToPortConnection
224 .putIfAbsent(bindingRequirementToCompute.getNode(), new ArrayList<>());
225 computeToPortConnection.get(bindingRequirementToCompute.getNode()).add(portId);
228 return computeToPortConnection;
231 private void extractComponents(ServiceTemplate serviceTemplate,
232 ToscaServiceModel toscaServiceModel,
233 ExtractCompositionDataContext context) {
234 Map<String, NodeTemplate> computeNodeTemplates = toscaAnalyzerService
235 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_COMPUTE,
237 if (MapUtils.isEmpty(computeNodeTemplates)) {
240 Map<String, List<String>> imageNodeTemplates = getComponentImages(computeNodeTemplates,
242 Map<String, List<String>> computeFlavorNodeTemplates =
243 getComponentComputeFlavor(computeNodeTemplates, toscaServiceModel);
244 Map<String, NodeTemplate> portNodeTemplates = toscaAnalyzerService
245 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_NETWORK_PORT,
247 Map<String, List<String>> computeToPortsConnection =
248 getComputeToPortsConnection(portNodeTemplates);
249 Map<String, List<String>> computesGroupedByType =
250 getNodeTemplatesGroupedByType(computeNodeTemplates);
252 computesGroupedByType.keySet()
255 !context.getCreatedComponents().contains(nodeType))
256 .forEach(nodeType -> extractComponent(serviceTemplate, computeToPortsConnection,
257 computesGroupedByType, imageNodeTemplates, computeFlavorNodeTemplates, nodeType,
261 private Map<String,List<String>> getComponentImages(Map<String, NodeTemplate>
262 computeNodeTemplates,
263 ToscaServiceModel toscaServiceModel) {
264 return getComponentProperty(ToscaConstants.COMPUTE_IMAGE, computeNodeTemplates, toscaServiceModel);
267 private Map<String,List<String>> getComponentComputeFlavor(Map<String, NodeTemplate>
268 computeNodeTemplates,
269 ToscaServiceModel toscaServiceModel) {
270 return getComponentProperty(ToscaConstants.COMPUTE_FLAVOR, computeNodeTemplates, toscaServiceModel);
273 private Map<String, List<String>> getComponentProperty(String propertyName,
274 Map<String, NodeTemplate> computeNodeTemplates,
275 ToscaServiceModel toscaServiceModel) {
276 Map<String,List<String>> componentPropertyValues = new HashMap<>();
277 for (String component : computeNodeTemplates.keySet()) {
278 List<String> computes = new ArrayList<>();
279 Map<String,Object> properties = computeNodeTemplates.get(component).getProperties();
281 if(MapUtils.isEmpty(properties)){
285 List<Object> computesList = properties.entrySet()
287 .filter(map -> map.getKey().equals(propertyName))
288 .map(Map.Entry::getValue)
289 .collect(Collectors.toList());
290 for (Object obj : computesList) {
291 if (obj instanceof String) {
292 computes.add((String) obj);
294 Map<String, String> objMap = new ObjectMapper().convertValue(obj, Map.class);
295 computes.add(getInputs(toscaServiceModel, objMap.get("get_input")));
298 componentPropertyValues.put(component,computes);
300 return componentPropertyValues;
303 private String getInputs(ToscaServiceModel toscaServiceModel, String inputValue) {
304 String mainTemplate = toscaServiceModel.getEntryDefinitionServiceTemplate();
305 List<ServiceTemplate> toscaServiceTemplates = toscaServiceModel.getServiceTemplates().entrySet()
307 .filter(map -> map.getKey().equals(mainTemplate))
308 .map(Map.Entry::getValue)
309 .collect(Collectors.toList());
310 ServiceTemplate serviceTemplate = toscaServiceTemplates.get(0);
312 if (Objects.nonNull(serviceTemplate.getTopology_template())
313 && MapUtils.isNotEmpty(serviceTemplate.getTopology_template().getInputs())) {
314 for (Map.Entry<String, ParameterDefinition> inputEntry : serviceTemplate
315 .getTopology_template().getInputs().entrySet()) {
316 if (inputEntry.getKey().equals(inputValue)) {
319 value= (String) inputEntry.getValue().get_default();
320 } catch (Exception e) {
321 logger.debug(e.getMessage(), e);
322 value = inputEntry.getValue().get_default().toString();
331 private void extractComponent(ServiceTemplate serviceTemplate,
332 Map<String, List<String>> computeToPortsConnection,
333 Map<String, List<String>> computesGroupedByType,
334 Map<String, List<String>> imageList,
335 Map<String, List<String>> computeFlavorNodeTemplates,
336 String computeNodeType,
337 ExtractCompositionDataContext context) {
338 ComponentData component = new ComponentData();
339 component.setName(computeNodeType);
340 component.setDisplayName(getComponentDisplayName(component.getName()));
341 Component componentModel = new Component();
342 componentModel.setData(component);
344 String computeId = computesGroupedByType.get(computeNodeType).get(0);
345 List<String> connectedPortIds = computeToPortsConnection.get(computeId);
346 List<String> images = imageList.get(computeId);
347 List<String> computeFlavors = computeFlavorNodeTemplates.get(computeId);
349 if (connectedPortIds != null) {
350 componentModel.setNics(new ArrayList<>());
351 componentModel.setImages(new ArrayList<>());
352 componentModel.setCompute(new ArrayList<>());
353 for (String portId : connectedPortIds) {
354 Nic port = extractPort(serviceTemplate, portId);
355 componentModel.getNics().add(port);
356 context.addNic(portId, port);
358 for (String image : images) {
359 Image img = new Image(image);
360 componentModel.getImages().add(img);
361 context.addImage(image, img);
363 for (String flavor : computeFlavors) {
364 ComputeData computeFlavor = new ComputeData(flavor);
365 componentModel.getCompute().add(computeFlavor);
366 context.addCompute(flavor,computeFlavor);
369 context.addComponent(componentModel);
370 context.getCreatedComponents().add(computeNodeType);
373 private Nic extractPort(ServiceTemplate serviceTemplate, String portNodeTemplateId) {
374 Optional<NodeTemplate> portNodeTemplate =
375 toscaAnalyzerService.getNodeTemplateById(serviceTemplate, portNodeTemplateId);
376 if (portNodeTemplate.isPresent()) {
377 Nic port = new Nic();
378 port.setName(portNodeTemplateId);
379 connectPortToNetwork(port, portNodeTemplate.get());
382 throw new CoreException(
383 new ToscaInvalidEntryNotFoundErrorBuilder("Node Template", portNodeTemplateId).build());
387 private Map<String, List<String>> getNodeTemplatesGroupedByType(
388 Map<String, NodeTemplate> nodeTemplates) {
389 Map<String, List<String>> nodeTemplatesGrouped =
390 new HashMap<>(); //key - node type, value - list of node ids with this type
391 for (String nodeId : nodeTemplates.keySet()) {
392 String nodeType = nodeTemplates.get(nodeId).getType();
393 nodeTemplatesGrouped.putIfAbsent(nodeType, new ArrayList<>());
394 nodeTemplatesGrouped.get(nodeType).add(nodeId);
396 return nodeTemplatesGrouped;
399 private List<Network> extractNetworks(ServiceTemplate serviceTemplate,
400 ToscaServiceModel toscaServiceModel) {
401 List<Network> networks = new ArrayList<>();
402 Map<String, NodeTemplate> networkNodeTemplates = toscaAnalyzerService
403 .getNodeTemplatesByType(serviceTemplate, ToscaNodeType.NATIVE_NETWORK,
405 if (MapUtils.isEmpty(networkNodeTemplates)) {
408 for (String networkId : networkNodeTemplates.keySet()) {
409 Network network = new Network();
410 network.setName(networkId);
411 Optional<Boolean> networkDhcpValue =
412 getNetworkDhcpValue(serviceTemplate, networkNodeTemplates.get(networkId));
413 network.setDhcp(networkDhcpValue.orElse(true));
414 networks.add(network);
419 //dhcp default value is true
420 private Optional<Boolean> getNetworkDhcpValue(ServiceTemplate serviceTemplate,
421 NodeTemplate networkNodeTemplate) {
422 if (networkNodeTemplate == null) {
423 return Optional.empty();
425 if (networkNodeTemplate.getProperties() == null
426 || networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME)
428 return Optional.of(true);
432 networkNodeTemplate.getProperties().get(ToscaConstants.DHCP_ENABLED_PROPERTY_NAME);
433 if (dhcp instanceof String) {
434 return Optional.of(Boolean.valueOf((String) dhcp));
435 } else if (dhcp instanceof Boolean) {
436 return Optional.of((Boolean) dhcp);
437 } else if (dhcp instanceof Map) {
438 String inputParameterName =
439 (String) ((Map) dhcp).get(ToscaFunctions.GET_INPUT.getDisplayName());
440 if (inputParameterName != null) {
441 ParameterDefinition inputParameterDefinition =
442 serviceTemplate.getTopology_template().getInputs().get(inputParameterName);
443 if (inputParameterDefinition != null) {
444 if (inputParameterDefinition.get_default() != null) {
445 return Optional.of(Boolean.valueOf(inputParameterDefinition.get_default().toString()));
448 throw new CoreException(
449 new ToscaInvalidEntryNotFoundErrorBuilder("Input Parameter", inputParameterName)
454 return Optional.of(true);
458 public String getComponentDisplayName(String componentName) {
459 if (componentName == null) {
462 String delimiterChar = ".";
463 if (componentName.contains(delimiterChar)) {
464 return componentName.substring(componentName.lastIndexOf(delimiterChar) + 1);
466 return componentName;