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 static java.util.Objects.requireNonNull;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Verify;
15 import com.google.common.collect.ImmutableMap;
16 import com.google.common.collect.ImmutableSet;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.HashSet;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.atomic.LongAdder;
24 import javax.annotation.Nonnull;
25 import javax.annotation.concurrent.NotThreadSafe;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
28 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
30 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
31 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
32 import org.opendaylight.protocol.bgp.rib.impl.spi.AbstractImportPolicy;
33 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
34 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
35 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
36 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
37 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
38 import org.opendaylight.protocol.bgp.rib.impl.stats.peer.route.PerTableTypeRouteCounter;
39 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
40 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
47 import org.opendaylight.yangtools.concepts.ListenerRegistration;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
52 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
55 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
56 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
62 * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
63 * routes in the context of the advertising peer's role and applies the inbound policy.
65 * Inbound policy is applied as follows:
67 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
68 * 2) check if a route is admissible based on attributes attached to it, as well as the
69 * advertising peer's role
70 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
74 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters, AutoCloseable {
75 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
77 private static final Set<YangInstanceIdentifier> EMPTY_SET = Collections.emptySet();
78 static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
80 private final class AdjInTracker implements PrefixesReceivedCounters, PrefixesInstalledCounters, AutoCloseable,
81 ClusteredDOMDataTreeChangeListener {
82 private final RIBSupportContextRegistry registry;
83 private final YangInstanceIdentifier peerIId;
84 private final YangInstanceIdentifier effRibTables;
85 private final ListenerRegistration<?> reg;
86 private final DOMTransactionChain chain;
87 private final PerTableTypeRouteCounter adjRibInRouteCounters;
88 private final Map<TablesKey, Set<YangInstanceIdentifier>> adjRibInRouteMap = new ConcurrentHashMap<>();
89 private final Map<TablesKey, LongAdder> prefixesReceived;
90 private final Map<TablesKey, LongAdder> prefixesInstalled;
92 AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry,
93 final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
94 @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters, @Nonnull Set<TablesKey> tables) {
95 this.registry = requireNonNull(registry);
96 this.chain = requireNonNull(chain);
97 this.peerIId = requireNonNull(peerIId);
98 this.effRibTables = this.peerIId.node(EffectiveRibIn.QNAME).node(Tables.QNAME);
99 this.adjRibInRouteCounters = requireNonNull(adjRibInRouteCounters);
100 this.prefixesInstalled = buildPrefixesTables(tables);
101 this.prefixesReceived = buildPrefixesTables(tables);
103 final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
104 this.peerIId.node(AdjRibIn.QNAME).node(Tables.QNAME));
105 LOG.debug("Registered Effective RIB on {}", this.peerIId);
106 this.reg = service.registerDataTreeChangeListener(treeId, this);
109 private Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
110 final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
111 tables.forEach(table -> b.put(table, new LongAdder()));
116 private void updateRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
117 @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
118 routeMap.putIfAbsent(tablesKey, new HashSet<>());
119 routeMap.get(tablesKey).add(routeId);
120 updateRouteCounter(counter, routeMap,tablesKey);
123 private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
124 @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
125 if (routeMap.containsKey(tablesKey)) {
126 routeMap.get(tablesKey).remove(routeId);
129 updateRouteCounter(counter, routeMap,tablesKey);
132 private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
133 @Nonnull final TablesKey tablesKey) {
134 routeMap.remove(tablesKey);
136 updateRouteCounter(counter, routeMap,tablesKey);
139 private void updateRouteCounter(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey,
140 Set<YangInstanceIdentifier>> routeMap, @Nonnull final TablesKey tablesKey) {
141 final int size = routeMap.getOrDefault(tablesKey, EMPTY_SET).size();
142 counter.setValueToCounterOrSetDefault(tablesKey, size);
145 private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy,
146 final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
147 LOG.debug("Process route {}", route.getIdentifier());
148 final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
149 final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
150 switch (route.getModificationType()) {
153 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
154 LOG.debug("Route deleted. routeId={}", routeId);
156 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
157 CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
163 case SUBTREE_MODIFIED:
165 tx.put(LogicalDatastoreType.OPERATIONAL, routeId, route.getDataAfter().get());
166 CountersUtil.increment(this.prefixesReceived.get(tablesKey), tablesKey);
167 // count adj-rib-in route first
168 updateRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
169 // Lookup per-table attributes from RIBSupport
170 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
171 final ContainerNode effectiveAttrs;
173 if (advertisedAttrs != null) {
174 effectiveAttrs = policy.effectiveAttributes(advertisedAttrs);
176 effectiveAttrs = null;
179 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
181 if (effectiveAttrs != null) {
182 tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
183 if(route.getModificationType() == ModificationType.WRITE) {
184 CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey);
187 LOG.warn("Route {} advertised empty attributes", routeId);
188 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
192 LOG.warn("Ignoring unhandled route {}", route);
197 private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
198 for (final DataTreeCandidateNode child : children) {
199 final PathArgument childIdentifier = child.getIdentifier();
200 final Optional<NormalizedNode<?, ?>> childDataAfter = child.getDataAfter();
201 final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
202 LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}", childIdentifier, child
203 .getModificationType(), childDataAfter, child.getDataBefore());
204 final YangInstanceIdentifier childPath = tablePath.node(childIdentifier);
205 switch (child.getModificationType()) {
208 tx.delete(LogicalDatastoreType.OPERATIONAL, childPath);
209 LOG.debug("Route deleted. routeId={}", childPath);
211 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, childPath);
212 CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
217 case SUBTREE_MODIFIED:
218 processModifiedRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
222 writeRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
226 LOG.warn("Ignoring unhandled child {}", child);
232 private void processModifiedRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier, final DOMDataWriteTransaction tx,
233 final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
234 if (TABLE_ROUTES.equals(childIdentifier)) {
235 for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
236 processRoute(tx, ribSupport, policy, childPath, route);
239 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
243 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) {
244 if (TABLE_ROUTES.equals(childIdentifier)) {
245 final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
246 if (!changedRoutes.isEmpty()) {
247 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
248 // Routes are special, as they may end up being filtered. The previous put conveniently
249 // ensured that we have them in at target, so a subsequent delete will not fail :)
250 for (final DataTreeCandidateNode route : changedRoutes) {
251 processRoute(tx, ribSupport, policy, childPath, route);
257 private RIBSupportContext getRibSupport(final NodeIdentifierWithPredicates tableKey) {
258 return this.registry.getRIBSupportContext(tableKey);
261 private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
262 return this.effRibTables.node(tableKey);
265 private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
266 final RIBSupportContext ribSupport = getRibSupport(tableKey);
267 final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
269 processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
272 private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
273 final RIBSupportContext ribSupport = getRibSupport(tableKey);
274 final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
276 // Create an empty table
277 LOG.trace("Create Empty table", tablePath);
278 ribSupport.createEmptyTableStructure(tx, tablePath);
280 processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
284 public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
285 LOG.trace("Data changed called to effective RIB. Change : {}", changes);
287 // we have a lot of transactions created for 'nothing' because a lot of changes
288 // are skipped, so ensure we only create one transaction when we really need it
289 DOMDataWriteTransaction tx = null;
290 for (final DataTreeCandidate tc : changes) {
291 final YangInstanceIdentifier rootPath = tc.getRootPath();
293 final DataTreeCandidateNode root = tc.getRootNode();
294 for (final DataTreeCandidateNode table : root.getChildNodes()) {
296 tx = this.chain.newWriteOnlyTransaction();
298 changeDataTree(tx, rootPath, root, table);
306 private void changeDataTree(final DOMDataWriteTransaction tx, final YangInstanceIdentifier rootPath,
307 final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
308 final PathArgument lastArg = table.getIdentifier();
309 Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
310 final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
311 final RIBSupport ribSupport = getRibSupport(tableKey).getRibSupport();
312 final ModificationType modificationType = root.getModificationType();
313 switch (modificationType) {
316 final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
317 LOG.debug("Delete Effective Table {} modification type {}, ", effectiveTablePath, modificationType);
319 // delete the corresponding effective table
320 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
321 final TablesKey tk = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
322 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tk);
323 CountersUtil.decrement(this.prefixesInstalled.get(tk), tk);
325 case SUBTREE_MODIFIED:
326 modifyTable(tx, tableKey, table);
329 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
333 writeTable(tx, tableKey, table);
336 LOG.warn("Ignoring unhandled root {}", root);
342 public void close() {
344 this.prefixesReceived.values().forEach(LongAdder::reset);
345 this.prefixesInstalled.values().forEach(LongAdder::reset);
349 public long getPrefixedReceivedCount(final TablesKey tablesKey) {
350 final LongAdder counter = this.prefixesReceived.get(tablesKey);
351 if (counter == null) {
354 return counter.longValue();
358 public Set<TablesKey> getTableKeys() {
359 return ImmutableSet.copyOf(this.prefixesReceived.keySet());
363 public boolean isSupported(final TablesKey tablesKey) {
364 return this.prefixesReceived.containsKey(tablesKey);
368 public long getPrefixedInstalledCount(final TablesKey tablesKey) {
369 final LongAdder counter = this.prefixesInstalled.get(tablesKey);
370 if (counter == null) {
373 return counter.longValue();
377 public long getTotalPrefixesInstalled() {
378 return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
382 private final AdjInTracker adjInTracker;
383 private final AbstractImportPolicy importPolicy;
385 static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
386 @Nonnull final YangInstanceIdentifier peerIId, @Nonnull final ImportPolicyPeerTracker importPolicyPeerTracker,
387 @Nonnull final RIBSupportContextRegistry registry, final PeerRole peerRole,
388 @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters, @Nonnull Set<TablesKey> tables) {
389 return new EffectiveRibInWriter(service, chain, peerIId, importPolicyPeerTracker, registry, peerRole,
390 adjRibInRouteCounters, tables);
393 private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
394 final ImportPolicyPeerTracker importPolicyPeerTracker, final RIBSupportContextRegistry registry, final PeerRole peerRole,
395 @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters, @Nonnull Set<TablesKey> tables) {
396 importPolicyPeerTracker.peerRoleChanged(peerIId, peerRole);
397 this.importPolicy = importPolicyPeerTracker.policyFor(IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerIId.getLastPathArgument()));
398 this.adjInTracker = new AdjInTracker(service, registry, chain, peerIId, adjRibInRouteCounters, tables);
402 public void close() {
403 this.adjInTracker.close();
407 public long getPrefixedReceivedCount(final TablesKey tablesKey) {
408 return this.adjInTracker.getPrefixedReceivedCount(tablesKey);
412 public Set<TablesKey> getTableKeys() {
413 return this.adjInTracker.getTableKeys();
417 public boolean isSupported(final TablesKey tablesKey) {
418 return this.adjInTracker.isSupported(tablesKey);
422 public long getPrefixedInstalledCount(@Nonnull final TablesKey tablesKey) {
423 return this.adjInTracker.getPrefixedInstalledCount(tablesKey);
427 public long getTotalPrefixesInstalled() {
428 return this.adjInTracker.getTotalPrefixesInstalled();