Rib Out Route Paths re evaluation.
[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 static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.MoreObjects;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.concurrent.ExecutionException;
23 import javax.annotation.Nonnull;
24 import javax.annotation.concurrent.GuardedBy;
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.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.TransactionChain;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
36 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
37 import org.opendaylight.mdsal.common.api.CommitInfo;
38 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
39 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
40 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
41 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
42 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
43 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
47 import org.opendaylight.protocol.bgp.rib.impl.state.BGPRIBStateImpl;
48 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
49 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
50 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
51 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.BgpId;
65 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
66 import org.opendaylight.yangtools.yang.common.QName;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
69 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
71 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
73 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
74 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
75 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
76 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
79
80 @ThreadSafe
81 public final class RIBImpl extends BGPRIBStateImpl implements RIB, TransactionChainListener,
82         SchemaContextListener, AutoCloseable {
83     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
84     private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
85
86     private final BGPDispatcher dispatcher;
87     private final AsNumber localAs;
88     private final BgpId bgpIdentifier;
89     private final Set<BgpTableType> localTables;
90     private final Set<TablesKey> localTablesKeys;
91     private final DOMDataBroker domDataBroker;
92     private final DataBroker dataBroker;
93     private final RIBExtensionConsumerContext extensions;
94     private final YangInstanceIdentifier yangRibId;
95     private final RIBSupportContextRegistryImpl ribContextRegistry;
96     private final CodecsRegistryImpl codecsRegistry;
97     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
98     private final DOMDataBrokerExtension domService;
99     private final Map<TransactionChain<?, ?>, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
100     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
101     private final RibId ribId;
102     private final BGPPeerTracker peerTracker = new BGPPeerTrackerImpl();
103     private final BGPRibRoutingPolicy ribPolicies;
104     @GuardedBy("this")
105     private ClusterSingletonServiceRegistration registration;
106     @GuardedBy("this")
107     private DOMTransactionChain domChain;
108     @GuardedBy("this")
109     private boolean isServiceInstantiated;
110     private Map<TablesKey, RibOutRefresh> vpnTableRefresher = new HashMap<>();
111
112     public RIBImpl(
113             final BGPTableTypeRegistryConsumer tableTypeRegistry,
114             final RibId ribId,
115             final AsNumber localAs,
116             final BgpId localBgpId,
117             final RIBExtensionConsumerContext extensions,
118             final BGPDispatcher dispatcher,
119             final CodecsRegistryImpl codecsRegistry,
120             final DOMDataBroker domDataBroker,
121             final DataBroker dataBroker,
122             final BGPRibRoutingPolicy ribPolicies,
123             final List<BgpTableType> localTables,
124             final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies
125     ) {
126         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
127                 localBgpId, localAs);
128         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
129         this.localAs = requireNonNull(localAs);
130         this.bgpIdentifier = requireNonNull(localBgpId);
131         this.dispatcher = requireNonNull(dispatcher);
132         this.localTables = ImmutableSet.copyOf(localTables);
133         this.localTablesKeys = new HashSet<>();
134         this.domDataBroker = requireNonNull(domDataBroker);
135         this.dataBroker = requireNonNull(dataBroker);
136         this.domService = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
137         this.extensions = requireNonNull(extensions);
138         this.ribPolicies = requireNonNull(ribPolicies);
139         this.codecsRegistry = codecsRegistry;
140         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
141         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
142         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
143         this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
144         this.ribId = ribId;
145
146         for (final BgpTableType t : this.localTables) {
147             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
148             this.localTablesKeys.add(key);
149         }
150     }
151
152     private synchronized void startLocRib(final TablesKey key) {
153         LOG.debug("Creating LocRib table for {}", key);
154         // create locRibWriter for each table
155         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
156
157         final RIBSupport<? extends Routes, ?, ?, ?> ribSupport = this.ribContextRegistry.getRIBSupport(key);
158         if (ribSupport != null) {
159             final MapEntryNode emptyTable = ribSupport.emptyTable();
160             final InstanceIdentifierBuilder tableId = YangInstanceIdentifier
161                     .builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME)).node(emptyTable.getIdentifier());
162
163             tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), emptyTable);
164             try {
165                 tx.commit().get();
166             } catch (final InterruptedException | ExecutionException e1) {
167                 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
168             }
169         } else {
170             LOG.warn("There's no registered RIB Context for {}", key.getAfi());
171         }
172     }
173
174     private synchronized void createLocRibWriter(final TablesKey key) {
175         final RIBSupport<?, ?, ?, ?> ribSupport = this.ribContextRegistry.getRIBSupport(key);
176         if (ribSupport == null) {
177             return;
178         }
179         LOG.debug("Creating LocRIB writer for key {}", key);
180         final BindingTransactionChain txChain = createPeerChain(this);
181         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
182         if (pathSelectionStrategy == null) {
183             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
184         }
185
186         final LocRibWriter locRibWriter = LocRibWriter.create(
187                 ribSupport,
188                 key,
189                 this.tableTypeRegistry.getAfiSafiType(key).get(),
190                 txChain,
191                 getInstanceIdentifier(),
192                 this.localAs,
193                 getDataBroker(),
194                 this.ribPolicies,
195                 this.peerTracker,
196                 pathSelectionStrategy);
197         this.vpnTableRefresher.put(key, locRibWriter);
198         registerTotalPathCounter(key, locRibWriter);
199         registerTotalPrefixesCounter(key, locRibWriter);
200         this.txChainToLocRibWriter.put(txChain, locRibWriter);
201     }
202
203     @Override
204     public String toString() {
205         return MoreObjects.toStringHelper(this).add("bgpId", bgpIdentifier).add("localTables", localTables).toString();
206     }
207
208     @Override
209     public synchronized void close() throws Exception {
210         if (this.registration != null) {
211             this.registration.close();
212             this.registration = null;
213         }
214     }
215
216     @Override
217     public AsNumber getLocalAs() {
218         return this.localAs;
219     }
220
221     @Override
222     public BgpId getBgpIdentifier() {
223         return this.bgpIdentifier;
224     }
225
226     @Nonnull
227     @Override
228     public Set<? extends BgpTableType> getLocalTables() {
229         return this.localTables;
230     }
231
232     @Override
233     public BGPDispatcher getDispatcher() {
234         return this.dispatcher;
235     }
236
237     @Override
238     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
239             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
240         LOG.error("Broken chain in RIB {} transaction {}",
241                 getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
242         if (this.txChainToLocRibWriter.containsKey(chain)) {
243             final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
244             final BindingTransactionChain newChain = createPeerChain(this);
245             startLocRib(locRibWriter.getTableKey());
246             locRibWriter.restart(newChain);
247             this.txChainToLocRibWriter.put(newChain, locRibWriter);
248         }
249     }
250
251     @Override
252     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
253         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
254     }
255
256     @Override
257     public Set<TablesKey> getLocalTablesKeys() {
258         return this.localTablesKeys;
259     }
260
261     @Override
262     public boolean supportsTable(final TablesKey tableKey) {
263         return this.localTablesKeys.contains(tableKey);
264     }
265
266     @Override
267     public BGPRibRoutingPolicy getRibPolicies() {
268         return this.ribPolicies;
269     }
270
271     @Override
272     public BGPPeerTracker getPeerTracker() {
273         return this.peerTracker;
274     }
275
276     @Override
277     public void refreshTable(final TablesKey tk, final PeerId peerId) {
278         this.vpnTableRefresher.get(tk).refreshTable(tk, peerId);
279     }
280
281     @Override
282     public DOMDataTreeChangeService getService() {
283         return (DOMDataTreeChangeService) this.domService;
284     }
285
286     @Override
287     public DataBroker getDataBroker() {
288         return this.dataBroker;
289     }
290
291     @Override
292     public YangInstanceIdentifier getYangRibId() {
293         return this.yangRibId;
294     }
295
296     @Override
297     public BindingTransactionChain createPeerChain(final TransactionChainListener listener) {
298         return this.dataBroker.createTransactionChain(listener);
299     }
300
301     @Override
302     public DOMTransactionChain createPeerDOMChain(final TransactionChainListener listener) {
303         return this.domDataBroker.createTransactionChain(listener);
304     }
305
306     @Override
307     public RIBExtensionConsumerContext getRibExtensions() {
308         return this.extensions;
309     }
310
311     @Override
312     public RIBSupportContextRegistry getRibSupportContext() {
313         return this.ribContextRegistry;
314     }
315
316     @Override
317     public void onGlobalContextUpdated(final SchemaContext context) {
318         this.codecsRegistry.onSchemaContextUpdated(context);
319     }
320
321     @Override
322     public CodecsRegistry getCodecsRegistry() {
323         return this.codecsRegistry;
324     }
325
326     public synchronized void instantiateServiceInstance() {
327         this.isServiceInstantiated = true;
328         setActive(true);
329         this.domChain = this.domDataBroker.createTransactionChain(this);
330         LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
331
332         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
333                 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
334
335         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
336                 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
337                 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
338                 .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
339                 .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
340                         .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
341                         .build()).build();
342
343
344         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
345
346         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
347         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
348         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
349
350         try {
351             trans.commit().get();
352         } catch (final InterruptedException | ExecutionException e) {
353             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
354         }
355
356         LOG.debug("Effective RIB created.");
357
358         this.localTablesKeys.forEach(this::startLocRib);
359         this.localTablesKeys.forEach(this::createLocRibWriter);
360     }
361
362     public synchronized FluentFuture<? extends CommitInfo> closeServiceInstance() {
363         if (!this.isServiceInstantiated) {
364             LOG.trace("RIB {} already closed", this.ribId.getValue());
365             return CommitInfo.emptyFluentFuture();
366         }
367         LOG.info("Close RIB {}", this.ribId.getValue());
368         this.isServiceInstantiated = false;
369         setActive(false);
370
371         this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
372         this.txChainToLocRibWriter.clear();
373
374         final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
375         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
376         final FluentFuture<? extends CommitInfo> cleanFuture = t.commit();
377         cleanFuture.addCallback(new FutureCallback<CommitInfo>() {
378             @Override
379             public void onSuccess(final CommitInfo result) {
380                 LOG.info("RIB cleaned {}", RIBImpl.this.ribId.getValue());
381             }
382
383             @Override
384             public void onFailure(final Throwable throwable) {
385                 LOG.error("Failed to clean RIB {}",
386                         RIBImpl.this.ribId.getValue(), throwable);
387             }
388         }, MoreExecutors.directExecutor());
389         this.domChain.close();
390         return cleanFuture;
391     }
392 }