*/
package org.opendaylight.controller.config.yang.bgp.rib.impl;
-import java.lang.management.ManagementFactory;
+import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
+import com.google.common.net.InetAddresses;
+import io.netty.util.concurrent.Future;
+
import java.net.InetSocketAddress;
import java.util.List;
-import javax.management.AttributeNotFoundException;
-import javax.management.InstanceNotFoundException;
-import javax.management.MBeanException;
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-import javax.management.ReflectionException;
-
+import org.opendaylight.bgpcep.tcpmd5.KeyMapping;
import org.opendaylight.controller.config.api.JmxAttributeValidationException;
import org.opendaylight.protocol.bgp.rib.impl.BGPPeer;
+import org.opendaylight.protocol.bgp.rib.impl.StrictBGPPeerRegistry;
+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.RIB;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.Lists;
-import com.google.common.net.InetAddresses;
-
/**
*
*/
-public final class BGPPeerModule extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractBGPPeerModule
-{
- private static final Logger LOG = LoggerFactory.getLogger(BGPPeerModule.class);
-
- public BGPPeerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
- super(identifier, dependencyResolver);
- }
-
- public BGPPeerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
- final BGPPeerModule oldModule, final java.lang.AutoCloseable oldInstance) {
-
- super(identifier, dependencyResolver, oldModule, oldInstance);
- }
-
- @Override
- protected void customValidation(){
- JmxAttributeValidationException.checkNotNull(getHost(),
- "value is not set.", hostJmxAttribute);
- JmxAttributeValidationException.checkNotNull(getPort(),
- "value is not set.", portJmxAttribute);
-
- if (getPassword() != null) {
- /*
- * This is a nasty hack, but we don't have another clean solution. We cannot allow
- * password being set if the injected dispatcher does not have the optional
- * md5-server-channel-factory set.
- *
- * FIXME: this is a use case for Module interfaces, e.g. RibImplModule
- * should something like isMd5ServerSupported()
- */
- final MBeanServer srv = ManagementFactory.getPlatformMBeanServer();
- try {
- final ObjectName ribi = (ObjectName) srv.getAttribute(getRib(), "CurrentImplementation");
-
- // FIXME: AbstractRIBImplModule.bgpDispatcherJmxAttribute.getAttributeName()
- final ObjectName disp = (ObjectName) srv.getAttribute(ribi, "BgpDispatcher");
-
- final ObjectName dispi = (ObjectName) srv.getAttribute(disp, "CurrentImplementation");
-
- // FIXME: AbstractBGPDispatcherImplModule.md5ChannelFactoryJmxAttribute.getAttributeName()
- final Object cf = srv.getAttribute(dispi, "Md5ChannelFactory");
- JmxAttributeValidationException.checkCondition(cf != null, "Underlying dispatcher does not support MD5 clients", passwordJmxAttribute);
- } catch (AttributeNotFoundException | InstanceNotFoundException
- | MBeanException | ReflectionException e) {
- JmxAttributeValidationException.wrap(e, "support could not be validated", passwordJmxAttribute);
- }
- }
- }
-
- private InetSocketAddress createAddress() {
- final IpAddress ip = getHost();
- if (ip.getIpv4Address() != null) {
- return new InetSocketAddress(InetAddresses.forString(ip.getIpv4Address().getValue()), getPort().getValue());
- } else if (ip.getIpv6Address() != null) {
- return new InetSocketAddress(InetAddresses.forString(ip.getIpv6Address().getValue()), getPort().getValue());
- } else {
- throw new IllegalStateException("Failed to handle host " + getHost());
- }
- }
-
- private static String peerName(final IpAddress host) {
- if (host.getIpv4Address() != null) {
- return host.getIpv4Address().getValue();
- }
- if (host.getIpv6Address() != null) {
- return host.getIpv6Address().getValue();
- }
-
- return null;
- }
-
- @Override
- public java.lang.AutoCloseable createInstance() {
- final RIB r = getRibDependency();
-
- final List<BgpParameters> tlvs = Lists.newArrayList();
- tlvs.add(new BgpParametersBuilder().setCParameters(
- new As4BytesCaseBuilder().setAs4BytesCapability(new As4BytesCapabilityBuilder().setAsNumber(r.getLocalAs()).build()).build()).build());
-
- for (final BgpTableType t : getAdvertizedTableDependency()) {
- if (!r.getLocalTables().contains(t)) {
- LOG.info("RIB instance does not list {} in its local tables. Incoming data will be dropped.", t);
- }
-
- tlvs.add(new BgpParametersBuilder().setCParameters(
- new MultiprotocolCaseBuilder().setMultiprotocolCapability(
- new MultiprotocolCapabilityBuilder(t).build()).build()).build());
- }
-
- // Remote AS number defaults to our local AS
- final AsNumber remoteAs;
- if (getRemoteAs() != null) {
- remoteAs = new AsNumber(getRemoteAs());
- } else {
- remoteAs = r.getLocalAs();
- }
-
- final String password;
- if (getPassword() != null) {
- password = getPassword().getValue();
- } else {
- password = null;
- }
-
- return new BGPPeer(peerName(getHost()), createAddress(), password,
- new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), tlvs), remoteAs, r);
- }
+public final class BGPPeerModule extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractBGPPeerModule {
+ private static final Logger LOG = LoggerFactory.getLogger(BGPPeerModule.class);
+
+ public BGPPeerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public BGPPeerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final BGPPeerModule oldModule,
+ final java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation() {
+ JmxAttributeValidationException.checkNotNull(getHost(), "value is not set.", hostJmxAttribute);
+ JmxAttributeValidationException.checkNotNull(getPort(), "value is not set.", portJmxAttribute);
+
+ if (getPassword() != null) {
+ /*
+ * This is a nasty hack, but we don't have another clean solution. We cannot allow
+ * password being set if the injected dispatcher does not have the optional
+ * md5-server-channel-factory set.
+ *
+ * FIXME: this is a use case for Module interfaces, e.g. RibImplModule
+ * should something like isMd5ServerSupported()
+ */
+
+ RIBImplModuleMXBean ribProxy = dependencyResolver.newMXBeanProxy(getRib(), RIBImplModuleMXBean.class);
+ BGPDispatcherImplModuleMXBean bgpDispatcherProxy = dependencyResolver.newMXBeanProxy(
+ ribProxy.getBgpDispatcher(), BGPDispatcherImplModuleMXBean.class);
+ boolean isMd5Supported = bgpDispatcherProxy.getMd5ChannelFactory() != null;
+
+ JmxAttributeValidationException.checkCondition(isMd5Supported,
+ "Underlying dispatcher does not support MD5 clients", passwordJmxAttribute);
+
+ }
+ }
+
+ private InetSocketAddress createAddress() {
+ final IpAddress ip = getHost();
+ if (ip.getIpv4Address() != null) {
+ return new InetSocketAddress(InetAddresses.forString(ip.getIpv4Address().getValue()), getPort().getValue());
+ } else if (ip.getIpv6Address() != null) {
+ return new InetSocketAddress(InetAddresses.forString(ip.getIpv6Address().getValue()), getPort().getValue());
+ } else {
+ throw new IllegalStateException("Failed to handle host " + getHost());
+ }
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ final RIB r = getRibDependency();
+
+ final List<BgpParameters> tlvs = getTlvs(r);
+ final AsNumber remoteAs = getAsOrDefault(r);
+ final String password = getPasswordOrNull();
+
+ final BGPSessionPreferences prefs = new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), tlvs);
+ final BGPPeer bgpClientPeer = new BGPPeer(peerName(getHostWithoutValue()), r);
+
+ getPeerRegistryBackwards().addPeer(getHostWithoutValue(), bgpClientPeer, prefs);
+
+ final AutoCloseable peerCloseable = new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ bgpClientPeer.close();
+ getPeerRegistryBackwards().removePeer(getHostWithoutValue());
+ }
+ };
+
+ // Initiate connection
+ if(getInitiateConnection()) {
+ final Future<Void> cf = initiateConnection(createAddress(), password, remoteAs, getPeerRegistryBackwards());
+ return new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ cf.cancel(true);
+ peerCloseable.close();
+ }
+ };
+ } else {
+ return peerCloseable;
+ }
+ }
+
+ private String getPasswordOrNull() {
+ final String password;
+ if (getPassword() != null) {
+ password = getPassword().getValue();
+ } else {
+ password = null;
+ }
+ return password;
+ }
+
+ private AsNumber getAsOrDefault(RIB r) {
+ // Remote AS number defaults to our local AS
+ final AsNumber remoteAs;
+ if (getRemoteAs() != null) {
+ remoteAs = new AsNumber(getRemoteAs());
+ } else {
+ remoteAs = r.getLocalAs();
+ }
+ return remoteAs;
+ }
+
+ private List<BgpParameters> getTlvs(RIB r) {
+ final List<BgpParameters> tlvs = Lists.newArrayList();
+ tlvs.add(new BgpParametersBuilder().setCParameters(
+ new As4BytesCaseBuilder().setAs4BytesCapability(new As4BytesCapabilityBuilder().setAsNumber(r.getLocalAs()).build()).build()).build());
+
+ for (final BgpTableType t : getAdvertizedTableDependency()) {
+ if (!r.getLocalTables().contains(t)) {
+ LOG.info("RIB instance does not list {} in its local tables. Incoming data will be dropped.", t);
+ }
+
+ tlvs.add(new BgpParametersBuilder().setCParameters(
+ new MultiprotocolCaseBuilder().setMultiprotocolCapability(new MultiprotocolCapabilityBuilder(t).build()).build()).build());
+ }
+ return tlvs;
+ }
+
+ public IpAddress getHostWithoutValue() {
+ // FIXME we need to remove field "value" from IpAddress since equals does not work as expected when value being present
+ // Remove after this bug is fixed https://bugs.opendaylight.org/show_bug.cgi?id=1276
+ final IpAddress host = super.getHost();
+ if(host.getIpv4Address() != null) {
+ return new IpAddress(host.getIpv4Address());
+ } else if(host.getIpv6Address() != null){
+ return new IpAddress(host.getIpv6Address());
+ }
+
+ throw new IllegalArgumentException("Unexpected host " + host);
+ }
+
+ private io.netty.util.concurrent.Future<Void> initiateConnection(final InetSocketAddress address, final String password, final AsNumber remoteAs, final BGPPeerRegistry registry) {
+ final KeyMapping keys;
+ if (password != null) {
+ keys = new KeyMapping();
+ keys.put(address.getAddress(), password.getBytes(Charsets.US_ASCII));
+ } else {
+ keys = null;
+ }
+
+ final RIB rib = getRibDependency();
+ return rib.getDispatcher().createReconnectingClient(address, remoteAs, registry, rib.getTcpStrategyFactory(),
+ rib.getSessionStrategyFactory(), keys);
+ }
+
+ private BGPPeerRegistry getPeerRegistryBackwards() {
+ return getPeerRegistry() == null ? StrictBGPPeerRegistry.GLOBAL : getPeerRegistryDependency();
+ }
+
+ private static String peerName(final IpAddress host) {
+ if (host.getIpv4Address() != null) {
+ return host.getIpv4Address().getValue();
+ }
+ if (host.getIpv6Address() != null) {
+ return host.getIpv6Address().getValue();
+ }
+
+ return null;
+ }
+
}