2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.protocol.bgp.rib.impl;
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.BGPRIB_NID;
12 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.LOCRIB_NID;
13 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.PEER_NID;
14 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.RIB_NID;
15 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES_NID;
17 import com.google.common.base.MoreObjects;
18 import com.google.common.collect.ImmutableSet;
19 import com.google.common.util.concurrent.FluentFuture;
20 import com.google.common.util.concurrent.FutureCallback;
21 import com.google.common.util.concurrent.MoreExecutors;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
27 import java.util.concurrent.ExecutionException;
28 import org.checkerframework.checker.lock.qual.GuardedBy;
29 import org.opendaylight.mdsal.binding.api.DataBroker;
30 import org.opendaylight.mdsal.binding.api.Transaction;
31 import org.opendaylight.mdsal.binding.api.TransactionChain;
32 import org.opendaylight.mdsal.binding.api.TransactionChainListener;
33 import org.opendaylight.mdsal.common.api.CommitInfo;
34 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
35 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
36 import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
40 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
41 import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
42 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
43 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
44 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
45 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
47 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
48 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
49 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
50 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
51 import org.opendaylight.protocol.bgp.rib.impl.state.BGPRibStateImpl;
52 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
53 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
54 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
55 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.BgpId;
68 import org.opendaylight.yangtools.yang.binding.ChildOf;
69 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
70 import org.opendaylight.yangtools.yang.binding.DataObject;
71 import org.opendaylight.yangtools.yang.binding.Identifiable;
72 import org.opendaylight.yangtools.yang.binding.Identifier;
73 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
74 import org.opendaylight.yangtools.yang.common.QName;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
76 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
77 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
78 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
80 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
81 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
85 // This class is thread-safe
86 public final class RIBImpl extends BGPRibStateImpl implements RIB, TransactionChainListener,
87 DOMTransactionChainListener, AutoCloseable {
88 private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
89 private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
91 private final BGPDispatcher dispatcher;
92 private final AsNumber localAs;
93 private final BgpId bgpIdentifier;
94 private final Set<BgpTableType> localTables;
95 private final Set<TablesKey> localTablesKeys;
96 private final DOMDataBroker domDataBroker;
97 private final DataBroker dataBroker;
98 private final RIBExtensionConsumerContext extensions;
99 private final YangInstanceIdentifier yangRibId;
100 private final RIBSupportContextRegistryImpl ribContextRegistry;
101 private final CodecsRegistry codecsRegistry;
102 private final BGPTableTypeRegistryConsumer tableTypeRegistry;
103 private final DOMDataBrokerExtension domService;
104 private final Map<TransactionChain, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
105 private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
106 private final RibId ribId;
107 private final BGPPeerTracker peerTracker = new BGPPeerTrackerImpl();
108 private final BGPRibRoutingPolicy ribPolicies;
110 private ClusterSingletonServiceRegistration registration;
112 private DOMTransactionChain domChain;
114 private boolean isServiceInstantiated;
115 private final Map<TablesKey, RibOutRefresh> vpnTableRefresher = new HashMap<>();
118 final BGPTableTypeRegistryConsumer tableTypeRegistry,
120 final AsNumber localAs,
121 final BgpId localBgpId,
122 final RIBExtensionConsumerContext extensions,
123 final BGPDispatcher dispatcher,
124 final CodecsRegistry codecsRegistry,
125 final DOMDataBroker domDataBroker,
126 final DataBroker dataBroker,
127 final BGPRibRoutingPolicy ribPolicies,
128 final List<BgpTableType> localTables,
129 final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies
131 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
132 localBgpId, localAs);
133 this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
134 this.localAs = requireNonNull(localAs);
135 this.bgpIdentifier = requireNonNull(localBgpId);
136 this.dispatcher = requireNonNull(dispatcher);
137 this.localTables = ImmutableSet.copyOf(localTables);
138 this.localTablesKeys = new HashSet<>();
139 this.domDataBroker = requireNonNull(domDataBroker);
140 this.dataBroker = requireNonNull(dataBroker);
141 this.domService = this.domDataBroker.getExtensions().get(DOMDataTreeChangeService.class);
142 this.extensions = requireNonNull(extensions);
143 this.ribPolicies = requireNonNull(ribPolicies);
144 this.codecsRegistry = codecsRegistry;
145 this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
146 this.yangRibId = YangInstanceIdentifier.builder().node(BGPRIB_NID).node(RIB_NID)
147 .nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
148 this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
151 for (final BgpTableType t : this.localTables) {
152 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
153 this.localTablesKeys.add(key);
157 private synchronized void startLocRib(final TablesKey key) {
158 LOG.debug("Creating LocRib table for {}", key);
159 // create locRibWriter for each table
160 final DOMDataTreeWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
162 final RIBSupport<? extends Routes, ?, ?, ?> ribSupport = this.ribContextRegistry.getRIBSupport(key);
163 if (ribSupport != null) {
164 final MapEntryNode emptyTable = ribSupport.emptyTable();
165 final InstanceIdentifierBuilder tableId = YangInstanceIdentifier
166 .builder(this.yangRibId.node(LOCRIB_NID).node(TABLES_NID)).node(emptyTable.getIdentifier());
168 tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), emptyTable);
171 } catch (final InterruptedException | ExecutionException e1) {
172 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
175 LOG.warn("There's no registered RIB Context for {}", key.getAfi());
179 private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
180 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>>
181 void createLocRibWriter(final TablesKey key) {
182 final RIBSupport<C, S, R, I> ribSupport = this.ribContextRegistry.getRIBSupport(key);
183 if (ribSupport == null) {
186 LOG.debug("Creating LocRIB writer for key {}", key);
187 final TransactionChain txChain = createPeerChain(this);
188 PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
189 if (pathSelectionStrategy == null) {
190 pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
193 final LocRibWriter<C, S, R, I> locRibWriter = LocRibWriter.create(
195 this.tableTypeRegistry.getAfiSafiType(key).get(),
197 getInstanceIdentifier(),
202 pathSelectionStrategy);
203 this.vpnTableRefresher.put(key, locRibWriter);
204 registerTotalPathCounter(key, locRibWriter);
205 registerTotalPrefixesCounter(key, locRibWriter);
206 this.txChainToLocRibWriter.put(txChain, locRibWriter);
210 public String toString() {
211 return MoreObjects.toStringHelper(this).add("bgpId", bgpIdentifier).add("localTables", localTables).toString();
215 public synchronized void close() {
216 if (this.registration != null) {
217 this.registration.close();
218 this.registration = null;
223 public AsNumber getLocalAs() {
228 public BgpId getBgpIdentifier() {
229 return this.bgpIdentifier;
233 public Set<? extends BgpTableType> getLocalTables() {
234 return this.localTables;
238 public BGPDispatcher getDispatcher() {
239 return this.dispatcher;
243 public synchronized void onTransactionChainFailed(final TransactionChain chain,
244 final Transaction transaction, final Throwable cause) {
245 LOG.error("Broken chain in RIB {} transaction {}",
246 getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
247 if (this.txChainToLocRibWriter.containsKey(chain)) {
248 final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
249 final TransactionChain newChain = createPeerChain(this);
250 startLocRib(locRibWriter.getTableKey());
251 locRibWriter.restart(newChain);
252 this.txChainToLocRibWriter.put(newChain, locRibWriter);
257 public synchronized void onTransactionChainFailed(final DOMTransactionChain chain,
258 final DOMDataTreeTransaction transaction, final Throwable cause) {
259 LOG.error("Broken chain in RIB {} transaction {}",
260 getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
264 public void onTransactionChainSuccessful(final TransactionChain chain) {
265 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
269 public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
270 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
274 public Set<TablesKey> getLocalTablesKeys() {
275 return this.localTablesKeys;
279 public boolean supportsTable(final TablesKey tableKey) {
280 return this.localTablesKeys.contains(tableKey);
284 public BGPRibRoutingPolicy getRibPolicies() {
285 return this.ribPolicies;
289 public BGPPeerTracker getPeerTracker() {
290 return this.peerTracker;
294 public void refreshTable(final TablesKey tk, final PeerId peerId) {
295 final RibOutRefresh table = this.vpnTableRefresher.get(tk);
297 table.refreshTable(tk, peerId);
302 public DOMDataTreeChangeService getService() {
303 return (DOMDataTreeChangeService) this.domService;
307 public DataBroker getDataBroker() {
308 return this.dataBroker;
312 public YangInstanceIdentifier getYangRibId() {
313 return this.yangRibId;
317 public TransactionChain createPeerChain(final TransactionChainListener listener) {
318 return this.dataBroker.createMergingTransactionChain(listener);
322 public DOMTransactionChain createPeerDOMChain(final DOMTransactionChainListener listener) {
323 return this.domDataBroker.createMergingTransactionChain(listener);
327 public RIBExtensionConsumerContext getRibExtensions() {
328 return this.extensions;
332 public RIBSupportContextRegistry getRibSupportContext() {
333 return this.ribContextRegistry;
337 public CodecsRegistry getCodecsRegistry() {
338 return this.codecsRegistry;
341 public synchronized void instantiateServiceInstance() {
342 this.isServiceInstantiated = true;
344 this.domChain = this.domDataBroker.createMergingTransactionChain(this);
345 LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
347 final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(BGPRIB_NID)
348 .addChild(ImmutableNodes.mapNodeBuilder(RIB_NID).build()).build();
350 final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
351 NodeIdentifierWithPredicates.of(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
352 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
353 .addChild(ImmutableNodes.mapNodeBuilder(PEER_NID).build())
354 .addChild(Builders.containerBuilder().withNodeIdentifier(LOCRIB_NID)
355 .addChild(ImmutableNodes.mapNodeBuilder(TABLES_NID).build())
358 final DOMDataTreeWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
360 // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
361 trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.create(BGPRIB_NID), bgpRib);
362 trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
365 trans.commit().get();
366 } catch (final InterruptedException | ExecutionException e) {
367 LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
370 LOG.debug("Effective RIB created.");
372 this.localTablesKeys.forEach(this::startLocRib);
373 this.localTablesKeys.forEach(this::createLocRibWriter);
376 public synchronized FluentFuture<? extends CommitInfo> closeServiceInstance() {
377 if (!this.isServiceInstantiated) {
378 LOG.trace("RIB {} already closed", this.ribId.getValue());
379 return CommitInfo.emptyFluentFuture();
381 LOG.info("Close RIB {}", this.ribId.getValue());
382 this.isServiceInstantiated = false;
385 this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
386 this.txChainToLocRibWriter.clear();
388 final DOMDataTreeWriteTransaction t = this.domChain.newWriteOnlyTransaction();
389 t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
390 final FluentFuture<? extends CommitInfo> cleanFuture = t.commit();
391 cleanFuture.addCallback(new FutureCallback<CommitInfo>() {
393 public void onSuccess(final CommitInfo result) {
394 LOG.info("RIB cleaned {}", RIBImpl.this.ribId.getValue());
398 public void onFailure(final Throwable throwable) {
399 LOG.error("Failed to clean RIB {}",
400 RIBImpl.this.ribId.getValue(), throwable);
402 }, MoreExecutors.directExecutor());
403 this.domChain.close();