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.binding.dom.codec.api.BindingNormalizedNodeSerializer;
39 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
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.rib.impl.spi.BGPDispatcher;
43 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
47 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
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.ExportPolicyPeerTracker;
51 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
52 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
53 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
54 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.path.attributes.Attributes;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.BgpTableType;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.BgpRib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.RibId;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.Rib;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.RibKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.LocRib;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.Peer;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpId;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
68 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
69 import org.opendaylight.yangtools.yang.common.QName;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
74 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
79 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
80 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
81 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
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;
88 public final class RIBImpl extends BGPRIBStateImpl implements RIB, TransactionChainListener,
89 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();
92 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);
94 private final BGPDispatcher dispatcher;
95 private final AsNumber localAs;
96 private final BgpId bgpIdentifier;
97 private final Set<BgpTableType> localTables;
98 private final Set<TablesKey> localTablesKeys;
99 private final DOMDataBroker domDataBroker;
100 private final RIBExtensionConsumerContext extensions;
101 private final YangInstanceIdentifier yangRibId;
102 private final YangInstanceIdentifier yangTables;
103 private final RIBSupportContextRegistryImpl ribContextRegistry;
104 private final CodecsRegistryImpl codecsRegistry;
106 private ClusterSingletonServiceRegistration registration;
107 private final DOMDataBrokerExtension service;
108 private final Map<TransactionChain<?, ?>, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
109 private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
110 private final ImportPolicyPeerTracker importPolicyPeerTracker;
111 private final RibId ribId;
112 private final BGPPeerTracker peerTracker;
113 private final BGPRibRoutingPolicy ribPolicies;
114 private final Map<TablesKey, ExportPolicyPeerTracker> exportPolicyPeerTrackerMap;
115 private final BindingNormalizedNodeSerializer bindingSerializer;
118 private DOMTransactionChain domChain;
120 private boolean isServiceInstantiated;
122 public RIBImpl(final RibId ribId,
123 final AsNumber localAs,
124 final BgpId localBgpId,
125 final ClusterIdentifier clusterId,
126 final RIBExtensionConsumerContext extensions,
127 final BGPDispatcher dispatcher,
128 final CodecsRegistryImpl codecsRegistry,
129 final DOMDataBroker domDataBroker,
130 final BGPRibRoutingPolicy ribPolicies,
131 final BGPPeerTracker bgpPeerTracker,
132 final List<BgpTableType> localTables,
133 final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies,
134 final BindingNormalizedNodeSerializer bindingSerializer
136 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
137 localBgpId, localAs);
138 this.localAs = requireNonNull(localAs);
139 this.bgpIdentifier = requireNonNull(localBgpId);
140 this.dispatcher = requireNonNull(dispatcher);
141 this.localTables = ImmutableSet.copyOf(localTables);
142 this.localTablesKeys = new HashSet<>();
143 this.domDataBroker = requireNonNull(domDataBroker);
144 this.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
145 this.extensions = requireNonNull(extensions);
146 this.ribPolicies = requireNonNull(ribPolicies);
147 this.peerTracker = requireNonNull(bgpPeerTracker);
148 this.codecsRegistry = codecsRegistry;
149 this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
150 final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
151 this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
152 this.yangTables = this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME);
153 this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
154 final ClusterIdentifier cId = clusterId == null ? new ClusterIdentifier(localBgpId) : clusterId;
156 final PolicyDatabase policyDatabase = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
157 this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl(policyDatabase);
159 final ImmutableMap.Builder<TablesKey, ExportPolicyPeerTracker> exportPolicies = new ImmutableMap.Builder<>();
160 for (final BgpTableType t : this.localTables) {
161 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
162 this.localTablesKeys.add(key);
163 exportPolicies.put(key, new ExportPolicyPeerTrackerImpl(policyDatabase, key));
165 this.exportPolicyPeerTrackerMap = exportPolicies.build();
166 this.bindingSerializer = requireNonNull(bindingSerializer);
169 private synchronized void startLocRib(final TablesKey key) {
170 LOG.debug("Creating LocRib table for {}", key);
171 // create locRibWriter for each table
172 final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
174 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
175 table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
176 table.withChild(EMPTY_TABLE_ATTRIBUTES);
178 final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
179 final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
180 tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
181 for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
182 table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
185 final RIBSupportContext supportContext = this.ribContextRegistry.getRIBSupportContext(key);
186 if (supportContext != null) {
187 final ChoiceNode routes = supportContext.getRibSupport().emptyRoutes();
188 table.withChild(routes);
190 tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
192 tx.submit().checkedGet();
193 } catch (final TransactionCommitFailedException e1) {
194 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
196 createLocRibWriter(key);
198 LOG.warn("There's no registered RIB Context for {}", key.getAfi());
202 private synchronized void createLocRibWriter(final TablesKey key) {
203 LOG.debug("Creating LocRIB writer for key {}", key);
204 final DOMTransactionChain txChain = createPeerChain(this);
205 PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
206 if (pathSelectionStrategy == null) {
207 pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy(this.peerTracker);
210 final LocRibWriter locRibWriter = LocRibWriter.create(this.ribContextRegistry, key, txChain,
211 getYangRibId(), this.localAs, getService(), this.exportPolicyPeerTrackerMap.get(key), pathSelectionStrategy);
212 registerTotalPathCounter(key, locRibWriter);
213 registerTotalPrefixesCounter(key, locRibWriter);
214 this.txChainToLocRibWriter.put(txChain, locRibWriter);
218 public String toString() {
219 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
222 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
223 return toStringHelper;
227 public synchronized void close() throws Exception {
228 if (this.registration != null) {
229 this.registration.close();
230 this.registration = null;
235 public AsNumber getLocalAs() {
240 public BgpId getBgpIdentifier() {
241 return this.bgpIdentifier;
246 public Set<? extends BgpTableType> getLocalTables() {
247 return this.localTables;
251 public BGPDispatcher getDispatcher() {
252 return this.dispatcher;
256 public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
257 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
258 if (this.txChainToLocRibWriter.containsKey(chain)) {
259 final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
260 final DOMTransactionChain newChain = createPeerChain(this);
261 locRibWriter.restart(newChain);
262 this.txChainToLocRibWriter.put(newChain, locRibWriter);
267 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
268 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
272 public Set<TablesKey> getLocalTablesKeys() {
273 return this.localTablesKeys;
277 public boolean supportsTable(final TablesKey tableKey) {
278 return this.localTablesKeys.contains(tableKey);
282 public BGPRibRoutingPolicy getRibPolicies() {
283 return this.ribPolicies;
287 public BGPPeerTracker getPeerTracker() {
288 return this.peerTracker;
292 public DOMDataTreeChangeService getService() {
293 return (DOMDataTreeChangeService) this.service;
297 public YangInstanceIdentifier getYangRibId() {
298 return this.yangRibId;
302 public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
303 return this.domDataBroker.createTransactionChain(listener);
307 public RIBExtensionConsumerContext getRibExtensions() {
308 return this.extensions;
312 public RIBSupportContextRegistry getRibSupportContext() {
313 return this.ribContextRegistry;
317 public void onGlobalContextUpdated(final SchemaContext context) {
318 this.codecsRegistry.onSchemaContextUpdated(context);
322 public CodecsRegistry getCodecsRegistry() {
323 return this.codecsRegistry;
327 public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
328 return this.importPolicyPeerTracker;
332 public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
333 return this.exportPolicyPeerTrackerMap.get(tablesKey);
336 public synchronized void instantiateServiceInstance() {
337 this.isServiceInstantiated = true;
339 this.domChain = this.domDataBroker.createTransactionChain(this);
340 LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
342 final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
343 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
345 final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
346 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
347 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
348 .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
349 .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
350 .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
354 final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
356 // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
357 trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
358 trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
361 trans.submit().checkedGet();
362 } catch (final TransactionCommitFailedException e) {
363 LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
366 LOG.debug("Effective RIB created.");
368 this.localTablesKeys.forEach(this::startLocRib);
371 public synchronized ListenableFuture<Void> closeServiceInstance() {
372 if (!this.isServiceInstantiated) {
373 LOG.trace("RIB {} already closed", this.ribId.getValue());
374 return Futures.immediateFuture(null);
376 LOG.info("Close RIB {}", this.ribId.getValue());
377 this.isServiceInstantiated = false;
380 this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
381 this.txChainToLocRibWriter.clear();
383 final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
384 t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
385 final ListenableFuture<Void> cleanFuture = t.submit();
387 this.domChain.close();
393 @SuppressWarnings("unchecked")
394 public final Optional<ContainerNode> toNormalizedNodeAttribute(
395 final RIBSupport ribSupport,
396 final NodeIdentifierWithPredicates routeIdentifier,
397 final Optional<Attributes> attributes) {
398 if (!attributes.isPresent()) {
399 return Optional.empty();
401 final InstanceIdentifier<Attributes> yii
402 = (InstanceIdentifier<Attributes>) this.bindingSerializer
403 .fromYangInstanceIdentifier(ribSupport.buildRouteAttributeYii(this.yangTables, routeIdentifier));
404 return Optional.of((ContainerNode) this.bindingSerializer.toNormalizedNode(yii, attributes.get()).getValue());
408 public final Optional<Attributes> getAttributes(
409 final RIBSupport ribSupport,
410 final NodeIdentifierWithPredicates routeIdentifier,
411 final NormalizedNode<?, ?> route) {
412 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes
413 .findNode(route, ribSupport.routeAttributesIdentifier()).orElse(null);
414 if (advertisedAttrs == null) {
415 return Optional.empty();
418 return Optional.ofNullable((Attributes) this.bindingSerializer
419 .fromNormalizedNode(ribSupport.buildRouteAttributeYii(this.yangTables, routeIdentifier),
420 advertisedAttrs).getValue());