HONEYCOMB-369: configurable BGP peers 91/7591/1
authorMarek Gradzki <mgradzki@cisco.com>
Thu, 13 Jul 2017 08:41:27 +0000 (10:41 +0200)
committerMarek Gradzki <mgradzki@cisco.com>
Mon, 17 Jul 2017 08:16:43 +0000 (08:16 +0000)
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)

18 files changed:
infra/bgp-distribution-test/asciidoc/Readme.adoc
infra/bgp-distribution-test/src/test/java/io/fd/honeycomb/infra/bgp/distro/BgpDistributionTest.java
infra/bgp-distribution-test/src/test/resources/bgp.json
infra/northbound/bgp/pom.xml
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/ApplicationRibWriterFactory.java [new file with mode: 0644]
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfiguration.java
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpNeighboursProvider.java [deleted file]
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWriterFactoryProvider.java [deleted file]
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWritersModule.java
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/BgpPeerWriterFactory.java [new file with mode: 0644]
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizer.java [new file with mode: 0644]
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizer.java [new file with mode: 0644]
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizer.java [new file with mode: 0644]
infra/northbound/bgp/src/main/resources/honeycomb-minimal-resources/config/bgp.json
infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizerTest.java [new file with mode: 0644]
infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizerTest.java [new file with mode: 0644]
infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizerTest.java [new file with mode: 0644]

index 25762d2..9576a61 100644 (file)
@@ -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
index a105b65..d102f76 100644 (file)
 
 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
index 5cc9a41..82838ff 100644 (file)
@@ -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
index 8f3c269..2831924 100644 (file)
             <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 (file)
index 0000000..104a859
--- /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.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)
+        );
+    }
+}
index 9335fb3..56a00ec 100644 (file)
@@ -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();
index dd6449d..3cab508 100644 (file)
@@ -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 (file)
index f79afc9..0000000
+++ /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 (file)
index f4d5b94..0000000
+++ /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)
-            );
-        }
-    }
-}
index b167029..589eeac 100644 (file)
@@ -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 (file)
index 0000000..064c70b
--- /dev/null
@@ -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 (file)
index 0000000..50ae45b
--- /dev/null
@@ -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 (file)
index 0000000..1505537
--- /dev/null
@@ -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 (file)
index 0000000..7e0dd8f
--- /dev/null
@@ -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"));
+    }
+}
index 093acb8..c61d83c 100644 (file)
@@ -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 (file)
index 0000000..4f34851
--- /dev/null
@@ -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 (file)
index 0000000..abf0568
--- /dev/null
@@ -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 (file)
index 0000000..f3c3c2e
--- /dev/null
@@ -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