Fix to allow RESTCONF PUTing of Flows
authorTony Tkacik <ttkacik@cisco.com>
Sun, 1 Dec 2013 16:53:21 +0000 (17:53 +0100)
committerEd Warnicke <eaw@cisco.com>
Mon, 9 Dec 2013 22:58:19 +0000 (14:58 -0800)
- Due to a bug in the JAXB spec, which is propogated to
JAXRS, which forbids '/', even escaped, in path segments
in URLs... we can't use the nesting by refs under /flows.

- Therefore I've moved flow config to
nodes -> node -> table -> flow

- Making that change in the existing FlowConsumerImpl proved extremely
hard.  Due to the press of time I've introduced a very simple set of
FlowProvider, FlowCommitHandler, FlowTransaction, FlowTransactionValidator,
to cleanly separate the logic.  We can migrate over the validation rules as
need be.

PatchSet 11: Abstract the Transaction.  Add Groups.
PatchSet 12: Fixes for Meters

PatchSet 13: Yet another rebase

PatchSet 14: Squashing https://git.opendaylight.org/gerrit/#/c/3304/8

Must proceed: http://git.opendaylight.org/gerrit/3596/1
in openflowplugin

Change-Id: I1f1cd04a04f7a30630062725e63112cda4b049f1
Signed-off-by: Ed Warnicke <eaw@cisco.com>
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
opendaylight/md-sal/sal-binding-broker/pom.xml
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AugmentationVerifier.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/BindingTestUtilities.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/MultipleAugmentationPuts.java [new file with mode: 0644]

index ac84aab977783edbebc416d4755379967f70e84c..f9f72094f816d8166c0067f7e1e1aff0829a2aac 100644 (file)
             <version>${slf4j.version}</version>
             <scope>test</scope>
         </dependency>
+            <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-inet-types</artifactId>
+            <version>2010.09.24.2-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+               <groupId>org.opendaylight.controller.model</groupId>
+               <artifactId>model-flow-base</artifactId>
+               <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+               <groupId>org.opendaylight.controller.model</groupId>
+               <artifactId>model-flow-service</artifactId>
+               <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+               <groupId>org.opendaylight.controller.model</groupId>
+               <artifactId>model-flow-statistics</artifactId>
+               <version>1.0-SNAPSHOT</version>
+        </dependency>
     </dependencies>
 </project>
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AugmentationVerifier.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AugmentationVerifier.java
new file mode 100644 (file)
index 0000000..bff77be
--- /dev/null
@@ -0,0 +1,32 @@
+package org.opendaylight.controller.sal.binding.test;
+
+import junit.framework.Assert;
+
+
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+
+public class AugmentationVerifier<T extends Augmentable<T>> {
+
+    private T object;
+
+    public AugmentationVerifier(T objectToVerify) {
+        this.object = objectToVerify;
+    }
+
+    public AugmentationVerifier<T> assertHasAugmentation(Class<? extends Augmentation<T>> augmentation) {
+        assertHasAugmentation(object, augmentation);
+        return (AugmentationVerifier<T>) this;
+    }
+
+    public static <T extends Augmentable<T>> void assertHasAugmentation(T object,
+            Class<? extends Augmentation<T>> augmentation) {
+        Assert.assertNotNull(object);
+        Assert.assertNotNull("Augmentation " + augmentation.getSimpleName() + " is not present.", object.getAugmentation(augmentation));
+    }
+
+    public static <T extends Augmentable<T>> AugmentationVerifier<T> from(T obj) {
+        return new AugmentationVerifier<T>(obj);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/BindingTestUtilities.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/BindingTestUtilities.java
new file mode 100644 (file)
index 0000000..7d14919
--- /dev/null
@@ -0,0 +1,5 @@
+package org.opendaylight.controller.sal.binding.test;
+
+public class BindingTestUtilities {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/MultipleAugmentationPuts.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/MultipleAugmentationPuts.java
new file mode 100644 (file)
index 0000000..6d1a699
--- /dev/null
@@ -0,0 +1,210 @@
+package org.opendaylight.controller.sal.binding.test.bugfix;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
+import org.opendaylight.controller.sal.binding.test.AugmentationVerifier;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter32;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter64;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.Duration;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.DurationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStatsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStatsKey;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+import com.google.common.collect.FluentIterable;
+
+import static org.junit.Assert.*;
+
+public class MultipleAugmentationPuts extends AbstractDataServiceTest implements DataChangeListener {
+
+    private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
+    private static final String NODE_ID = "openflow:1";
+
+    private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+
+    private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
+            NODE_ID);
+
+    private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
+            .toInstance();
+
+    private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier
+            .builder(NODES_INSTANCE_ID_BA) //
+            .child(Node.class, NODE_KEY).toInstance();
+
+    private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier NODE_INSTANCE_ID_BI = //
+    org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
+            .node(Nodes.QNAME) //
+            .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
+            .toInstance();
+    private DataChangeEvent<InstanceIdentifier<?>, DataObject> receivedChangeEvent;
+
+    /**
+     * Test for Bug 148
+     * 
+     * @throws Exception
+     */
+    @Test
+    public void testAugmentSerialization() throws Exception {
+
+        baDataService.registerDataChangeListener(NODES_INSTANCE_ID_BA, this);
+
+        Node flowCapableNode = createTestNode(FlowCapableNode.class, flowCapableNodeAugmentation());
+        commitNodeAndVerifyTransaction(flowCapableNode);
+
+        assertNotNull(receivedChangeEvent);
+        verifyNode((Nodes) receivedChangeEvent.getUpdatedOperationalSubtree(), flowCapableNode);
+
+        Nodes nodes = checkForNodes();
+        verifyNode(nodes, flowCapableNode).assertHasAugmentation(FlowCapableNode.class);
+        ;
+        assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
+        Node meterStatsNode = createTestNode(NodeMeterStatistics.class, nodeMeterStatistics());
+        commitNodeAndVerifyTransaction(meterStatsNode);
+
+        assertNotNull(receivedChangeEvent);
+        verifyNode((Nodes) receivedChangeEvent.getUpdatedOperationalSubtree(), meterStatsNode);
+
+        assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
+
+        Node mergedNode = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
+
+        AugmentationVerifier.from(mergedNode) //
+                .assertHasAugmentation(FlowCapableNode.class) //
+                .assertHasAugmentation(NodeMeterStatistics.class);
+
+        assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
+
+        Node meterStatsNodeWithDuration = createTestNode(NodeMeterStatistics.class, nodeMeterStatistics(5, true));
+        commitNodeAndVerifyTransaction(meterStatsNodeWithDuration);
+
+        
+        Node nodeWithUpdatedList = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
+        AugmentationVerifier.from(nodeWithUpdatedList) //
+                .assertHasAugmentation(FlowCapableNode.class) //
+                .assertHasAugmentation(NodeMeterStatistics.class);
+        
+        List<MeterStats> meterStats = nodeWithUpdatedList.getAugmentation(NodeMeterStatistics.class).getMeterStatistics().getMeterStats();
+        assertNotNull(meterStats);
+        assertFalse(meterStats.isEmpty());
+        assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
+        testNodeRemove();
+    }
+
+    private <T extends Augmentation<Node>> Node createTestNode(Class<T> augmentationClass, T augmentation) {
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        nodeBuilder.setId(new NodeId(NODE_ID));
+        nodeBuilder.setKey(NODE_KEY);
+        nodeBuilder.addAugmentation(augmentationClass, augmentation);
+        return nodeBuilder.build();
+    }
+
+    private DataModificationTransaction commitNodeAndVerifyTransaction(Node original) throws Exception {
+        DataModificationTransaction transaction = baDataService.beginTransaction();
+        transaction.putOperationalData(NODE_INSTANCE_ID_BA, original);
+        RpcResult<TransactionStatus> result = transaction.commit().get();
+        assertEquals(TransactionStatus.COMMITED, result.getResult());
+        return transaction;
+    }
+
+    private void testNodeRemove() throws Exception {
+        DataModificationTransaction transaction = baDataService.beginTransaction();
+        transaction.removeOperationalData(NODE_INSTANCE_ID_BA);
+        RpcResult<TransactionStatus> result = transaction.commit().get();
+        assertEquals(TransactionStatus.COMMITED, result.getResult());
+
+        Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
+        assertNull(node);
+    }
+
+    private AugmentationVerifier<Node> verifyNode(Nodes nodes, Node original) {
+        assertNotNull(nodes);
+        assertNotNull(nodes.getNode());
+        assertEquals(1, nodes.getNode().size());
+        Node readedNode = nodes.getNode().get(0);
+        assertEquals(original.getId(), readedNode.getId());
+        assertEquals(original.getKey(), readedNode.getKey());
+        return new AugmentationVerifier<Node>(readedNode);
+    }
+
+    private void assertBindingIndependentVersion(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
+        CompositeNode node = biDataService.readOperationalData(nodeId);
+        assertNotNull(node);
+    }
+
+    private Nodes checkForNodes() {
+        return (Nodes) baDataService.readOperationalData(NODES_INSTANCE_ID_BA);
+    }
+
+    private NodeMeterStatistics nodeMeterStatistics() {
+        return nodeMeterStatistics(10, false);
+    }
+
+    private NodeMeterStatistics nodeMeterStatistics(int count, boolean setDuration) {
+        NodeMeterStatisticsBuilder nmsb = new NodeMeterStatisticsBuilder();
+        MeterStatisticsBuilder meterStats = new MeterStatisticsBuilder();
+
+        List<MeterStats> stats = new ArrayList<>(count);
+        for (int i = 0; i <= count; i++) {
+            MeterStatsBuilder statistic = new MeterStatsBuilder();
+            statistic.setKey(new MeterStatsKey(new MeterId((long) i)));
+            statistic.setByteInCount(new Counter64(BigInteger.valueOf(34590 + i)));
+            statistic.setFlowCount(new Counter32(4569L + i));
+
+            if (setDuration) {
+                DurationBuilder duration = new DurationBuilder();
+                duration.setNanosecond(new Counter32(70L));
+                statistic.setDuration(duration.build());
+            }
+
+            stats.add(statistic.build());
+        }
+        meterStats.setMeterStats(stats);
+        nmsb.setMeterStatistics(meterStats.build());
+        return nmsb.build();
+    }
+
+    private FlowCapableNode flowCapableNodeAugmentation() {
+        FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
+        fnub.setHardware("Hardware Foo");
+        fnub.setManufacturer("Manufacturer Foo");
+        fnub.setSerialNumber("Serial Foo");
+        fnub.setDescription("Description Foo");
+        fnub.setSoftware("JUnit emulated");
+        FlowCapableNode fnu = fnub.build();
+        return fnu;
+    }
+
+    @Override
+    public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        receivedChangeEvent = change;
+    }
+
+}