diff options
author | 2017-05-18 16:46:46 +0200 | |
---|---|---|
committer | 2017-05-29 08:36:30 +0200 | |
commit | 685bffccd16d58fc5c1dd5c118ee74aa39effa68 (patch) | |
tree | a9e9dd8277e818d6bb3c6ced7ad5ae74bab3c07c /infra/bgp-distribution/src | |
parent | 46589c652a58b1cd56dab268b59d96bc20b5dd1f (diff) |
HONEYCOMB-355: move BGP to separate distribution
Given current YangBindingProviderModule implementation (HONEYCOMB-358),
needed to preserve footprint of minimal distro.
The change also:
- reverts logger&persistence config changes
introduced by https://gerrit.fd.io/r/#/c/5972/
- improves logging for BGP component initialization
Change-Id: Ie223833518be21628edb7ace36c7618e427585e0
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
Diffstat (limited to 'infra/bgp-distribution/src')
25 files changed, 1474 insertions, 0 deletions
diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BGPDispatcherImplProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BGPDispatcherImplProvider.java new file mode 100644 index 000000000..c9ec39f35 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BGPDispatcherImplProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.infra.bgp; + +import com.google.inject.Inject; +import io.fd.honeycomb.infra.distro.ProviderTrait; +import io.netty.channel.EventLoopGroup; +import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionConsumerContext; +import org.opendaylight.protocol.bgp.rib.impl.BGPDispatcherImpl; +import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher; + +final class BGPDispatcherImplProvider extends ProviderTrait<BGPDispatcher> { + @Inject + private BGPExtensionConsumerContext consumerContext; + @Inject + private EventLoopGroup threadGroup; + + @Override + protected BGPDispatcher create() { + return new BGPDispatcherImpl(consumerContext.getMessageRegistry(), threadGroup, threadGroup); + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BGPExtensionConsumerContextProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BGPExtensionConsumerContextProvider.java new file mode 100644 index 000000000..00e99ab99 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BGPExtensionConsumerContextProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.infra.bgp; + +import com.google.inject.Inject; +import io.fd.honeycomb.infra.distro.ProviderTrait; +import java.util.ArrayList; +import java.util.Set; +import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionConsumerContext; +import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionProviderActivator; +import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionProviderContext; +import org.opendaylight.protocol.bgp.parser.spi.pojo.SimpleBGPExtensionProviderContext; +import org.opendaylight.protocol.bgp.parser.spi.pojo.SimpleBGPExtensionProviderContextActivator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class BGPExtensionConsumerContextProvider extends ProviderTrait<BGPExtensionConsumerContext> { + private static final Logger LOG = LoggerFactory.getLogger(BGPExtensionConsumerContextProvider.class); + + @Inject + private Set<BGPExtensionProviderActivator> activators; + + @Override + protected BGPExtensionConsumerContext create() { + final BGPExtensionProviderContext ctx = new SimpleBGPExtensionProviderContext(); + final SimpleBGPExtensionProviderContextActivator activator = + new SimpleBGPExtensionProviderContextActivator(ctx, new ArrayList<>(activators)); + LOG.debug("Starting BGPExtensionConsumerContext with activators: {}", activators); + activator.start(); + return ctx; + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BGPPeerRegistryProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BGPPeerRegistryProvider.java new file mode 100644 index 000000000..217c43823 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BGPPeerRegistryProvider.java @@ -0,0 +1,123 @@ +/* + * 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 com.google.common.base.Preconditions.checkState; +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.infra.distro.ProviderTrait; +import java.io.InputStream; +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.StrictBGPPeerRegistry; +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.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.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 BGPPeerRegistryProvider extends ProviderTrait<BGPPeerRegistry> { + private static final Logger LOG = LoggerFactory.getLogger(BGPPeerRegistryProvider.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; + + @Override + protected BGPPeerRegistry create() { + final BGPPeerRegistry peerRegistry = StrictBGPPeerRegistry.instance(); + final Neighbors 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("Created BGPPeerRegistry with neighbours {}", neighbors); + return peerRegistry; + } + + private Neighbors readNeighbours() { + LOG.debug("Reading BGP neighbours from {}", PEERS_CFG); + final InputStream resourceStream = this.getClass().getResourceAsStream(PEERS_CFG); + checkState(resourceStream != null, "Resource %s not found", PEERS_CFG); + + 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/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfiguration.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfiguration.java new file mode 100644 index 000000000..9182dd3a0 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfiguration.java @@ -0,0 +1,68 @@ +/* + * 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.base.MoreObjects; +import java.util.Optional; +import net.jmob.guice.conf.core.BindConfig; +import net.jmob.guice.conf.core.InjectConfig; +import net.jmob.guice.conf.core.Syntax; + +/** + * This is the Java equivalent for bgp.json file. We use guice-config library to load all the config attributes + * into this class instance. + * + * The BindConfig annotation tells that bgp.json file should be looked up on classpath root. + */ +@BindConfig(value = "bgp", syntax = Syntax.JSON) +public class BgpConfiguration { + + public boolean isBgpEnabled() { + return Boolean.valueOf(bgpEnabled); + } + + public boolean isBgpMultiplePathsEnabled() { + return Boolean.valueOf(bgpMultiplePaths.get()); + } + + @InjectConfig("bgp-enabled") + public String bgpEnabled; + @InjectConfig("bgp-binding-address") + public Optional<String> bgpBindingAddress; + @InjectConfig("bgp-port") + public Optional<Integer> bgpPort; + @InjectConfig("bgp-as-number") + public Optional<Integer> bgpAsNumber; + @InjectConfig("bgp-receive-multiple-paths") + public Optional<String> bgpMultiplePaths; + @InjectConfig("bgp-send-max-paths") + public Optional<Integer> bgpSendMaxMaths; + @InjectConfig("bgp-netty-threads") + public Integer bgpNettyThreads; + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("bgpEnabled", bgpEnabled) + .add("bgpBindingAddress", bgpBindingAddress) + .add("bgpPort", bgpPort) + .add("bgp-as-number", bgpAsNumber) + .add("bgp-netty-threads", bgpNettyThreads) + .add("bgp-receive-multiple-paths", bgpMultiplePaths) + .add("bgp-send-max-paths", bgpSendMaxMaths) + .toString(); + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfigurationModule.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfigurationModule.java new file mode 100644 index 000000000..a1c2e765b --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpConfigurationModule.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.infra.bgp; + +import com.google.inject.AbstractModule; +import net.jmob.guice.conf.core.ConfigurationModule; + +/** + * Load the configuration from json into HoneycombConfiguration and make it available. + */ +public class BgpConfigurationModule extends AbstractModule { + + protected void configure() { + install(ConfigurationModule.create()); + // Inject non-dependency configuration + requestInjection(BgpConfiguration.class); + } + +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpExtensionsModule.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpExtensionsModule.java new file mode 100644 index 000000000..461fb8918 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpExtensionsModule.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.infra.bgp; + +import com.google.inject.AbstractModule; +import com.google.inject.multibindings.Multibinder; +import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionConsumerContext; +import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionProviderActivator; +import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext; +import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionProviderActivator; + +/** + * Registers BGP extensions provided by ODL implementation. + * TODO add support for flowspec (requires some special initialization) + */ +public final class BgpExtensionsModule extends AbstractModule { + + protected void configure() { + // 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 + configureRIBExtensions(); + configureBGPExtensions(); + } + + private void configureRIBExtensions() { + final Multibinder<RIBExtensionProviderActivator> ribExtensionBinder = Multibinder.newSetBinder(binder(), + RIBExtensionProviderActivator.class); + ribExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.evpn.impl.RIBActivator.class); + ribExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.inet.RIBActivator.class); + ribExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.labeled.unicast.RIBActivator.class); + ribExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.linkstate.impl.RIBActivator.class); + ribExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.l3vpn.ipv4.RibIpv4Activator.class); + ribExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.l3vpn.ipv6.RibIpv6Activator.class); + bind(RIBExtensionConsumerContext.class).toProvider(RIBExtensionConsumerContextProvider.class); + } + + private void configureBGPExtensions() { + final Multibinder<BGPExtensionProviderActivator> bgpExtensionBinder = Multibinder.newSetBinder(binder(), + BGPExtensionProviderActivator.class); + bgpExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.parser.impl.BGPActivator.class); + bgpExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.evpn.impl.BGPActivator.class); + bgpExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.inet.BGPActivator.class); + bgpExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.labeled.unicast.BGPActivator.class); + bgpExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.linkstate.impl.BGPActivator.class); + bgpExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.l3vpn.ipv4.BgpIpv4Activator.class); + bgpExtensionBinder.addBinding().to(org.opendaylight.protocol.bgp.l3vpn.ipv6.BgpIpv6Activator.class); + bind(BGPExtensionConsumerContext.class).toProvider(BGPExtensionConsumerContextProvider.class); + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java new file mode 100644 index 000000000..128e9f0de --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpModule.java @@ -0,0 +1,82 @@ +/* + * 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 io.fd.honeycomb.infra.distro.data.InmemoryDOMDataBrokerProvider.CONFIG; +import static io.fd.honeycomb.infra.distro.data.InmemoryDOMDataBrokerProvider.OPERATIONAL; + +import com.google.inject.PrivateModule; +import com.google.inject.Singleton; +import com.google.inject.name.Names; +import io.fd.honeycomb.infra.distro.data.BindingDataBrokerProvider; +import io.fd.honeycomb.infra.distro.data.DataStoreProvider; +import io.fd.honeycomb.infra.distro.data.InmemoryDOMDataBrokerProvider; +import io.netty.channel.EventLoopGroup; +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.spi.BGPDispatcher; +import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry; +import org.opendaylight.protocol.bgp.rib.impl.spi.RIB; + +public final class BgpModule extends PrivateModule { + + static final String HONEYCOMB_BGP = "honeycomb-bgp"; + + protected void configure() { + // Create BGPDispatcher BGPDispatcher for creating BGP clients + bind(EventLoopGroup.class).toProvider(BgpNettyThreadGroupProvider.class).in(Singleton.class); + bind(BGPDispatcher.class).toProvider(BGPDispatcherImplProvider.class).in(Singleton.class); + + configureRIB(); + + // Configure peer registry + bind(BGPOpenConfigMappingService.class).toInstance(new BGPOpenConfigMappingServiceImpl()); + bind(BGPPeerRegistry.class).toProvider(BGPPeerRegistryProvider.class); + + // Create BGP server instance + bind(BgpServerProvider.BgpServer.class).toProvider(BgpServerProvider.class).in(Singleton.class); + expose(BgpServerProvider.BgpServer.class); + } + + private void configureRIB() { + // Create inmemory config data store for HONEYCOMB_BGP + bind(InMemoryDOMDataStore.class).annotatedWith(Names.named(CONFIG)) + .toProvider(new DataStoreProvider(CONFIG, LogicalDatastoreType.CONFIGURATION)) + .in(Singleton.class); + + // Create inmemory operational data store for HONEYCOMB_BGP + bind(InMemoryDOMDataStore.class).annotatedWith(Names.named(OPERATIONAL)) + .toProvider(new DataStoreProvider(OPERATIONAL, LogicalDatastoreType.OPERATIONAL)) + .in(Singleton.class); + + // Wrap datastores as DOMDataBroker + // TODO make executor service configurable + bind(DOMDataBroker.class).toProvider(InmemoryDOMDataBrokerProvider.class).in(Singleton.class); + + // Wrap DOMDataBroker as BA data broker (required by BgpReaderFactoryProvider) + bind(DataBroker.class).annotatedWith(Names.named(HONEYCOMB_BGP)).toProvider(BindingDataBrokerProvider.class) + .in(Singleton.class); + expose(DataBroker.class).annotatedWith(Names.named(HONEYCOMB_BGP)); + + // Create RIB instance + bind(RIB.class).toProvider(BgpRIBProvider.class).in(Singleton.class); + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpNettyThreadGroupProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpNettyThreadGroupProvider.java new file mode 100644 index 000000000..8208a4320 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpNettyThreadGroupProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 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.util.concurrent.ThreadFactoryBuilder; +import com.google.inject.Inject; +import io.fd.honeycomb.infra.distro.ProviderTrait; +import io.netty.channel.nio.NioEventLoopGroup; + +final class BgpNettyThreadGroupProvider extends ProviderTrait<NioEventLoopGroup> { + + @Inject + private BgpConfiguration cfgAttributes; + + @Override + protected NioEventLoopGroup create() { + return new NioEventLoopGroup(cfgAttributes.bgpNettyThreads, + new ThreadFactoryBuilder().setNameFormat("bgp-netty-%d").build()); + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpRIBProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpRIBProvider.java new file mode 100644 index 000000000..11ee4cf8d --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpRIBProvider.java @@ -0,0 +1,121 @@ +/* + * 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.ImmutableList; +import com.google.inject.Inject; +import io.fd.honeycomb.infra.distro.ProviderTrait; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.broker.impl.PingPongDataBroker; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration; +import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode; +import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigMappingService; +import org.opendaylight.protocol.bgp.rib.impl.RIBImpl; +import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher; +import org.opendaylight.protocol.bgp.rib.impl.spi.RIB; +import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext; +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.multiprotocol.rev151009.bgp.common.afi.safi.list.AfiSafiBuilder; +import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.IPV4UNICAST; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber; +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.AfiSafi2; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.AfiSafi2Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class BgpRIBProvider extends ProviderTrait<RIB> { + private static final Logger LOG = LoggerFactory.getLogger(BgpRIBProvider.class); + private static final String HC_BGP_INSTANCE_NAME = "hc-bgp-instance"; + + @Inject + private BgpConfiguration cfg; + @Inject + private RIBExtensionConsumerContext extensions; + @Inject + private BGPDispatcher dispatcher; + @Inject + private BindingToNormalizedNodeCodec codec; + @Inject + private DOMDataBroker domBroker; + @Inject + private BGPOpenConfigMappingService mappingService; + @Inject + private SchemaService schemaService; + + @Override + protected RIB create() { + final AsNumber asNumber = new AsNumber(cfg.bgpAsNumber.get().longValue()); + final Ipv4Address routerId = new Ipv4Address(cfg.bgpBindingAddress.get()); + final ClusterIdentifier clusterId = new ClusterIdentifier(routerId); + LOG.debug("Creating BGP RIB: routerId={}, asNumber={}", routerId, asNumber); + // TODO configure other BGP Multiprotocol extensions: + final List<AfiSafi> afiSafi = ImmutableList.of(new AfiSafiBuilder().setAfiSafiName(IPV4UNICAST.class) + .addAugmentation(AfiSafi2.class, + new AfiSafi2Builder().setReceive(cfg.isBgpMultiplePathsEnabled()) + .setSendMax(cfg.bgpSendMaxMaths.get().shortValue()).build()) + .build()); + final Map<TablesKey, PathSelectionMode> pathSelectionModes = mappingService.toPathSelectionMode(afiSafi) + .entrySet().stream().collect(Collectors.toMap(entry -> + new TablesKey(entry.getKey().getAfi(), entry.getKey().getSafi()), Map.Entry::getValue)); + // based on RIBImpl.createRib + final RIBImpl rib = + new RIBImpl(new NoopClusterSingletonServiceProvider(), new RibId(HC_BGP_INSTANCE_NAME), asNumber, + new BgpId(routerId), clusterId, extensions, dispatcher, codec, new PingPongDataBroker(domBroker), + mappingService.toTableTypes(afiSafi), pathSelectionModes, extensions.getClassLoadingStrategy(), null); + + // required for proper RIB's CodecRegistry initialization (based on RIBImpl.start) + schemaService.registerSchemaContextListener(rib); + + LOG.debug("BGP RIB created successfully: {}", rib); + return rib; + } + + /** + * HC does not support clustering, but BGP uses {@link ClusterSingletonServiceProvider} + * to initialize {@link RIBImpl}. Therefore we provide this dummy implementation. + */ + private static final class NoopClusterSingletonServiceProvider implements ClusterSingletonServiceProvider { + private static final Logger LOG = LoggerFactory.getLogger(NoopClusterSingletonServiceProvider.class); + + private static final ClusterSingletonServiceRegistration REGISTRATION = + () -> LOG.debug("Closing ClusterSingletonServiceRegistration"); + + @Override + public ClusterSingletonServiceRegistration registerClusterSingletonService( + final ClusterSingletonService clusterSingletonService) { + clusterSingletonService.instantiateServiceInstance(); + return REGISTRATION; + } + + @Override + public void close() throws Exception { + LOG.debug("Closing NoopClusterSingletonServiceProvider"); + } + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpReaderFactoryProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpReaderFactoryProvider.java new file mode 100644 index 000000000..b79074f10 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpReaderFactoryProvider.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.infra.bgp; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.honeycomb.infra.distro.ProviderTrait; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.honeycomb.translate.util.read.BindingBrokerReader; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRibBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +final class BgpReaderFactoryProvider extends ProviderTrait<ReaderFactory> { + + @Inject + @Named(BgpModule.HONEYCOMB_BGP) + private DataBroker bgpDataBroker; + + @Override + protected BgpReaderFactory create() { + return new BgpReaderFactory(bgpDataBroker); + } + + /** + * {@link ReaderFactory} provides reader form BGP's dedicated data store. + * Makes BGP operational data available over NETCONF/RESTCONF. + */ + private static final class BgpReaderFactory implements ReaderFactory { + + private final DataBroker bgpDataBroker; + + BgpReaderFactory(final DataBroker bgpDataBroker) { + this.bgpDataBroker = bgpDataBroker; + } + + @Override + public void init(final ModifiableReaderRegistryBuilder registry) { + registry.add(new BindingBrokerReader<>(InstanceIdentifier.create(BgpRib.class), + bgpDataBroker, LogicalDatastoreType.OPERATIONAL, BgpRibBuilder.class)); + } + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpReadersModule.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpReadersModule.java new file mode 100644 index 000000000..745a94269 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpReadersModule.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.infra.bgp; + +import com.google.inject.AbstractModule; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import io.fd.honeycomb.translate.read.ReaderFactory; + +public class BgpReadersModule extends AbstractModule { + + protected void configure() { + // 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 + final Multibinder<ReaderFactory> binder = Multibinder.newSetBinder(binder(), ReaderFactory.class); + binder.addBinding().toProvider(BgpReaderFactoryProvider.class).in(Singleton.class); + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpServerProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpServerProvider.java new file mode 100644 index 000000000..27dad8840 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpServerProvider.java @@ -0,0 +1,107 @@ +/* + * 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.base.Preconditions; +import com.google.inject.Inject; +import io.fd.honeycomb.infra.distro.ProviderTrait; +import io.netty.channel.Channel; +import io.netty.channel.ChannelConfig; +import io.netty.channel.ChannelFuture; +import io.netty.channel.epoll.Epoll; +import io.netty.channel.epoll.EpollChannelOption; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +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.BGPSessionPreferences; +import org.opendaylight.protocol.bgp.rib.impl.spi.PeerRegistryListener; +import org.opendaylight.protocol.concepts.KeyMapping; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class BgpServerProvider extends ProviderTrait<BgpServerProvider.BgpServer> { + private static final Logger LOG = LoggerFactory.getLogger(BgpServerProvider.class); + @Inject + private BgpConfiguration cfg; + @Inject + private BGPPeerRegistry peerRegistry; + @Inject + private BGPDispatcher dispatcher; + + @Override + protected BgpServer create() { + // code based on org.opendaylight.controller.config.yang.bgp.rib.impl.BGPPeerAcceptorModule from Boron-SR3 + final InetAddress bindingAddress; + try { + bindingAddress = InetAddress.getByName(cfg.bgpBindingAddress.get()); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("Illegal BGP binding address", e); + } + final InetSocketAddress address = new InetSocketAddress(bindingAddress, cfg.bgpPort.get()); + LOG.debug("Creating BgpServer for {}", address); + final ChannelFuture localServer = dispatcher.createServer(peerRegistry, address); + localServer.addListener(future -> { + Preconditions.checkArgument(future.isSuccess(), "Unable to start bgp server on %s", address, future.cause()); + final Channel channel = localServer.channel(); + if (Epoll.isAvailable()) { + peerRegistry.registerPeerRegisterListener(new PeerRegistryListenerImpl(channel.config())); + } + }); + final BgpServer server = new BgpServer(localServer); + LOG.debug("BgpServer successfully created."); + return server; + } + + public static final class BgpServer { + private ChannelFuture localServer; + + BgpServer(final ChannelFuture localServer) { + this.localServer = localServer; + } + + public ChannelFuture getLocalServer() { + return localServer; + } + } + + private static final class PeerRegistryListenerImpl implements PeerRegistryListener { + private final ChannelConfig channelConfig; + private final KeyMapping keys; + + PeerRegistryListenerImpl(final ChannelConfig channelConfig) { + this.channelConfig = channelConfig; + this.keys = KeyMapping.getKeyMapping(); + } + @Override + public void onPeerAdded(final IpAddress ip, final BGPSessionPreferences prefs) { + if (prefs.getMd5Password().isPresent()) { + this.keys.put(IetfInetUtil.INSTANCE.inetAddressFor(ip), prefs.getMd5Password().get()); + this.channelConfig.setOption(EpollChannelOption.TCP_MD5SIG, this.keys); + } + } + @Override + public void onPeerRemoved(final IpAddress ip) { + if (this.keys.remove(IetfInetUtil.INSTANCE.inetAddressFor(ip)) != null) { + this.channelConfig.setOption(EpollChannelOption.TCP_MD5SIG, this.keys); + } + } + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpWriterFactoryProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpWriterFactoryProvider.java new file mode 100644 index 000000000..081fbd782 --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpWriterFactoryProvider.java @@ -0,0 +1,90 @@ +/* + * 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.infra.distro.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.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 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) + ), + new BindingBrokerWriter<>(InstanceIdentifier.create(ApplicationRib.class), dataBroker) + ); + } + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpWritersModule.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpWritersModule.java new file mode 100644 index 000000000..f1e92c4ba --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/BgpWritersModule.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.infra.bgp; + +import com.google.inject.AbstractModule; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import io.fd.honeycomb.translate.write.WriterFactory; + +public class BgpWritersModule extends AbstractModule { + + protected void configure() { + // 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 + final Multibinder<WriterFactory> binder = Multibinder.newSetBinder(binder(), WriterFactory.class); + binder.addBinding().toProvider(BgpWriterFactoryProvider.class).in(Singleton.class); + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/RIBExtensionConsumerContextProvider.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/RIBExtensionConsumerContextProvider.java new file mode 100644 index 000000000..b90b7880c --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/RIBExtensionConsumerContextProvider.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.infra.bgp; + +import com.google.inject.Inject; +import io.fd.honeycomb.infra.distro.ProviderTrait; +import java.util.ArrayList; +import java.util.Set; +import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext; +import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionProviderActivator; +import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionProviderContext; +import org.opendaylight.protocol.bgp.rib.spi.SimpleRIBExtensionProviderContext; +import org.opendaylight.protocol.bgp.rib.spi.SimpleRIBExtensionProviderContextActivator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RIBExtensionConsumerContextProvider extends ProviderTrait<RIBExtensionConsumerContext> { + private static final Logger LOG = LoggerFactory.getLogger(RIBExtensionConsumerContextProvider.class); + @Inject + private Set<RIBExtensionProviderActivator> activators; + + @Override + protected RIBExtensionConsumerContext create() { + final RIBExtensionProviderContext ctx = new SimpleRIBExtensionProviderContext(); + final SimpleRIBExtensionProviderContextActivator activator = + new SimpleRIBExtensionProviderContextActivator(ctx, new ArrayList<>(activators)); + LOG.debug("Starting RIBExtensionConsumerContext with activators: {}", activators); + activator.start(); + return ctx; + } +} diff --git a/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/distro/Main.java b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/distro/Main.java new file mode 100644 index 000000000..0204e1f5c --- /dev/null +++ b/infra/bgp-distribution/src/main/java/io/fd/honeycomb/infra/bgp/distro/Main.java @@ -0,0 +1,73 @@ +/* + * 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.distro; + +import static io.fd.honeycomb.infra.distro.ActiveModuleProvider.STANDARD_MODULES_RELATIVE_PATH; +import static io.fd.honeycomb.infra.distro.ActiveModuleProvider.aggregateResources; +import static io.fd.honeycomb.infra.distro.ActiveModuleProvider.loadActiveModules; + +import com.google.inject.ConfigurationException; +import com.google.inject.CreationException; +import com.google.inject.Injector; +import com.google.inject.Module; +import com.google.inject.ProvisionException; +import io.fd.honeycomb.infra.bgp.BgpConfiguration; +import io.fd.honeycomb.infra.bgp.BgpServerProvider; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class Main { + + private static final Logger LOG = LoggerFactory.getLogger(Main.class); + + private Main() { + } + + public static void main(String[] args) { + final ClassLoader classLoader = Main.class.getClassLoader(); + init(loadActiveModules(aggregateResources(STANDARD_MODULES_RELATIVE_PATH, classLoader))); + } + + /** + * Initialize the Honeycomb with provided modules + */ + public static Injector init(final Set<? extends Module> modules) { + try { + Injector injector = io.fd.honeycomb.infra.distro.Main.init(modules); + final BgpConfiguration bgpAttributes = injector.getInstance(BgpConfiguration.class); + + if (bgpAttributes.isBgpEnabled()) { + LOG.info("Starting BGP"); + injector.getInstance(BgpServerProvider.BgpServer.class); + LOG.info("BGP started successfully!"); + } + + return injector; + } catch (CreationException | ProvisionException | ConfigurationException e) { + LOG.error("Failed to initialize Honeycomb components", e); + throw e; + } catch (RuntimeException e) { + LOG.error("Unexpected initialization failure", e); + throw e; + } finally { + // Trigger gc to force collect initial garbage + dedicated classloader + System.gc(); + } + } + +} diff --git a/infra/bgp-distribution/src/main/resources/honeycomb-minimal-resources/config/bgp-peers.json b/infra/bgp-distribution/src/main/resources/honeycomb-minimal-resources/config/bgp-peers.json new file mode 100644 index 000000000..6de2065b8 --- /dev/null +++ b/infra/bgp-distribution/src/main/resources/honeycomb-minimal-resources/config/bgp-peers.json @@ -0,0 +1,48 @@ +{ + "bgp-openconfig-extensions:neighbors": { + "neighbor": [ + { + "neighbor-address": "10.25.1.9", + "config": { + "peer-group": "application-peers" + }, + "afi-safis": { + "afi-safi": [ + { + "afi-safi-name": "openconfig-bgp-types:IPV4-UNICAST", + "receive": true, + "send-max": 0 + } + ] + } + }, + { + "neighbor-address": "127.0.0.2", + "config": { + "peer-type": "INTERNAL" + }, + "timers": { + "config": { + "connect-retry": 10, + "hold-time": 90 + } + }, + "transport": { + "config": { + "remote-port": 17900, + "passive-mode": false + } + }, + "afi-safis": { + "afi-safi": [ + { + "afi-safi-name": "openconfig-bgp-types:IPV4-UNICAST", + "receive": true, + "send-max": 0 + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/infra/bgp-distribution/src/main/resources/honeycomb-minimal-resources/config/bgp.json b/infra/bgp-distribution/src/main/resources/honeycomb-minimal-resources/config/bgp.json new file mode 100644 index 000000000..2664c9ed6 --- /dev/null +++ b/infra/bgp-distribution/src/main/resources/honeycomb-minimal-resources/config/bgp.json @@ -0,0 +1,9 @@ +{ + "bgp-enabled": "true", + "bgp-binding-address": "127.0.0.1", + "bgp-port": 1790, + "bgp-as-number": 65000, + "bgp-receive-multiple-paths": "true", + "bgp-send-max-paths": 0, + "bgp-netty-threads": 2 +}
\ No newline at end of file diff --git a/infra/bgp-distribution/src/test/java/io/fd/honeycomb/infra/bgp/distro/BgpDistributionTest.java b/infra/bgp-distribution/src/test/java/io/fd/honeycomb/infra/bgp/distro/BgpDistributionTest.java new file mode 100644 index 000000000..095d61c39 --- /dev/null +++ b/infra/bgp-distribution/src/test/java/io/fd/honeycomb/infra/bgp/distro/BgpDistributionTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016 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.distro; + +import static com.google.common.collect.ImmutableSet.of; + +import com.google.common.io.ByteStreams; +import com.google.inject.Module; +import com.mashape.unirest.http.Unirest; +import io.fd.honeycomb.infra.bgp.BgpConfigurationModule; +import io.fd.honeycomb.infra.bgp.BgpExtensionsModule; +import io.fd.honeycomb.infra.bgp.BgpModule; +import io.fd.honeycomb.infra.bgp.BgpReadersModule; +import io.fd.honeycomb.infra.bgp.BgpWritersModule; +import io.fd.honeycomb.infra.distro.cfgattrs.CfgAttrsModule; +import io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule; +import io.fd.honeycomb.infra.distro.data.context.ContextPipelineModule; +import io.fd.honeycomb.infra.distro.initializer.InitializerPipelineModule; +import io.fd.honeycomb.infra.distro.netconf.NetconfModule; +import io.fd.honeycomb.infra.distro.netconf.NetconfReadersModule; +import io.fd.honeycomb.infra.distro.restconf.RestconfModule; +import io.fd.honeycomb.infra.distro.schema.SchemaModule; +import io.fd.honeycomb.infra.distro.schema.YangBindingProviderModule; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.util.Set; +import javax.net.ssl.SSLContext; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContexts; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BgpDistributionTest { + + private static final Logger LOG = LoggerFactory.getLogger(BgpDistributionTest.class); + private static final String CERT_PASSWORD = "testing"; + private static final int HELLO_WAIT = 2500; + + private static final int BGP_MSG_TYPE_OFFSET = 18; // 16 (MARKER) + 2 (LENGTH); + private static final byte BGP_OPEN_MSG_TYPE = 1; + private static final int BGP_PORT = 1790; + + public static final Set<Module> BASE_MODULES = of( + new YangBindingProviderModule(), + new SchemaModule(), + new ConfigAndOperationalPipelineModule(), + new ContextPipelineModule(), + new InitializerPipelineModule(), + new NetconfModule(), + new NetconfReadersModule(), + new RestconfModule(), + new CfgAttrsModule(), + new BgpModule(), + new BgpExtensionsModule(), + new BgpReadersModule(), + new BgpWritersModule(), + new BgpConfigurationModule()); + + @Before + public void setUp() throws Exception { + SSLContext sslcontext = SSLContexts.custom() + .loadTrustMaterial(getClass().getResource("/honeycomb-keystore"), + CERT_PASSWORD.toCharArray(), new TrustSelfSignedStrategy()) + .build(); + + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext); + CloseableHttpClient httpclient = HttpClients.custom() + .setSSLSocketFactory(sslsf) + .build(); + Unirest.setHttpClient(httpclient); + } + + @Test(timeout = 60000) + public void test() throws Exception { + io.fd.honeycomb.infra.bgp.distro.Main.init(BASE_MODULES); + LOG.info("Testing Honeycomb BGP distribution"); + 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"); + try (final Socket localhost = new Socket(bgpHost, BGP_PORT, bgpPeerAddress, 0); + final InputStream inputStream = localhost.getInputStream()) { + // Wait until bgp message is sent + Thread.sleep(HELLO_WAIT); + + final byte[] msg = readMessage(inputStream); + LOG.info("Received BGP message: {}", msg); + + Assert.assertEquals(BGP_OPEN_MSG_TYPE, msg[BGP_MSG_TYPE_OFFSET]); + } + } +}
\ No newline at end of file diff --git a/infra/bgp-distribution/src/test/resources/WEB-INF/web.xml b/infra/bgp-distribution/src/test/resources/WEB-INF/web.xml new file mode 100644 index 000000000..6cf487170 --- /dev/null +++ b/infra/bgp-distribution/src/test/resources/WEB-INF/web.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + ~ Copyright (c) 2016 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. + --> + +<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" + version="3.0"> + + <servlet> + <servlet-name>JAXRSRestconf</servlet-name> + <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> + <init-param> + <param-name>javax.ws.rs.Application</param-name> + <param-value>org.opendaylight.netconf.sal.rest.impl.RestconfApplication</param-value> + </init-param> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>JAXRSRestconf</servlet-name> + <url-pattern>/*</url-pattern> + </servlet-mapping> + <filter> + <filter-name>cross-origin-restconf</filter-name> + <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class> + <init-param> + <param-name>allowedOrigins</param-name> + <param-value>*</param-value> + </init-param> + <init-param> + <param-name>allowedMethods</param-name> + <param-value>GET,POST,OPTIONS,DELETE,PUT,HEAD</param-value> + </init-param> + <init-param> + <param-name>allowedHeaders</param-name> + <param-value>origin, content-type, accept, authorization</param-value> + </init-param> + <init-param> + <param-name>exposedHeaders</param-name> + <param-value>location</param-value> + </init-param> + </filter> + <filter-mapping> + <filter-name>cross-origin-restconf</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + + <security-constraint> + <web-resource-collection> + <web-resource-name>NB api</web-resource-name> + <url-pattern>/*</url-pattern> + <http-method>POST</http-method> + <http-method>GET</http-method> + <http-method>PUT</http-method> + <http-method>PATCH</http-method> + <http-method>DELETE</http-method> + <http-method>HEAD</http-method> + </web-resource-collection> + </security-constraint> + +</web-app> diff --git a/infra/bgp-distribution/src/test/resources/bgp-peers.json b/infra/bgp-distribution/src/test/resources/bgp-peers.json new file mode 100644 index 000000000..3a7e01060 --- /dev/null +++ b/infra/bgp-distribution/src/test/resources/bgp-peers.json @@ -0,0 +1,90 @@ +{ + "bgp-openconfig-extensions:neighbors": { + "neighbor": [ + { + "neighbor-address": "10.25.1.9", + "config": { + "peer-group": "application-peers" + }, + "afi-safis": { + "afi-safi": [ + { + "afi-safi-name": "openconfig-bgp-types:IPV4-UNICAST", + "receive": true, + "send-max": 0 + } + ] + } + }, + { + "neighbor-address": "10.25.1.10", + "config": { + "peer-group": "application-peers" + }, + "afi-safis": { + "afi-safi": [ + { + "afi-safi-name": "openconfig-bgp-types:IPV4-UNICAST", + "receive": true, + "send-max": 0 + } + ] + } + }, + { + "neighbor-address": "127.0.0.2", + "config": { + "peer-type": "INTERNAL" + }, + "timers": { + "config": { + "connect-retry": 10, + "hold-time": 90 + } + }, + "transport": { + "config": { + "remote-port": 17900, + "passive-mode": false + } + }, + "afi-safis": { + "afi-safi": [ + { + "afi-safi-name": "openconfig-bgp-types:IPV4-UNICAST", + "receive": true, + "send-max": 0 + } + ] + } + }, + { + "neighbor-address": "127.0.0.3", + "config": { + "peer-type": "EXTERNAL" + }, + "timers": { + "config": { + "connect-retry": 10, + "hold-time": 90 + } + }, + "transport": { + "config": { + "remote-port": 17900, + "passive-mode": false + } + }, + "afi-safis": { + "afi-safi": [ + { + "afi-safi-name": "openconfig-bgp-types:IPV4-UNICAST", + "receive": true, + "send-max": 0 + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/infra/bgp-distribution/src/test/resources/bgp.json b/infra/bgp-distribution/src/test/resources/bgp.json new file mode 100644 index 000000000..494b6a965 --- /dev/null +++ b/infra/bgp-distribution/src/test/resources/bgp.json @@ -0,0 +1,9 @@ + { + "bgp-enabled": "true", + "bgp-binding-address": "127.0.0.1", + "bgp-port": 1790, + "bgp-as-number": 65000, + "bgp-receive-multiple-paths": "true", + "bgp-send-max-paths": 0, + "bgp-netty-threads": 2 +}
\ No newline at end of file diff --git a/infra/bgp-distribution/src/test/resources/honeycomb-keystore b/infra/bgp-distribution/src/test/resources/honeycomb-keystore Binary files differnew file mode 100644 index 000000000..44093dc09 --- /dev/null +++ b/infra/bgp-distribution/src/test/resources/honeycomb-keystore diff --git a/infra/bgp-distribution/src/test/resources/honeycomb.json b/infra/bgp-distribution/src/test/resources/honeycomb.json new file mode 100644 index 000000000..a0b2a633c --- /dev/null +++ b/infra/bgp-distribution/src/test/resources/honeycomb.json @@ -0,0 +1,40 @@ + { + "persisted-context-path": "/tmp/honeycomb/persist/context/data.json", + "persisted-context-restoration-type": "Merge", + "persisted-config-path": "/tmp/honeycomb/persist/config/data.json", + "persisted-config-restoration-type": "Merge", + + "notification-service-queue-depth": 1, + + "restconf-http-enabled": "true", + "restconf-root-path": "/restconf", + "restconf-binding-address": "127.0.0.1", + "restconf-port": 8182, + "restconf-https-enabled": "true", + "restconf-https-binding-address": "127.0.0.1", + "restconf-https-port": 8444, + "restconf-keystore": "/honeycomb-keystore", + "restconf-keystore-password": "testing", + "restconf-keystore-manager-password": "testing", + "restconf-truststore": "/honeycomb-keystore", + "restconf-truststore-password": "testing", + "restconf-websocket-port": 7780, + "restconf-pool-max-size": 10, + "restconf-pool-min-size": 1, + "restconf-acceptors-size": 1, + "restconf-selectors-size": 1, + "restconf-https-acceptors-size": 1, + "restconf-https-selectors-size": 1, + + "netconf-netty-threads": 2, + "netconf-tcp-enabled" : "true", + "netconf-tcp-binding-address": "127.0.0.1", + "netconf-tcp-binding-port": 7778, + "netconf-ssh-enabled" : "true", + "netconf-ssh-binding-address": "127.0.0.1", + "netconf-ssh-binding-port": 2832, + "netconf-notification-stream-name": "honeycomb", + + "username": "admin", + "password": "admin" +}
\ No newline at end of file diff --git a/infra/bgp-distribution/src/test/resources/logback.xml b/infra/bgp-distribution/src/test/resources/logback.xml new file mode 100644 index 000000000..2ee89db4e --- /dev/null +++ b/infra/bgp-distribution/src/test/resources/logback.xml @@ -0,0 +1,31 @@ + <!-- + ~ Copyright (c) 2016 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. + --> + +<configuration scan="true"> + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</pattern> + </encoder> + </appender> + + <root level="warn"> + <appender-ref ref="STDOUT" /> + </root> + + <logger name="org.opendaylight" level="INFO"/> + <logger name="io.fd" level="INFO"/> +</configuration> |