Merge "Refactor linkstate."
[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.ImmutableSet;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.Futures;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.Set;
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 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
34 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
35 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOut;
36 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOutRegistration;
37 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
38 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
39 import org.opendaylight.protocol.bgp.rib.spi.AbstractAdjRIBs;
40 import org.opendaylight.protocol.bgp.rib.spi.AdjRIBsIn;
41 import org.opendaylight.protocol.bgp.rib.spi.BGPObjectComparator;
42 import org.opendaylight.protocol.bgp.rib.spi.Peer;
43 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
44 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.Nlri;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.PathAttributes;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.WithdrawnRoutes;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes2;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.destination.type.DestinationIpv4CaseBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.destination.type.destination.ipv4._case.DestinationIpv4Builder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlriBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlriBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
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.rib.rev130925.rib.tables.routes.Ipv4RoutesCase;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.Ipv6RoutesCase;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
77 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
80
81 @ThreadSafe
82 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener {
83     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
84     private static final Update EOR = new UpdateBuilder().build();
85     private static final TablesKey IPV4_UNICAST_TABLE = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
86
87     /*
88      * FIXME: performance: this needs to be turned into a Peer->offset map.
89      *        The offset is used to locate a the per-peer state entry in the
90      *        RIB tables.
91      *
92      *        For the first release, that map is updated whenever configuration
93      *        changes and remains constant on peer flaps. On re-configuration
94      *        a resize task is scheduled, so large tables may take some time
95      *        before they continue reacting to updates.
96      *
97      *        For subsequent releases, if we make the reformat process concurrent,
98      *        we can trigger reformats when Graceful Restart Time expires for a
99      *        particular peer.
100      */
101     private final ConcurrentMap<Peer, AdjRIBsOut> ribOuts = new ConcurrentHashMap<>();
102     private final ReconnectStrategyFactory tcpStrategyFactory;
103     private final ReconnectStrategyFactory sessionStrategyFactory;
104
105     /**
106      * BGP Best Path selection comparator for ingress best path selection.
107      */
108     private final BGPObjectComparator comparator;
109     private final BGPDispatcher dispatcher;
110     private final BindingTransactionChain chain;
111     private final AsNumber localAs;
112     private final Ipv4Address bgpIdentifier;
113     private final Set<BgpTableType> localTables;
114     private final RIBTables tables;
115     private final BlockingQueue<Peer> peers;
116     private final DataBroker dataBroker;
117
118     private final Runnable scheduler = new Runnable() {
119         @Override
120         public void run() {
121             try {
122                 final Peer peer = RIBImpl.this.peers.take();
123                 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
124                 for (final BgpTableType key : RIBImpl.this.localTables) {
125                     final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
126                     final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
127                     adj.addAllEntries(trans);
128                     Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
129                         @Override
130                         public void onSuccess(final Void result) {
131                             LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
132                         }
133                         @Override
134                         public void onFailure(final Throwable t) {
135                             LOG.error("Failed to update peer {} with RIB {}", peer, t);
136                         }
137                     });
138                 }
139             } catch (final InterruptedException e) {
140                 LOG.info("Scheduler thread was interrupted.", e);
141             }
142         }
143     };
144
145     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final RIBExtensionConsumerContext extensions,
146         final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory,
147         final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final List<BgpTableType> localTables) {
148         super(InstanceIdentifier.builder(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))).build());
149         this.chain = dps.createTransactionChain(this);
150         this.localAs = Preconditions.checkNotNull(localAs);
151         this.comparator = new BGPObjectComparator(localAs);
152         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
153         this.dispatcher = Preconditions.checkNotNull(dispatcher);
154         this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
155         this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
156         this.localTables = ImmutableSet.copyOf(localTables);
157         this.tables = new RIBTables(extensions);
158         this.peers = new LinkedBlockingQueue<>();
159         this.dataBroker = dps;
160
161         LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
162
163         final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
164
165         // put empty BgpRib if not exists
166         trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(), new RibBuilder().setKey(new RibKey(ribId)).setId(ribId).setLocRib(
167             new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
168
169         for (final BgpTableType t : localTables) {
170             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
171             if (this.tables.create(trans, this, key) == null) {
172                 LOG.debug("Did not create local table for unhandled table type {}", t);
173             }
174         }
175
176         Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
177             @Override
178             public void onSuccess(final Void result) {
179                 LOG.trace("Change committed successfully");
180             }
181
182             @Override
183             public void onFailure(final Throwable t) {
184                 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
185             }
186         });
187     }
188
189     synchronized void initTables(final byte[] remoteBgpId) {
190     }
191
192     @Override
193     public synchronized void updateTables(final Peer peer, final Update message) {
194         final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
195
196         if (!EOR.equals(message)) {
197             final WithdrawnRoutes wr = message.getWithdrawnRoutes();
198             if (wr != null) {
199                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
200                 if (ari != null) {
201                     /*
202                      * create MPUnreach for the routes to be handled in the same way as linkstate routes
203                      */
204                     ari.removeRoutes(
205                         trans,
206                         peer,
207                         new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
208                             new WithdrawnRoutesBuilder().setDestinationType(
209                                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
210                                     new DestinationIpv4Builder().setIpv4Prefixes(wr.getWithdrawnRoutes()).build()).build()).build()).build());
211                 } else {
212                     LOG.debug("Not removing objects from unhandled IPv4 Unicast");
213                 }
214             }
215
216             final PathAttributes attrs = message.getPathAttributes();
217             if (attrs != null) {
218                 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
219                 if (mpu != null) {
220                     final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
221                     // EOR messages do not contain withdrawn routes
222                     if (nlri.getWithdrawnRoutes() != null) {
223                         final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
224                         if (ari != null) {
225                             ari.removeRoutes(trans, peer, nlri);
226                         } else {
227                             LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
228                         }
229                     }
230                 }
231             }
232
233             final Nlri ar = message.getNlri();
234             if (ar != null) {
235                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
236                 if (ari != null) {
237                     /*
238                      * create MPReach for the routes to be handled in the same way as linkstate routes
239                      */
240                     final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
241                         UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
242                             new AdvertizedRoutesBuilder().setDestinationType(
243                                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
244                                     new DestinationIpv4Builder().setIpv4Prefixes(ar.getNlri()).build()).build()).build());
245                     if (attrs != null) {
246                         b.setCNextHop(attrs.getCNextHop());
247                     }
248
249                     ari.addRoutes(trans, peer, b.build(), attrs);
250                 } else {
251                     LOG.debug("Not adding objects from unhandled IPv4 Unicast");
252                 }
253             }
254
255             if (attrs != null) {
256                 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
257                 if (mpr != null) {
258                     final MpReachNlri nlri = mpr.getMpReachNlri();
259
260                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
261                     if (ari != null) {
262                         if (message.equals(ari.endOfRib())) {
263                             ari.markUptodate(trans, peer);
264                         } else {
265                             ari.addRoutes(trans, peer, nlri, attrs);
266                         }
267                     } else {
268                         LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
269                     }
270                 }
271             }
272         } else {
273             final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
274             if (ari != null) {
275                 ari.markUptodate(trans, peer);
276             } else {
277                 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
278             }
279         }
280
281         Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
282             @Override
283             public void onSuccess(final Void result) {
284                 LOG.debug("RIB modification successfully committed.");
285             }
286
287             @Override
288             public void onFailure(final Throwable t) {
289                 LOG.error("Failed to commit RIB modification", t);
290             }
291         });
292     }
293
294     @Override
295     public synchronized void clearTable(final Peer peer, final TablesKey key) {
296         final AdjRIBsIn<?, ?> ari = this.tables.get(key);
297         if (ari != null) {
298             final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
299             ari.clear(trans, peer);
300
301             Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
302                 @Override
303                 public void onSuccess(final Void result) {
304                     LOG.trace("Table {} cleared successfully", key);
305                 }
306
307                 @Override
308                 public void onFailure(final Throwable t) {
309                     LOG.error("Failed to clear table {}", key, t);
310                 }
311             });
312         }
313     }
314
315     @Override
316     public String toString() {
317         return addToStringAttributes(Objects.toStringHelper(this)).toString();
318     }
319
320     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
321         return toStringHelper;
322     }
323
324     @SuppressWarnings("unchecked")
325     protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
326         return (AdjRIBsIn<K, V>) this.tables.get(key);
327     }
328
329     @Override
330     public void close() throws InterruptedException, ExecutionException {
331         final WriteTransaction t = this.chain.newWriteOnlyTransaction();
332         t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
333         t.submit().get();
334         this.chain.close();
335     }
336
337     @Override
338     public AsNumber getLocalAs() {
339         return this.localAs;
340     }
341
342     @Override
343     public Ipv4Address getBgpIdentifier() {
344         return this.bgpIdentifier;
345     }
346
347     @Override
348     public Set<? extends BgpTableType> getLocalTables() {
349         return this.localTables;
350     }
351
352     @Override
353     public ReconnectStrategyFactory getTcpStrategyFactory() {
354         return this.tcpStrategyFactory;
355     }
356
357     @Override
358     public ReconnectStrategyFactory getSessionStrategyFactory() {
359         return this.sessionStrategyFactory;
360     }
361
362     @Override
363     public BGPDispatcher getDispatcher() {
364         return this.dispatcher;
365     }
366
367     @Override
368     public void initTable(final Peer bgpPeer, final TablesKey key) {
369         // FIXME: BUG-196: support graceful restart
370     }
371
372     @Override
373     public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
374         final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
375             @Override
376             protected void removeRegistration() {
377                 RIBImpl.this.ribOuts.remove(peer, aro);
378             }
379         };
380
381         this.ribOuts.put(peer, aro);
382         LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
383         try {
384             this.peers.put(peer);
385             new Thread(this.scheduler).start();
386         } catch (final InterruptedException e) {
387             //
388         }
389         return reg;
390     }
391
392     @Override
393     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
394         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
395     }
396
397     @Override
398     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
399         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
400     }
401
402     @Override
403     public long getRoutesCount(final TablesKey key) {
404         try {
405             final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
406                     getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
407             if (tableMaybe.isPresent()) {
408                 final Tables table = tableMaybe.get();
409                 if (table.getRoutes() instanceof Ipv4RoutesCase) {
410                     final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
411                     if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
412                         return routesCase.getIpv4Routes().getIpv4Route().size();
413                     }
414                 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
415                     final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
416                     if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
417                         return routesCase.getIpv6Routes().getIpv6Route().size();
418                     }
419                 }
420             }
421         } catch (final ReadFailedException e) {
422             //no-op
423         }
424         return 0;
425     }
426 }