BUG-4931: Simple routing policy
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / controller / config / yang / bgp / rib / impl / BGPPeerModule.java
old mode 100644 (file)
new mode 100755 (executable)
index 9ace4ae..06a830d
 package org.opendaylight.controller.config.yang.bgp.rib.impl;
 
 import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import com.google.common.net.InetAddresses;
 import io.netty.util.concurrent.Future;
 import java.net.InetSocketAddress;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.protocol.bgp.openconfig.spi.BGPConfigModuleTracker;
+import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigProvider;
+import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenconfigMapper;
+import org.opendaylight.protocol.bgp.openconfig.spi.InstanceConfigurationIdentifier;
+import org.opendaylight.protocol.bgp.openconfig.spi.pojo.BGPPeerInstanceConfiguration;
+import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
+import org.opendaylight.protocol.bgp.parser.spi.MultiprotocolCapabilitiesUtil;
 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.protocol.util.Ipv6Util;
 import org.opendaylight.tcpmd5.api.KeyMapping;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.BgpParameters;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.BgpParametersBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.OptionalCapabilities;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.OptionalCapabilitiesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.As4BytesCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.as4.bytes._case.As4BytesCapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.BgpParameters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.BgpParametersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.OptionalCapabilities;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.OptionalCapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.CParametersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.c.parameters.As4BytesCapabilityBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.GracefulRestartCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.MultiprotocolCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.graceful.restart._case.GracefulRestartCapabilityBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.multiprotocol._case.MultiprotocolCapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.CParameters1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.CParameters1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.mp.capabilities.AddPathCapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.mp.capabilities.GracefulRestartCapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.mp.capabilities.MultiprotocolCapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.mp.capabilities.add.path.capability.AddressFamilies;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.tcpmd5.cfg.rev140427.Rfc2385Key;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,10 +81,14 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang
 
     @Override
     protected void customValidation() {
-        JmxAttributeValidationException.checkNotNull(getHost(), "value is not set.", hostJmxAttribute);
+        final IpAddress host = getHost();
+        JmxAttributeValidationException.checkNotNull(host, "value is not set.", hostJmxAttribute);
+        JmxAttributeValidationException.checkCondition(host.getIpv4Address() != null || host.getIpv6Address() != null,
+            "Unexpected host", hostJmxAttribute);
+
         JmxAttributeValidationException.checkNotNull(getPort(), "value is not set.", portJmxAttribute);
 
-        if (getPassword() != null) {
+        if (getOptionaPassword(getPassword()).isPresent()) {
             /*
              *  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
@@ -88,17 +107,21 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang
                 "Underlying dispatcher does not support MD5 clients", passwordJmxAttribute);
 
         }
+
+        if (getPeerRole() != null) {
+            final boolean isNotPeerRoleInternal= getPeerRole() != PeerRole.Internal;
+            JmxAttributeValidationException.checkCondition(isNotPeerRoleInternal,
+                "Internal Peer Role is reserved for Application Peer use.", peerRoleJmxAttribute);
+        }
     }
 
     private InetSocketAddress createAddress() {
         final IpAddress ip = getHost();
+        Preconditions.checkArgument(ip.getIpv4Address() != null || ip.getIpv6Address() != null, "Failed to handle host %s", ip);
         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());
         }
+        return new InetSocketAddress(InetAddresses.forString(ip.getIpv6Address().getValue()), getPort().getValue());
     }
 
     @Override
@@ -107,30 +130,34 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang
 
         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 BGPSessionPreferences prefs = new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), remoteAs, tlvs);
         final BGPPeer bgpClientPeer;
+        final IpAddress host = getNormalizedHost();
         if (getPeerRole() != null) {
-            bgpClientPeer = new BGPPeer(peerName(getHostWithoutValue()), r, getPeerRole());
+            bgpClientPeer = new BGPPeer(peerName(host), r, getPeerRole(), getSimpleRoutingPolicy(), getRpcRegistryDependency());
         } else {
-            bgpClientPeer = new BGPPeer(peerName(getHostWithoutValue()), r, PeerRole.Ibgp);
+            bgpClientPeer = new BGPPeer(peerName(host), r, PeerRole.Ibgp, getSimpleRoutingPolicy(), getRpcRegistryDependency());
         }
 
         bgpClientPeer.registerRootRuntimeBean(getRootRuntimeBeanRegistratorWrapper());
 
-        getPeerRegistryBackwards().addPeer(getHostWithoutValue(), bgpClientPeer, prefs);
+        getPeerRegistryBackwards().addPeer(host, bgpClientPeer, prefs);
+
+        final BGPPeerModuleTracker moduleTracker = new BGPPeerModuleTracker(r.getOpenConfigProvider());
+        moduleTracker.onInstanceCreate();
 
         final CloseableNoEx peerCloseable = new CloseableNoEx() {
             @Override
             public void close() {
                 bgpClientPeer.close();
-                getPeerRegistryBackwards().removePeer(getHostWithoutValue());
+                getPeerRegistryBackwards().removePeer(host);
+                moduleTracker.onInstanceClose();
             }
         };
 
         // Initiate connection
         if(getInitiateConnection()) {
-            final Future<Void> cf = initiateConnection(createAddress(), password, remoteAs, getPeerRegistryBackwards());
+            final Future<Void> cf = initiateConnection(createAddress(), getOptionaPassword(getPassword()), getPeerRegistryBackwards());
             return new CloseableNoEx() {
                 @Override
                 public void close() {
@@ -148,16 +175,6 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang
         void close();
     }
 
-    private String getPasswordOrNull() {
-        final String password;
-        if (getPassword() != null) {
-            password = getPassword().getValue();
-        } else {
-            password = null;
-        }
-        return password;
-    }
-
     private AsNumber getAsOrDefault(final RIB r) {
         // Remote AS number defaults to our local AS
         final AsNumber remoteAs;
@@ -172,48 +189,64 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang
     private List<BgpParameters> getTlvs(final RIB r) {
         final List<BgpParameters> tlvs = new ArrayList<>();
         final List<OptionalCapabilities> caps = new ArrayList<>();
-        caps.add(new OptionalCapabilitiesBuilder().setCParameters(
-            new As4BytesCaseBuilder().setAs4BytesCapability(new As4BytesCapabilityBuilder().setAsNumber(r.getLocalAs()).build()).build()).build());
-        caps.add(new OptionalCapabilitiesBuilder().setCParameters(
-            new GracefulRestartCaseBuilder().setGracefulRestartCapability(
-                new GracefulRestartCapabilityBuilder().build()).build()).build());
+        caps.add(new OptionalCapabilitiesBuilder().setCParameters(new CParametersBuilder().setAs4BytesCapability(
+            new As4BytesCapabilityBuilder().setAsNumber(r.getLocalAs()).build()).build()).build());
+        caps.add(new OptionalCapabilitiesBuilder().setCParameters(new CParametersBuilder().addAugmentation(CParameters1.class,
+            new CParameters1Builder().setGracefulRestartCapability(new GracefulRestartCapabilityBuilder().build()).build()).build()).build());
+
+        if (getRouteRefresh()) {
+            caps.add(new OptionalCapabilitiesBuilder().setCParameters(MultiprotocolCapabilitiesUtil.RR_CAPABILITY).build());
+        }
+
+        if (!getAddPathDependency().isEmpty()) {
+            final List<AddressFamilies> addPathFamilies = filterAddPathDependency(getAddPathDependency());
+            caps.add(new OptionalCapabilitiesBuilder().setCParameters(new CParametersBuilder().addAugmentation(CParameters1.class,
+                new CParameters1Builder().setAddPathCapability(new AddPathCapabilityBuilder().setAddressFamilies(addPathFamilies).build()).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);
             }
 
-            caps.add(new OptionalCapabilitiesBuilder().setCParameters(
-                new MultiprotocolCaseBuilder().setMultiprotocolCapability(new MultiprotocolCapabilityBuilder(t).build()).build()).build());
+            caps.add(new OptionalCapabilitiesBuilder().setCParameters(new CParametersBuilder().addAugmentation(CParameters1.class,
+                new CParameters1Builder().setMultiprotocolCapability(new MultiprotocolCapabilityBuilder(t).build()).build()).build()).build());
         }
         tlvs.add(new BgpParametersBuilder().setOptionalCapabilities(caps).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());
+    private List<AddressFamilies> filterAddPathDependency(final List<AddressFamilies> addPathDependency) {
+        final Map<BgpTableType, AddressFamilies> filteredFamilies = new HashMap<BgpTableType, AddressFamilies>();
+        for (final AddressFamilies family : addPathDependency) {
+            final BgpTableType key = new BgpTableTypeImpl(family.getAfi(), family.getSafi());
+            if (!filteredFamilies.containsKey(key)) {
+                filteredFamilies.put(key, family);
+            } else {
+                LOG.info("Ignoring Add-path dependency {}", family);
+            }
         }
+        return new ArrayList<AddressFamilies>(filteredFamilies.values());
+    }
 
-        throw new IllegalArgumentException("Unexpected host " + host);
+    public IpAddress getNormalizedHost() {
+        final IpAddress host = getHost();
+        if(host.getIpv6Address() != null){
+            return new IpAddress(Ipv6Util.getFullForm(host.getIpv6Address()));
+        }
+        return 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) {
+    private io.netty.util.concurrent.Future<Void> initiateConnection(final InetSocketAddress address, final Optional<Rfc2385Key> password, final BGPPeerRegistry registry) {
+        KeyMapping keys = null;
+        if (password.isPresent()) {
             keys = new KeyMapping();
-            keys.put(address.getAddress(), password.getBytes(Charsets.US_ASCII));
-        } else {
-            keys = null;
+            keys.put(address.getAddress(), password.get().getValue().getBytes(Charsets.US_ASCII));
         }
 
         final RIB rib = getRibDependency();
-        return rib.getDispatcher().createReconnectingClient(address, remoteAs, registry, rib.getTcpStrategyFactory(),
-            rib.getSessionStrategyFactory(), keys);
+        final Optional<KeyMapping> optionalKey = Optional.fromNullable(keys);
+        return rib.getDispatcher().createReconnectingClient(address, registry, getRetrytimer(), optionalKey);
     }
 
     private BGPPeerRegistry getPeerRegistryBackwards() {
@@ -231,4 +264,42 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang
         return null;
     }
 
+    private final class BGPPeerModuleTracker implements BGPConfigModuleTracker {
+
+        private final BGPOpenconfigMapper<BGPPeerInstanceConfiguration> neighborProvider;
+        private final BGPPeerInstanceConfiguration bgpPeerInstanceConfiguration;
+
+        public BGPPeerModuleTracker(final Optional<BGPOpenConfigProvider> openconfigProvider) {
+            if (openconfigProvider.isPresent()) {
+                this.neighborProvider = openconfigProvider.get().getOpenConfigMapper(BGPPeerInstanceConfiguration.class);
+            } else {
+                this.neighborProvider = null;
+            }
+            final InstanceConfigurationIdentifier identifier = new InstanceConfigurationIdentifier(getIdentifier().getInstanceName());
+            this.bgpPeerInstanceConfiguration = new BGPPeerInstanceConfiguration(identifier, Rev130715Util.getIpvAddress(getNormalizedHost()),
+                    Rev130715Util.getPort(getPort().getValue()), getHoldtimer(), getPeerRole(), getInitiateConnection(),
+                        getAdvertizedTableDependency(), Rev130715Util.getASNumber(getAsOrDefault(getRibDependency()).getValue()),
+                        getOptionaPassword(getPassword()), getAddPathDependency());
+        }
+
+        @Override
+        public void onInstanceCreate() {
+            if (this.neighborProvider != null) {
+                this.neighborProvider.writeConfiguration(this.bgpPeerInstanceConfiguration);
+            }
+        }
+
+        @Override
+        public void onInstanceClose() {
+            if (this.neighborProvider != null) {
+                this.neighborProvider.removeConfiguration(this.bgpPeerInstanceConfiguration);
+            }
+        }
+
+    }
+
+    private Optional<Rfc2385Key> getOptionaPassword(final Rfc2385Key password) {
+        return password != null && ! password.getValue().isEmpty() ? Optional.of(password) : Optional.<Rfc2385Key>absent();
+    }
+
 }