ip-sgt-distribution-service 33/42033/15
authorMatej Perina <mperina@cisco.com>
Wed, 21 Sep 2016 12:23:15 +0000 (14:23 +0200)
committerMatej Perina <mperina@cisco.com>
Thu, 8 Dec 2016 12:51:26 +0000 (12:51 +0000)
    - rebased
    - version bump to next master (carbon)
    - added support for clustering

Change-Id: I82fb13997031240e8b223d518349ab0e5124bace
Signed-off-by: Matej Perina <mperina@cisco.com>
Signed-off-by: Michal Rehak <mirehak@cisco.com>
16 files changed:
artifacts/pom.xml
features/pom.xml
features/src/main/features/features.xml
ip-sgt-distribution-service/pom.xml [new file with mode: 0644]
ip-sgt-distribution-service/src/main/config/default-config.xml [new file with mode: 0644]
ip-sgt-distribution-service/src/main/java/org/opendaylight/controller/config/yang/config/ip/sgt/distribution/service/cfg/IpSgtDistributionServiceInstance.java [new file with mode: 0644]
ip-sgt-distribution-service/src/main/java/org/opendaylight/controller/config/yang/config/ip/sgt/distribution/service/cfg/IpSgtDistributionServiceModule.java [new file with mode: 0644]
ip-sgt-distribution-service/src/main/java/org/opendaylight/controller/config/yang/config/ip/sgt/distribution/service/cfg/IpSgtDistributionServiceModuleFactory.java [new file with mode: 0644]
ip-sgt-distribution-service/src/main/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/IpSgtDistributionServiceImpl.java [new file with mode: 0644]
ip-sgt-distribution-service/src/main/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/SxpCapableNodeListener.java [new file with mode: 0644]
ip-sgt-distribution-service/src/main/resources/org/opendaylight/blueprint/ip-sgt-distribution-service.xml [new file with mode: 0644]
ip-sgt-distribution-service/src/main/yang/ip-sgt-distribution-service-cfg.yang [new file with mode: 0644]
ip-sgt-distribution-service/src/main/yang/ip-sgt-distribution.yang [new file with mode: 0644]
ip-sgt-distribution-service/src/test/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/IpSgtDistributionServiceImplTest.java [new file with mode: 0644]
ip-sgt-distribution-service/src/test/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/SxpCapableNodeListenerTest.java [new file with mode: 0644]
pom.xml

index c8e2a2a2ef890dbfa0f855322f82a77f4b9b78a2..c0745a9718fb10e5e2568f3ecc3330bcb5fda1c9 100755 (executable)
         <artifactId>sxp-ep-provider</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>ip-sgt-distribution-service</artifactId>
+        <version>${project.version}</version>
+      </dependency>
 
       <!-- GBP configuration -->
       <dependency>
         <type>xml</type>
         <classifier>config</classifier>
       </dependency>
+      <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>ip-sgt-distribution-service</artifactId>
+        <version>${project.version}</version>
+        <type>xml</type>
+        <classifier>config</classifier>
+      </dependency>
       <dependency>
         <groupId>${project.groupId}</groupId>
         <artifactId>sxp-ise-adapter</artifactId>
index 39c5339025f08c50b24f35f84717456e7ac81f00..658a9d4baa3562b426d3dbfa63ef5ed263865aa2 100755 (executable)
       <artifactId>sxp-core</artifactId>
       <version>${sxp.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.sxp</groupId>
+      <artifactId>sxp-controller</artifactId>
+      <version>${sxp.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.sxp</groupId>
       <artifactId>features</artifactId>
       <groupId>org.opendaylight.groupbasedpolicy</groupId>
       <artifactId>sxp-ep-provider</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.groupbasedpolicy</groupId>
+      <artifactId>ip-sgt-distribution-service</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.groupbasedpolicy</groupId>
       <artifactId>ios-xe-renderer</artifactId>
       <type>xml</type>
       <classifier>config</classifier>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.groupbasedpolicy</groupId>
+      <artifactId>ip-sgt-distribution-service</artifactId>
+      <type>xml</type>
+      <classifier>config</classifier>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.groupbasedpolicy</groupId>
       <artifactId>ios-xe-renderer</artifactId>
index 12d381fe741ffbecc765680c5d5f58961d692d82..e419b79c6975a1285ae92518a83a7c7b241b6db6 100755 (executable)
         <bundle>mvn:org.opendaylight.groupbasedpolicy/groupbasedpolicy-ui-bundle/{{VERSION}}</bundle>
     </feature>
 
+    <!--
+        SXP Distribution Service
+    -->
+    <feature name='odl-groupbasedpolicy-ip-sgt-distribution-service' version='${project.version}' description='OpenDaylight :: groupbasedpolicy :: ip-sgt-distribution-service'>
+        <feature version="${project.version}">odl-groupbasedpolicy-base</feature>
+        <feature version="${sxp.version}">odl-sxp-core</feature>
+        <feature version="${sxp.version}">odl-sxp-controller</feature>
+        <bundle>mvn:org.opendaylight.groupbasedpolicy/ip-sgt-distribution-service/{{VERSION}}</bundle>
+        <configfile finalname="${config.configfile.directory}/15-groupbasedpolicy-ip-sgt-distribution-service.xml">mvn:org.opendaylight.groupbasedpolicy/ip-sgt-distribution-service/{{VERSION}}/xml/config</configfile>
+    </feature>
+
     <!--
          NE Location Provider
     -->
diff --git a/ip-sgt-distribution-service/pom.xml b/ip-sgt-distribution-service/pom.xml
new file mode 100644 (file)
index 0000000..dd95633
--- /dev/null
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>config-parent</artifactId>
+        <version>0.6.0-SNAPSHOT</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.groupbasedpolicy</groupId>
+    <artifactId>ip-sgt-distribution-service</artifactId>
+    <version>0.5.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <name>groupbasedpolicy-ip-sgt-distribution-service</name>
+
+    <properties>
+        <sxp.version>1.4.0-SNAPSHOT</sxp.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.sxp</groupId>
+                <artifactId>sxp-core</artifactId>
+                <version>${sxp.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.sxp</groupId>
+                <artifactId>sxp-api</artifactId>
+                <version>${sxp.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.sxp</groupId>
+                <artifactId>sxp-controller</artifactId>
+                <version>${sxp.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>ietf-topology</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>ietf-inet-types-2013-07-15</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>yang-ext</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.sxp</groupId>
+            <artifactId>sxp-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.sxp</groupId>
+            <artifactId>sxp-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.sxp</groupId>
+            <artifactId>sxp-controller</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.groupbasedpolicy</groupId>
+            <artifactId>groupbasedpolicy</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    <!-- testing dependencies -->
+        <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>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-broker-impl</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <!-- project build -->
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/ip-sgt-distribution-service/src/main/config/default-config.xml b/ip-sgt-distribution-service/src/main/config/default-config.xml
new file mode 100644 (file)
index 0000000..6381bca
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<snapshot>
+    <configuration>
+        <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <module>
+                    <type xmlns:ip-sgt-distribution-service="urn:opendaylight:params:xml:ns:yang:controller:config:ip:sgt:distribution:service:cfg">
+                        ip-sgt-distribution-service:ip-sgt-distribution-service
+                    </type>
+                    <name>ip-sgt-distribution-service</name>
+                </module>
+            </modules>
+        </data>
+
+    </configuration>
+
+    <required-capabilities>
+        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:ip:sgt:distribution:service:cfg?module=ip-sgt-distribution-service-cfg&amp;revision=2016-07-15</capability>
+    </required-capabilities>
+
+</snapshot>
diff --git a/ip-sgt-distribution-service/src/main/java/org/opendaylight/controller/config/yang/config/ip/sgt/distribution/service/cfg/IpSgtDistributionServiceInstance.java b/ip-sgt-distribution-service/src/main/java/org/opendaylight/controller/config/yang/config/ip/sgt/distribution/service/cfg/IpSgtDistributionServiceInstance.java
new file mode 100644 (file)
index 0000000..7e9c87e
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.yang.config.ip.sgt.distribution.service.cfg;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.config.yang.config.groupbasedpolicy.GroupbasedpolicyInstance;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.groupbasedpolicy.ip.sgt.distribution.service.impl.IpSgtDistributionServiceImpl;
+import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
+import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
+import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
+import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.IpSgtDistributionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.RemoveIpSgtBindingFromPeerInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.SendIpSgtBindingToPeerInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.SxpControllerService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class IpSgtDistributionServiceInstance
+        implements ClusterSingletonService, IpSgtDistributionService, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(IpSgtDistributionServiceInstance.class);
+    private static final ServiceGroupIdentifier IDENTIFIER =
+            ServiceGroupIdentifier.create(GroupbasedpolicyInstance.GBP_SERVICE_GROUP_IDENTIFIER);
+    private IpSgtDistributionServiceImpl ipSgtDistributionServiceImpl;
+    private DataBroker dataBroker;
+    private SxpControllerService sxpService;
+    private IpAddress sourceIp;
+    private ClusterSingletonServiceProvider clusterSingletonService;
+    private ClusterSingletonServiceRegistration singletonServiceRegistration;
+
+    public IpSgtDistributionServiceInstance(final DataBroker dataBroker, final SxpControllerService sxpService,
+            final String sourceIp, final ClusterSingletonServiceProvider clusterSingletonService) {
+        this.dataBroker = Preconditions.checkNotNull(dataBroker);
+        this.sxpService = Preconditions.checkNotNull(sxpService);
+        this.sourceIp = new IpAddress(Preconditions.checkNotNull(sourceIp.toCharArray()));
+        this.clusterSingletonService = Preconditions.checkNotNull(clusterSingletonService);
+    }
+
+    public void initialize() {
+        LOG.info("Clustering session initiated for {}", this.getClass().getSimpleName());
+        singletonServiceRegistration = clusterSingletonService.registerClusterSingletonService(this);
+    }
+
+    @Override
+    public ServiceGroupIdentifier getIdentifier() {
+        return IDENTIFIER;
+    }
+
+    @Override
+    public void close() throws Exception {
+        LOG.info("Clustering provider closed for {}", this.getClass().getSimpleName());
+        if (singletonServiceRegistration != null) {
+            try {
+                singletonServiceRegistration.close();
+            } catch (Exception e) {
+                LOG.warn("{} closed unexpectedly", this.getClass().getSimpleName(), e);
+            }
+            singletonServiceRegistration = null;
+        }
+    }
+
+    @Override
+    public Future<RpcResult<Void>> removeIpSgtBindingFromPeer(RemoveIpSgtBindingFromPeerInput input) {
+        return ipSgtDistributionServiceImpl.removeIpSgtBindingFromPeer(input);
+    }
+
+    @Override
+    public Future<RpcResult<Void>> sendIpSgtBindingToPeer(SendIpSgtBindingToPeerInput input) {
+        return ipSgtDistributionServiceImpl.sendIpSgtBindingToPeer(input);
+    }
+
+    @Override
+    public ListenableFuture<Void> closeServiceInstance() {
+        LOG.info("Instance {} closed", this.getClass().getSimpleName());
+        try {
+            ipSgtDistributionServiceImpl.close();
+        } catch (Exception e) {
+            LOG.error("Closing {} wasnt succesfull", ipSgtDistributionServiceImpl.getClass().getSimpleName());
+        }
+        return Futures.immediateFuture(null);
+    }
+
+    @Override
+    public void instantiateServiceInstance() {
+        ipSgtDistributionServiceImpl = new IpSgtDistributionServiceImpl(dataBroker, sxpService, sourceIp);
+    }
+
+}
diff --git a/ip-sgt-distribution-service/src/main/java/org/opendaylight/controller/config/yang/config/ip/sgt/distribution/service/cfg/IpSgtDistributionServiceModule.java b/ip-sgt-distribution-service/src/main/java/org/opendaylight/controller/config/yang/config/ip/sgt/distribution/service/cfg/IpSgtDistributionServiceModule.java
new file mode 100644 (file)
index 0000000..bf036ac
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.controller.config.yang.config.ip.sgt.distribution.service.cfg;
+
+import org.opendaylight.controller.sal.common.util.NoopAutoCloseable;
+
+public class IpSgtDistributionServiceModule extends org.opendaylight.controller.config.yang.config.ip.sgt.distribution.service.cfg.AbstractIpSgtDistributionServiceModule {
+    public IpSgtDistributionServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public IpSgtDistributionServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.ip.sgt.distribution.service.cfg.IpSgtDistributionServiceModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void customValidation() {
+        // add custom validation form module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return NoopAutoCloseable.INSTANCE;
+    }
+
+}
diff --git a/ip-sgt-distribution-service/src/main/java/org/opendaylight/controller/config/yang/config/ip/sgt/distribution/service/cfg/IpSgtDistributionServiceModuleFactory.java b/ip-sgt-distribution-service/src/main/java/org/opendaylight/controller/config/yang/config/ip/sgt/distribution/service/cfg/IpSgtDistributionServiceModuleFactory.java
new file mode 100644 (file)
index 0000000..ddd9d24
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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.controller.config.yang.config.ip.sgt.distribution.service.cfg;
+public class IpSgtDistributionServiceModuleFactory extends org.opendaylight.controller.config.yang.config.ip.sgt.distribution.service.cfg.AbstractIpSgtDistributionServiceModuleFactory {
+
+}
diff --git a/ip-sgt-distribution-service/src/main/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/IpSgtDistributionServiceImpl.java b/ip-sgt-distribution-service/src/main/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/IpSgtDistributionServiceImpl.java
new file mode 100644 (file)
index 0000000..766431f
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * 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.ip.sgt.distribution.service.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.IpSgtDistributionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.RemoveIpSgtBindingFromPeerInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.SendIpSgtBindingToPeerInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.rpc.fields.Binding;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.rpc.fields.binding.PeerNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.AddNodeInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.AddNodeInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.AddNodeOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.SxpControllerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.master.database.fields.MasterDatabaseBinding;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.master.database.fields.MasterDatabaseBindingBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.master.database.fields.MasterDatabaseBindingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.SxpNodeIdentity;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.SxpDomains;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomainKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.databases.fields.MasterDatabase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+public class IpSgtDistributionServiceImpl implements AutoCloseable, IpSgtDistributionService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(IpSgtDistributionServiceImpl.class);
+    public static final String SXP_NODE_DESCRIPTION = "ODL-GBP SXP node";
+    public static final String SXP_TOPOLOGY_ID = "sxp";
+    private final String SXP_NODE_ID;
+    private DataBroker dataBroker;
+    private IpAddress sourceIp;
+    private SxpCapableNodeListener nodeCollector;
+
+    public IpSgtDistributionServiceImpl(DataBroker dataBroker, SxpControllerService sxpService, IpAddress sourceIp) {
+        this.dataBroker = Preconditions.checkNotNull(dataBroker);
+        this.sourceIp = Preconditions.checkNotNull(sourceIp);
+        Preconditions.checkNotNull(sxpService);
+
+        if (sourceIp.getIpv4Address() != null) {
+            SXP_NODE_ID = sourceIp.getIpv4Address().getValue();
+        } else {
+            SXP_NODE_ID = sourceIp.getIpv6Address().getValue();
+        }
+        createSxpNode(sxpService);
+        nodeCollector = new SxpCapableNodeListener(dataBroker, SXP_NODE_ID);
+
+    }
+
+    private void createSxpNode(SxpControllerService sxpService) {
+        AddNodeInput addNodeInput = new AddNodeInputBuilder().setNodeId(new NodeId(SXP_NODE_ID))
+            .setSourceIp(sourceIp)
+            .setDescription(SXP_NODE_DESCRIPTION)
+            .build();
+        Future<RpcResult<AddNodeOutput>> addNodeResult = sxpService.addNode(addNodeInput);
+        try {
+            if (!addNodeResult.get().getResult().isResult()) {
+                LOG.error("RPC add-node wasn't successfull");
+            }
+        } catch (Exception e) {
+            LOG.error("RPC add-node wasn't successfull");
+        }
+    }
+
+    @Override
+    public Future<RpcResult<Void>> sendIpSgtBindingToPeer(SendIpSgtBindingToPeerInput input) {
+        Map<String, Multimap<Sgt, IpPrefix>> bindingsMap = new HashMap<>();
+        boolean success = true;
+        for (Binding binding : input.getBinding()) {
+            success = transformChanges(binding, bindingsMap);
+            if (!success) {
+                break;
+            }
+        }
+        if (!success) {
+            return Futures.immediateCheckedFuture(RpcResultBuilder.<Void>failed().build());
+        }
+        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        bindingsMap.entrySet().forEach(bindingEntries -> {
+            String domainId = bindingEntries.getKey();
+            bindingEntries.getValue().entries().forEach(binding -> writeBinding(binding, domainId, wtx));
+        });
+        ListenableFuture<Void> submit = wtx.submit();
+        SettableFuture<RpcResult<Void>> future = SettableFuture.create();
+        Futures.addCallback(submit, new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(Void result) {
+                future.set(RpcResultBuilder.<Void>success().build());
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                future.set(RpcResultBuilder.<Void>failed().build());
+
+            }
+
+        });
+        return future;
+    }
+
+    private boolean transformChanges(Binding binding, Map<String, Multimap<Sgt, IpPrefix>> bindingsMap) {
+        Sgt sgt = binding.getSgt();
+        IpPrefix addr = binding.getIpPrefix();
+        for (PeerNode peer : binding.getPeerNode()) {
+            String domainId = nodeCollector.getDomainIdForPeer((InstanceIdentifier<Node>) peer.getNodeIid());
+            if (domainId == null) {
+                LOG.debug("Node {} is not SXP capable", peer.getNodeIid());
+                return false;
+            }
+            Multimap<Sgt, IpPrefix> domainBindingMap = bindingsMap.get(domainId);
+            if (domainBindingMap == null) {
+                domainBindingMap = ArrayListMultimap.create();
+                bindingsMap.put(domainId, domainBindingMap);
+            }
+            domainBindingMap.get(sgt).add(addr);
+        }
+        return true;
+    }
+
+    private void writeBinding(Entry<Sgt, IpPrefix> binding, String domainId, WriteTransaction wtx) {
+        IpPrefix addr = binding.getValue();
+        InstanceIdentifier<MasterDatabaseBinding> iid = bindingIid(domainId, addr);
+        MasterDatabaseBinding newBinding = createBinding(binding);
+        wtx.put(LogicalDatastoreType.CONFIGURATION, iid, newBinding);
+    }
+
+    private InstanceIdentifier<MasterDatabaseBinding> bindingIid(String domainId, IpPrefix prefix) {
+        return InstanceIdentifier.builder(NetworkTopology.class)
+            .child(Topology.class, new TopologyKey(new TopologyId(SXP_TOPOLOGY_ID)))
+            .child(Node.class,
+                    new NodeKey(
+                            new org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId(
+                                    SXP_NODE_ID)))
+            .augmentation(SxpNodeIdentity.class)
+            .child(SxpDomains.class)
+            .child(SxpDomain.class, new SxpDomainKey(domainId))
+            .child(MasterDatabase.class)
+            .child(MasterDatabaseBinding.class, new MasterDatabaseBindingKey(prefix))
+            .build();
+    }
+
+    private MasterDatabaseBinding createBinding(Entry<Sgt, IpPrefix> binding) {
+        return new MasterDatabaseBindingBuilder().setIpPrefix(binding.getValue())
+            .setSecurityGroupTag(binding.getKey())
+            .build();
+    }
+
+    @Override
+    public Future<RpcResult<Void>> removeIpSgtBindingFromPeer(RemoveIpSgtBindingFromPeerInput input) {
+        Map<String, Multimap<Sgt, IpPrefix>> bindingsMap = new HashMap<>();
+        boolean success = true;
+        for (Binding binding : input.getBinding()) {
+            success = transformChanges(binding, bindingsMap);
+            if (!success) {
+                break;
+            }
+        }
+        if (!success) {
+            return Futures.immediateCheckedFuture(RpcResultBuilder.<Void>failed().build());
+        }
+        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        bindingsMap.entrySet().forEach(bindingEntries -> {
+            String domainId = bindingEntries.getKey();
+            bindingEntries.getValue().entries().forEach(binding -> removeBinding(binding, domainId, wtx));
+        });
+        ListenableFuture<Void> submit = wtx.submit();
+        SettableFuture<RpcResult<Void>> future = SettableFuture.create();
+        Futures.addCallback(submit, new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(Void result) {
+                future.set(RpcResultBuilder.<Void>success().build());
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                future.set(RpcResultBuilder.<Void>failed().build());
+
+            }
+
+        });
+        return future;
+    }
+
+    private void removeBinding(Entry<Sgt, IpPrefix> binding, String domainId, WriteTransaction wtx) {
+        IpPrefix addr = binding.getValue();
+        InstanceIdentifier<MasterDatabaseBinding> iid = bindingIid(domainId, addr);
+        wtx.delete(LogicalDatastoreType.CONFIGURATION, iid);
+    }
+
+    @Override
+    public void close() throws Exception {
+        nodeCollector.close();
+    }
+
+}
diff --git a/ip-sgt-distribution-service/src/main/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/SxpCapableNodeListener.java b/ip-sgt-distribution-service/src/main/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/SxpCapableNodeListener.java
new file mode 100644 (file)
index 0000000..af6c39a
--- /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.ip.sgt.distribution.service.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.SxpConnectionAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.sxp.connection.fields.SxpConnection;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.SxpNodeIdentity;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.SxpDomains;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomainBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomainKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.connection.fields.ConnectionTimersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.connections.fields.ConnectionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.connections.fields.connections.ConnectionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.ConnectionMode;
+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.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class SxpCapableNodeListener implements DataTreeChangeListener<SxpConnection> {
+
+    private String SXP_NODE_ID;
+    private ListenerRegistration<SxpCapableNodeListener> listenerRegistration;
+    private Map<InstanceIdentifier<Node>, String> sxpCapableNodes = new HashMap<>();
+    private DataBroker dataBroker;
+
+    public SxpCapableNodeListener(DataBroker dataBroker, String sxpNodeId) {
+        SXP_NODE_ID = sxpNodeId;
+        this.dataBroker = dataBroker;
+        DataTreeIdentifier<SxpConnection> iid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.builder(NetworkTopology.class)
+                    .child(Topology.class)
+                    .child(Node.class)
+                    .augmentation(SxpConnectionAugmentation.class)
+                    .child(SxpConnection.class)
+                    .build());
+        listenerRegistration = dataBroker.registerDataTreeChangeListener(iid, this);
+    }
+
+    @Override
+    public synchronized void onDataTreeChanged(Collection<DataTreeModification<SxpConnection>> changes) {
+        changes.forEach((change) -> {
+            InstanceIdentifier<Node> nodeIid = change.getRootPath().getRootIdentifier().firstIdentifierOf(Node.class);
+            DataObjectModification<SxpConnection> rootNode = change.getRootNode();
+            String domainId = createDomainId(nodeIid);
+            switch (rootNode.getModificationType()) {
+                case DELETE: {
+                    removeSxpDomain(domainId);
+                    sxpCapableNodes.remove(nodeIid);
+                    break;
+                }
+                case WRITE:
+                case SUBTREE_MODIFIED: {
+                    createSxpDomain(rootNode.getDataAfter(), domainId);
+                    sxpCapableNodes.put(nodeIid, domainId);
+                    break;
+                }
+            }
+        });
+    }
+
+    private String createDomainId(InstanceIdentifier<Node> nodeIid) {
+        String topologyId = nodeIid.firstKeyOf(Topology.class).getTopologyId().getValue();
+        String nodeId = nodeIid.firstKeyOf(Node.class).getNodeId().getValue();
+        return topologyId + "/" + nodeId;
+    }
+
+    private void removeSxpDomain(String domainId) {
+        InstanceIdentifier<SxpDomain> iid = sxpDomainIid(domainId);
+        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        wtx.delete(LogicalDatastoreType.CONFIGURATION, iid);
+        wtx.submit();
+    }
+
+    private void createSxpDomain(SxpConnection sxpData, String domainId) {
+        IpAddress peerAddr = sxpData.getIpAddress();
+        PortNumber port = sxpData.getPortNumber();
+        String password = sxpData.getPassword();
+        InstanceIdentifier<SxpDomain> iid = sxpDomainIid(domainId);
+        SxpDomain domain = new SxpDomainBuilder().setDomainName(domainId)
+            .setConnections(new ConnectionsBuilder()
+                .setConnection(Collections.singletonList(new ConnectionBuilder().setPeerAddress(peerAddr)
+                    .setTcpPort(port)
+                    .setMode(ConnectionMode.Speaker)
+                    .setPassword(password)
+                    .setConnectionTimers(new ConnectionTimersBuilder().build())
+                    .setDescription("Connection to " + domainId)
+                    .build()))
+                .build())
+            .build();
+        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        wtx.merge(LogicalDatastoreType.CONFIGURATION, iid, domain);
+        wtx.submit();
+    }
+
+    private InstanceIdentifier<SxpDomain> sxpDomainIid(String domainId) {
+        return InstanceIdentifier.builder(NetworkTopology.class)
+            .child(Topology.class, new TopologyKey(new TopologyId(IpSgtDistributionServiceImpl.SXP_TOPOLOGY_ID)))
+            .child(Node.class, new NodeKey(new NodeId(SXP_NODE_ID)))
+            .augmentation(SxpNodeIdentity.class)
+            .child(SxpDomains.class)
+            .child(SxpDomain.class, new SxpDomainKey(domainId))
+            .build();
+    }
+
+    synchronized String getDomainIdForPeer(InstanceIdentifier<Node> peer) {
+        return sxpCapableNodes.get(peer);
+    }
+
+    void close() {
+        listenerRegistration.close();
+    }
+}
diff --git a/ip-sgt-distribution-service/src/main/resources/org/opendaylight/blueprint/ip-sgt-distribution-service.xml b/ip-sgt-distribution-service/src/main/resources/org/opendaylight/blueprint/ip-sgt-distribution-service.xml
new file mode 100644 (file)
index 0000000..b2c1539
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+           odl:use-default-for-reference-types="true">
+
+    <reference id="dataBroker" interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"/>
+    <reference id="clusterSingletonService" interface="org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider"/>
+    <odl:rpc-service id="sxpControllerService" interface="org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.SxpControllerService"/>
+
+    <!-- Modules /-->
+    <bean id="ipSgtDistributionService" class="org.opendaylight.controller.config.yang.config.ip.sgt.distribution.service.cfg.IpSgtDistributionServiceInstance"
+        init-method="initialize" destroy-method="close">
+        <argument ref="dataBroker"/>
+        <argument ref="sxpControllerService"/>
+        <argument value="127.0.0.1"/>
+        <argument ref="clusterSingletonService"/>
+    </bean>
+</blueprint>
\ No newline at end of file
diff --git a/ip-sgt-distribution-service/src/main/yang/ip-sgt-distribution-service-cfg.yang b/ip-sgt-distribution-service/src/main/yang/ip-sgt-distribution-service-cfg.yang
new file mode 100644 (file)
index 0000000..c8d2ae1
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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
+ */
+
+module ip-sgt-distribution-service-cfg {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:ip:sgt:distribution:service:cfg";
+    prefix "ip-sgt-distribution-service-cfg";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; }
+    import ietf-inet-types { prefix inet; revision-date 2013-07-15; }
+
+    description
+        "This module contains the base YANG definitions for
+        ip/sgt distribution service implementation.";
+
+    revision "2016-07-15" {
+        description
+            "Initial revision.";
+    }
+
+    identity ip-sgt-distribution-service {
+        base "config:module-type";
+
+        config:java-name-prefix IpSgtDistributionService;
+    }
+
+    // Augments the 'configuration' choice node under modules/module.
+    augment "/config:modules/config:module/config:configuration" {
+        case ip-sgt-distribution-service {
+            when "/config:modules/config:module/config:type = 'ip-sgt-distribution-service'";
+        }
+    }
+}
diff --git a/ip-sgt-distribution-service/src/main/yang/ip-sgt-distribution.yang b/ip-sgt-distribution-service/src/main/yang/ip-sgt-distribution.yang
new file mode 100644 (file)
index 0000000..c2e36d8
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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
+ */
+
+module ip-sgt-distribution {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:ip:sgt:distribution";
+    prefix "ip-sgt-distribution";
+
+    import sxp-database { prefix sxp-database; revision-date 2016-03-08; }
+    import sxp-protocol { prefix sxppt; revision-date 2014-10-02; }
+    import ietf-inet-types { prefix inet; revision-date 2013-07-15; }
+    import network-topology { prefix nt; revision-date 2013-10-21; }
+    import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+    description
+        "This module contains the base YANG definitions for
+        ip/sgt distribution models.";
+
+    revision "2016-07-15" {
+        description
+            "Initial revision.";
+    }
+
+    grouping rpc-fields {
+        list binding {
+            key "sgt ip-prefix";
+            leaf sgt {
+                type sxp-database:sgt;
+            }
+            leaf ip-prefix {
+                type inet:ip-prefix;
+            }
+            list peer-node {
+                key "node-iid";
+                leaf node-iid {
+                    type instance-identifier;
+                }
+            }
+        }
+    }
+
+    grouping sxp-connection-fields {
+        container sxp-connection {
+            leaf ip-address {
+                type inet:ip-address;
+            }
+            leaf port-number {
+                type inet:port-number;
+                default 64999;
+            }
+            leaf password {
+                type string;
+            }
+            leaf version {
+                type sxppt:version;
+                default "version4";
+            }
+        }
+    }
+
+    rpc send-ip-sgt-binding-to-peer {
+        input {
+            uses rpc-fields;
+        }
+    }
+
+    rpc remove-ip-sgt-binding-from-peer {
+        input {
+            uses rpc-fields;
+        }
+    }
+
+    augment /nt:network-topology/nt:topology/nt:node {
+        ext:augment-identifier "sxp-connection-augmentation";
+        uses sxp-connection-fields;
+    }
+}
diff --git a/ip-sgt-distribution-service/src/test/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/IpSgtDistributionServiceImplTest.java b/ip-sgt-distribution-service/src/test/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/IpSgtDistributionServiceImplTest.java
new file mode 100644 (file)
index 0000000..2be7495
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * 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.ip.sgt.distribution.service.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.concurrent.Future;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.RemoveIpSgtBindingFromPeerInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.RemoveIpSgtBindingFromPeerInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.SendIpSgtBindingToPeerInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.SendIpSgtBindingToPeerInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.rpc.fields.BindingBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.rpc.fields.binding.PeerNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.AddNodeInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.AddNodeInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.AddNodeOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.AddNodeOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.controller.rev141002.SxpControllerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.master.database.fields.MasterDatabaseBinding;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.master.database.fields.MasterDatabaseBindingBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.master.database.fields.MasterDatabaseBindingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.SxpNodeIdentity;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.SxpDomains;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomainKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.databases.fields.MasterDatabase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.google.common.util.concurrent.Futures;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(IpSgtDistributionServiceImpl.class)
+public class IpSgtDistributionServiceImplTest {
+
+    private final IpAddress ADDR = new IpAddress(new Ipv4Address("10.0.0.1"));
+    private final IpPrefix BINDING_ADDR = new IpPrefix(new Ipv4Prefix("192.168.50.1/32"));
+    private final Sgt BINDING_SGT = new Sgt(1010);
+    private final String NODE_ID = "node1";
+    private final String TOPOLOGY_ID = "test-topology";
+    private final String DOMAIN_ID = TOPOLOGY_ID + "/" + NODE_ID;
+    private final InstanceIdentifier<Node> PEER_IID = InstanceIdentifier.builder(NetworkTopology.class)
+        .child(Topology.class, new TopologyKey(new TopologyId("test-topology")))
+        .child(Node.class, new NodeKey(
+                new org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId(NODE_ID)))
+        .build();
+    private final InstanceIdentifier<Node> SXP_NODE_IID = InstanceIdentifier.builder(NetworkTopology.class)
+        .child(Topology.class, new TopologyKey(new TopologyId("sxp")))
+        .child(Node.class,
+                new NodeKey(
+                        new org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId(
+                                ADDR.getIpv4Address().getValue())))
+        .build();
+    private DataBroker dataBroker;
+    private IpSgtDistributionServiceImpl impl;
+    private SxpCapableNodeListener nodeListener;
+
+    @Before
+    public void init() throws Exception {
+        SxpControllerService sxpService = mock(SxpControllerService.class);
+        when(sxpService.addNode(any())).thenReturn(Futures.immediateFuture(
+                RpcResultBuilder.<AddNodeOutput>success(new AddNodeOutputBuilder().setResult(true).build()).build()));
+        nodeListener = PowerMockito.mock(SxpCapableNodeListener.class);
+        PowerMockito.whenNew(SxpCapableNodeListener.class).withAnyArguments().thenReturn(nodeListener);
+        dataBroker = mock(DataBroker.class);
+        impl = new IpSgtDistributionServiceImpl(dataBroker, sxpService, ADDR);
+    }
+
+    @Test
+    public void testInit() throws Exception {
+        RpcProviderRegistry rpcProvider = mock(RpcProviderRegistry.class);
+        SxpControllerService sxpService = mock(SxpControllerService.class);
+        when(rpcProvider.getRpcService(SxpControllerService.class)).thenReturn(sxpService);
+        when(sxpService.addNode(any())).thenReturn(Futures.immediateFuture(
+                RpcResultBuilder.<AddNodeOutput>success(new AddNodeOutputBuilder().setResult(true).build()).build()));
+        SxpCapableNodeListener nodeListener = PowerMockito.mock(SxpCapableNodeListener.class);
+        PowerMockito.whenNew(SxpCapableNodeListener.class).withAnyArguments().thenReturn(nodeListener);
+        impl = new IpSgtDistributionServiceImpl(dataBroker, sxpService, ADDR);
+        AddNodeInput addNodeInput = new AddNodeInputBuilder().setNodeId(new NodeId(ADDR.getIpv4Address().getValue()))
+            .setSourceIp(ADDR)
+            .setDescription(IpSgtDistributionServiceImpl.SXP_NODE_DESCRIPTION)
+            .build();
+        verify(sxpService).addNode(eq(addNodeInput));
+        PowerMockito.verifyNew(SxpCapableNodeListener.class);
+    }
+
+    @Test
+    public void testSendIpSgtBindingToPeer_successfullWrite() throws Exception {
+        SendIpSgtBindingToPeerInput input =
+                new SendIpSgtBindingToPeerInputBuilder()
+                    .setBinding(
+                            Collections
+                                .singletonList(new BindingBuilder().setIpPrefix(BINDING_ADDR)
+                                    .setSgt(BINDING_SGT)
+                                    .setPeerNode(Collections
+                                        .singletonList(new PeerNodeBuilder().setNodeIid(PEER_IID).build()))
+                                    .build()))
+                    .build();
+        WriteTransaction wtx = mock(WriteTransaction.class);
+        when(dataBroker.newWriteOnlyTransaction()).thenReturn(wtx);
+        when(wtx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+        when(nodeListener.getDomainIdForPeer(PEER_IID)).thenReturn(DOMAIN_ID);
+        Future<RpcResult<Void>> response = impl.sendIpSgtBindingToPeer(input);
+        new MasterDatabaseBindingBuilder().setIpPrefix(BINDING_ADDR).setSecurityGroupTag(BINDING_SGT).build();
+        verify(wtx).put(eq(LogicalDatastoreType.CONFIGURATION),
+                eq(InstanceIdentifier.builder(SXP_NODE_IID)
+                    .augmentation(SxpNodeIdentity.class)
+                    .child(SxpDomains.class)
+                    .child(SxpDomain.class, new SxpDomainKey(DOMAIN_ID))
+                    .child(MasterDatabase.class)
+                    .child(MasterDatabaseBinding.class, new MasterDatabaseBindingKey(BINDING_ADDR))
+                    .build()),
+                eq(new MasterDatabaseBindingBuilder().setIpPrefix(BINDING_ADDR)
+                    .setSecurityGroupTag(BINDING_SGT)
+                    .build()));
+        assertTrue(response.get().isSuccessful());
+    }
+
+    @Test
+    public void testSendIpSgtBindingToPeer_failedWrite() throws Exception {
+        SendIpSgtBindingToPeerInput input =
+                new SendIpSgtBindingToPeerInputBuilder()
+                    .setBinding(
+                            Collections
+                                .singletonList(new BindingBuilder().setIpPrefix(BINDING_ADDR)
+                                    .setSgt(BINDING_SGT)
+                                    .setPeerNode(Collections
+                                        .singletonList(new PeerNodeBuilder().setNodeIid(PEER_IID).build()))
+                                    .build()))
+                    .build();
+        WriteTransaction wtx = mock(WriteTransaction.class);
+        when(dataBroker.newWriteOnlyTransaction()).thenReturn(wtx);
+        when(wtx.submit()).thenReturn(Futures.immediateFailedCheckedFuture(new TransactionCommitFailedException("")));
+        when(nodeListener.getDomainIdForPeer(PEER_IID)).thenReturn(DOMAIN_ID);
+        Future<RpcResult<Void>> response = impl.sendIpSgtBindingToPeer(input);
+        assertFalse(response.get().isSuccessful());
+    }
+
+    @Test
+    public void testSendIpSgtBindingToPeer_noSxpCapableNode() throws Exception {
+        SendIpSgtBindingToPeerInput input =
+                new SendIpSgtBindingToPeerInputBuilder()
+                    .setBinding(
+                            Collections
+                                .singletonList(new BindingBuilder().setIpPrefix(BINDING_ADDR)
+                                    .setSgt(BINDING_SGT)
+                                    .setPeerNode(Collections
+                                        .singletonList(new PeerNodeBuilder().setNodeIid(PEER_IID).build()))
+                                    .build()))
+                    .build();
+        when(nodeListener.getDomainIdForPeer(PEER_IID)).thenReturn(null);
+        Future<RpcResult<Void>> response = impl.sendIpSgtBindingToPeer(input);
+        assertFalse(response.get().isSuccessful());
+    }
+
+    @Test
+    public void testRemoveIpSgtBindingFromPeer_successfullWrite() throws Exception {
+        RemoveIpSgtBindingFromPeerInput input =
+                new RemoveIpSgtBindingFromPeerInputBuilder()
+                    .setBinding(
+                            Collections
+                                .singletonList(new BindingBuilder().setIpPrefix(BINDING_ADDR)
+                                    .setSgt(BINDING_SGT)
+                                    .setPeerNode(Collections
+                                        .singletonList(new PeerNodeBuilder().setNodeIid(PEER_IID).build()))
+                                    .build()))
+                    .build();
+        WriteTransaction wtx = mock(WriteTransaction.class);
+        when(dataBroker.newWriteOnlyTransaction()).thenReturn(wtx);
+        when(wtx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+        when(nodeListener.getDomainIdForPeer(PEER_IID)).thenReturn(DOMAIN_ID);
+        Future<RpcResult<Void>> response = impl.removeIpSgtBindingFromPeer(input);
+        new MasterDatabaseBindingBuilder().setIpPrefix(BINDING_ADDR).setSecurityGroupTag(BINDING_SGT).build();
+        verify(wtx).delete(eq(LogicalDatastoreType.CONFIGURATION),
+                eq(InstanceIdentifier.builder(SXP_NODE_IID)
+                    .augmentation(SxpNodeIdentity.class)
+                    .child(SxpDomains.class)
+                    .child(SxpDomain.class, new SxpDomainKey(DOMAIN_ID))
+                    .child(MasterDatabase.class)
+                    .child(MasterDatabaseBinding.class, new MasterDatabaseBindingKey(BINDING_ADDR))
+                    .build()));
+        assertTrue(response.get().isSuccessful());
+    }
+
+    @Test
+    public void testRemoveIpSgtBindingFromPeer_failedWrite() throws Exception {
+        RemoveIpSgtBindingFromPeerInput input =
+                new RemoveIpSgtBindingFromPeerInputBuilder()
+                    .setBinding(
+                            Collections
+                                .singletonList(new BindingBuilder().setIpPrefix(BINDING_ADDR)
+                                    .setSgt(BINDING_SGT)
+                                    .setPeerNode(Collections
+                                        .singletonList(new PeerNodeBuilder().setNodeIid(PEER_IID).build()))
+                                    .build()))
+                    .build();
+        WriteTransaction wtx = mock(WriteTransaction.class);
+        when(dataBroker.newWriteOnlyTransaction()).thenReturn(wtx);
+        when(wtx.submit()).thenReturn(Futures.immediateFailedCheckedFuture(new TransactionCommitFailedException("")));
+        when(nodeListener.getDomainIdForPeer(PEER_IID)).thenReturn(DOMAIN_ID);
+        Future<RpcResult<Void>> response = impl.removeIpSgtBindingFromPeer(input);
+        assertFalse(response.get().isSuccessful());
+    }
+
+    @Test
+    public void testRemoveIpSgtBindingFromPeer_noSxpCapableNode() throws Exception {
+        RemoveIpSgtBindingFromPeerInput input =
+                new RemoveIpSgtBindingFromPeerInputBuilder()
+                    .setBinding(
+                            Collections
+                                .singletonList(new BindingBuilder().setIpPrefix(BINDING_ADDR)
+                                    .setSgt(BINDING_SGT)
+                                    .setPeerNode(Collections
+                                        .singletonList(new PeerNodeBuilder().setNodeIid(PEER_IID).build()))
+                                    .build()))
+                    .build();
+        when(nodeListener.getDomainIdForPeer(PEER_IID)).thenReturn(null);
+        Future<RpcResult<Void>> response = impl.removeIpSgtBindingFromPeer(input);
+        assertFalse(response.get().isSuccessful());
+    }
+}
diff --git a/ip-sgt-distribution-service/src/test/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/SxpCapableNodeListenerTest.java b/ip-sgt-distribution-service/src/test/java/org/opendaylight/groupbasedpolicy/ip/sgt/distribution/service/impl/SxpCapableNodeListenerTest.java
new file mode 100644 (file)
index 0000000..540ad75
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * 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.ip.sgt.distribution.service.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+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.ReadFailedException;
+import org.opendaylight.groupbasedpolicy.test.CustomDataBrokerTest;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.SxpConnectionAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.SxpConnectionAugmentationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.sxp.connection.fields.SxpConnection;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.sxp.connection.fields.SxpConnectionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.SxpNodeIdentity;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.SxpNodeIdentityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.SxpDomains;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.SxpDomainsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomainKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.connections.fields.Connections;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.connections.fields.connections.Connection;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.ConnectionMode;
+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.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.CheckedFuture;
+
+public class SxpCapableNodeListenerTest extends CustomDataBrokerTest {
+
+    private final String SXP_NODE_ID = "sxp_node";
+    private final String NODE_ID = "node1";
+    private final IpAddress IP_ADDR = new IpAddress(new Ipv4Address("10.0.0.1"));
+    private final String PASSWD = "cisco123";
+    private final String TOPOLOGY_ID = "topology";
+    private final String DOMAIN_ID = TOPOLOGY_ID + "/" + NODE_ID;
+    private final PortNumber SXP_PORT = new PortNumber(64999);
+    private DataBroker dataBroker;
+    private SxpCapableNodeListener nodeListener;
+
+    @Override
+    public Collection<Class<?>> getClassesFromModules() {
+        return ImmutableList.of(Topology.class, SxpDomain.class, SxpConnectionAugmentation.class);
+    }
+
+    @Before
+    public void init() throws Exception {
+        dataBroker = getDataBroker();
+        nodeListener = new SxpCapableNodeListener(dataBroker, SXP_NODE_ID);
+    }
+
+    @Test
+    public void testInit() throws Exception {
+        dataBroker = mock(DataBroker.class);
+        nodeListener = new SxpCapableNodeListener(dataBroker, SXP_NODE_ID);
+        DataTreeIdentifier<SxpConnection> iid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.builder(NetworkTopology.class)
+                    .child(Topology.class)
+                    .child(Node.class)
+                    .augmentation(SxpConnectionAugmentation.class)
+                    .child(SxpConnection.class)
+                    .build());
+        verify(dataBroker).registerDataTreeChangeListener(iid, nodeListener);
+    }
+
+    @Test
+    public void testClose() {
+        dataBroker = mock(DataBroker.class);
+        ListenerRegistration<SxpCapableNodeListener> registration = mock(ListenerRegistration.class);
+        when(dataBroker.registerDataTreeChangeListener(any(), isA(SxpCapableNodeListener.class)))
+            .thenReturn(registration);
+        nodeListener = new SxpCapableNodeListener(dataBroker, SXP_NODE_ID);
+        nodeListener.close();
+        verify(registration).close();
+    }
+
+    @Test
+    public void testOnDataTreeChange_createAndDeleteNode() throws Exception {
+        Node sxpNode =
+                new NodeBuilder().setNodeId(new NodeId(SXP_NODE_ID))
+                    .addAugmentation(SxpNodeIdentity.class,
+                            new SxpNodeIdentityBuilder().setSxpDomains(new SxpDomainsBuilder().build()).build())
+                    .build();
+        InstanceIdentifier<Node> sxpNodeIid =
+                InstanceIdentifier.builder(NetworkTopology.class)
+                    .child(Topology.class,
+                            new TopologyKey(new TopologyId(IpSgtDistributionServiceImpl.SXP_TOPOLOGY_ID)))
+                    .child(Node.class, new NodeKey(new NodeId(SXP_NODE_ID)))
+                    .build();
+        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        wtx.put(LogicalDatastoreType.CONFIGURATION, sxpNodeIid, sxpNode, true);
+        Node node = new NodeBuilder().setNodeId(new NodeId(NODE_ID))
+            .addAugmentation(SxpConnectionAugmentation.class, new SxpConnectionAugmentationBuilder()
+                .setSxpConnection(new SxpConnectionBuilder().setIpAddress(IP_ADDR).setPassword(PASSWD).build()).build())
+            .build();
+        InstanceIdentifier<Node> nodeIid = InstanceIdentifier.builder(NetworkTopology.class)
+            .child(Topology.class, new TopologyKey(new TopologyId(TOPOLOGY_ID)))
+            .child(Node.class, new NodeKey(new NodeId(NODE_ID)))
+            .build();
+        wtx.put(LogicalDatastoreType.CONFIGURATION, nodeIid, node, true);
+        wtx.submit().get();
+        assertEquals(DOMAIN_ID, nodeListener.getDomainIdForPeer(nodeIid));
+        InstanceIdentifier<SxpDomain> domainIid =
+                InstanceIdentifier.builder(NetworkTopology.class)
+                    .child(Topology.class,
+                            new TopologyKey(new TopologyId(IpSgtDistributionServiceImpl.SXP_TOPOLOGY_ID)))
+                    .child(Node.class, new NodeKey(new NodeId(SXP_NODE_ID)))
+                    .augmentation(SxpNodeIdentity.class)
+                    .child(SxpDomains.class)
+                    .child(SxpDomain.class, new SxpDomainKey(DOMAIN_ID))
+                    .build();
+        ReadOnlyTransaction rtx = dataBroker.newReadOnlyTransaction();
+        CheckedFuture<Optional<SxpDomain>, ReadFailedException> read =
+                rtx.read(LogicalDatastoreType.CONFIGURATION, domainIid);
+        Optional<SxpDomain> optionalDomain = read.get();
+        assertTrue(optionalDomain.isPresent());
+        SxpDomain sxpDomain = optionalDomain.get();
+        Connections connections = sxpDomain.getConnections();
+        assertNotNull(connections);
+        List<Connection> connectionList = connections.getConnection();
+        assertNotNull(connectionList);
+        assertEquals(1, connectionList.size());
+        Connection connection = connectionList.get(0);
+        assertEquals(IP_ADDR, connection.getPeerAddress());
+        assertEquals(PASSWD, connection.getPassword());
+        assertEquals(SXP_PORT, connection.getTcpPort());
+        assertEquals(ConnectionMode.Speaker, connection.getMode());
+
+        wtx = dataBroker.newWriteOnlyTransaction();
+        wtx.delete(LogicalDatastoreType.CONFIGURATION, nodeIid);
+        wtx.submit().get();
+        assertNull(nodeListener.getDomainIdForPeer(nodeIid));
+        rtx = dataBroker.newReadOnlyTransaction();
+        read = rtx.read(LogicalDatastoreType.CONFIGURATION, domainIid);
+        assertFalse(read.get().isPresent());
+    }
+}
diff --git a/pom.xml b/pom.xml
index eb27a1274038540f14d9a51a8a6b84523100ba70..8ff08dec66f906a0d45ef51d1701d98ff27eb094 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -35,6 +35,7 @@
     <module>distribution-karaf</module>
     <module>features</module>
     <module>sxp-integration</module>
+    <module>ip-sgt-distribution-service</module>
   </modules>
 
   <build>