Merge changes Ia42de3cd,Iae8be8b9
[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))).toInstance());
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
222                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
223                     if (ari != null) {
224                         ari.removeRoutes(trans, peer, nlri);
225                     } else {
226                         LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
227                     }
228                 }
229             }
230
231             final Nlri ar = message.getNlri();
232             if (ar != null) {
233                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
234                 if (ari != null) {
235                     /*
236                      * create MPReach for the routes to be handled in the same way as linkstate routes
237                      */
238                     final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
239                         UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
240                             new AdvertizedRoutesBuilder().setDestinationType(
241                                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
242                                     new DestinationIpv4Builder().setIpv4Prefixes(ar.getNlri()).build()).build()).build());
243                     if (attrs != null) {
244                         b.setCNextHop(attrs.getCNextHop());
245                     }
246
247                     ari.addRoutes(trans, peer, b.build(), attrs);
248                 } else {
249                     LOG.debug("Not adding objects from unhandled IPv4 Unicast");
250                 }
251             }
252
253             if (attrs != null) {
254                 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
255                 if (mpr != null) {
256                     final MpReachNlri nlri = mpr.getMpReachNlri();
257
258                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
259                     if (ari != null) {
260                         if (message.equals(ari.endOfRib())) {
261                             ari.markUptodate(trans, peer);
262                         } else {
263                             ari.addRoutes(trans, peer, nlri, attrs);
264                         }
265                     } else {
266                         LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
267                     }
268                 }
269             }
270         } else {
271             final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
272             if (ari != null) {
273                 ari.markUptodate(trans, peer);
274             } else {
275                 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
276             }
277         }
278
279         Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
280             @Override
281             public void onSuccess(final Void result) {
282                 LOG.debug("RIB modification successfully committed.");
283             }
284
285             @Override
286             public void onFailure(final Throwable t) {
287                 LOG.error("Failed to commit RIB modification", t);
288             }
289         });
290     }
291
292     @Override
293     public synchronized void clearTable(final Peer peer, final TablesKey key) {
294         final AdjRIBsIn<?, ?> ari = this.tables.get(key);
295         if (ari != null) {
296             final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
297             ari.clear(trans, peer);
298
299             Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
300                 @Override
301                 public void onSuccess(final Void result) {
302                     LOG.trace("Table {} cleared successfully", key);
303                 }
304
305                 @Override
306                 public void onFailure(final Throwable t) {
307                     LOG.error("Failed to clear table {}", key, t);
308                 }
309             });
310         }
311     }
312
313     @Override
314     public String toString() {
315         return addToStringAttributes(Objects.toStringHelper(this)).toString();
316     }
317
318     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
319         return toStringHelper;
320     }
321
322     @SuppressWarnings("unchecked")
323     protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
324         return (AdjRIBsIn<K, V>) this.tables.get(key);
325     }
326
327     @Override
328     public void close() throws InterruptedException, ExecutionException {
329         final WriteTransaction t = this.chain.newWriteOnlyTransaction();
330         t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
331         t.submit().get();
332         this.chain.close();
333     }
334
335     @Override
336     public AsNumber getLocalAs() {
337         return this.localAs;
338     }
339
340     @Override
341     public Ipv4Address getBgpIdentifier() {
342         return this.bgpIdentifier;
343     }
344
345     @Override
346     public Set<? extends BgpTableType> getLocalTables() {
347         return this.localTables;
348     }
349
350     @Override
351     public ReconnectStrategyFactory getTcpStrategyFactory() {
352         return this.tcpStrategyFactory;
353     }
354
355     @Override
356     public ReconnectStrategyFactory getSessionStrategyFactory() {
357         return this.sessionStrategyFactory;
358     }
359
360     @Override
361     public BGPDispatcher getDispatcher() {
362         return this.dispatcher;
363     }
364
365     @Override
366     public void initTable(final Peer bgpPeer, final TablesKey key) {
367         // FIXME: BUG-196: support graceful restart
368     }
369
370     @Override
371     public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
372         final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
373             @Override
374             protected void removeRegistration() {
375                 RIBImpl.this.ribOuts.remove(peer, aro);
376             }
377         };
378
379         this.ribOuts.put(peer, aro);
380         LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
381         try {
382             this.peers.put(peer);
383             new Thread(this.scheduler).start();
384         } catch (final InterruptedException e) {
385             //
386         }
387         return reg;
388     }
389
390     @Override
391     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
392         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
393     }
394
395     @Override
396     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
397         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
398     }
399
400     @Override
401     public long getRoutesCount(final TablesKey key) {
402         try {
403             final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
404                     getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
405             if (tableMaybe.isPresent()) {
406                 final Tables table = tableMaybe.get();
407                 if (table.getRoutes() instanceof Ipv4RoutesCase) {
408                     final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
409                     if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
410                         return routesCase.getIpv4Routes().getIpv4Route().size();
411                     }
412                 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
413                     final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
414                     if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
415                         return routesCase.getIpv6Routes().getIpv6Route().size();
416                     }
417                 }
418             }
419         } catch (final ReadFailedException e) {
420             //no-op
421         }
422         return 0;
423     }
424 }