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.spi.RIBExtensionConsumerContext;
21 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
22 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;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
27 import org.opendaylight.yangtools.concepts.ListenerRegistration;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
32 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
34 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
35 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * Implementation of the BGP import policy. Listens on all Adj-RIB-In, inspects all inbound
41 * routes in the context of the advertising peer's role and applies the inbound policy.
43 * Inbound policy is applied as follows:
45 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
46 * 2) check if a route is admissible based on attributes attached to it, as well as the
47 * advertising peer's role
48 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
50 * Note that we maintain the peer roles using a DCL, even if we could look up our internal
51 * structures. This is done so we maintain causality and loose coupling.
54 final class EffectiveRibInWriter implements AutoCloseable {
55 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
56 private static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
59 * Maintains {@link TableRouteListener} instances.
61 private final class AdjInTracker implements AutoCloseable, DOMDataTreeChangeListener {
62 private final RIBExtensionConsumerContext registry;
63 private final YangInstanceIdentifier ribId;
64 private final ListenerRegistration<?> reg;
65 private final DOMTransactionChain chain;
67 AdjInTracker(final DOMDataTreeChangeService service, final RIBExtensionConsumerContext registry, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
68 this.registry = Preconditions.checkNotNull(registry);
69 this.chain = Preconditions.checkNotNull(chain);
70 this.ribId = Preconditions.checkNotNull(ribId);
72 final YangInstanceIdentifier tableId = ribId.node(Peer.QNAME).node(AdjRibIn.QNAME).node(Tables.QNAME);
73 final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId);
74 this.reg = service.registerDataTreeChangeListener(treeId, this);
77 private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
78 switch (route.getModificationType()) {
80 // Delete has already been affected by the store in caller, so this is a no-op.
83 LOG.info("Merge on {} reported, this should never have happened, ignoring", route);
88 case SUBTREE_MODIFIED:
90 // Lookup per-table attributes from RIBSupport
91 final ContainerNode adverisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
92 final ContainerNode effectiveAttrs;
94 if (adverisedAttrs != null) {
95 effectiveAttrs = policy.effectiveAttributes(adverisedAttrs);
98 * Speed hack: if we determine that the policy has passed the attributes
99 * back unmodified, the corresponding change has already been written in
100 * our caller. There is no need to perform any further processing.
102 * We also use direct object comparison to make the check very fast, as
103 * it may not be that common, in which case it does not make sense to pay
104 * the full equals price.
106 if (effectiveAttrs == adverisedAttrs) {
110 effectiveAttrs = null;
113 final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
114 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
116 if (effectiveAttrs != null) {
117 tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
119 LOG.warn("Route {} advertised empty attributes", route.getDataAfter());
120 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
124 LOG.warn("Ignoring unhandled route {}", route);
129 private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final NodeIdentifierWithPredicates peerKey, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
130 final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
132 for (DataTreeCandidateNode child : children) {
133 switch (child.getModificationType()) {
135 tx.delete(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()));
138 LOG.info("Merge on {} reported, this should never have happened, ignoring", child);
143 case SUBTREE_MODIFIED:
145 tx.put(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()), child.getDataAfter().get());
147 // Routes are special, as they may end up being filtered. The previous put conveniently
148 // ensured that we have them in at target, so a subsequent delete will not fail :)
149 if (TABLE_ROUTES.equals(child.getIdentifier())) {
150 final YangInstanceIdentifier routesPath = tablePath.node(Routes.QNAME);
151 for (DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
152 processRoute(tx, ribSupport, policy, routesPath, route);
157 LOG.warn("Ignoring unhandled child {}", child);
163 private RIBSupport getRibSupport(final NodeIdentifierWithPredicates tableKey) {
164 // FIXME: use codec to translate tableKey
165 return registry.getRIBSupport(null);
168 private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey) {
169 return ribId.node(peerKey).node(EffectiveRibIn.QNAME).node(tableKey);
172 private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
173 final RIBSupport ribSupport = getRibSupport(tableKey);
174 final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
176 processTableChildren(tx, ribSupport, peerKey, tablePath, table.getChildNodes());
179 private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
180 final RIBSupport ribSupport = getRibSupport(tableKey);
181 final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
183 // Create an empty table
184 TableContext.clearTable(tx, ribSupport, tablePath);
186 processTableChildren(tx, ribSupport, peerKey, tablePath, table.getChildNodes());
190 public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
191 final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
193 for (DataTreeCandidate tc : changes) {
194 final YangInstanceIdentifier rootPath = tc.getRootPath();
196 // Obtain the peer's key
197 final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(rootPath);
199 // Extract the table key, this should be safe based on the path where we subscribed,
200 // but let's verify explicitly.
201 final PathArgument lastArg = rootPath.getLastPathArgument();
202 Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
203 final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
205 final DataTreeCandidateNode root = tc.getRootNode();
206 switch (root.getModificationType()) {
208 // delete the corresponding effective table
209 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath(peerKey, tableKey));
212 // TODO: upstream API should never give us this, as it leaks how the delta was created.
213 LOG.info("Merge on {} reported, this should never have happened, but attempting to cope", rootPath);
214 modifyTable(tx, peerKey, tableKey, root);
216 case SUBTREE_MODIFIED:
217 modifyTable(tx, peerKey, tableKey, root);
220 LOG.info("Ignoring spurious notification on {} data {}", rootPath, root);
223 writeTable(tx, peerKey, tableKey, root);
226 LOG.warn("Ignoring unhandled root {}", root);
235 public void close() {
236 // FIXME: wipe all effective routes?
241 private final ImportPolicyPeerTracker peerPolicyTracker;
242 private final AdjInTracker adjInTracker;
244 private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
245 // FIXME: proper arguments
246 this.peerPolicyTracker = new ImportPolicyPeerTracker(service, ribId, null);
247 this.adjInTracker = new AdjInTracker(service, null, chain, ribId);
251 public void close() {
252 adjInTracker.close();
253 peerPolicyTracker.close();