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.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.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.path.attributes.Attributes;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.BgpTableType;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.BgpRib;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.RibId;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.Rib;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.RibKey;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.LocRib;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.Peer;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpId;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
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.NodeIdentifier;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
73 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
78 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
79 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
80 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
81 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
82 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
87 public final class RIBImpl extends BGPRIBStateImpl implements RIB, TransactionChainListener,
88 SchemaContextListener, AutoCloseable {
89 private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
90 private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
91 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);
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 RIBExtensionConsumerContext extensions;
100 private final YangInstanceIdentifier yangRibId;
101 private final YangInstanceIdentifier yangTables;
102 private final RIBSupportContextRegistryImpl ribContextRegistry;
103 private final CodecsRegistryImpl codecsRegistry;
105 private ClusterSingletonServiceRegistration registration;
106 private final DOMDataBrokerExtension service;
107 private final Map<TransactionChain<?, ?>, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
108 private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
109 private final RibId ribId;
110 private final BGPPeerTracker peerTracker;
111 private final BGPRibRoutingPolicy ribPolicies;
112 private final Map<TablesKey, ExportPolicyPeerTracker> exportPolicyPeerTrackerMap;
113 private final BindingNormalizedNodeSerializer bindingSerializer;
116 private DOMTransactionChain domChain;
118 private boolean isServiceInstantiated;
120 public RIBImpl(final RibId ribId,
121 final AsNumber localAs,
122 final BgpId localBgpId,
123 final ClusterIdentifier clusterId,
124 final RIBExtensionConsumerContext extensions,
125 final BGPDispatcher dispatcher,
126 final CodecsRegistryImpl codecsRegistry,
127 final DOMDataBroker domDataBroker,
128 final BGPRibRoutingPolicy ribPolicies,
129 final BGPPeerTracker bgpPeerTracker,
130 final List<BgpTableType> localTables,
131 final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies,
132 final BindingNormalizedNodeSerializer bindingSerializer
134 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
135 localBgpId, localAs);
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.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
143 this.extensions = requireNonNull(extensions);
144 this.ribPolicies = requireNonNull(ribPolicies);
145 this.peerTracker = requireNonNull(bgpPeerTracker);
146 this.codecsRegistry = codecsRegistry;
147 this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
148 final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
149 this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
150 this.yangTables = this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME);
151 this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
152 final ClusterIdentifier cId = clusterId == null ? new ClusterIdentifier(localBgpId) : clusterId;
154 final PolicyDatabase policyDatabase = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
156 final ImmutableMap.Builder<TablesKey, ExportPolicyPeerTracker> exportPolicies = new ImmutableMap.Builder<>();
157 for (final BgpTableType t : this.localTables) {
158 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
159 this.localTablesKeys.add(key);
160 exportPolicies.put(key, new ExportPolicyPeerTrackerImpl(policyDatabase, key));
162 this.exportPolicyPeerTrackerMap = exportPolicies.build();
163 this.bindingSerializer = requireNonNull(bindingSerializer);
166 private synchronized void startLocRib(final TablesKey key) {
167 LOG.debug("Creating LocRib table for {}", key);
168 // create locRibWriter for each table
169 final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
171 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
172 table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
173 table.withChild(EMPTY_TABLE_ATTRIBUTES);
175 final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
176 final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
177 tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
178 for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
179 table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
182 final RIBSupportContext supportContext = this.ribContextRegistry.getRIBSupportContext(key);
183 if (supportContext != null) {
184 final ChoiceNode routes = supportContext.getRibSupport().emptyRoutes();
185 table.withChild(routes);
187 tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
189 tx.submit().checkedGet();
190 } catch (final TransactionCommitFailedException e1) {
191 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
193 createLocRibWriter(key);
195 LOG.warn("There's no registered RIB Context for {}", key.getAfi());
199 private synchronized void createLocRibWriter(final TablesKey key) {
200 LOG.debug("Creating LocRIB writer for key {}", key);
201 final DOMTransactionChain txChain = createPeerChain(this);
202 PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
203 if (pathSelectionStrategy == null) {
204 pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy(this.peerTracker);
207 final LocRibWriter locRibWriter = LocRibWriter.create(this.ribContextRegistry, key, txChain,
208 getYangRibId(), this.localAs, getService(), this.exportPolicyPeerTrackerMap.get(key), pathSelectionStrategy);
209 registerTotalPathCounter(key, locRibWriter);
210 registerTotalPrefixesCounter(key, locRibWriter);
211 this.txChainToLocRibWriter.put(txChain, locRibWriter);
215 public String toString() {
216 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
219 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
220 return toStringHelper;
224 public synchronized void close() throws Exception {
225 if (this.registration != null) {
226 this.registration.close();
227 this.registration = null;
232 public AsNumber getLocalAs() {
237 public BgpId getBgpIdentifier() {
238 return this.bgpIdentifier;
243 public Set<? extends BgpTableType> getLocalTables() {
244 return this.localTables;
248 public BGPDispatcher getDispatcher() {
249 return this.dispatcher;
253 public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
254 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
255 if (this.txChainToLocRibWriter.containsKey(chain)) {
256 final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
257 final DOMTransactionChain newChain = createPeerChain(this);
258 locRibWriter.restart(newChain);
259 this.txChainToLocRibWriter.put(newChain, locRibWriter);
264 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
265 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
269 public Set<TablesKey> getLocalTablesKeys() {
270 return this.localTablesKeys;
274 public boolean supportsTable(final TablesKey tableKey) {
275 return this.localTablesKeys.contains(tableKey);
279 public BGPRibRoutingPolicy getRibPolicies() {
280 return this.ribPolicies;
284 public BGPPeerTracker getPeerTracker() {
285 return this.peerTracker;
289 public DOMDataTreeChangeService getService() {
290 return (DOMDataTreeChangeService) this.service;
294 public YangInstanceIdentifier getYangRibId() {
295 return this.yangRibId;
299 public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
300 return this.domDataBroker.createTransactionChain(listener);
304 public RIBExtensionConsumerContext getRibExtensions() {
305 return this.extensions;
309 public RIBSupportContextRegistry getRibSupportContext() {
310 return this.ribContextRegistry;
314 public void onGlobalContextUpdated(final SchemaContext context) {
315 this.codecsRegistry.onSchemaContextUpdated(context);
319 public CodecsRegistry getCodecsRegistry() {
320 return this.codecsRegistry;
324 public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
325 return this.exportPolicyPeerTrackerMap.get(tablesKey);
328 public synchronized void instantiateServiceInstance() {
329 this.isServiceInstantiated = true;
331 this.domChain = this.domDataBroker.createTransactionChain(this);
332 LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
334 final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
335 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
337 final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
338 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
339 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
340 .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
341 .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
342 .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
346 final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
348 // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
349 trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
350 trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
353 trans.submit().checkedGet();
354 } catch (final TransactionCommitFailedException e) {
355 LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
358 LOG.debug("Effective RIB created.");
360 this.localTablesKeys.forEach(this::startLocRib);
363 public synchronized ListenableFuture<Void> closeServiceInstance() {
364 if (!this.isServiceInstantiated) {
365 LOG.trace("RIB {} already closed", this.ribId.getValue());
366 return Futures.immediateFuture(null);
368 LOG.info("Close RIB {}", this.ribId.getValue());
369 this.isServiceInstantiated = false;
372 this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
373 this.txChainToLocRibWriter.clear();
375 final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
376 t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
377 final ListenableFuture<Void> cleanFuture = t.submit();
379 this.domChain.close();
385 @SuppressWarnings("unchecked")
386 public final Optional<ContainerNode> toNormalizedNodeAttribute(
387 final RIBSupport ribSupport,
388 final NodeIdentifierWithPredicates routeIdentifier,
389 final Optional<Attributes> attributes) {
390 if (!attributes.isPresent()) {
391 return Optional.empty();
393 final InstanceIdentifier<Attributes> yii
394 = (InstanceIdentifier<Attributes>) this.bindingSerializer
395 .fromYangInstanceIdentifier(ribSupport.buildRouteAttributeYii(this.yangTables, routeIdentifier));
396 return Optional.of((ContainerNode) this.bindingSerializer.toNormalizedNode(yii, attributes.get()).getValue());
400 public final Optional<Attributes> getAttributes(
401 final RIBSupport ribSupport,
402 final NodeIdentifierWithPredicates routeIdentifier,
403 final NormalizedNode<?, ?> route) {
404 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes
405 .findNode(route, ribSupport.routeAttributesIdentifier()).orElse(null);
406 if (advertisedAttrs == null) {
407 return Optional.empty();
410 return Optional.ofNullable((Attributes) this.bindingSerializer
411 .fromNormalizedNode(ribSupport.buildRouteAttributeYii(this.yangTables, routeIdentifier),
412 advertisedAttrs).getValue());