Bug-2080: BGP statistics.
[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 import java.util.Collections;
18 import java.util.List;
19 import java.util.concurrent.BlockingQueue;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.LinkedBlockingQueue;
24 import javax.annotation.concurrent.ThreadSafe;
25 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
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.BgpRibBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
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.LocRib;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRibBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.Ipv4RoutesCase;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.Ipv6RoutesCase;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
78 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
79 import org.slf4j.Logger;
80 import org.slf4j.LoggerFactory;
81
82 @ThreadSafe
83 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener {
84     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
85     private static final Update EOR = new UpdateBuilder().build();
86     private static final TablesKey IPV4_UNICAST_TABLE = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
87     private final ConcurrentMap<Peer, AdjRIBsOut> ribOuts = new ConcurrentHashMap<>();
88     private final ReconnectStrategyFactory tcpStrategyFactory;
89     private final ReconnectStrategyFactory sessionStrategyFactory;
90     private final BGPObjectComparator comparator;
91     private final BGPDispatcher dispatcher;
92     private final BindingTransactionChain chain;
93     private final AsNumber localAs;
94     private final Ipv4Address bgpIdentifier;
95     private final List<BgpTableType> localTables;
96     private final RIBTables tables;
97     private final BlockingQueue<Peer> peers;
98     private final DataBroker dataBroker;
99     private final Thread scheduler = new Thread(new Runnable() {
100
101         @Override
102         public void run() {
103             try {
104                 final Peer peer = RIBImpl.this.peers.take();
105                 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
106                 for (final BgpTableType key : RIBImpl.this.localTables) {
107                     final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
108                     final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
109                     adj.addAllEntries(trans);
110                     Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
111                         @Override
112                         public void onSuccess(final Void result) {
113                             LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
114                         }
115
116                         @Override
117                         public void onFailure(final Throwable t) {
118                             LOG.error("Failed to update peer {} with RIB {}", peer, t);
119                         }
120                     });
121                 }
122             } catch (final InterruptedException e) {
123
124             }
125         }
126     });
127
128     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final RIBExtensionConsumerContext extensions,
129         final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory,
130         final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final List<BgpTableType> localTables) {
131         super(InstanceIdentifier.builder(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))).toInstance());
132         this.chain = dps.createTransactionChain(this);
133         this.localAs = Preconditions.checkNotNull(localAs);
134         this.comparator = new BGPObjectComparator(localAs);
135         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
136         this.dispatcher = Preconditions.checkNotNull(dispatcher);
137         this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
138         this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
139         this.localTables = ImmutableList.copyOf(localTables);
140         this.tables = new RIBTables(extensions);
141         this.peers = new LinkedBlockingQueue<>();
142         this.dataBroker = dps;
143
144         LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
145
146         final ReadWriteTransaction trans = this.chain.newReadWriteTransaction();
147         Optional<Rib> o;
148         try {
149             o = trans.read(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier()).get();
150         } catch (InterruptedException | ExecutionException e) {
151             throw new IllegalStateException("Failed to read topology", e);
152         }
153         Preconditions.checkState(!o.isPresent(), "Data provider conflict detected on object {}", getInstanceIdentifier());
154
155         // put empty BgpRib if not exists
156         trans.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(BgpRib.class).build(), new BgpRibBuilder().build());
157         trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(), new RibBuilder().setKey(new RibKey(ribId)).setId(ribId).setLocRib(
158             new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build());
159
160         for (final BgpTableType t : localTables) {
161             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
162             if (this.tables.create(trans, this, key) == null) {
163                 LOG.debug("Did not create local table for unhandled table type {}", t);
164             }
165         }
166
167         Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
168             @Override
169             public void onSuccess(final Void result) {
170                 LOG.trace("Change committed successfully");
171             }
172
173             @Override
174             public void onFailure(final Throwable t) {
175                 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
176             }
177         });
178     }
179
180     synchronized void initTables(final byte[] remoteBgpId) {
181     }
182
183     @Override
184     public synchronized void updateTables(final Peer peer, final Update message) {
185         final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
186
187         if (!EOR.equals(message)) {
188             final WithdrawnRoutes wr = message.getWithdrawnRoutes();
189             if (wr != null) {
190                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
191                 if (ari != null) {
192                     /*
193                      * create MPUnreach for the routes to be handled in the same way as linkstate routes
194                      */
195                     ari.removeRoutes(
196                         trans,
197                         peer,
198                         new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
199                             new WithdrawnRoutesBuilder().setDestinationType(
200                                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
201                                     new DestinationIpv4Builder().setIpv4Prefixes(wr.getWithdrawnRoutes()).build()).build()).build()).build());
202                 } else {
203                     LOG.debug("Not removing objects from unhandled IPv4 Unicast");
204                 }
205             }
206
207             final PathAttributes attrs = message.getPathAttributes();
208             if (attrs != null) {
209                 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
210                 if (mpu != null) {
211                     final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
212
213                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
214                     if (ari != null) {
215                         ari.removeRoutes(trans, peer, nlri);
216                     } else {
217                         LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
218                     }
219                 }
220             }
221
222             final Nlri ar = message.getNlri();
223             if (ar != null) {
224                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
225                 if (ari != null) {
226                     /*
227                      * create MPReach for the routes to be handled in the same way as linkstate routes
228                      */
229                     final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
230                         UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
231                             new AdvertizedRoutesBuilder().setDestinationType(
232                                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
233                                     new DestinationIpv4Builder().setIpv4Prefixes(ar.getNlri()).build()).build()).build());
234                     if (attrs != null) {
235                         b.setCNextHop(attrs.getCNextHop());
236                     }
237
238                     ari.addRoutes(trans, peer, b.build(), attrs);
239                 } else {
240                     LOG.debug("Not adding objects from unhandled IPv4 Unicast");
241                 }
242             }
243
244             if (attrs != null) {
245                 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
246                 if (mpr != null) {
247                     final MpReachNlri nlri = mpr.getMpReachNlri();
248
249                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
250                     if (ari != null) {
251                         if (message.equals(ari.endOfRib())) {
252                             ari.markUptodate(trans, peer);
253                         } else {
254                             ari.addRoutes(trans, peer, nlri, attrs);
255                         }
256                     } else {
257                         LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
258                     }
259                 }
260             }
261         } else {
262             final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
263             if (ari != null) {
264                 ari.markUptodate(trans, peer);
265             } else {
266                 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
267             }
268         }
269
270         Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
271             @Override
272             public void onSuccess(final Void result) {
273                 LOG.debug("RIB modification successfully committed.");
274             }
275
276             @Override
277             public void onFailure(final Throwable t) {
278                 LOG.error("Failed to commit RIB modification", t);
279             }
280         });
281     }
282
283     @Override
284     public synchronized void clearTable(final Peer peer, final TablesKey key) {
285         final AdjRIBsIn<?, ?> ari = this.tables.get(key);
286         if (ari != null) {
287             final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
288             ari.clear(trans, peer);
289
290             Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
291                 @Override
292                 public void onSuccess(final Void result) {
293                     LOG.trace("Table {} cleared successfully", key);
294                 }
295
296                 @Override
297                 public void onFailure(final Throwable t) {
298                     LOG.error("Failed to clear table {}", key, t);
299                 }
300             });
301         }
302     }
303
304     @Override
305     public String toString() {
306         return addToStringAttributes(Objects.toStringHelper(this)).toString();
307     }
308
309     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
310         return toStringHelper;
311     }
312
313     @SuppressWarnings("unchecked")
314     protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
315         return (AdjRIBsIn<K, V>) this.tables.get(key);
316     }
317
318     @Override
319     public void close() throws InterruptedException, ExecutionException {
320         final WriteTransaction t = this.chain.newWriteOnlyTransaction();
321         t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
322         t.submit().get();
323         this.chain.close();
324     }
325
326     @Override
327     public AsNumber getLocalAs() {
328         return this.localAs;
329     }
330
331     @Override
332     public Ipv4Address getBgpIdentifier() {
333         return this.bgpIdentifier;
334     }
335
336     @Override
337     public List<? extends BgpTableType> getLocalTables() {
338         return this.localTables;
339     }
340
341     @Override
342     public ReconnectStrategyFactory getTcpStrategyFactory() {
343         return this.tcpStrategyFactory;
344     }
345
346     @Override
347     public ReconnectStrategyFactory getSessionStrategyFactory() {
348         return this.sessionStrategyFactory;
349     }
350
351     @Override
352     public BGPDispatcher getDispatcher() {
353         return this.dispatcher;
354     }
355
356     @Override
357     public void initTable(final Peer bgpPeer, final TablesKey key) {
358         // FIXME: BUG-196: support graceful restart
359     }
360
361     @Override
362     public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
363         final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
364             @Override
365             protected void removeRegistration() {
366                 RIBImpl.this.ribOuts.remove(peer, aro);
367             }
368         };
369
370         this.ribOuts.put(peer, aro);
371         LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
372         try {
373             this.peers.put(peer);
374             this.scheduler.run();
375         } catch (final InterruptedException e) {
376             //
377         }
378         return reg;
379     }
380
381     @Override
382     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
383         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
384     }
385
386     @Override
387     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
388         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
389     }
390
391     @Override
392     public long getRoutesCount(final TablesKey key) {
393         try {
394             final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
395                     getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
396             if (tableMaybe.isPresent()) {
397                 final Tables table = tableMaybe.get();
398                 if (table.getRoutes() instanceof Ipv4RoutesCase) {
399                     final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
400                     if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
401                         return routesCase.getIpv4Routes().getIpv4Route().size();
402                     }
403                 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
404                     final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
405                     if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
406                         return routesCase.getIpv6Routes().getIpv6Route().size();
407                     }
408                 }
409             }
410         } catch (ReadFailedException e) {
411             //no-op
412         }
413         return 0;
414     }
415 }