import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
private static final Logger LOG = LoggerFactory.getLogger(AbstractIPRIBSupport.class);
private static final NodeIdentifier ROUTES = new NodeIdentifier(Routes.QNAME);
- protected AbstractIPRIBSupport() {
-
+ protected AbstractIPRIBSupport(final QName routesContainer) {
+ super(routesContainer);
}
protected abstract NodeIdentifier routeIdentifier();
- protected abstract NodeIdentifier routesIdentifier();
@Override
public final Collection<Class<? extends DataObject>> cacheableAttributeObjects() {
abstract void apply(DOMDataWriteTransaction tx, YangInstanceIdentifier base, MapEntryNode route, final ContainerNode attributes);
}
- private final void processDestination(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId,
+ private final void processDestination(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath,
final ContainerNode destination, final ContainerNode attributes, final ApplyRoute function) {
if (destination != null) {
final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = destination.getChild(routeIdentifier());
if (maybeRoutes.isPresent()) {
final DataContainerChild<? extends PathArgument, ?> routes = maybeRoutes.get();
if (routes instanceof MapNode) {
- final YangInstanceIdentifier base = tableId.node(ROUTES).node(routesIdentifier());
+ final YangInstanceIdentifier base = tablePath.node(ROUTES).node(routesContainerIdentifier());
for (MapEntryNode e : ((MapNode)routes).getValue()) {
function.apply(tx, base, e, attributes);
}
}
@Override
- protected void putDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode destination, final ContainerNode attributes) {
- processDestination(tx, tableId, destination, attributes, ApplyRoute.PUT);
+ protected void putDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode destination, final ContainerNode attributes) {
+ processDestination(tx, tablePath, destination, attributes, ApplyRoute.PUT);
}
@Override
- protected void deleteDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode destination) {
- processDestination(tx, tableId, destination, null, ApplyRoute.DELETE);
+ protected void deleteDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode destination) {
+ processDestination(tx, tablePath, destination, null, ApplyRoute.DELETE);
}
}
package org.opendaylight.protocol.bgp.rib.impl;
import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
import javax.annotation.concurrent.NotThreadSafe;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
+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;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
*/
@NotThreadSafe
final class EffectiveRibInWriter implements AutoCloseable {
-
private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
+ private static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
/**
- * Maintains the individual routes for a particular table's routes under:
- * /bgp-rib/rib/peer/adj-rib-in/tables/routes
+ * Maintains {@link TableRouteListener} instances.
*/
- private final class TableRouteListener implements DOMDataTreeChangeListener {
- private final NodeIdentifierWithPredicates tableKey;
- private final YangInstanceIdentifier target;
- private final RIBSupport ribSupport;
- private final PeerId peerId;
-
- TableRouteListener(final RIBSupport ribSupport, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey) {
- this.ribSupport = Preconditions.checkNotNull(ribSupport);
- this.tableKey = Preconditions.checkNotNull(tableKey);
-
- // Lookup peer ID
- this.peerId = IdentifierUtils.peerId(peerKey);
-
- // FIXME: need target table ID
- target = null;
- }
-
- private void updateRoutes(final DOMDataWriteTransaction tx, final DataTreeCandidateNode routes, final ContainerNode effectiveAttrs) {
- final YangInstanceIdentifier routeId = target.node(routes.getIdentifier());
+ private final class AdjInTracker implements AutoCloseable, DOMDataTreeChangeListener {
+ private final RIBExtensionConsumerContext registry;
+ private final YangInstanceIdentifier ribId;
+ private final ListenerRegistration<?> reg;
+ private final DOMTransactionChain chain;
- if (effectiveAttrs != null) {
- tx.put(LogicalDatastoreType.OPERATIONAL, routeId, routes.getDataAfter().get());
- tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributes()), effectiveAttrs);
- } else if (routes.getDataBefore().isPresent()) {
- tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
- }
+ AdjInTracker(final DOMDataTreeChangeService service, final RIBExtensionConsumerContext registry, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
+ this.registry = Preconditions.checkNotNull(registry);
+ this.chain = Preconditions.checkNotNull(chain);
+ this.ribId = Preconditions.checkNotNull(ribId);
+ final YangInstanceIdentifier tableId = ribId.node(Peer.QNAME).node(AdjRibIn.QNAME).node(Tables.QNAME);
+ final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId);
+ this.reg = service.registerDataTreeChangeListener(treeId, this);
}
- @Override
- public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
- // FIXME: note that we need to detect table clears efficiently and propagate them
-
- final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
-
- for (DataTreeCandidate tc : changes) {
+ private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
+ switch (route.getModificationType()) {
+ case DELETE:
+ // Delete has already been affected by the store in caller, so this is a no-op.
+ break;
+ case MERGE:
+ LOG.info("Merge on {} reported, this should never have happened, ignoring", route);
+ break;
+ case UNMODIFIED:
+ // No-op
+ break;
+ case SUBTREE_MODIFIED:
+ case WRITE:
// Lookup per-table attributes from RIBSupport
- final ContainerNode adverisedAttrs = (ContainerNode) NormalizedNodes.findNode(tc.getRootNode().getDataAfter(), ribSupport.routeAttributes()).orNull();
+ final ContainerNode adverisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
final ContainerNode effectiveAttrs;
- if (adverisedAttrs != null && tc.getRootNode().getDataAfter().isPresent()) {
- final AbstractImportPolicy policy = peerPolicyTracker.policyFor(peerId);
+ if (adverisedAttrs != null) {
effectiveAttrs = policy.effectiveAttributes(adverisedAttrs);
+
+ /*
+ * Speed hack: if we determine that the policy has passed the attributes
+ * back unmodified, the corresponding change has already been written in
+ * our caller. There is no need to perform any further processing.
+ *
+ * We also use direct object comparison to make the check very fast, as
+ * it may not be that common, in which case it does not make sense to pay
+ * the full equals price.
+ */
+ if (effectiveAttrs == adverisedAttrs) {
+ return;
+ }
} else {
effectiveAttrs = null;
}
- LOG.debug("Route change {} effective attributes {}", tc.getRootPath(), effectiveAttrs);
+ final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
+ LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
- updateRoutes(tx, tc.getRootNode(), effectiveAttrs);
+ if (effectiveAttrs != null) {
+ tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
+ } else {
+ LOG.warn("Route {} advertised empty attributes", route.getDataAfter());
+ tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
+ }
+ break;
+ default:
+ LOG.warn("Ignoring unhandled route {}", route);
+ break;
}
+ }
- tx.submit();
+ private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
+ for (DataTreeCandidateNode child : children) {
+ switch (child.getModificationType()) {
+ case DELETE:
+ tx.delete(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()));
+ break;
+ case MERGE:
+ LOG.info("Merge on {} reported, this should never have happened, ignoring", child);
+ break;
+ case UNMODIFIED:
+ // No-op
+ break;
+ case SUBTREE_MODIFIED:
+ case WRITE:
+ tx.put(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()), child.getDataAfter().get());
+
+ // Routes are special, as they may end up being filtered. The previous put conveniently
+ // ensured that we have them in at target, so a subsequent delete will not fail :)
+ if (TABLE_ROUTES.equals(child.getIdentifier())) {
+ final YangInstanceIdentifier routesPath = tablePath.node(Routes.QNAME);
+ for (DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
+ processRoute(tx, ribSupport, policy, routesPath, route);
+ }
+ }
+ break;
+ default:
+ LOG.warn("Ignoring unhandled child {}", child);
+ break;
+ }
+ }
}
- }
- /**
- * Maintains {@link TableRouteListener} instances.
- */
- private final class TableListener implements DOMDataTreeChangeListener {
- private final Map<YangInstanceIdentifier, ListenerRegistration<?>> routeListeners = new HashMap<>();
- private final RIBExtensionConsumerContext registry;
- private final DOMDataTreeChangeService service;
+ private RIBSupport getRibSupport(final NodeIdentifierWithPredicates tableKey) {
+ // FIXME: use codec to translate tableKey
+ return registry.getRIBSupport(null);
+ }
- TableListener(final DOMDataTreeChangeService service, final RIBExtensionConsumerContext registry) {
- this.registry = Preconditions.checkNotNull(registry);
- this.service = Preconditions.checkNotNull(service);
+ private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey) {
+ return ribId.node(peerKey).node(EffectiveRibIn.QNAME).node(tableKey);
+ }
+
+ private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
+ final RIBSupport ribSupport = getRibSupport(tableKey);
+ final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
+
+ final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
+ processTableChildren(tx, ribSupport, policy, tablePath, table.getChildNodes());
+ }
+
+ private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
+ final RIBSupport ribSupport = getRibSupport(tableKey);
+ final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
+
+ // Create an empty table
+ TableContext.clearTable(tx, ribSupport, tablePath);
+
+ final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
+ processTableChildren(tx, ribSupport, policy, tablePath, table.getChildNodes());
}
@Override
public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
+ final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
for (DataTreeCandidate tc : changes) {
+ final YangInstanceIdentifier rootPath = tc.getRootPath();
+
// Obtain the peer's key
- final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(tc.getRootPath());
+ final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(rootPath);
- // Lookup
- final NodeIdentifierWithPredicates tableKey = IdentifierUtils.tableKey(tc.getRootPath());
+ // Extract the table key, this should be safe based on the path where we subscribed,
+ // but let's verify explicitly.
+ final PathArgument lastArg = rootPath.getLastPathArgument();
+ Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
+ final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
- switch (tc.getRootNode().getModificationType()) {
+ final DataTreeCandidateNode root = tc.getRootNode();
+ switch (root.getModificationType()) {
case DELETE:
- final ListenerRegistration<?> reg = routeListeners.remove(tc.getRootPath());
- if (reg != null) {
- reg.close();
- }
+ // delete the corresponding effective table
+ tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath(peerKey, tableKey));
+ break;
+ case MERGE:
+ // TODO: upstream API should never give us this, as it leaks how the delta was created.
+ LOG.info("Merge on {} reported, this should never have happened, but attempting to cope", rootPath);
+ modifyTable(tx, peerKey, tableKey, root);
+ break;
+ case SUBTREE_MODIFIED:
+ modifyTable(tx, peerKey, tableKey, root);
+ break;
+ case UNMODIFIED:
+ LOG.info("Ignoring spurious notification on {} data {}", rootPath, root);
break;
case WRITE:
- // FIXME: use codec to translate
- final RIBSupport ribSupport = registry.getRIBSupport(null);
- if (ribSupport != null) {
- final TableRouteListener routeListener = new TableRouteListener(ribSupport, peerKey, tableKey);
- final ListenerRegistration<?> r = service.registerDataTreeChangeListener(
- new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tc.getRootPath()), routeListener);
-
- routeListeners.put(tc.getRootPath(), r);
- } else {
- LOG.warn("No RIB support for table {}, ignoring advertisements from peer %s", tableKey, peerKey);
- }
+ writeTable(tx, peerKey, tableKey, root);
break;
default:
- // No-op
+ LOG.warn("Ignoring unhandled root {}", root);
break;
}
}
+
+ tx.submit();
+ }
+
+ @Override
+ public void close() {
+ // FIXME: wipe all effective routes?
+ reg.close();
}
}
private final ImportPolicyPeerTracker peerPolicyTracker;
- private final DOMTransactionChain chain;
+ private final AdjInTracker adjInTracker;
private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
- this.chain = Preconditions.checkNotNull(chain);
-
this.peerPolicyTracker = new ImportPolicyPeerTracker(service, ribId);
-
- // FIXME: subscribe tableListener
+ // FIXME: proper argument
+ this.adjInTracker = new AdjInTracker(service, null, chain, ribId);
}
@Override
public void close() {
+ adjInTracker.close();
peerPolicyTracker.close();
}
}
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.ipv4.routes._case.Ipv4Routes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.ipv4.routes._case.ipv4.routes.Ipv4Route;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.route.Attributes;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+/**
+ * Class supporting IPv4 unicast RIBs.
+ */
final class IPv4RIBSupport extends AbstractIPRIBSupport {
private static final IPv4RIBSupport SINGLETON = new IPv4RIBSupport();
private final ChoiceNode emptyRoutes = Builders.choiceBuilder()
.withNodeIdentifier(new NodeIdentifier(Ipv4Routes.QNAME))
.withChild(ImmutableNodes.mapNodeBuilder(Ipv4Route.QNAME).build()).build()).build();
private final NodeIdentifier destination = new NodeIdentifier(DestinationIpv4Case.QNAME);
- private final NodeIdentifier routes = new NodeIdentifier(Ipv4Routes.QNAME);
private final NodeIdentifier route = new NodeIdentifier(Ipv4Route.QNAME);
- private final NodeIdentifier attributes = new NodeIdentifier(QName.create(Ipv4Route.QNAME, Attributes.QNAME.getLocalName()));
-
- private static final QName PREFIX_QNAME = QName.create(Ipv4Route.QNAME, "prefix");
private IPv4RIBSupport() {
-
+ super(Ipv4Routes.QNAME);
}
static IPv4RIBSupport getInstance() {
return emptyRoutes;
}
- @Override
- public NodeIdentifier routeAttributes() {
- return attributes;
- }
-
@Override
protected NodeIdentifier destinationIdentifier() {
return destination;
protected NodeIdentifier routeIdentifier() {
return route;
}
-
- @Override
- protected NodeIdentifier routesIdentifier() {
- return routes;
- }
}
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.ipv6.routes._case.Ipv6Routes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.ipv6.routes._case.ipv6.routes.Ipv6Route;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.route.Attributes;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+/**
+ * Class supporting IPv6 unicast RIBs.
+ */
final class IPv6RIBSupport extends AbstractIPRIBSupport {
private static final IPv6RIBSupport SINGLETON = new IPv6RIBSupport();
private final ChoiceNode emptyRoutes = Builders.choiceBuilder()
.withNodeIdentifier(new NodeIdentifier(Ipv6Routes.QNAME))
.withChild(ImmutableNodes.mapNodeBuilder(Ipv6Route.QNAME).build()).build()).build();
private final NodeIdentifier destination = new NodeIdentifier(DestinationIpv6Case.QNAME);
- private final NodeIdentifier routes = new NodeIdentifier(Ipv6Routes.QNAME);
private final NodeIdentifier route = new NodeIdentifier(Ipv6Route.QNAME);
- private final NodeIdentifier attributes = new NodeIdentifier(QName.create(Ipv6Route.QNAME, Attributes.QNAME.getLocalName()));
private IPv6RIBSupport() {
-
+ super(Ipv6Routes.QNAME);
}
static IPv6RIBSupport getInstance() {
return emptyRoutes;
}
- @Override
- public NodeIdentifier routeAttributes() {
- return attributes;
- }
-
@Override
protected NodeIdentifier destinationIdentifier() {
return destination;
protected NodeIdentifier routeIdentifier() {
return route;
}
-
- @Override
- protected NodeIdentifier routesIdentifier() {
- return routes;
- }
}
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
private final YangInstanceIdentifier target;
private final DOMTransactionChain chain;
private final ExportPolicyPeerTracker peerPolicyTracker;
- private final RIBSupport ribSupport;
+ private final NodeIdentifier attributesIdentifier;
private final Long ourAs;
LocRibWriter(final RIBSupport ribSupport, final DOMTransactionChain chain, final YangInstanceIdentifier target, final Long ourAs) {
this.chain = Preconditions.checkNotNull(chain);
this.target = Preconditions.checkNotNull(target);
this.ourAs = Preconditions.checkNotNull(ourAs);
- this.ribSupport = Preconditions.checkNotNull(ribSupport);
+ this.attributesIdentifier = ribSupport.routeAttributesIdentifier();
// FIXME: proper values
this.peerPolicyTracker = new ExportPolicyPeerTracker(null, null);
if (effectiveAttributes != null && value != null && !peerId.equals(pid.getKey())) {
tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
- tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(ribSupport.routeAttributes()), effectiveAttributes);
+ tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(attributesIdentifier), effectiveAttributes);
} else {
tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
}
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
+import java.util.Collection;
+import java.util.Collections;
+import javax.annotation.Nonnull;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.DestinationType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.reach.nlri.AdvertizedRoutes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.unreach.nlri.WithdrawnRoutes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.route.Attributes;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final NodeIdentifier ADVERTIZED_ROUTES = new NodeIdentifier(AdvertizedRoutes.QNAME);
private static final NodeIdentifier WITHDRAWN_ROUTES = new NodeIdentifier(WithdrawnRoutes.QNAME);
private static final NodeIdentifier DESTINATION_TYPE = new NodeIdentifier(DestinationType.QNAME);
+ private final NodeIdentifier routesContainerIdentifier;
+ private final NodeIdentifier routeAttributesIdentifier;
- protected AbstractRIBSupport() {
+ /**
+ * Default constructor. Requires the QName of the container augmented under the routes choice
+ * node in instantiations of the rib grouping. It is assumed that this container is defined by
+ * the same model which populates it with route grouping instantiation, and by extension with
+ * the route attributes container.
+ *
+ * @param routesContainer QName of the container in routes choice, must not be null.
+ */
+ protected AbstractRIBSupport(final @Nonnull QName routesContainer) {
+ this.routesContainerIdentifier = new NodeIdentifier(routesContainer);
+ this.routeAttributesIdentifier = new NodeIdentifier(QName.cachedReference(QName.create(routesContainer, Attributes.QNAME.getLocalName())));
+ }
+ /**
+ * Return the {@link NodeIdentifier} of the AFI/SAFI-specific container under
+ * the RIB routes.
+ *
+ * @return Container identifier, may not be null.
+ */
+ protected final NodeIdentifier routesContainerIdentifier() {
+ return routesContainerIdentifier;
}
protected abstract NodeIdentifier destinationIdentifier();
- protected abstract void deleteDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tableId, ContainerNode destination);
- protected abstract void putDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tableId, ContainerNode destination, ContainerNode attributes);
+ protected abstract void deleteDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tablePath, ContainerNode destination);
+ protected abstract void putDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tablePath, ContainerNode destination, ContainerNode attributes);
private static ContainerNode getDestination(final DataContainerChild<? extends PathArgument, ?> routes, final NodeIdentifier destinationId) {
if (routes instanceof ContainerNode) {
}
@Override
- public final void deleteRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode nlri) {
+ public final NodeIdentifier routeAttributesIdentifier() {
+ return routeAttributesIdentifier;
+ }
+
+ @Override
+ public final Collection<DataTreeCandidateNode> changedRoutes(final DataTreeCandidateNode routes) {
+ final DataTreeCandidateNode myRoutes = routes.getModifiedChild(routesContainerIdentifier);
+ if (myRoutes == null) {
+ return Collections.emptySet();
+ }
+
+ // Well, given the remote possibility of augmentation, we should perform a filter here,
+ // to make sure the type matches what routeType() reports.
+ return myRoutes.getChildNodes();
+ }
+
+ @Override
+ public final YangInstanceIdentifier routePath(final YangInstanceIdentifier routesPath, final PathArgument routeId) {
+ return routesPath.node(routesContainerIdentifier).node(routeId);
+ }
+
+ @Override
+ public final void deleteRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode nlri) {
final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = nlri.getChild(WITHDRAWN_ROUTES);
if (maybeRoutes.isPresent()) {
final ContainerNode destination = getDestination(maybeRoutes.get(), destinationIdentifier());
if (destination != null) {
- deleteDestinationRoutes(tx, tableId, destination);
+ deleteDestinationRoutes(tx, tablePath, destination);
}
} else {
LOG.debug("Withdrawn routes are not present in NLRI {}", nlri);
}
@Override
- public final void putRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode nlri, final ContainerNode attributes) {
+ public final void putRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode nlri, final ContainerNode attributes) {
final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = nlri.getChild(ADVERTIZED_ROUTES);
if (maybeRoutes.isPresent()) {
final ContainerNode destination = getDestination(maybeRoutes.get(), destinationIdentifier());
if (destination != null) {
- putDestinationRoutes(tx, tableId, destination, attributes);
+ putDestinationRoutes(tx, tablePath, destination, attributes);
}
} else {
LOG.debug("Advertized routes are not present in NLRI {}", nlri);
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+/**
+ * Interface implemented for AFI/SAFI-specific RIB extensions. The extensions need
+ * to register an implementation of this class and the RIB core then calls into it
+ * to inquire about details specific to that particular model.
+ */
public interface RIBSupport {
/**
* Return the table-type-specific empty routes container, as augmented into the
* @return Protocol-specific case in the routes choice, may not be null.
*/
@Nonnull ChoiceNode emptyRoutes();
- @Nonnull NodeIdentifier routeAttributes();
+
+ /**
+ * Return the localized identifier of the attributes route member, as expanded
+ * from the route grouping in the specific augmentation of the base routes choice.
+ *
+ * @return The attributes identifier, may not be null.
+ */
+ @Nonnull NodeIdentifier routeAttributesIdentifier();
@Nonnull Collection<Class<? extends DataObject>> cacheableAttributeObjects();
@Nonnull Collection<Class<? extends DataObject>> cacheableNlriObjects();
- void deleteRoutes(@Nonnull DOMDataWriteTransaction tx, @Nonnull YangInstanceIdentifier tableId, @Nonnull ContainerNode nlri);
- void putRoutes(@Nonnull DOMDataWriteTransaction tx, @Nonnull YangInstanceIdentifier tableId, @Nonnull ContainerNode nlri, @Nonnull ContainerNode attributes);
+ void deleteRoutes(@Nonnull DOMDataWriteTransaction tx, @Nonnull YangInstanceIdentifier tablePath, @Nonnull ContainerNode nlri);
+ void putRoutes(@Nonnull DOMDataWriteTransaction tx, @Nonnull YangInstanceIdentifier tablePath, @Nonnull ContainerNode nlri, @Nonnull ContainerNode attributes);
+ @Nonnull Collection<DataTreeCandidateNode> changedRoutes(@Nonnull DataTreeCandidateNode routes);
+ @Nonnull YangInstanceIdentifier routePath(@Nonnull YangInstanceIdentifier routesPath, @Nonnull PathArgument routeId);
}