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