Release version 1.13.7
[sdc.git] / openecomp-be / lib / openecomp-sdc-enrichment-lib / openecomp-sdc-enrichment-impl / src / main / java / org / openecomp / sdc / enrichment / impl / tosca / PortMirroringEnricher.java
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.openecomp.sdc.enrichment.impl.tosca;
17
18 import static org.openecomp.sdc.tosca.services.DataModelUtil.getClonedObject;
19 import static org.openecomp.sdc.tosca.services.ToscaConstants.PORT_MIRRORING_CAPABILITY_CP_PROPERTY_NAME;
20 import static org.openecomp.sdc.tosca.services.ToscaConstants.PORT_MIRRORING_CAPABILITY_ID;
21
22 import com.google.common.collect.ImmutableMap;
23 import com.google.common.collect.ImmutableSet;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.Optional;
31 import java.util.Set;
32 import java.util.stream.Collectors;
33 import org.apache.commons.collections4.CollectionUtils;
34 import org.apache.commons.collections4.MapUtils;
35 import org.onap.sdc.tosca.datatypes.model.Import;
36 import org.onap.sdc.tosca.datatypes.model.NodeTemplate;
37 import org.onap.sdc.tosca.datatypes.model.RequirementAssignment;
38 import org.onap.sdc.tosca.datatypes.model.ServiceTemplate;
39 import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
40 import org.openecomp.sdc.datatypes.error.ErrorMessage;
41 import org.openecomp.sdc.enrichment.impl.tosca.model.PortMirroringConnectionPointDescription;
42 import org.openecomp.sdc.tosca.datatypes.ToscaNodeType;
43 import org.openecomp.sdc.tosca.datatypes.ToscaServiceModel;
44 import org.openecomp.sdc.tosca.services.DataModelUtil;
45 import org.openecomp.sdc.tosca.services.ToscaConstants;
46 import org.openecomp.sdc.tosca.services.ToscaUtil;
47 import org.openecomp.sdc.translator.services.heattotosca.HeatToToscaUtil;
48 import org.openecomp.sdc.translator.services.heattotosca.globaltypes.GlobalTypesGenerator;
49
50 public class PortMirroringEnricher {
51
52     private static final String ABSTRACT_LINK_REQUIREMENT_ID_PREFIX = ToscaConstants.LINK_REQUIREMENT_ID + "_";
53     private static final int ABSTRACT_LINK_REQUIREMENT_ID_PREFIX_LENGTH = ABSTRACT_LINK_REQUIREMENT_ID_PREFIX.length();
54     private static final Map<String, String> nodeTypeExternalNodeType = initializeNodeTypeExternalNodeType();
55     //Map of service template file name and map of all port node template ids, node template
56     private final Map<String, Map<String, NodeTemplate>> portNodeTemplates = new HashMap<>();
57     //Map of service template file name and map of external port node template ids, node template
58     private final Map<String, Map<String, NodeTemplate>> externalPortNodeTemplates = new HashMap<>();
59     //Map of substitution service template name and the list of ports with link requirement from the abstract
60     private final Map<String, List<String>> portNodeTemplateIdsFromAbstract = new HashMap<>();
61     private final Map<String, ServiceTemplate> globalTypesServiceTemplate = GlobalTypesGenerator
62         .getGlobalTypesServiceTemplate(OnboardingTypesEnum.ZIP);
63
64     private static ImmutableMap<String, String> initializeNodeTypeExternalNodeType() {
65         return ImmutableMap.<String, String>builder().put(ToscaNodeType.CONTRAIL_PORT, ToscaNodeType.EXTERNAL_CONTRAIL_PORT)
66             .put(ToscaNodeType.CONTRAILV2_VIRTUAL_MACHINE_INTERFACE, ToscaNodeType.EXTERNAL_VMI_PORT)
67             .put(ToscaNodeType.NEUTRON_PORT, ToscaNodeType.EXTERNAL_NEUTRON_PORT).build();
68     }
69
70     /**
71      * Enrich tosca for port mirroring.
72      *
73      * @param toscaServiceModel the tosca service model
74      * @return the map          Error descriptor map
75      */
76     public Map<String, List<ErrorMessage>> enrich(ToscaServiceModel toscaServiceModel) {
77         Map<String, List<ErrorMessage>> errors = new HashMap<>();
78         Map<String, ServiceTemplate> serviceTemplates = toscaServiceModel.getServiceTemplates();
79         serviceTemplates.entrySet().stream()
80             //Skipping the service templates which do not contain topology template
81             .filter(serviceTemplateEntry -> serviceTemplateEntry.getValue().getTopology_template() != null).forEach(serviceTemplateEntry ->
82             //Collect all the ports across all the service templates
83             collectPorts(serviceTemplateEntry.getValue()));
84         //Collect External ports from the list of all ports collected above
85         filterExternalPorts();
86         //Handle external port changes
87         handleExternalPorts(toscaServiceModel);
88         return errors;
89     }
90
91     private void collectPorts(ServiceTemplate serviceTemplate) {
92         Map<String, NodeTemplate> nodeTemplates = DataModelUtil.getNodeTemplates(serviceTemplate);
93         if (Objects.isNull(nodeTemplates)) {
94             return;
95         }
96         //Get all concrete port node templates from the service template
97         Map<String, NodeTemplate> serviceTemplatePortNodeTemplates = nodeTemplates.entrySet().stream().filter(
98             nodeTemplateEntry -> (Objects.nonNull(nodeTemplateEntry.getValue())) && (isPortNodeTemplate(nodeTemplateEntry.getValue().getType())))
99             .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
100         portNodeTemplates.put(ToscaUtil.getServiceTemplateFileName(serviceTemplate), serviceTemplatePortNodeTemplates);
101         //Get all linked internal ports from abstract node template link requirements
102         collectLinkedInternalPorts(nodeTemplates);
103     }
104
105     private void collectLinkedInternalPorts(Map<String, NodeTemplate> nodeTemplates) {
106         List<String> abstractLinkedPortNodeTemplates = new ArrayList<>();
107         for (Map.Entry<String, NodeTemplate> nodeTemplateEntry : nodeTemplates.entrySet()) {
108             NodeTemplate nodeTemplate = nodeTemplateEntry.getValue();
109             if (isSubstitutableNodeTemplate(nodeTemplate)) {
110                 handleSubstitutableNodeTemplate(abstractLinkedPortNodeTemplates, nodeTemplate);
111             }
112         }
113     }
114
115     private void handleSubstitutableNodeTemplate(List<String> abstractLinkedPortNodeTemplates, NodeTemplate nodeTemplate) {
116         List<Map<String, RequirementAssignment>> requirements = nodeTemplate.getRequirements();
117         if (Objects.isNull(requirements)) {
118             return;
119         }
120         requirements.forEach(requirement -> addInternalPortToAbstractNode(requirement, abstractLinkedPortNodeTemplates));
121         if (CollectionUtils.isNotEmpty(abstractLinkedPortNodeTemplates)) {
122             //Populate a map of the substitution service templates and list of internal ports
123             addCollectedPortsToAbstractServiceTemplatePortMap(nodeTemplate, abstractLinkedPortNodeTemplates);
124         }
125     }
126
127     private void addInternalPortToAbstractNode(Map<String, RequirementAssignment> requirement, List<String> abstractLinkedPortNodeTemplates) {
128         String requirementId = requirement.keySet().iterator().next();
129         if (requirementId.startsWith(ABSTRACT_LINK_REQUIREMENT_ID_PREFIX)) {
130             //Collect port node template ids from the link requirement ids in the abstract node template
131             abstractLinkedPortNodeTemplates.add(requirementId.substring(ABSTRACT_LINK_REQUIREMENT_ID_PREFIX_LENGTH));
132         }
133     }
134
135     private void addCollectedPortsToAbstractServiceTemplatePortMap(NodeTemplate nodeTemplate, List<String> abstractLinkedPortNodeTemplates) {
136         String substitutionServiceTemplateName;
137         if (Objects.isNull(nodeTemplate.getProperties())) {
138             return;
139         }
140         Map serviceTemplateFilter = (Map<String, Object>) nodeTemplate.getProperties().get(ToscaConstants.SERVICE_TEMPLATE_FILTER_PROPERTY_NAME);
141         substitutionServiceTemplateName = (String) serviceTemplateFilter.get(ToscaConstants.SUBSTITUTE_SERVICE_TEMPLATE_PROPERTY_NAME);
142         if (Objects.isNull(substitutionServiceTemplateName)) {
143             return;
144         }
145         if (portNodeTemplateIdsFromAbstract.containsKey(substitutionServiceTemplateName)) {
146             List<String> portList = portNodeTemplateIdsFromAbstract.get(substitutionServiceTemplateName);
147             portList.addAll(abstractLinkedPortNodeTemplates);
148             portNodeTemplateIdsFromAbstract.put(substitutionServiceTemplateName, portList);
149         } else {
150             portNodeTemplateIdsFromAbstract.put(substitutionServiceTemplateName, abstractLinkedPortNodeTemplates);
151         }
152     }
153
154     private void filterExternalPorts() {
155         for (Map.Entry<String, Map<String, NodeTemplate>> portNodeTemplateEntry : portNodeTemplates.entrySet()) {
156             Map<String, NodeTemplate> externalPorts = new HashMap<>();
157             String serviceTemplateFileName = portNodeTemplateEntry.getKey();
158             Map<String, NodeTemplate> portNodeTemplateMap = portNodeTemplateEntry.getValue();
159             for (Map.Entry<String, NodeTemplate> portNodeTemplate : portNodeTemplateMap.entrySet()) {
160                 String nodeTemplateId = portNodeTemplate.getKey();
161                 NodeTemplate nodeTemplate = portNodeTemplate.getValue();
162                 if (!isInternalPort(serviceTemplateFileName, nodeTemplateId, nodeTemplate)) {
163                     //External Port
164                     externalPorts.putIfAbsent(nodeTemplateId, nodeTemplate);
165                 }
166             }
167             externalPortNodeTemplates.putIfAbsent(serviceTemplateFileName, externalPorts);
168         }
169     }
170
171     private void updateExternalPortNodeTemplate(NodeTemplate externalPortNodeTemplate) {
172         String currentPortNodeType = externalPortNodeTemplate.getType();
173         if (nodeTypeExternalNodeType.containsKey(currentPortNodeType)) {
174             externalPortNodeTemplate.setType(nodeTypeExternalNodeType.get(currentPortNodeType));
175         }
176         addPortMirroringCapability(externalPortNodeTemplate);
177     }
178
179     private void handleExternalPorts(ToscaServiceModel toscaServiceModel) {
180         for (Map.Entry<String, Map<String, NodeTemplate>> entry : externalPortNodeTemplates.entrySet()) {
181             ServiceTemplate serviceTemplate = toscaServiceModel.getServiceTemplates().get(entry.getKey());
182             Map<String, NodeTemplate> serviceTemplateExternalPortNodeTemplates = entry.getValue();
183             if (MapUtils.isEmpty(serviceTemplateExternalPortNodeTemplates)) {
184                 continue;
185             }
186             handleExternalPortNodeTemplates(serviceTemplate, serviceTemplateExternalPortNodeTemplates);
187             addGlobalTypeImport(serviceTemplate);
188         }
189     }
190
191     private void handleExternalPortNodeTemplates(ServiceTemplate serviceTemplate, Map<String, NodeTemplate> externalPortNodeTemplates) {
192         for (Map.Entry<String, NodeTemplate> externalNodeTemplate : externalPortNodeTemplates.entrySet()) {
193             updateExternalPortNodeTemplate(externalNodeTemplate.getValue());
194             if (Objects.nonNull(DataModelUtil.getSubstitutionMappings(serviceTemplate))) {
195                 //Add port mirroring capability to substitution mapping for external ports
196                 addPortMirroringSubstitutionMappingCapability(serviceTemplate, externalNodeTemplate.getKey());
197             }
198         }
199     }
200
201     private void addPortMirroringSubstitutionMappingCapability(ServiceTemplate serviceTemplate, String externalPortNodeTemplateId) {
202         List<String> portMirroringCapability = new LinkedList<>();
203         portMirroringCapability.add(externalPortNodeTemplateId);
204         portMirroringCapability.add(PORT_MIRRORING_CAPABILITY_ID);
205         String substitutionMappingCapabilityId = PORT_MIRRORING_CAPABILITY_ID + "_" + externalPortNodeTemplateId;
206         DataModelUtil.addSubstitutionMappingCapability(serviceTemplate, substitutionMappingCapabilityId, portMirroringCapability);
207     }
208
209     private void addPortMirroringCapability(NodeTemplate portNodeTemplate) {
210         Map<String, Object> portMirroringCapabilityProperties = new HashMap<>();
211         PortMirroringConnectionPointDescription connectionPoint = new PortMirroringConnectionPointDescription();
212         if (Objects.nonNull(portNodeTemplate.getProperties())) {
213             setConnectionPointNetworkRole(portNodeTemplate, connectionPoint);
214         }
215         if (Objects.nonNull(portNodeTemplate.getRequirements())) {
216             setConnectionPointNfcType(portNodeTemplate, connectionPoint);
217         }
218         if (!connectionPoint.isEmpty()) {
219             portMirroringCapabilityProperties.put(PORT_MIRRORING_CAPABILITY_CP_PROPERTY_NAME, connectionPoint);
220             DataModelUtil.addNodeTemplateCapability(portNodeTemplate, PORT_MIRRORING_CAPABILITY_ID, portMirroringCapabilityProperties, null);
221         }
222     }
223
224     private void setConnectionPointNfcType(NodeTemplate portNodeTemplate, PortMirroringConnectionPointDescription connectionPoint) {
225         //Get NFC_Type from the binding requirement node
226         Optional<List<RequirementAssignment>> requirementAssignment = DataModelUtil
227             .getRequirementAssignment(portNodeTemplate.getRequirements(), ToscaConstants.BINDING_REQUIREMENT_ID);
228         if (requirementAssignment.isPresent()) {
229             RequirementAssignment bindingRequirementAssignment = requirementAssignment.get().get(0);
230             String node = bindingRequirementAssignment.getNode();
231             connectionPoint.setNfc_naming_code(node);
232         }
233     }
234
235     private void setConnectionPointNetworkRole(NodeTemplate portNodeTemplate, PortMirroringConnectionPointDescription connectionPoint) {
236         Object networkRolePropertyValue = portNodeTemplate.getProperties().get(ToscaConstants.PORT_NETWORK_ROLE_PROPERTY_NAME);
237         if (Objects.nonNull(networkRolePropertyValue)) {
238             Object portMirroringNetworkRolePropertyVal = getClonedObject(networkRolePropertyValue);
239             connectionPoint.setNetwork_role(portMirroringNetworkRolePropertyVal);
240         }
241     }
242
243     private void addGlobalTypeImport(ServiceTemplate serviceTemplate) {
244         List<Map<String, Import>> imports = serviceTemplate.getImports();
245         Map<String, Import> openecompIndexImport = new HashMap<>();
246         openecompIndexImport
247             .put("openecomp_index", HeatToToscaUtil.createServiceTemplateImport(globalTypesServiceTemplate.get("openecomp/_index.yml")));
248         imports.add(openecompIndexImport);
249     }
250
251     private boolean isPortNodeTemplate(String nodeType) {
252         //Check if node corresponds to a concrete port node
253         Set<String> portNodeTypes = getPortNodeTypes();
254         return Objects.nonNull(nodeType) && portNodeTypes.contains(nodeType);
255     }
256
257     private Set<String> getPortNodeTypes() {
258         return ImmutableSet.of(ToscaNodeType.NEUTRON_PORT, ToscaNodeType.CONTRAILV2_VIRTUAL_MACHINE_INTERFACE, ToscaNodeType.CONTRAIL_PORT);
259     }
260
261     private boolean isSubstitutableNodeTemplate(NodeTemplate nodeTemplate) {
262         return Objects.nonNull(nodeTemplate.getDirectives()) && nodeTemplate.getDirectives()
263             .contains(ToscaConstants.NODE_TEMPLATE_DIRECTIVE_SUBSTITUTABLE);
264     }
265
266     private boolean isInternalPort(String serviceTemplateFileName, String nodeTemplateId, NodeTemplate nodeTemplate) {
267         return isAbstractInternalPort(serviceTemplateFileName, nodeTemplateId) || isConcreteInternalPort(nodeTemplate);
268     }
269
270     private boolean isAbstractInternalPort(String serviceTemplateFileName, String nodeTemplateId) {
271         //Check if port corresponds to an abstract internal port
272         return portNodeTemplateIdsFromAbstract.containsKey(serviceTemplateFileName) && portNodeTemplateIdsFromAbstract.get(serviceTemplateFileName)
273             .contains(nodeTemplateId);
274     }
275
276     private boolean isConcreteInternalPort(NodeTemplate nodeTemplate) {
277         //Check if node template contains a link requirement
278         List<Map<String, RequirementAssignment>> requirements = nodeTemplate.getRequirements();
279         if (Objects.isNull(requirements)) {
280             return false;
281         }
282         for (Map<String, RequirementAssignment> requirement : requirements) {
283             String requirementId = requirement.keySet().iterator().next();
284             if (requirementId.equals(ToscaConstants.LINK_REQUIREMENT_ID)) {
285                 return true;
286             }
287         }
288         return false;
289     }
290 }