Implementing VPP Renderer registration 52/39652/6
authorMichal Cmarada <mcmarada@cisco.com>
Wed, 1 Jun 2016 15:42:42 +0000 (17:42 +0200)
committerMartin Sunal <msunal@cisco.com>
Thu, 2 Jun 2016 19:23:07 +0000 (19:23 +0000)
Change-Id: I516aa14fd96f0931dfd3a1e60a40c0cdec6b041a
Signed-off-by: Michal Cmarada <mcmarada@cisco.com>
19 files changed:
features/src/main/features/features.xml
renderers/vpp/pom.xml
renderers/vpp/src/main/config/default-config.xml
renderers/vpp/src/main/java/org/opendaylight/controller/config/yang/config/vpp_provider/impl/GbpVppProviderModule.java
renderers/vpp/src/main/java/org/opendaylight/controller/config/yang/config/vpp_provider/impl/VppRenderer.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppNodeListener.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/manager/VppNodeManager.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/Action.java [new file with mode: 0755]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/AllowAction.java [new file with mode: 0755]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/Classifier.java [new file with mode: 0755]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/EtherTypeClassifier.java [new file with mode: 0755]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/IpProtoClassifier.java [new file with mode: 0755]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/General.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/VppIidFactory.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/VppNodeWriter.java [new file with mode: 0644]
renderers/vpp/src/main/yang/vpp-provider-impl.yang
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/VppRendererDataBrokerTest.java
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/manager/VppManagerDataStoreTest.java [new file with mode: 0644]
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/VppNodeWriterTest.java [new file with mode: 0644]

index 591ed7fc0df403096139c1955058755327f8a166..c1497cd6a2b8904a9069faf09c41fa9ae1942e87 100755 (executable)
     -->
     <feature name='odl-groupbasedpolicy-vpp' version='${project.version}' description='OpenDaylight :: groupbasedpolicy :: VPP renderer '>
         <feature version="${project.version}">odl-groupbasedpolicy-base</feature>
+        <feature version="${netconf.version}">odl-netconf-topology</feature>
         <bundle>mvn:org.opendaylight.groupbasedpolicy/l2-l3-domain-extension/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.groupbasedpolicy/vpp-renderer/{{VERSION}}</bundle>
         <configfile finalname="${config.configfile.directory}/15-l2-l3-domain-extension.xml">mvn:org.opendaylight.groupbasedpolicy/l2-l3-domain-extension/{{VERSION}}/xml/config</configfile>
index 3ea402b0ca56eb32419ebcd3824a2f3432276a31..6ff00a804f398bceeea2fb8031977010de801073 100644 (file)
   <artifactId>vpp-renderer</artifactId>
   <packaging>bundle</packaging>
 
-  <properties></properties>
+  <properties>
+    <netconf.version>1.1.0-SNAPSHOT</netconf.version>
+  </properties>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.opendaylight.netconf</groupId>
+        <artifactId>netconf-artifacts</artifactId>
+        <version>${netconf.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>mdsal-artifacts</artifactId>
+        <version>${mdsal.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
 
   <dependencies>
     <!-- project specific dependencies -->
       <groupId>org.opendaylight.mdsal.model</groupId>
       <artifactId>ietf-interfaces</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.netconf</groupId>
+      <artifactId>sal-netconf-connector</artifactId>
+    </dependency>
 
     <!-- testing dependencies -->
     <dependency>
index baff11b49262e1b626b0629c34378aaabface589..8049bcfaa3e52b0bc6bf410dcb38d339672edb67 100644 (file)
                       <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
                       <name>binding-data-broker</name>
                     </data-broker>
+                    <broker>
+                        <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+                        <name>binding-osgi-broker</name>
+                    </broker>
                 </module>
             </modules>
         </data>
index cb462bdb404a7156e1b984d1803401cfea262237..744f116c9f814ae575d7ae1d904ddaedbcbcfb04 100644 (file)
@@ -30,7 +30,7 @@ public class GbpVppProviderModule extends org.opendaylight.controller.config.yan
 
     @Override
     public java.lang.AutoCloseable createInstance() {
-        final VppRenderer vppRenderer = new VppRenderer(getDataBrokerDependency());
+        final VppRenderer vppRenderer = new VppRenderer(getDataBrokerDependency(), getBrokerDependency());
 
         LOG.info("VPP Renderer instance has been created");
 
index f3885f2d3dbc4e2e1e48b9b12dafa771eb02feab..d0e9ac411cea5afa39662070704c6a3d15cc51c2 100644 (file)
-/*\r
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-\r
-package org.opendaylight.controller.config.yang.config.vpp_provider.impl;\r
-\r
-import com.google.common.base.Preconditions;\r
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-public class VppRenderer implements AutoCloseable {\r
-    private static final Logger LOG = LoggerFactory.getLogger(VppRenderer.class);\r
-\r
-    private DataBroker dataBroker;\r
-\r
-    public VppRenderer(DataBroker dataBroker) {\r
-        Preconditions.checkNotNull(dataBroker);\r
-\r
-        this.dataBroker = dataBroker;\r
-\r
-        LOG.info("VPP Renderer has Started");\r
-    }\r
-\r
-    @Override\r
-    public void close() throws Exception {\r
-        this.dataBroker = null;\r
-    }\r
-\r
-}\r
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. 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.controller.config.yang.config.vpp_provider.impl;
+
+import java.util.List;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.listener.VppNodeListener;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.manager.VppNodeManager;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.sf.AllowAction;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.sf.EtherTypeClassifier;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.Renderer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.RendererBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.RendererKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.CapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererNodesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.SupportedActionDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.SupportedActionDefinitionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.SupportedClassifierDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.SupportedClassifierDefinitionBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+
+public class VppRenderer implements AutoCloseable, BindingAwareProvider {
+
+    private static final Logger LOG = LoggerFactory.getLogger(VppRenderer.class);
+
+    private final List<SupportedActionDefinition> actionDefinitions =
+            ImmutableList.of(new SupportedActionDefinitionBuilder().setActionDefinitionId(new AllowAction().getId())
+                .setSupportedParameterValues(new AllowAction().getSupportedParameterValues())
+                .build());
+    private final List<SupportedClassifierDefinition> classifierDefinitions = ImmutableList
+        .of(new SupportedClassifierDefinitionBuilder().setClassifierDefinitionId(new EtherTypeClassifier(null).getId())
+            .setSupportedParameterValues(new EtherTypeClassifier(null).getSupportedParameterValues())
+            .build());
+
+    private DataBroker dataBroker;
+    private VppNodeManager vppNodeManager;
+    private VppNodeListener vppNodeListener;
+
+    public VppRenderer(DataBroker dataBroker, BindingAwareBroker bindingAwareBroker) {
+        this.dataBroker = Preconditions.checkNotNull(dataBroker);
+        bindingAwareBroker.registerProvider(this);
+    }
+
+    @Override
+    public void close() throws Exception {
+        LOG.info("Closing Vpp renderer");
+        if (vppNodeListener != null) {
+            vppNodeListener.close();
+        }
+        unregisterFromRendererManager();
+    }
+
+    @Override
+    public void onSessionInitiated(BindingAwareBroker.ProviderContext providerContext) {
+        LOG.info("starting vpp renderer");
+
+        // vpp-node-manager
+        vppNodeManager = new VppNodeManager(dataBroker, providerContext);
+        vppNodeListener = new VppNodeListener(dataBroker, vppNodeManager);
+
+        registerToRendererManager();
+    }
+
+    private void registerToRendererManager() {
+        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+
+        Renderer renderer = new RendererBuilder().setName(VppNodeManager.vppRenderer)
+            .setRendererNodes(new RendererNodesBuilder().build())
+            .setCapabilities(new CapabilitiesBuilder().setSupportedActionDefinition(actionDefinitions)
+                .setSupportedClassifierDefinition(classifierDefinitions)
+                .build())
+            .build();
+
+        writeTransaction.put(LogicalDatastoreType.OPERATIONAL, VppIidFactory.getRendererIID(renderer.getKey()),
+                renderer, true);
+        CheckedFuture<Void, TransactionCommitFailedException> future = writeTransaction.submit();
+        Futures.addCallback(future, new FutureCallback<Void>() {
+
+            @Override
+            public void onFailure(Throwable throwable) {
+                LOG.error("Could not register renderer {}: {}", renderer, throwable);
+            }
+
+            @Override
+            public void onSuccess(Void result) {
+                LOG.debug("Renderer {} successfully registered.", renderer);
+            }
+        });
+    }
+
+
+    private void unregisterFromRendererManager() {
+        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.delete(LogicalDatastoreType.OPERATIONAL, VppIidFactory.getRendererIID(new RendererKey(VppNodeManager.vppRenderer)));
+
+        CheckedFuture<Void, TransactionCommitFailedException> future = writeTransaction.submit();
+        Futures.addCallback(future, new FutureCallback<Void>() {
+
+            @Override
+            public void onFailure(Throwable throwable) {
+                LOG.error("Could not unregister renderer {}: {}", VppNodeManager.vppRenderer, throwable);
+            }
+
+            @Override
+            public void onSuccess(Void result) {
+                LOG.debug("Renderer {} successfully unregistered.", VppNodeManager.vppRenderer);
+            }
+        });
+    }
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppNodeListener.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppNodeListener.java
new file mode 100644 (file)
index 0000000..df39b16
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. 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.groupbasedpolicy.renderer.vpp.listener;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.manager.VppNodeManager;
+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.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+public class VppNodeListener implements DataTreeChangeListener<Node>, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(VppNodeListener.class);
+
+    private final ListenerRegistration<VppNodeListener> listenerRegistration;
+    private final VppNodeManager nodeManager;
+
+    public VppNodeListener(DataBroker dataBroker, VppNodeManager nodeManager) {
+        this.nodeManager = Preconditions.checkNotNull(nodeManager);
+        // Register listener
+        final DataTreeIdentifier<Node> networkTopologyPath = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
+                InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class).child(Node.class).build());
+        listenerRegistration =
+                Preconditions.checkNotNull(dataBroker.registerDataTreeChangeListener(networkTopologyPath, this));
+        LOG.info("Network-Topology VppNodelistener registered");
+    }
+
+    @Override
+    public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<Node>> changes) {
+        LOG.debug("Topology Node changed. Changes {}", changes);
+
+        for (DataTreeModification<Node> modification : changes) {
+            DataObjectModification<Node> rootNode = modification.getRootNode();
+            Node dataAfter = rootNode.getDataAfter();
+            Node dataBefore = rootNode.getDataBefore();
+            nodeManager.syncNodes(dataAfter, dataBefore);
+        }
+    }
+
+    public ListenerRegistration<VppNodeListener> getRegistrationObject() {
+        return listenerRegistration;
+    }
+
+    @Override
+    public void close() throws Exception {
+        listenerRegistration.close();
+    }
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/manager/VppNodeManager.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/manager/VppNodeManager.java
new file mode 100644 (file)
index 0000000..ba0ef5a
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. 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.groupbasedpolicy.renderer.vpp.manager;
+
+import static org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus.Connected;
+import static org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus.Connecting;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppNodeWriter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.RendererName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
+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.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+public class VppNodeManager {
+
+    public static final RendererName vppRenderer = new RendererName("vpp-renderer");
+    private static final TopologyId TOPOLOGY_ID = new TopologyId("topology-netconf");
+    private static final Logger LOG = LoggerFactory.getLogger(VppNodeManager.class);
+    private static final Map<InstanceIdentifier<Node>, DataBroker> netconfNodeCache = new HashMap<>();
+    private static final String V3PO_CAPABILITY = "(urn:opendaylight:params:xml:ns:yang:v3po?revision=2015-01-05)v3po";
+    private static final String INTERFACES_CAPABILITY =
+            "(urn:ietf:params:xml:ns:yang:ietf-interfaces?revision=2014-05-08)ietf-interfaces";
+    private final DataBroker dataBroker;
+    private final MountPointService mountService;
+    private final List<String> requiredCapabilities;
+
+    public VppNodeManager(DataBroker dataBroker, BindingAwareBroker.ProviderContext session) {
+        this.dataBroker = Preconditions.checkNotNull(dataBroker);
+        mountService = Preconditions.checkNotNull(session.getSALService(MountPointService.class));
+        requiredCapabilities = initializeRequiredCapabilities();
+    }
+
+    static DataBroker getDataBrokerFromCache(InstanceIdentifier<Node> iid) {
+        return netconfNodeCache.get(iid); // TODO read from DS
+    }
+
+    /**
+     * Synchronizes nodes to DataStore based on their modification state which results in
+     * create/update/remove of Node.
+     */
+    public void syncNodes(Node dataAfter, Node dataBefore) {
+        // New node
+        if (dataBefore == null && dataAfter != null) {
+            createNode(dataAfter);
+        }
+        // Connected/disconnected node
+        if (dataBefore != null && dataAfter != null) {
+            updateNode(dataAfter);
+        }
+        // Removed node
+        if (dataBefore != null && dataAfter == null) {
+            removeNode(dataBefore);
+        }
+    }
+
+    private void createNode(Node node) {
+        LOG.info("Registering new node {}", node.getNodeId().getValue());
+        NetconfNode netconfNode = getNodeAugmentation(node);
+        if (netconfNode == null) {
+            return;
+        }
+        NetconfNodeConnectionStatus.ConnectionStatus connectionStatus = netconfNode.getConnectionStatus();
+        switch (connectionStatus) {
+            case Connecting:
+                LOG.info("Connecting device {} ...", node.getNodeId().getValue());
+                break;
+            case Connected:
+                resolveConnectedNode(node, netconfNode);
+                LOG.info("Node {} is capable and ready", node.getNodeId().getValue());
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void updateNode(Node node) {
+        NetconfNode netconfNode = getNodeAugmentation(node);
+        if (netconfNode == null || netconfNode.getConnectionStatus() == null) {
+            return;
+        }
+        NetconfNodeConnectionStatus.ConnectionStatus afterNodeStatus = netconfNode.getConnectionStatus();
+        if (afterNodeStatus.equals(Connected)) {
+            resolveConnectedNode(node, netconfNode);
+            LOG.info("Node {} is capable and ready", node.getNodeId().getValue());
+        }
+        if (afterNodeStatus.equals(Connecting)) {
+            resolveDisconnectedNode(node);
+            LOG.info("Node {} has been disconnected, removing from available nodes", node.getNodeId().getValue());
+        }
+    }
+
+    private void removeNode(Node node) {
+        resolveDisconnectedNode(node);
+        LOG.info("Node {} has been removed", node.getNodeId().getValue());
+    }
+
+    private void resolveConnectedNode(Node node, NetconfNode netconfNode) {
+        InstanceIdentifier<Node> mountPointIid = getMountpointIid(node);
+        // Mountpoint iid == path in renderer-node
+        RendererNode rendererNode = remapNode(mountPointIid);
+        VppNodeWriter vppNodeWriter = new VppNodeWriter();
+        vppNodeWriter.cache(rendererNode);
+        if (!isCapableNetconfDevice(node, netconfNode)) {
+            return;
+        }
+        vppNodeWriter.commitToDatastore(dataBroker);
+        DataBroker mountpoint = getNodeMountPoint(mountPointIid);
+        netconfNodeCache.put(mountPointIid, mountpoint);
+    }
+
+    private void resolveDisconnectedNode(Node node) {
+        InstanceIdentifier<Node> mountPointIid = getMountpointIid(node);
+        RendererNode rendererNode = remapNode(mountPointIid);
+        VppNodeWriter vppNodeWriter = new VppNodeWriter();
+        vppNodeWriter.cache(rendererNode);
+        vppNodeWriter.removeFromDatastore(dataBroker);
+        netconfNodeCache.remove(mountPointIid);
+    }
+
+    private RendererNode remapNode(InstanceIdentifier<Node> path) {
+        RendererNodeBuilder rendererNodeBuilder = new RendererNodeBuilder();
+        rendererNodeBuilder.setKey(new RendererNodeKey(path)).setNodePath(path);
+        return rendererNodeBuilder.build();
+    }
+
+    private InstanceIdentifier<Node> getMountpointIid(Node node) {
+        return InstanceIdentifier.builder(NetworkTopology.class)
+            .child(Topology.class, new TopologyKey(TOPOLOGY_ID))
+            .child(Node.class, new NodeKey(node.getNodeId()))
+            .build();
+    }
+
+    private boolean isCapableNetconfDevice(Node node, NetconfNode netconfAugmentation) {
+        if (netconfAugmentation.getAvailableCapabilities() == null
+                || netconfAugmentation.getAvailableCapabilities().getAvailableCapability() == null
+                || netconfAugmentation.getAvailableCapabilities().getAvailableCapability().isEmpty()) {
+            LOG.warn("Node {} does not contain any capabilities", node.getNodeId().getValue());
+            return false;
+        }
+        if (!capabilityCheck(netconfAugmentation.getAvailableCapabilities().getAvailableCapability())) {
+            LOG.warn("Node {} does not contain all capabilities required by vpp-renderer", node.getNodeId().getValue());
+            return false;
+        }
+        return true;
+    }
+
+    private boolean capabilityCheck(final List<String> capabilities) {
+        for (String requiredCapability : requiredCapabilities) {
+            if (!capabilities.contains(requiredCapability)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private DataBroker getNodeMountPoint(InstanceIdentifier<Node> mountPointIid) {
+        Optional<MountPoint> optionalObject = mountService.getMountPoint(mountPointIid);
+        MountPoint mountPoint;
+        if (optionalObject.isPresent()) {
+            mountPoint = optionalObject.get();
+            if (mountPoint != null) {
+                Optional<DataBroker> optionalDataBroker = mountPoint.getService(DataBroker.class);
+                if (optionalDataBroker.isPresent()) {
+                    return optionalDataBroker.get();
+                } else {
+                    LOG.debug("Cannot obtain data broker from mountpoint {}", mountPoint);
+                }
+            } else {
+                LOG.debug("Cannot obtain mountpoint with IID {}", mountPointIid);
+            }
+        }
+        return null;
+    }
+
+    private NetconfNode getNodeAugmentation(Node node) {
+        NetconfNode netconfNode = node.getAugmentation(NetconfNode.class);
+        if (netconfNode == null) {
+            LOG.warn("Node {} is not a netconf device", node.getNodeId().getValue());
+            return null;
+        }
+        return netconfNode;
+    }
+
+    /**
+     * Initialize all common capabilities required by VPP renderer. Any connected node is examined
+     * whether it's
+     * an appropriate device to handle configuration created by this renderer. A device must support
+     * all capabilities
+     * in list below.
+     *
+     * @return list of string representations of required capabilities
+     */
+    private List<String> initializeRequiredCapabilities() {
+        // Required device capabilities
+
+        String[] capabilityEntries = {V3PO_CAPABILITY, INTERFACES_CAPABILITY};
+        return Arrays.asList(capabilityEntries);
+    }
+
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/Action.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/Action.java
new file mode 100755 (executable)
index 0000000..e5b4cd3
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. 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.groupbasedpolicy.renderer.vpp.sf;
+
+import java.util.List;
+
+import org.opendaylight.groupbasedpolicy.api.Validator;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.action.definition.SupportedParameterValues;
+
+/**
+ * Represent an action definition, and provide tools for generating
+ * flow instructions based on the action.
+ */
+public abstract class Action implements Validator<ActionInstance> {
+
+    /**
+     * Get the action definition for this action.
+     *
+     * @return the {@link ActionDefinition} for this action
+     */
+    public abstract ActionDefinitionId getId();
+
+    /**
+     * Get the action definition for this action.
+     *
+     * @return the {@link ActionDefinition} for this action
+     */
+    public abstract ActionDefinition getActionDef();
+
+    /**
+     * The result represents supported parameters for the action by renderer.
+     *
+     * @return list of supported parameters by the action
+     */
+    public abstract List<SupportedParameterValues> getSupportedParameterValues();
+
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/AllowAction.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/AllowAction.java
new file mode 100755 (executable)
index 0000000..1ad169a
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. 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.groupbasedpolicy.renderer.vpp.sf;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.opendaylight.groupbasedpolicy.api.ValidationResult;
+import org.opendaylight.groupbasedpolicy.api.sf.AllowActionDefinition;
+import org.opendaylight.groupbasedpolicy.dto.ValidationResultBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.action.definition.SupportedParameterValues;
+
+/**
+ * Allow action.
+ */
+public class AllowAction extends Action {
+
+    @Override
+    public ActionDefinitionId getId() {
+        return AllowActionDefinition.ID;
+    }
+
+    @Override
+    public ActionDefinition getActionDef() {
+        return AllowActionDefinition.DEFINITION;
+    }
+
+    @Override
+    public ValidationResult validate(ActionInstance actionInstance) {
+        return new ValidationResultBuilder().success().build();
+    }
+
+    @Override
+    public List<SupportedParameterValues> getSupportedParameterValues() {
+        // allow action definition has no parameter
+        return Collections.emptyList();
+    }
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/Classifier.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/Classifier.java
new file mode 100755 (executable)
index 0000000..f6beb86
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. 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.groupbasedpolicy.renderer.vpp.sf;
+
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ClassifierDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.classifier.definition.SupportedParameterValues;
+
+/**
+ * Represent a classifier definition.
+ */
+public abstract class Classifier {
+
+    protected final Classifier parent;
+
+    public static final EtherTypeClassifier ETHER_TYPE_CL = new EtherTypeClassifier(null);
+    public static final IpProtoClassifier IP_PROTO_CL = new IpProtoClassifier(ETHER_TYPE_CL);
+
+    protected Classifier(Classifier parent) {
+        this.parent = parent;
+    }
+
+    /**
+     * Get the classifier definition id for this classifier.
+     *
+     * @return the {@link ClassifierDefinitionId} for this classifier
+     */
+    public abstract ClassifierDefinitionId getId();
+
+    /**
+     * Get the classifier definition for this classifier.
+     *
+     * @return the {@link ClassifierDefinition} for this classifier
+     */
+    public abstract ClassifierDefinition getClassifierDefinition();
+
+    /**
+     * Get parent for this classifier.
+     *
+     * @return parent classifier, see {@link Classifier}
+     */
+    public final Classifier getParent() {
+        return parent;
+    }
+
+    /**
+     * The result represents supported parameters for the classifier by renderer.
+     *
+     * @return list of supported parameters by the classifier
+     */
+    public abstract List<SupportedParameterValues> getSupportedParameterValues();
+
+    /**
+     * Checks presence of required {@code params} in order to decide if classifier can update
+     * {@code matches} properly.
+     *
+     * @param params inserted parameters, not null
+     * @throws IllegalArgumentException when any of required {@code params} is not present
+     */
+    protected abstract void checkPresenceOfRequiredParams(Map<String, ParameterValue> params);
+
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/EtherTypeClassifier.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/EtherTypeClassifier.java
new file mode 100755 (executable)
index 0000000..447fb1b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. 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.groupbasedpolicy.renderer.vpp.sf;
+
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.groupbasedpolicy.api.sf.EtherTypeClassifierDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ClassifierDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.has.parameters.type.parameter.type.IntBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.classifier.definition.SupportedParameterValues;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.classifier.definition.SupportedParameterValuesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.supported._int.value.fields.SupportedIntValue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.supported._int.value.fields.SupportedIntValueBuilder;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Match on the ether type of the traffic.
+ */
+public class EtherTypeClassifier extends Classifier {
+
+    public EtherTypeClassifier(Classifier parent) {
+        super(parent);
+    }
+
+    @Override
+    public ClassifierDefinitionId getId() {
+        return EtherTypeClassifierDefinition.ID;
+    }
+
+    @Override
+    public ClassifierDefinition getClassifierDefinition() {
+        return EtherTypeClassifierDefinition.DEFINITION;
+    }
+
+    @Override
+    public List<SupportedParameterValues> getSupportedParameterValues() {
+
+        List<SupportedIntValue> values = ImmutableList.of(
+                new SupportedIntValueBuilder().setValue(EtherTypeClassifierDefinition.IPv4_VALUE).build(),
+                new SupportedIntValueBuilder().setValue(EtherTypeClassifierDefinition.IPv6_VALUE).build());
+        SupportedParameterValuesBuilder builder = new SupportedParameterValuesBuilder();
+        builder.setParameterName(new ParameterName(EtherTypeClassifierDefinition.ETHERTYPE_PARAM));
+        builder.setParameterType(new IntBuilder().setSupportedIntValue(values).build());
+
+        return ImmutableList.of(builder.build());
+    }
+
+    @Override
+    protected void checkPresenceOfRequiredParams(Map<String, ParameterValue> params) {
+        if (params.get(EtherTypeClassifierDefinition.ETHERTYPE_PARAM) == null) {
+            throw new IllegalArgumentException(
+                    "Parameter " + EtherTypeClassifierDefinition.ETHERTYPE_PARAM + " not specified.");
+        }
+        if (params.get(EtherTypeClassifierDefinition.ETHERTYPE_PARAM).getIntValue() == null) {
+            throw new IllegalArgumentException(
+                    "Value of " + EtherTypeClassifierDefinition.ETHERTYPE_PARAM + " parameter is not present.");
+        }
+    }
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/IpProtoClassifier.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/sf/IpProtoClassifier.java
new file mode 100755 (executable)
index 0000000..019bde0
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. 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.groupbasedpolicy.renderer.vpp.sf;
+
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.groupbasedpolicy.api.sf.IpProtoClassifierDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ClassifierDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.has.parameters.type.parameter.type.IntBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.classifier.definition.SupportedParameterValues;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.classifier.definition.SupportedParameterValuesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.supported._int.value.fields.SupportedIntValue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.supported._int.value.fields.SupportedIntValueBuilder;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Match on the IP protocol of IP traffic.
+ */
+public class IpProtoClassifier extends Classifier {
+
+    protected IpProtoClassifier(Classifier parent) {
+        super(parent);
+    }
+
+    @Override
+    public ClassifierDefinitionId getId() {
+        return IpProtoClassifierDefinition.ID;
+    }
+
+    @Override
+    public ClassifierDefinition getClassifierDefinition() {
+        return IpProtoClassifierDefinition.DEFINITION;
+    }
+
+    @Override
+    public List<SupportedParameterValues> getSupportedParameterValues() {
+
+        List<SupportedIntValue> values = ImmutableList.of(
+                new SupportedIntValueBuilder().setValue(IpProtoClassifierDefinition.ICMP_VALUE).build(),
+                new SupportedIntValueBuilder().setValue(IpProtoClassifierDefinition.SCTP_VALUE).build(),
+                new SupportedIntValueBuilder().setValue(IpProtoClassifierDefinition.TCP_VALUE).build(),
+                new SupportedIntValueBuilder().setValue(IpProtoClassifierDefinition.UDP_VALUE).build());
+        SupportedParameterValuesBuilder builder = new SupportedParameterValuesBuilder();
+        builder.setParameterName(new ParameterName(IpProtoClassifierDefinition.PROTO_PARAM));
+        builder.setParameterType(new IntBuilder().setSupportedIntValue(values).build());
+
+        return ImmutableList.of(builder.build());
+    }
+
+    @Override
+    protected void checkPresenceOfRequiredParams(Map<String, ParameterValue> params) {
+        if (params.get(IpProtoClassifierDefinition.PROTO_PARAM) == null) {
+            throw new IllegalArgumentException(
+                    "Parameter " + IpProtoClassifierDefinition.PROTO_PARAM + " not specified.");
+        }
+        if (params.get(IpProtoClassifierDefinition.PROTO_PARAM).getIntValue() == null) {
+            throw new IllegalArgumentException(
+                    "Value of " + IpProtoClassifierDefinition.PROTO_PARAM + " parameter is not present.");
+        }
+    }
+
+    /**
+     * Return the IpProtocol value. May return null.
+     *
+     * @param params the parameters of classifier-instance inserted by user
+     * @return the IpProtocol value
+     */
+    public static Long getIpProtoValue(Map<String, ParameterValue> params) {
+        if (params == null) {
+            return null;
+        }
+        if (params.get(IpProtoClassifierDefinition.PROTO_PARAM) == null) {
+            return null;
+        }
+        Long proto = params.get(IpProtoClassifierDefinition.PROTO_PARAM).getIntValue();
+        if (proto != null) {
+            return proto;
+        }
+        return null;
+    }
+}
index ab59e4b1e3cb975b494203e6d8c18292596c7149..41ce1748541af3e7154327768ac4b967887f9dce 100644 (file)
@@ -1,31 +1,32 @@
-/*\r
- * Copyright (c) 2016 Cisco Systems. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-\r
-package org.opendaylight.groupbasedpolicy.renderer.vpp.util;\r
-\r
-\r
-import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;\r
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;\r
-import org.opendaylight.yangtools.yang.binding.DataObject;\r
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
-\r
-public class General {\r
-\r
-    /**\r
-     * Operations that can be executed over ConfigCommand. Operation names reflect operations used in WriteTransaction.\r
-     * For more information on these operations, please see the documentation in:\r
-     * <br>\r
-     * {@link WriteTransaction#put(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}<br>\r
-     * {@link WriteTransaction#merge(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}<br>\r
-     * {@link WriteTransaction#delete(LogicalDatastoreType, InstanceIdentifier)}<br>\r
-     *\r
-     */\r
-    public enum Operations {\r
-        PUT, DELETE, MERGE\r
-    }\r
-}\r
+/*
+ * Copyright (c) 2016 Cisco Systems. 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.groupbasedpolicy.renderer.vpp.util;
+
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class General {
+
+    /**
+     * Operations that can be executed over ConfigCommand. Operation names reflect operations used
+     * in WriteTransaction.
+     * For more information on these operations, please see the documentation in:
+     * <br>
+     * {@link WriteTransaction#put(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+     * <br>
+     * {@link WriteTransaction#merge(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+     * <br>
+     * {@link WriteTransaction#delete(LogicalDatastoreType, InstanceIdentifier)}<br>
+     */
+    public enum Operations {
+        PUT, DELETE, MERGE
+    }
+}
index 19a672cd39edcf7683fba912c803d28c6ef2d548..2e523e832818a9c2dd3862a6a8aaec4adda07c3b 100644 (file)
@@ -8,9 +8,22 @@
 
 package org.opendaylight.groupbasedpolicy.renderer.vpp.util;
 
+import org.opendaylight.groupbasedpolicy.renderer.vpp.manager.VppNodeManager;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.Renderers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.Renderer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.RendererKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererNodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNodeKey;
+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.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
 public class VppIidFactory {
@@ -18,4 +31,30 @@ public class VppIidFactory {
     public static InstanceIdentifier<Interface> getInterfaceIID(InterfaceKey interfaceKey) {
         return InstanceIdentifier.create(Interfaces.class).child(Interface.class, interfaceKey);
     }
+
+    public static InstanceIdentifier<Renderer> getRendererIID(RendererKey rendererKey) {
+        return InstanceIdentifier.create(Renderers.class).child(Renderer.class, rendererKey);
+    }
+
+    public static InstanceIdentifier<RendererNodes> getRendererNodesIid() {
+        return InstanceIdentifier.builder(Renderers.class)
+            .child(Renderer.class, new RendererKey(VppNodeManager.vppRenderer))
+            .child(RendererNodes.class)
+            .build();
+    }
+
+    public static InstanceIdentifier<RendererNode> getRendererNodeIid(RendererNode rendererNode) {
+        return InstanceIdentifier.builder(Renderers.class)
+            .child(Renderer.class, new RendererKey(VppNodeManager.vppRenderer))
+            .child(RendererNodes.class)
+            .child(RendererNode.class, new RendererNodeKey(rendererNode.getNodePath()))
+            .build();
+    }
+
+    public static InstanceIdentifier<Node> getNodeIid(NodeKey key) {
+        TopologyKey topologyKey = new TopologyKey(new TopologyId("topology-netconf"));
+        return InstanceIdentifier.builder(NetworkTopology.class)
+                .child(Topology.class,topologyKey)
+                .child(Node.class, key).build();
+    }
 }
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/VppNodeWriter.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/VppNodeWriter.java
new file mode 100644 (file)
index 0000000..1e65549
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. 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.groupbasedpolicy.renderer.vpp.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererNodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererNodesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNode;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+public class VppNodeWriter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(VppNodeWriter.class);
+    private List<RendererNode> rendererNodesCache;
+
+    public VppNodeWriter() {
+        rendererNodesCache = new ArrayList<>();
+    }
+
+    public void cache(RendererNode node) {
+        rendererNodesCache.add(node);
+    }
+
+    /**
+     * Put all cached items to data store.
+     *
+     * @param dataBroker appropriate data provider
+     */
+    public void commitToDatastore(DataBroker dataBroker) {
+        RendererNodes rendererNodes = buildRendererNodes();
+        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        InstanceIdentifier<RendererNodes> iid = VppIidFactory.getRendererNodesIid();
+        try {
+            wtx.merge(LogicalDatastoreType.OPERATIONAL, iid, rendererNodes, true);
+            CheckedFuture<Void, TransactionCommitFailedException> submitFuture = wtx.submit();
+            submitFuture.checkedGet();
+            // Clear cache
+            rendererNodesCache.clear();
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("Write transaction failed to {}", e.getMessage());
+        } catch (Exception e) {
+            LOG.error("Failed to .. {}", e.getMessage());
+        }
+    }
+
+    /**
+     * Removes all cached items from data store.
+     *
+     * @param dataBroker appropriate data provider
+     */
+    public void removeFromDatastore(DataBroker dataBroker) {
+        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        for (RendererNode nodeToRemove : rendererNodesCache) {
+            InstanceIdentifier<RendererNode> iid = VppIidFactory.getRendererNodeIid(nodeToRemove);
+            try {
+                wtx.delete(LogicalDatastoreType.OPERATIONAL, iid);
+                CheckedFuture<Void, TransactionCommitFailedException> submitFuture = wtx.submit();
+                submitFuture.checkedGet();
+                // Clear cache
+            } catch (TransactionCommitFailedException e) {
+                LOG.error("Write transaction failed to {}", e.getMessage());
+            } catch (Exception e) {
+                LOG.error("Failed to .. {}", e.getMessage());
+            }
+        }
+        rendererNodesCache.clear();
+    }
+
+    private RendererNodes buildRendererNodes() {
+        RendererNodesBuilder rendererNodesBuilder = new RendererNodesBuilder();
+        rendererNodesBuilder.setRendererNode(new ArrayList<>(rendererNodesCache));
+        return rendererNodesBuilder.build();
+    }
+}
index 7169ca14a95c6b5c06d082f6c4c183d30589b36b..6401c4b987ad74c55f72589dc591123798e1738d 100644 (file)
@@ -46,6 +46,15 @@ module vpp-provider-impl {
                     }
                 }
             }
+            // binding aware broker
+            container broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity mdsal:binding-broker-osgi-registry;
+                    }
+                }
+            }
         }
     }
 }
index 8cd7458a0562c2908aec68c5df2209a3560413cb..551f19b83734d49be6c4df4eca0767a44b169841 100644 (file)
@@ -11,7 +11,12 @@ package org.opendaylight.groupbasedpolicy.renderer.vpp;
 import com.google.common.collect.ImmutableList;\r
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;\r
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.Renderer;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUser;\r
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;\r
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;\r
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;\r
 \r
 import javax.annotation.Nonnull;\r
 import java.util.Collection;\r
@@ -21,6 +26,7 @@ public class VppRendererDataBrokerTest extends CustomDataBrokerTest {
     @Nonnull\r
     @Override\r
     public Collection<Class<?>> getClassesFromModules() {\r
-        return ImmutableList.of(Interfaces.class, Interface.class, VhostUser.class);\r
+        return ImmutableList.of(Interfaces.class, Interface.class, VhostUser.class, NetworkTopology.class,\r
+                Topology.class, Node.class, NetconfNode.class, Renderer.class);\r
     }\r
 }\r
diff --git a/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/manager/VppManagerDataStoreTest.java b/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/manager/VppManagerDataStoreTest.java
new file mode 100644 (file)
index 0000000..537adc7
--- /dev/null
@@ -0,0 +1,159 @@
+
+package org.opendaylight.groupbasedpolicy.renderer.vpp.manager;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.controller.md.sal.binding.api.*;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.VppRendererDataBrokerTest;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.listener.VppNodeListener;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.Renderer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.RendererKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.AvailableCapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.UnavailableCapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test for {@link VppNodeManager} and {@link VppNodeListener}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class VppManagerDataStoreTest extends VppRendererDataBrokerTest {
+
+    private static final String V3PO_CAPABILITY = "(urn:opendaylight:params:xml:ns:yang:v3po?revision=2015-01-05)v3po";
+    private static final String INTERFACES_CAPABILITY =
+            "(urn:ietf:params:xml:ns:yang:ietf-interfaces?revision=2014-05-08)ietf-interfaces";
+    private static final String NODE_NAME = "testVpp";
+
+    private final InstanceIdentifier<Node> nodeIid = VppIidFactory.getNodeIid(new NodeKey(new NodeId(NODE_NAME)));
+
+    @Mock
+    BindingAwareBroker.ProviderContext providerContext;
+    @Mock
+    MountPointService mountPointService;
+    @Mock
+    MountPoint mountPoint;
+    @Mock
+    DataBroker dataBroker2;
+
+    private DataBroker dataBroker;
+    private VppNodeListener vppNodeListener;
+    private VppNodeManager vppNodeManager;
+
+    @Before
+    public void setUp() throws Exception {
+        Mockito.when(providerContext.getSALService(Matchers.<Class<MountPointService>>any()))
+            .thenReturn(mountPointService);
+        Mockito.when(mountPointService.getMountPoint(Matchers.<InstanceIdentifier<Node>>any()))
+            .thenReturn(Optional.of(mountPoint));
+        Mockito.when(mountPoint.getService(Matchers.<Class<DataBroker>>any())).thenReturn(Optional.of(dataBroker2));
+        dataBroker = getDataBroker();
+        vppNodeManager = new VppNodeManager(dataBroker, providerContext);
+        vppNodeListener = new VppNodeListener(dataBroker, vppNodeManager);
+    }
+
+    private Node createNode(final String name, NetconfNodeConnectionStatus.ConnectionStatus status) {
+        Host host = new Host(new IpAddress(new Ipv4Address("192.168.255.101")));
+        PortNumber portNumber = new PortNumber(2830);
+
+        List<String> avaibleCapabilitiesList = new ArrayList<>();
+        avaibleCapabilitiesList.add(V3PO_CAPABILITY);
+        avaibleCapabilitiesList.add(INTERFACES_CAPABILITY);
+
+        NetconfNode netconfNode = new NetconfNodeBuilder().setHost(host)
+            .setPort(portNumber)
+            .setUnavailableCapabilities(new UnavailableCapabilitiesBuilder().build())
+            .setAvailableCapabilities(
+                    new AvailableCapabilitiesBuilder().setAvailableCapability(avaibleCapabilitiesList).build())
+            .setConnectionStatus(status)
+            .build();
+
+        return new NodeBuilder().setNodeId(new NodeId(name)).addAugmentation(NetconfNode.class, netconfNode).build();
+    }
+
+    @Test
+    public void connectNode() throws ReadFailedException {
+        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        Node testVppNode = createNode(NODE_NAME, NetconfNodeConnectionStatus.ConnectionStatus.Connected);
+
+        writeTransaction.put(LogicalDatastoreType.OPERATIONAL, nodeIid, testVppNode, true);
+
+        writeTransaction.submit();
+
+        ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction();
+        CheckedFuture<Optional<Renderer>, ReadFailedException> future =
+                readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL,
+                        VppIidFactory.getRendererIID(new RendererKey(VppNodeManager.vppRenderer)));
+        Optional<Renderer> rendererOptional = future.checkedGet();
+
+        Assert.assertTrue(rendererOptional.isPresent());
+        Assert.assertEquals(1, rendererOptional.get().getRendererNodes().getRendererNode().size());
+        Assert.assertEquals(nodeIid, rendererOptional.get().getRendererNodes().getRendererNode().get(0).getNodePath());
+    }
+
+    @Test
+    public void disconnectNode() throws ReadFailedException, InterruptedException {
+        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        Node testVppNode = createNode(NODE_NAME, NetconfNodeConnectionStatus.ConnectionStatus.Connected);
+
+        writeTransaction.put(LogicalDatastoreType.OPERATIONAL, nodeIid, testVppNode, true);
+
+        writeTransaction.submit();
+
+        ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction();
+        CheckedFuture<Optional<Renderer>, ReadFailedException> future =
+                readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL,
+                        VppIidFactory.getRendererIID(new RendererKey(VppNodeManager.vppRenderer)));
+        Optional<Renderer> rendererOptional = future.checkedGet();
+
+        Assert.assertTrue(rendererOptional.isPresent());
+        Assert.assertEquals(1, rendererOptional.get().getRendererNodes().getRendererNode().size());
+        Assert.assertEquals(nodeIid, rendererOptional.get().getRendererNodes().getRendererNode().get(0).getNodePath());
+
+        WriteTransaction writeTransaction2 = dataBroker.newWriteOnlyTransaction();
+        Node testVppNode2 = createNode(NODE_NAME, NetconfNodeConnectionStatus.ConnectionStatus.Connecting);
+
+        writeTransaction2.put(LogicalDatastoreType.OPERATIONAL, nodeIid, testVppNode2, true);
+
+        writeTransaction2.submit();
+
+        ReadOnlyTransaction readOnlyTransaction2 = dataBroker.newReadOnlyTransaction();
+        CheckedFuture<Optional<Renderer>, ReadFailedException> future2 =
+                readOnlyTransaction2.read(LogicalDatastoreType.OPERATIONAL,
+                        VppIidFactory.getRendererIID(new RendererKey(VppNodeManager.vppRenderer)));
+        Optional<Renderer> rendererOptional2 = future2.checkedGet();
+
+        Assert.assertTrue(rendererOptional2.isPresent());
+        Assert.assertEquals(0, rendererOptional2.get().getRendererNodes().getRendererNode().size());
+    }
+
+    @After
+    public void cleanUp() throws Exception {
+        vppNodeListener.close();
+    }
+}
diff --git a/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/VppNodeWriterTest.java b/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/VppNodeWriterTest.java
new file mode 100644 (file)
index 0000000..911d2c5
--- /dev/null
@@ -0,0 +1,94 @@
+package org.opendaylight.groupbasedpolicy.renderer.vpp.util;
+
+import com.google.common.util.concurrent.Futures;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.Renderer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererNodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNodeBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Test for {@link VppNodeWriter}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class VppNodeWriterTest {
+
+    private static final String RENDERER_NAME = "vpp-renderer";
+
+    @Mock
+    private DataBroker dataBroker;
+    @Mock
+    private WriteTransaction wTx;
+    @Captor
+    private ArgumentCaptor<InstanceIdentifier<RendererNodes>> rendererNodesPathCpt;
+    @Captor
+    private ArgumentCaptor<RendererNodes> rendererNodesCpt;
+
+    private InOrder inOrder;
+
+    private VppNodeWriter nodeWriter;
+
+    @Before
+    public void setUp() throws Exception {
+        nodeWriter = new VppNodeWriter();
+        Mockito.when(dataBroker.newWriteOnlyTransaction()).thenReturn(wTx);
+        Mockito.when(wTx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testCommitToDatastore_with_node() throws Exception {
+        final RendererNode node = new RendererNodeBuilder().build();
+        nodeWriter.cache(node);
+        nodeWriter.commitToDatastore(dataBroker);
+
+        commonChecks();
+
+        final RendererNodes rendererNodes = rendererNodesCpt.getValue();
+        Assert.assertEquals(1, rendererNodes.getRendererNode().size());
+    }
+
+    @Test
+    public void testCommitToDatastore_empty() throws Exception {
+        nodeWriter.commitToDatastore(dataBroker);
+
+        commonChecks();
+
+        final RendererNodes rendererNodes = rendererNodesCpt.getValue();
+        Assert.assertEquals(0, rendererNodes.getRendererNode().size());
+    }
+
+    private void commonChecks() {
+        inOrder = Mockito.inOrder(dataBroker, wTx);
+        inOrder.verify(dataBroker).newWriteOnlyTransaction();
+        inOrder.verify(wTx).merge(Matchers.eq(LogicalDatastoreType.OPERATIONAL), rendererNodesPathCpt.capture(),
+                rendererNodesCpt.capture(), Matchers.eq(true));
+        inOrder.verify(wTx).submit();
+
+        final InstanceIdentifier<RendererNodes> rendererNodesPath = rendererNodesPathCpt.getValue();
+        Assert.assertEquals(RENDERER_NAME, extractRendererName(rendererNodesPath));
+    }
+
+    private String extractRendererName(final InstanceIdentifier<RendererNodes> rendererNodesPath) {
+        return rendererNodesPath.firstKeyOf(Renderer.class).getName().getValue();
+    }
+}