From d2664a4f307a833e293b2dfbe44d9ab6579eef2d Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Thu, 13 Jul 2017 10:41:27 +0200 Subject: 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 (cherry picked from commit a3d562afdd96d4c37fe608af99f364e879ee92b6) --- infra/bgp-distribution-test/asciidoc/Readme.adoc | 4 +- .../infra/bgp/distro/BgpDistributionTest.java | 61 +++++++-- .../src/test/resources/bgp.json | 1 + infra/northbound/bgp/pom.xml | 12 ++ .../infra/bgp/ApplicationRibWriterFactory.java | 89 +++++++++++++ .../fd/honeycomb/infra/bgp/BgpConfiguration.java | 3 + .../java/io/fd/honeycomb/infra/bgp/BgpModule.java | 13 +- .../honeycomb/infra/bgp/BgpNeighboursProvider.java | 129 ------------------- .../infra/bgp/BgpWriterFactoryProvider.java | 102 --------------- .../fd/honeycomb/infra/bgp/BgpWritersModule.java | 19 ++- .../infra/bgp/neighbors/BgpPeerWriterFactory.java | 110 ++++++++++++++++ .../infra/bgp/neighbors/NeighborCustomizer.java | 139 +++++++++++++++++++++ .../bgp/neighbors/NetworkInstanceCustomizer.java | 70 +++++++++++ .../infra/bgp/neighbors/ProtocolCustomizer.java | 75 +++++++++++ .../honeycomb-minimal-resources/config/bgp.json | 1 + .../bgp/neighbors/NeighborCustomizerTest.java | 124 ++++++++++++++++++ .../neighbors/NetworkInstanceCustomizerTest.java | 67 ++++++++++ .../bgp/neighbors/ProtocolCustomizerTest.java | 74 +++++++++++ 18 files changed, 837 insertions(+), 256 deletions(-) create mode 100644 infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/ApplicationRibWriterFactory.java delete mode 100644 infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpNeighboursProvider.java delete mode 100644 infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/BgpWriterFactoryProvider.java create mode 100644 infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/BgpPeerWriterFactory.java create mode 100644 infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizer.java create mode 100644 infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizer.java create mode 100644 infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizer.java create mode 100644 infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NeighborCustomizerTest.java create mode 100644 infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/NetworkInstanceCustomizerTest.java create mode 100644 infra/northbound/bgp/src/test/java/io/fd/honeycomb/infra/bgp/neighbors/ProtocolCustomizerTest.java (limited to 'infra') diff --git a/infra/bgp-distribution-test/asciidoc/Readme.adoc b/infra/bgp-distribution-test/asciidoc/Readme.adoc index 25762d25f..9576a61b5 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 a105b6532..d102f76b4 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 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 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 5cc9a419c..82838ff43 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 8f3c26905..2831924d4 100644 --- a/infra/northbound/bgp/pom.xml +++ b/infra/northbound/bgp/pom.xml @@ -111,6 +111,18 @@ org.opendaylight.bgpcep bgp-l3vpn + + + + junit + junit + test + + + org.mockito + mockito-core + test + \ 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 000000000..104a85922 --- /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 AR_IID = + InstanceIdentifier.create(ApplicationRib.class); + private static final InstanceIdentifier TABLES_IID = AR_IID.child(Tables.class); + private static final InstanceIdentifier IPV4_ROUTES_IID = TABLES_IID.child((Class) Ipv4Routes.class); + private static final InstanceIdentifier IPV4_ROUTE_IID = IPV4_ROUTES_IID.child(Ipv4Route.class); + private static final InstanceIdentifier LABELED_UNICAST_ROUTES_IID = TABLES_IID.child((Class) LabeledUnicastRoutes.class); + private static final InstanceIdentifier 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 9335fb33d..56a00ecb0 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 bgpMultiplePaths; @InjectConfig("bgp-send-max-paths") public Optional bgpSendMaxMaths; + @InjectConfig("bgp-network-instance-name") + public String bgpNetworkInstanceName; @InjectConfig("bgp-protocol-instance-name") public Optional 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 dd6449d0d..3cab508b4 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 f79afc9a2..000000000 --- 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 { - 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 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 neighborsII = bgpII.child(Neighbors.class); - - final YangInstanceIdentifier neighborsYII = codec.toYangInstanceIdentifier(neighborsII); - final SchemaContext schemaContext = schemaService.getGlobalContext(); - final Optional 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, 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 f4d5b9416..000000000 --- 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 { - @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 AR_IID = - InstanceIdentifier.create(ApplicationRib.class); - private static final InstanceIdentifier TABLES_IID = AR_IID.child(Tables.class); - private static final InstanceIdentifier IPV4_ROUTES_IID = TABLES_IID.child((Class) Ipv4Routes.class); - private static final InstanceIdentifier IPV4_ROUTE_IID = IPV4_ROUTES_IID.child(Ipv4Route.class); - private static final InstanceIdentifier LABELED_UNICAST_ROUTES_IID = TABLES_IID.child((Class) LabeledUnicastRoutes.class); - private static final InstanceIdentifier 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 b16702968..589eeac7b 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 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 000000000..064c70b57 --- /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 NETWORK_INSTANCE_ID = + InstanceIdentifier.create(NetworkInstances.class) + .child(NetworkInstance.class); + + private static final InstanceIdentifier PROTOCOL_ID = + NETWORK_INSTANCE_ID.child(Protocols.class).child(Protocol.class); + + private static final InstanceIdentifier 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 = 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 000000000..50ae45b40 --- /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 { + 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, 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 id, + @Nonnull final PeerBean peer) { + peers.put(id, peer); + } + + @VisibleForTesting + synchronized boolean isPeerConfigured(@Nonnull final InstanceIdentifier id) { + return peers.containsKey(id); + } + + @Override + public synchronized void writeCurrentAttributes(@Nonnull final InstanceIdentifier 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 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 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 000000000..1505537ab --- /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 { + 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 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 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 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 000000000..7e0dd8f45 --- /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 { + 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 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 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 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 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 093acb85b..c61d83cb0 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 000000000..4f348510a --- /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 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 000000000..abf056850 --- /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 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 000000000..f3c3c2e16 --- /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 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 -- cgit 1.2.3-korg