BUG-6497: fixup augmentation lookup
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / EffectiveRibInWriter.java
index 8b25aea06f5f2f74407fa3be09e884a2295b2ff2..b62e0ec879c6b74f134de4ef278c193e2d1b1135 100644 (file)
@@ -7,24 +7,34 @@
  */
 package org.opendaylight.protocol.bgp.rib.impl;
 
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Verify;
 import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import javax.annotation.Nonnull;
 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.ClusteredDOMDataTreeChangeListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.protocol.bgp.rib.impl.spi.AbstractImportPolicy;
+import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
+import org.opendaylight.protocol.bgp.rib.impl.stats.peer.route.PerTableTypeRouteCounter;
+import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
-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.PeerRole;
 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;
+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.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -32,14 +42,16 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 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;
 
 /**
- * Implementation of the BGP import policy. Listens on all Adj-RIB-In, inspects all inbound
+ * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
  * routes in the context of the advertising peer's role and applies the inbound policy.
  *
  * Inbound policy is applied as follows:
@@ -49,78 +61,123 @@ import org.slf4j.LoggerFactory;
  *    advertising peer's role
  * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
  *
- * Note that we maintain the peer roles using a DCL, even if we could look up our internal
- * structures. This is done so we maintain causality and loose coupling.
  */
 @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);
+    protected static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
 
-    /**
-     * Maintains {@link TableRouteListener} instances.
-     */
-    private final class AdjInTracker implements AutoCloseable, DOMDataTreeChangeListener {
+    private final class AdjInTracker implements AutoCloseable, ClusteredDOMDataTreeChangeListener {
         private final RIBSupportContextRegistry registry;
-        private final YangInstanceIdentifier ribId;
+        private final YangInstanceIdentifier peerIId;
+        private final YangInstanceIdentifier effRibTables;
         private final ListenerRegistration<?> reg;
         private final DOMTransactionChain chain;
+        private final PerTableTypeRouteCounter effectiveRibInRouteCounters;
+        private final PerTableTypeRouteCounter adjRibInRouteCounters;
+        private final Map<TablesKey, Set<YangInstanceIdentifier>> effectiveRibInRouteMap = new ConcurrentHashMap<>();
+        private final Map<TablesKey, Set<YangInstanceIdentifier>> adjRibInRouteMap = new ConcurrentHashMap<>();
 
-        AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
+        AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
+                @Nonnull final PerTableTypeRouteCounter effectiveRibInRouteCounters, @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters) {
             this.registry = Preconditions.checkNotNull(registry);
             this.chain = Preconditions.checkNotNull(chain);
-            this.ribId = Preconditions.checkNotNull(ribId);
+            this.peerIId = Preconditions.checkNotNull(peerIId);
+            this.effRibTables = this.peerIId.node(EffectiveRibIn.QNAME).node(Tables.QNAME);
+            this.effectiveRibInRouteCounters = Preconditions.checkNotNull(effectiveRibInRouteCounters);
+            this.adjRibInRouteCounters = Preconditions.checkNotNull(adjRibInRouteCounters);
 
-            final YangInstanceIdentifier tableId = ribId.node(Peer.QNAME).node(AdjRibIn.QNAME).node(Tables.QNAME);
-            final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId);
+            final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, this.peerIId.node(AdjRibIn.QNAME).node(Tables.QNAME));
+            LOG.debug("Registered Effective RIB on {}", this.peerIId);
             this.reg = service.registerDataTreeChangeListener(treeId, this);
         }
 
+        /**
+         * @deprecated Should always pass in route counter
+         * @param service
+         * @param registry
+         * @param chain
+         * @param peerIId
+         */
+        @Deprecated
+        AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId) {
+            this(service, registry, chain, peerIId, new PerTableTypeRouteCounter("effective-rib-in"), new PerTableTypeRouteCounter("adj-rib-in"));
+        }
+
+        private void updateRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
+                @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
+            routeMap.putIfAbsent(tablesKey, new HashSet<>());
+            routeMap.get(tablesKey).add(routeId);
+
+            updateRouteCounter(counter, routeMap,tablesKey);
+        }
+
+        private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
+                @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
+            if (routeMap.containsKey(tablesKey)) {
+                routeMap.get(tablesKey).remove(routeId);
+            }
+
+            updateRouteCounter(counter, routeMap,tablesKey);
+        }
+
+        private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
+                @Nonnull final TablesKey tablesKey) {
+            routeMap.remove(tablesKey);
+
+            updateRouteCounter(counter, routeMap,tablesKey);
+        }
+
+        private void updateRouteCounter(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
+                @Nonnull final TablesKey tablesKey) {
+            counter.getCounterOrSetDefault(tablesKey)
+            .setCount(routeMap.getOrDefault(tablesKey, new HashSet<>()).size());
+        }
+
         private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
-            LOG.debug("Process route {}", route);
+            LOG.debug("Process route {}", route.getIdentifier());
+            final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
+            final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
             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);
+            case DISAPPEARED:
+                tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
+                LOG.debug("Route deleted. routeId={}", routeId);
+
+                deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
+                deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
                 break;
             case UNMODIFIED:
                 // No-op
                 break;
+            case APPEARED:
             case SUBTREE_MODIFIED:
             case WRITE:
+                tx.put(LogicalDatastoreType.OPERATIONAL, routeId, route.getDataAfter().get());
+                // count adj-rib-in route first
+                updateRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
+                updateRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
                 // Lookup per-table attributes from RIBSupport
-                final ContainerNode adverisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
+                final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
                 final ContainerNode effectiveAttrs;
 
-                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;
-                    }
+                if (advertisedAttrs != null) {
+                    effectiveAttrs = policy.effectiveAttributes(advertisedAttrs);
                 } else {
                     effectiveAttrs = null;
                 }
 
-                final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
                 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
 
                 if (effectiveAttrs != null) {
                     tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
+
+                    updateRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
                 } else {
-                    LOG.warn("Route {} advertised empty attributes", route.getDataAfter());
+                    LOG.warn("Route {} advertised empty attributes", routeId);
                     tx.delete(LogicalDatastoreType.OPERATIONAL,  routeId);
+
+                    deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
                 }
                 break;
             default:
@@ -129,32 +186,33 @@ final class EffectiveRibInWriter implements AutoCloseable {
             }
         }
 
-        private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final NodeIdentifierWithPredicates peerKey, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
-            final AbstractImportPolicy policy = EffectiveRibInWriter.this.peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
-
+        private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
             for (final DataTreeCandidateNode child : children) {
+                final PathArgument childIdentifier = child.getIdentifier();
+                final Optional<NormalizedNode<?, ?>> childDataAfter = child.getDataAfter();
+                final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
+                LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}", childIdentifier, child
+                        .getModificationType(), childDataAfter, child.getDataBefore());
+                final YangInstanceIdentifier childPath = tablePath.node(childIdentifier);
                 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);
+                case DISAPPEARED:
+                    tx.delete(LogicalDatastoreType.OPERATIONAL, childPath);
+                    LOG.debug("Route deleted. routeId={}", childPath);
+
+                    deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, childPath);
+                    deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, childPath);
                     break;
                 case UNMODIFIED:
                     // No-op
                     break;
                 case SUBTREE_MODIFIED:
+                    processModifiedRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
+                    break;
+                case APPEARED:
                 case WRITE:
-                    tx.put(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()), child.getDataAfter().get());
+                    writeRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
 
-                    // 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 (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
-                            processRoute(tx, ribSupport, policy, routesPath, route);
-                        }
-                    }
                     break;
                 default:
                     LOG.warn("Ignoring unhandled child {}", child);
@@ -163,103 +221,154 @@ final class EffectiveRibInWriter implements AutoCloseable {
             }
         }
 
+        private void processModifiedRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier, final DOMDataWriteTransaction tx,
+                final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
+            if (TABLE_ROUTES.equals(childIdentifier)) {
+                for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
+                    processRoute(tx, ribSupport, policy, childPath, route);
+                }
+            } else {
+                tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
+            }
+        }
+
+        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) {
+            if (TABLE_ROUTES.equals(childIdentifier)) {
+                final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
+                if (!changedRoutes.isEmpty()) {
+                    tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.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 :)
+                    for (final DataTreeCandidateNode route : changedRoutes) {
+                        processRoute(tx, ribSupport, policy, childPath, route);
+                    }
+                }
+            }
+        }
+
         private RIBSupportContext getRibSupport(final NodeIdentifierWithPredicates tableKey) {
-            // FIXME: use codec to translate tableKey
-            return this.registry.getRIBSupportContext(null);
+            return this.registry.getRIBSupportContext(tableKey);
         }
 
-        private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey) {
-            return this.ribId.node(Peer.QNAME).node(peerKey).node(EffectiveRibIn.QNAME).node(tableKey);
+        private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
+            return this.effRibTables.node(tableKey);
         }
 
-        private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
+        private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
             final RIBSupportContext ribSupport = getRibSupport(tableKey);
-            final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
+            final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
 
-            processTableChildren(tx, ribSupport.getRibSupport(), peerKey, tablePath, table.getChildNodes());
+            processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
         }
 
-        private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
+        private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
             final RIBSupportContext ribSupport = getRibSupport(tableKey);
-            final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
+            final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
 
             // Create an empty table
-            ribSupport.clearTable(tx,tablePath);
+            LOG.trace("Create Empty table", tablePath);
+            ribSupport.createEmptyTableStructure(tx, tablePath);
 
-            processTableChildren(tx, ribSupport.getRibSupport(), peerKey, tablePath, table.getChildNodes());
+            processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
         }
 
         @Override
-        public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
+        public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
             LOG.trace("Data changed called to effective RIB. Change : {}", changes);
-            final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
 
+            // we have a lot of transactions created for 'nothing' because a lot of changes
+            // are skipped, so ensure we only create one transaction when we really need it
+            DOMDataWriteTransaction tx = null;
             for (final DataTreeCandidate tc : changes) {
                 final YangInstanceIdentifier rootPath = tc.getRootPath();
 
-                // Obtain the peer's key
-                final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(rootPath);
-
-                // 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;
-
                 final DataTreeCandidateNode root = tc.getRootNode();
-                switch (root.getModificationType()) {
-                case DELETE:
-                    // 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:
-                    writeTable(tx, peerKey, tableKey, root);
-                    break;
-                default:
-                    LOG.warn("Ignoring unhandled root {}", root);
-                    break;
+                for (final DataTreeCandidateNode table : root.getChildNodes()) {
+                    if (tx == null) {
+                        tx = this.chain.newWriteOnlyTransaction();
+                    }
+                    changeDataTree(tx, rootPath, root, table);
                 }
             }
+            if (tx != null) {
+                tx.submit();
+            }
+        }
 
-            tx.submit();
+        private void changeDataTree(final DOMDataWriteTransaction tx, final YangInstanceIdentifier rootPath,
+                final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
+            final PathArgument lastArg = table.getIdentifier();
+            Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
+            final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
+            final RIBSupport ribSupport = getRibSupport(tableKey).getRibSupport();
+            final ModificationType modificationType = root.getModificationType();
+            switch (modificationType) {
+            case DELETE:
+            case DISAPPEARED:
+                final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
+                LOG.debug("Delete Effective Table {} modification type {}, ", effectiveTablePath, modificationType);
+
+                // delete the corresponding effective table
+                tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
+
+                deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, new TablesKey(ribSupport.getAfi(), ribSupport.getSafi()));
+                deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, new TablesKey(ribSupport.getAfi(), ribSupport.getSafi()));
+                break;
+            case SUBTREE_MODIFIED:
+                modifyTable(tx, tableKey, table);
+                break;
+            case UNMODIFIED:
+                LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
+                break;
+            case APPEARED:
+            case WRITE:
+                writeTable(tx, tableKey, table);
+                break;
+            default:
+                LOG.warn("Ignoring unhandled root {}", root);
+                break;
+            }
         }
 
         @Override
         public void close() {
-            // FIXME: wipe all effective routes?
             this.reg.close();
         }
     }
 
-    private final ImportPolicyPeerTracker peerPolicyTracker;
     private final AdjInTracker adjInTracker;
+    private final AbstractImportPolicy importPolicy;
 
+    @Deprecated
     static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
-        @Nonnull final YangInstanceIdentifier ribId, @Nonnull final PolicyDatabase pd, @Nonnull final RIBSupportContextRegistry registry) {
-        return new EffectiveRibInWriter(service, chain, ribId, pd, registry);
+            @Nonnull final YangInstanceIdentifier peerIId, @Nonnull final ImportPolicyPeerTracker importPolicyPeerTracker, @Nonnull final RIBSupportContextRegistry registry, final PeerRole peerRole) {
+        return new EffectiveRibInWriter(service, chain, peerIId, importPolicyPeerTracker, registry, peerRole);
     }
 
-    private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier ribId,
-        final PolicyDatabase pd, final RIBSupportContextRegistry registry) {
+    static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
+            @Nonnull final YangInstanceIdentifier peerIId, @Nonnull final ImportPolicyPeerTracker importPolicyPeerTracker, @Nonnull final RIBSupportContextRegistry registry, final PeerRole peerRole,
+            @Nonnull final PerTableTypeRouteCounter effectiveRouteCounters, @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters) {
+        return new EffectiveRibInWriter(service, chain, peerIId, importPolicyPeerTracker, registry, peerRole, effectiveRouteCounters, adjRibInRouteCounters);
+    }
+
+    @Deprecated
+    private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
+            final ImportPolicyPeerTracker importPolicyPeerTracker, final RIBSupportContextRegistry registry, final PeerRole peerRole) {
+        importPolicyPeerTracker.peerRoleChanged(peerIId, peerRole);
+        this.importPolicy = importPolicyPeerTracker.policyFor(IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerIId.getLastPathArgument()));
+        this.adjInTracker = new AdjInTracker(service, registry, chain, peerIId);
+    }
 
-        this.peerPolicyTracker = new ImportPolicyPeerTracker(service, ribId, pd);
-        this.adjInTracker = new AdjInTracker(service, registry, chain, ribId);
+    private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
+            final ImportPolicyPeerTracker importPolicyPeerTracker, final RIBSupportContextRegistry registry, final PeerRole peerRole,
+            @Nonnull final PerTableTypeRouteCounter effectiveRouteCounters, @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters) {
+        importPolicyPeerTracker.peerRoleChanged(peerIId, peerRole);
+        this.importPolicy = importPolicyPeerTracker.policyFor(IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerIId.getLastPathArgument()));
+        this.adjInTracker = new AdjInTracker(service, registry, chain, peerIId, effectiveRouteCounters, adjRibInRouteCounters);
     }
 
     @Override
     public void close() {
         this.adjInTracker.close();
-        this.peerPolicyTracker.close();
     }
 }