BUG-2383: introduce ExportPolicyPeerTracker 95/16095/5
authorRobert Varga <rovarga@cisco.com>
Thu, 5 Mar 2015 20:31:50 +0000 (21:31 +0100)
committerRobert Varga <rovarga@cisco.com>
Mon, 9 Mar 2015 10:00:13 +0000 (11:00 +0100)
This splits out the policy tracker and implemnts proper policy tracking.
We optimize for lookup speeds, expecting the peers not to change much.

Change-Id: I92b27d54abb77ce10864d9ec8709a57aab7b87c7
Signed-off-by: Robert Varga <rovarga@cisco.com>
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/PeerExportGroup.java [new file with mode: 0644]

diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java
new file mode 100644 (file)
index 0000000..8d5e9e6
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.rib.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+import java.util.AbstractMap;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+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.NodeIdentifierWithPredicates;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tracks peers for adj-rib-out writeout.
+ */
+final class ExportPolicyPeerTracker extends AbstractPeerRoleTracker {
+    private static final Logger LOG = LoggerFactory.getLogger(ExportPolicyPeerTracker.class);
+    private static final Function<YangInstanceIdentifier, Entry<PeerId, YangInstanceIdentifier>> GENERATE_PEERID = new Function<YangInstanceIdentifier, Entry<PeerId, YangInstanceIdentifier>>() {
+        @Override
+        public Entry<PeerId, YangInstanceIdentifier> apply(final YangInstanceIdentifier input) {
+            final PeerId peerId = IdentifierUtils.peerId((NodeIdentifierWithPredicates) input.getLastPathArgument());
+            return new AbstractMap.SimpleImmutableEntry<>(peerId, input);
+        }
+    };
+
+    private final Map<YangInstanceIdentifier, PeerRole> peerRoles = new HashMap<>();
+    private volatile Map<PeerRole, PeerExportGroup> groups = Collections.emptyMap();
+
+    protected ExportPolicyPeerTracker(final DOMDataTreeChangeService service, final YangInstanceIdentifier ribId) {
+        super(service, ribId);
+    }
+
+    private static Map<PeerRole, PeerExportGroup> createGroups(final Map<YangInstanceIdentifier, PeerRole> peerPathRoles) {
+        if (peerPathRoles.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        // Index things nicely for easy access
+        final Multimap<PeerRole, YangInstanceIdentifier> roleToIds = ArrayListMultimap.create(PeerRole.values().length, 2);
+        final Map<PeerId, PeerRole> idToRole = new HashMap<>();
+        for (Entry<YangInstanceIdentifier, PeerRole> e : peerPathRoles.entrySet()) {
+            roleToIds.put(e.getValue(), e.getKey());
+            idToRole.put(IdentifierUtils.peerId((NodeIdentifierWithPredicates) e.getKey().getLastPathArgument()), e.getValue());
+        }
+
+        // Optimized immutable copy, reused for all PeerGroups
+        final Map<PeerId, PeerRole> peerRoles = ImmutableMap.copyOf(idToRole);
+
+        final Map<PeerRole, PeerExportGroup> ret = new EnumMap<>(PeerRole.class);
+        for (Entry<PeerRole, Collection<YangInstanceIdentifier>> e : roleToIds.asMap().entrySet()) {
+            final AbstractExportPolicy policy = AbstractExportPolicy.forRole(e.getKey());
+            final Collection<Entry<PeerId, YangInstanceIdentifier>> peers = ImmutableList.copyOf(Collections2.transform(e.getValue(), GENERATE_PEERID));
+
+            ret.put(e.getKey(), new PeerExportGroup(peers, peerRoles, policy));
+        }
+
+        return ret;
+    }
+
+    @Override
+    protected void peerRoleChanged(final YangInstanceIdentifier peerPath, final PeerRole role) {
+        /*
+         * This is a sledgehammer approach to the problem: modify the role map first,
+         * then construct the group map from scratch.
+         */
+        final PeerRole oldRole;
+        if (role != null) {
+            oldRole = peerRoles.put(peerPath, role);
+        } else {
+            oldRole = peerRoles.remove(peerPath);
+        }
+
+        if (role != oldRole) {
+            LOG.debug("Peer {} changed role from {} to {}", peerPath, oldRole, role);
+            groups = createGroups(peerRoles);
+        }
+    }
+
+    PeerExportGroup getPeerGroup(final PeerRole role) {
+        return groups.get(Preconditions.checkNotNull(role));
+    }
+}
\ No newline at end of file
index c36ed77d0f048cdcccecd9be6d8d6ee1304c6563..9f8ce9627c259a27046b1d7d4db78adb8b60757c 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.protocol.bgp.rib.impl;
 import com.google.common.base.Preconditions;
 import com.google.common.primitives.UnsignedInteger;
 import java.util.Collection;
-import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -31,22 +30,27 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
 
 // FIXME: instantiate for each table, listen on wildcard peer and routes
 @NotThreadSafe
-final class LocRibWriter implements DOMDataTreeChangeListener {
+final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
     private final Map<PathArgument, RouteEntry> routeEntries = new HashMap<>();
     private final YangInstanceIdentifier target;
     private final DOMTransactionChain chain;
+    private final ExportPolicyPeerTracker peerPolicyTracker;
     private final RIBSupport ribSupport;
     private final Long ourAs;
 
-    // FIXME: these maps need to be populated
-    private final Map<PeerRole, Map<PeerId, YangInstanceIdentifier>> peersToUpdate = new EnumMap<>(PeerRole.class);
-    private final Map<PeerId, PeerRole> peers = new HashMap<>();
-
     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);
+
+        // FIXME: proper values
+        this.peerPolicyTracker = new ExportPolicyPeerTracker(null, null);
+    }
+
+    @Override
+    public void close() {
+        peerPolicyTracker.close();
     }
 
     @Override
@@ -114,25 +118,23 @@ final class LocRibWriter implements DOMDataTreeChangeListener {
              * if we have two eBGP peers, for example, there is no reason why we should perform the translation
              * multiple times.
              */
-            for (Entry<PeerRole, AbstractExportPolicy> pe : AbstractExportPolicy.POLICIES.entrySet()) {
-                final Map<PeerId, YangInstanceIdentifier> toPeers = peersToUpdate.get(pe.getKey());
-                if (toPeers == null || toPeers.isEmpty()) {
-                    continue;
-                }
-
-                final ContainerNode attributes = null;
-                final PeerId peerId = e.getKey().getPeerId();
-                final ContainerNode effectiveAttributes = pe.getValue().effectiveAttributes(peers.get(peerId), attributes);
-
-                for (Entry<PeerId, YangInstanceIdentifier> pid : toPeers.entrySet()) {
-                    // This points to adj-rib-out for a particlar peer/table combination
-                    final YangInstanceIdentifier routeTarget = pid.getValue().node(e.getKey().getRouteId());
-
-                    if (effectiveAttributes != null && value != null && !peerId.equals(pid.getKey())) {
-                        tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
-                        tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(ribSupport.routeAttributes()), effectiveAttributes);
-                    } else {
-                        tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
+            for (PeerRole role : PeerRole.values()) {
+                final PeerExportGroup peerGroup = peerPolicyTracker.getPeerGroup(role);
+                if (peerGroup != null) {
+                    final ContainerNode attributes = null;
+                    final PeerId peerId = e.getKey().getPeerId();
+                    final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(peerId, attributes);
+
+                    for (Entry<PeerId, YangInstanceIdentifier> pid : peerGroup.getPeers()) {
+                        // This points to adj-rib-out for a particular peer/table combination
+                        final YangInstanceIdentifier routeTarget = pid.getValue().node(e.getKey().getRouteId());
+
+                        if (effectiveAttributes != null && value != null && !peerId.equals(pid.getKey())) {
+                            tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
+                            tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(ribSupport.routeAttributes()), effectiveAttributes);
+                        } else {
+                            tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
+                        }
                     }
                 }
             }
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/PeerExportGroup.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/PeerExportGroup.java
new file mode 100644 (file)
index 0000000..d57a10f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.rib.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+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.schema.ContainerNode;
+
+/**
+ * A collection of peers sharing the same export policy.
+ */
+final class PeerExportGroup {
+    private final Collection<Entry<PeerId, YangInstanceIdentifier>> peers;
+    private final Map<PeerId, PeerRole> peerRoles;
+    private final AbstractExportPolicy policy;
+
+    PeerExportGroup(final Collection<Entry<PeerId, YangInstanceIdentifier>> peers, final Map<PeerId, PeerRole> peerRoles, final AbstractExportPolicy policy) {
+        this.peers = Preconditions.checkNotNull(peers);
+        this.peerRoles = Preconditions.checkNotNull(peerRoles);
+        this.policy = Preconditions.checkNotNull(policy);
+    }
+
+    ContainerNode effectiveAttributes(final PeerId sourcePeerId, final ContainerNode attributes) {
+        return policy.effectiveAttributes(peerRoles.get(sourcePeerId), attributes);
+    }
+
+    Collection<Entry<PeerId, YangInstanceIdentifier>> getPeers() {
+        return peers;
+    }
+}
\ No newline at end of file