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.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Verify;
13 import java.util.Collection;
14 import javax.annotation.Nonnull;
15 import javax.annotation.concurrent.NotThreadSafe;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
18 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
19 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
20 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
21 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
22 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
23 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
24 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
25 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
31 import org.opendaylight.yangtools.concepts.ListenerRegistration;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
36 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
39 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
40 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 * Implementation of the BGP import policy. Listens on all Adj-RIB-In, inspects all inbound
47 * routes in the context of the advertising peer's role and applies the inbound policy.
49 * Inbound policy is applied as follows:
51 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
52 * 2) check if a route is admissible based on attributes attached to it, as well as the
53 * advertising peer's role
54 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
56 * Note that we maintain the peer roles using a DCL, even if we could look up our internal
57 * structures. This is done so we maintain causality and loose coupling.
60 final class EffectiveRibInWriter implements AutoCloseable {
61 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
62 protected static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
63 private static final NodeIdentifier ADJRIBIN_NID = new NodeIdentifier(AdjRibIn.QNAME);
64 private static final NodeIdentifier TABLES_NID = new NodeIdentifier(Tables.QNAME);
66 private final class AdjInTracker implements AutoCloseable, DOMDataTreeChangeListener {
67 private final RIBSupportContextRegistry registry;
68 private final YangInstanceIdentifier ribId;
69 private final ListenerRegistration<?> reg;
70 private final DOMTransactionChain chain;
72 AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
73 this.registry = Preconditions.checkNotNull(registry);
74 this.chain = Preconditions.checkNotNull(chain);
75 this.ribId = Preconditions.checkNotNull(ribId);
77 final YangInstanceIdentifier tableId = ribId.node(Peer.QNAME).node(Peer.QNAME);
78 final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId);
79 LOG.debug("Registered Effective RIB on {}", tableId);
80 this.reg = service.registerDataTreeChangeListener(treeId, this);
83 private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
84 LOG.debug("Process route {}", route.getIdentifier());
85 final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
86 switch (route.getModificationType()) {
89 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
95 case SUBTREE_MODIFIED:
97 tx.put(LogicalDatastoreType.OPERATIONAL, routeId, route.getDataAfter().get());
98 // Lookup per-table attributes from RIBSupport
99 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
100 final ContainerNode effectiveAttrs;
102 if (advertisedAttrs != null) {
103 effectiveAttrs = policy.effectiveAttributes(advertisedAttrs);
105 effectiveAttrs = null;
108 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
110 if (effectiveAttrs != null) {
111 tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
113 LOG.warn("Route {} advertised empty attributes", routeId);
114 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
118 LOG.warn("Ignoring unhandled route {}", route);
123 private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final NodeIdentifierWithPredicates peerKey, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
124 final AbstractImportPolicy policy = EffectiveRibInWriter.this.peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
126 for (final DataTreeCandidateNode child : children) {
127 final PathArgument childIdentifier = child.getIdentifier();
128 final Optional<NormalizedNode<?, ?>> childDataAfter = child.getDataAfter();
129 LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}", childIdentifier, child
130 .getModificationType(), childDataAfter, child.getDataBefore());
131 final YangInstanceIdentifier childPath = tablePath.node(childIdentifier);
132 switch (child.getModificationType()) {
135 tx.delete(LogicalDatastoreType.OPERATIONAL, tablePath.node(childIdentifier));
140 case SUBTREE_MODIFIED:
141 processModifiedRouteTables(child, childIdentifier,tx, ribSupport, policy, childPath, childDataAfter);
145 writeRouteTables(child, childIdentifier,tx, ribSupport, policy, childPath, childDataAfter);
149 LOG.warn("Ignoring unhandled child {}", child);
155 private void processModifiedRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier, final DOMDataWriteTransaction tx,
156 final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
157 if (TABLE_ROUTES.equals(childIdentifier)) {
158 for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
159 processRoute(tx, ribSupport, policy, childPath, route);
162 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
166 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) {
167 if (TABLE_ROUTES.equals(childIdentifier)) {
168 final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
169 if (!changedRoutes.isEmpty()) {
170 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
171 // Routes are special, as they may end up being filtered. The previous put conveniently
172 // ensured that we have them in at target, so a subsequent delete will not fail :)
173 for (final DataTreeCandidateNode route : changedRoutes) {
174 processRoute(tx, ribSupport, policy, childPath, route);
180 private RIBSupportContext getRibSupport(final NodeIdentifierWithPredicates tableKey) {
181 return this.registry.getRIBSupportContext(tableKey);
184 private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey) {
185 return this.ribId.node(Peer.QNAME).node(peerKey).node(EffectiveRibIn.QNAME).node(Tables.QNAME).node(tableKey);
188 private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
189 final RIBSupportContext ribSupport = getRibSupport(tableKey);
190 final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
192 processTableChildren(tx, ribSupport.getRibSupport(), peerKey, tablePath, table.getChildNodes());
195 private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
196 final RIBSupportContext ribSupport = getRibSupport(tableKey);
197 final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
199 // Create an empty table
200 LOG.trace("Create Empty table", tablePath);
201 ribSupport.createEmptyTableStructure(tx, tablePath);
203 processTableChildren(tx, ribSupport.getRibSupport(), peerKey, tablePath, table.getChildNodes());
207 public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
208 LOG.trace("Data changed called to effective RIB. Change : {}", changes);
210 // we have a lot of transactions created for 'nothing' because a lot of changes
211 // are skipped, so ensure we only create one transaction when we really need it
212 DOMDataWriteTransaction tx = null;
213 for (final DataTreeCandidate tc : changes) {
214 final YangInstanceIdentifier rootPath = tc.getRootPath();
216 // Obtain the peer's key
217 final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(rootPath);
218 final DataTreeCandidateNode root = tc.getRootNode();
220 //Perform first PeerRoleChange, since it will remove peer from policy if peer session is close
221 peerRoleChange(root, rootPath);
223 if (tc.getRootNode().getModificationType().equals(ModificationType.DELETE)) {
226 // filter out any change outside AdjRibsIn
227 final DataTreeCandidateNode ribIn = root.getModifiedChild(ADJRIBIN_NID);
229 LOG.debug("Skipping change {}", root.getIdentifier());
232 final DataTreeCandidateNode tables = ribIn.getModifiedChild(TABLES_NID);
233 if (tables == null) {
234 LOG.debug("Skipping change {}", root.getIdentifier());
237 for (final DataTreeCandidateNode table : tables.getChildNodes()) {
239 tx = this.chain.newWriteOnlyTransaction();
241 changeDataTree(tx, rootPath, root, peerKey, table);
249 private void changeDataTree(final DOMDataWriteTransaction tx, final YangInstanceIdentifier rootPath,
250 final DataTreeCandidateNode root, final NodeIdentifierWithPredicates peerKey, final DataTreeCandidateNode table) {
251 final PathArgument lastArg = table.getIdentifier();
252 Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
253 final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
254 final ModificationType modificationType = root.getModificationType();
255 switch (modificationType) {
258 final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(peerKey, tableKey);
259 LOG.debug("Delete Effective Table {} modification type {}, ", effectiveTablePath, modificationType);
260 // delete the corresponding effective table
261 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
263 case SUBTREE_MODIFIED:
264 modifyTable(tx, peerKey, tableKey, table);
267 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
271 writeTable(tx, peerKey, tableKey, table);
274 LOG.warn("Ignoring unhandled root {}", root);
280 public void close() {
281 // FIXME: wipe all effective routes?
286 private void peerRoleChange(final DataTreeCandidateNode root, final YangInstanceIdentifier rootPath) {
287 final DataTreeCandidateNode roleChange = root.getModifiedChild(AbstractPeerRoleTracker.PEER_ROLE_NID);
288 if (roleChange != null) {
289 EffectiveRibInWriter.this.peerPolicyTracker.onDataTreeChanged(roleChange, IdentifierUtils.peerPath(rootPath));
293 private final ImportPolicyPeerTracker peerPolicyTracker;
294 private final AdjInTracker adjInTracker;
296 static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
297 @Nonnull final YangInstanceIdentifier ribId, @Nonnull final PolicyDatabase pd, @Nonnull final RIBSupportContextRegistry registry) {
298 return new EffectiveRibInWriter(service, chain, ribId, pd, registry);
301 private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier ribId,
302 final PolicyDatabase pd, final RIBSupportContextRegistry registry) {
303 this.peerPolicyTracker = new ImportPolicyPeerTracker(pd);
304 this.adjInTracker = new AdjInTracker(service, registry, chain, ribId);
308 public void close() {
309 this.adjInTracker.close();