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.concurrent.NotThreadSafe;
14 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
15 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
16 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
17 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
18 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
19 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
20 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
21 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
22 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.graceful.restart._case.graceful.restart.capability.Tables;
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.Routes;
28 import org.opendaylight.yangtools.concepts.ListenerRegistration;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
33 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
35 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
36 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * Implementation of the BGP import policy. Listens on all Adj-RIB-In, inspects all inbound
42 * routes in the context of the advertising peer's role and applies the inbound policy.
44 * Inbound policy is applied as follows:
46 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
47 * 2) check if a route is admissible based on attributes attached to it, as well as the
48 * advertising peer's role
49 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
51 * Note that we maintain the peer roles using a DCL, even if we could look up our internal
52 * structures. This is done so we maintain causality and loose coupling.
55 final class EffectiveRibInWriter implements AutoCloseable {
56 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
57 private static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
60 * Maintains {@link TableRouteListener} instances.
62 private final class AdjInTracker implements AutoCloseable, DOMDataTreeChangeListener {
63 private final RIBSupportContextRegistry registry;
64 private final YangInstanceIdentifier ribId;
65 private final ListenerRegistration<?> reg;
66 private final DOMTransactionChain chain;
68 AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
69 this.registry = Preconditions.checkNotNull(registry);
70 this.chain = Preconditions.checkNotNull(chain);
71 this.ribId = Preconditions.checkNotNull(ribId);
73 final YangInstanceIdentifier tableId = ribId.node(Peer.QNAME).node(AdjRibIn.QNAME).node(Tables.QNAME);
74 final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId);
75 this.reg = service.registerDataTreeChangeListener(treeId, this);
78 private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
79 switch (route.getModificationType()) {
81 // Delete has already been affected by the store in caller, so this is a no-op.
84 LOG.info("Merge on {} reported, this should never have happened, ignoring", route);
89 case SUBTREE_MODIFIED:
91 // Lookup per-table attributes from RIBSupport
92 final ContainerNode adverisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
93 final ContainerNode effectiveAttrs;
95 if (adverisedAttrs != null) {
96 effectiveAttrs = policy.effectiveAttributes(adverisedAttrs);
99 * Speed hack: if we determine that the policy has passed the attributes
100 * back unmodified, the corresponding change has already been written in
101 * our caller. There is no need to perform any further processing.
103 * We also use direct object comparison to make the check very fast, as
104 * it may not be that common, in which case it does not make sense to pay
105 * the full equals price.
107 if (effectiveAttrs == adverisedAttrs) {
111 effectiveAttrs = null;
114 final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
115 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
117 if (effectiveAttrs != null) {
118 tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
120 LOG.warn("Route {} advertised empty attributes", route.getDataAfter());
121 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
125 LOG.warn("Ignoring unhandled route {}", route);
130 private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final NodeIdentifierWithPredicates peerKey, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
131 final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
133 for (DataTreeCandidateNode child : children) {
134 switch (child.getModificationType()) {
136 tx.delete(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()));
139 LOG.info("Merge on {} reported, this should never have happened, ignoring", child);
144 case SUBTREE_MODIFIED:
146 tx.put(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()), child.getDataAfter().get());
148 // Routes are special, as they may end up being filtered. The previous put conveniently
149 // ensured that we have them in at target, so a subsequent delete will not fail :)
150 if (TABLE_ROUTES.equals(child.getIdentifier())) {
151 final YangInstanceIdentifier routesPath = tablePath.node(Routes.QNAME);
152 for (DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
153 processRoute(tx, ribSupport, policy, routesPath, route);
158 LOG.warn("Ignoring unhandled child {}", child);
164 private RIBSupportContext getRibSupport(final NodeIdentifierWithPredicates tableKey) {
165 // FIXME: use codec to translate tableKey
166 return registry.getRIBSupportContext(null);
169 private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey) {
170 return ribId.node(peerKey).node(EffectiveRibIn.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 final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
194 for (DataTreeCandidate tc : changes) {
195 final YangInstanceIdentifier rootPath = tc.getRootPath();
197 // Obtain the peer's key
198 final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(rootPath);
200 // Extract the table key, this should be safe based on the path where we subscribed,
201 // but let's verify explicitly.
202 final PathArgument lastArg = rootPath.getLastPathArgument();
203 Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
204 final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
206 final DataTreeCandidateNode root = tc.getRootNode();
207 switch (root.getModificationType()) {
209 // delete the corresponding effective table
210 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath(peerKey, tableKey));
213 // TODO: upstream API should never give us this, as it leaks how the delta was created.
214 LOG.info("Merge on {} reported, this should never have happened, but attempting to cope", rootPath);
215 modifyTable(tx, peerKey, tableKey, root);
217 case SUBTREE_MODIFIED:
218 modifyTable(tx, peerKey, tableKey, root);
221 LOG.info("Ignoring spurious notification on {} data {}", rootPath, root);
224 writeTable(tx, peerKey, tableKey, root);
227 LOG.warn("Ignoring unhandled root {}", root);
236 public void close() {
237 // FIXME: wipe all effective routes?
242 private final ImportPolicyPeerTracker peerPolicyTracker;
243 private final AdjInTracker adjInTracker;
245 private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
246 // FIXME: proper arguments
247 this.peerPolicyTracker = new ImportPolicyPeerTracker(service, ribId, null);
248 this.adjInTracker = new AdjInTracker(service, null, chain, ribId);
252 public void close() {
253 adjInTracker.close();
254 peerPolicyTracker.close();