Add NetvirtItUtils.verifyFlowByFields 05/42405/1
authorJosh <jhershbe@redhat.com>
Sun, 24 Jul 2016 14:13:27 +0000 (16:13 +0200)
committerJosh <jhershbe@redhat.com>
Sun, 24 Jul 2016 15:33:42 +0000 (17:33 +0200)
This method checks that, for a flow which
exists in config, a flow with the
same priority and match fields exists in
operational. The flow is retrieved from
configuration datastore by flow-id and
then compared to flows in the operational
until a match is found...or not.

Change-Id: I4d1b55ec97dc22baccbc37618b5ad868983a555a
Signed-off-by: Josh <jhershbe@redhat.com>
openstack/net-virt-it/src/test/java/org/opendaylight/netvirt/openstack/netvirt/it/NetvirtIT.java
openstack/utils/netvirt-it-utils/pom.xml
openstack/utils/netvirt-it-utils/src/main/java/org/opendaylight/netvirt/utils/netvirt/it/utils/NetvirtItUtils.java

index e10559ae138800c49761d1dd0394dc247eaa4233..6eab51c98458c3cf5d583057bd101c8f8a1cbc72 100644 (file)
@@ -353,13 +353,13 @@ public class NetvirtIT extends AbstractMdsalTestBase {
                 staticPipelineFound.add(service);
             }
             String flowId = "DEFAULT_PIPELINE_FLOW_" + pipelineOrchestrator.getTable(service);
-            nvItUtils.verifyFlow(datapathId, flowId, pipelineOrchestrator.getTable(service));
+            nvItUtils.verifyFlowByFields(datapathId, flowId, pipelineOrchestrator.getTable(service), 5000);
         }
         assertEquals("did not find all expected flows in static pipeline",
                 staticPipeline.size(), staticPipelineFound.size());
 
         String flowId = "TableOffset_" + pipelineOrchestrator.getTable(Service.CLASSIFIER);
-        nvItUtils.verifyFlow(datapathId, flowId, Service.CLASSIFIER.getTable());
+        nvItUtils.verifyFlowByFields(datapathId, flowId, Service.CLASSIFIER.getTable(), 5000);
 
         Assert.assertTrue(southboundUtils.deleteBridge(connectionInfo, NetvirtITConstants.INTEGRATION_BRIDGE_NAME));
         Thread.sleep(1000);
@@ -417,7 +417,7 @@ public class NetvirtIT extends AbstractMdsalTestBase {
                     staticPipelineFound.add(service);
                 }
                 String flowId = "DEFAULT_PIPELINE_FLOW_" + pipelineOrchestrator.getTable(service);
-                nvItUtils.verifyFlow(nodeInfo.datapathId, flowId, pipelineOrchestrator.getTable(service));
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, flowId, pipelineOrchestrator.getTable(service), 5000);
             }
             assertEquals("did not find all expected flows in static pipeline",
                     staticPipeline.size(), staticPipelineFound.size());
@@ -489,7 +489,7 @@ public class NetvirtIT extends AbstractMdsalTestBase {
             Thread.sleep(1000);
 
             String flowId = "Egress_DHCP_Client"  + "_Permit_";
-            nvItUtils.verifyFlow(nodeInfo.datapathId, flowId, pipelineOrchestrator.getTable(Service.EGRESS_ACL));
+            nvItUtils.verifyFlowByFields(nodeInfo.datapathId, flowId, pipelineOrchestrator.getTable(Service.EGRESS_ACL), 5000);
 
             testDefaultSG(nport, nodeInfo.datapathId, nn, tenantId, portId);
             Thread.sleep(1000);
@@ -558,10 +558,10 @@ public class NetvirtIT extends AbstractMdsalTestBase {
         LOG.info("Neutron ports have been added");
         Thread.sleep(10000);
         String flowId = "Egress_IP" + nn.getProviderSegmentationID() + "_" + nport.getMacAddress() + "_Permit_";
-        nvItUtils.verifyFlow(datapathId, flowId, pipelineOrchestrator.getTable(Service.EGRESS_ACL));
+        nvItUtils.verifyFlowByFields(datapathId, flowId, pipelineOrchestrator.getTable(Service.EGRESS_ACL), 5000);
 
         flowId = "Ingress_IP" + nn.getProviderSegmentationID() + "_" + nport.getMacAddress() + "_Permit_";
-        nvItUtils.verifyFlow(datapathId, flowId, pipelineOrchestrator.getTable(Service.INGRESS_ACL));
+        nvItUtils.verifyFlowByFields(datapathId, flowId, pipelineOrchestrator.getTable(Service.INGRESS_ACL), 5000);
 
         ineutronSecurityGroupCRUD.removeNeutronSecurityGroup(neutronSG.getID());
         ineutronSecurityRuleCRUD.removeNeutronSecurityRule(nsrEG.getID());
@@ -588,18 +588,18 @@ public class NetvirtIT extends AbstractMdsalTestBase {
             net.createPort(nodeInfo.bridgeNode, "vm1");
             net.createPort(nodeInfo.bridgeNode, "vm2");
 
-            Thread.sleep(3000);
+            //Thread.sleep(5000);
 
             // Check flows created for all ports
             for (NeutronNetItUtil.PortInfo portInfo : net.portInfoByName.values()) {
-                nvItUtils.verifyFlow(nodeInfo.datapathId, "DropFilter_" + portInfo.ofPort,
-                        pipelineOrchestrator.getTable(Service.CLASSIFIER));
-                nvItUtils.verifyFlow(nodeInfo.datapathId, "LocalMac_" + net.segId + "_" + portInfo.ofPort
-                                            + "_" + portInfo.mac, pipelineOrchestrator.getTable(Service.CLASSIFIER));
-                nvItUtils.verifyFlow(nodeInfo.datapathId, "ArpResponder_" + net.segId + "_" + portInfo.ip,
-                                            pipelineOrchestrator.getTable(Service.ARP_RESPONDER));
-                nvItUtils.verifyFlow(nodeInfo.datapathId, "UcastOut_" + net.segId + "_" + portInfo.ofPort
-                                            + "_" + portInfo.mac, pipelineOrchestrator.getTable(Service.L2_FORWARDING));
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "DropFilter_" + portInfo.ofPort,
+                        pipelineOrchestrator.getTable(Service.CLASSIFIER), 5000);
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "LocalMac_" + net.segId + "_" + portInfo.ofPort
+                                            + "_" + portInfo.mac, pipelineOrchestrator.getTable(Service.CLASSIFIER), 5000);
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "ArpResponder_" + net.segId + "_" + portInfo.ip,
+                                            pipelineOrchestrator.getTable(Service.ARP_RESPONDER), 5000);
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "UcastOut_" + net.segId + "_" + portInfo.ofPort
+                                            + "_" + portInfo.mac, pipelineOrchestrator.getTable(Service.L2_FORWARDING), 5000);
             }
 
             for (NeutronNetItUtil.PortInfo portInfo : net.portInfoByName.values()) {
@@ -607,37 +607,35 @@ public class NetvirtIT extends AbstractMdsalTestBase {
                 if (portInfo.name.equals("dhcp")) {
                     continue;
                 }
-                nvItUtils.verifyFlow(nodeInfo.datapathId, "Ingress_ARP_" + net.segId + "_" + portInfo.ofPort + "_",
-                        pipelineOrchestrator.getTable(Service.INGRESS_ACL));
-                nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_Allow_VM_IP_MAC_" + portInfo.ofPort
-                                + portInfo.mac + "_Permit_", pipelineOrchestrator.getTable(Service.EGRESS_ACL));
-                nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_ARP_" + net.segId + "_" + portInfo.ofPort + "_",
-                        pipelineOrchestrator.getTable(Service.EGRESS_ACL));
-                nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCP_Server_" + portInfo.ofPort + "_DROP_",
-                        pipelineOrchestrator.getTable(Service.EGRESS_ACL));
-                nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCPv6_Server_" + portInfo.ofPort + "_DROP_",
-                        pipelineOrchestrator.getTable(Service.EGRESS_ACL));
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "Ingress_ARP_" + net.segId + "_" + portInfo.ofPort + "_",
+                        pipelineOrchestrator.getTable(Service.INGRESS_ACL), 5000);
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "Egress_Allow_VM_IP_MAC_" + portInfo.ofPort
+                                + portInfo.mac + "_Permit_", pipelineOrchestrator.getTable(Service.EGRESS_ACL), 5000);
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "Egress_ARP_" + net.segId + "_" + portInfo.ofPort + "_",
+                        pipelineOrchestrator.getTable(Service.EGRESS_ACL), 5000);
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "Egress_DHCP_Server_" + portInfo.ofPort + "_DROP_",
+                        pipelineOrchestrator.getTable(Service.EGRESS_ACL), 5000);
+                nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "Egress_DHCPv6_Server_" + portInfo.ofPort + "_DROP_",
+                        pipelineOrchestrator.getTable(Service.EGRESS_ACL), 5000);
             }
 
             // Check ingress/egress acl flows for DHCP
-            nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCP_Client_Permit_",
-                    pipelineOrchestrator.getTable(Service.EGRESS_ACL));
-            nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCPv6_Client_Permit_",
-                    pipelineOrchestrator.getTable(Service.EGRESS_ACL));
-            nvItUtils.verifyFlow(nodeInfo.datapathId, "Ingress_DHCPv6_Server" + net.segId + "_"
-                    + net.macFor(1) + "_Permit_", pipelineOrchestrator.getTable(Service.INGRESS_ACL));
-            nvItUtils.verifyFlow(nodeInfo.datapathId, "Ingress_DHCP_Server" + net.segId + "_"
-                    + net.macFor(1) + "_Permit_", pipelineOrchestrator.getTable(Service.INGRESS_ACL));
+            nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "Egress_DHCP_Client_Permit_",
+                    pipelineOrchestrator.getTable(Service.EGRESS_ACL), 5000);
+            nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "Egress_DHCPv6_Client_Permit_",
+                    pipelineOrchestrator.getTable(Service.EGRESS_ACL), 5000);
+            nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "Ingress_DHCPv6_Server" + net.segId + "_"
+                    + net.macFor(1) + "_Permit_", pipelineOrchestrator.getTable(Service.INGRESS_ACL), 5000);
+            nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "Ingress_DHCP_Server" + net.segId + "_"
+                    + net.macFor(1) + "_Permit_", pipelineOrchestrator.getTable(Service.INGRESS_ACL), 5000);
 
             // Check l2 broadcast flows
-            nvItUtils.verifyFlow(nodeInfo.datapathId, "TunnelFloodOut_" + net.segId,
-                    pipelineOrchestrator.getTable(Service.L2_FORWARDING));
-            nvItUtils.verifyFlow(nodeInfo.datapathId, "BcastOut_" + net.segId,
-                    pipelineOrchestrator.getTable(Service.L2_FORWARDING));
-
-            //TBD Figure out why this does not work:
-            //nvItUtils.verifyFlow(nodeInfo.datapathId, "TunnelMiss_" + net.segId,
-            //        pipelineOrchestrator.getTable(Service.L2_FORWARDING));
+            nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "TunnelFloodOut_" + net.segId,
+                    pipelineOrchestrator.getTable(Service.L2_FORWARDING), 5000);
+            nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "BcastOut_" + net.segId,
+                    pipelineOrchestrator.getTable(Service.L2_FORWARDING), 5000);
+            nvItUtils.verifyFlowByFields(nodeInfo.datapathId, "TunnelMiss_" + net.segId,
+                    pipelineOrchestrator.getTable(Service.L2_FORWARDING), 5000);
 
 
             net.preparePortForPing("vm1");
@@ -648,6 +646,7 @@ public class NetvirtIT extends AbstractMdsalTestBase {
             nodeInfo.disconnect();
         } catch (Exception e) {
             LOG.warn("testNeutronNet: Exception thrown by OvsDocker.OvsDocker()", e);
+            Assert.fail("Exception thrown during testNeutronNet " + e.toString());
         }
     }
 
index 9c04be3e3ee3a3e542fb315b1b8dfe4372c86d15..1490018b06f6ff6ded4fb2443b3a8afb590d8112 100644 (file)
@@ -32,6 +32,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
 
   <properties>
     <ovsdb.version>1.3.0-SNAPSHOT</ovsdb.version>
+    <openflowplugin.version>0.3.0-SNAPSHOT</openflowplugin.version>
   </properties>
 
   <dependencies>
@@ -92,6 +93,16 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
           <artifactId>junit</artifactId>
           <scope>compile</scope>
       </dependency>
+      <dependency>
+          <groupId>org.opendaylight.openflowplugin</groupId>
+          <artifactId>openflowplugin-extension-api</artifactId>
+          <version>${openflowplugin.version}</version>
+      </dependency>
+      <dependency>
+          <groupId>org.opendaylight.openflowplugin</groupId>
+          <artifactId>openflowplugin-extension-nicira</artifactId>
+          <version>${openflowplugin.version}</version>
+      </dependency>
   </dependencies>
   <build>
     <plugins>
index b53816ac6cf08eb2f7f5a007576864f1332f0d9d..0d62545906db03ccaa5919c77453b0d4bdc598dc 100644 (file)
@@ -10,6 +10,8 @@ package org.opendaylight.netvirt.utils.netvirt.it.utils;
 
 import static org.junit.Assert.assertNotNull;
 
+import com.google.common.collect.Lists;
+import org.junit.Assert;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
@@ -21,14 +23,38 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchNodesNodeTableFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchNotifPacketIn;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchNotifSwitchFlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchNotifUpdateFlowStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchRpcAddFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchRpcRemoveFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchRpcUpdateFlowOriginal;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchRpcUpdateFlowUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralExtensionListGrouping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.general.extension.list.grouping.ExtensionList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.match.rev140714.AllMatchesGrouping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.match.rev140714.NxAugMatchNodesNodeTableFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.match.rev140714.NxAugMatchNotifPacketIn;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.match.rev140714.NxAugMatchNotifSwitchFlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.match.rev140714.NxAugMatchNotifUpdateFlowStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.match.rev140714.NxAugMatchRpcAddFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.match.rev140714.NxAugMatchRpcRemoveFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.match.rev140714.NxAugMatchRpcUpdateFlowOriginal;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.match.rev140714.NxAugMatchRpcUpdateFlowUpdated;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.List;
+import java.util.Objects;
+
 /**
  * This class contains various utility methods used in netvirt integration tests (IT).
  */
@@ -38,6 +64,26 @@ public class NetvirtItUtils {
     SouthboundUtils southboundUtils;
     DataBroker dataBroker;
 
+    private final static Class[] MATCH_AUGMENTATIONS = {GeneralAugMatchNodesNodeTableFlow.class,
+            GeneralAugMatchNotifUpdateFlowStats.class,
+            GeneralAugMatchNotifPacketIn.class,
+            GeneralAugMatchNotifSwitchFlowRemoved.class,
+            GeneralAugMatchRpcAddFlow.class,
+            GeneralAugMatchRpcRemoveFlow.class,
+            GeneralAugMatchRpcUpdateFlowOriginal.class,
+            GeneralAugMatchRpcUpdateFlowUpdated.class};
+
+    private final static Class[] EXT_LIST_AUGMENTATIONS = {NxAugMatchNodesNodeTableFlow.class,
+            NxAugMatchNotifUpdateFlowStats.class,
+            NxAugMatchNotifPacketIn.class,
+            NxAugMatchNotifSwitchFlowRemoved.class,
+            NxAugMatchRpcAddFlow.class,
+            NxAugMatchRpcRemoveFlow.class,
+            NxAugMatchRpcUpdateFlowOriginal.class,
+            NxAugMatchRpcUpdateFlowUpdated.class};
+
+    private static Integer DEFAULT_PRIORITY = new Integer(32768);
+
     /**
      * Create a new NetvirtItUtils instance.
      * @param dataBroker  md-sal data broker
@@ -74,8 +120,8 @@ public class NetvirtItUtils {
     }
 
     /**
-     * Verify that the given flow was installed in a table. This method will wait 10 seconds for the flows
-     * to appear in each of the md-sal CONFIGURATION and OPERATIONAL data stores
+     * Verify <strong>by flow id</strong> that the given flow was installed in a table. This method will wait 10
+     * seconds for the flows to appear in each of the md-sal CONFIGURATION and OPERATIONAL data stores
      * @param datapathId dpid where flow is installed
      * @param flowId The "name" of the flow, e.g., "TunnelFloodOut_100"
      * @param table integer value of table
@@ -108,6 +154,216 @@ public class NetvirtItUtils {
                 flow);
     }
 
+    /**
+     * Verify that a flow in CONFIGURATION exists also in OPERATIONAL. This is done by looking up the flow in
+     * CONFIGURATION by flowId and searching the flow's priority and matches in the OPERATIONAL.
+     * @param datapathId dpid where flow is installed
+     * @param flowId The "name" of the flow, e.g., "TunnelFloodOut_100"
+     * @param tableId integer value of table
+     * @param waitFor Retry every second for waitFor milliseconds
+     * @throws InterruptedException if interrupted while waiting for flow to appear in mdsal
+     */
+    public void verifyFlowByFields(long datapathId, String flowId, short tableId, int waitFor) throws InterruptedException {
+        long start = System.currentTimeMillis();
+        do {
+            try {
+                verifyFlowByFields(datapathId, flowId, tableId);
+                return;
+            } catch (AssertionError e) {
+                if((System.currentTimeMillis() - start) >= waitFor) {
+                    throw e;
+                }
+                Thread.sleep(1000);
+            }
+        } while (true);
+    }
+
+    /**
+     * Verify that a flow in CONFIGURATION exists also in OPERATIONAL. This is done by looking up the flow in
+     * CONFIGURATION by flowId and searching the flow's priority and matches in the OPERATIONAL.
+     * @param datapathId dpid where flow is installed
+     * @param flowId The "name" of the flow, e.g., "TunnelFloodOut_100"
+     * @param tableId integer value of table
+     * @throws InterruptedException if interrupted while waiting for flow to appear in mdsal
+     */
+    public void verifyFlowByFields(long datapathId, String flowId, short tableId) throws InterruptedException {
+        org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder nodeBuilder =
+                FlowUtils.createNodeBuilder(datapathId);
+        FlowBuilder flowBuilder =
+                FlowUtils.initFlowBuilder(new FlowBuilder(), flowId, tableId);
+
+        InstanceIdentifier<Flow> iid = FlowUtils.createFlowPath(flowBuilder, nodeBuilder);
+
+        NotifyingDataChangeListener waitForIt = new NotifyingDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+                iid, null);
+        waitForIt.registerDataChangeListener(dataBroker);
+        waitForIt.waitForCreation(10000);
+
+        Flow configFlow = FlowUtils.getFlow(flowBuilder, nodeBuilder,
+                dataBroker.newReadOnlyTransaction(), LogicalDatastoreType.CONFIGURATION);
+        assertNotNull("Could not read flow " + flowId + " from configuration", configFlow);
+
+        Table table = FlowUtils.getTable(nodeBuilder, tableId, dataBroker.newReadOnlyTransaction(),
+                                                                                    LogicalDatastoreType.OPERATIONAL);
+        assertNotNull("Could not read table " + tableId + " from operational", table);
+
+        List<Flow> flows = table.getFlow();
+        Assert.assertNotNull("No flows found for table", flows);
+
+
+        for(Flow opFlow : flows) {
+            if (checkFlowsEqual(configFlow, opFlow)) {
+                return;
+            }
+        }
+        Assert.fail("Could not find matching flow in operational for " + flowId);
+    }
+
+    private boolean checkFlowsEqual(Flow configFlow, Flow opFlow) {
+        Integer configPrio = configFlow.getPriority();
+        Integer opPrio = opFlow.getPriority();
+        if (!Objects.equals(configPrio == null ? DEFAULT_PRIORITY : configPrio,
+                                                    opPrio == null ? DEFAULT_PRIORITY : opPrio)) {
+            return false;
+        }
+        return areMatchesEqual(configFlow.getMatch(), opFlow.getMatch());
+    }
+
+    private boolean areMatchesEqual(Match m1, Match m2) {
+        if (m1 == null && m2 == null) {
+            return true;
+        }
+
+        if (m1 == null || m2 == null) {
+            return false;
+        }
+
+        if (!Objects.equals(m1.getInPort(), m2.getInPort())) { return false; }
+        if (!Objects.equals(m1.getInPhyPort(), m2.getInPhyPort())) { return false; }
+        if (!Objects.equals(m1.getMetadata(), m2.getMetadata())) { return false; }
+        if (!Objects.equals(m1.getTunnel(), m2.getTunnel())) { return false; }
+        if (!Objects.equals(m1.getEthernetMatch(), m2.getEthernetMatch())) { return false; }
+        if (!Objects.equals(m1.getVlanMatch(), m2.getVlanMatch())) { return false; }
+        if (!Objects.equals(m1.getIpMatch(), m2.getIpMatch())) { return false; }
+        if (!Objects.equals(m1.getLayer3Match(), m2.getLayer3Match())) { return false; }
+        if (!Objects.equals(m1.getLayer4Match(), m2.getLayer4Match())) { return false; }
+        if (!Objects.equals(m1.getIcmpv4Match(), m2.getIcmpv4Match())) { return false; }
+        if (!Objects.equals(m1.getIcmpv6Match(), m2.getIcmpv6Match())) { return false; }
+        if (!Objects.equals(m1.getProtocolMatchFields(), m2.getProtocolMatchFields())) { return false; }
+        if (!Objects.equals(m1.getTcpFlagMatch(), m2.getTcpFlagMatch())) { return false; }
+
+        MatchAugmentationIterator it = new MatchAugmentationIterator(m1);
+        List<AllMatchesGrouping> side1Matches = Lists.newArrayList();
+        AllMatchesGrouping aug;
+        while (null != (aug = it.next())) {
+            side1Matches.add(aug);
+        }
+
+        it = new MatchAugmentationIterator(m2);
+        while(null != (aug = it.next())) {
+            if (!isMatchInList(aug, side1Matches)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private boolean isMatchInList(AllMatchesGrouping match, List<AllMatchesGrouping> matchList) {
+        for (AllMatchesGrouping match2 : matchList) {
+            if (areMatchAugmentationsEqual(match, match2)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    class MatchAugmentationIterator {
+
+        Match myMatch;
+
+        public MatchAugmentationIterator(Match match) {
+            this.myMatch = match;
+        }
+
+        private int matchAugmentationIdx = 0;
+        private int extListListIdx = 0;
+        private int extListAugmentationIdx = 0;
+
+        public AllMatchesGrouping next() {
+
+            while (matchAugmentationIdx < MATCH_AUGMENTATIONS.length) {
+                GeneralExtensionListGrouping extensionListGrouping = (GeneralExtensionListGrouping)
+                        myMatch.getAugmentation(MATCH_AUGMENTATIONS[matchAugmentationIdx]);
+
+                if (null != extensionListGrouping) {
+                    List<ExtensionList> extListList = extensionListGrouping.getExtensionList();
+
+                    while (extListListIdx < extListList.size()) {
+
+                        ExtensionList extList = extListList.get(extListListIdx);
+
+                        while(extListAugmentationIdx < EXT_LIST_AUGMENTATIONS.length) {
+                            Augmentation res = extList.getExtension().getAugmentation(
+                                    EXT_LIST_AUGMENTATIONS[extListAugmentationIdx]);
+                            ++extListAugmentationIdx;
+                            if (res != null) {
+                                return (AllMatchesGrouping) res;
+                            }
+                        }
+                        extListAugmentationIdx = 0;
+                        ++extListListIdx;
+                    }
+                    extListListIdx = 0;
+                }
+                ++matchAugmentationIdx;
+
+            }
+
+            return null;
+        }
+
+    }
+
+    private boolean areMatchAugmentationsEqual(AllMatchesGrouping obj1, AllMatchesGrouping obj2) {
+        if (obj1 == null && obj2 == null) {
+            return true;
+        }
+
+        if (obj1 == null || obj2 == null) {
+            return false;
+        }
+        if (!Objects.equals(obj1.getNxmOfEthSrc(), obj2.getNxmOfEthSrc())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfArpOp(), obj2.getNxmOfArpOp())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfUdpDst(), obj2.getNxmOfUdpDst())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxNshc3(), obj2.getNxmNxNshc3())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxCtZone(), obj2.getNxmNxCtZone())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxArpSha(), obj2.getNxmNxArpSha())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfIcmpType(), obj2.getNxmOfIcmpType())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxNshc1(), obj2.getNxmNxNshc1())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfArpSpa(), obj2.getNxmOfArpSpa())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxTunIpv4Dst(), obj2.getNxmNxTunIpv4Dst())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfTcpSrc(), obj2.getNxmOfTcpSrc())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxNshc4(), obj2.getNxmNxNshc4())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfArpTpa(), obj2.getNxmOfArpTpa())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfTcpDst(), obj2.getNxmOfTcpDst())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxNsi(), obj2.getNxmNxNsi())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxNshc2(), obj2.getNxmNxNshc2())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxArpTha(), obj2.getNxmNxArpTha())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxReg(), obj2.getNxmNxReg())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfIpSrc(), obj2.getNxmOfIpSrc())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfEthType(), obj2.getNxmOfEthType())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfEthDst(), obj2.getNxmOfEthDst())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfUdpSrc(), obj2.getNxmOfUdpSrc())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxCtState(), obj2.getNxmNxCtState())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxTunIpv4Src(), obj2.getNxmNxTunIpv4Src())) { return false; }
+        if (!Objects.equals(obj1.getNxmOfIpDst(), obj2.getNxmOfIpDst())) { return false; }
+        if (!Objects.equals(obj1.getNxmNxNsp(), obj2.getNxmNxNsp())) { return false; }
+
+        return true;
+    }
+
     /**
      * Log the flows in a given table.
      * @param datapathId dpid