Bug 8211: Fixed bug that failed to associate MD-SAL flow with VTN flow. 28/54928/1
authorShigeru Yasuda <s-yasuda@da.jp.nec.com>
Wed, 12 Apr 2017 07:41:12 +0000 (16:41 +0900)
committerVenkatrangan Govindarajan <venkatrangang@hcl.com>
Thu, 13 Apr 2017 05:09:51 +0000 (05:09 +0000)
This patch makes the VTN Manager accept IN_PORT match specified by
port number only.

Change-Id: I97a55769b9690f8d2aaabe609899007cdeecce76
Signed-off-by: Shigeru Yasuda <s-yasuda@da.jp.nec.com>
(cherry picked from commit a5e981cd23a31f212c9458e8c7788b24210d0c14)

manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/flow/stats/AddedFlowStats.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/flow/stats/StatsTimerTask.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/util/inventory/NodeUtils.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/util/inventory/SalPort.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestNodeConnectorId.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/util/inventory/NodeUtilsTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/util/inventory/SalPortTest.java

index 1102027717da285b1116588f5a72fa523bd02cb9..cd94351f2f59786652007f293a9d9b615d2f2791 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016 NEC Corporation. All rights reserved.
+ * Copyright (c) 2015, 2017 NEC Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -22,12 +22,12 @@ import org.opendaylight.vtn.manager.internal.TxContext;
 import org.opendaylight.vtn.manager.internal.TxQueue;
 import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
 import org.opendaylight.vtn.manager.internal.util.IdentifiedData;
-import org.opendaylight.vtn.manager.internal.util.MiscUtils;
 import org.opendaylight.vtn.manager.internal.util.flow.FlowCache;
 import org.opendaylight.vtn.manager.internal.util.flow.FlowFinder;
 import org.opendaylight.vtn.manager.internal.util.flow.FlowStatsUtils;
 import org.opendaylight.vtn.manager.internal.util.flow.FlowUtils;
 import org.opendaylight.vtn.manager.internal.util.flow.match.FlowMatchUtils;
+import org.opendaylight.vtn.manager.internal.util.inventory.NodeUtils;
 import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
 import org.opendaylight.vtn.manager.internal.util.log.VTNLogLevel;
 import org.opendaylight.vtn.manager.internal.util.tx.AbstractTxTask;
@@ -147,7 +147,7 @@ public final class AddedFlowStats extends AbstractTxTask<Void> {
                         vdf.getFlowId().getValue());
             } else {
                 // Verify the ingress flow entry.
-                flow = verifyIngressFlow(ctx, vdf, vfent, flow);
+                flow = verifyIngressFlow(ctx, ingressNode, vdf, vfent, flow);
             }
         }
 
@@ -158,21 +158,24 @@ public final class AddedFlowStats extends AbstractTxTask<Void> {
      * Verify the ingress flow entry of the VTN data flow.
      *
      * @param ctx    MD-SAL datastore transaction context.
+     * @param snode  A {@link SalNode} instance that specifies the ingress
+     *               node.
      * @param vdf    The VTN data flow.
      * @param vfent  The ingress flow entry of the VTN data flow.
      * @param flow   The MD-SAL flow entry associated with the ingress flow
      *               entry.
      * @return  {@code flow} on success. {@code null} on failure.
      */
-    private Flow verifyIngressFlow(TxContext ctx, VtnDataFlow vdf,
-                                   VtnFlowEntry vfent, Flow flow) {
-        // One VTN data flow should never installs more than one flow
-        // entry into the same node.
+    private Flow verifyIngressFlow(TxContext ctx, SalNode snode,
+                                   VtnDataFlow vdf, VtnFlowEntry vfent,
+                                   Flow flow) {
+        // One VTN data flow should never install more than one flow entry
+        // into the same node.
         NodeConnectorId vport =
             FlowMatchUtils.getIngressPort(vfent.getMatch());
         NodeConnectorId port = FlowMatchUtils.getIngressPort(flow.getMatch());
         Flow result = null;
-        if (MiscUtils.equalsUri(vport, port)) {
+        if (NodeUtils.equalsNodeConnectorId(snode, vport, port)) {
             // Verify flow priority.
             Integer vpri = vfent.getPriority();
             Integer pri = flow.getPriority();
index a87d1bf9107757af6cc2e78ca61dfd549e558c46..10977737aa8ce603ec2579cb62efcf926c760844 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016 NEC Corporation. All rights reserved.
+ * Copyright (c) 2015, 2017 NEC Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -109,7 +109,12 @@ public final class StatsTimerTask extends TimerTask {
                             String tname, VtnDataFlow vdf) throws VTNException {
             VtnDataFlowKey key = vdf.getKey();
             BigInteger id = key.getFlowId().getValue();
-            FlowId fid = FlowUtils.createMdFlowId(id);
+            FlowId fid = vdf.getSalFlowId();
+            if (fid == null) {
+                ctx.log(LOG, VTNLogLevel.TRACE,
+                        "Use MD-SAL flow ID derived from VTN flow ID: {}", id);
+                fid = FlowUtils.createMdFlowId(id);
+            }
 
             // Read flow statistics collected by the MD-SAL statistics manager.
             FlowCache fc = new FlowCache(vdf);
index 6fc5bd86b82ba15701e84d4fc04c7f8d33a39264..8f542c46439ea8badc5b736c22ca14c024351a8a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016 NEC Corporation. All rights reserved.
+ * Copyright (c) 2014, 2017 NEC Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -8,11 +8,14 @@
 
 package org.opendaylight.vtn.manager.internal.util.inventory;
 
+import org.opendaylight.vtn.manager.internal.util.MiscUtils;
 import org.opendaylight.vtn.manager.internal.util.rpc.RpcException;
 
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.node.info.VtnPort;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnPortDesc;
 
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+
 /**
  * {@code NodeUtils} class is a collection of utility class methods related to
  * node-id and node-connector-id, aka switch and switch port identifier.
@@ -162,6 +165,31 @@ public final class NodeUtils {
         return new VtnPortDesc(builder.toString());
     }
 
+    /**
+     * Determine whether the given two URIs are identical or not.
+     *
+     * @param snode  A {@link SalNode} instance that specifies the node.
+     *               This is used to parse node-connector-id that contains
+     *               port number only.
+     * @param id1    The first node-connector-id to be compared.
+     * @param id2    The second node-connector-id to be compared.
+     * @return  {@code true} only if {@code id1} and {@code id2} are identical.
+     */
+    public static boolean equalsNodeConnectorId(
+        SalNode snode, NodeConnectorId id1, NodeConnectorId id2) {
+        // At first try to compare as Uri string.
+        boolean ret = MiscUtils.equalsUri(id1, id2);
+        if (!ret) {
+            // Try to compare as SalPort.
+            SalPort sport1 = SalPort.create(snode, id1);
+            if (sport1 != null) {
+                ret = sport1.equals(SalPort.create(snode, id2));
+            }
+        }
+
+        return ret;
+    }
+
     /**
      * Check whether the given parameters specifies a valid switch port or not.
      *
index 9639946a5f77b8536196f945b5a69732061845a8..3aeb3ea1f40f66f925a0ef2981e00e741cc45ab8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 NEC Corporation. All rights reserved.
+ * Copyright (c) 2015, 2017 NEC Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -82,6 +82,26 @@ public final class SalPort extends SalNode {
         return create(id.getValue());
     }
 
+    /**
+     * Convert a MD-SAL node connector ID into a {@code SalPort} instance.
+     *
+     * @param snode  A {@link SalNode} instance that specifies the node.
+     *               This parameter is used only if {@code id} does not contain
+     *               node ID.
+     * @param id     A {@link NodeConnectorId} instance.
+     * @return  A {@code SalPort} instance on success.
+     *          {@code null} on failure.
+     */
+    public static SalPort create(SalNode snode, NodeConnectorId id) {
+        SalPort sport = create(id);
+        if (sport == null && snode != null && id != null) {
+            // node-connector-id may contain only a port ID.
+            sport = create(snode.getNodeNumber(), id.getValue());
+        }
+
+        return sport;
+    }
+
     /**
      * Convert a MD-SAL node connector reference into a {@code SalPort}
      * instance.
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestNodeConnectorId.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestNodeConnectorId.java
new file mode 100644 (file)
index 0000000..e3e5ed4
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017 NEC Corporation. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal;
+
+import java.util.Objects;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+
+/**
+ * An implementation of {@link NodeConnectorId} that bypasses value check.
+ */
+public final class TestNodeConnectorId extends NodeConnectorId {
+    /**
+     * A value of node-connector-id.
+     */
+    private final String  value;
+
+    /**
+     * Construct an empty instance.
+     */
+    public TestNodeConnectorId() {
+        this(null);
+    }
+
+    /**
+     * Construct a new instance.
+     *
+     * @param v  A value of node-connector-id.
+     */
+    public TestNodeConnectorId(String v) {
+        super("openflow:1:2");
+        value = v;
+    }
+
+    // Uri
+
+    /**
+     * Return the value of this node-connector-id.
+     *
+     * @return  The value of this node-connector-id.
+     */
+    @Override
+    public String getValue() {
+        return value;
+    }
+
+    // Object
+
+    /**
+     * Determine whether the given object is identical to this object.
+     *
+     * @param o  An object to be compared.
+     * @return   {@code true} if identical. Otherwise {@code false}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        boolean ret = (o == this);
+        if (!ret && o != null && getClass().equals(o.getClass())) {
+            TestNodeConnectorId ncId = (TestNodeConnectorId)o;
+            ret = Objects.equals(value, ncId.value);
+        }
+
+        return ret;
+    }
+
+    /**
+     * Return the hash code of this object.
+     *
+     * @return  The hash code.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(getClass(), value);
+    }
+}
index 4d2809039a8acbc4e55cac221ec75b128a2e3265..5490e5228fa0b73df22b29a5ce64b5677adfb44a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015 NEC Corporation. All rights reserved.
+ * Copyright (c) 2014, 2017 NEC Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -8,12 +8,17 @@
 
 package org.opendaylight.vtn.manager.internal.util.inventory;
 
+import static org.opendaylight.vtn.manager.internal.util.inventory.NodeUtils.equalsNodeConnectorId;
+
+import java.math.BigInteger;
+
 import org.junit.Test;
 
 import org.opendaylight.vtn.manager.internal.util.rpc.RpcErrorTag;
 import org.opendaylight.vtn.manager.internal.util.rpc.RpcException;
 
 import org.opendaylight.vtn.manager.internal.TestBase;
+import org.opendaylight.vtn.manager.internal.TestNodeConnectorId;
 import org.opendaylight.vtn.manager.internal.TestVtnPortDesc;
 
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.node.info.VtnPort;
@@ -21,6 +26,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev15020
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnErrorTag;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnPortDesc;
 
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+
 /**
  * JUnit test for {@link NodeUtils}.
  */
@@ -301,4 +308,135 @@ public class NodeUtilsTest extends TestBase {
             }
         }
     }
+
+    /**
+     * Test case for
+     * {@link NodeUtils#equalsNodeConnectorId(SalNode, NodeConnectorId, NodeConnectorId)}.
+     */
+    @Test
+    public void testEqualsNodeConnectorId() {
+        BigInteger[] nodeIds = {
+            BigInteger.ONE,
+            BigInteger.valueOf(2L),
+            BigInteger.valueOf(7777777L),
+            BigInteger.valueOf(0x123456789L),
+            new BigInteger("18446744073709551615"),
+        };
+        SalNode anotherNode = new SalNode(999L);
+
+        long[] portIds = {
+            1L, 2L, 345L, 6789L, 0xabcdefL, 0xffffff00L,
+        };
+
+        SalNode nullNode = null;
+        NodeConnectorId nullNcId = null;
+        NodeConnectorId empty1 = new TestNodeConnectorId();
+        NodeConnectorId empty2 = new TestNodeConnectorId();
+
+        NodeConnectorId[] badIds = {
+            null, empty1,
+
+            // Invalid node type.
+            new NodeConnectorId(""),
+            new NodeConnectorId("proto1:1:3"),
+            new NodeConnectorId("of:2:1"),
+            new NodeConnectorId("openflow1:1:4"),
+
+            // Too large DPID.
+            new NodeConnectorId("openflow:18446744073709551616:3"),
+            new NodeConnectorId("openflow:18446744073709551617:2"),
+            new NodeConnectorId("openflow:99999999999999999999999:2"),
+            new NodeConnectorId("openflow:999999999999999999999999:LOCAL"),
+
+            // Negative DPID.
+            new NodeConnectorId("openflow:-1:1"),
+            new NodeConnectorId("openflow:-12345678:2"),
+            new NodeConnectorId("openflow:-3333333333333333333333333:2"),
+
+            // Invalid DPID.
+            new NodeConnectorId("openflow:0x12345678:1"),
+            new NodeConnectorId("openflow:1234abcd:1"),
+            new NodeConnectorId("openflow:Bad DPID:1"),
+
+            // Invalid port number.
+            new NodeConnectorId("4294967041"),
+            new NodeConnectorId("4294967042"),
+            new NodeConnectorId("4294967295"),
+            new NodeConnectorId("9999999999999999999999999999"),
+            new NodeConnectorId("-1"),
+            new NodeConnectorId("-2"),
+            new NodeConnectorId("-1000000"),
+            new NodeConnectorId("abcde"),
+            new NodeConnectorId("bad port ID"),
+        };
+
+        for (BigInteger nodeId: nodeIds) {
+            SalNode snode = new SalNode(nodeId.longValue());
+            SalNode[] snodes = {nullNode, snode, anotherNode};
+            for (long portId1: portIds) {
+                for (long portId2: portIds) {
+                    // In case where both node-connector-ids are
+                    // fully-qualified.
+                    String str1 = String.format("openflow:%s:%d",
+                                                nodeId, portId1);
+                    String str2 = String.format("openflow:%s:%d",
+                                                nodeId, portId2);
+                    NodeConnectorId id1 = new NodeConnectorId(str1);
+                    NodeConnectorId id2 = new NodeConnectorId(str2);
+                    boolean match = (portId1 == portId2);
+                    for (SalNode sn: snodes) {
+                        assertEquals(match,
+                                     equalsNodeConnectorId(sn, id1, id2));
+                    }
+
+                    // In case where only one node-connector-id is
+                    // fully-qualified.
+                    str2 = Long.toString(portId2);
+                    id2 = new NodeConnectorId(str2);
+                    for (SalNode sn: snodes) {
+                        boolean expected = (match && snode.equals(sn));
+                        assertEquals(expected,
+                                     equalsNodeConnectorId(sn, id1, id2));
+                        assertEquals(expected,
+                                     equalsNodeConnectorId(sn, id2, id1));
+                    }
+
+                    // Should return if one node-connector-id is invalid.
+                    NodeConnectorId[] ids = {id1, id2};
+                    for (SalNode sn: snodes) {
+                        for (NodeConnectorId id: ids) {
+                            for (NodeConnectorId bad: badIds) {
+                                assertFalse(equalsNodeConnectorId(sn, id, bad));
+                                assertFalse(equalsNodeConnectorId(sn, bad, id));
+                            }
+                        }
+                    }
+
+                    // In case where both node-connector-ids are specified by
+                    // port number.
+                    str1 = Long.toString(portId1);
+                    id1 = new NodeConnectorId(str1);
+                    if (match) {
+                        assertEquals(id2, id1);
+                    } else {
+                        assertNotEquals(id2, id1);
+                    }
+                    for (SalNode sn: snodes) {
+                        assertEquals(match,
+                                     equalsNodeConnectorId(sn, id1, id2));
+                    }
+                }
+            }
+
+            for (SalNode sn: snodes) {
+                // Should return true if both node-connector-ids are null.
+                assertTrue(equalsNodeConnectorId(sn, nullNcId, nullNcId));
+                assertFalse(equalsNodeConnectorId(sn, empty1, nullNcId));
+                assertFalse(equalsNodeConnectorId(sn, nullNcId, empty1));
+
+                // Should return true if both node-connector-ids are empty.
+                assertTrue(equalsNodeConnectorId(sn, empty1, empty2));
+            }
+        }
+    }
 }
index 9e9a18853369fb4125a45f2a72b2657dc19891e5..06c5d5ec69465b5a8a44a9780be0ac8713329f87 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016 NEC Corporation. All rights reserved.
+ * Copyright (c) 2015, 2017 NEC Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -22,6 +22,7 @@ import org.opendaylight.vtn.manager.internal.util.flow.FlowUtils;
 import org.opendaylight.vtn.manager.internal.util.pathpolicy.PathPolicyUtils;
 
 import org.opendaylight.vtn.manager.internal.TestBase;
+import org.opendaylight.vtn.manager.internal.TestNodeConnectorId;
 
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
@@ -233,6 +234,105 @@ public class SalPortTest extends TestBase {
         }
     }
 
+    /**
+     * Test case for {@link SalPort#create(SalNode, NodeConnectorId)}.
+     */
+    @Test
+    public void testCreateNodeConnectorId() {
+        BigInteger[] nodeIds = {
+            BigInteger.ONE,
+            BigInteger.valueOf(2L),
+            BigInteger.valueOf(345678L),
+            BigInteger.valueOf(0x12345678L),
+            new BigInteger("18446744073709551615"),
+        };
+        long[] portIds = {
+            1L, 2L, 3456789L, 0xabcdefL, 0xffffff00L,
+        };
+
+        String[] bad = {
+            // Invalid node type.
+            "",
+            "proto1:1:3",
+            "of:2:1",
+            "openflow1:1:4",
+
+            // Too large DPID.
+            "openflow:18446744073709551616:3",
+            "openflow:18446744073709551617:2",
+            "openflow:99999999999999999999999:2",
+            "openflow:999999999999999999999999:LOCAL",
+
+            // Negative DPID.
+            "openflow:-1:1",
+            "openflow:-12345678:2",
+            "openflow:-3333333333333333333333333:2",
+
+            // Invalid DPID.
+            "openflow:0x12345678:1",
+            "openflow:1234abcd:1",
+            "openflow:Bad DPID:1",
+
+            // Invalid port number.
+            "4294967041",
+            "4294967042",
+            "4294967295",
+            "9999999999999999999999999999",
+            "-1",
+            "-2",
+            "-1000000",
+            "abcde",
+            "bad port ID",
+        };
+
+        SalNode nullNode = null;
+        NodeConnectorId nullNcId = null;
+        NodeConnectorId emptyNcId = new TestNodeConnectorId();
+
+        // Should return false if both SalNode and node-connector-id are null.
+        assertNull(SalPort.create(nullNode, nullNcId));
+
+        for (BigInteger nodeId: nodeIds) {
+            SalNode snode = new SalNode(nodeId.longValue());
+
+            // Should return false if node-connector-id is null.
+            assertNull(SalPort.create(snode, nullNcId));
+
+            // Should return false if node-connector-id is empty.
+            assertNull(SalPort.create(snode, emptyNcId));
+
+            for (long portId: portIds) {
+                // SalNode should be ignored if node-connector-id is
+                // fully-qualified.
+                NodeConnectorId ncId = new NodeConnectorId(
+                    String.format("openflow:%s:%d", nodeId, portId));
+                SalNode[] snodes = {nullNode, snode};
+                for (SalNode sn: snodes) {
+                    SalPort sport = SalPort.create(sn, ncId);
+                    assertNotNull(sport);
+                    assertEquals(nodeId.longValue(), sport.getNodeNumber());
+                    assertEquals(portId, sport.getPortNumber());
+                }
+
+                // In case where node-connector-id contains port number only.
+                ncId = new NodeConnectorId(Long.toString(portId));
+                SalPort sport = SalPort.create(snode, ncId);
+                assertNotNull(sport);
+                assertEquals(nodeId.longValue(), sport.getNodeNumber());
+                assertEquals(portId, sport.getPortNumber());
+
+                // Should return false if SalNode is null.
+                assertNull(SalPort.create(nullNode, ncId));
+            }
+
+            // Should return false if node-connector-id is invalid.
+            for (String id: bad) {
+                NodeConnectorId ncId = new NodeConnectorId(id);
+                assertNull(SalPort.create(snode, ncId));
+            }
+        }
+    }
+
     /**
      * Test case for {@link SalPort#create(VtnPortLocation)}.
      */