Support symmetric chain classification 26/67326/4
authorJaime Caamaño Ruiz <jcaamano@suse.com>
Thu, 18 Jan 2018 17:55:34 +0000 (18:55 +0100)
committerSam Hague <shague@redhat.com>
Mon, 22 Jan 2018 20:14:33 +0000 (20:14 +0000)
Symmetric chain classification through networking-sfc requires a flow
classifier that defines both a source and a destination logical port.
This patch adds support for such scenario.

RedirectToSfc can now specify a SFP name. Classification for the
forward RSP of that SFP will be done on the source port. Classification
for the reverse RSP of that SFP will be done on the destination port.
Sfc translator has been modified to specify a SFP name in
RedirectToSfc instead of a RSP name.

When RedirectToSfc specifies a RSP name, if the RSP is not reverse,
classification will be done in the source port. Classification will
happen in the destination port otherwise. No classification will be
done if no source port is specified for a reverse RSP, and viceversa.
If a network is specified instead, all the ports of the network will be
considered source ports if the RSP is not reverse, and destination
ports otherwise, meaning that classification will happen on those ports
wether the RSP is forward or reverse. This maintains legacy behavior
with respect network classification.

Change-Id: I86d85a58688367591784f4361aacd095e9cbccf0
Signed-off-by: Jaime Caamaño Ruiz <jcaamano@suse.com>
vpnservice/sfc/classifier/impl/src/main/java/org/opendaylight/netvirt/sfc/classifier/providers/SfcProvider.java
vpnservice/sfc/classifier/impl/src/main/java/org/opendaylight/netvirt/sfc/classifier/service/domain/impl/ConfigurationClassifierImpl.java
vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/flowclassifier/FlowClassifierTranslator.java
vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortChainListener.java

index e0dab41fa055320b653fee1c0c17b104295707cc..5a2eef54802f2e583a79b10da1329b37a9068d95 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.netvirt.sfc.classifier.providers;
 
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
@@ -17,6 +18,7 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfpName;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.RenderedServicePaths;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePathKey;
@@ -25,6 +27,9 @@ import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev14070
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocator;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunctionKey;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPathsState;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.state.ServiceFunctionPathState;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.state.ServiceFunctionPathStateKey;
 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sff.logical.rev160620.LogicalInterfaceLocator;
 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sff.logical.rev160620.service.functions.service.function.sf.data.plane.locator.locator.type.LogicalInterface;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -59,6 +64,20 @@ public class SfcProvider {
         return rsp;
     }
 
+    public Optional<List<String>> readServicePathState(String sfpName) {
+        ServiceFunctionPathStateKey serviceFunctionPathStateKey = new ServiceFunctionPathStateKey(new SfpName(sfpName));
+        InstanceIdentifier<ServiceFunctionPathState> sfpIiD;
+        sfpIiD = InstanceIdentifier.builder(ServiceFunctionPathsState.class)
+                .child(ServiceFunctionPathState.class, serviceFunctionPathStateKey)
+                .build();
+        return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, sfpIiD)
+                .toJavaUtil()
+                .map(ServiceFunctionPathState::getSfpRenderedServicePath)
+                .map(sfpRenderedServicePaths -> sfpRenderedServicePaths.stream()
+                        .map(sfpRenderedServicePath -> sfpRenderedServicePath.getName().getValue())
+                        .collect(Collectors.toList()));
+    }
+
     public Optional<String> getFirstHopSfInterfaceFromRsp(RenderedServicePath rsp) {
         return getRspFirstHop(rsp).flatMap(this::getHopSfInterface);
     }
index 17c1c46709e3f0d9d16087f05639f64230514cee..7ed28d632cf6de6ad14ce43a33107ea5918daffd 100644 (file)
@@ -19,6 +19,7 @@ import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
@@ -73,47 +74,170 @@ public class ConfigurationClassifierImpl implements ClassifierState {
                 .map(AccessListEntries::getAce)
                 .filter(Objects::nonNull)
                 .flatMap(List::stream)
-                .map(this::getEntries)
+                .map(this::getEntriesForAce)
                 .flatMap(Set::stream)
                 .collect(Collectors.toSet());
     }
 
-    public List<Acl> readAcls() {
+    private List<Acl> readAcls() {
         InstanceIdentifier<AccessLists> aclsIID = InstanceIdentifier.builder(AccessLists.class).build();
-        com.google.common.base.Optional<AccessLists> acls =
-                MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, aclsIID);
+        Optional<AccessLists> acls;
+        acls = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, aclsIID).toJavaUtil();
         LOG.trace("Acls read from datastore: {}", acls);
-        return acls.toJavaUtil().map(AccessLists::getAcl).orElse(Collections.emptyList());
+        return acls.map(AccessLists::getAcl).orElse(Collections.emptyList());
     }
 
-    public Set<ClassifierRenderableEntry> getEntries(Ace ace) {
+    private Set<ClassifierRenderableEntry> getEntriesForAce(Ace ace) {
+        LOG.info("Generating classifier entries for Ace: {}", ace.getRuleName());
+        LOG.trace("Ace details: {}", ace);
 
-        LOG.trace("Get entries for Ace {}", ace);
+        Optional<NetvirtsfcAclActions> sfcActions = Optional.ofNullable(ace.getActions())
+                .map(actions -> actions.getAugmentation(RedirectToSfc.class));
+        String rspName = sfcActions.map(NetvirtsfcAclActions::getRspName).map(Strings::emptyToNull).orElse(null);
+        String sfpName = sfcActions.map(NetvirtsfcAclActions::getSfpName).map(Strings::emptyToNull).orElse(null);
 
-        Matches matches = ace.getMatches();
+        if (rspName == null && sfpName == null) {
+            LOG.debug("Ace has no valid SFC redirect action, ignoring");
+            return Collections.emptySet();
+        }
+        if (rspName != null && sfpName != null) {
+            LOG.error("Ace has both a SFP and a RSP as redirect actions, ignoring as not supported");
+            return Collections.emptySet();
+        }
 
+        Matches matches = ace.getMatches();
         if (matches == null) {
-            LOG.trace("Ace has no matches");
+            LOG.warn("Ace has no matches, ignoring");
+            return Collections.emptySet();
+        }
+
+        NeutronNetwork network = matches.getAugmentation(NeutronNetwork.class);
+        if (sfpName != null && network != null) {
+            LOG.error("Ace has a SFP redirect action with a neutron network match, ignoring as not supported");
             return Collections.emptySet();
         }
 
-        RenderedServicePath rsp = Optional.ofNullable(ace.getActions())
-                .map(actions -> actions.getAugmentation(RedirectToSfc.class))
-                .map(NetvirtsfcAclActions::getRspName)
-                .flatMap(sfcProvider::getRenderedServicePath)
+        String sourcePort = Optional.ofNullable(matches.getAugmentation(NeutronPorts.class))
+                .map(NeutronPorts::getSourcePortUuid)
+                .map(Strings::emptyToNull)
+                .orElse(null);
+        String destinationPort = Optional.ofNullable(matches.getAugmentation(NeutronPorts.class))
+                .map(NeutronPorts::getDestinationPortUuid)
+                .map(Strings::emptyToNull)
                 .orElse(null);
 
+        if (rspName != null) {
+            return getEntriesForRspRedirect(sourcePort, destinationPort, network, rspName, matches);
+        }
+
+        return getEntriesForSfpRedirect(sourcePort, destinationPort, sfpName, matches);
+    }
+
+    private Set<ClassifierRenderableEntry> getEntriesForRspRedirect(
+            String sourcePort,
+            String destinationPort,
+            NeutronNetwork neutronNetwork,
+            String rspName,
+            Matches matches) {
+
+        List<String> interfaces = new ArrayList<>();
+        if (neutronNetwork != null) {
+            interfaces.addAll(netvirtProvider.getLogicalInterfacesFromNeutronNetwork(neutronNetwork));
+        }
+
+        RenderedServicePath rsp = sfcProvider.getRenderedServicePath(rspName).orElse(null);
         if (rsp == null) {
-            LOG.trace("Ace has no valid SFC redirect action");
+            LOG.error("RSP {} could not be read from database", rspName);
+            return Collections.emptySet();
+        }
+
+        if (rsp.isReversePath()) {
+            interfaces.add(destinationPort);
+            if (sourcePort != null) {
+                LOG.warn("Source port ignored with redirect to reverse RSP");
+            }
+        } else {
+            if (destinationPort != null) {
+                LOG.warn("Destination port ignored with redirect to forward RSP");
+            }
+            interfaces.add(sourcePort);
+        }
+
+        if (interfaces.isEmpty()) {
+            LOG.warn("Ace has no interfaces to match against");
+            return Collections.emptySet();
+        }
+
+        return this.buildEntries(interfaces, matches, rsp);
+    }
+
+    private Set<ClassifierRenderableEntry> getEntriesForSfpRedirect(
+            String sourcePort,
+            String destinationPort,
+            String sfpName,
+            Matches matches) {
+
+        if (sourcePort == null && destinationPort == null) {
+            LOG.warn("Ace has no interfaces to match against");
+            return Collections.emptySet();
+        }
+
+        if (Objects.equals(sourcePort, destinationPort)) {
+            LOG.error("Specifying the same source and destination port is not valid configuration");
+            return Collections.emptySet();
+        }
+
+        List<String> rspNames = sfcProvider.readServicePathState(sfpName).orElse(Collections.emptyList());
+        if (rspNames.isEmpty()) {
+            LOG.warn("There is currently no RSPs for SFP {}", sfpName);
             return Collections.emptySet();
         }
 
+        Set<ClassifierRenderableEntry> entries = new HashSet<>();
+        boolean haveAllRsps = false;
+        RenderedServicePath forwardRsp = null;
+        RenderedServicePath reverseRsp = null;
+        for (String anRspName : rspNames) {
+            RenderedServicePath rsp = sfcProvider.getRenderedServicePath(anRspName).orElse(null);
+            if (rsp.isReversePath() && destinationPort != null) {
+                reverseRsp = rsp;
+                haveAllRsps = forwardRsp != null || sourcePort == null;
+            }
+            if (!rsp.isReversePath() && sourcePort != null) {
+                forwardRsp = rsp;
+                haveAllRsps = reverseRsp != null || destinationPort == null;
+            }
+            if (haveAllRsps) {
+                break;
+            }
+        }
+
+        if (reverseRsp != null) {
+            entries.addAll(this.buildEntries(Collections.singletonList(destinationPort), matches, reverseRsp));
+        } else if (destinationPort != null) {
+            LOG.warn("No reverse RSP found for SFP {} and destination port {}", sfpName, destinationPort);
+        }
+
+        if (forwardRsp != null) {
+            entries.addAll(this.buildEntries(Collections.singletonList(sourcePort), matches, forwardRsp));
+        } else if (sourcePort != null) {
+            LOG.warn("No forward RSP found for SFP {} and source port {}", sfpName, sourcePort);
+        }
+
+        return entries;
+    }
+
+    private Set<ClassifierRenderableEntry> buildEntries(
+            @NonNull List<String> interfaces,
+            @NonNull Matches matches,
+            @NonNull RenderedServicePath rsp) {
+
         Long nsp = rsp.getPathId();
         Short nsi = rsp.getStartingIndex();
         Short nsl = rsp.getRenderedServicePathHop() == null ? null : (short) rsp.getRenderedServicePathHop().size();
 
         if (nsp == null || nsi == null || nsl == null) {
-            LOG.trace("RSP has no valid NSI or NSP or length");
+            LOG.error("RSP has no valid NSI or NSP or length");
             return Collections.emptySet();
         }
 
@@ -138,27 +262,10 @@ public class ConfigurationClassifierImpl implements ClassifierState {
             return Collections.emptySet();
         }
 
-        List<String> interfaces = new ArrayList<>();
-        NeutronNetwork network = matches.getAugmentation(NeutronNetwork.class);
-        if (network != null) {
-            interfaces.addAll(netvirtProvider.getLogicalInterfacesFromNeutronNetwork(network));
-        }
-
-        Optional.ofNullable(matches.getAugmentation(NeutronPorts.class))
-                .map(NeutronPorts::getSourcePortUuid)
-                .filter(port -> !Strings.isNullOrEmpty(port) && !interfaces.contains(port))
-                .ifPresent(interfaces::add);
-
-        if (interfaces.isEmpty()) {
-            LOG.error("Ace has no neutron entity to match against");
-            return Collections.emptySet();
-        }
-
         Map<NodeId, List<InterfaceKey>> nodeToInterfaces = new HashMap<>();
         for (String iface : interfaces) {
-            geniusProvider.getNodeIdFromLogicalInterface(iface).ifPresent(
-                nodeId -> nodeToInterfaces.computeIfAbsent(nodeId, key -> new ArrayList<>()).add(
-                        new InterfaceKey(iface)));
+            geniusProvider.getNodeIdFromLogicalInterface(iface).ifPresent(nodeId ->
+                    nodeToInterfaces.computeIfAbsent(nodeId, key -> new ArrayList<>()).add(new InterfaceKey(iface)));
         }
 
         LOG.trace("Got classifier nodes and interfaces: {}", nodeToInterfaces);
index f0733ad35e6f8c67dc96dac622d2b2e428a8c273..a3203170fcd167606f730be475d1e799cc538e60 100644 (file)
@@ -55,7 +55,7 @@ public final class FlowClassifierTranslator {
         return buildAcl(flowClassifier, null);
     }
 
-    public static Acl buildAcl(SfcFlowClassifier flowClassifier, String rspName) {
+    public static Acl buildAcl(SfcFlowClassifier flowClassifier, String sfpName) {
         LOG.info("OpenStack Networking SFC pushed Flow classifier : {}", flowClassifier);
         AclBuilder aclBuilder = new AclBuilder();
         AceBuilder aceBuilder = new AceBuilder();
@@ -148,8 +148,8 @@ public final class FlowClassifierTranslator {
         matchesBuilder.addAugmentation(NeutronPorts.class, neutronPortsBuilder.build());
 
         //Set redirect-to-rsp action if rsp name is provided
-        if (rspName != null) {
-            redirectToSfcBuilder.setRspName(rspName);
+        if (sfpName != null) {
+            redirectToSfcBuilder.setSfpName(sfpName);
             actionsBuilder.addAugmentation(RedirectToSfc.class, redirectToSfcBuilder.build());
             aceBuilder.setActions(actionsBuilder.build());
         }
index 8966a4d8395a2bfd957cff330d5c60747ccfe3c3..5329f9ef2d27a5530afba2d1cd5f2db0629dce24 100644 (file)
@@ -179,8 +179,9 @@ public class NeutronPortChainListener extends DelegatingDataTreeListener<PortCha
         LOG.info("Add service function path {}", sfp);
         sfcMdsalHelper.addServiceFunctionPath(sfp);
 
-        //TODO:Generate Flow Classifiers and augment RSP on it.
-
+        // TODO This can be removed after oxygen
+        // In Oxygen, RSP will be automatically created from the SFP without
+        // the need to call the rpc, which will be deprecated
         if (this.rspService != null) {
             // Build Create Rendered Service Path input
             CreateRenderedPathInput rpInput = PortChainTranslator.buildCreateRenderedServicePathInput(sfp);
@@ -192,8 +193,7 @@ public class NeutronPortChainListener extends DelegatingDataTreeListener<PortCha
                 try {
                     if (result.get() != null) {
                         CreateRenderedPathOutput output = result.get().getResult();
-                        LOG.debug("RSP name received from SFC : {}", output.getName());
-                        processFlowClassifiers(newPortChain, newPortChain.getFlowClassifiers(), output.getName());
+                        LOG.debug("RSP created on SFC : {}", output.getName());
                     } else {
                         LOG.error("RSP creation failed : {}", rpInput);
                     }
@@ -205,13 +205,16 @@ public class NeutronPortChainListener extends DelegatingDataTreeListener<PortCha
             LOG.error("Rendered Path Service is not available, can't create Rendered Path for Port Chain",
                     newPortChain);
         }
+
+        // Add ACLs from flow classifiers
+        processFlowClassifiers(newPortChain, newPortChain.getFlowClassifiers(), sfp.getName().getValue());
     }
 
-    private void processFlowClassifiers(PortChain pc, List<Uuid> flowClassifiers, String rspName) {
+    private void processFlowClassifiers(PortChain pc, List<Uuid> flowClassifiers, String sfpName) {
         for (Uuid uuid : flowClassifiers) {
             SfcFlowClassifier fc = neutronMdsalHelper.getNeutronFlowClassifier(uuid);
             if (fc != null) {
-                Acl acl = FlowClassifierTranslator.buildAcl(fc, rspName);
+                Acl acl = FlowClassifierTranslator.buildAcl(fc, sfpName);
                 if (acl != null) {
                     sfcMdsalHelper.addAclFlowClassifier(acl);
                 } else {