summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Gradzki <mgradzki@cisco.com>2017-07-13 10:41:27 +0200
committerMarek Gradzki <mgradzki@cisco.com>2017-07-17 08:16:43 +0000
commitd2664a4f307a833e293b2dfbe44d9ab6579eef2d (patch)
treeea8d05794c7d110f7a15d18874678925db44e230
parenta4a488f7cdd04498bfcda068bf171516a9afe4f0 (diff)
HONEYCOMB-369: configurable BGP peers
BGP peer configuration is no longer read from bgp-peer.json file. Netconf/Restconf is can be used instead. BGP peer configuration in HC follows openconfig-extensions model (as in ODL BGP): * http://docs.opendaylight.org/en/stable-boron/user-guide/bgp-user-guide.html#bgp-peering * http://docs.opendaylight.org/en/stable-boron/user-guide/bgp-user-guide.html#bgp-application-peer-and-programmable-rib Change-Id: I91aa6c4fc0923edbacf6cd10abd3957569a4f8c6 Signed-off-by: Marek Gradzki <mgradzki@cisco.com> (cherry picked from commit a3d562afdd96d4c37fe608af99f364e879ee92b6)
-rw-r--r--infra/bgp-distribution-test/asciidoc/Readme.adoc4
-rw-r--r--infra/bgp-distribution-test/src/test/java/io/fd/honeycomb/infra/bgp/distro/BgpDistributionTest.java61
-rw-r--r--infra/bgp-distribution-test/src/test/resources/bgp.json1
-rw-r--r--infra/northbound/bgp/pom.xml12
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/ApplicationRibWriterFactory.java89
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfiguration.java3
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java13
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpNeighboursProvider.java129
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWriterFactoryProvider.java102
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWritersModule.java19
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/BgpPeerWriterFactory.java110
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizer.java139
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizer.java70
-rw-r--r--infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizer.java75
-rw-r--r--infra/northbound/bgp/src/main/resources/honeycomb-minimal-resources/config/bgp.json1
-rw-r--r--infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizerTest.java124
-rw-r--r--infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizerTest.java67
-rw-r--r--infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizerTest.java74
18 files changed, 837 insertions, 256 deletions
diff --git a/infra/bgp-distribution-test/asciidoc/Readme.adoc b/infra/bgp-distribution-test/asciidoc/Readme.adoc
index 25762d2..9576a61 100644
--- a/infra/bgp-distribution-test/asciidoc/Readme.adoc
+++ b/infra/bgp-distribution-test/asciidoc/Readme.adoc
@@ -1,6 +1,6 @@
= bgp-distribution-test
-Distribution tests use generated files(yang-module-index)
+Distribution tests use generated files (yang-module-index)
that are generated after the build phase that maven invoke unit test.
Therefore these tests must be part or separate project that uses distribution
-as dependency that has these files allready generated. \ No newline at end of file
+as dependency that has these files already generated. \ No newline at end of file
diff --git a/infra/bgp-distribution-test/src/test/java/io/fd/honeycomb/infra/bgp/distro/BgpDistributionTest.java b/infra/bgp-distribution-test/src/test/java/io/fd/honeycomb/infra/bgp/distro/BgpDistributionTest.java
index a105b65..d102f76 100644
--- a/infra/bgp-distribution-test/src/test/java/io/fd/honeycomb/infra/bgp/distro/BgpDistributionTest.java
+++ b/infra/bgp-distribution-test/src/test/java/io/fd/honeycomb/infra/bgp/distro/BgpDistributionTest.java
@@ -16,14 +16,21 @@
package io.fd.honeycomb.infra.bgp.distro;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Charsets;
import com.google.common.io.ByteStreams;
+import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
+import com.mashape.unirest.http.exceptions.UnirestException;
import io.fd.honeycomb.infra.distro.Main;
import io.fd.honeycomb.infra.distro.activation.ActivationModule;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
@@ -37,6 +44,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BgpDistributionTest {
+ private static final String BGP_HOST_ADDRESS = "127.0.0.1";
+ private static final int HTTP_PORT = 8182;
+ private static final String UNAME = "admin";
+ private static final String PASSWORD = "admin";
private static final Logger LOG = LoggerFactory.getLogger(BgpDistributionTest.class);
private static final String CERT_PASSWORD = "testing";
@@ -67,18 +78,38 @@ public class BgpDistributionTest {
assertBgp();
}
- private byte[] readMessage(final InputStream inputStream) throws IOException {
- final int available = inputStream.available();
- final byte[] msg = new byte[available];
- ByteStreams.read(inputStream, msg, 0, available);
- return msg;
- }
-
private void assertBgp() throws Exception {
// Wait until BGP server is started
Thread.sleep(HELLO_WAIT);
- final InetAddress bgpHost = InetAddress.getByName("127.0.0.1");
- final InetAddress bgpPeerAddress = InetAddress.getByName("127.0.0.2");
+
+ configureBgpPeers();
+
+ assertBgpOpenIsSent("127.0.0.2");
+ assertBgpOpenIsSent("127.0.0.3");
+ }
+
+ private void configureBgpPeers() throws UnirestException, IOException {
+ final String url =
+ "http://" + BGP_HOST_ADDRESS + ":" + HTTP_PORT
+ + "/restconf/config/openconfig-network-instance:network-instances/network-instance/global-bgp/"
+ + "openconfig-network-instance:protocols/protocol/openconfig-policy-types:BGP/hc-bgp-instance/"
+ + "bgp/bgp-openconfig-extensions:neighbors";
+
+ final String request =
+ new String(Files.readAllBytes(Paths.get("src/test/resources/bgp-peers.json")), Charsets.UTF_8);
+ final HttpResponse<String> response =
+ Unirest.put(url)
+ .basicAuth(UNAME, PASSWORD)
+ .header("Content-Type", "application/json")
+ .body(request)
+ .asString();
+
+ assertSuccessStatus(response);
+ }
+
+ private void assertBgpOpenIsSent(final String peerAddress) throws IOException, InterruptedException {
+ final InetAddress bgpHost = InetAddress.getByName(BGP_HOST_ADDRESS);
+ final InetAddress bgpPeerAddress = InetAddress.getByName(peerAddress);
try (final Socket localhost = new Socket(bgpHost, BGP_PORT, bgpPeerAddress, 0);
final InputStream inputStream = localhost.getInputStream()) {
// Wait until bgp message is sent
@@ -90,4 +121,16 @@ public class BgpDistributionTest {
Assert.assertEquals(BGP_OPEN_MSG_TYPE, msg[BGP_MSG_TYPE_OFFSET]);
}
}
+
+ private byte[] readMessage(final InputStream inputStream) throws IOException {
+ final int available = inputStream.available();
+ final byte[] msg = new byte[available];
+ ByteStreams.read(inputStream, msg, 0, available);
+ return msg;
+ }
+
+ private void assertSuccessStatus(final HttpResponse<String> jsonNodeHttpResponse) {
+ assertTrue(jsonNodeHttpResponse.getStatus() >= 200);
+ assertTrue(jsonNodeHttpResponse.getStatus() < 400);
+ }
} \ No newline at end of file
diff --git a/infra/bgp-distribution-test/src/test/resources/bgp.json b/infra/bgp-distribution-test/src/test/resources/bgp.json
index 5cc9a41..82838ff 100644
--- a/infra/bgp-distribution-test/src/test/resources/bgp.json
+++ b/infra/bgp-distribution-test/src/test/resources/bgp.json
@@ -4,6 +4,7 @@
"bgp-as-number": 65000,
"bgp-receive-multiple-paths": "true",
"bgp-send-max-paths": 0,
+ "bgp-network-instance-name": "global-bgp",
"bgp-protocol-instance-name": "hc-bgp-instance",
"bgp-netty-threads": 2
} \ No newline at end of file
diff --git a/infra/northbound/bgp/pom.xml b/infra/northbound/bgp/pom.xml
index 8f3c269..2831924 100644
--- a/infra/northbound/bgp/pom.xml
+++ b/infra/northbound/bgp/pom.xml
@@ -111,6 +111,18 @@
<groupId>org.opendaylight.bgpcep</groupId>
<artifactId>bgp-l3vpn</artifactId>
</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>
</dependencies>
</project> \ No newline at end of file
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/ApplicationRibWriterFactory.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/ApplicationRibWriterFactory.java
new file mode 100644
index 0000000..104a859
--- /dev/null
+++ b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/ApplicationRibWriterFactory.java
@@ -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.infra.bgp;
+
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import io.fd.honeycomb.translate.util.write.BindingBrokerWriter;
+import io.fd.honeycomb.translate.write.WriterFactory;
+import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+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.labeled.unicast.rev150525.labeled.unicast.LabelStack;
+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.path.attributes.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.LocalPref;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.Origin;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.ApplicationRib;
+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.types.rev130919.next.hop.c.next.hop.ipv4.next.hop._case.Ipv4NextHop;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * {@link WriterFactory} for BGP Application RIB write integration with HC writer registry.
+ * Uses BindingBrokerWriter to write routes via dedicated broker that, unlike
+ * {@link io.fd.honeycomb.data.impl.DataBroker DataBroker}, supports tx chains and DOMDataChangeListener registration
+ * extensively used by ODL's bgp.
+ *
+ * As a bonus BGP routes persisted and available for read via RESTCONF/NETCONF.
+ */
+final class ApplicationRibWriterFactory implements WriterFactory {
+ @Inject
+ @Named(BgpModule.HONEYCOMB_BGP)
+ private DataBroker dataBroker;
+
+ private static final InstanceIdentifier<ApplicationRib> AR_IID =
+ InstanceIdentifier.create(ApplicationRib.class);
+ private static final InstanceIdentifier<Tables> TABLES_IID = AR_IID.child(Tables.class);
+ private static final InstanceIdentifier<Ipv4Routes> IPV4_ROUTES_IID = TABLES_IID.child((Class) Ipv4Routes.class);
+ private static final InstanceIdentifier<Ipv4Route> IPV4_ROUTE_IID = IPV4_ROUTES_IID.child(Ipv4Route.class);
+ private static final InstanceIdentifier<LabeledUnicastRoutes> LABELED_UNICAST_ROUTES_IID = TABLES_IID.child((Class) LabeledUnicastRoutes.class);
+ private static final InstanceIdentifier<LabeledUnicastRoute> LABELED_UNICAST_ROUTE_IID = LABELED_UNICAST_ROUTES_IID.child(LabeledUnicastRoute.class);
+
+ // TODO (HONEYCOMB-359):
+ // BGP models are huge, we need some kind of wildcarded subtree writer, that works for whole subtree.
+ // 1) we can either move checking handledTypes to writers (getHandledTypes, isAffected, writer.getHandedTypes, ...)
+ // but then precondition check in flatWriterRegistry might be slower (we need to check if we have all writers
+ // in order to avoid unnecessary reverts).
+ //
+ // 2) alternative is to compute all child nodes during initialization (might introduce some footprint penalty).
+ @Override
+ public void init(final ModifiableWriterRegistryBuilder registry) {
+ registry.subtreeAdd(
+ Sets.newHashSet(
+ TABLES_IID,
+ IPV4_ROUTES_IID,
+ IPV4_ROUTES_IID.child(Ipv4Route.class),
+ IPV4_ROUTE_IID.child(Attributes.class),
+ IPV4_ROUTE_IID.child(Attributes.class).child(Origin.class),
+ IPV4_ROUTE_IID.child(Attributes.class).child(LocalPref.class),
+ IPV4_ROUTE_IID.child(Attributes.class).child(Ipv4NextHop.class),
+ LABELED_UNICAST_ROUTES_IID,
+ LABELED_UNICAST_ROUTE_IID,
+ LABELED_UNICAST_ROUTE_IID.child(Attributes.class),
+ LABELED_UNICAST_ROUTE_IID.child(Attributes.class).child(Origin.class),
+ LABELED_UNICAST_ROUTE_IID.child(Attributes.class).child(LocalPref.class),
+ LABELED_UNICAST_ROUTE_IID.child(Attributes.class).child(Ipv4NextHop.class),
+ LABELED_UNICAST_ROUTE_IID.child(LabelStack.class)
+ ),
+ new BindingBrokerWriter<>(InstanceIdentifier.create(ApplicationRib.class), dataBroker)
+ );
+ }
+}
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfiguration.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfiguration.java
index 9335fb3..56a00ec 100644
--- a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfiguration.java
+++ b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfiguration.java
@@ -45,6 +45,8 @@ public class BgpConfiguration {
public Optional<String> bgpMultiplePaths;
@InjectConfig("bgp-send-max-paths")
public Optional<Integer> bgpSendMaxMaths;
+ @InjectConfig("bgp-network-instance-name")
+ public String bgpNetworkInstanceName;
@InjectConfig("bgp-protocol-instance-name")
public Optional<String> bgpProtocolInstanceName;
@InjectConfig("bgp-netty-threads")
@@ -57,6 +59,7 @@ public class BgpConfiguration {
.add("bgpAsNumber", bgpAsNumber)
.add("bgpMultiplePaths", bgpMultiplePaths)
.add("bgpSendMaxMaths", bgpSendMaxMaths)
+ .add("bgpNetworkInstanceName", bgpNetworkInstanceName)
.add("bgpProtocolInstanceName", bgpProtocolInstanceName)
.add("bgpNettyThreads", bgpNettyThreads)
.toString();
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java
index dd6449d..3cab508 100644
--- a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java
+++ b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java
@@ -31,13 +31,8 @@ import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
-import org.opendaylight.protocol.bgp.openconfig.impl.BGPOpenConfigMappingServiceImpl;
-import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigMappingService;
-import org.opendaylight.protocol.bgp.rib.impl.StrictBGPPeerRegistry;
import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
-import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.BgpNeighbors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,16 +49,9 @@ public final class BgpModule extends PrivateModule {
configureRIB();
- // Configure peer registry
- bind(BGPOpenConfigMappingService.class).toInstance(new BGPOpenConfigMappingServiceImpl());
- bind(BGPPeerRegistry.class).toInstance(StrictBGPPeerRegistry.instance());
-
// Create BGP server instance (initialize eagerly to start BGP)
bind(BgpServerProvider.BgpServer.class).toProvider(BgpServerProvider.class).asEagerSingleton();
- // Initialize BgpNeighbours (initialize eagerly to start BGP neighbours)
- bind(BgpNeighbors.class).toProvider(BgpNeighboursProvider.class).asEagerSingleton();
-
// Listens for local RIB modifications and passes routes to translation layer
// (initialize eagerly to configure RouteWriters)
bind(RibWriter.class).toProvider(LocRibWriterProvider.class).asEagerSingleton();
@@ -96,5 +84,6 @@ public final class BgpModule extends PrivateModule {
// Create RIB instance
bind(RIB.class).toProvider(BgpRIBProvider.class).in(Singleton.class);
+ expose(RIB.class);
}
}
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpNeighboursProvider.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpNeighboursProvider.java
deleted file mode 100644
index f79afc9..0000000
--- a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpNeighboursProvider.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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 static com.google.common.base.Preconditions.checkNotNull;
-import static io.fd.honeycomb.translate.util.JsonUtils.readContainerEntryJson;
-import static org.opendaylight.protocol.bgp.openconfig.impl.util.OpenConfigUtil.APPLICATION_PEER_GROUP_NAME;
-import static org.opendaylight.yangtools.sal.binding.generator.impl.BindingSchemaContextUtils.findDataNodeContainer;
-
-import com.google.common.base.Optional;
-import com.google.inject.Inject;
-import io.fd.honeycomb.binding.init.ProviderTrait;
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
-import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigMappingService;
-import org.opendaylight.protocol.bgp.rib.impl.config.AppPeer;
-import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
-import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
-import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.BgpNeighborPeerGroupConfig;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.BgpNeighbors;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.Neighbor;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.NeighborsBuilder;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceKey;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.ProtocolKey;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.policy.types.rev151009.BGP;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Config2;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Protocol1;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-final class BgpNeighboursProvider extends ProviderTrait<BgpNeighbors> {
- private static final Logger LOG = LoggerFactory.getLogger(BgpNeighboursProvider.class);
- private static final String PEERS_CFG = "/bgp-peers.json";
- @Inject
- private BindingToNormalizedNodeCodec codec;
- @Inject
- private RIB globalRib;
- @Inject
- private BGPOpenConfigMappingService mappingService;
- @Inject
- private SchemaService schemaService;
- @Inject
- private BGPPeerRegistry peerRegistry;
-
- @Override
- protected BgpNeighbors create() {
- LOG.info("Initializing BgpNeighbours");
- final BgpNeighbors neighbors = readNeighbours();
- for (final Neighbor neighbor : neighbors.getNeighbor()) {
- if (isApplicationPeer(neighbor)) {
- LOG.trace("Starting AppPeer for {}", neighbor);
- new AppPeer().start(globalRib, neighbor, mappingService, null);
- } else {
- LOG.trace("Starting BgpPeer for {}", neighbor);
- new BgpPeer(null, peerRegistry).start(globalRib, neighbor, mappingService, null);
- }
- }
- LOG.debug("BgpNeighbours initialized: {}", neighbors);
- return neighbors;
- }
-
- private Neighbors readNeighbours() {
- LOG.debug("Reading BGP neighbours from {}", PEERS_CFG);
- final InputStream resourceStream = this.getClass().getResourceAsStream(PEERS_CFG);
- if (resourceStream == null) {
- LOG.warn("Unable to open {}. Skipping BGP neighbour configuration.", PEERS_CFG);
- return new NeighborsBuilder().setNeighbor(Collections.emptyList()).build();
- }
-
- final InstanceIdentifier<Bgp> bgpII = InstanceIdentifier.create(NetworkInstances.class)
- .child(NetworkInstance.class, new NetworkInstanceKey("dummy-value")).child(Protocols.class)
- .child(Protocol.class, new ProtocolKey(BGP.class, "dummy-value")).augmentation(Protocol1.class)
- .child(Bgp.class);
- final InstanceIdentifier<Neighbors> neighborsII = bgpII.child(Neighbors.class);
-
- final YangInstanceIdentifier neighborsYII = codec.toYangInstanceIdentifier(neighborsII);
- final SchemaContext schemaContext = schemaService.getGlobalContext();
- final Optional<DataNodeContainer> parentNode = findDataNodeContainer(schemaContext, bgpII);
- final ContainerNode parentContainer = readContainerEntryJson(schemaContext, resourceStream,
- (SchemaNode) parentNode.get(),
- (YangInstanceIdentifier.NodeIdentifier) neighborsYII.getLastPathArgument());
- final NormalizedNode<?, ?> neighborsContainer = parentContainer.getValue().iterator().next();
-
- final Map.Entry<InstanceIdentifier<?>, DataObject> entry = codec.fromNormalizedNode(neighborsYII, neighborsContainer);
- checkNotNull(entry, "Failed to deserialize neighbours configuration at %s", PEERS_CFG);
- return (Neighbors) entry.getValue();
- }
-
- private static boolean isApplicationPeer(@Nonnull final Neighbor neighbor) {
- return java.util.Optional.of(neighbor.getConfig())
- .map(config -> config.getAugmentation(Config2.class))
- .map(BgpNeighborPeerGroupConfig::getPeerGroup)
- .map(APPLICATION_PEER_GROUP_NAME::equals)
- .orElse(false);
- }
-}
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWriterFactoryProvider.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWriterFactoryProvider.java
deleted file mode 100644
index f4d5b94..0000000
--- a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWriterFactoryProvider.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.common.collect.Sets;
-import com.google.inject.Inject;
-import com.google.inject.name.Named;
-import io.fd.honeycomb.binding.init.ProviderTrait;
-import io.fd.honeycomb.translate.util.write.BindingBrokerWriter;
-import io.fd.honeycomb.translate.write.WriterFactory;
-import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-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.labeled.unicast.rev150525.labeled.unicast.LabelStack;
-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.path.attributes.Attributes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.LocalPref;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.Origin;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.ApplicationRib;
-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.types.rev130919.next.hop.c.next.hop.ipv4.next.hop._case.Ipv4NextHop;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-final class BgpWriterFactoryProvider extends ProviderTrait<WriterFactory> {
- @Inject
- @Named(BgpModule.HONEYCOMB_BGP)
- private DataBroker bgpDataBroker;
- @Override
- protected BgpWriterFactory create() {
- return new BgpWriterFactory(bgpDataBroker);
- }
-
- /**
- * {@link WriterFactory} for BGP cfg write integration with HC writer registry.
- * Using BindingBrokerWriter to write BGP configuration data via dedicated broker that, unlike
- * {@link io.fd.honeycomb.data.impl.DataBroker}, supports tx chains and DOMDataChangeListener registration
- * extensively used by ODL's bgp.
- *
- * As a bonus BGP route configuration is persisted and available for read via RESTCONF/NETCONF.
- */
- private static final class BgpWriterFactory implements WriterFactory {
- private final DataBroker dataBroker;
-
- private static final InstanceIdentifier<ApplicationRib> AR_IID =
- InstanceIdentifier.create(ApplicationRib.class);
- private static final InstanceIdentifier<Tables> TABLES_IID = AR_IID.child(Tables.class);
- private static final InstanceIdentifier<Ipv4Routes> IPV4_ROUTES_IID = TABLES_IID.child((Class) Ipv4Routes.class);
- private static final InstanceIdentifier<Ipv4Route> IPV4_ROUTE_IID = IPV4_ROUTES_IID.child(Ipv4Route.class);
- private static final InstanceIdentifier<LabeledUnicastRoutes> LABELED_UNICAST_ROUTES_IID = TABLES_IID.child((Class) LabeledUnicastRoutes.class);
- private static final InstanceIdentifier<LabeledUnicastRoute> LABELED_UNICAST_ROUTE_IID = LABELED_UNICAST_ROUTES_IID.child(LabeledUnicastRoute.class);
-
- private BgpWriterFactory(final DataBroker dataBroker) {
- this.dataBroker = dataBroker;
- }
-
- // TODO (HONEYCOMB-359):
- // BGP models are huge, we need some kind of wildcarded subtree writer, that works for whole subtree.
- // 1) we can either move checking handledTypes to writers (getHandledTypes, isAffected, writer.getHandedTypes, ...)
- // but then precondition check in flatWriterRegistry might be slower (we need to check if we have all writers
- // in order to avoid unnecessary reverts).
- //
- // 2) alternative is to compute all child nodes during initialization (might introduce some footprint penalty).
- @Override
- public void init(final ModifiableWriterRegistryBuilder registry) {
- registry.subtreeAdd(
- Sets.newHashSet(
- TABLES_IID,
- IPV4_ROUTES_IID,
- IPV4_ROUTES_IID.child(Ipv4Route.class),
- IPV4_ROUTE_IID.child(Attributes.class),
- IPV4_ROUTE_IID.child(Attributes.class).child(Origin.class),
- IPV4_ROUTE_IID.child(Attributes.class).child(LocalPref.class),
- IPV4_ROUTE_IID.child(Attributes.class).child(Ipv4NextHop.class),
- LABELED_UNICAST_ROUTES_IID,
- LABELED_UNICAST_ROUTE_IID,
- LABELED_UNICAST_ROUTE_IID.child(Attributes.class),
- LABELED_UNICAST_ROUTE_IID.child(Attributes.class).child(Origin.class),
- LABELED_UNICAST_ROUTE_IID.child(Attributes.class).child(LocalPref.class),
- LABELED_UNICAST_ROUTE_IID.child(Attributes.class).child(Ipv4NextHop.class),
- LABELED_UNICAST_ROUTE_IID.child(LabelStack.class)
- ),
- new BindingBrokerWriter<>(InstanceIdentifier.create(ApplicationRib.class), dataBroker)
- );
- }
- }
-}
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWritersModule.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWritersModule.java
index b167029..589eeac 100644
--- a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWritersModule.java
+++ b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWritersModule.java
@@ -19,19 +19,34 @@ package io.fd.honeycomb.infra.bgp;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import com.google.inject.multibindings.Multibinder;
+import io.fd.honeycomb.infra.bgp.neighbors.BgpPeerWriterFactory;
import io.fd.honeycomb.translate.write.WriterFactory;
+import org.opendaylight.protocol.bgp.openconfig.impl.BGPOpenConfigMappingServiceImpl;
+import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigMappingService;
+import org.opendaylight.protocol.bgp.rib.impl.StrictBGPPeerRegistry;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * Provides integration of various BGP components with WriterRegistry
+ * in order to enable configuration updates/read via RESTCONF/NETCONF.
+ */
public final class BgpWritersModule extends AbstractModule {
private static final Logger LOG = LoggerFactory.getLogger(BgpWritersModule.class);
protected void configure() {
- LOG.debug("Initializing BgpReadersModule");
+ LOG.debug("Initializing BgpWritersModule");
// This should be part of BgpModule, but that one is Private and Multibinders + private BASE_MODULES
// do not work together, that's why there's a dedicated module here
// https://github.com/google/guice/issues/906
+
+ // Configure peer registry
+ bind(BGPOpenConfigMappingService.class).toInstance(new BGPOpenConfigMappingServiceImpl());
+ bind(BGPPeerRegistry.class).toInstance(StrictBGPPeerRegistry.instance());
+
final Multibinder<WriterFactory> binder = Multibinder.newSetBinder(binder(), WriterFactory.class);
- binder.addBinding().toProvider(BgpWriterFactoryProvider.class).in(Singleton.class);
+ binder.addBinding().to(ApplicationRibWriterFactory.class).in(Singleton.class);
+ binder.addBinding().to(BgpPeerWriterFactory.class).in(Singleton.class);
}
}
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/BgpPeerWriterFactory.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/BgpPeerWriterFactory.java
new file mode 100644
index 0000000..064c70b
--- /dev/null
+++ b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/BgpPeerWriterFactory.java
@@ -0,0 +1,110 @@
+/*
+ * 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.neighbors;
+
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+import io.fd.honeycomb.infra.bgp.BgpConfiguration;
+import io.fd.honeycomb.translate.impl.write.GenericListWriter;
+import io.fd.honeycomb.translate.write.WriterFactory;
+import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
+import javax.annotation.Nonnull;
+import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigMappingService;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
+import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.multiprotocol.rev151009.bgp.common.afi.safi.list.AfiSafi;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.AfiSafis;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.Config;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.Timers;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.Transport;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.Neighbor;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.AfiSafi1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Config1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Config2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Protocol1;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Initializes writer for Bgp Neighbors ({@link Neighbor} node) and all its parents required by HC infra.
+ */
+public final class BgpPeerWriterFactory implements WriterFactory {
+ private static final InstanceIdentifier<NetworkInstance> NETWORK_INSTANCE_ID =
+ InstanceIdentifier.create(NetworkInstances.class)
+ .child(NetworkInstance.class);
+
+ private static final InstanceIdentifier<Protocol> PROTOCOL_ID =
+ NETWORK_INSTANCE_ID.child(Protocols.class).child(Protocol.class);
+
+ private static final InstanceIdentifier<Neighbor> NEIGHBOR_ID =
+ PROTOCOL_ID.augmentation(Protocol1.class).child(Bgp.class).child(Neighbors.class).child(Neighbor.class);
+
+ @Inject
+ private BgpConfiguration configuration;
+ @Inject
+ private RIB globalRib;
+ @Inject
+ private BGPOpenConfigMappingService mappingService;
+ @Inject
+ private BGPPeerRegistry peerRegistry;
+
+ @Override
+ public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) {
+ // NetworkInstances
+ // NetworkInstance =
+ registry.add(new GenericListWriter<>(NETWORK_INSTANCE_ID,
+ new NetworkInstanceCustomizer(configuration.bgpNetworkInstanceName)));
+
+ // Protocols
+ // Protocol =
+ registry.add(
+ new GenericListWriter<>(PROTOCOL_ID, new ProtocolCustomizer(configuration.bgpProtocolInstanceName.get())));
+
+ // Protocol1 augmentation (from bgp-openconfig-extensions)
+ // Bgp
+ // Neighbors
+ // Neighbor=
+ final InstanceIdentifier<Neighbor> neighbor = InstanceIdentifier.create(Neighbor.class);
+ registry.subtreeAdd(
+ // TODO (HONEYCOMB-359): there might be more subnodes that needs to be handled
+ Sets.newHashSet(
+ neighbor.child(Config.class),
+ neighbor.child(Config.class).augmentation(Config2.class),
+ neighbor.child(AfiSafis.class),
+ neighbor.child(AfiSafis.class).child(AfiSafi.class),
+ neighbor.child(AfiSafis.class).child(AfiSafi.class).augmentation(AfiSafi1.class),
+ neighbor.child(Timers.class),
+ neighbor.child(Timers.class).child(
+ org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.timers.Config.class),
+ neighbor.child(Transport.class),
+ neighbor.child(Transport.class).child(
+ org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.transport.Config.class),
+ neighbor.child(Transport.class).child(
+ org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.transport.Config.class)
+ .augmentation(Config1.class)
+ ),
+ new GenericListWriter<>(
+ NEIGHBOR_ID,
+ new NeighborCustomizer(globalRib, peerRegistry, mappingService)));
+ }
+}
+
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizer.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizer.java
new file mode 100644
index 0000000..50ae45b
--- /dev/null
+++ b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizer.java
@@ -0,0 +1,139 @@
+/*
+ * 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.neighbors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.opendaylight.protocol.bgp.openconfig.impl.util.OpenConfigUtil.APPLICATION_PEER_GROUP_NAME;
+
+import com.google.common.annotations.VisibleForTesting;
+import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigMappingService;
+import org.opendaylight.protocol.bgp.rib.impl.config.AppPeer;
+import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
+import org.opendaylight.protocol.bgp.rib.impl.config.PeerBean;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
+import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.BgpNeighborPeerGroupConfig;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.Neighbor;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.NeighborKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Config2;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Writer responsible for management of BGP Neighbors. Partially based on BgpDeployerImpl from ODL's BGP (was hard to
+ * use directly due to OSGI dependencies).
+ */
+@ThreadSafe
+final class NeighborCustomizer implements ListWriterCustomizer<Neighbor, NeighborKey> {
+ private static final Logger LOG = LoggerFactory.getLogger(NeighborCustomizer.class);
+ private final RIB globalRib;
+ private final BGPPeerRegistry peerRegistry;
+ private final BGPOpenConfigMappingService mappingService;
+
+ @GuardedBy("this")
+ private final Map<InstanceIdentifier<Neighbor>, PeerBean> peers = new HashMap<>();
+
+ public NeighborCustomizer(@Nonnull final RIB globalRib, @Nonnull final BGPPeerRegistry peerRegistry,
+ @Nonnull final BGPOpenConfigMappingService mappingService) {
+ this.globalRib = checkNotNull(globalRib, "globalRib should not be null");
+ this.peerRegistry = checkNotNull(peerRegistry, "globalRib should not be null");
+ this.mappingService = checkNotNull(mappingService, "globalRib should not be null");
+ }
+
+ @VisibleForTesting
+ synchronized void addPeer(@Nonnull final InstanceIdentifier<Neighbor> id,
+ @Nonnull final PeerBean peer) {
+ peers.put(id, peer);
+ }
+
+ @VisibleForTesting
+ synchronized boolean isPeerConfigured(@Nonnull final InstanceIdentifier<Neighbor> id) {
+ return peers.containsKey(id);
+ }
+
+ @Override
+ public synchronized void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Neighbor> id,
+ @Nonnull final Neighbor neighbor,
+ @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ final PeerBean peer;
+ if (isApplicationPeer(neighbor)) {
+ LOG.debug("Creating AppPeer bean for {}: {}", id, neighbor);
+ peer = new AppPeer();
+ } else {
+ LOG.debug("Starting BgpPeer bean for {}: {}", id, neighbor);
+ peer = new BgpPeer(null, peerRegistry);
+ }
+ LOG.debug("Starting bgp peer for {}", id);
+ peer.start(globalRib, neighbor, mappingService, null);
+ addPeer(id, peer);
+ }
+
+ @Override
+ public synchronized void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Neighbor> id,
+ @Nonnull final Neighbor dataBefore,
+ @Nonnull final Neighbor dataAfter,
+ @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ LOG.debug("Updating Peer instance {} with configuration: {}", id, dataAfter);
+ final PeerBean peer = peers.get(id);
+ checkState(peer != null, "Could not find peer bean while updating neighbor {}", id);
+ closePeerBean(peer);
+ peer.start(globalRib, dataAfter, mappingService, null);
+ LOG.debug("Peer instance updated {}", peer);
+ }
+
+ @Override
+ public synchronized void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Neighbor> id,
+ @Nonnull final Neighbor dataBefore,
+ @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ LOG.debug("Removing Peer instance: {}", id);
+ final PeerBean peer = peers.remove(id);
+ if (peer != null) {
+ closePeerBean(peer);
+ LOG.debug("Peer instance removed {}", peer);
+ }
+ }
+
+ private static boolean isApplicationPeer(@Nonnull final Neighbor neighbor) {
+ return java.util.Optional.of(neighbor.getConfig())
+ .map(config -> config.getAugmentation(Config2.class))
+ .map(BgpNeighborPeerGroupConfig::getPeerGroup)
+ .map(APPLICATION_PEER_GROUP_NAME::equals)
+ .orElse(false);
+ }
+
+ private static void closePeerBean(final PeerBean peer) {
+ try {
+ peer.closeServiceInstance().get();
+ } catch (final Exception e) {
+ LOG.error("Peer instance failed to close service instance", e);
+ }
+ peer.close();
+ }
+}
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizer.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizer.java
new file mode 100644
index 0000000..1505537
--- /dev/null
+++ b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizer.java
@@ -0,0 +1,70 @@
+/*
+ * 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.neighbors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Dummy customizer for handling network-instance node required by BGP peer configuration (HC implements the same model
+ * as ODL BGP).
+ *
+ * Ensures that at most one network instance is created.
+ *
+ * Update and delete is not supported, because HC does not support runtime BGP server reconfiguration.
+ */
+final class NetworkInstanceCustomizer implements ListWriterCustomizer<NetworkInstance, NetworkInstanceKey> {
+ private final String networkInstanceName;
+
+ NetworkInstanceCustomizer(@Nonnull final String networkInstanceName) {
+ this.networkInstanceName = checkNotNull(networkInstanceName, "network instance name should not be null");
+ }
+
+ @Override
+ public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<NetworkInstance> id,
+ @Nonnull final NetworkInstance dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final String instanceName = dataAfter.getName();
+ checkArgument(networkInstanceName.equals(instanceName),
+ "Only single network instance named %s is supported, but %s was given", networkInstanceName, instanceName);
+ }
+
+ @Override
+ public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<NetworkInstance> id,
+ @Nonnull final NetworkInstance dataBefore,
+ @Nonnull final NetworkInstance dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter,
+ new UnsupportedOperationException("Network instance update is not supported"));
+ }
+
+ @Override
+ public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<NetworkInstance> id,
+ @Nonnull final NetworkInstance dataBefore,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ throw new WriteFailedException.DeleteFailedException(id,
+ new UnsupportedOperationException("Network instance delete is not supported"));
+ }
+}
diff --git a/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizer.java b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizer.java
new file mode 100644
index 0000000..7e0dd8f
--- /dev/null
+++ b/infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizer.java
@@ -0,0 +1,75 @@
+/*
+ * 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.neighbors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.ProtocolKey;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.policy.types.rev151009.BGP;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.policy.types.rev151009.InstallProtocolType;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Dummy customizer for handling network-instance's protocol node required by BGP peer configuration (HC implements the
+ * same model as ODL BGP).
+ *
+ * Ensures that at most one protocol is created.
+ *
+ * Update and delete is not supported, because HC does not support runtime BGP server reconfiguration.
+ */
+final class ProtocolCustomizer implements ListWriterCustomizer<Protocol, ProtocolKey> {
+ private final String protocolInstanceName;
+
+ ProtocolCustomizer(@Nonnull final String protocolInstanceName) {
+ this.protocolInstanceName = checkNotNull(protocolInstanceName, "protocol instance name should not be null");
+ }
+
+ @Override
+ public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Protocol> id,
+ @Nonnull final Protocol dataAfter, @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ final String protocolName = dataAfter.getName();
+ checkArgument(protocolInstanceName.equals(protocolName),
+ "Only single protocol named %s is supported, but %s was given", protocolInstanceName, protocolName);
+
+ final Class<? extends InstallProtocolType> identifier = dataAfter.getIdentifier();
+ checkArgument(BGP.class.equals(identifier),
+ "Only BGP protocol type is supported, but %s was given", identifier);
+ }
+
+ @Override
+ public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Protocol> id,
+ @Nonnull final Protocol dataBefore, @Nonnull final Protocol dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter,
+ new UnsupportedOperationException("Network instance protocol update is not supported"));
+ }
+
+ @Override
+ public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Protocol> id,
+ @Nonnull final Protocol dataBefore, @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ throw new WriteFailedException.DeleteFailedException(id,
+ new UnsupportedOperationException("Network instance protocol delete is not supported"));
+ }
+}
diff --git a/infra/northbound/bgp/src/main/resources/honeycomb-minimal-resources/config/bgp.json b/infra/northbound/bgp/src/main/resources/honeycomb-minimal-resources/config/bgp.json
index 093acb8..c61d83c 100644
--- a/infra/northbound/bgp/src/main/resources/honeycomb-minimal-resources/config/bgp.json
+++ b/infra/northbound/bgp/src/main/resources/honeycomb-minimal-resources/config/bgp.json
@@ -4,6 +4,7 @@
"bgp-as-number": 65000,
"bgp-receive-multiple-paths": "true",
"bgp-send-max-paths": 0,
+ "bgp-network-instance-name": "global-bgp",
"bgp-protocol-instance-name": "hc-bgp-instance",
"bgp-netty-threads": 2
} \ No newline at end of file
diff --git a/infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizerTest.java b/infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizerTest.java
new file mode 100644
index 0000000..4f34851
--- /dev/null
+++ b/infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizerTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.neighbors;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+import static org.opendaylight.protocol.bgp.openconfig.impl.util.OpenConfigUtil.APPLICATION_PEER_GROUP_NAME;
+
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
+import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigMappingService;
+import org.opendaylight.protocol.bgp.rib.impl.config.PeerBean;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
+import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.ConfigBuilder;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.Neighbor;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.NeighborBuilder;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.NeighborKey;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.PeerType;
+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.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Config2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Config2Builder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public class NeighborCustomizerTest {
+ private static final IpAddress IP = new IpAddress(new Ipv4Address("10.25.1.9"));
+ private static final InstanceIdentifier<Neighbor> ID =
+ InstanceIdentifier.create(Neighbors.class).child(Neighbor.class, new NeighborKey(IP));
+
+ @Mock
+ private RIB globalRib;
+ @Mock
+ private BGPPeerRegistry peerRegistry;
+ @Mock
+ private BGPOpenConfigMappingService mappingService;
+ @Mock
+ private WriteContext ctx;
+
+ private NeighborCustomizer customizer;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ when(globalRib.getYangRibId()).thenReturn(YangInstanceIdentifier.EMPTY);
+ when(globalRib.getRibIServiceGroupIdentifier()).thenReturn(ServiceGroupIdentifier.create("sgid"));
+ customizer = new NeighborCustomizer(globalRib, peerRegistry, mappingService);
+ }
+
+ @Test
+ public void testAddAppPeer() throws WriteFailedException {
+ final Neighbor neighbor = new NeighborBuilder()
+ .setNeighborAddress(IP)
+ .setConfig(
+ new ConfigBuilder()
+ .addAugmentation(
+ Config2.class,
+ new Config2Builder().setPeerGroup(APPLICATION_PEER_GROUP_NAME).build()
+ ).build())
+ .build();
+ customizer.writeCurrentAttributes(ID, neighbor, ctx);
+ assertTrue(customizer.isPeerConfigured(ID));
+ }
+
+ @Test
+ public void testAddInternalPeer() throws WriteFailedException {
+ final Neighbor neighbor = new NeighborBuilder()
+ .setNeighborAddress(IP)
+ .setConfig(
+ new ConfigBuilder()
+ .setPeerType(PeerType.INTERNAL)
+ .build())
+ .build();
+ customizer.writeCurrentAttributes(ID, neighbor, ctx);
+ assertTrue(customizer.isPeerConfigured(ID));
+ }
+
+ @Test
+ public void testUpdate() throws WriteFailedException {
+ final PeerBean peer = mock(PeerBean.class);
+ customizer.addPeer(ID, peer);
+ final Neighbor before = mock(Neighbor.class);
+ final Neighbor after = mock(Neighbor.class);
+ customizer.updateCurrentAttributes(ID, before, after, ctx);
+ verify(peer).closeServiceInstance();
+ verify(peer).close();
+ verify(peer).start(globalRib, after, mappingService, null);
+ }
+
+ @Test
+ public void testDelete() throws WriteFailedException {
+ final PeerBean peer = mock(PeerBean.class);
+ customizer.addPeer(ID, peer);
+ final Neighbor before = mock(Neighbor.class);
+ customizer.deleteCurrentAttributes(ID, before, ctx);
+ verify(peer).closeServiceInstance();
+ verify(peer).close();
+ assertFalse(customizer.isPeerConfigured(ID));
+ }
+} \ No newline at end of file
diff --git a/infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizerTest.java b/infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizerTest.java
new file mode 100644
index 0000000..abf0568
--- /dev/null
+++ b/infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizerTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.neighbors;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class NetworkInstanceCustomizerTest {
+ private static final String INSTANCE_NAME = "test-instance";
+ private static final InstanceIdentifier<NetworkInstance> ID = InstanceIdentifier.create(NetworkInstance.class);
+
+ @Mock
+ private WriteContext ctx;
+
+ private NetworkInstanceCustomizer customizer;
+
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ customizer = new NetworkInstanceCustomizer(INSTANCE_NAME);
+ }
+
+ @Test
+ public void testWrite() throws WriteFailedException {
+ final NetworkInstance networkInstance = new NetworkInstanceBuilder().setName(INSTANCE_NAME).build();
+ customizer.writeCurrentAttributes(ID, networkInstance, ctx);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testWriteInvalidInstanceName() throws WriteFailedException {
+ final NetworkInstance networkInstance = new NetworkInstanceBuilder().setName("some-other-name").build();
+ customizer.writeCurrentAttributes(ID, networkInstance, ctx);
+ }
+
+ @Test(expected = WriteFailedException.UpdateFailedException.class)
+ public void testUpdate() throws WriteFailedException {
+ customizer.updateCurrentAttributes(ID, mock(NetworkInstance.class), mock(NetworkInstance.class), ctx);
+ }
+
+ @Test(expected = WriteFailedException.DeleteFailedException.class)
+ public void testDelete() throws WriteFailedException {
+ customizer.deleteCurrentAttributes(ID, mock(NetworkInstance.class), ctx);
+ }
+} \ No newline at end of file
diff --git a/infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizerTest.java b/infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizerTest.java
new file mode 100644
index 0000000..f3c3c2e
--- /dev/null
+++ b/infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizerTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.neighbors;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.ProtocolBuilder;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.policy.types.rev151009.BGP;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class ProtocolCustomizerTest {
+ private static final String INSTANCE_NAME = "test-instance";
+ private static final InstanceIdentifier<Protocol> ID = InstanceIdentifier.create(Protocol.class);
+
+ @Mock
+ private WriteContext ctx;
+
+ private ProtocolCustomizer customizer;
+
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ customizer = new ProtocolCustomizer(INSTANCE_NAME);
+ }
+
+ @Test
+ public void testWrite() throws WriteFailedException {
+ final Protocol protocol = new ProtocolBuilder().setName(INSTANCE_NAME).setIdentifier(BGP.class).build();
+ customizer.writeCurrentAttributes(ID, protocol, ctx);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testWriteInvalidProtocolName() throws WriteFailedException {
+ final Protocol protocol = new ProtocolBuilder().setName("some-other-name").setIdentifier(BGP.class).build();
+ customizer.writeCurrentAttributes(ID, protocol, ctx);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testWriteInvalidProtocolType() throws WriteFailedException {
+ final Protocol protocol = new ProtocolBuilder().setName(INSTANCE_NAME).build();
+ customizer.writeCurrentAttributes(ID, protocol, ctx);
+ }
+
+ @Test(expected = WriteFailedException.UpdateFailedException.class)
+ public void testUpdate() throws WriteFailedException {
+ customizer.updateCurrentAttributes(ID, mock(Protocol.class), mock(Protocol.class), ctx);
+ }
+
+ @Test(expected = WriteFailedException.DeleteFailedException.class)
+ public void testDelete() throws WriteFailedException {
+ customizer.deleteCurrentAttributes(ID, mock(Protocol.class), ctx);
+ }
+} \ No newline at end of file