Merge "Port mirroring"
diff --git a/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/capabilities.yml b/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/capabilities.yml
index e395e3f..285d6b2 100644
--- a/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/capabilities.yml
+++ b/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/capabilities.yml
@@ -147,4 +147,11 @@
     derived_from: tosca.capabilities.Root
 
   tosca.capabilities.nfv.ext.LocalAttachment:
-    derived_from: tosca.capabilities.Root
\ No newline at end of file
+    derived_from: tosca.capabilities.Root
+  # New capability types for Port Mirroring
+  org.openecomp.capabilities.PortMirroring:
+    derived_from: tosca.capabilities.Root
+    properties:
+      connection_point:
+        type: org.openecomp.datatypes.PortMirroringConnectionPointDescription
+        required: true
\ No newline at end of file
diff --git a/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/data.yml b/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/data.yml
index 85ff50c..c395675 100644
--- a/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/data.yml
+++ b/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/data.yml
@@ -92,7 +92,7 @@
         type: string
         status: SUPPORTED
         required: true
-        
+
   org.openecomp.datatypes.EcompHoming:
     derived_from: org.openecomp.datatypes.Root
     properties:
@@ -468,7 +468,7 @@
         description: Referenc to naming policy that OPENECOMP will use when the name is auto-generated
         type: string
         required: false
-                
+
   org.openecomp.datatypes.Naming:
     derived_from: org.openecomp.datatypes.Root
     properties:
@@ -478,7 +478,7 @@
         type: boolean
         default: true
         required: true
-  
+
   org.openecomp.datatypes.EcompGeneratedNaming:
     derived_from: org.openecomp.datatypes.Naming
     properties:
@@ -486,7 +486,7 @@
         description: Referenc to naming policy that OPENECOMP will use when the name is auto-generated
         type: string
         required: false
-                                
+
   org.openecomp.datatypes.UserDefinedNaming:
     derived_from: org.openecomp.datatypes.Naming
     properties:
@@ -555,6 +555,18 @@
         required: false
         default: md5
 
+  # New data types for Port Mirroring
+  org.openecomp.datatypes.PortMirroringConnectionPointDescription:
+    properties:
+      nf_type:
+        type: string
+      nfc_type:
+        type: string
+      network_role:
+        type: string
+      pps_capacity:
+        type: string
+
   tosca.datatypes.nfv.RequestedAdditionalCapability:
     derived_from: tosca.datatypes.Root
     properties:
diff --git a/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/nodes.yml b/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/nodes.yml
index da41c93..810bf30 100644
--- a/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/nodes.yml
+++ b/common/openecomp-tosca-datatype/src/main/resources/globalTypes/openecomp/nodes.yml
@@ -570,6 +570,222 @@
         entry_schema:
           type: org.openecomp.datatypes.ImageInfo
         required: false
+
+  ## New node types for Port Mirroring
+  org.openecomp.nodes.ServiceProxy:
+    derived_from: tosca.nodes.Root
+
+  org.openecomp.nodes.PortMirroringConfiguration:
+    derived_from: tosca.nodes.Root
+    requirements:
+    - source:
+        capability: org.openecomp.capabilities.PortMirroring
+        occurrences:
+        - 1
+        - UNBOUNDED
+    - collector:
+        capability: org.openecomp.capabilities.PortMirroring
+        occurrences:
+        - 1
+        - 1
+
+  org.openecomp.resource.cp.v2.extCP:
+    derived_from: org.openecomp.resource.cp.nodes.network.Port
+    description: The SDC External Connection Point base type
+    capabilities:
+      port_mirroring:
+        type: org.openecomp.capabilities.PortMirroring
+
+  org.openecomp.resource.cp.v2.extNeutronCP:
+    derived_from: org.openecomp.resource.cp.v2.extCP
+    properties:
+      port_security_enabled:
+        type: boolean
+        description: Flag to enable/disable port security on the network
+        required: false
+        status: SUPPORTED
+      device_id:
+        type: string
+        description: Device ID of this port
+        required: false
+        status: SUPPORTED
+      qos_policy:
+        type: string
+        description: The name or ID of QoS policy to attach to this network
+        required: false
+        status: SUPPORTED
+      allowed_address_pairs:
+        type: list
+        description: Additional MAC/IP address pairs allowed to pass through the port
+        required: false
+        status: SUPPORTED
+        entry_schema:
+          type: org.openecomp.datatypes.heat.network.AddressPair
+      binding:vnic_type:
+        type: string
+        description: The vnic type to be bound on the neutron port
+        required: false
+        status: SUPPORTED
+        constraints:
+        - valid_values:
+          - macvtap
+          - direct
+          - normal
+      value_specs:
+        type: map
+        description: Extra parameters to include in the request
+        required: false
+        default: {
+          }
+        status: SUPPORTED
+        entry_schema:
+          type: string
+      device_owner:
+        type: string
+        description: Name of the network owning the port
+        required: false
+        status: SUPPORTED
+      network:
+        type: string
+        description: Network this port belongs to
+        required: false
+        status: SUPPORTED
+      replacement_policy:
+        type: string
+        description: Policy on how to respond to a stack-update for this resource
+        required: false
+        default: AUTO
+        status: SUPPORTED
+        constraints:
+        - valid_values:
+          - REPLACE_ALWAYS
+          - AUTO
+      security_groups:
+        type: list
+        description: List of security group names or IDs
+        required: false
+        status: SUPPORTED
+        entry_schema:
+          type: string
+      fixed_ips:
+        type: list
+        description: Desired IPs for this port
+        required: false
+        status: SUPPORTED
+        entry_schema:
+          type: org.openecomp.datatypes.heat.neutron.port.FixedIps
+      mac_address:
+        type: string
+        description: MAC address to give to this port
+        required: false
+        status: SUPPORTED
+      admin_state_up:
+        type: boolean
+        description: A boolean value specifying the administrative status of the network
+        required: false
+        default: true
+        status: SUPPORTED
+      name:
+        type: string
+        description: A symbolic name for this port
+        required: false
+        status: SUPPORTED
+    attributes:
+      tenant_id:
+        type: string
+        description: Tenant owning the port
+        status: SUPPORTED
+      network_id:
+        type: string
+        description: Unique identifier for the network owning the port
+        status: SUPPORTED
+      qos_policy_id:
+        type: string
+        description: The QoS policy ID attached to this network
+        status: SUPPORTED
+      show:
+        type: string
+        description: Detailed information about resource
+        status: SUPPORTED
+      subnets:
+        type: list
+        description: Subnets of this network
+        status: SUPPORTED
+        entry_schema:
+          type: string
+      status:
+        type: string
+        description: The status of the network
+        status: SUPPORTED
+    capabilities:
+      attachment:
+        type: tosca.capabilities.Attachment
+        occurrences:
+        - 1
+        - UNBOUNDED
+      binding:
+        type: tosca.capabilities.network.Bindable
+        valid_source_types:
+        - org.openecomp.resource.cp.nodes.heat.network.contrailV2.VLANSubInterface
+        occurrences:
+        - 0
+        - UNBOUNDED
+
+  org.openecomp.resource.cp.v2.extContrailCP:
+    derived_from: org.openecomp.resource.cp.v2.extCP
+    properties:
+      static_routes:
+        type: list
+        description: An ordered list of static routes to be added to this interface
+        required: false
+        status: SUPPORTED
+        entry_schema:
+          type: org.openecomp.datatypes.heat.network.contrail.port.StaticRoute
+      virtual_network:
+        type: string
+        description: Virtual Network for this interface
+        required: true
+        status: SUPPORTED
+      static_route:
+        type: boolean
+        description: Static route enabled
+        required: false
+        default: false
+        status: SUPPORTED
+      allowed_address_pairs:
+        type: list
+        description: List of allowed address pair for this interface
+        required: false
+        status: SUPPORTED
+        entry_schema:
+          type: org.openecomp.datatypes.heat.network.contrail.AddressPair
+      shared_ip:
+        type: boolean
+        description: Shared ip enabled
+        required: false
+        default: false
+        status: SUPPORTED
+      ip_address:
+        type: string
+        description: IP for this interface
+        required: false
+        status: SUPPORTED
+      interface_type:
+        type: string
+        description: Interface type
+        required: true
+        status: SUPPORTED
+        constraints:
+        - valid_values:
+          - management
+          - left
+          - right
+          - other
+    attributes:
+      fq_name:
+        type: string
+        description: fq_name
+        status: SUPPORTED
   tosca.nodes.nfv.NS.vEPC_NS:
     derived_from: tosca.nodes.nfv.NS
     properties:
diff --git a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/PortMirroringEnricher.java b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/PortMirroringEnricher.java
new file mode 100644
index 0000000..130590c
--- /dev/null
+++ b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/PortMirroringEnricher.java
@@ -0,0 +1,330 @@
+package org.openecomp.sdc.enrichment.impl.tosca;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.openecomp.sdc.datatypes.error.ErrorMessage;
+import org.openecomp.sdc.enrichment.impl.tosca.model.PortMirroringConnectionPointDescription;
+import org.openecomp.sdc.logging.context.impl.MdcDataDebugMessage;
+import org.openecomp.sdc.tosca.datatypes.ToscaNodeType;
+import org.openecomp.sdc.tosca.datatypes.ToscaServiceModel;
+import org.openecomp.sdc.tosca.datatypes.model.CapabilityAssignment;
+import org.openecomp.sdc.tosca.datatypes.model.Import;
+import org.openecomp.sdc.tosca.datatypes.model.NodeTemplate;
+import org.openecomp.sdc.tosca.datatypes.model.PropertyDefinition;
+import org.openecomp.sdc.tosca.datatypes.model.RequirementAssignment;
+import org.openecomp.sdc.tosca.datatypes.model.ServiceTemplate;
+import org.openecomp.sdc.tosca.services.DataModelUtil;
+import org.openecomp.sdc.tosca.services.ToscaConstants;
+import org.openecomp.sdc.tosca.services.ToscaUtil;
+import org.openecomp.sdc.translator.services.heattotosca.HeatToToscaUtil;
+import org.openecomp.sdc.translator.services.heattotosca.globaltypes.GlobalTypesGenerator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static org.openecomp.sdc.tosca.services.DataModelUtil.getClonedObject;
+import static org.openecomp.sdc.tosca.services.ToscaConstants.PORT_MIRRORING_CAPABILITY_CP_PROPERTY_NAME;
+import static org.openecomp.sdc.tosca.services.ToscaConstants.PORT_MIRRORING_CAPABILITY_ID;
+
+public class PortMirroringEnricher {
+  //Map of service template file name and map of all port node template ids, node template
+  private Map<String, Map<String, NodeTemplate>> portNodeTemplates = new HashMap<>();
+  //Map of service template file name and map of external port node template ids, node template
+  private Map<String, Map<String, NodeTemplate>> externalPortNodeTemplates = new HashMap<>();
+  //Map of substitution service template name and the list of ports with link requirement from
+  // the abstract
+  private Map<String, List<String>> portNodeTemplateIdsFromAbstract = new HashMap<>();
+  private static MdcDataDebugMessage mdcDataDebugMessage = new MdcDataDebugMessage();
+  private Map<String, ServiceTemplate> globalTypesServiceTemplate =
+      GlobalTypesGenerator.getGlobalTypesServiceTemplate();
+
+  /**
+   * Enrich tosca for port mirroring.
+   *
+   * @param toscaServiceModel the tosca service model
+   * @return the map          Error descriptor map
+   */
+  public Map<String,List<ErrorMessage>> enrich(ToscaServiceModel toscaServiceModel) {
+    mdcDataDebugMessage.debugEntryMessage(null);
+    Map<String, List<ErrorMessage>> errors = new HashMap<>();
+      Map<String, ServiceTemplate> serviceTemplates = toscaServiceModel.getServiceTemplates();
+      serviceTemplates.entrySet().stream()
+          //Skipping the service templates which do not contain topology template
+          .filter(serviceTemplateEntry -> serviceTemplateEntry.getValue()
+              .getTopology_template() != null)
+          .forEach(serviceTemplateEntry ->
+              //Collect all the ports across all the service templates
+              collectPorts(serviceTemplateEntry.getValue()));
+      //Collect External ports from the list of all ports collected above
+      filterExternalPorts(toscaServiceModel);
+      //Handle external port changes
+      handleExternalPorts(toscaServiceModel);
+    mdcDataDebugMessage.debugExitMessage(null);
+    return errors;
+  }
+
+  private void collectPorts(ServiceTemplate serviceTemplate) {
+    Map<String, NodeTemplate> nodeTemplates =
+        serviceTemplate.getTopology_template().getNode_templates();
+    if (Objects.nonNull(nodeTemplates)) {
+      //Get all concrete port node templates from the service template
+      Map<String, NodeTemplate> serviceTemplatePortNodeTemplates = nodeTemplates.entrySet().stream()
+          .filter(nodeTemplateEntry -> isPortNodeTemplate(nodeTemplateEntry.getValue()))
+          .collect(Collectors.toMap(nodeTemplateEntry -> nodeTemplateEntry.getKey(),
+              nodeTemplateEntry -> nodeTemplateEntry.getValue()));
+
+      portNodeTemplates.put(ToscaUtil.getServiceTemplateFileName(serviceTemplate),
+          serviceTemplatePortNodeTemplates);
+
+      //Get all linked internal ports from abstract node template link requirements
+      List<String> abstractLinkedPortNodeTemplates = new ArrayList<>();
+      for (Map.Entry<String, NodeTemplate> nodeTemplateEntry : nodeTemplates.entrySet()) {
+        NodeTemplate nodeTemplate = nodeTemplateEntry.getValue();
+        if (isSubstitutableNodeTemplate(nodeTemplate)) {
+          List<Map<String, RequirementAssignment>> requirements = nodeTemplate.getRequirements();
+          if (Objects.nonNull(requirements)) {
+            for (Map<String, RequirementAssignment> requirement : requirements) {
+              String requirementId = requirement.keySet().iterator().next();
+              String abstractLinkRequirementIdPrefix = ToscaConstants.LINK_REQUIREMENT_ID + "_";
+              if (requirementId.startsWith(abstractLinkRequirementIdPrefix)) {
+                //Collect port node template ids from the link requirement ids in the abstract
+                // node template
+                abstractLinkedPortNodeTemplates.add(requirementId.substring(requirementId
+                    .indexOf("_") + 1));
+              }
+            }
+          }
+          if (CollectionUtils.isNotEmpty(abstractLinkedPortNodeTemplates)) {
+            //Populate a map of the substitution service templates and list of internal ports
+            addCollectedPortsToAbstractServiceTemplatePortMap(nodeTemplate,
+                abstractLinkedPortNodeTemplates);
+          }
+        }
+      }
+    }
+  }
+
+  private void addCollectedPortsToAbstractServiceTemplatePortMap(NodeTemplate nodeTemplate,
+                                                                 List<String>
+                                                                     abstractLinkedPortNodeTemplates) {
+    String substitutionServiceTemplateName = null;
+    if (nodeTemplate.getProperties() != null) {
+      Map serviceTemplateFilter = (Map<String, Object>) nodeTemplate.getProperties()
+          .get(ToscaConstants.SERVICE_TEMPLATE_FILTER_PROPERTY_NAME);
+      substitutionServiceTemplateName = (String)
+          serviceTemplateFilter.get(ToscaConstants.SUBSTITUTE_SERVICE_TEMPLATE_PROPERTY_NAME);
+      if (Objects.nonNull(substitutionServiceTemplateName)) {
+        if (portNodeTemplateIdsFromAbstract.containsKey(substitutionServiceTemplateName)) {
+          List<String> portList =
+              portNodeTemplateIdsFromAbstract.get(substitutionServiceTemplateName);
+          portList.addAll(abstractLinkedPortNodeTemplates);
+          portNodeTemplateIdsFromAbstract.put(substitutionServiceTemplateName, portList);
+        } else {
+          portNodeTemplateIdsFromAbstract.put(substitutionServiceTemplateName,
+              abstractLinkedPortNodeTemplates);
+        }
+      }
+    }
+  }
+
+  private void filterExternalPorts(ToscaServiceModel toscaServiceModel) {
+    for (Map.Entry<String, Map<String, NodeTemplate>> portNodeTemplateEntry : portNodeTemplates
+        .entrySet()) {
+      Map<String, NodeTemplate> externalPorts = new HashMap<>();
+      String serviceTemplateFileName = portNodeTemplateEntry.getKey();
+      Map<String, NodeTemplate> portNodeTemplateMap = portNodeTemplateEntry.getValue();
+      for (Map.Entry<String, NodeTemplate> portNodeTemplate : portNodeTemplateMap.entrySet()) {
+        String nodeTemplateId = portNodeTemplate.getKey();
+        NodeTemplate nodeTemplate = portNodeTemplate.getValue();
+        String newPortNodeType = nodeTemplate.getType();
+        if (!isInternalPort(serviceTemplateFileName, nodeTemplateId, nodeTemplate)) {
+          //External Port
+          externalPorts.putIfAbsent(nodeTemplateId, nodeTemplate);
+        }
+      }
+      externalPortNodeTemplates.putIfAbsent(serviceTemplateFileName, externalPorts);
+    }
+  }
+
+  private void updateExternalPortNodeTemplate(NodeTemplate externalPortNodeTemplate,
+                                              ToscaServiceModel toscaServiceModel) {
+    String currentPortNodeType = externalPortNodeTemplate.getType();
+    if (currentPortNodeType.equals(ToscaNodeType.CONTRAIL_PORT)
+        || currentPortNodeType.equals(ToscaNodeType.CONTRAILV2_VIRTUAL_MACHINE_INTERFACE)) {
+      //Set external contrail port node type
+      externalPortNodeTemplate.setType(ToscaNodeType.EXTERNAL_CONTRAIL_PORT);
+      addPortMirroringCapability(externalPortNodeTemplate);
+    } else if (currentPortNodeType.equals(ToscaNodeType.NEUTRON_PORT)) {
+      //Set external neutron port node type
+      externalPortNodeTemplate.setType(ToscaNodeType.EXTERNAL_NEUTRON_PORT);
+      addPortMirroringCapability(externalPortNodeTemplate);
+    }
+  }
+
+  private void handleExternalPorts(ToscaServiceModel toscaServiceModel) {
+
+    for (Map.Entry<String, Map<String, NodeTemplate>> entry : externalPortNodeTemplates
+        .entrySet()) {
+      String serviceTemplateName = entry.getKey();
+      ServiceTemplate serviceTemplate =
+          toscaServiceModel.getServiceTemplates().get(serviceTemplateName);
+      Map<String, NodeTemplate> externalNodeTemplates = entry.getValue();
+      if (MapUtils.isNotEmpty(externalNodeTemplates)) {
+        for (Map.Entry<String, NodeTemplate> externalNodeTemplate : externalNodeTemplates
+            .entrySet()) {
+          String externalPortNodeTemplateId = externalNodeTemplate.getKey();
+          updateExternalPortNodeTemplate(externalNodeTemplate.getValue(), toscaServiceModel);
+          if (serviceTemplate.getTopology_template().getSubstitution_mappings() != null) {
+            //Add port mirroring capability to substitution mapping for external ports
+            addPortMirroringSubstitutionMappingCapability(serviceTemplate,
+                externalPortNodeTemplateId);
+          }
+          handleExternalPortProperties(externalNodeTemplate.getValue());
+        }
+        addGlobalTypeImport(serviceTemplate);
+      }
+    }
+  }
+
+  private void handleExternalPortProperties(NodeTemplate portNodeTemplate){
+
+    ServiceTemplate serviceTemplate = globalTypesServiceTemplate.get("openecomp/nodes.yml");
+    String externalPortType = portNodeTemplate.getType();
+    Map<String, PropertyDefinition> globalTypesportProperties = new HashMap<>();
+    globalTypesportProperties.putAll(serviceTemplate.getNode_types().get("org.openecomp.resource.cp.nodes.network.Port").getProperties());
+    globalTypesportProperties.putAll(serviceTemplate.getNode_types().get(externalPortType).getProperties());
+
+    Map<String, Object> properties = portNodeTemplate.getProperties();
+    Map<String, Object> filteredProperties = new HashMap<>();
+
+    if(MapUtils.isEmpty(properties)){
+      return;
+    }
+
+    for(Map.Entry<String, Object> propertyEntry: properties.entrySet()){
+      if(globalTypesportProperties.containsKey(propertyEntry.getKey())){
+        filteredProperties.put(propertyEntry.getKey(), propertyEntry.getValue());
+      }
+    }
+
+    if(!MapUtils.isEmpty(filteredProperties)) {
+      portNodeTemplate.setProperties(filteredProperties);
+    }else{
+      portNodeTemplate.setProperties(null);
+    }
+
+  }
+
+  private void addPortMirroringSubstitutionMappingCapability(ServiceTemplate serviceTemplate,
+                                                             String externalPortNodeTemplateId) {
+    List<String> portMirroringCapability = new LinkedList<>();
+    portMirroringCapability.add(externalPortNodeTemplateId);
+    portMirroringCapability.add(PORT_MIRRORING_CAPABILITY_ID);
+    String substitutionMappingCapabilityId = PORT_MIRRORING_CAPABILITY_ID + "_"
+        + externalPortNodeTemplateId;
+    DataModelUtil.addSubstitutionMappingCapability(serviceTemplate,
+        substitutionMappingCapabilityId, portMirroringCapability);
+  }
+
+  private void addPortMirroringCapability(NodeTemplate portNodeTemplate) {
+    List<Map<String, CapabilityAssignment>> capabilities = portNodeTemplate.getCapabilities();
+    if (Objects.isNull(capabilities)) {
+      capabilities = new ArrayList<>();
+    }
+    Map<String, Object> portMirroringCapabilityProperties = new HashMap<>();
+    PortMirroringConnectionPointDescription connectionPoint = new
+        PortMirroringConnectionPointDescription();
+    //Get Network role property
+    if (Objects.nonNull(portNodeTemplate.getProperties())) {
+      Object networkRolePropertyValue =
+          portNodeTemplate.getProperties().get(ToscaConstants.PORT_NETWORK_ROLE_PROPERTY_NAME);
+      if (Objects.nonNull(networkRolePropertyValue)) {
+        Object portMirroringNetworkRolePropertyVal = getClonedObject(networkRolePropertyValue);
+        connectionPoint.setNetwork_role(portMirroringNetworkRolePropertyVal);
+      }
+    }
+    //Get NFC_Type from the binding requirement node
+    if (Objects.nonNull(portNodeTemplate.getRequirements())) {
+      Optional<List<RequirementAssignment>> requirementAssignment =
+          DataModelUtil.getRequirementAssignment(portNodeTemplate.getRequirements(), ToscaConstants
+              .BINDING_REQUIREMENT_ID);
+      if (requirementAssignment.isPresent()) {
+        RequirementAssignment bindingRequirementAssignment = requirementAssignment.get().get(0);
+        String node = bindingRequirementAssignment.getNode();
+        connectionPoint.setNfc_type(node);
+      }
+    }
+
+    if (!connectionPoint.isEmpty()) {
+      portMirroringCapabilityProperties.put(PORT_MIRRORING_CAPABILITY_CP_PROPERTY_NAME,
+          connectionPoint);
+      DataModelUtil.addNodeTemplateCapability(portNodeTemplate,
+          PORT_MIRRORING_CAPABILITY_ID, portMirroringCapabilityProperties, null);
+    }
+  }
+
+  private void addGlobalTypeImport(ServiceTemplate serviceTemplate) {
+    List<Map<String, Import>> imports = serviceTemplate.getImports();
+    Map<String, Import> openecompIndexImport = new HashMap<>();
+    openecompIndexImport.put("openecomp_index",
+        HeatToToscaUtil.createServiceTemplateImport(globalTypesServiceTemplate
+            .get("openecomp/_index.yml")));
+    imports.add(openecompIndexImport);
+  }
+
+  private boolean isPortNodeTemplate(NodeTemplate nodeTemplate) {
+    String nodeType = nodeTemplate.getType();
+    //Check if node corresponds to a concrete port node
+    if (nodeType.equals(ToscaNodeType.NEUTRON_PORT)
+        || nodeType.equals(ToscaNodeType.CONTRAILV2_VIRTUAL_MACHINE_INTERFACE)
+        || nodeType.equals(ToscaNodeType.CONTRAIL_PORT)
+        || nodeType.equals(ToscaNodeType.NETWORK_PORT)
+        || nodeType.equals(ToscaNodeType.NATIVE_NETWORK_PORT)) {
+      return true;
+    }
+    return false;
+  }
+
+  private boolean isSubstitutableNodeTemplate(NodeTemplate nodeTemplate) {
+    if (Objects.nonNull(nodeTemplate.getDirectives())) {
+      return nodeTemplate.getDirectives().contains(ToscaConstants
+          .NODE_TEMPLATE_DIRECTIVE_SUBSTITUTABLE);
+    }
+    return false;
+  }
+
+  private boolean isInternalPort(String serviceTemplateFileName, String nodeTemplateId,
+                                 NodeTemplate nodeTemplate) {
+    return isAbstractInternalPort(serviceTemplateFileName, nodeTemplateId)
+        || isConcreteInternalPort(nodeTemplate);
+  }
+
+  private boolean isAbstractInternalPort(String serviceTemplateFileName, String nodeTemplateId) {
+    //Check if port corresponds to an abstract internal port
+    if (portNodeTemplateIdsFromAbstract.containsKey(serviceTemplateFileName)) {
+      return portNodeTemplateIdsFromAbstract.get(serviceTemplateFileName).contains(nodeTemplateId);
+    }
+    return false;
+  }
+
+
+  private boolean isConcreteInternalPort(NodeTemplate nodeTemplate) {
+    //Check if node template contains a link requirement
+    List<Map<String, RequirementAssignment>> requirements = nodeTemplate.getRequirements();
+    if (Objects.nonNull(requirements)) {
+      for (Map<String, RequirementAssignment> requirement : requirements) {
+        String requirementId = requirement.keySet().iterator().next();
+        if (requirementId.equals(ToscaConstants.LINK_REQUIREMENT_ID)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/ToscaEnricher.java b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/ToscaEnricher.java
index f1804c8..090c3ae 100644
--- a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/ToscaEnricher.java
+++ b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/ToscaEnricher.java
@@ -37,6 +37,7 @@
   public Map<String, List<ErrorMessage>> enrich() {
     Map<String, List<ErrorMessage>> errors = new HashMap<>();
     errors.putAll(enrichAbstractSubstitute());
+    errors.putAll(enrichPortMirroring());
 
     return errors;
   }
@@ -55,4 +56,14 @@
     mdcDataDebugMessage.debugExitMessage(null, null);
     return enrichErrors;
   }
+
+  private Map<String, List<ErrorMessage>> enrichPortMirroring() {
+    mdcDataDebugMessage.debugEntryMessage(null, null);
+    Map<String, List<ErrorMessage>> enrichErrors;
+    ToscaServiceModel toscaModel = (ToscaServiceModel) model;
+    PortMirroringEnricher portMirroringEnricher = new PortMirroringEnricher();
+    enrichErrors = portMirroringEnricher.enrich(toscaModel);
+    mdcDataDebugMessage.debugExitMessage(null, null);
+    return enrichErrors;
+  }
 }
diff --git a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/model/PortMirroringConnectionPointDescription.java b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/model/PortMirroringConnectionPointDescription.java
new file mode 100644
index 0000000..c166938
--- /dev/null
+++ b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/src/main/java/org/openecomp/sdc/enrichment/impl/tosca/model/PortMirroringConnectionPointDescription.java
@@ -0,0 +1,60 @@
+package org.openecomp.sdc.enrichment.impl.tosca.model;
+
+import java.util.Objects;
+
+@SuppressWarnings("CheckStyle")
+public class PortMirroringConnectionPointDescription {
+  private String nf_type;
+  private String nfc_type;
+  //Keeping below attributes as objects to accomodate for tosca functions for property
+  // values like get_input, get_attribute
+  private Object network_role;
+  private Object pps_capacity;
+
+  public PortMirroringConnectionPointDescription() {
+    //Populating empty strings as default values to be populated in tosca
+    nf_type = "";
+    nfc_type = "";
+    network_role = "";
+    pps_capacity = "";
+  }
+
+  public String getNf_type() {
+    return nf_type;
+  }
+
+  public void setNf_type(String nf_type) {
+    this.nf_type = nf_type;
+  }
+
+  public String getNfc_type() {
+    return nfc_type;
+  }
+
+  public void setNfc_type(String nfc_type) {
+    this.nfc_type = nfc_type;
+  }
+
+  public Object getNetwork_role() {
+    return network_role;
+  }
+
+  public void setNetwork_role(Object network_role) {
+    this.network_role = network_role;
+  }
+
+  public Object getPps_capacity() {
+    return pps_capacity;
+  }
+
+  public void setPps_capacity(String pps_capacity) {
+    this.pps_capacity = pps_capacity;
+  }
+
+  public boolean isEmpty() {
+    return Objects.isNull(nf_type)
+        && Objects.isNull(nfc_type)
+        && Objects.isNull(network_role)
+        && Objects.isNull(pps_capacity);
+  }
+}
diff --git a/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/datatypes/ToscaNodeType.java b/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/datatypes/ToscaNodeType.java
index d8e0902..952a9cf 100644
--- a/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/datatypes/ToscaNodeType.java
+++ b/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/datatypes/ToscaNodeType.java
@@ -33,7 +33,9 @@
       config.getAsString(ConfigConstants.NAMESPACE, ConfigConstants.PREFIX_NODE_TYPE_VFC);
   public static final String CP_NODE_TYPE_PREFIX =
       config.getAsString(ConfigConstants.NAMESPACE, ConfigConstants.PREFIX_NODE_TYPE_CP);
-  public static final String NETWORK_NODE_TYPE_PREFIX =
+  public static String EXTERNAL_CP_NODE_TYPE_PREFIX =
+      config.getAsString(ConfigConstants.NAMESPACE, ConfigConstants.PREFIX_NODE_TYPE_EXTERNAL_CP);
+  public static String NETWORK_NODE_TYPE_PREFIX =
       config.getAsString(ConfigConstants.NAMESPACE, ConfigConstants.PREFIX_NODE_TYPE_NETWORK);
   public static final String ABSTRACT_NODE_TYPE_PREFIX =
       config.getAsString(ConfigConstants.NAMESPACE, ConfigConstants.PREFIX_NODE_TYPE_ABSTARCT);
@@ -77,6 +79,10 @@
   public static final String NETWORK_SUB_INTERFACE = CP_NODE_TYPE_PREFIX + "network.SubInterface";
   public static final String CONTRAILV2_VLAN_SUB_INTERFACE = CP_NODE_TYPE_PREFIX
       + "heat.network.contrailV2.VLANSubInterface";
+  //Port Mirroring external node types
+  public static String EXTERNAL_CP = EXTERNAL_CP_NODE_TYPE_PREFIX + "extCP";
+  public static String EXTERNAL_CONTRAIL_PORT = EXTERNAL_CP_NODE_TYPE_PREFIX + "extContrailCP";
+  public static String EXTERNAL_NEUTRON_PORT = EXTERNAL_CP_NODE_TYPE_PREFIX + "extNeutronCP";
 
   public static final String ABSTRACT_SUBSTITUTE = ABSTRACT_NODE_TYPE_PREFIX + "AbstractSubstitute";
   public static final String VFC_ABSTRACT_SUBSTITUTE = ABSTRACT_NODE_TYPE_PREFIX + "VFC";
diff --git a/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/ConfigConstants.java b/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/ConfigConstants.java
index 0e4bc22..91103c5 100644
--- a/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/ConfigConstants.java
+++ b/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/ConfigConstants.java
@@ -36,6 +36,8 @@
   public static final String PREFIX_NODE_TYPE_VFC = PREFIX + ".nodeType.vfc";
   public static final String PREFIX_NODE_TYPE_NETWORK = PREFIX + ".nodeType.network";
   public static final String PREFIX_NODE_TYPE_CP = PREFIX + ".nodeType.connectionPoint";
+  public static final String PREFIX_NODE_TYPE_EXTERNAL_CP = PREFIX + ".nodeType"
+      + ".external.connectionPoint";
   public static final String PREFIX_NODE_TYPE_ABSTARCT = PREFIX + ".nodeType.abstract";
   public static final String PREFIX_NODE_TYPE_RULE = PREFIX + ".nodeType.rule";
 
diff --git a/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/DataModelUtil.java b/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/DataModelUtil.java
index 0effd40..d223b5f 100644
--- a/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/DataModelUtil.java
+++ b/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/DataModelUtil.java
@@ -37,6 +37,7 @@
 import org.openecomp.sdc.tosca.datatypes.ToscaFunctions;
 import org.openecomp.sdc.tosca.datatypes.ToscaRelationshipType;
 import org.openecomp.sdc.tosca.datatypes.model.AttributeDefinition;
+import org.openecomp.sdc.tosca.datatypes.model.CapabilityAssignment;
 import org.openecomp.sdc.tosca.datatypes.model.CapabilityDefinition;
 import org.openecomp.sdc.tosca.datatypes.model.CapabilityType;
 import org.openecomp.sdc.tosca.datatypes.model.Constraint;
@@ -157,6 +158,47 @@
   }
 
   /**
+   * Add substitution mapping capability.
+   *
+   * @param serviceTemplate                   the service template
+   * @param substitutionMappingCapabilityId   the substitution mapping capability id
+   * @param substitutionMappingCapabilityList the substitution mapping capability list
+   */
+  public static void addSubstitutionMappingCapability(ServiceTemplate serviceTemplate,
+                                                      String substitutionMappingCapabilityId,
+                                                      List<String> substitutionMappingCapabilityList) {
+
+
+    mdcDataDebugMessage.debugEntryMessage(null, null);
+
+    if (serviceTemplate == null) {
+      MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
+          LoggerTragetServiceName.ADD_ENTITIES_TO_TOSCA, ErrorLevel.ERROR.name(),
+          LoggerErrorCode.DATA_ERROR.getErrorCode(), LoggerErrorDescription.INVALID_ADD_ACTION);
+      throw new CoreException(
+          new InvalidAddActionNullEntityErrorBuilder("Substitution Mapping Capabilities",
+              "Service Template").build());
+    }
+
+    if (serviceTemplate.getTopology_template() == null) {
+      serviceTemplate.setTopology_template(new TopologyTemplate());
+    }
+    if (serviceTemplate.getTopology_template().getSubstitution_mappings() == null) {
+      serviceTemplate.getTopology_template().setSubstitution_mappings(new SubstitutionMapping());
+    }
+    if (serviceTemplate.getTopology_template().getSubstitution_mappings().getCapabilities()
+        == null) {
+      serviceTemplate.getTopology_template().getSubstitution_mappings()
+          .setCapabilities(new HashMap<>());
+    }
+
+    serviceTemplate.getTopology_template().getSubstitution_mappings().getCapabilities()
+        .putIfAbsent(substitutionMappingCapabilityId, substitutionMappingCapabilityList);
+
+    mdcDataDebugMessage.debugExitMessage(null, null);
+  }
+
+  /**
    * Add node template.
    *
    * @param serviceTemplate the service template
@@ -1445,11 +1487,11 @@
       ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
       clonedObjectValue = objectInputStream.readObject();
     } catch (NotSerializableException ex) {
-        logger.debug(ex.getMessage(), ex);
-        return getClonedObject(obj, obj.getClass());
+      logger.debug(ex.getMessage(), ex);
+      return getClonedObject(obj, obj.getClass());
     } catch (IOException | ClassNotFoundException ex) {
-        logger.debug(ex.getMessage(), ex);
-        return null;
+      logger.debug(ex.getMessage(), ex);
+      return null;
     }
     return clonedObjectValue;
   }
@@ -1510,6 +1552,30 @@
     return substitutionMapping;
   }
 
+  /**
+   * Add node template capability.
+   *
+   * @param nodeTemplate         the node template
+   * @param capabilityId         the capability id
+   * @param capabilityProperties the capability properties
+   * @param capabilityAttributes the capability attributes
+   */
+  public static void addNodeTemplateCapability(NodeTemplate nodeTemplate, String capabilityId,
+                                               Map<String, Object> capabilityProperties,
+                                               Map<String, Object> capabilityAttributes) {
+    List<Map<String, CapabilityAssignment>> capabilities = nodeTemplate.getCapabilities();
+    if (Objects.isNull(capabilities)) {
+      capabilities = new ArrayList<>();
+    }
+    CapabilityAssignment capabilityAssignment = new CapabilityAssignment();
+    capabilityAssignment.setProperties(capabilityProperties);
+    capabilityAssignment.setAttributes(capabilityAttributes);
+    Map<String, CapabilityAssignment> nodeTemplateCapability = new HashMap<>();
+    nodeTemplateCapability.put(capabilityId, capabilityAssignment);
+    capabilities.add(nodeTemplateCapability);
+    nodeTemplate.setCapabilities(capabilities);
+  }
+
   private static Map<String, List<String>> manageRequirementMapping(
       List<Map<String, RequirementDefinition>> requirementList,
       Map<String, List<String>> requirementSubstitutionMapping) {
diff --git a/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/ToscaConstants.java b/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/ToscaConstants.java
index cf0c763..4d45e8f 100644
--- a/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/ToscaConstants.java
+++ b/openecomp-be/lib/openecomp-tosca-lib/src/main/java/org/openecomp/sdc/tosca/services/ToscaConstants.java
@@ -40,6 +40,7 @@
   public static final String SCALABLE_CAPABILITY_ID = "scalable";
   public static final String ATTACHMENT_CAPABILITY_ID = "attachment";
   public static final String FEATURE_CAPABILITY_ID = "feature";
+  public static final String PORT_MIRRORING_CAPABILITY_ID = "port_mirroring";
 
   //General
   public static final String TOSCA_DEFINITIONS_VERSION = "tosca_simple_yaml_1_0_0";
@@ -62,6 +63,8 @@
 
   public static final String PORT_FIXED_IPS = "fixed_ips";
   public static final String PORT_ALLOWED_ADDRESS_PAIRS = "allowed_address_pairs";
+  public static final String PORT_NETWORK_ROLE_PROPERTY_NAME = "network_role";
+  public static final String PORT_MIRRORING_CAPABILITY_CP_PROPERTY_NAME = "connection_point";
   public static final String MAC_ADDRESS = "mac_address";
 
   public static final String COMPUTE_IMAGE = "image";