2 * Copyright (c) 2015 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 com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Verify;
13 import java.util.Collection;
14 import java.util.HashSet;
17 import java.util.concurrent.ConcurrentHashMap;
18 import javax.annotation.Nonnull;
19 import javax.annotation.concurrent.NotThreadSafe;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
22 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
23 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
24 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
25 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
26 import org.opendaylight.protocol.bgp.rib.impl.spi.AbstractImportPolicy;
27 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
28 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
29 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
30 import org.opendaylight.protocol.bgp.rib.impl.stats.peer.route.PerTableTypeRouteCounter;
31 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
32 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
44 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
47 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
48 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
55 * routes in the context of the advertising peer's role and applies the inbound policy.
57 * Inbound policy is applied as follows:
59 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
60 * 2) check if a route is admissible based on attributes attached to it, as well as the
61 * advertising peer's role
62 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
66 final class EffectiveRibInWriter implements AutoCloseable {
67 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
68 protected static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
70 private final class AdjInTracker implements AutoCloseable, DOMDataTreeChangeListener {
71 private final RIBSupportContextRegistry registry;
72 private final YangInstanceIdentifier peerIId;
73 private final YangInstanceIdentifier effRibTables;
74 private final ListenerRegistration<?> reg;
75 private final DOMTransactionChain chain;
76 private final PerTableTypeRouteCounter effectiveRibInRouteCounters;
77 private final PerTableTypeRouteCounter adjRibInRouteCounters;
78 private final Map<TablesKey, Set<YangInstanceIdentifier>> effectiveRibInRouteMap = new ConcurrentHashMap<>();
79 private final Map<TablesKey, Set<YangInstanceIdentifier>> adjRibInRouteMap = new ConcurrentHashMap<>();
81 AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
82 @Nonnull final PerTableTypeRouteCounter effectiveRibInRouteCounters, @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters) {
83 this.registry = Preconditions.checkNotNull(registry);
84 this.chain = Preconditions.checkNotNull(chain);
85 this.peerIId = Preconditions.checkNotNull(peerIId);
86 this.effRibTables = this.peerIId.node(EffectiveRibIn.QNAME).node(Tables.QNAME);
87 this.effectiveRibInRouteCounters = Preconditions.checkNotNull(effectiveRibInRouteCounters);
88 this.adjRibInRouteCounters = Preconditions.checkNotNull(adjRibInRouteCounters);
90 final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, this.peerIId.node(AdjRibIn.QNAME).node(Tables.QNAME));
91 LOG.debug("Registered Effective RIB on {}", this.peerIId);
92 this.reg = service.registerDataTreeChangeListener(treeId, this);
96 * @deprecated Should always pass in route counter
103 AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId) {
104 this(service, registry, chain, peerIId, new PerTableTypeRouteCounter("effective-rib-in"), new PerTableTypeRouteCounter("adj-rib-in"));
107 private void updateRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
108 @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
109 routeMap.putIfAbsent(tablesKey, new HashSet<>());
110 routeMap.get(tablesKey).add(routeId);
112 updateRouteCounter(counter, routeMap,tablesKey);
115 private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
116 @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
117 if (routeMap.containsKey(tablesKey)) {
118 routeMap.get(tablesKey).remove(routeId);
121 updateRouteCounter(counter, routeMap,tablesKey);
124 private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
125 @Nonnull final TablesKey tablesKey) {
126 routeMap.remove(tablesKey);
128 updateRouteCounter(counter, routeMap,tablesKey);
131 private void updateRouteCounter(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
132 @Nonnull final TablesKey tablesKey) {
133 counter.getCounterOrSetDefault(tablesKey)
134 .setCount(routeMap.getOrDefault(tablesKey, new HashSet<>()).size());
137 private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
138 LOG.debug("Process route {}", route.getIdentifier());
139 final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
140 final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
141 switch (route.getModificationType()) {
144 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
145 LOG.debug("Route deleted. routeId={}", routeId);
147 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
148 deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
154 case SUBTREE_MODIFIED:
156 tx.put(LogicalDatastoreType.OPERATIONAL, routeId, route.getDataAfter().get());
157 // count adj-rib-in route first
158 updateRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
159 updateRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
160 // Lookup per-table attributes from RIBSupport
161 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
162 final ContainerNode effectiveAttrs;
164 if (advertisedAttrs != null) {
165 effectiveAttrs = policy.effectiveAttributes(advertisedAttrs);
167 effectiveAttrs = null;
170 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
172 if (effectiveAttrs != null) {
173 tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
175 updateRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
177 LOG.warn("Route {} advertised empty attributes", routeId);
178 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
180 deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
184 LOG.warn("Ignoring unhandled route {}", route);
189 private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
190 for (final DataTreeCandidateNode child : children) {
191 final PathArgument childIdentifier = child.getIdentifier();
192 final Optional<NormalizedNode<?, ?>> childDataAfter = child.getDataAfter();
193 final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
194 LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}", childIdentifier, child
195 .getModificationType(), childDataAfter, child.getDataBefore());
196 final YangInstanceIdentifier childPath = tablePath.node(childIdentifier);
197 switch (child.getModificationType()) {
200 tx.delete(LogicalDatastoreType.OPERATIONAL, childPath);
201 LOG.debug("Route deleted. routeId={}", childPath);
203 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, childPath);
204 deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, childPath);
209 case SUBTREE_MODIFIED:
210 processModifiedRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
214 writeRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
218 LOG.warn("Ignoring unhandled child {}", child);
224 private void processModifiedRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier, final DOMDataWriteTransaction tx,
225 final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
226 if (TABLE_ROUTES.equals(childIdentifier)) {
227 for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
228 processRoute(tx, ribSupport, policy, childPath, route);
231 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
235 private void writeRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier, final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
236 if (TABLE_ROUTES.equals(childIdentifier)) {
237 final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
238 if (!changedRoutes.isEmpty()) {
239 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
240 // Routes are special, as they may end up being filtered. The previous put conveniently
241 // ensured that we have them in at target, so a subsequent delete will not fail :)
242 for (final DataTreeCandidateNode route : changedRoutes) {
243 processRoute(tx, ribSupport, policy, childPath, route);
249 private RIBSupportContext getRibSupport(final NodeIdentifierWithPredicates tableKey) {
250 return this.registry.getRIBSupportContext(tableKey);
253 private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
254 return this.effRibTables.node(tableKey);
257 private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
258 final RIBSupportContext ribSupport = getRibSupport(tableKey);
259 final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
261 processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
264 private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
265 final RIBSupportContext ribSupport = getRibSupport(tableKey);
266 final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
268 // Create an empty table
269 LOG.trace("Create Empty table", tablePath);
270 ribSupport.createEmptyTableStructure(tx, tablePath);
272 processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
276 public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
277 LOG.trace("Data changed called to effective RIB. Change : {}", changes);
279 // we have a lot of transactions created for 'nothing' because a lot of changes
280 // are skipped, so ensure we only create one transaction when we really need it
281 DOMDataWriteTransaction tx = null;
282 for (final DataTreeCandidate tc : changes) {
283 final YangInstanceIdentifier rootPath = tc.getRootPath();
285 final DataTreeCandidateNode root = tc.getRootNode();
286 for (final DataTreeCandidateNode table : root.getChildNodes()) {
288 tx = this.chain.newWriteOnlyTransaction();
290 changeDataTree(tx, rootPath, root, table);
298 private void changeDataTree(final DOMDataWriteTransaction tx, final YangInstanceIdentifier rootPath,
299 final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
300 final PathArgument lastArg = table.getIdentifier();
301 Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
302 final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
303 final RIBSupport ribSupport = getRibSupport(tableKey).getRibSupport();
304 final ModificationType modificationType = root.getModificationType();
305 switch (modificationType) {
308 final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
309 LOG.debug("Delete Effective Table {} modification type {}, ", effectiveTablePath, modificationType);
311 // delete the corresponding effective table
312 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
314 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, new TablesKey(ribSupport.getAfi(), ribSupport.getSafi()));
315 deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, new TablesKey(ribSupport.getAfi(), ribSupport.getSafi()));
317 case SUBTREE_MODIFIED:
318 modifyTable(tx, tableKey, table);
321 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
325 writeTable(tx, tableKey, table);
328 LOG.warn("Ignoring unhandled root {}", root);
334 public void close() {
339 private final AdjInTracker adjInTracker;
340 private final AbstractImportPolicy importPolicy;
343 static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
344 @Nonnull final YangInstanceIdentifier peerIId, @Nonnull final ImportPolicyPeerTracker importPolicyPeerTracker, @Nonnull final RIBSupportContextRegistry registry, final PeerRole peerRole) {
345 return new EffectiveRibInWriter(service, chain, peerIId, importPolicyPeerTracker, registry, peerRole);
348 static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
349 @Nonnull final YangInstanceIdentifier peerIId, @Nonnull final ImportPolicyPeerTracker importPolicyPeerTracker, @Nonnull final RIBSupportContextRegistry registry, final PeerRole peerRole,
350 @Nonnull final PerTableTypeRouteCounter effectiveRouteCounters, @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters) {
351 return new EffectiveRibInWriter(service, chain, peerIId, importPolicyPeerTracker, registry, peerRole, effectiveRouteCounters, adjRibInRouteCounters);
355 private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
356 final ImportPolicyPeerTracker importPolicyPeerTracker, final RIBSupportContextRegistry registry, final PeerRole peerRole) {
357 importPolicyPeerTracker.peerRoleChanged(peerIId, peerRole);
358 this.importPolicy = importPolicyPeerTracker.policyFor(IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerIId.getLastPathArgument()));
359 this.adjInTracker = new AdjInTracker(service, registry, chain, peerIId);
362 private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
363 final ImportPolicyPeerTracker importPolicyPeerTracker, final RIBSupportContextRegistry registry, final PeerRole peerRole,
364 @Nonnull final PerTableTypeRouteCounter effectiveRouteCounters, @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters) {
365 importPolicyPeerTracker.peerRoleChanged(peerIId, peerRole);
366 this.importPolicy = importPolicyPeerTracker.policyFor(IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerIId.getLastPathArgument()));
367 this.adjInTracker = new AdjInTracker(service, registry, chain, peerIId, effectiveRouteCounters, adjRibInRouteCounters);
371 public void close() {
372 this.adjInTracker.close();