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.Preconditions;
11 import com.google.common.base.Verify;
12 import java.util.Collection;
13 import javax.annotation.Nonnull;
14 import javax.annotation.concurrent.NotThreadSafe;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
17 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
18 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
19 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
20 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
21 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
22 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
23 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
29 import org.opendaylight.yangtools.concepts.ListenerRegistration;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
34 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
36 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
37 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
42 * Implementation of the BGP import policy. Listens on all Adj-RIB-In, inspects all inbound
43 * routes in the context of the advertising peer's role and applies the inbound policy.
45 * Inbound policy is applied as follows:
47 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
48 * 2) check if a route is admissible based on attributes attached to it, as well as the
49 * advertising peer's role
50 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
52 * Note that we maintain the peer roles using a DCL, even if we could look up our internal
53 * structures. This is done so we maintain causality and loose coupling.
56 final class EffectiveRibInWriter implements AutoCloseable {
57 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
58 private static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
59 private static final NodeIdentifier ADJRIBIN_NID = new NodeIdentifier(AdjRibIn.QNAME);
60 private static final NodeIdentifier TABLES_NID = new NodeIdentifier(Tables.QNAME);
63 * Maintains {@link TableRouteListener} instances.
65 private final class AdjInTracker implements AutoCloseable, DOMDataTreeChangeListener {
66 private final RIBSupportContextRegistry registry;
67 private final YangInstanceIdentifier ribId;
68 private final ListenerRegistration<?> reg;
69 private final DOMTransactionChain chain;
71 AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
72 this.registry = Preconditions.checkNotNull(registry);
73 this.chain = Preconditions.checkNotNull(chain);
74 this.ribId = Preconditions.checkNotNull(ribId);
76 final YangInstanceIdentifier tableId = ribId.node(Peer.QNAME).node(Peer.QNAME);
77 final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId);
78 LOG.debug("Registered Effective RIB on {}", tableId);
79 this.reg = service.registerDataTreeChangeListener(treeId, this);
82 private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
83 LOG.debug("Process route {}", route);
84 switch (route.getModificationType()) {
86 // Delete has already been affected by the store in caller, so this is a no-op.
91 case SUBTREE_MODIFIED:
93 // Lookup per-table attributes from RIBSupport
94 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
95 final ContainerNode effectiveAttrs;
97 if (advertisedAttrs != null) {
98 effectiveAttrs = policy.effectiveAttributes(advertisedAttrs);
101 * Speed hack: if we determine that the policy has passed the attributes
102 * back unmodified, the corresponding change has already been written in
103 * our caller. There is no need to perform any further processing.
105 * We also use direct object comparison to make the check very fast, as
106 * it may not be that common, in which case it does not make sense to pay
107 * the full equals price.
109 if (effectiveAttrs == advertisedAttrs) {
110 LOG.trace("Effective and local attributes are equal. Quit processing route {}", route);
114 effectiveAttrs = null;
117 final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
118 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
120 if (effectiveAttrs != null) {
121 tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
123 LOG.warn("Route {} advertised empty attributes", routeId);
124 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
128 LOG.warn("Ignoring unhandled route {}", route);
133 private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final NodeIdentifierWithPredicates peerKey, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
134 final AbstractImportPolicy policy = EffectiveRibInWriter.this.peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
136 for (final DataTreeCandidateNode child : children) {
137 LOG.debug("Process table children {}", child);
138 switch (child.getModificationType()) {
140 tx.delete(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()));
145 case SUBTREE_MODIFIED:
147 tx.put(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()), child.getDataAfter().get());
149 // Routes are special, as they may end up being filtered. The previous put conveniently
150 // ensured that we have them in at target, so a subsequent delete will not fail :)
151 if (TABLE_ROUTES.equals(child.getIdentifier())) {
152 final YangInstanceIdentifier routesPath = tablePath.node(Routes.QNAME);
153 for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
154 processRoute(tx, ribSupport, policy, routesPath, route);
159 LOG.warn("Ignoring unhandled child {}", child);
165 private RIBSupportContext getRibSupport(final NodeIdentifierWithPredicates tableKey) {
166 return this.registry.getRIBSupportContext(tableKey);
169 private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey) {
170 return this.ribId.node(Peer.QNAME).node(peerKey).node(EffectiveRibIn.QNAME).node(Tables.QNAME).node(tableKey);
173 private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
174 final RIBSupportContext ribSupport = getRibSupport(tableKey);
175 final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
177 processTableChildren(tx, ribSupport.getRibSupport(), peerKey, tablePath, table.getChildNodes());
180 private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
181 final RIBSupportContext ribSupport = getRibSupport(tableKey);
182 final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
184 // Create an empty table
185 ribSupport.clearTable(tx,tablePath);
187 processTableChildren(tx, ribSupport.getRibSupport(), peerKey, tablePath, table.getChildNodes());
191 public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
192 LOG.trace("Data changed called to effective RIB. Change : {}", changes);
193 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
195 for (final DataTreeCandidate tc : changes) {
196 final YangInstanceIdentifier rootPath = tc.getRootPath();
198 // Obtain the peer's key
199 final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(rootPath);
200 final DataTreeCandidateNode root = tc.getRootNode();
202 // call out peer-role has changed
203 final DataTreeCandidateNode roleChange = root.getModifiedChild(AbstractPeerRoleTracker.PEER_ROLE_NID);
204 if (roleChange != null) {
205 EffectiveRibInWriter.this.peerPolicyTracker.onDataTreeChanged(roleChange, IdentifierUtils.peerPath(rootPath));
208 // filter out any change outside AdjRibsIn
209 final DataTreeCandidateNode ribIn = root.getModifiedChild(ADJRIBIN_NID);
211 LOG.debug("Skipping change {}", tc.getRootNode());
214 final DataTreeCandidateNode tables = ribIn.getModifiedChild(TABLES_NID);
215 if (tables == null) {
216 LOG.debug("Skipping change {}", tc.getRootNode());
219 for (final DataTreeCandidateNode table : tables.getChildNodes()) {
220 final PathArgument lastArg = table.getIdentifier();
221 Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
222 final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
224 switch (root.getModificationType()) {
226 // delete the corresponding effective table
227 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath(peerKey, tableKey));
230 // TODO: upstream API should never give us this, as it leaks how the delta was created.
231 LOG.info("Merge on {} reported, this should never have happened, but attempting to cope", rootPath);
232 modifyTable(tx, peerKey, tableKey, table);
234 case SUBTREE_MODIFIED:
235 modifyTable(tx, peerKey, tableKey, table);
238 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
241 writeTable(tx, peerKey, tableKey, table);
244 LOG.warn("Ignoring unhandled root {}", root);
253 public void close() {
254 // FIXME: wipe all effective routes?
259 private final ImportPolicyPeerTracker peerPolicyTracker;
260 private final AdjInTracker adjInTracker;
262 static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
263 @Nonnull final YangInstanceIdentifier ribId, @Nonnull final PolicyDatabase pd, @Nonnull final RIBSupportContextRegistry registry) {
264 return new EffectiveRibInWriter(service, chain, ribId, pd, registry);
267 private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier ribId,
268 final PolicyDatabase pd, final RIBSupportContextRegistry registry) {
269 this.peerPolicyTracker = new ImportPolicyPeerTracker(pd);
270 this.adjInTracker = new AdjInTracker(service, registry, chain, ribId);
274 public void close() {
275 this.adjInTracker.close();