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