Merge "BUG-1025 : Moved log level of unrecognized attribute in BGP to WARN."
[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
126                     synchronized (RIBImpl.this) {
127                         final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
128                         final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
129                         adj.addAllEntries(trans);
130                         Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
131                             @Override
132                             public void onSuccess(final Void result) {
133                                 LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
134                             }
135                             @Override
136                             public void onFailure(final Throwable t) {
137                                 LOG.error("Failed to update peer {} with RIB {}", peer, t);
138                             }
139                         });
140                     }
141                 }
142             } catch (final InterruptedException e) {
143                 LOG.info("Scheduler thread was interrupted.", e);
144             }
145         }
146     };
147
148     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final RIBExtensionConsumerContext extensions,
149         final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory,
150         final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final List<BgpTableType> localTables) {
151         super(InstanceIdentifier.builder(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))).build());
152         this.chain = dps.createTransactionChain(this);
153         this.localAs = Preconditions.checkNotNull(localAs);
154         this.comparator = new BGPObjectComparator(localAs);
155         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
156         this.dispatcher = Preconditions.checkNotNull(dispatcher);
157         this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
158         this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
159         this.localTables = ImmutableSet.copyOf(localTables);
160         this.tables = new RIBTables(extensions);
161         this.peers = new LinkedBlockingQueue<>();
162         this.dataBroker = dps;
163
164         LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
165
166         final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
167
168         // put empty BgpRib if not exists
169         trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(), new RibBuilder().setKey(new RibKey(ribId)).setId(ribId).setLocRib(
170             new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
171
172         for (final BgpTableType t : localTables) {
173             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
174             if (this.tables.create(trans, this, key) == null) {
175                 LOG.debug("Did not create local table for unhandled table type {}", t);
176             }
177         }
178
179         Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
180             @Override
181             public void onSuccess(final Void result) {
182                 LOG.trace("Change committed successfully");
183             }
184
185             @Override
186             public void onFailure(final Throwable t) {
187                 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
188             }
189         });
190     }
191
192     synchronized void initTables(final byte[] remoteBgpId) {
193     }
194
195     @Override
196     public synchronized void updateTables(final Peer peer, final Update message) {
197         final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
198
199         if (!EOR.equals(message)) {
200             final WithdrawnRoutes wr = message.getWithdrawnRoutes();
201             if (wr != null) {
202                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
203                 if (ari != null) {
204                     /*
205                      * create MPUnreach for the routes to be handled in the same way as linkstate routes
206                      */
207                     ari.removeRoutes(
208                         trans,
209                         peer,
210                         new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
211                             new WithdrawnRoutesBuilder().setDestinationType(
212                                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
213                                     new DestinationIpv4Builder().setIpv4Prefixes(wr.getWithdrawnRoutes()).build()).build()).build()).build());
214                 } else {
215                     LOG.debug("Not removing objects from unhandled IPv4 Unicast");
216                 }
217             }
218
219             final PathAttributes attrs = message.getPathAttributes();
220             if (attrs != null) {
221                 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
222                 if (mpu != null) {
223                     final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
224                     // EOR messages do not contain withdrawn routes
225                     if (nlri.getWithdrawnRoutes() != null) {
226                         final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
227                         if (ari != null) {
228                             ari.removeRoutes(trans, peer, nlri);
229                         } else {
230                             LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
231                         }
232                     }
233                 }
234             }
235
236             final Nlri ar = message.getNlri();
237             if (ar != null) {
238                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
239                 if (ari != null) {
240                     /*
241                      * create MPReach for the routes to be handled in the same way as linkstate routes
242                      */
243                     final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
244                         UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
245                             new AdvertizedRoutesBuilder().setDestinationType(
246                                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
247                                     new DestinationIpv4Builder().setIpv4Prefixes(ar.getNlri()).build()).build()).build());
248                     if (attrs != null) {
249                         b.setCNextHop(attrs.getCNextHop());
250                     }
251
252                     ari.addRoutes(trans, peer, b.build(), attrs);
253                 } else {
254                     LOG.debug("Not adding objects from unhandled IPv4 Unicast");
255                 }
256             }
257
258             if (attrs != null) {
259                 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
260                 if (mpr != null) {
261                     final MpReachNlri nlri = mpr.getMpReachNlri();
262
263                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
264                     if (ari != null) {
265                         if (message.equals(ari.endOfRib())) {
266                             ari.markUptodate(trans, peer);
267                         } else {
268                             ari.addRoutes(trans, peer, nlri, attrs);
269                         }
270                     } else {
271                         LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
272                     }
273                 }
274             }
275         } else {
276             final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
277             if (ari != null) {
278                 ari.markUptodate(trans, peer);
279             } else {
280                 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
281             }
282         }
283
284         Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
285             @Override
286             public void onSuccess(final Void result) {
287                 LOG.debug("RIB modification successfully committed.");
288             }
289
290             @Override
291             public void onFailure(final Throwable t) {
292                 LOG.error("Failed to commit RIB modification", t);
293             }
294         });
295     }
296
297     @Override
298     public synchronized void clearTable(final Peer peer, final TablesKey key) {
299         final AdjRIBsIn<?, ?> ari = this.tables.get(key);
300         if (ari != null) {
301             final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
302             ari.clear(trans, peer);
303
304             Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
305                 @Override
306                 public void onSuccess(final Void result) {
307                     LOG.trace("Table {} cleared successfully", key);
308                 }
309
310                 @Override
311                 public void onFailure(final Throwable t) {
312                     LOG.error("Failed to clear table {}", key, t);
313                 }
314             });
315         }
316     }
317
318     @Override
319     public String toString() {
320         return addToStringAttributes(Objects.toStringHelper(this)).toString();
321     }
322
323     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
324         return toStringHelper;
325     }
326
327     @SuppressWarnings("unchecked")
328     protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
329         return (AdjRIBsIn<K, V>) this.tables.get(key);
330     }
331
332     @Override
333     public void close() throws InterruptedException, ExecutionException {
334         final WriteTransaction t = this.chain.newWriteOnlyTransaction();
335         t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
336         t.submit().get();
337         this.chain.close();
338     }
339
340     @Override
341     public AsNumber getLocalAs() {
342         return this.localAs;
343     }
344
345     @Override
346     public Ipv4Address getBgpIdentifier() {
347         return this.bgpIdentifier;
348     }
349
350     @Override
351     public Set<? extends BgpTableType> getLocalTables() {
352         return this.localTables;
353     }
354
355     @Override
356     public ReconnectStrategyFactory getTcpStrategyFactory() {
357         return this.tcpStrategyFactory;
358     }
359
360     @Override
361     public ReconnectStrategyFactory getSessionStrategyFactory() {
362         return this.sessionStrategyFactory;
363     }
364
365     @Override
366     public BGPDispatcher getDispatcher() {
367         return this.dispatcher;
368     }
369
370     @Override
371     public void initTable(final Peer bgpPeer, final TablesKey key) {
372         // FIXME: BUG-196: support graceful restart
373     }
374
375     @Override
376     public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
377         final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
378             @Override
379             protected void removeRegistration() {
380                 RIBImpl.this.ribOuts.remove(peer, aro);
381             }
382         };
383
384         this.ribOuts.put(peer, aro);
385         LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
386         try {
387             this.peers.put(peer);
388             new Thread(this.scheduler).start();
389         } catch (final InterruptedException e) {
390             //
391         }
392         return reg;
393     }
394
395     @Override
396     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
397         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
398     }
399
400     @Override
401     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
402         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
403     }
404
405     @Override
406     public long getRoutesCount(final TablesKey key) {
407         try {
408             final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
409                     getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
410             if (tableMaybe.isPresent()) {
411                 final Tables table = tableMaybe.get();
412                 if (table.getRoutes() instanceof Ipv4RoutesCase) {
413                     final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
414                     if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
415                         return routesCase.getIpv4Routes().getIpv4Route().size();
416                     }
417                 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
418                     final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
419                     if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
420                         return routesCase.getIpv6Routes().getIpv6Route().size();
421                     }
422                 }
423             }
424         } catch (final ReadFailedException e) {
425             //no-op
426         }
427         return 0;
428     }
429 }