BUG-7976: Race between peer removal and routes update
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / BGPPeer.java
index 7c5230169830415bbc3b62a42218bb86d1f731f9..4cbcd0cef849d7c73af7e4259ba06e723b38b0ff 100644 (file)
@@ -13,7 +13,10 @@ import static org.opendaylight.protocol.bgp.rib.impl.AdjRibInWriter.isLearnNone;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.net.InetAddresses;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -47,9 +50,11 @@ import org.opendaylight.protocol.bgp.rib.impl.stats.peer.BGPSessionStats;
 import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
 import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
 import org.opendaylight.protocol.bgp.rib.spi.BGPTerminationReason;
+import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
 import org.opendaylight.protocol.bgp.rib.spi.Peer;
 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
+import org.opendaylight.protocol.concepts.AbstractRegistration;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.DestinationIpv4Builder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
@@ -58,7 +63,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
 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.AttributesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpAddPathTableType;
+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.RouteRefresh;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.SendReceive;
 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.multiprotocol.rev130919.update.attributes.MpReachNlri;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlriBuilder;
@@ -87,7 +95,7 @@ import org.slf4j.LoggerFactory;
  * Class representing a peer. We have a single instance for each peer, which provides translation from BGP events into
  * RIB actions.
  */
-public class BGPPeer implements BGPSessionListener, Peer, AutoCloseable, BGPPeerRuntimeMXBean, TransactionChainListener {
+public class BGPPeer implements BGPSessionListener, Peer, BGPPeerRuntimeMXBean, TransactionChainListener {
 
     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
 
@@ -114,29 +122,35 @@ public class BGPPeer implements BGPSessionListener, Peer, AutoCloseable, BGPPeer
     private final PeerRole peerRole;
     private final Optional<SimpleRoutingPolicy> simpleRoutingPolicy;
     private final BGPPeerStats peerStats;
+    private YangInstanceIdentifier peerIId;
+    private final Set<AbstractRegistration> tableRegistration = new HashSet<>();
 
     public BGPPeer(final String name, final RIB rib, final PeerRole role, final SimpleRoutingPolicy peerStatus, final RpcProviderRegistry rpcRegistry) {
         this.peerRole = role;
         this.simpleRoutingPolicy = Optional.ofNullable(peerStatus);
         this.rib = Preconditions.checkNotNull(rib);
         this.name = name;
-        this.chain = rib.createPeerChain(this);
-        this.ribWriter = AdjRibInWriter.create(rib.getYangRibId(), this.peerRole, this.simpleRoutingPolicy, this.chain);
         this.rpcRegistry = rpcRegistry;
         this.peerStats = new BGPPeerStatsImpl(this.name, this.tables);
-
-        // add current peer to "configured BGP peer" stats
-        this.rib.getRenderStats().getConfiguredPeerCounter().increaseCount();
+        this.chain = rib.createPeerChain(this);
     }
 
     public BGPPeer(final String name, final RIB rib, final PeerRole role, final RpcProviderRegistry rpcRegistry) {
         this(name, rib, role, null, rpcRegistry);
     }
 
+    public void instantiateServiceInstance() {
+        // add current peer to "configured BGP peer" stats
+        this.rib.getRenderStats().getConfiguredPeerCounter().increaseCount();
+        this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this.simpleRoutingPolicy, this.chain);
+    }
+
+    // FIXME ListenableFuture<?> should be used once closeServiceInstance uses wildcard too
     @Override
-    public synchronized void close() {
-        releaseConnection();
+    public synchronized ListenableFuture<Void> close() {
+        final ListenableFuture<Void> future = releaseConnection();
         this.chain.close();
+        return future;
     }
 
     @Override
@@ -160,7 +174,7 @@ public class BGPPeer implements BGPSessionListener, Peer, AutoCloseable, BGPPeer
         final AdjRibOutListener listener = this.adjRibOutListenerSet.get(key);
         if (listener != null) {
             listener.close();
-            this.adjRibOutListenerSet.remove(listener);
+            this.adjRibOutListenerSet.remove(key);
             createAdjRibOutListener(RouterIds.createPeerId(session.getBgpId()), key, listener.isMpSupported());
         } else {
             LOG.info("Ignoring RouteRefresh message. Afi/Safi is not supported: {}, {}.", rrAfi, rrSafi);
@@ -276,26 +290,37 @@ public class BGPPeer implements BGPSessionListener, Peer, AutoCloseable, BGPPeer
     @Override
     public synchronized void onSessionUp(final BGPSession session) {
         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
-        LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name, addPathTablesType,
-                session.getAdvertisedAddPathTableTypes());
+        final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
+        LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name, advertizedTableTypes, addPathTablesType);
         this.session = session;
 
         this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
         final PeerId peerId = RouterIds.createPeerId(session.getBgpId());
 
-        this.tables.addAll(this.session.getAdvertisedTableTypes().stream().map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
+        this.tables.addAll(advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
         final boolean announceNone = isAnnounceNone(this.simpleRoutingPolicy);
+        final Map<TablesKey, SendReceive> addPathTableMaps = mapTableTypesFamilies(addPathTablesType);
+        this.peerIId = this.rib.getYangRibId().node(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer.QNAME)
+            .node(IdentifierUtils.domPeerId(peerId));
+
         if(!announceNone) {
             createAdjRibOutListener(peerId);
         }
+        this.tables.forEach(tablesKey -> {
+            final ExportPolicyPeerTracker exportTracker = this.rib.getExportPolicyPeerTracker(tablesKey);
+            if (exportTracker != null) {
+                this.tableRegistration.add(exportTracker.registerPeer(peerId, addPathTableMaps.get(tablesKey), this.peerIId, this.peerRole,
+                    this.simpleRoutingPolicy));
+            }
+        });
         addBgp4Support(peerId, announceNone);
 
         if(!isLearnNone(this.simpleRoutingPolicy)) {
-            final YangInstanceIdentifier peerIId = this.rib.getYangRibId().node(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer.QNAME).node(IdentifierUtils.domPeerId(peerId));
-            this.effRibInWriter = EffectiveRibInWriter.create(this.rib.getService(), this.rib.createPeerChain(this), peerIId, this.rib.getImportPolicyPeerTracker(),
-                    this.rib.getRibSupportContext(), this.peerRole, this.peerStats.getEffectiveRibInRouteCounters(), this.peerStats.getAdjRibInRouteCounters());
+            this.effRibInWriter = EffectiveRibInWriter.create(this.rib.getService(), this.rib.createPeerChain(this), this.peerIId,
+                this.rib.getImportPolicyPeerTracker(), this.rib.getRibSupportContext(), this.peerRole,
+                this.peerStats.getEffectiveRibInRouteCounters(), this.peerStats.getAdjRibInRouteCounters());
         }
-        this.ribWriter = this.ribWriter.transform(peerId, this.rib.getRibSupportContext(), this.tables, addPathTablesType);
+        this.ribWriter = this.ribWriter.transform(peerId, this.rib.getRibSupportContext(), this.tables, addPathTableMaps);
 
         // register BGP Peer stats
         this.peerStats.getSessionEstablishedCounter().increaseCount();
@@ -331,21 +356,23 @@ public class BGPPeer implements BGPSessionListener, Peer, AutoCloseable, BGPPeer
         // not particularly nice
         if (context != null && this.session instanceof BGPSessionImpl) {
             this.adjRibOutListenerSet.put(key, AdjRibOutListener.create(peerId, key, this.rib.getYangRibId(), this.rib.getCodecsRegistry(),
-                    context.getRibSupport(), this.rib.getService(), ((BGPSessionImpl) this.session).getLimiter(), mpSupport, this.peerStats.getAdjRibOutRouteCounters().init(key)));
+                context.getRibSupport(), this.rib.getService(), ((BGPSessionImpl) this.session).getLimiter(), mpSupport,
+                this.peerStats.getAdjRibOutRouteCounters().init(key)));
         }
     }
 
-    private synchronized void cleanup() {
+    private ListenableFuture<Void> cleanup() {
         // FIXME: BUG-196: support graceful
-        for (final AdjRibOutListener adjRibOutListener : this.adjRibOutListenerSet.values()) {
-            adjRibOutListener.close();
-        }
+        this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
         this.adjRibOutListenerSet.clear();
         if (this.effRibInWriter != null) {
             this.effRibInWriter.close();
         }
-        this.ribWriter.removePeer();
         this.tables.clear();
+        if (this.ribWriter != null) {
+            return this.ribWriter.removePeer();
+        }
+        return Futures.immediateFuture(null);
     }
 
     @Override
@@ -381,22 +408,23 @@ public class BGPPeer implements BGPSessionListener, Peer, AutoCloseable, BGPPeer
     }
 
     @Override
-    public void releaseConnection() {
+    public synchronized ListenableFuture<Void> releaseConnection() {
         if (this.rpcRegistration != null) {
             this.rpcRegistration.close();
         }
-        addPeerToDisconnectedSharedList();
-        cleanup();
+        closeRegistration();
+        final ListenableFuture<Void> future = cleanup();
         dropConnection();
+        return future;
     }
 
-    private void addPeerToDisconnectedSharedList() {
-        if(this.session != null) {
-            this.rib.getCacheDisconnectedPeers().insertDesconectedPeer(this.session.getBgpId());
+    private void closeRegistration() {
+        for (final AbstractRegistration tableCloseable : this.tableRegistration) {
+            tableCloseable.close();
         }
+        this.tableRegistration.clear();
     }
 
-    @GuardedBy("this")
     private void dropConnection() {
         if (this.runtimeReg != null) {
             this.runtimeReg.close();
@@ -414,11 +442,6 @@ public class BGPPeer implements BGPSessionListener, Peer, AutoCloseable, BGPPeer
         }
     }
 
-    @Override
-    public boolean isSessionActive() {
-        return this.session != null;
-    }
-
     @Override
     public synchronized byte[] getRawIdentifier() {
         return Arrays.copyOf(this.rawIdentifier, this.rawIdentifier.length);
@@ -436,10 +459,6 @@ public class BGPPeer implements BGPSessionListener, Peer, AutoCloseable, BGPPeer
         }
     }
 
-    public synchronized void registerRootRuntimeBean(final BGPPeerRuntimeRegistrator registrator) {
-        this.registrator = registrator;
-    }
-
     @Override
     public BgpSessionState getBgpSessionState() {
         if (this.session instanceof BGPSessionStats) {
@@ -471,4 +490,9 @@ public class BGPPeer implements BGPSessionListener, Peer, AutoCloseable, BGPPeer
     public void markUptodate(final TablesKey tablesKey) {
         this.ribWriter.markTableUptodate(tablesKey);
     }
+
+    private static Map<TablesKey, SendReceive> mapTableTypesFamilies(final List<AddressFamilies> addPathTablesType) {
+        return ImmutableMap.copyOf(addPathTablesType.stream().collect(Collectors.toMap(af -> new TablesKey(af.getAfi(), af.getSafi()),
+            BgpAddPathTableType::getSendReceive)));
+    }
 }