import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
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.AdjRibOut;
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;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
private static final QName PEER_ID_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-id"));
private static final QName PEER_ROLE_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-role"));
private static final NodeIdentifier ADJRIBIN = new NodeIdentifier(AdjRibIn.QNAME);
+ private static final NodeIdentifier ADJRIBOUT = new NodeIdentifier(AdjRibOut.QNAME);
private static final NodeIdentifier EFFRIBIN = new NodeIdentifier(EffectiveRibIn.QNAME);
private static final NodeIdentifier PEER_ID = new NodeIdentifier(PEER_ID_QNAME);
private static final NodeIdentifier PEER_ROLE = new NodeIdentifier(PEER_ROLE_QNAME);
// FIXME: is there a utility method to construct this?
private static final ContainerNode EMPTY_ADJRIBIN = Builders.containerBuilder().withNodeIdentifier(ADJRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
private static final ContainerNode EMPTY_EFFRIBIN = Builders.containerBuilder().withNodeIdentifier(EFFRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
+ private static final ContainerNode EMPTY_ADJRIBOUT = Builders.containerBuilder().withNodeIdentifier(ADJRIBOUT).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
private final Map<TablesKey, TableContext> tables;
private final YangInstanceIdentifier tablesRoot;
pb.withChild(ImmutableNodes.leafNode(PEER_ROLE, this.role));
pb.withChild(EMPTY_ADJRIBIN);
pb.withChild(EMPTY_EFFRIBIN);
+ pb.withChild(EMPTY_ADJRIBOUT);
tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath, pb.build());
LOG.debug("New peer {} structure installed.", newPeerPath);
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
+import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
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.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.EffectiveRibIn;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+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.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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-// FIXME: instantiate for each table, listen on wildcard peer and routes
@NotThreadSafe
final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LocRibWriter.class);
+
private final Map<PathArgument, RouteEntry> routeEntries = new HashMap<>();
private final YangInstanceIdentifier target;
private final DOMTransactionChain chain;
private final ExportPolicyPeerTracker peerPolicyTracker;
private final NodeIdentifier attributesIdentifier;
private final Long ourAs;
+ private final RIBSupport ribSupport;
LocRibWriter(final RIBSupport ribSupport, final DOMTransactionChain chain, final YangInstanceIdentifier target, final Long ourAs,
final DOMDataTreeChangeService service, final PolicyDatabase pd) {
this.ourAs = Preconditions.checkNotNull(ourAs);
this.attributesIdentifier = ribSupport.routeAttributesIdentifier();
this.peerPolicyTracker = new ExportPolicyPeerTracker(service, target, pd);
+ this.ribSupport = ribSupport;
- service.registerDataTreeChangeListener(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, target.node(EffectiveRibIn.QNAME)), this);
+ service.registerDataTreeChangeListener(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, target), this);
}
- public static LocRibWriter create(@Nonnull final RIBSupport ribSupport, @Nonnull final DOMTransactionChain chain, @Nonnull final YangInstanceIdentifier target,
+ public static LocRibWriter create(@Nonnull final RIBSupport ribSupport, @Nonnull final TablesKey tablesKey, @Nonnull final DOMTransactionChain chain, @Nonnull final YangInstanceIdentifier target,
@Nonnull final AsNumber ourAs, @Nonnull final DOMDataTreeChangeService service, @Nonnull final PolicyDatabase pd) {
- return new LocRibWriter(ribSupport, chain, target, ourAs.getValue(), service, pd);
+
+ final YangInstanceIdentifier tableId = target.node(Peer.QNAME).node(Peer.QNAME).node(EffectiveRibIn.QNAME).node(Tables.QNAME).node(RibSupportUtils.toYangTablesKey(tablesKey));
+
+ final QName list = BindingReflections.findQName(ribSupport.routesListClass());
+ final YangInstanceIdentifier routeId = tableId.node(Routes.QNAME).node(BindingReflections.findQName(ribSupport.routesContainerClass())).node(list);
+
+ return new LocRibWriter(ribSupport, chain, routeId, ourAs.getValue(), service, pd);
}
@Override
this.peerPolicyTracker.close();
}
+ private static void printChildren(final DataTreeCandidateNode root) {
+ LOG.debug("Candidate node {} type {} identifier {}", root, root.getModificationType(), root.getIdentifier());
+ if (ModificationType.WRITE.equals(root.getModificationType())) {
+ LOG.debug("Data after {}", root.getDataAfter());
+ }
+ if (root.getChildNodes().isEmpty()) {
+ return;
+ }
+ for (final DataTreeCandidateNode child : root.getChildNodes()) {
+ printChildren(child);
+ }
+ }
+
@Override
public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
+ LOG.trace("Received data change to LocRib {}", Arrays.toString(changes.toArray()));
/*
* We use two-stage processing here in hopes that we avoid duplicate
* calculations when multiple peers have changed a particular entry.
*/
final Map<RouteUpdateKey, RouteEntry> toUpdate = new HashMap<>();
for (final DataTreeCandidate tc : changes) {
+ printChildren(tc.getRootNode());
+
final YangInstanceIdentifier path = tc.getRootPath();
final PathArgument routeId = path.getLastPathArgument();
final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(path);
if (entry == null) {
entry = new RouteEntry();
this.routeEntries.put(routeId, entry);
+ LOG.trace("Created new entry for {}", routeId);
}
- entry.addRoute(routerId, (ContainerNode) tc.getRootNode().getDataAfter().get());
+ final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(tc.getRootNode().getDataAfter(), this.ribSupport.routeAttributesIdentifier()).orNull();
+ entry.addRoute(routerId, advertisedAttrs);
+ LOG.trace("Added route from {} attributes{}", routerId, advertisedAttrs);
} else if (entry != null && entry.removeRoute(routerId)) {
this.routeEntries.remove(routeId);
entry = null;
+ LOG.trace("Removed route from {}", routerId);
}
+ LOG.debug("Updated route {} entry {}", routeId, entry);
toUpdate.put(new RouteUpdateKey(peerId, routeId), entry);
}
// Now walk all updated entries
for (final Entry<RouteUpdateKey, RouteEntry> e : toUpdate.entrySet()) {
+ LOG.trace("Walking through {}", e);
final RouteEntry entry = e.getValue();
final NormalizedNode<?, ?> value;
if (entry != null) {
if (!entry.selectBest(this.ourAs)) {
// Best path has not changed, no need to do anything else. Proceed to next route.
+ LOG.trace("Continuing");
continue;
}
-
value = entry.bestValue(e.getKey().getRouteId());
+ LOG.trace("Selected best value {}", value);
} else {
value = null;
}
if (value != null) {
+ LOG.debug("Write route to LocRib {}", value);
tx.put(LogicalDatastoreType.OPERATIONAL, this.target.node(e.getKey().getRouteId()), value);
} else {
+ LOG.debug("Delete route from LocRib {}", entry);
tx.delete(LogicalDatastoreType.OPERATIONAL, this.target.node(e.getKey().getRouteId()));
}
final YangInstanceIdentifier routeTarget = pid.getValue().node(e.getKey().getRouteId());
if (effectiveAttributes != null && value != null && !peerId.equals(pid.getKey())) {
+ LOG.debug("Write route to AdjRibsOut {}", value);
tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(this.attributesIdentifier), effectiveAttributes);
} else {
import com.google.common.primitives.UnsignedInteger;
import java.util.Objects;
import javax.annotation.concurrent.NotThreadSafe;
+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.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* A single route entry inside a route table. Maintains the attributes of
*/
@NotThreadSafe
final class RouteEntry {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RouteEntry.class);
+
private static final ContainerNode[] EMPTY_ATTRIBUTES = new ContainerNode[0];
private OffsetMap offsets = OffsetMap.EMPTY;
private BestPath bestPath;
void addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
- int offset = offsets.offsetOf(routerId);
+ int offset = this.offsets.offsetOf(routerId);
if (offset < 0) {
- final OffsetMap newOffsets = offsets.with(routerId);
+ final OffsetMap newOffsets = this.offsets.with(routerId);
offset = newOffsets.offsetOf(routerId);
- final ContainerNode[] newAttributes = newOffsets.expand(offsets, this.values, offset);
+ final ContainerNode[] newAttributes = newOffsets.expand(this.offsets, this.values, offset);
this.values = newAttributes;
this.offsets = newOffsets;
}
- offsets.setValue(this.values, offset, attributes);
+ this.offsets.setValue(this.values, offset, attributes);
}
// Indicates whether this was the last route
boolean removeRoute(final UnsignedInteger routerId) {
- if (offsets.size() != 1) {
+ if (this.offsets.size() != 1) {
// FIXME: actually shrink the array
- int offset = offsets.offsetOf(routerId);
- offsets.setValue(values, offset, null);
+ final int offset = this.offsets.offsetOf(routerId);
+ this.offsets.setValue(this.values, offset, null);
return false;
} else {
return true;
for (int i = 0; i < this.offsets.size(); ++i) {
final UnsignedInteger routerId = this.offsets.getRouterId(i);
final ContainerNode attributes = this.offsets.getValue(this.values, i);
-
+ LOG.trace("Processing router id {} attributes {}", routerId, attributes);
selector.processPath(routerId, attributes);
}
// Get the newly-selected best path.
final BestPath newBestPath = selector.result();
// FIXME: run deeper comparison
- final boolean ret = !Objects.equals(bestPath, newBestPath);
-
- bestPath = newBestPath;
+ final boolean ret = !Objects.equals(this.bestPath, newBestPath);
+ LOG.trace("Previous best {}, current best {}, result {}", this.bestPath, newBestPath, ret);
+ this.bestPath = newBestPath;
return ret;
}
NormalizedNode<?, ?> bestValue(final PathArgument key) {
- // TODO Auto-generated method stub
- return null;
+ final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> b = Builders.mapEntryBuilder();
+ b.withNodeIdentifier((NodeIdentifierWithPredicates) key);
+ b.addChild(this.bestPath.getState().getAttributes());
+ return b.build();
}
}