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;
12 import com.google.common.base.MoreObjects;
13 import com.google.common.base.MoreObjects.ToStringHelper;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.util.concurrent.Futures;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.List;
22 import java.util.Map.Entry;
23 import java.util.Optional;
25 import javax.annotation.Nonnull;
26 import javax.annotation.concurrent.GuardedBy;
27 import javax.annotation.concurrent.ThreadSafe;
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.common.api.data.TransactionCommitFailedException;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
37 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
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.rib.impl.spi.BGPDispatcher;
42 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
43 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
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.ExportPolicyPeerTracker;
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.RibSupportUtils;
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.message.rev171207.path.attributes.Attributes;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.BgpTableType;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.BgpRib;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.RibId;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.Rib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.RibKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.LocRib;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.Peer;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpId;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.opendaylight.yangtools.yang.common.QName;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
69 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
72 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
77 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
78 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
79 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
80 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
81 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
86 public final class RIBImpl extends BGPRIBStateImpl implements RIB, TransactionChainListener,
87 SchemaContextListener, 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();
90 private static final ContainerNode EMPTY_TABLE_ATTRIBUTES = ImmutableNodes.containerNode(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.tables.Attributes.QNAME);
92 private final BGPDispatcher dispatcher;
93 private final AsNumber localAs;
94 private final BgpId bgpIdentifier;
95 private final Set<BgpTableType> localTables;
96 private final Set<TablesKey> localTablesKeys;
97 private final DOMDataBroker domDataBroker;
98 private final RIBExtensionConsumerContext extensions;
99 private final YangInstanceIdentifier yangRibId;
100 private final RIBSupportContextRegistryImpl ribContextRegistry;
101 private final CodecsRegistryImpl codecsRegistry;
103 private ClusterSingletonServiceRegistration registration;
104 private final DOMDataBrokerExtension service;
105 private final Map<TransactionChain<?, ?>, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
106 private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
107 private final ImportPolicyPeerTracker importPolicyPeerTracker;
108 private final RibId ribId;
109 private final BGPPeerTracker peerTracker;
110 private final Map<TablesKey, ExportPolicyPeerTracker> exportPolicyPeerTrackerMap;
112 private DOMTransactionChain domChain;
114 private boolean isServiceInstantiated;
116 public RIBImpl(final RibId ribId,
117 final AsNumber localAs,
118 final BgpId localBgpId,
119 final ClusterIdentifier clusterId,
120 final RIBExtensionConsumerContext extensions,
121 final BGPDispatcher dispatcher,
122 final CodecsRegistryImpl codecsRegistry,
123 final DOMDataBroker domDataBroker,
124 final BGPPeerTracker bgpPeerTracker,
125 final List<BgpTableType> localTables,
126 @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies) {
127 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
128 localBgpId, localAs);
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.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
136 this.extensions = requireNonNull(extensions);
137 this.peerTracker = requireNonNull(bgpPeerTracker);
138 this.codecsRegistry = codecsRegistry;
139 this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
140 final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
141 this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
142 this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
143 final ClusterIdentifier cId = clusterId == null ? new ClusterIdentifier(localBgpId) : clusterId;
145 final PolicyDatabase policyDatabase = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
146 this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl(policyDatabase);
148 final ImmutableMap.Builder<TablesKey, ExportPolicyPeerTracker> exportPolicies = new ImmutableMap.Builder<>();
149 for (final BgpTableType t : this.localTables) {
150 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
151 this.localTablesKeys.add(key);
152 exportPolicies.put(key, new ExportPolicyPeerTrackerImpl(policyDatabase, key));
154 this.exportPolicyPeerTrackerMap = exportPolicies.build();
157 private synchronized void startLocRib(final TablesKey key) {
158 LOG.debug("Creating LocRib table for {}", key);
159 // create locRibWriter for each table
160 final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
162 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
163 table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
164 table.withChild(EMPTY_TABLE_ATTRIBUTES);
166 final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
167 final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
168 tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
169 for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
170 table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
173 final RIBSupportContext supportContext = this.ribContextRegistry.getRIBSupportContext(key);
174 if (supportContext != null) {
175 final ChoiceNode routes = supportContext.getRibSupport().emptyRoutes();
176 table.withChild(routes);
178 tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
180 tx.submit().checkedGet();
181 } catch (final TransactionCommitFailedException e1) {
182 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
184 createLocRibWriter(key);
186 LOG.warn("There's no registered RIB Context for {}", key.getAfi());
190 private synchronized void createLocRibWriter(final TablesKey key) {
191 LOG.debug("Creating LocRIB writer for key {}", key);
192 final DOMTransactionChain txChain = createPeerChain(this);
193 PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
194 if (pathSelectionStrategy == null) {
195 pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy(this.peerTracker);
198 final LocRibWriter locRibWriter = LocRibWriter.create(this.ribContextRegistry, key, txChain,
199 getYangRibId(), this.localAs, getService(), this.exportPolicyPeerTrackerMap.get(key), pathSelectionStrategy);
200 registerTotalPathCounter(key, locRibWriter);
201 registerTotalPrefixesCounter(key, locRibWriter);
202 this.txChainToLocRibWriter.put(txChain, locRibWriter);
206 public String toString() {
207 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
210 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
211 return toStringHelper;
215 public synchronized void close() throws Exception {
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;
234 public Set<? extends BgpTableType> getLocalTables() {
235 return this.localTables;
239 public BGPDispatcher getDispatcher() {
240 return this.dispatcher;
244 public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
245 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
246 if (this.txChainToLocRibWriter.containsKey(chain)) {
247 final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
248 final DOMTransactionChain newChain = createPeerChain(this);
249 locRibWriter.restart(newChain);
250 this.txChainToLocRibWriter.put(newChain, locRibWriter);
255 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
256 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
260 public Set<TablesKey> getLocalTablesKeys() {
261 return this.localTablesKeys;
265 public boolean supportsTable(final TablesKey tableKey) {
266 return this.localTablesKeys.contains(tableKey);
270 public BGPPeerTracker getPeerTracker() {
271 return this.peerTracker;
275 public DOMDataTreeChangeService getService() {
276 return (DOMDataTreeChangeService) this.service;
280 public YangInstanceIdentifier getYangRibId() {
281 return this.yangRibId;
285 public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
286 return this.domDataBroker.createTransactionChain(listener);
290 public RIBExtensionConsumerContext getRibExtensions() {
291 return this.extensions;
295 public RIBSupportContextRegistry getRibSupportContext() {
296 return this.ribContextRegistry;
300 public void onGlobalContextUpdated(final SchemaContext context) {
301 this.codecsRegistry.onSchemaContextUpdated(context);
305 public CodecsRegistry getCodecsRegistry() {
306 return this.codecsRegistry;
310 public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
311 return this.importPolicyPeerTracker;
315 public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
316 return this.exportPolicyPeerTrackerMap.get(tablesKey);
319 public synchronized void instantiateServiceInstance() {
320 this.isServiceInstantiated = true;
322 this.domChain = this.domDataBroker.createTransactionChain(this);
323 LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
325 final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
326 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
328 final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
329 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
330 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
331 .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
332 .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
333 .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
337 final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
339 // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
340 trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
341 trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
344 trans.submit().checkedGet();
345 } catch (final TransactionCommitFailedException e) {
346 LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
349 LOG.debug("Effective RIB created.");
351 this.localTablesKeys.forEach(this::startLocRib);
354 public synchronized ListenableFuture<Void> closeServiceInstance() {
355 if (!this.isServiceInstantiated) {
356 LOG.trace("RIB {} already closed", this.ribId.getValue());
357 return Futures.immediateFuture(null);
359 LOG.info("Close RIB {}", this.ribId.getValue());
360 this.isServiceInstantiated = false;
363 this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
364 this.txChainToLocRibWriter.clear();
366 final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
367 t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
368 final ListenableFuture<Void> cleanFuture = t.submit();
370 this.domChain.close();
376 @SuppressWarnings("unchecked")
377 public final Optional<ContainerNode> toNormalizedNodeAttribute(
378 final RIBSupport ribSupport,
379 final NodeIdentifierWithPredicates routeIdentifier,
380 final Optional<Attributes> attributes) {
381 if (!attributes.isPresent()) {
382 return Optional.empty();
384 return Optional.empty();
388 public final Optional<Attributes> getAttributes(
389 final RIBSupport ribSupport,
390 final NodeIdentifierWithPredicates routeIdentifier,
391 final NormalizedNode<?, ?> route) {
392 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes
393 .findNode(route, ribSupport.routeAttributesIdentifier()).orElse(null);
394 if (advertisedAttrs == null) {
395 return Optional.empty();
398 return Optional.empty();