Introduced InterfaceManager in VPP renderer 77/39677/10
authorMichal Cmarada <mcmarada@cisco.com>
Thu, 2 Jun 2016 16:41:54 +0000 (18:41 +0200)
committerMartin Sunal <msunal@cisco.com>
Thu, 2 Jun 2016 21:32:14 +0000 (23:32 +0200)
- creates vhostuser interfaces on VPP (vhostuser interface
  is not assigned to any bridge domain)
- trigger is creating of vpp-endpoint from on vpp-renderer.yang
- does not handle case when a netconf device is connected
  after vpp-endpoint was created

Change-Id: I11ea9e0446d9297129b9a928a8417b95944739e1
Signed-off-by: Martin Sunal <msunal@cisco.com>
Signed-off-by: Michal Cmarada <mcmarada@cisco.com>
21 files changed:
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/util/IidFactory.java
renderers/vpp/pom.xml
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/commands/VhostUserCommand.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/DtoChangeEvent.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/NodeOperEvent.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/VppEndpointConfEvent.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/InterfaceManager.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/VppEndpointLocationProvider.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/RendererPolicyListener.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppEndpointListener.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppNodeListener.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/MountedDataBrokerProvider.java [new file with mode: 0644]
renderers/vpp/src/main/yang/vbd/v3po@2015-01-05.yang
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/commands/VhostUserCommandTest.java
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/DtoChangeEventTest.java [new file with mode: 0644]
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/NodeOperEventTest.java [new file with mode: 0644]
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/VppEndpointConfEventTest.java [new file with mode: 0644]
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/InterfaceManagerTest.java [new file with mode: 0644]
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppEndpointListenerTest.java [new file with mode: 0644]
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/manager/VppManagerDataStoreTest.java

index f04cf411878b634ad68ad2be511f28ed1b5bcba1..b9a85bd55394bac5cfc88f24e9f19f145f9d4ecf 100644 (file)
@@ -115,7 +115,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics.store.rev151215.statistics.store.StatisticRecord;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics.store.rev151215.statistics.store.StatisticRecordKey;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
 
 public class IidFactory {
 
@@ -465,12 +464,22 @@ public class IidFactory {
                 .build();
     }
 
-    public static InstanceIdentifier<ProviderAddressEndpointLocation> providerAddressEndpointLocationIid(String provider,
-            Class<? extends AddressType> addrType, String addr, Class<? extends ContextType> cType,
+    public static InstanceIdentifier<ProviderAddressEndpointLocation> providerAddressEndpointLocationIid(
+            String provider, Class<? extends AddressType> addrType, String addr, Class<? extends ContextType> cType,
             ContextId containment) {
+        return providerAddressEndpointLocationIid(new ProviderName(provider),
+                new ProviderAddressEndpointLocationKey(addr, addrType, containment, cType));
+    }
+
+    public static InstanceIdentifier<ProviderAddressEndpointLocation> providerAddressEndpointLocationIid(
+            ProviderName provider, ProviderAddressEndpointLocationKey providerAddressEndpointLocationKey) {
+        return locationProviderIid(provider).child(ProviderAddressEndpointLocation.class,
+                providerAddressEndpointLocationKey);
+    }
+
+    public static InstanceIdentifier<LocationProvider> locationProviderIid(ProviderName provider) {
         return InstanceIdentifier.builder(LocationProviders.class)
-                .child(LocationProvider.class, new LocationProviderKey(new ProviderName(provider)))
-            .child(ProviderAddressEndpointLocation.class, new ProviderAddressEndpointLocationKey(addr, addrType, containment, cType))
+            .child(LocationProvider.class, new LocationProviderKey(provider))
             .build();
     }
 
index 6ff00a804f398bceeea2fb8031977010de801073..c13abe18ebcbb3781221bbfb46336140efb3abf7 100644 (file)
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.groupbasedpolicy</groupId>
+      <artifactId>groupbasedpolicy</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
index d0e9ac411cea5afa39662070704c6a3d15cc51c2..668fc62b99d2a220aa5535619089496bfcebc17d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * 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,
@@ -11,15 +11,19 @@ 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.MountPointService;
 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.iface.InterfaceManager;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.listener.VppEndpointListener;
 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.MountedDataBrokerProvider;
 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;
@@ -35,6 +39,7 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.eventbus.EventBus;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
@@ -52,9 +57,13 @@ public class VppRenderer implements AutoCloseable, BindingAwareProvider {
             .setSupportedParameterValues(new EtherTypeClassifier(null).getSupportedParameterValues())
             .build());
 
-    private DataBroker dataBroker;
+    private final DataBroker dataBroker;
+
     private VppNodeManager vppNodeManager;
+    private InterfaceManager interfaceManager;
+
     private VppNodeListener vppNodeListener;
+    private VppEndpointListener vppEndpointListener;
 
     public VppRenderer(DataBroker dataBroker, BindingAwareBroker bindingAwareBroker) {
         this.dataBroker = Preconditions.checkNotNull(dataBroker);
@@ -67,6 +76,12 @@ public class VppRenderer implements AutoCloseable, BindingAwareProvider {
         if (vppNodeListener != null) {
             vppNodeListener.close();
         }
+        if (vppEndpointListener != null) {
+            vppEndpointListener.close();
+        }
+        if (interfaceManager != null) {
+            interfaceManager.close();
+        }
         unregisterFromRendererManager();
     }
 
@@ -74,9 +89,16 @@ public class VppRenderer implements AutoCloseable, BindingAwareProvider {
     public void onSessionInitiated(BindingAwareBroker.ProviderContext providerContext) {
         LOG.info("starting vpp renderer");
 
-        // vpp-node-manager
+        MountPointService mountService = Preconditions.checkNotNull(providerContext.getSALService(MountPointService.class));
+        MountedDataBrokerProvider mountDataProvider = new MountedDataBrokerProvider(mountService);
         vppNodeManager = new VppNodeManager(dataBroker, providerContext);
-        vppNodeListener = new VppNodeListener(dataBroker, vppNodeManager);
+
+        EventBus dtoEventBus = new EventBus("DTO events");
+        interfaceManager = new InterfaceManager(mountDataProvider, dataBroker);
+        dtoEventBus.register(interfaceManager);
+
+        vppNodeListener = new VppNodeListener(dataBroker, vppNodeManager, dtoEventBus);
+        vppEndpointListener = new VppEndpointListener(dataBroker, dtoEventBus);
 
         registerToRendererManager();
     }
index a47d04f1b02b4cb12fbc7238b6d53039b88f2157..1321603364149d44125b624ab86e410b070626bb 100644 (file)
@@ -8,10 +8,10 @@
 \r
 package org.opendaylight.groupbasedpolicy.renderer.vpp.commands;\r
 \r
-import com.google.common.base.Preconditions;\r
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;\r
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;\r
 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General;\r
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General.Operations;\r
 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;\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.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;\r
@@ -21,11 +21,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentationBuilder;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2Builder;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUserBuilder;\r
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.interconnection.BridgeBasedBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.attributes.interconnection.BridgeBasedBuilder;\r
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
-public class VhostUserCommand extends AbstractInterfaceCommand {\r
+import com.google.common.base.Preconditions;\r
+import com.google.common.base.Strings;\r
+\r
+public class VhostUserCommand extends AbstractInterfaceCommand<VhostUserCommand> {\r
 \r
     private static final Logger LOG = LoggerFactory.getLogger(VhostUserCommand.class);\r
     private String socket;\r
@@ -64,15 +67,15 @@ public class VhostUserCommand extends AbstractInterfaceCommand {
         switch (getOperation()) {\r
 \r
             case PUT:\r
-                LOG.info("Executing Add operation for command: {}", this);\r
+                LOG.debug("Executing Add operation for command: {}", this);\r
                 put(readWriteTransaction);\r
                 break;\r
             case DELETE:\r
-                LOG.info("Executing Delete operation for command: {}", this);\r
+                LOG.debug("Executing Delete operation for command: {}", this);\r
                 delete(readWriteTransaction);\r
                 break;\r
             case MERGE:\r
-                LOG.info("Executing Update operation for command: {}", this);\r
+                LOG.debug("Executing Update operation for command: {}", this);\r
                 merge(readWriteTransaction);\r
                 break;\r
             default:\r
@@ -82,8 +85,6 @@ public class VhostUserCommand extends AbstractInterfaceCommand {
     }\r
 \r
     private void put(ReadWriteTransaction readWriteTransaction) {\r
-        Preconditions.checkNotNull(name, "Interface name should not be null");\r
-\r
         InterfaceBuilder interfaceBuilder = getVhostUserInterfaceBuilder();\r
 \r
         readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION,\r
@@ -91,8 +92,6 @@ public class VhostUserCommand extends AbstractInterfaceCommand {
     }\r
 \r
     private void merge(ReadWriteTransaction readWriteTransaction) {\r
-        Preconditions.checkNotNull(name, "Interface name should not be null");\r
-\r
         InterfaceBuilder interfaceBuilder = getVhostUserInterfaceBuilder();\r
 \r
             readWriteTransaction.merge(LogicalDatastoreType.CONFIGURATION,\r
@@ -110,14 +109,14 @@ public class VhostUserCommand extends AbstractInterfaceCommand {
                         .setLinkUpDownTrapEnable(Interface.LinkUpDownTrapEnable.Enabled);\r
 \r
         // Create the vhost augmentation\r
-        VppInterfaceAugmentation vppAugmentation =\r
-                new VppInterfaceAugmentationBuilder()\r
-                        .setVhostUser(new VhostUserBuilder().setRole(role).setSocket(socket).build())\r
-                        .setL2(new L2Builder()\r
-                                .setInterconnection(new BridgeBasedBuilder().setBridgeDomain(bridgeDomain).build()).build())\r
-                        .build();\r
-\r
-        interfaceBuilder.addAugmentation(VppInterfaceAugmentation.class, vppAugmentation);\r
+        VppInterfaceAugmentationBuilder vppAugmentationBuilder = new VppInterfaceAugmentationBuilder()\r
+            .setVhostUser(new VhostUserBuilder().setRole(role).setSocket(socket).build());\r
+        if (!Strings.isNullOrEmpty(bridgeDomain)) {\r
+            vppAugmentationBuilder.setL2(new L2Builder()\r
+                .setInterconnection(new BridgeBasedBuilder().setBridgeDomain(bridgeDomain).build()).build());\r
+        }\r
+\r
+        interfaceBuilder.addAugmentation(VppInterfaceAugmentation.class, vppAugmentationBuilder.build());\r
         return interfaceBuilder;\r
     }\r
 \r
@@ -131,6 +130,15 @@ public class VhostUserCommand extends AbstractInterfaceCommand {
 \r
     }\r
 \r
+    @Override\r
+    public String toString() {\r
+        return "VhostUserCommand [socket=" + socket + ", role=" + role + ", bridgeDomain=" + bridgeDomain\r
+                + ", operation=" + operation + ", name=" + name + ", description=" + description + ", enabled="\r
+                + enabled + "]";\r
+    }\r
+\r
+\r
+\r
     public static class VhostUserCommandBuilder {\r
 \r
         private String name;\r
@@ -213,7 +221,9 @@ public class VhostUserCommand extends AbstractInterfaceCommand {
         public VhostUserCommand build() {\r
             Preconditions.checkArgument(this.name != null);\r
             Preconditions.checkArgument(this.operation != null);\r
-            Preconditions.checkArgument(this.socket != null);\r
+            if (operation == Operations.PUT) {\r
+                Preconditions.checkArgument(this.socket != null);\r
+            }\r
 \r
             return new VhostUserCommand(this);\r
         }\r
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/DtoChangeEvent.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/DtoChangeEvent.java
new file mode 100644 (file)
index 0000000..182cc4c
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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.event;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+public abstract class DtoChangeEvent<T extends DataObject> {
+
+    protected final InstanceIdentifier<T> iid;
+    protected final Optional<T> before;
+    protected final Optional<T> after;
+
+    public static enum DtoModificationType {
+        CREATED, UPDATED, DELETED;
+    }
+
+    public DtoChangeEvent(@Nonnull InstanceIdentifier<T> iid, @Nullable T before, @Nullable T after) {
+        this.iid = Preconditions.checkNotNull(iid);
+        Preconditions.checkArgument(!(before == null && after == null),
+                "before and after cannot be null at the same time");
+        this.before = Optional.fromNullable(before);
+        this.after = Optional.fromNullable(after);
+    }
+
+    public @Nonnull InstanceIdentifier<T> getIid() {
+        return iid;
+    }
+
+    public Optional<T> getBefore() {
+        return before;
+    }
+
+    public Optional<T> getAfter() {
+        return after;
+    }
+
+    /**
+     * Returns:<br>
+     * {@link DtoModificationType#CREATED} - when {@link #isDtoCreated()} is {@code true}<br>
+     * {@link DtoModificationType#UPDATED} - when {@link #isDtoUpdated()} is {@code true}<br>
+     * {@link DtoModificationType#DELETED} - when {@link #isDtoDeleted()} is {@code true}
+     * 
+     * @return DtoModificationType
+     */
+    public @Nonnull DtoModificationType getDtoModificationType() {
+        if (isDtoCreated()) {
+            return DtoModificationType.CREATED;
+        }
+        if (isDtoUpdated()) {
+            return DtoModificationType.UPDATED;
+        }
+        if (isDtoDeleted()) {
+            return DtoModificationType.DELETED;
+        }
+        throw new IllegalStateException("Unknown DTO modification type.");
+    }
+
+    /**
+     * Checks if {@link #getBefore()} is NOT present and if {@link #getAfter()} is present
+     * 
+     * @return {@code true} if DTO is created; {@code false} otherwise
+     */
+    public boolean isDtoCreated() {
+        return !before.isPresent() && after.isPresent();
+    }
+
+    /**
+     * Checks if {@link #getBefore()} is present and if {@link #getAfter()} is present
+     * 
+     * @return {@code true} if DTO is updated; {@code false} otherwise
+     */
+    public boolean isDtoUpdated() {
+        return before.isPresent() && after.isPresent();
+    }
+
+    /**
+     * Checks if {@link #getBefore()} is present and if {@link #getAfter()} is NOT present
+     * 
+     * @return {@code true} if DTO is deleted; {@code false} otherwise
+     */
+    public boolean isDtoDeleted() {
+        return before.isPresent() && !after.isPresent();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((after == null) ? 0 : after.hashCode());
+        result = prime * result + ((before == null) ? 0 : before.hashCode());
+        result = prime * result + ((iid == null) ? 0 : iid.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof DtoChangeEvent))
+            return false;
+        DtoChangeEvent other = (DtoChangeEvent) obj;
+        if (after == null) {
+            if (other.after != null)
+                return false;
+        } else if (!after.equals(other.after))
+            return false;
+        if (before == null) {
+            if (other.before != null)
+                return false;
+        } else if (!before.equals(other.before))
+            return false;
+        if (iid == null) {
+            if (other.iid != null)
+                return false;
+        } else if (!iid.equals(other.iid))
+            return false;
+        return true;
+    }
+
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/NodeOperEvent.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/NodeOperEvent.java
new file mode 100644 (file)
index 0000000..2ad4154
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.event;
+
+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.ConnectionStatus;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+public class NodeOperEvent extends DtoChangeEvent<Node> {
+
+    public NodeOperEvent(InstanceIdentifier<Node> iid, Node before, Node after) {
+        super(iid, before, after);
+        if (before != null) {
+            Preconditions.checkArgument(before.getAugmentation(NetconfNode.class) != null);
+        }
+        if (after != null) {
+            Preconditions.checkArgument(after.getAugmentation(NetconfNode.class) != null);
+        }
+    }
+
+    public boolean isAfterConnected() {
+        return isConnected(after);
+    }
+
+    public boolean isBeforeConnected() {
+        return isConnected(before);
+    }
+
+    private static boolean isConnected(Optional<Node> potentialNode) {
+        if (!potentialNode.isPresent()) {
+            return false;
+        }
+        NetconfNode netconfNode = potentialNode.get().getAugmentation(NetconfNode.class);
+        if (ConnectionStatus.Connected == netconfNode.getConnectionStatus()) {
+            return true;
+        }
+        return false;
+    }
+
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/VppEndpointConfEvent.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/VppEndpointConfEvent.java
new file mode 100644 (file)
index 0000000..8034a6a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.event;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class VppEndpointConfEvent extends DtoChangeEvent<VppEndpoint> {
+
+    public VppEndpointConfEvent(InstanceIdentifier<VppEndpoint> iid, VppEndpoint before, VppEndpoint after) {
+        super(iid, before, after);
+    }
+
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/InterfaceManager.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/InterfaceManager.java
new file mode 100644 (file)
index 0000000..c4940e2
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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.iface;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.ConfigCommand;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand.VhostUserCommandBuilder;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.event.NodeOperEvent;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.event.VppEndpointConfEvent;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General.Operations;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.MountedDataBrokerProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint.InterfaceTypeChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUserRole;
+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;
+import com.google.common.base.Strings;
+import com.google.common.eventbus.Subscribe;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+
+public class InterfaceManager implements AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(InterfaceManager.class);
+    private final MountedDataBrokerProvider mountDataProvider;
+    private final VppEndpointLocationProvider vppEndpointLocationProvider;
+
+    public InterfaceManager(@Nonnull MountedDataBrokerProvider mountDataProvider, @Nonnull DataBroker dataProvider) {
+        this.mountDataProvider = Preconditions.checkNotNull(mountDataProvider);
+        this.vppEndpointLocationProvider = new VppEndpointLocationProvider(dataProvider);
+    }
+
+    @Subscribe
+    public void vppEndpointChanged(VppEndpointConfEvent event) {
+        switch (event.getDtoModificationType()) {
+            case CREATED:
+                vppEndpointCreated(event.getAfter().get());
+                break;
+            case UPDATED:
+                vppEndpointDeleted(event.getBefore().get());
+                vppEndpointCreated(event.getAfter().get());
+                break;
+            case DELETED:
+                vppEndpointDeleted(event.getBefore().get());
+                break;
+        }
+    }
+
+    private void vppEndpointCreated(VppEndpoint vppEndpoint) {
+        Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.PUT);
+        if (!potentialIfaceCommand.isPresent()) {
+            return;
+        }
+        ConfigCommand ifaceWithoutBdCommand = potentialIfaceCommand.get();
+        InstanceIdentifier<?> vppNodeIid = vppEndpoint.getVppNodePath();
+        Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
+        if (!potentialVppDataProvider.isPresent()) {
+            LOG.debug("Cannot get data broker for mount point {}", vppNodeIid);
+            return;
+        }
+        DataBroker vppDataBroker = potentialVppDataProvider.get();
+        createIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
+    }
+
+    private void createIfaceOnVpp(ConfigCommand createIfaceWithoutBdCommand, DataBroker vppDataBroker,
+            VppEndpoint vppEndpoint, InstanceIdentifier<?> vppNodeIid) {
+        ReadWriteTransaction rwTx = vppDataBroker.newReadWriteTransaction();
+        createIfaceWithoutBdCommand.execute(rwTx);
+        Futures.addCallback(rwTx.submit(), new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(Void result) {
+                LOG.debug("Create interface on VPP command was successful:\nVPP: {}\nCommand: {}", vppNodeIid,
+                        createIfaceWithoutBdCommand);
+                vppEndpointLocationProvider.createLocationForVppEndpoint(vppEndpoint);
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.error("Create interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
+                        createIfaceWithoutBdCommand, t);
+            }
+        });
+    }
+
+    private void vppEndpointDeleted(VppEndpoint vppEndpoint) {
+        Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.DELETE);
+        if (!potentialIfaceCommand.isPresent()) {
+            return;
+        }
+        ConfigCommand ifaceWithoutBdCommand = potentialIfaceCommand.get();
+        InstanceIdentifier<?> vppNodeIid = vppEndpoint.getVppNodePath();
+        Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
+        if (!potentialVppDataProvider.isPresent()) {
+            LOG.debug("Cannot get data broker for mount point {}", vppNodeIid);
+            return;
+        }
+        DataBroker vppDataBroker = potentialVppDataProvider.get();
+        deleteIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
+    }
+
+    private void deleteIfaceOnVpp(ConfigCommand deleteIfaceWithoutBdCommand, DataBroker vppDataBroker,
+            VppEndpoint vppEndpoint, InstanceIdentifier<?> vppNodeIid) {
+        ReadWriteTransaction rwTx = vppDataBroker.newReadWriteTransaction();
+        deleteIfaceWithoutBdCommand.execute(rwTx);
+        Futures.addCallback(rwTx.submit(), new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(Void result) {
+                LOG.debug("Delete interface on VPP command was successful:\nVPP: {}\nCommand: {}", vppNodeIid,
+                        deleteIfaceWithoutBdCommand);
+                vppEndpointLocationProvider.deleteLocationForVppEndpoint(vppEndpoint);
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.error("Delete interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
+                        deleteIfaceWithoutBdCommand, t);
+            }
+        });
+    }
+
+    @Subscribe
+    public void vppNodeChanged(NodeOperEvent event) {
+        switch (event.getDtoModificationType()) {
+            case CREATED:
+                if (event.isAfterConnected()) {
+                    // TODO read VppEndpoints or cache them during vppEndpointChanged()
+                }
+                break;
+            case UPDATED:
+                if (!event.isBeforeConnected() && event.isAfterConnected()) {
+                    // TODO reconciliation - diff between disconnected snapshot and current snapshot
+                }
+                break;
+            case DELETED:
+                if (event.isBeforeConnected()) {
+                    // TODO we could do snapshot of VppEndpoints 
+                    // which can be used for reconciliation
+                }
+                break;
+        }
+    }
+
+    private static Optional<ConfigCommand> createInterfaceWithoutBdCommand(@Nonnull VppEndpoint vppEp,
+            @Nonnull Operations operations) {
+        if (!hasNodeAndInterface(vppEp)) {
+            LOG.debug("Interface command is not created for {}", vppEp);
+            return Optional.absent();
+        }
+        VhostUserCommandBuilder builder = VhostUserCommand.builder();
+        builder.setName(vppEp.getVppInterfaceName());
+        InterfaceTypeChoice interfaceTypeChoice = vppEp.getInterfaceTypeChoice();
+        if (interfaceTypeChoice instanceof VhostUserCase) {
+            VhostUserCase vhostUserIface = (VhostUserCase) interfaceTypeChoice;
+            String socket = vhostUserIface.getSocket();
+            if (Strings.isNullOrEmpty(socket)) {
+                LOG.debug("Vhost user interface command is not created because socket is missing. {}", vppEp);
+                return Optional.absent();
+            }
+            builder.setSocket(socket);
+            builder.setRole(VhostUserRole.Client);
+        }
+        VhostUserCommand vhostUserCommand =
+                builder.setOperation(operations).setDescription(vppEp.getDescription()).build();
+        return Optional.of(vhostUserCommand);
+    }
+
+    private static boolean hasNodeAndInterface(VppEndpoint vppEp) {
+        if (vppEp.getVppNodePath() == null) {
+            LOG.trace("vpp-node is missing. {}", vppEp);
+            return false;
+        }
+        if (Strings.isNullOrEmpty(vppEp.getVppInterfaceName())) {
+            LOG.trace("vpp-interface-name is missing. {}", vppEp);
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void close() throws Exception {
+        vppEndpointLocationProvider.close();
+    }
+
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/VppEndpointLocationProvider.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/VppEndpointLocationProvider.java
new file mode 100644 (file)
index 0000000..f1e8c1e
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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.iface;
+
+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.groupbasedpolicy.util.IidFactory;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.ProviderName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProviderBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocationKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+
+public class VppEndpointLocationProvider implements AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(VppEndpointLocationProvider.class);
+    public static final ProviderName VPP_ENDPOINT_LOCATION_PROVIDER =
+            new ProviderName("VPP endpoint location provider");
+    public static final long PROVIDER_PRIORITY = 10L;
+    private static final String INTERFACE_PATH = "/ietf-interfaces:interfaces/ietf-interfaces:interface";
+    private final DataBroker dataProvider;
+
+    public VppEndpointLocationProvider(DataBroker dataProvider) {
+        this.dataProvider = Preconditions.checkNotNull(dataProvider);
+        LocationProvider locationProvider = new LocationProviderBuilder().setProvider(VPP_ENDPOINT_LOCATION_PROVIDER)
+            .setPriority(PROVIDER_PRIORITY)
+            .build();
+        WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+        wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER),
+                locationProvider, true);
+
+        Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(Void result) {
+                LOG.trace("{} was created", VPP_ENDPOINT_LOCATION_PROVIDER.getValue());
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.error("{} was NOT created", VPP_ENDPOINT_LOCATION_PROVIDER.getValue());
+            }
+        });
+    }
+
+    public void createLocationForVppEndpoint(VppEndpoint vppEndpoint) {
+        ProviderAddressEndpointLocation providerAddressEndpointLocation =
+                createProviderAddressEndpointLocation(vppEndpoint);
+        WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+        wTx.put(LogicalDatastoreType.CONFIGURATION,
+                IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
+                        providerAddressEndpointLocation.getKey()),
+                providerAddressEndpointLocation);
+
+        Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(Void result) {
+                LOG.trace("{} provides location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(),
+                        providerAddressEndpointLocation);
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.error("{} failed to provide location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(),
+                        providerAddressEndpointLocation, t);
+            }
+        });
+    }
+
+    public static ProviderAddressEndpointLocation createProviderAddressEndpointLocation(VppEndpoint vppEndpoint) {
+        String restIfacePath = INTERFACE_PATH + createRestInterfaceKey(vppEndpoint.getVppInterfaceName());
+        AbsoluteLocation absoluteLocation = new AbsoluteLocationBuilder()
+            .setLocationType(new ExternalLocationCaseBuilder().setExternalNodeMountPoint(vppEndpoint.getVppNodePath())
+                .setExternalNodeConnector(restIfacePath)
+                .build())
+            .build();
+        return new ProviderAddressEndpointLocationBuilder()
+            .setKey(createProviderAddressEndpointLocationKey(vppEndpoint))
+            .setAbsoluteLocation(absoluteLocation)
+            .build();
+    }
+
+    public void deleteLocationForVppEndpoint(VppEndpoint vppEndpoint) {
+        ProviderAddressEndpointLocationKey provAddrEpLocKey = createProviderAddressEndpointLocationKey(vppEndpoint);
+        WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+        wTx.delete(LogicalDatastoreType.CONFIGURATION,
+                IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER, provAddrEpLocKey));
+        Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(Void result) {
+                LOG.trace("{} removes location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(), provAddrEpLocKey);
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.error("{} failed to remove location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(),
+                        provAddrEpLocKey, t);
+            }
+        });
+    }
+
+    private static ProviderAddressEndpointLocationKey createProviderAddressEndpointLocationKey(VppEndpoint vppEndpoint) {
+        return new ProviderAddressEndpointLocationKey(vppEndpoint.getAddress(), vppEndpoint.getAddressType(),
+                vppEndpoint.getContextId(), vppEndpoint.getContextType());
+    }
+
+    private static String createRestInterfaceKey(String ifaceName) {
+        return "[ietf-interfaces:name='" + ifaceName + "']";
+    }
+
+    @Override
+    public void close() throws Exception {
+        WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+        wTx.delete(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER));
+        wTx.submit();
+    }
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/RendererPolicyListener.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/RendererPolicyListener.java
new file mode 100644 (file)
index 0000000..fdbbae5
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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 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.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
+import org.opendaylight.groupbasedpolicy.util.IidFactory;
+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.RendererPolicy;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class RendererPolicyListener extends DataTreeChangeHandler<RendererPolicy> {
+
+    // TODO move to common place
+    private static final RendererName RENDERER_NAME = new RendererName("VPP renderer");
+
+    protected RendererPolicyListener(DataBroker dataProvider) {
+        super(dataProvider);
+        registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
+                IidFactory.rendererIid(RENDERER_NAME).child(RendererPolicy.class)));
+    }
+
+    @Override
+    protected void onWrite(DataObjectModification<RendererPolicy> rootNode,
+            InstanceIdentifier<RendererPolicy> rootIdentifier) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    protected void onDelete(DataObjectModification<RendererPolicy> rootNode,
+            InstanceIdentifier<RendererPolicy> rootIdentifier) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    protected void onSubtreeModified(DataObjectModification<RendererPolicy> rootNode,
+            InstanceIdentifier<RendererPolicy> rootIdentifier) {
+        // TODO Auto-generated method stub
+
+    }
+
+}
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppEndpointListener.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppEndpointListener.java
new file mode 100644 (file)
index 0000000..5c31684
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 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.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.event.VppEndpointConfEvent;
+import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.eventbus.EventBus;
+
+public class VppEndpointListener extends DataTreeChangeHandler<VppEndpoint> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(VppEndpointListener.class);
+    private EventBus eventBus;
+
+    public VppEndpointListener(DataBroker dataProvider, EventBus eventBus) {
+        super(dataProvider);
+        this.eventBus = Preconditions.checkNotNull(eventBus);
+        registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.builder(Config.class).child(VppEndpoint.class).build()));
+    }
+
+    @Override
+    protected void onWrite(DataObjectModification<VppEndpoint> rootNode,
+            InstanceIdentifier<VppEndpoint> rootIdentifier) {
+        VppEndpointConfEvent event =
+                new VppEndpointConfEvent(rootIdentifier, rootNode.getDataBefore(), rootNode.getDataAfter());
+        LOG.trace("Dispatching event on write: {}", event);
+        eventBus.post(event);
+    }
+
+    @Override
+    protected void onDelete(DataObjectModification<VppEndpoint> rootNode,
+            InstanceIdentifier<VppEndpoint> rootIdentifier) {
+        VppEndpointConfEvent event =
+                new VppEndpointConfEvent(rootIdentifier, rootNode.getDataBefore(), rootNode.getDataAfter());
+        LOG.trace("Dispatching event on delete: {}", event);
+        eventBus.post(event);
+    }
+
+    @Override
+    protected void onSubtreeModified(DataObjectModification<VppEndpoint> rootNode,
+            InstanceIdentifier<VppEndpoint> rootIdentifier) {
+        VppEndpointConfEvent event =
+                new VppEndpointConfEvent(rootIdentifier, rootNode.getDataBefore(), rootNode.getDataAfter());
+        LOG.trace("Dispatching event on subtree modified: {}", event);
+        eventBus.post(event);
+    }
+
+}
index df39b165f2c90ed1dcd057444cc863ec729dfcdc..086b51953249c43a10d37a309ed0eeb21fb586d7 100644 (file)
@@ -18,6 +18,7 @@ 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.event.NodeOperEvent;
 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;
@@ -28,6 +29,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Preconditions;
+import com.google.common.eventbus.EventBus;
 
 public class VppNodeListener implements DataTreeChangeListener<Node>, AutoCloseable {
 
@@ -35,9 +37,11 @@ public class VppNodeListener implements DataTreeChangeListener<Node>, AutoClosea
 
     private final ListenerRegistration<VppNodeListener> listenerRegistration;
     private final VppNodeManager nodeManager;
+    private final EventBus eventBus;
 
-    public VppNodeListener(DataBroker dataBroker, VppNodeManager nodeManager) {
+    public VppNodeListener(DataBroker dataBroker, VppNodeManager nodeManager, EventBus eventBus) {
         this.nodeManager = Preconditions.checkNotNull(nodeManager);
+        this.eventBus = Preconditions.checkNotNull(eventBus);
         // Register listener
         final DataTreeIdentifier<Node> networkTopologyPath = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
                 InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class).child(Node.class).build());
@@ -54,6 +58,9 @@ public class VppNodeListener implements DataTreeChangeListener<Node>, AutoClosea
             DataObjectModification<Node> rootNode = modification.getRootNode();
             Node dataAfter = rootNode.getDataAfter();
             Node dataBefore = rootNode.getDataBefore();
+            NodeOperEvent event =
+                    new NodeOperEvent(modification.getRootPath().getRootIdentifier(), dataBefore, dataAfter);
+            eventBus.post(event);
             nodeManager.syncNodes(dataAfter, dataBefore);
         }
     }
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/MountedDataBrokerProvider.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/MountedDataBrokerProvider.java
new file mode 100644 (file)
index 0000000..071e14d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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 javax.annotation.Nonnull;
+
+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.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 MountedDataBrokerProvider {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MountedDataBrokerProvider.class);
+    private final MountPointService mountService;
+
+    public MountedDataBrokerProvider(@Nonnull MountPointService mountService) {
+        this.mountService = Preconditions.checkNotNull(mountService);
+    }
+
+    public Optional<DataBroker> getDataBrokerForMountPoint(@Nonnull InstanceIdentifier<?> iidToMountPoint) {
+        Optional<MountPoint> potentialMountPoint = mountService.getMountPoint(iidToMountPoint);
+        if (!potentialMountPoint.isPresent()) {
+            LOG.debug("Mount point does not exist for {}", iidToMountPoint);
+            return Optional.absent();
+        }
+        return potentialMountPoint.get().getService(DataBroker.class);
+    }
+}
index 3a71ffd46dc4f79a5eb17186ac7b1122dd54f173..c1210a923d15a34e22d0161c3de7489447ec86bb 100644 (file)
@@ -100,6 +100,26 @@ module v3po {
     }
   }
 
+  identity vxlan-gpe-tunnel {
+    base if:interface-type;
+  }
+
+  typedef vxlan-gpe-vni {
+    description "VNI used in a VXLAN-GPE tunnel";
+    type uint32 {
+      range "0..16777215";
+    }
+  }
+
+  typedef vxlan-gpe-next-protocol {
+    type enumeration {
+      enum ipv4;
+      enum ipv6;
+      enum ethernet;
+      enum nsh;
+    }
+  }
+
   grouping bridge-domain-attributes {
     leaf flood {
       type boolean;
@@ -183,6 +203,33 @@ module v3po {
     }
   }
 
+  grouping ethernet-base-attributes {
+    leaf mtu {
+      type uint16 {
+        range "64..9216";
+      }
+      units "octets";
+      default 9216;
+      description
+      "The size, in octets, of the largest packet that the
+       hardware interface will send and receive.";
+    }
+  }
+
+  grouping ethernet-state-attributes {
+    leaf manufacturer-description {
+      type string;
+      config false;
+    }
+    leaf duplex {
+      type enumeration {
+        enum "half";
+        enum "full";
+      }
+      config false;
+    }
+  }
+
   grouping vhost-user-interface-base-attributes {
     leaf socket {
       type string {
@@ -193,25 +240,69 @@ module v3po {
       type vhost-user-role;
       default "server";
     }
+    description "vhost-user settings";
   }
 
   grouping vhost-user-interface-state-attributes {
-      leaf features {
-        type uint64;
-        config false;
-      }
-      leaf virtio-net-hdr-size {
-        type uint32;
-        config false;
-      }
-      leaf num-memory-regions {
-        type uint32;
-        config false;
-      }
-      leaf connect-error {
-        type string;
-        config false;
-      }
+    leaf features {
+      type uint64;
+      config false;
+    }
+    leaf virtio-net-hdr-size {
+      type uint32;
+      config false;
+    }
+    leaf num-memory-regions {
+      type uint32;
+      config false;
+    }
+    leaf connect-error {
+      type string;
+      config false;
+    }
+  }
+
+  grouping vxlan-base-attributes {
+    // FIXME: this should be in an vxlan-specific extension
+    leaf src {
+      /*mandatory true;*/
+      type inet:ip-address;
+    }
+    leaf dst {
+      /*mandatory true;*/
+      type inet:ip-address;
+    }
+    leaf vni {
+      /*mandatory true;*/
+      type vxlan-vni;
+    }
+    leaf encap-vrf-id {
+      type uint32;
+    }
+  }
+
+  grouping vxlan-gpe-base-attributes {
+    leaf local {
+      /*mandatory true;*/
+      type inet:ip-address;
+    }
+    leaf remote {
+      /*mandatory true;*/
+      type inet:ip-address;
+    }
+    leaf vni {
+      /*mandatory true;*/
+      type vxlan-gpe-vni;
+    }
+    leaf next-protocol {
+      type vxlan-gpe-next-protocol;
+    }
+    leaf encap-vrf-id {
+      type uint32;
+    }
+    leaf decap-vrf-id {
+      type uint32;
+    }
   }
 
   grouping vlan-tag-rewrite-attributes {
@@ -231,83 +322,16 @@ module v3po {
     }
   }
 
-  augment /if:interfaces/if:interface {
-    ext:augment-identifier "vpp-interface-augmentation";
-
-    // FIXME using ietf-interfaces model for vpp interfaces makes it hard to implement because:
-    // 1. The link between interface type and this augmentation is unclear
-    // 2. Only this augmentation with combination of ifc type is trigger to do something for vpp, what if user only configures base interface stuff ? + We need to get leaves defined by ietf-interfaces when we are processing this augment
-
-    container sub-interface {
-      when "../if:type = 'v3po:sub-interface'";
-      leaf super-interface {
-        type if:interface-ref;
-      }
-      uses sub-interface-base-attributes;
-    }
-
-    container tap {
-      when "../if:type = 'v3po:tap'";
-      uses tap-interface-base-attributes;
-      uses tap-interface-config-attributes;
-    }
-
-    container ethernet {
-      when "../if:type = 'ianaift:ethernetCsmacd'";
-      leaf mtu {
-        type uint16 {
-          range "64..9216";
-        }
-        units "octets";
-        default 9216;
-        description
-        "The size, in octets, of the largest packet that the
-         hardware interface will send and receive.";
-      }
-    }
-    container routing {
-      leaf vrf-id {
-        type uint32;
-        default 0;
-      }
-    }
-    container vhost-user {
-      when "../if:type = 'v3po:vhost-user'";
-      uses vhost-user-interface-base-attributes;
-      description "vhost-user settings";
-    }
-    container vxlan {
-      // FIXME: this should be in an vxlan-specific extension
-      when "../if:type = 'v3po:vxlan-tunnel'";
-
-      leaf src {
-        /*mandatory true;*/
-        type inet:ip-address;
-      }
-      leaf dst {
-        /*mandatory true;*/
-        type inet:ip-address;
-      }
-      leaf vni {
-        /*mandatory true;*/
-        type vxlan-vni;
-      }
-      leaf encap-vrf-id {
-        type uint32;
-      }
-    }
-    container l2 {
+  grouping l2-attributes {
       description
       "Parameters for configuring Layer2 features on interfaces.";
-      must "(not (../if:ipv4[if:enabled = 'true']/if:address/if:ip) and " +
-      "not (../if:ipv6[if:enabled = 'true']/if:address/if:ip))";
 
       choice interconnection {
         case xconnect-based {
           leaf xconnect-outgoing-interface {
             /* Don't allow selection of this interface */
             must "../../if:name != current()";
-            type if:interface-ref;
+            type if:interface-ref; // todo use interface-state-ref for operational data?
             description
               "L2 xconnect mode";
           }
@@ -341,9 +365,66 @@ module v3po {
           }
         }
       }
-      container vlan-tag-rewrite {
+      container vlan-tag-rewrite { // todo valid only for sub-interfaces
         uses vlan-tag-rewrite-attributes;
       }
+  }
+
+  augment /if:interfaces/if:interface {
+    ext:augment-identifier "vpp-interface-augmentation";
+
+    // FIXME using ietf-interfaces model for vpp interfaces makes it hard to implement because:
+    // 1. The link between interface type and this augmentation is unclear
+    // 2. Only this augmentation with combination of ifc type is trigger to do something for vpp, what if user only configures base interface stuff ? + We need to get leaves defined by ietf-interfaces when we are processing this augment
+    // 3. The ietf-interfaces model does not define groupings which makes types reuse difficult
+
+    container sub-interface {
+      when "../if:type = 'v3po:sub-interface'";
+      leaf super-interface {
+        type if:interface-ref;
+      }
+      uses sub-interface-base-attributes;
+    }
+
+    container tap {
+      when "../if:type = 'v3po:tap'";
+      uses tap-interface-base-attributes;
+      uses tap-interface-config-attributes;
+    }
+
+    container ethernet {
+      when "../if:type = 'ianaift:ethernetCsmacd'";
+      uses ethernet-base-attributes;
+    }
+
+    container routing {
+      leaf vrf-id { // todo no routing info for oper, is it possible to get it from the vpp?
+        type uint32;
+        default 0;
+      }
+    }
+
+    container vhost-user {
+      when "../if:type = 'v3po:vhost-user'";
+      uses vhost-user-interface-base-attributes;
+    }
+
+    container vxlan {
+      when "../if:type = 'v3po:vxlan-tunnel'";
+      uses vxlan-base-attributes;
+    }
+
+    container l2 {
+      must "(not (../if:ipv4[if:enabled = 'true']/if:address/if:ip) and " +
+      "not (../if:ipv6[if:enabled = 'true']/if:address/if:ip))";
+
+      uses l2-attributes;
+    }
+
+    container vxlan-gpe {
+      when "../if:type = 'v3po:vxlan-gpe-tunnel'";
+
+      uses vxlan-gpe-base-attributes;
     }
   }
 
@@ -401,79 +482,36 @@ module v3po {
 
     container tap {
       when "../if:type = 'v3po:tap'";
-      uses tap-interface-base-attributes {
-        refine tap-name {
-            config false;
-        }
-      }
+      uses tap-interface-base-attributes;
     }
 
     container ethernet {
       when "../if:type = 'ianaift:ethernetCsmacd'";
-      leaf mtu {
-        type uint16;
-      }
-      leaf manufacturer-description {
-        type string;
-      }
-      leaf duplex {
-        type enumeration {
-          enum "half";
-          enum "full";
-        }
-      }
+      uses ethernet-base-attributes;
+      uses ethernet-state-attributes;
     }
+
     container vhost-user {
       when "../if:type = 'v3po:vhost-user'";
-      uses vhost-user-interface-base-attributes {
-        refine socket {
-            config false;
-        }
-        refine role {
-            config false;
-        }
-      }
+      uses vhost-user-interface-base-attributes;
       uses vhost-user-interface-state-attributes;
     }
+
     container vxlan {
       when "../if:type = 'v3po:vxlan-tunnel'";
+      uses vxlan-base-attributes;
+    }
+    container vxlan-gpe {
+      when "../if:type = 'v3po:vxlan-gpe-tunnel'";
 
-      leaf src {
-        type inet:ip-address;
-      }
-      leaf dst {
-        type inet:ip-address;
-      }
-
-      leaf vni {
-        type uint32;
-      }
-      leaf encap-vrf-id {
-        type uint32;
-      }
+      uses vxlan-gpe-base-attributes;
     }
+
     container l2 {
-      choice interconnection {
-        case xconnect-based {
-          leaf xconnect-outgoing-interface {
-            type if:interface-ref;
-          }
-        }
-        case bridge-based {
-          leaf bridge-domain {
-            type bridge-domain-ref;
-          }
-          leaf split-horizon-group {
-            type uint8;
-          }
-          leaf bridged-virtual-interface {
-            type boolean;
-          }
-        }
-      }
-      container vlan-tag-rewrite {
-        uses vlan-tag-rewrite-attributes;
-      }
+      must "(not (../if:ipv4[if:enabled = 'true']/if:address/if:ip) and " +
+      "not (../if:ipv6[if:enabled = 'true']/if:address/if:ip))";
+
+      uses l2-attributes;
     }
   }
 
index 63dc7b2de1e2840ba25ca3da487eb1a3edaf1d8d..56a3da326d035465ed3aee48e0b52be1c939117c 100644 (file)
@@ -8,7 +8,6 @@
 \r
 package org.opendaylight.groupbasedpolicy.renderer.vpp.commands;\r
 \r
-import com.google.common.base.Optional;\r
 import org.junit.Assert;\r
 import org.junit.Before;\r
 import org.junit.Test;\r
@@ -20,17 +19,19 @@ import org.opendaylight.groupbasedpolicy.renderer.vpp.VppRendererDataBrokerTest;
 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General;\r
 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;\r
 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;\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.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;\r
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUserRole;\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.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentationBuilder;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2Builder;\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.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUserBuilder;\r
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.interconnection.BridgeBased;\r
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.interconnection.BridgeBasedBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.attributes.interconnection.BridgeBased;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.attributes.interconnection.BridgeBasedBuilder;\r
+\r
+import com.google.common.base.Optional;\r
 \r
 public class VhostUserCommandTest extends VppRendererDataBrokerTest {\r
 \r
diff --git a/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/DtoChangeEventTest.java b/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/DtoChangeEventTest.java
new file mode 100644 (file)
index 0000000..b01938e
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.event;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+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.NodeId;
+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.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;
+
+public class DtoChangeEventTest {
+
+    private final static TopologyKey TOPO_KEY = new TopologyKey(new TopologyId("topo1"));
+    private final static NodeKey NODE_KEY = new NodeKey(new NodeId("node1"));
+    private final static InstanceIdentifier<Node> NODE_IID = InstanceIdentifier.builder(NetworkTopology.class)
+        .child(Topology.class, TOPO_KEY)
+        .child(Node.class, NODE_KEY)
+        .build();
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    private static class DummyDtoEvent extends DtoChangeEvent<Node> {
+
+        public DummyDtoEvent(InstanceIdentifier<Node> iid, Node before, Node after) {
+            super(iid, before, after);
+        }
+    }
+
+    @Test
+    public void testConstructor_nodeCreated() {
+        Node node = new NodeBuilder().setKey(NODE_KEY).build();
+        DummyDtoEvent event = new DummyDtoEvent(NODE_IID, null, node);
+        Assert.assertNotNull(event.getIid());
+        Assert.assertTrue(event.getAfter().isPresent());
+        Assert.assertFalse(event.getBefore().isPresent());
+        Assert.assertEquals(DtoChangeEvent.DtoModificationType.CREATED, event.getDtoModificationType());
+        Assert.assertTrue(event.isDtoCreated());
+        Assert.assertFalse(event.isDtoDeleted());
+        Assert.assertFalse(event.isDtoUpdated());
+    }
+
+    @Test
+    public void testConstructor_nodeDeleted() {
+        Node node = new NodeBuilder().setKey(NODE_KEY).build();
+        DummyDtoEvent event = new DummyDtoEvent(NODE_IID, node, null);
+        Assert.assertNotNull(event.getIid());
+        Assert.assertFalse(event.getAfter().isPresent());
+        Assert.assertTrue(event.getBefore().isPresent());
+        Assert.assertEquals(DtoChangeEvent.DtoModificationType.DELETED, event.getDtoModificationType());
+        Assert.assertFalse(event.isDtoCreated());
+        Assert.assertTrue(event.isDtoDeleted());
+        Assert.assertFalse(event.isDtoUpdated());
+    }
+
+    @Test
+    public void testConstructor_nodeUpdated() {
+        Node node = new NodeBuilder().setKey(NODE_KEY).build();
+        DummyDtoEvent event = new DummyDtoEvent(NODE_IID, node, node);
+        Assert.assertNotNull(event.getIid());
+        Assert.assertTrue(event.getAfter().isPresent());
+        Assert.assertTrue(event.getBefore().isPresent());
+        Assert.assertEquals(DtoChangeEvent.DtoModificationType.UPDATED, event.getDtoModificationType());
+        Assert.assertFalse(event.isDtoCreated());
+        Assert.assertFalse(event.isDtoDeleted());
+        Assert.assertTrue(event.isDtoUpdated());
+    }
+
+    @Test
+    public void testConstructor_nullNodes_Exception() {
+        thrown.expect(IllegalArgumentException.class);
+        new DummyDtoEvent(NODE_IID, null, null);
+    }
+
+    @Test
+    public void testConstructor_nullIid_Exception() {
+        Node node = new NodeBuilder().setKey(NODE_KEY).build();
+        thrown.expect(NullPointerException.class);
+        new DummyDtoEvent(null, node, node);
+    }
+}
diff --git a/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/NodeOperEventTest.java b/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/NodeOperEventTest.java
new file mode 100644 (file)
index 0000000..67f12cd
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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.event;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+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.ConnectionStatus;
+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.NodeId;
+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.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;
+
+public class NodeOperEventTest {
+
+    private final static TopologyKey TOPO_KEY = new TopologyKey(new TopologyId("topo1"));
+    private final static NodeKey NODE_KEY = new NodeKey(new NodeId("node1"));
+    private final static InstanceIdentifier<Node> NODE_IID = InstanceIdentifier.builder(NetworkTopology.class)
+        .child(Topology.class, TOPO_KEY)
+        .child(Node.class, NODE_KEY)
+        .build();
+    private final static NetconfNode NETCONF_NODE_AUG_CONNECTED =
+            new NetconfNodeBuilder().setConnectionStatus(ConnectionStatus.Connected).build();
+    private final static NetconfNode NETCONF_NODE_AUG_CONNECTING =
+            new NetconfNodeBuilder().setConnectionStatus(ConnectionStatus.Connecting).build();
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Test
+    public void testConstructor_nodeCreated() {
+        Node node = new NodeBuilder().setKey(NODE_KEY)
+            .addAugmentation(NetconfNode.class, NETCONF_NODE_AUG_CONNECTED)
+            .build();
+        NodeOperEvent event = new NodeOperEvent(NODE_IID, null, node);
+        Assert.assertTrue(event.getAfter().isPresent());
+        Assert.assertFalse(event.getBefore().isPresent());
+    }
+
+    @Test
+    public void testConstructor_nodeDeleted() {
+        Node node = new NodeBuilder().setKey(NODE_KEY)
+            .addAugmentation(NetconfNode.class, NETCONF_NODE_AUG_CONNECTED)
+            .build();
+        NodeOperEvent event = new NodeOperEvent(NODE_IID, node, null);
+        Assert.assertFalse(event.getAfter().isPresent());
+        Assert.assertTrue(event.getBefore().isPresent());
+    }
+
+    @Test
+    public void testConstructor_nodeUpdated() {
+        Node node = new NodeBuilder().setKey(NODE_KEY)
+            .addAugmentation(NetconfNode.class, NETCONF_NODE_AUG_CONNECTED)
+            .build();
+        NodeOperEvent event = new NodeOperEvent(NODE_IID, node, node);
+        Assert.assertTrue(event.getAfter().isPresent());
+        Assert.assertTrue(event.getBefore().isPresent());
+    }
+
+    @Test
+    public void testConstructor_beforeNodeMissingNetconfNodeAug_Exception() {
+        Node node = new NodeBuilder().setKey(NODE_KEY).build();
+        thrown.expect(IllegalArgumentException.class);
+        new NodeOperEvent(NODE_IID, node, null);
+    }
+
+    @Test
+    public void testConstructor_afterNodeMissingNetconfNodeAug_Exception() {
+        Node node = new NodeBuilder().setKey(NODE_KEY).build();
+        thrown.expect(IllegalArgumentException.class);
+        new NodeOperEvent(NODE_IID, null, node);
+    }
+
+    @Test
+    public void testConstructor_nullNodes_Exception() {
+        thrown.expect(IllegalArgumentException.class);
+        new NodeOperEvent(NODE_IID, null, null);
+    }
+
+    @Test
+    public void testConstructor_nullIid_Exception() {
+        Node node = new NodeBuilder().setKey(NODE_KEY)
+            .addAugmentation(NetconfNode.class, NETCONF_NODE_AUG_CONNECTED)
+            .build();
+        thrown.expect(NullPointerException.class);
+        new NodeOperEvent(null, node, node);
+    }
+
+    @Test
+    public void testIsAfterConnected() {
+        Node node = new NodeBuilder().setKey(NODE_KEY)
+            .addAugmentation(NetconfNode.class, NETCONF_NODE_AUG_CONNECTED)
+            .build();
+        NodeOperEvent event = new NodeOperEvent(NODE_IID, null, node);
+        Assert.assertTrue(event.isAfterConnected());
+    }
+
+    @Test
+    public void testIsBeforeConnected() {
+        Node node = new NodeBuilder().setKey(NODE_KEY)
+            .addAugmentation(NetconfNode.class, NETCONF_NODE_AUG_CONNECTED)
+            .build();
+        NodeOperEvent event = new NodeOperEvent(NODE_IID, node, null);
+        Assert.assertTrue(event.isBeforeConnected());
+    }
+
+    @Test
+    public void testIsAfterConnected_false() {
+        Node node = new NodeBuilder().setKey(NODE_KEY)
+            .addAugmentation(NetconfNode.class, NETCONF_NODE_AUG_CONNECTING)
+            .build();
+        NodeOperEvent event = new NodeOperEvent(NODE_IID, null, node);
+        Assert.assertFalse(event.isAfterConnected());
+    }
+    
+    @Test
+    public void testIsBeforeConnected_false() {
+        Node node = new NodeBuilder().setKey(NODE_KEY)
+            .addAugmentation(NetconfNode.class, NETCONF_NODE_AUG_CONNECTING)
+            .build();
+        NodeOperEvent event = new NodeOperEvent(NODE_IID, null, node);
+        Assert.assertFalse(event.isBeforeConnected());
+    }
+
+}
diff --git a/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/VppEndpointConfEventTest.java b/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/event/VppEndpointConfEventTest.java
new file mode 100644 (file)
index 0000000..d37fbfd
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.event;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.AddressType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.ContextType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class VppEndpointConfEventTest {
+
+    private final static String ADDRESS = "1.1.1.1/32";
+    private final static ContextId CONTEXT_ID = new ContextId("ctx1");
+    private final static VppEndpointKey BASIC_VPP_EP_KEY =
+            new VppEndpointKey(ADDRESS, AddressType.class, CONTEXT_ID, ContextType.class);
+    private final static InstanceIdentifier<VppEndpoint> BASIC_VPP_EP_IID =
+            InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, BASIC_VPP_EP_KEY).build();
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Test
+    public void testConstructor_vppEpCreated() {
+        VppEndpoint vppEndpoint = basicVppEpBuilder().build();
+        VppEndpointConfEvent event = new VppEndpointConfEvent(BASIC_VPP_EP_IID, null, vppEndpoint);
+        Assert.assertTrue(event.getAfter().isPresent());
+        Assert.assertFalse(event.getBefore().isPresent());
+    }
+
+    @Test
+    public void testConstructor_vppEpDeleted() {
+        VppEndpoint vppEndpoint = basicVppEpBuilder().build();
+        VppEndpointConfEvent event = new VppEndpointConfEvent(BASIC_VPP_EP_IID, vppEndpoint, null);
+        Assert.assertFalse(event.getAfter().isPresent());
+        Assert.assertTrue(event.getBefore().isPresent());
+    }
+
+    @Test
+    public void testConstructor_vppEpUpdated() {
+        VppEndpoint vppEndpoint = basicVppEpBuilder().build();
+        VppEndpointConfEvent event = new VppEndpointConfEvent(BASIC_VPP_EP_IID, vppEndpoint, vppEndpoint);
+        Assert.assertTrue(event.getAfter().isPresent());
+        Assert.assertTrue(event.getBefore().isPresent());
+    }
+
+    @Test
+    public void testConstructor_nullVppEp_Exception() {
+        thrown.expect(IllegalArgumentException.class);
+        new VppEndpointConfEvent(BASIC_VPP_EP_IID, null, null);
+    }
+
+    private VppEndpointBuilder basicVppEpBuilder() {
+        return new VppEndpointBuilder().setAddress(ADDRESS)
+            .setAddressType(AddressType.class)
+            .setContextId(CONTEXT_ID)
+            .setContextType(ContextType.class);
+    }
+
+}
diff --git a/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/InterfaceManagerTest.java b/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/InterfaceManagerTest.java
new file mode 100644 (file)
index 0000000..f2be664
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * 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.iface;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.event.VppEndpointConfEvent;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.MountedDataBrokerProvider;
+import org.opendaylight.groupbasedpolicy.test.CustomDataBrokerTest;
+import org.opendaylight.groupbasedpolicy.util.IidFactory;
+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.common.rev140421.ContextId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.LocationProviders;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.AddressType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.ContextType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUser;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUserRole;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.attributes.Interconnection;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.attributes.interconnection.BridgeBased;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains;
+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.NodeId;
+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 com.google.common.base.Optional;
+import com.google.common.base.Strings;
+
+public class InterfaceManagerTest extends CustomDataBrokerTest {
+
+    private final static String ADDRESS = "1.1.1.1/32";
+    private final static ContextId CONTEXT_ID = new ContextId("ctx1");
+    private final static String IFACE_NAME = "ifaceName1";
+    private final static VppEndpointKey BASIC_VPP_EP_KEY =
+            new VppEndpointKey(ADDRESS, AddressType.class, CONTEXT_ID, ContextType.class);
+    private final static InstanceIdentifier<VppEndpoint> BASIC_VPP_EP_IID =
+            InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, BASIC_VPP_EP_KEY).build();
+    private final static TopologyKey TOPO_KEY = new TopologyKey(new TopologyId("topo1"));
+    private final static NodeKey NODE_KEY = new NodeKey(new NodeId("node1"));
+    private final static InstanceIdentifier<Node> NODE_IID = InstanceIdentifier.builder(NetworkTopology.class)
+        .child(Topology.class, TOPO_KEY)
+        .child(Node.class, NODE_KEY)
+        .build();
+    private final static String SOCKET = "socket1";
+
+    private InterfaceManager manager;
+    private MountedDataBrokerProvider mountedDataProviderMock;
+    private DataBroker mountPointDataBroker;
+    private DataBroker dataBroker;
+
+    @Override
+    public Collection<Class<?>> getClassesFromModules() {
+        return Arrays.asList(Node.class, VppEndpoint.class, Interfaces.class, BridgeDomains.class,
+                LocationProviders.class);
+    }
+
+    @Before
+    public void init() throws Exception {
+        mountedDataProviderMock = Mockito.mock(MountedDataBrokerProvider.class);
+        mountPointDataBroker = getDataBroker();
+        setup(); // initialize new data broker for ODL data store
+        dataBroker = getDataBroker();
+        Mockito.when(mountedDataProviderMock.getDataBrokerForMountPoint(Mockito.any(InstanceIdentifier.class)))
+            .thenReturn(Optional.of(mountPointDataBroker));
+        manager = new InterfaceManager(mountedDataProviderMock, dataBroker);
+    }
+
+    @Test
+    public void testVppEndpointChanged_created() throws Exception {
+        VppEndpoint vhostEp = vhostVppEpBuilder().build();
+        VppEndpointConfEvent event = new VppEndpointConfEvent(BASIC_VPP_EP_IID, null, vhostEp);
+
+        manager.vppEndpointChanged(event);
+        // assert state on data store behind mount point
+        ReadOnlyTransaction rTxMount = mountPointDataBroker.newReadOnlyTransaction();
+        Optional<Interface> potentialIface =
+                rTxMount.read(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.builder(Interfaces.class)
+                    .child(Interface.class, new InterfaceKey(vhostEp.getVppInterfaceName()))
+                    .build()).get();
+        Assert.assertTrue(potentialIface.isPresent());
+        Interface iface = potentialIface.get();
+        Assert.assertEquals(VhostUser.class, iface.getType());
+        Assert.assertTrue(iface.isEnabled());
+        VppInterfaceAugmentation vppIface = iface.getAugmentation(VppInterfaceAugmentation.class);
+        Assert.assertNotNull(vppIface);
+        org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUser vhostUserIface =
+                vppIface.getVhostUser();
+        Assert.assertNotNull(vhostUserIface);
+        Assert.assertEquals(VhostUserRole.Client, vhostUserIface.getRole());
+        Assert.assertEquals(SOCKET, vhostUserIface.getSocket());
+        L2 l2Iface = vppIface.getL2();
+        if (l2Iface != null) {
+            Interconnection interconnection = l2Iface.getInterconnection();
+            if (interconnection != null) {
+                if (interconnection instanceof BridgeBased) {
+                    BridgeBased bridgeL2Iface = (BridgeBased) interconnection;
+                    Assert.assertTrue(Strings.isNullOrEmpty(bridgeL2Iface.getBridgeDomain()));
+                }
+            }
+        }
+        // assert state on ODL data store
+        ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
+        Optional<LocationProvider> optLocationProvider = rTx.read(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VppEndpointLocationProvider.VPP_ENDPOINT_LOCATION_PROVIDER)).get();
+        Assert.assertTrue(optLocationProvider.isPresent());
+        List<ProviderAddressEndpointLocation> epLocs = optLocationProvider.get().getProviderAddressEndpointLocation();
+        Assert.assertNotNull(epLocs);
+        Assert.assertEquals(1, epLocs.size());
+        ProviderAddressEndpointLocation epLoc = VppEndpointLocationProvider.createProviderAddressEndpointLocation(vhostEp);
+        Assert.assertEquals(epLoc, epLocs.get(0));
+    }
+
+    @Test
+    public void testVppEndpointChanged_deleted() throws Exception {
+        VppEndpoint vhostEp = vhostVppEpBuilder().build();
+        VppEndpointConfEvent createVppEpEvent = new VppEndpointConfEvent(BASIC_VPP_EP_IID, null, vhostEp);
+        VppEndpointConfEvent deleteVppEpEvent = new VppEndpointConfEvent(BASIC_VPP_EP_IID, vhostEp, null);
+
+        manager.vppEndpointChanged(createVppEpEvent);
+        manager.vppEndpointChanged(deleteVppEpEvent);
+        // assert state on data store behind mount point
+        ReadOnlyTransaction rTxMount = mountPointDataBroker.newReadOnlyTransaction();
+        Optional<Interface> potentialIface =
+                rTxMount.read(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.builder(Interfaces.class)
+                    .child(Interface.class, new InterfaceKey(vhostEp.getVppInterfaceName()))
+                    .build()).get();
+        Assert.assertFalse(potentialIface.isPresent());
+        // assert state on ODL data store
+        ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
+        ProviderAddressEndpointLocation providerAddressEndpointLocation =
+                VppEndpointLocationProvider.createProviderAddressEndpointLocation(vhostEp);
+        InstanceIdentifier<ProviderAddressEndpointLocation> providerAddressEndpointLocationIid = IidFactory
+            .providerAddressEndpointLocationIid(VppEndpointLocationProvider.VPP_ENDPOINT_LOCATION_PROVIDER,
+                    providerAddressEndpointLocation.getKey());
+        Optional<ProviderAddressEndpointLocation> optProvEpLoc =
+                rTx.read(LogicalDatastoreType.CONFIGURATION, providerAddressEndpointLocationIid).get();
+        Assert.assertFalse(optProvEpLoc.isPresent());
+    }
+
+    private VppEndpointBuilder vhostVppEpBuilder() {
+        return basicVppEpBuilder().setVppInterfaceName(IFACE_NAME)
+            .setVppNodePath(NODE_IID)
+            .setInterfaceTypeChoice(new VhostUserCaseBuilder().setSocket(SOCKET).build());
+    }
+
+    private VppEndpointBuilder basicVppEpBuilder() {
+        return new VppEndpointBuilder().setAddress(ADDRESS)
+            .setAddressType(AddressType.class)
+            .setContextId(CONTEXT_ID)
+            .setContextType(ContextType.class);
+    }
+}
diff --git a/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppEndpointListenerTest.java b/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/listener/VppEndpointListenerTest.java
new file mode 100644 (file)
index 0000000..37c68c8
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.Arrays;
+import java.util.Collection;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+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.groupbasedpolicy.renderer.vpp.event.VppEndpointConfEvent;
+import org.opendaylight.groupbasedpolicy.test.CustomDataBrokerTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.LocationProviders;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.AddressType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.ContextType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.Forwarding;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.eventbus.EventBus;
+
+public class VppEndpointListenerTest extends CustomDataBrokerTest {
+
+    private final static String ADDRESS = "1.1.1.1/32";
+    private final static ContextId CONTEXT_ID = new ContextId("ctx1");
+    private final static String IFACE_NAME = "ifaceName";
+
+    private DataBroker dataBroker;
+    private VppEndpointListener listener;
+    private EventBus eventBusMock;
+
+    @Override
+    public Collection<Class<?>> getClassesFromModules() {
+        return Arrays.asList(Node.class, VppEndpoint.class, Forwarding.class, LocationProviders.class);
+    }
+
+    @Before
+    public void init() {
+        dataBroker = getDataBroker();
+        eventBusMock = Mockito.mock(EventBus.class);
+        listener = new VppEndpointListener(dataBroker, eventBusMock);
+    }
+
+    @Test
+    public void testOnWrite() throws Exception {
+        ArgumentCaptor<VppEndpointConfEvent> argVppEpEvent = ArgumentCaptor.forClass(VppEndpointConfEvent.class);
+        VppEndpoint vppEndpoint = new VppEndpointBuilder().setAddress(ADDRESS)
+            .setAddressType(AddressType.class)
+            .setContextId(CONTEXT_ID)
+            .setContextType(ContextType.class)
+            .build();
+        InstanceIdentifier<VppEndpoint> vppEpIid =
+                InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEndpoint.getKey()).build();
+        WriteTransaction wTx = getDataBroker().newWriteOnlyTransaction();
+        wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEndpoint);
+        wTx.submit().get();
+
+        Mockito.verify(eventBusMock).post(argVppEpEvent.capture());
+        VppEndpointConfEvent capturedVppEpEvent = argVppEpEvent.getValue();
+        Assert.assertEquals(vppEpIid, capturedVppEpEvent.getIid());
+        assertEqualsOptional(null, capturedVppEpEvent.getBefore());
+        assertEqualsOptional(vppEndpoint, capturedVppEpEvent.getAfter());
+    }
+
+    @Test
+    public void testOnDelete() throws Exception {
+        ArgumentCaptor<VppEndpointConfEvent> argVppEpEvent = ArgumentCaptor.forClass(VppEndpointConfEvent.class);
+        VppEndpoint vppEndpoint = new VppEndpointBuilder().setAddress(ADDRESS)
+            .setAddressType(AddressType.class)
+            .setContextId(CONTEXT_ID)
+            .setContextType(ContextType.class)
+            .build();
+        InstanceIdentifier<VppEndpoint> vppEpIid =
+                InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEndpoint.getKey()).build();
+        WriteTransaction wTx = getDataBroker().newWriteOnlyTransaction();
+        wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEndpoint);
+        wTx.submit().get();
+        wTx = getDataBroker().newWriteOnlyTransaction();
+        wTx.delete(LogicalDatastoreType.CONFIGURATION, vppEpIid);
+        wTx.submit().get();
+
+        Mockito.verify(eventBusMock, Mockito.times(2)).post(argVppEpEvent.capture());
+        VppEndpointConfEvent capturedVppEpEvent = argVppEpEvent.getAllValues().get(1);
+        Assert.assertEquals(vppEpIid, capturedVppEpEvent.getIid());
+        assertEqualsOptional(vppEndpoint, capturedVppEpEvent.getBefore());
+        assertEqualsOptional(null, capturedVppEpEvent.getAfter());
+    }
+
+    @Test
+    public void testOnSubtreeModified() throws Exception {
+        ArgumentCaptor<VppEndpointConfEvent> argVppEpEvent = ArgumentCaptor.forClass(VppEndpointConfEvent.class);
+        VppEndpointBuilder vppEndpointBuilder = new VppEndpointBuilder().setAddress(ADDRESS)
+            .setAddressType(AddressType.class)
+            .setContextId(CONTEXT_ID)
+            .setContextType(ContextType.class);
+        VppEndpoint vppEndpoint = vppEndpointBuilder.build();
+        InstanceIdentifier<VppEndpoint> vppEpIid =
+                InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEndpoint.getKey()).build();
+        WriteTransaction wTx = getDataBroker().newWriteOnlyTransaction();
+        wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEndpoint);
+        wTx.submit().get();
+        VppEndpoint modifiedVppEndpoint = vppEndpointBuilder.setVppInterfaceName(IFACE_NAME).build();
+        wTx = getDataBroker().newWriteOnlyTransaction();
+        wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, modifiedVppEndpoint);
+        wTx.submit().get();
+
+        Mockito.verify(eventBusMock, Mockito.times(2)).post(argVppEpEvent.capture());
+        VppEndpointConfEvent capturedFirstVppEpEvent = argVppEpEvent.getAllValues().get(0);
+        Assert.assertEquals(vppEpIid, capturedFirstVppEpEvent.getIid());
+        assertEqualsOptional(null, capturedFirstVppEpEvent.getBefore());
+        assertEqualsOptional(vppEndpoint, capturedFirstVppEpEvent.getAfter());
+        VppEndpointConfEvent capturedSecondVppEpEvent = argVppEpEvent.getAllValues().get(1);
+        Assert.assertEquals(vppEpIid, capturedSecondVppEpEvent.getIid());
+        assertEqualsOptional(vppEndpoint, capturedSecondVppEpEvent.getBefore());
+        assertEqualsOptional(modifiedVppEndpoint, capturedSecondVppEpEvent.getAfter());
+    }
+
+    private <T> void assertEqualsOptional(T expected, Optional<T> actual) {
+        if (expected == null) {
+            Assert.assertFalse(actual.isPresent());
+        } else {
+            Assert.assertTrue(actual.isPresent());
+            Assert.assertEquals(expected, actual.get());
+        }
+    }
+
+}
index 537adc7edfffe9460d9974c055789be118690f3b..f5d3518ac283e7440558b54f732e8f491e340f39 100644 (file)
@@ -2,6 +2,7 @@
 package org.opendaylight.groupbasedpolicy.renderer.vpp.manager;
 
 import com.google.common.base.Optional;
+import com.google.common.eventbus.EventBus;
 import com.google.common.util.concurrent.CheckedFuture;
 import org.junit.After;
 import org.junit.Assert;
@@ -74,7 +75,7 @@ public class VppManagerDataStoreTest extends VppRendererDataBrokerTest {
         Mockito.when(mountPoint.getService(Matchers.<Class<DataBroker>>any())).thenReturn(Optional.of(dataBroker2));
         dataBroker = getDataBroker();
         vppNodeManager = new VppNodeManager(dataBroker, providerContext);
-        vppNodeListener = new VppNodeListener(dataBroker, vppNodeManager);
+        vppNodeListener = new VppNodeListener(dataBroker, vppNodeManager, new EventBus());
     }
 
     private Node createNode(final String name, NetconfNodeConnectionStatus.ConnectionStatus status) {