HONEYCOMB-356: API implementation 86/7186/2
authorMarek Gradzki <mgradzki@cisco.com>
Mon, 19 Jun 2017 06:30:19 +0000 (08:30 +0200)
committerMarek Gradzki <mgradzki@cisco.com>
Mon, 19 Jun 2017 11:23:52 +0000 (13:23 +0200)
RibWriter registers DataTreeChangeListener for given route type.
RouteWriter recevies create/update/delete notifications for single route
modifications in LocRib DS.

Change-Id: I4832abfb25aa189ecd3964febd6071f9a25117b2
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
infra/bgp-distribution/pom.xml
infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java
infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/LocRibWriterProvider.java [new file with mode: 0644]
infra/bgp-translate-api/pom.xml
infra/bgp-translate-impl/asciidoc/Readme.adoc [new file with mode: 0644]
infra/bgp-translate-impl/pom.xml [new file with mode: 0644]
infra/bgp-translate-impl/src/main/java/io/fd/honeycomb/bgp/translate/impl/LocRibChangeListener.java [new file with mode: 0644]
infra/bgp-translate-impl/src/main/java/io/fd/honeycomb/bgp/translate/impl/LocRibWriter.java [new file with mode: 0644]
infra/bgp-translate-impl/src/main/test/java/io/fd/honeycomb/bgp/translate/impl/LocRibChangeListenerTest.java [new file with mode: 0644]
infra/bgp-translate-impl/src/main/test/java/io/fd/honeycomb/bgp/translate/impl/LocRibWriterTest.java [new file with mode: 0644]
infra/pom.xml

index c70d030..62bc957 100644 (file)
             <artifactId>minimal-distribution</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>io.fd.honeycomb</groupId>
+            <artifactId>bgp-translate-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.honeycomb</groupId>
+            <artifactId>bgp-translate-impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
 
         <!-- ODL-BGP -->
         <dependency>
index efef5a7..a26b5c1 100644 (file)
@@ -25,6 +25,7 @@ import com.google.inject.name.Names;
 import io.fd.honeycomb.infra.distro.data.BindingDataBrokerProvider;
 import io.fd.honeycomb.infra.distro.data.DataStoreProvider;
 import io.fd.honeycomb.infra.distro.data.InmemoryDOMDataBrokerProvider;
+import io.fd.honeycomb.translate.bgp.RibWriter;
 import io.netty.channel.EventLoopGroup;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@@ -61,6 +62,10 @@ public final class BgpModule extends PrivateModule {
         // Initialize BgpNeighbours
         bind(BgpNeighbors.class).toProvider(BgpNeighboursProvider.class).in(Singleton.class);
         expose(BgpNeighbors.class);
+
+        // Listens for local RIB modifications and passes routes to translation layer
+        bind(RibWriter.class).toProvider(LocRibWriterProvider.class).asEagerSingleton();
+        expose(RibWriter.class);
     }
 
     private void configureRIB() {
diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/LocRibWriterProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/LocRibWriterProvider.java
new file mode 100644 (file)
index 0000000..1fc6b25
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.infra.bgp;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import io.fd.honeycomb.bgp.translate.impl.LocRibWriter;
+import io.fd.honeycomb.infra.distro.ProviderTrait;
+import io.fd.honeycomb.translate.bgp.RouteWriterFactory;
+import java.util.HashSet;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+
+final class LocRibWriterProvider extends ProviderTrait<LocRibWriter> {
+
+    @Inject
+    @Named(BgpModule.HONEYCOMB_BGP)
+    private DataBroker bgpDataBroker;
+    @Inject(optional = true)
+    private Set<RouteWriterFactory> writerFactories = new HashSet<>();
+
+    @Override
+    protected LocRibWriter create() {
+        final LocRibWriter registry = new LocRibWriter(bgpDataBroker);
+        writerFactories.stream().forEach(factory -> factory.init(registry));
+        return registry;
+    }
+}
index ff214c0..ce15289 100644 (file)
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2017 Cisco and/or its affiliates.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at:
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
 <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">
diff --git a/infra/bgp-translate-impl/asciidoc/Readme.adoc b/infra/bgp-translate-impl/asciidoc/Readme.adoc
new file mode 100644 (file)
index 0000000..df50659
--- /dev/null
@@ -0,0 +1,11 @@
+= bgp-translate-impl
+
+Provides simple implementation of route writers.
+
+LocRibWriter provides route translation for routes in local RIB.
+LocRibWriter registers DataTreeChangeListener for given route type to specific route writers.
+
+RouteWriters receive create/update/delete notifications for single route modifications
+in LocRib DS.
+
+RouteWriter can translata RIB update to any other format, e.g. device FIB.
\ No newline at end of file
diff --git a/infra/bgp-translate-impl/pom.xml b/infra/bgp-translate-impl/pom.xml
new file mode 100644 (file)
index 0000000..364e40b
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2017 Cisco and/or its affiliates.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at:
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<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">
+    <parent>
+        <groupId>io.fd.honeycomb.common</groupId>
+        <artifactId>impl-parent</artifactId>
+        <version>1.17.07-SNAPSHOT</version>
+        <relativePath>../../common/impl-parent</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>io.fd.honeycomb</groupId>
+    <artifactId>bgp-translate-impl</artifactId>
+    <name>${project.artifactId}</name>
+    <version>1.17.07-SNAPSHOT</version>
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-common-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.honeycomb</groupId>
+            <artifactId>bgp-translate-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- models used in tests -->
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>bgp-inet</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>bgp-labeled-unicast</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/infra/bgp-translate-impl/src/main/java/io/fd/honeycomb/bgp/translate/impl/LocRibChangeListener.java b/infra/bgp-translate-impl/src/main/java/io/fd/honeycomb/bgp/translate/impl/LocRibChangeListener.java
new file mode 100644 (file)
index 0000000..7e9a3c1
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.bgp.translate.impl;
+
+import com.google.common.base.Preconditions;
+import io.fd.honeycomb.translate.bgp.RouteWriter;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import java.util.Collection;
+import javax.annotation.Nonnull;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class LocRibChangeListener implements DataTreeChangeListener<Route> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(LocRibChangeListener.class);
+    private final RouteWriter writer;
+
+    LocRibChangeListener(final RouteWriter writer) {
+        this.writer = writer;
+    }
+
+    @Override
+    public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Route>> changes) {
+        for (DataTreeModification<Route> change : changes) {
+            final DataObjectModification<Route> rootNode = change.getRootNode();
+            final DataTreeIdentifier<Route> rootPath = change.getRootPath();
+            final Route dataBefore = rootNode.getDataBefore();
+            final Route dataAfter = rootNode.getDataAfter();
+            LOG.trace("Received LocRib change({}): before={} after={}", rootNode.getModificationType(), dataBefore, dataAfter);
+
+            try {
+                processChange(rootPath.getRootIdentifier(), dataBefore, dataAfter);
+            } catch (WriteFailedException e) {
+                LOG.warn("Route translation failed", e);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void processChange(final InstanceIdentifier<Route> id, final Route dataBefore, final Route dataAfter) throws WriteFailedException {
+        if (isCreate(dataBefore, dataAfter)) {
+            writer.create(id, dataAfter);
+        } else if (isDelete(dataBefore, dataAfter)) {
+            writer.delete(id, dataBefore);
+        } else {
+            Preconditions.checkArgument(dataBefore != null && dataAfter != null, "No data to process");
+            writer.update(id, dataBefore, dataAfter);
+        }
+    }
+
+    private static boolean isCreate(final DataObject dataBefore, final DataObject dataAfter) {
+        return dataBefore == null && dataAfter != null;
+    }
+
+    private static boolean isDelete(final DataObject dataBefore, final DataObject dataAfter) {
+        return dataAfter == null && dataBefore != null;
+    }
+}
diff --git a/infra/bgp-translate-impl/src/main/java/io/fd/honeycomb/bgp/translate/impl/LocRibWriter.java b/infra/bgp-translate-impl/src/main/java/io/fd/honeycomb/bgp/translate/impl/LocRibWriter.java
new file mode 100644 (file)
index 0000000..ce3c0c7
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.bgp.translate.impl;
+
+import com.google.common.base.Preconditions;
+import io.fd.honeycomb.translate.bgp.RibWriter;
+import io.fd.honeycomb.translate.bgp.RouteWriter;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Provides route translation for routes in local RIB.
+ */
+public final class LocRibWriter implements RibWriter {
+    private final DataBroker bgpDataBroker;
+
+    public LocRibWriter(final DataBroker bgpDataBroker) {
+        this.bgpDataBroker = bgpDataBroker;
+    }
+
+    @Override
+    public void register(@Nonnull final RouteWriter writer) {
+        @SuppressWarnings("unchecked")
+        final InstanceIdentifier<Route> managedId = (InstanceIdentifier<Route>)writer.getManagedDataObjectType();
+        final Class routeType = managedId.getTargetType();
+        Preconditions.checkArgument(Route.class.isAssignableFrom(routeType),
+            "{} managed by {} is not subclass of Route", routeType, writer);
+        Preconditions.checkArgument(managedId.firstIdentifierOf(LocRib.class) != null,
+            "{} managed by {} does not contain LocRib.class", managedId, writer);
+        Preconditions.checkArgument(managedId.isWildcarded(),
+            "{} managed by {} should not contain route key", managedId, writer);
+
+        // TODO(HONEYCOMB-367): updates for whole list instead of list item
+        // are needed to support deleteALL (might be required for performance reasons).
+        bgpDataBroker
+            .registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, managedId),
+            new LocRibChangeListener(writer));
+    }
+}
diff --git a/infra/bgp-translate-impl/src/main/test/java/io/fd/honeycomb/bgp/translate/impl/LocRibChangeListenerTest.java b/infra/bgp-translate-impl/src/main/test/java/io/fd/honeycomb/bgp/translate/impl/LocRibChangeListenerTest.java
new file mode 100644 (file)
index 0000000..8418f01
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.bgp.translate.impl;
+
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
+import io.fd.honeycomb.translate.bgp.RouteWriter;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class LocRibChangeListenerTest {
+
+    private static final DataTreeIdentifier<Route> ID =
+        new DataTreeIdentifier<>(OPERATIONAL, InstanceIdentifier.create(Route.class));
+
+    @Mock
+    private RouteWriter<Route> routeWriter;
+    @Mock
+    private DataObjectModification<Route> rootNode;
+
+    private LocRibChangeListener locRibListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        locRibListener = new LocRibChangeListener(routeWriter);
+    }
+
+    @Test
+    public void testDataTreeChanged() throws WriteFailedException {
+        final Route route1 = Mockito.mock(Route.class);
+        final Route route2 = Mockito.mock(Route.class);
+        locRibListener.onDataTreeChanged(Arrays.asList(
+            mockDateTreeModification(null, route1),
+            mockDateTreeModification(route1, route2),
+            mockDateTreeModification(route2, null))
+        );
+        Mockito.verify(routeWriter).create(ID.getRootIdentifier(), route1);
+        Mockito.verify(routeWriter).update(ID.getRootIdentifier(), route1, route2);
+        Mockito.verify(routeWriter).delete(ID.getRootIdentifier(), route2);
+    }
+
+    @Test
+    public void testDataTreeChangedFailed() throws WriteFailedException.CreateFailedException {
+        final Route dataAfter = Mockito.mock(Route.class);
+        Mockito.doThrow(new WriteFailedException.CreateFailedException(ID.getRootIdentifier(), dataAfter))
+            .when(routeWriter)
+            .create(ArgumentMatchers.any(), ArgumentMatchers.any());
+        locRibListener.onDataTreeChanged(Collections.singletonList(mockDateTreeModification(null, dataAfter)));
+        Mockito.verify(routeWriter).create(ID.getRootIdentifier(), dataAfter);
+    }
+
+    @SuppressWarnings("unchecked")
+    private DataTreeModification<Route> mockDateTreeModification(final Route dataBefore, final Route dataAfter) {
+        final DataTreeModification<Route> modification = Mockito.mock(DataTreeModification.class);
+        final DataObjectModification<Route> rootNode = Mockito.mock(DataObjectModification.class);
+        Mockito.when(rootNode.getDataBefore()).thenReturn(dataBefore);
+        Mockito.when(rootNode.getDataAfter()).thenReturn(dataAfter);
+        Mockito.when(modification.getRootPath()).thenReturn(ID);
+        Mockito.when(modification.getRootNode()).thenReturn(rootNode);
+        return modification;
+    }
+}
\ No newline at end of file
diff --git a/infra/bgp-translate-impl/src/main/test/java/io/fd/honeycomb/bgp/translate/impl/LocRibWriterTest.java b/infra/bgp-translate-impl/src/main/test/java/io/fd/honeycomb/bgp/translate/impl/LocRibWriterTest.java
new file mode 100644 (file)
index 0000000..391ee4c
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.bgp.translate.impl;
+
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
+import io.fd.honeycomb.translate.bgp.RouteWriter;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+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.bgp.inet.rev150305.ipv4.routes.Ipv4Routes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.routes.ipv4.routes.Ipv4Route;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.routes.ipv4.routes.Ipv4RouteKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.labeled.unicast.rev150525.labeled.unicast.routes.LabeledUnicastRoutes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.labeled.unicast.rev150525.labeled.unicast.routes.list.LabeledUnicastRoute;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+
+public class LocRibWriterTest {
+
+    private static final InstanceIdentifier<Tables> TABLES = InstanceIdentifier.create(BgpRib.class).child(Rib.class)
+        .child(LocRib.class).child(Tables.class);
+
+    @SuppressWarnings("unchecked")
+    private static final KeyedInstanceIdentifier<Ipv4Route, Ipv4RouteKey>
+        SPECIFIC_IP4_ROUTE_ID =
+        InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(new RibId("some-rib"))).child(LocRib.class)
+            .child(Tables.class, new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class))
+            .child((Class) Ipv4Routes.class)
+            .child(Ipv4Route.class, new Ipv4RouteKey(new PathId(1L), new Ipv4Prefix("1.2.3.4/24")));
+
+    @SuppressWarnings("unchecked")
+    private static final InstanceIdentifier<Ipv4Route> IP4_ROUTE_ID =
+        TABLES.child((Class) Ipv4Routes.class).child(Ipv4Route.class);
+
+    @SuppressWarnings("unchecked")
+    private static final InstanceIdentifier<LabeledUnicastRoute> LABELED_IP4_ID =
+        TABLES.child((Class) LabeledUnicastRoutes.class).child(LabeledUnicastRoute.class);
+
+    @Mock
+    private DataBroker bgpDataBroker;
+    private LocRibWriter ribWriter;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ribWriter = new LocRibWriter(bgpDataBroker);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRegisterFailsForNonRoute() {
+        ribWriter.register(new NoopWriter(IP4_ROUTE_ID.child(Attributes.class)));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRegisterFailsForNonLocRibRoute() {
+        ribWriter.register(new NoopWriter(InstanceIdentifier.create(Ipv4Route.class)));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRegisterFailsForSpecificLocRibRoute() {
+        ribWriter.register(new NoopWriter(SPECIFIC_IP4_ROUTE_ID));
+    }
+
+    @Test
+    public void testRegisterIpv4RouteWriter() {
+        ribWriter.register(new NoopWriter(IP4_ROUTE_ID));
+        Mockito.verify(bgpDataBroker).registerDataTreeChangeListener(
+            ArgumentMatchers.eq(new DataTreeIdentifier<>(OPERATIONAL, IP4_ROUTE_ID)), ArgumentMatchers.any());
+    }
+
+    @Test
+    public void testRegisterLabeledIpv4RouteWriter() {
+        ribWriter.register(new NoopWriter(LABELED_IP4_ID));
+        Mockito.verify(bgpDataBroker).registerDataTreeChangeListener(
+            ArgumentMatchers.eq(new DataTreeIdentifier<>(OPERATIONAL, LABELED_IP4_ID)), ArgumentMatchers.any());
+    }
+
+    private static final class NoopWriter implements RouteWriter {
+        private final InstanceIdentifier<? extends Route> id;
+
+        private NoopWriter(@Nonnull final InstanceIdentifier id) {
+            this.id = id;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Nonnull
+        @Override
+        public InstanceIdentifier getManagedDataObjectType() {
+            return id;
+        }
+
+        @Override
+        public void create(@Nonnull final InstanceIdentifier id, @Nullable final Route dataAfter)
+            throws WriteFailedException.CreateFailedException {
+        }
+
+        @Override
+        public void delete(@Nonnull final InstanceIdentifier id, @Nullable final Route dataBefore)
+            throws WriteFailedException.DeleteFailedException {
+        }
+
+        @Override
+        public void update(@Nonnull final InstanceIdentifier id, @Nullable final Route dataBefore,
+                           @Nullable final Route dataAfter) throws WriteFailedException.UpdateFailedException {
+        }
+    }
+}
\ No newline at end of file
index da0a9f3..cfe99a7 100644 (file)
@@ -42,6 +42,7 @@
     <module>impl</module>
     <module>minimal-distribution</module>
     <module>bgp-translate-api</module>
+    <module>bgp-translate-impl</module>
     <module>bgp-distribution</module>
     <module>it</module>
     <module>test-utils</module>