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 com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.BGPRIB_NID;
13 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.LOCRIB_NID;
14 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.PEER_NID;
15 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.RIB_NID;
16 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES_NID;
18 import com.google.common.base.MoreObjects;
19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.util.concurrent.FluentFuture;
21 import com.google.common.util.concurrent.FutureCallback;
22 import com.google.common.util.concurrent.MoreExecutors;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.List;
28 import java.util.concurrent.ExecutionException;
29 import java.util.stream.Collectors;
30 import org.checkerframework.checker.lock.qual.GuardedBy;
31 import org.opendaylight.mdsal.common.api.CommitInfo;
32 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
33 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
34 import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
35 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
38 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
39 import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
40 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
41 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
42 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
43 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
47 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
48 import org.opendaylight.protocol.bgp.rib.impl.state.BGPRibStateImpl;
49 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
50 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
51 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
52 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.BgpId;
64 import org.opendaylight.yangtools.yang.binding.ChildOf;
65 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
66 import org.opendaylight.yangtools.yang.binding.DataObject;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68 import org.opendaylight.yangtools.yang.common.QName;
69 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
72 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
74 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
75 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
79 // This class is thread-safe
80 public final class RIBImpl extends BGPRibStateImpl implements RIB, DOMTransactionChainListener {
81 private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
82 private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
84 private final BGPDispatcher dispatcher;
85 private final AsNumber localAs;
86 private final BgpId bgpIdentifier;
87 private final Set<BgpTableType> localTables;
88 private final Set<TablesKey> localTablesKeys;
89 private final DOMDataBroker domDataBroker;
90 private final RIBExtensionConsumerContext extensions;
91 private final YangInstanceIdentifier yangRibId;
92 private final RIBSupportContextRegistryImpl ribContextRegistry;
93 private final CodecsRegistry codecsRegistry;
94 private final BGPTableTypeRegistryConsumer tableTypeRegistry;
95 private final DOMDataBrokerExtension domService;
96 private final Map<DOMTransactionChain, LocRibWriter<?, ?>> txChainToLocRibWriter = new HashMap<>();
97 private final Map<TablesKey, RibOutRefresh> vpnTableRefresher = new HashMap<>();
98 private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
99 private final RibId ribId;
100 private final BGPPeerTracker peerTracker = new BGPPeerTrackerImpl();
101 private final BGPRibRoutingPolicy ribPolicies;
103 private DOMTransactionChain domChain;
105 private boolean isServiceInstantiated;
108 final BGPTableTypeRegistryConsumer tableTypeRegistry,
110 final AsNumber localAs,
111 final BgpId localBgpId,
112 final RIBExtensionConsumerContext extensions,
113 final BGPDispatcher dispatcher,
114 final CodecsRegistry codecsRegistry,
115 final DOMDataBroker domDataBroker,
116 final BGPRibRoutingPolicy ribPolicies,
117 final List<BgpTableType> localTables,
118 final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies
120 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
121 localBgpId, localAs);
122 this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
123 this.localAs = requireNonNull(localAs);
124 bgpIdentifier = requireNonNull(localBgpId);
125 this.dispatcher = requireNonNull(dispatcher);
127 this.localTables = ImmutableSet.copyOf(localTables);
128 // FIXME: can this be immutable?
129 localTablesKeys = localTables.stream()
130 .map(t -> new TablesKey(t.getAfi(), t.getSafi()))
131 .collect(Collectors.toCollection(HashSet::new));
133 this.domDataBroker = requireNonNull(domDataBroker);
134 domService = domDataBroker.getExtensions().get(DOMDataTreeChangeService.class);
135 this.extensions = requireNonNull(extensions);
136 this.ribPolicies = requireNonNull(ribPolicies);
137 this.codecsRegistry = codecsRegistry;
138 ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, codecsRegistry);
139 yangRibId = YangInstanceIdentifier.builder().node(BGPRIB_NID).node(RIB_NID)
140 .nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
141 this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
145 // FIXME: make this asynchronous?
146 private synchronized void startLocRib(final TablesKey key) {
147 LOG.debug("Creating LocRib table for {}", key);
148 // create locRibWriter for each table
149 final DOMDataTreeWriteTransaction tx = domChain.newWriteOnlyTransaction();
151 final RIBSupport<? extends Routes, ?> ribSupport = ribContextRegistry.getRIBSupport(key);
152 if (ribSupport != null) {
153 final MapEntryNode emptyTable = ribSupport.emptyTable();
154 final InstanceIdentifierBuilder tableId = YangInstanceIdentifier
155 .builder(yangRibId.node(LOCRIB_NID).node(TABLES_NID)).node(emptyTable.name());
157 tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), emptyTable);
160 } catch (final InterruptedException | ExecutionException e) {
161 LOG.error("Failed to initiate LocRIB for key {}", key, e);
164 LOG.warn("There's no registered RIB Context for {}", key.getAfi());
168 private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>>
169 void createLocRibWriter(final TablesKey key) {
170 final RIBSupport<C, S> ribSupport = ribContextRegistry.getRIBSupport(key);
171 if (ribSupport == null) {
174 LOG.debug("Creating LocRIB writer for key {}", key);
175 final DOMTransactionChain txChain = createPeerDOMChain(this);
176 PathSelectionMode pathSelectionStrategy = bestPathSelectionStrategies.get(key);
177 if (pathSelectionStrategy == null) {
178 pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
181 final LocRibWriter<C, S> locRibWriter = LocRibWriter.create(
183 verifyNotNull(tableTypeRegistry.getAfiSafiType(key)),
190 pathSelectionStrategy);
191 vpnTableRefresher.put(key, locRibWriter);
192 registerTotalPathCounter(key, locRibWriter);
193 registerTotalPrefixesCounter(key, locRibWriter);
194 txChainToLocRibWriter.put(txChain, locRibWriter);
198 public String toString() {
199 return MoreObjects.toStringHelper(this).add("bgpId", bgpIdentifier).add("localTables", localTables).toString();
203 public AsNumber getLocalAs() {
208 public BgpId getBgpIdentifier() {
209 return bgpIdentifier;
213 public Set<? extends BgpTableType> getLocalTables() {
218 public BGPDispatcher getDispatcher() {
223 public synchronized void onTransactionChainFailed(final DOMTransactionChain chain,
224 final DOMDataTreeTransaction transaction, final Throwable cause) {
225 LOG.error("Broken chain in RIB {} transaction {}",
226 getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
227 final LocRibWriter<?, ?> locRibWriter = txChainToLocRibWriter.remove(chain);
228 if (locRibWriter != null) {
229 final DOMTransactionChain newChain = createPeerDOMChain(this);
230 startLocRib(locRibWriter.getTableKey());
231 locRibWriter.restart(newChain);
232 txChainToLocRibWriter.put(newChain, locRibWriter);
237 public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
238 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
242 public Set<TablesKey> getLocalTablesKeys() {
243 return localTablesKeys;
247 public boolean supportsTable(final TablesKey tableKey) {
248 return localTablesKeys.contains(tableKey);
252 public BGPRibRoutingPolicy getRibPolicies() {
257 public BGPPeerTracker getPeerTracker() {
262 public void refreshTable(final TablesKey tk, final PeerId peerId) {
263 final RibOutRefresh table = vpnTableRefresher.get(tk);
265 table.refreshTable(tk, peerId);
270 public DOMDataTreeChangeService getService() {
271 return (DOMDataTreeChangeService) domService;
275 public YangInstanceIdentifier getYangRibId() {
280 public DOMTransactionChain createPeerDOMChain(final DOMTransactionChainListener listener) {
281 return domDataBroker.createMergingTransactionChain(listener);
285 public RIBExtensionConsumerContext getRibExtensions() {
290 public RIBSupportContextRegistry getRibSupportContext() {
291 return ribContextRegistry;
295 public CodecsRegistry getCodecsRegistry() {
296 return codecsRegistry;
299 public synchronized void instantiateServiceInstance() {
300 isServiceInstantiated = true;
302 domChain = domDataBroker.createMergingTransactionChain(this);
303 LOG.debug("Instantiating RIB table {} at {}", ribId, yangRibId);
305 final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(BGPRIB_NID)
306 .addChild(ImmutableNodes.mapNodeBuilder(RIB_NID).build()).build();
308 final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
309 NodeIdentifierWithPredicates.of(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()))
310 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, ribId.getValue()))
311 .addChild(ImmutableNodes.mapNodeBuilder(PEER_NID).build())
312 .addChild(Builders.containerBuilder().withNodeIdentifier(LOCRIB_NID)
313 .addChild(ImmutableNodes.mapNodeBuilder(TABLES_NID).build())
316 final DOMDataTreeWriteTransaction trans = domChain.newWriteOnlyTransaction();
318 // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
319 trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.of(BGPRIB_NID), bgpRib);
320 trans.put(LogicalDatastoreType.OPERATIONAL, yangRibId, ribInstance);
323 trans.commit().get();
324 } catch (final InterruptedException | ExecutionException e) {
325 LOG.error("Failed to initiate RIB {}", yangRibId, e);
328 LOG.debug("Effective RIB created.");
330 localTablesKeys.forEach(this::startLocRib);
331 localTablesKeys.forEach(this::createLocRibWriter);
334 public synchronized FluentFuture<? extends CommitInfo> closeServiceInstance() {
335 if (!isServiceInstantiated) {
336 LOG.trace("RIB {} already closed", ribId.getValue());
337 return CommitInfo.emptyFluentFuture();
339 LOG.info("Close RIB {}", ribId.getValue());
340 isServiceInstantiated = false;
343 txChainToLocRibWriter.values().forEach(LocRibWriter::close);
344 txChainToLocRibWriter.clear();
346 final DOMDataTreeWriteTransaction t = domChain.newWriteOnlyTransaction();
347 t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
348 final FluentFuture<? extends CommitInfo> cleanFuture = t.commit();
349 cleanFuture.addCallback(new FutureCallback<CommitInfo>() {
351 public void onSuccess(final CommitInfo result) {
352 LOG.info("RIB cleaned {}", ribId.getValue());
356 public void onFailure(final Throwable throwable) {
357 LOG.error("Failed to clean RIB {}",
358 ribId.getValue(), throwable);
360 }, MoreExecutors.directExecutor());