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