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.Verify;
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableSet;
15 import java.util.Collection;
17 import java.util.Optional;
19 import java.util.concurrent.atomic.LongAdder;
20 import javax.annotation.Nonnull;
21 import javax.annotation.concurrent.NotThreadSafe;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
24 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
25 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
26 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
27 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
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.state.peer.PrefixesInstalledCounters;
31 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
32 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
33 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
34 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
35 import org.opendaylight.protocol.bgp.rib.spi.entry.AttributeBindingCodecSerializer;
36 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
37 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.path.attributes.Attributes;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerRole;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.AdjRibIn;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.EffectiveRibIn;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.tables.Routes;
46 import org.opendaylight.yangtools.concepts.ListenerRegistration;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
51 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
54 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
60 * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
61 * routes in the context of the advertising peer's role and applies the inbound policy.
63 * Inbound policy is applied as follows:
65 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
66 * 2) check if a route is admissible based on attributes attached to it, as well as the
67 * advertising peer's role
68 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
71 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters, AutoCloseable {
72 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
73 static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
75 private final class AdjInTracker implements PrefixesReceivedCounters, PrefixesInstalledCounters, AutoCloseable,
76 ClusteredDOMDataTreeChangeListener {
77 private final RIBSupportContextRegistry registry;
78 private final YangInstanceIdentifier peerIId;
79 private final YangInstanceIdentifier effRibTables;
80 private final ListenerRegistration<?> reg;
81 private final DOMTransactionChain chain;
82 private final Map<TablesKey, LongAdder> prefixesReceived;
83 private final Map<TablesKey, LongAdder> prefixesInstalled;
84 private final BGPRibRoutingPolicy ribPolicies;
85 private final BGPPeerTracker peerTracker;
86 private final AttributeBindingCodecSerializer attBindingCodecSerializer;
87 private final PeerId peerId;
89 AdjInTracker(final RIB rib,
90 final DOMTransactionChain chain,
91 final YangInstanceIdentifier peerIId,
92 @Nonnull final Set<TablesKey> tables) {
93 this.registry = requireNonNull(rib.getRibSupportContext());
94 this.chain = requireNonNull(chain);
95 this.peerIId = requireNonNull(peerIId);
96 this.effRibTables = this.peerIId.node(EffectiveRibIn.QNAME).node(Tables.QNAME);
97 this.prefixesInstalled = buildPrefixesTables(tables);
98 this.prefixesReceived = buildPrefixesTables(tables);
99 this.ribPolicies = requireNonNull(rib.getRibPolicies());
100 this.peerTracker = requireNonNull(rib.getPeerTracker());
101 this.attBindingCodecSerializer = rib;
102 this.peerId = IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerIId.getLastPathArgument());
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 = requireNonNull(rib.getService()).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()));
115 private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport,
116 final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
117 LOG.debug("Process route {}", route.getIdentifier());
118 final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
119 final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
120 switch (route.getModificationType()) {
123 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
124 LOG.debug("Route deleted. routeId={}", routeId);
125 CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
131 case SUBTREE_MODIFIED:
133 final NormalizedNode<?, ?> advRoute = route.getDataAfter().get();
134 tx.put(LogicalDatastoreType.OPERATIONAL, routeId, advRoute);
135 CountersUtil.increment(this.prefixesReceived.get(tablesKey), tablesKey);
136 // Lookup per-table attributes from RIBSupport
138 final NodeIdentifierWithPredicates routeIdentifier = ribSupport
139 .createRouteKeyPathArgument((NodeIdentifierWithPredicates) route.getIdentifier());
140 Optional<Attributes> advertisedAttrs = this.attBindingCodecSerializer
141 .getAttributes(ribSupport, routeIdentifier, advRoute);
143 final Optional<Attributes> effectiveAttrs;
144 if (advertisedAttrs.isPresent()) {
145 final PeerRole peerRole = this.peerTracker.getRole(this.peerId);
146 final BGPRouteEntryImportParameters ribPolicyParameters =
147 new BGPRouteEntryImportParametersImpl(
148 (NodeIdentifierWithPredicates) route.getIdentifier(), this.peerId, peerRole);
149 effectiveAttrs = this.ribPolicies
150 .applyImportPolicies(ribPolicyParameters, advertisedAttrs.get());
151 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs,
155 effectiveAttrs = Optional.empty();
158 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
160 final Optional<ContainerNode> normEffAtt = this.attBindingCodecSerializer
161 .toNormalizedNodeAttribute(ribSupport, routeIdentifier, effectiveAttrs);
162 if (normEffAtt.isPresent()) {
163 tx.put(LogicalDatastoreType.OPERATIONAL,
164 routeId.node(ribSupport.routeAttributesIdentifier()), normEffAtt.get());
165 if (route.getModificationType() == ModificationType.WRITE) {
166 CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey);
169 LOG.warn("Route {} advertised empty attributes", routeId);
170 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
174 LOG.warn("Ignoring unhandled route {}", route);
179 private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
180 for (final DataTreeCandidateNode child : children) {
181 final PathArgument childIdentifier = child.getIdentifier();
182 final Optional<NormalizedNode<?, ?>> childDataAfter = child.getDataAfter();
183 final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
184 LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}", childIdentifier, child
185 .getModificationType(), childDataAfter, child.getDataBefore());
186 final YangInstanceIdentifier childPath = tablePath.node(childIdentifier);
187 switch (child.getModificationType()) {
190 tx.delete(LogicalDatastoreType.OPERATIONAL, childPath);
191 LOG.debug("Route deleted. routeId={}", childPath);
192 CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
197 case SUBTREE_MODIFIED:
198 processModifiedRouteTables(child, childIdentifier, tx, ribSupport, childPath, childDataAfter);
202 writeRouteTables(child, childIdentifier, tx, ribSupport, childPath, childDataAfter);
206 LOG.warn("Ignoring unhandled child {}", child);
212 private void processModifiedRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier, final DOMDataWriteTransaction tx,
213 final RIBSupport ribSupport, final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
214 if (TABLE_ROUTES.equals(childIdentifier)) {
215 for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
216 processRoute(tx, ribSupport, childPath, route);
219 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
223 private void writeRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier,
224 final DOMDataWriteTransaction tx, final RIBSupport ribSupport,
225 final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
226 if (TABLE_ROUTES.equals(childIdentifier)) {
227 final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
228 if (!changedRoutes.isEmpty()) {
229 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
230 // Routes are special, as they may end up being filtered. The previous put conveniently
231 // ensured that we have them in at target, so a subsequent delete will not fail :)
232 for (final DataTreeCandidateNode route : changedRoutes) {
233 processRoute(tx, ribSupport, childPath, route);
239 private RIBSupportContext getRibSupport(final NodeIdentifierWithPredicates tableKey) {
240 return this.registry.getRIBSupportContext(tableKey);
243 private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
244 return this.effRibTables.node(tableKey);
247 private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
248 final RIBSupportContext ribSupport = getRibSupport(tableKey);
249 final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
251 processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
254 private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
255 final RIBSupportContext ribSupport = getRibSupport(tableKey);
256 final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
258 // Create an empty table
259 LOG.trace("Create Empty table", tablePath);
260 ribSupport.createEmptyTableStructure(tx, tablePath);
262 processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
266 public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
267 LOG.trace("Data changed called to effective RIB. Change : {}", changes);
269 // we have a lot of transactions created for 'nothing' because a lot of changes
270 // are skipped, so ensure we only create one transaction when we really need it
271 DOMDataWriteTransaction tx = null;
272 for (final DataTreeCandidate tc : changes) {
273 final YangInstanceIdentifier rootPath = tc.getRootPath();
275 final DataTreeCandidateNode root = tc.getRootNode();
276 for (final DataTreeCandidateNode table : root.getChildNodes()) {
278 tx = this.chain.newWriteOnlyTransaction();
280 changeDataTree(tx, rootPath, root, table);
288 private void changeDataTree(final DOMDataWriteTransaction tx, final YangInstanceIdentifier rootPath,
289 final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
290 final PathArgument lastArg = table.getIdentifier();
291 Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
292 final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
293 final RIBSupport ribSupport = getRibSupport(tableKey).getRibSupport();
294 final ModificationType modificationType = root.getModificationType();
295 switch (modificationType) {
298 final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
299 LOG.debug("Delete Effective Table {} modification type {}, ", effectiveTablePath, modificationType);
301 // delete the corresponding effective table
302 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
303 final TablesKey tk = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
304 CountersUtil.decrement(this.prefixesInstalled.get(tk), tk);
306 case SUBTREE_MODIFIED:
307 modifyTable(tx, tableKey, table);
310 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
314 writeTable(tx, tableKey, table);
317 LOG.warn("Ignoring unhandled root {}", root);
323 public synchronized void close() {
325 this.prefixesReceived.values().forEach(LongAdder::reset);
326 this.prefixesInstalled.values().forEach(LongAdder::reset);
330 public long getPrefixedReceivedCount(final TablesKey tablesKey) {
331 final LongAdder counter = this.prefixesReceived.get(tablesKey);
332 if (counter == null) {
335 return counter.longValue();
339 public Set<TablesKey> getTableKeys() {
340 return ImmutableSet.copyOf(this.prefixesReceived.keySet());
344 public boolean isSupported(final TablesKey tablesKey) {
345 return this.prefixesReceived.containsKey(tablesKey);
349 public long getPrefixedInstalledCount(final TablesKey tablesKey) {
350 final LongAdder counter = this.prefixesInstalled.get(tablesKey);
351 if (counter == null) {
354 return counter.longValue();
358 public long getTotalPrefixesInstalled() {
359 return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
363 private final AdjInTracker adjInTracker;
365 static EffectiveRibInWriter create(@Nonnull final RIB rib,
366 @Nonnull final DOMTransactionChain chain,
367 @Nonnull final YangInstanceIdentifier peerIId,
368 @Nonnull final Set<TablesKey> tables) {
369 return new EffectiveRibInWriter(rib, chain, peerIId, tables);
372 private EffectiveRibInWriter(final RIB rib, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
373 @Nonnull final Set<TablesKey> tables) {
374 this.adjInTracker = new AdjInTracker(rib, chain, peerIId, tables);
378 public void close() {
379 this.adjInTracker.close();
383 public long getPrefixedReceivedCount(final TablesKey tablesKey) {
384 return this.adjInTracker.getPrefixedReceivedCount(tablesKey);
388 public Set<TablesKey> getTableKeys() {
389 return this.adjInTracker.getTableKeys();
393 public boolean isSupported(final TablesKey tablesKey) {
394 return this.adjInTracker.isSupported(tablesKey);
398 public long getPrefixedInstalledCount(@Nonnull final TablesKey tablesKey) {
399 return this.adjInTracker.getPrefixedInstalledCount(tablesKey);
403 public long getTotalPrefixesInstalled() {
404 return this.adjInTracker.getTotalPrefixesInstalled();