BUG-1116 : if a new peer connects, advertize everything in loc-rib
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / RIBImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import com.google.common.base.Objects;
11 import com.google.common.base.Objects.ToStringHelper;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.Futures;
17
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.BlockingQueue;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.ConcurrentMap;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.LinkedBlockingQueue;
25 import javax.annotation.concurrent.ThreadSafe;
26
27 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
28 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
29 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
30 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
31 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
34 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
35 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
36 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOut;
37 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOutRegistration;
38 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
39 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
40 import org.opendaylight.protocol.bgp.rib.spi.AbstractAdjRIBs;
41 import org.opendaylight.protocol.bgp.rib.spi.AdjRIBsIn;
42 import org.opendaylight.protocol.bgp.rib.spi.BGPObjectComparator;
43 import org.opendaylight.protocol.bgp.rib.spi.Peer;
44 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
45 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.Nlri;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.PathAttributes;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.WithdrawnRoutes;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes2;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.destination.type.DestinationIpv4CaseBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.destination.type.destination.ipv4._case.DestinationIpv4Builder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlriBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlriBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRibBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRibBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
75 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 @ThreadSafe
80 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener {
81     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
82     private static final Update EOR = new UpdateBuilder().build();
83     private static final TablesKey IPV4_UNICAST_TABLE = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
84     private final ConcurrentMap<Peer, AdjRIBsOut> ribOuts = new ConcurrentHashMap<>();
85     private final ReconnectStrategyFactory tcpStrategyFactory;
86     private final ReconnectStrategyFactory sessionStrategyFactory;
87     private final BGPObjectComparator comparator;
88     private final BGPDispatcher dispatcher;
89     private final BindingTransactionChain chain;
90     private final AsNumber localAs;
91     private final Ipv4Address bgpIdentifier;
92     private final List<BgpTableType> localTables;
93     private final RIBTables tables;
94     private final BlockingQueue<Peer> peers;
95     private final Thread scheduler = new Thread(new Runnable() {
96
97         @Override
98         public void run() {
99             try {
100                 final Peer peer = RIBImpl.this.peers.take();
101                 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
102                 for (final BgpTableType key : RIBImpl.this.localTables) {
103                     final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
104                     final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
105                     adj.addAllEntries(trans);
106                     Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
107                         @Override
108                         public void onSuccess(final Void result) {
109                             LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
110                         }
111
112                         @Override
113                         public void onFailure(final Throwable t) {
114                             LOG.error("Failed to update peer {} with RIB {}", peer, t);
115                         }
116                     });
117                 }
118             } catch (final InterruptedException e) {
119
120             }
121         }
122     });
123
124     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final RIBExtensionConsumerContext extensions,
125             final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory,
126             final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final List<BgpTableType> localTables) {
127         super(InstanceIdentifier.builder(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))).toInstance());
128         this.chain = dps.createTransactionChain(this);
129         this.localAs = Preconditions.checkNotNull(localAs);
130         this.comparator = new BGPObjectComparator(localAs);
131         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
132         this.dispatcher = Preconditions.checkNotNull(dispatcher);
133         this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
134         this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
135         this.localTables = ImmutableList.copyOf(localTables);
136         this.tables = new RIBTables(extensions);
137         this.peers = new LinkedBlockingQueue<>();
138
139         LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
140
141         final ReadWriteTransaction trans = chain.newReadWriteTransaction();
142         Optional<Rib> o;
143         try {
144             o = trans.read(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier()).get();
145         } catch (InterruptedException | ExecutionException e) {
146             throw new IllegalStateException("Failed to read topology", e);
147         }
148         Preconditions.checkState(!o.isPresent(), "Data provider conflict detected on object {}", getInstanceIdentifier());
149
150         // put empty BgpRib if not exists
151         trans.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(BgpRib.class).build(), new BgpRibBuilder().build());
152         trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(), new RibBuilder().setKey(new RibKey(ribId)).setId(ribId).setLocRib(
153                 new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build());
154
155         for (BgpTableType t : localTables) {
156             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
157             if (this.tables.create(trans, this, key) == null) {
158                 LOG.debug("Did not create local table for unhandled table type {}", t);
159             }
160         }
161
162         Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
163             @Override
164             public void onSuccess(final Void result) {
165                 LOG.trace("Change committed successfully");
166             }
167
168             @Override
169             public void onFailure(final Throwable t) {
170                 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
171             }
172         });
173     }
174
175     synchronized void initTables(final byte[] remoteBgpId) {
176     }
177
178     @Override
179     public synchronized void updateTables(final Peer peer, final Update message) {
180         final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
181
182         if (!EOR.equals(message)) {
183             final WithdrawnRoutes wr = message.getWithdrawnRoutes();
184             if (wr != null) {
185                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
186                 if (ari != null) {
187                     ari.removeRoutes(
188                             trans,
189                             peer,
190                             new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
191                                     new WithdrawnRoutesBuilder().setDestinationType(
192                                             new DestinationIpv4CaseBuilder().setDestinationIpv4(
193                                                     new DestinationIpv4Builder().setIpv4Prefixes(wr.getWithdrawnRoutes()).build()).build()).build()).build());
194                 } else {
195                     LOG.debug("Not removing objects from unhandled IPv4 Unicast");
196                 }
197             }
198
199             final PathAttributes attrs = message.getPathAttributes();
200             if (attrs != null) {
201                 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
202                 if (mpu != null) {
203                     final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
204
205                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
206                     if (ari != null) {
207                         ari.removeRoutes(trans, peer, nlri);
208                     } else {
209                         LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
210                     }
211                 }
212             }
213
214             final Nlri ar = message.getNlri();
215             if (ar != null) {
216                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
217                 if (ari != null) {
218                     final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
219                             UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
220                                     new AdvertizedRoutesBuilder().setDestinationType(
221                                             new DestinationIpv4CaseBuilder().setDestinationIpv4(
222                                                     new DestinationIpv4Builder().setIpv4Prefixes(ar.getNlri()).build()).build()).build());
223                     if (attrs != null) {
224                         b.setCNextHop(attrs.getCNextHop());
225                     }
226
227                     ari.addRoutes(trans, peer, b.build(), attrs);
228                 } else {
229                     LOG.debug("Not adding objects from unhandled IPv4 Unicast");
230                 }
231             }
232
233             if (attrs != null) {
234                 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
235                 if (mpr != null) {
236                     final MpReachNlri nlri = mpr.getMpReachNlri();
237
238                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
239                     if (ari != null) {
240                         if (message.equals(ari.endOfRib())) {
241                             ari.markUptodate(trans, peer);
242                         } else {
243                             ari.addRoutes(trans, peer, nlri, attrs);
244                         }
245                     } else {
246                         LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
247                     }
248                 }
249             }
250         } else {
251             final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
252             if (ari != null) {
253                 ari.markUptodate(trans, peer);
254             } else {
255                 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
256             }
257         }
258
259         Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
260             @Override
261             public void onSuccess(final Void result) {
262                 LOG.debug("RIB modification successfully committed.");
263             }
264
265             @Override
266             public void onFailure(final Throwable t) {
267                 LOG.error("Failed to commit RIB modification", t);
268             }
269         });
270     }
271
272     @Override
273     public synchronized void clearTable(final Peer peer, final TablesKey key) {
274         final AdjRIBsIn<?, ?> ari = this.tables.get(key);
275         if (ari != null) {
276             final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(ribOuts, comparator, this.chain.newWriteOnlyTransaction());
277             ari.clear(trans, peer);
278
279             Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
280                 @Override
281                 public void onSuccess(final Void result) {
282                     LOG.trace("Table {} cleared successfully", key);
283                 }
284
285                 @Override
286                 public void onFailure(final Throwable t) {
287                     LOG.error("Failed to clear table {}", key, t);
288                 }
289             });
290         }
291     }
292
293     @Override
294     public String toString() {
295         return addToStringAttributes(Objects.toStringHelper(this)).toString();
296     }
297
298     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
299         return toStringHelper;
300     }
301
302     @Override
303     public void close() throws InterruptedException, ExecutionException {
304         final WriteTransaction t = this.chain.newWriteOnlyTransaction();
305         t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
306         t.submit().get();
307         chain.close();
308     }
309
310     @Override
311     public AsNumber getLocalAs() {
312         return this.localAs;
313     }
314
315     @Override
316     public Ipv4Address getBgpIdentifier() {
317         return this.bgpIdentifier;
318     }
319
320     @Override
321     public List<? extends BgpTableType> getLocalTables() {
322         return this.localTables;
323     }
324
325     @Override
326     public ReconnectStrategyFactory getTcpStrategyFactory() {
327         return this.tcpStrategyFactory;
328     }
329
330     @Override
331     public ReconnectStrategyFactory getSessionStrategyFactory() {
332         return this.sessionStrategyFactory;
333     }
334
335     @Override
336     public BGPDispatcher getDispatcher() {
337         return this.dispatcher;
338     }
339
340     @Override
341     public void initTable(final Peer bgpPeer, final TablesKey key) {
342         // FIXME: BUG-196: support graceful restart
343     }
344
345     @Override
346     public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
347         final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
348             @Override
349             protected void removeRegistration() {
350                 ribOuts.remove(peer, aro);
351             }
352         };
353
354         this.ribOuts.put(peer, aro);
355         LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
356         try {
357             this.peers.put(peer);
358             this.scheduler.run();
359         } catch (final InterruptedException e) {
360             //
361         }
362         return reg;
363     }
364
365     @Override
366     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
367         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
368     }
369
370     @Override
371     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
372         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
373     }
374 }