BUG-338 Allow incomming BGP connections.
[bgpcep.git] / bgp / rib-impl-config / src / main / java / org / opendaylight / controller / config / yang / bgp / rib / impl / BGPPeerModule.java
index af8f6ef4b98a661acf28f27992515ac5fdbca532..c54c485468c5945ed034826c254bff9df945df60 100644 (file)
  */
 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;
@@ -43,117 +43,174 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.mult
 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;
+    }
+
 }