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.rev180329.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.opendaylight.yangtools.yang.model.api.SchemaContext;
83 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
84 import org.slf4j.Logger;
85 import org.slf4j.LoggerFactory;
87 // This class is thread-safe
88 public final class RIBImpl extends BGPRibStateImpl implements RIB, TransactionChainListener,
89 DOMTransactionChainListener, SchemaContextListener, AutoCloseable {
90 private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
91 private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
93 private final BGPDispatcher dispatcher;
94 private final AsNumber localAs;
95 private final BgpId bgpIdentifier;
96 private final Set<BgpTableType> localTables;
97 private final Set<TablesKey> localTablesKeys;
98 private final DOMDataBroker domDataBroker;
99 private final DataBroker dataBroker;
100 private final RIBExtensionConsumerContext extensions;
101 private final YangInstanceIdentifier yangRibId;
102 private final RIBSupportContextRegistryImpl ribContextRegistry;
103 private final CodecsRegistryImpl codecsRegistry;
104 private final BGPTableTypeRegistryConsumer tableTypeRegistry;
105 private final DOMDataBrokerExtension domService;
106 private final Map<TransactionChain, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
107 private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
108 private final RibId ribId;
109 private final BGPPeerTracker peerTracker = new BGPPeerTrackerImpl();
110 private final BGPRibRoutingPolicy ribPolicies;
112 private ClusterSingletonServiceRegistration registration;
114 private DOMTransactionChain domChain;
116 private boolean isServiceInstantiated;
117 private final Map<TablesKey, RibOutRefresh> vpnTableRefresher = new HashMap<>();
120 final BGPTableTypeRegistryConsumer tableTypeRegistry,
122 final AsNumber localAs,
123 final BgpId localBgpId,
124 final RIBExtensionConsumerContext extensions,
125 final BGPDispatcher dispatcher,
126 final CodecsRegistryImpl codecsRegistry,
127 final DOMDataBroker domDataBroker,
128 final DataBroker dataBroker,
129 final BGPRibRoutingPolicy ribPolicies,
130 final List<BgpTableType> localTables,
131 final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies
133 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
134 localBgpId, localAs);
135 this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
136 this.localAs = requireNonNull(localAs);
137 this.bgpIdentifier = requireNonNull(localBgpId);
138 this.dispatcher = requireNonNull(dispatcher);
139 this.localTables = ImmutableSet.copyOf(localTables);
140 this.localTablesKeys = new HashSet<>();
141 this.domDataBroker = requireNonNull(domDataBroker);
142 this.dataBroker = requireNonNull(dataBroker);
143 this.domService = this.domDataBroker.getExtensions().get(DOMDataTreeChangeService.class);
144 this.extensions = requireNonNull(extensions);
145 this.ribPolicies = requireNonNull(ribPolicies);
146 this.codecsRegistry = codecsRegistry;
147 this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
148 this.yangRibId = YangInstanceIdentifier.builder().node(BGPRIB_NID).node(RIB_NID)
149 .nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
150 this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
153 for (final BgpTableType t : this.localTables) {
154 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
155 this.localTablesKeys.add(key);
159 private synchronized void startLocRib(final TablesKey key) {
160 LOG.debug("Creating LocRib table for {}", key);
161 // create locRibWriter for each table
162 final DOMDataTreeWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
164 final RIBSupport<? extends Routes, ?, ?, ?> ribSupport = this.ribContextRegistry.getRIBSupport(key);
165 if (ribSupport != null) {
166 final MapEntryNode emptyTable = ribSupport.emptyTable();
167 final InstanceIdentifierBuilder tableId = YangInstanceIdentifier
168 .builder(this.yangRibId.node(LOCRIB_NID).node(TABLES_NID)).node(emptyTable.getIdentifier());
170 tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), emptyTable);
173 } catch (final InterruptedException | ExecutionException e1) {
174 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
177 LOG.warn("There's no registered RIB Context for {}", key.getAfi());
181 private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
182 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>>
183 void createLocRibWriter(final TablesKey key) {
184 final RIBSupport<C, S, R, I> ribSupport = this.ribContextRegistry.getRIBSupport(key);
185 if (ribSupport == null) {
188 LOG.debug("Creating LocRIB writer for key {}", key);
189 final TransactionChain txChain = createPeerChain(this);
190 PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
191 if (pathSelectionStrategy == null) {
192 pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
195 final LocRibWriter<C, S, R, I> locRibWriter = LocRibWriter.create(
197 this.tableTypeRegistry.getAfiSafiType(key).get(),
199 getInstanceIdentifier(),
204 pathSelectionStrategy);
205 this.vpnTableRefresher.put(key, locRibWriter);
206 registerTotalPathCounter(key, locRibWriter);
207 registerTotalPrefixesCounter(key, locRibWriter);
208 this.txChainToLocRibWriter.put(txChain, locRibWriter);
212 public String toString() {
213 return MoreObjects.toStringHelper(this).add("bgpId", bgpIdentifier).add("localTables", localTables).toString();
217 public synchronized void close() {
218 if (this.registration != null) {
219 this.registration.close();
220 this.registration = null;
225 public AsNumber getLocalAs() {
230 public BgpId getBgpIdentifier() {
231 return this.bgpIdentifier;
235 public Set<? extends BgpTableType> getLocalTables() {
236 return this.localTables;
240 public BGPDispatcher getDispatcher() {
241 return this.dispatcher;
245 public synchronized void onTransactionChainFailed(final TransactionChain chain,
246 final Transaction transaction, final Throwable cause) {
247 LOG.error("Broken chain in RIB {} transaction {}",
248 getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
249 if (this.txChainToLocRibWriter.containsKey(chain)) {
250 final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
251 final TransactionChain newChain = createPeerChain(this);
252 startLocRib(locRibWriter.getTableKey());
253 locRibWriter.restart(newChain);
254 this.txChainToLocRibWriter.put(newChain, locRibWriter);
259 public synchronized void onTransactionChainFailed(final DOMTransactionChain chain,
260 final DOMDataTreeTransaction transaction, final Throwable cause) {
261 LOG.error("Broken chain in RIB {} transaction {}",
262 getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
266 public void onTransactionChainSuccessful(final TransactionChain chain) {
267 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
271 public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
272 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
276 public Set<TablesKey> getLocalTablesKeys() {
277 return this.localTablesKeys;
281 public boolean supportsTable(final TablesKey tableKey) {
282 return this.localTablesKeys.contains(tableKey);
286 public BGPRibRoutingPolicy getRibPolicies() {
287 return this.ribPolicies;
291 public BGPPeerTracker getPeerTracker() {
292 return this.peerTracker;
296 public void refreshTable(final TablesKey tk, final PeerId peerId) {
297 final RibOutRefresh table = this.vpnTableRefresher.get(tk);
299 table.refreshTable(tk, peerId);
304 public DOMDataTreeChangeService getService() {
305 return (DOMDataTreeChangeService) this.domService;
309 public DataBroker getDataBroker() {
310 return this.dataBroker;
314 public YangInstanceIdentifier getYangRibId() {
315 return this.yangRibId;
319 public TransactionChain createPeerChain(final TransactionChainListener listener) {
320 return this.dataBroker.createMergingTransactionChain(listener);
324 public DOMTransactionChain createPeerDOMChain(final DOMTransactionChainListener listener) {
325 return this.domDataBroker.createMergingTransactionChain(listener);
329 public RIBExtensionConsumerContext getRibExtensions() {
330 return this.extensions;
334 public RIBSupportContextRegistry getRibSupportContext() {
335 return this.ribContextRegistry;
339 public void onGlobalContextUpdated(final SchemaContext context) {
340 this.codecsRegistry.onSchemaContextUpdated(context);
344 public CodecsRegistry getCodecsRegistry() {
345 return this.codecsRegistry;
348 public synchronized void instantiateServiceInstance() {
349 this.isServiceInstantiated = true;
351 this.domChain = this.domDataBroker.createMergingTransactionChain(this);
352 LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
354 final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(BGPRIB_NID)
355 .addChild(ImmutableNodes.mapNodeBuilder(RIB_NID).build()).build();
357 final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
358 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
359 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
360 .addChild(ImmutableNodes.mapNodeBuilder(PEER_NID).build())
361 .addChild(Builders.containerBuilder().withNodeIdentifier(LOCRIB_NID)
362 .addChild(ImmutableNodes.mapNodeBuilder(TABLES_NID).build())
365 final DOMDataTreeWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
367 // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
368 trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.create(BGPRIB_NID), bgpRib);
369 trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
372 trans.commit().get();
373 } catch (final InterruptedException | ExecutionException e) {
374 LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
377 LOG.debug("Effective RIB created.");
379 this.localTablesKeys.forEach(this::startLocRib);
380 this.localTablesKeys.forEach(this::createLocRibWriter);
383 public synchronized FluentFuture<? extends CommitInfo> closeServiceInstance() {
384 if (!this.isServiceInstantiated) {
385 LOG.trace("RIB {} already closed", this.ribId.getValue());
386 return CommitInfo.emptyFluentFuture();
388 LOG.info("Close RIB {}", this.ribId.getValue());
389 this.isServiceInstantiated = false;
392 this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
393 this.txChainToLocRibWriter.clear();
395 final DOMDataTreeWriteTransaction t = this.domChain.newWriteOnlyTransaction();
396 t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
397 final FluentFuture<? extends CommitInfo> cleanFuture = t.commit();
398 cleanFuture.addCallback(new FutureCallback<CommitInfo>() {
400 public void onSuccess(final CommitInfo result) {
401 LOG.info("RIB cleaned {}", RIBImpl.this.ribId.getValue());
405 public void onFailure(final Throwable throwable) {
406 LOG.error("Failed to clean RIB {}",
407 RIBImpl.this.ribId.getValue(), throwable);
409 }, MoreExecutors.directExecutor());
410 this.domChain.close();