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