Clean up RibSupport registration
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / EffectiveRibInWriter.java
index 0c78b7784fe39ce60811378cea72aab15adcca2a..3e89777836aaa35c9fbbde4c84768d406bf168aa 100644 (file)
@@ -8,12 +8,16 @@
 package org.opendaylight.protocol.bgp.rib.impl;
 
 import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
+import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBIN_ATTRIBUTES_AID;
 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBIN_NID;
 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ATTRIBUTES_NID;
 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.EFFRIBIN_NID;
+import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.LLGR_STALE_NID;
 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ROUTES_NID;
 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES_NID;
+import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.UPTODATE_NID;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -29,16 +33,15 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.LongAdder;
-import javax.annotation.Nonnull;
-import javax.annotation.concurrent.GuardedBy;
-import javax.annotation.concurrent.NotThreadSafe;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-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.checkerframework.checker.lock.qual.GuardedBy;
+import org.checkerframework.checker.lock.qual.Holding;
 import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.CommunityUtil;
 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
@@ -47,22 +50,22 @@ import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
+import org.opendaylight.protocol.bgp.rib.spi.RIBNormalizedNodes;
 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
 import org.opendaylight.protocol.bgp.route.targetcontrain.spi.ClientRouteTargetContrainCache;
 import org.opendaylight.protocol.bgp.route.targetcontrain.spi.RouteTargetMembeshipUtil;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.AfiSafiType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.attributes.Communities;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.Communities;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.RouteTargetConstrainSubsequentAddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.route.target.constrain.routes.route.target.constrain.routes.RouteTargetConstrainRoute;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.MplsLabeledVpnSubsequentAddressFamily;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteTarget;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv4AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv6AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.MplsLabeledVpnSubsequentAddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RouteTarget;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
@@ -74,10 +77,10 @@ import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 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.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -93,20 +96,22 @@ import org.slf4j.LoggerFactory;
  * 2) check if a route is admissible based on attributes attached to it, as well as the
  * advertising peer's role
  * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
+ *
+ * <p>
+ * This class is NOT thread-safe.
  */
-@NotThreadSafe
 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
         AutoCloseable, ClusteredDOMDataTreeChangeListener {
 
     private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
-    private static final TablesKey IVP4_VPN_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
-            MplsLabeledVpnSubsequentAddressFamily.class);
-    private static final TablesKey IVP6_VPN_TABLE_KEY = new TablesKey(Ipv6AddressFamily.class,
-            MplsLabeledVpnSubsequentAddressFamily.class);
-    private static final ImmutableList<Communities> STALE_LLGR_COMMUNUTIES = ImmutableList.of(
-        StaleCommunities.STALE_LLGR);
+    private static final TablesKey IVP4_VPN_TABLE_KEY =
+        new TablesKey(Ipv4AddressFamily.VALUE, MplsLabeledVpnSubsequentAddressFamily.VALUE);
+    private static final TablesKey IVP6_VPN_TABLE_KEY =
+        new TablesKey(Ipv6AddressFamily.VALUE, MplsLabeledVpnSubsequentAddressFamily.VALUE);
+    private static final ImmutableList<Communities> STALE_LLGR_COMMUNUTIES =
+        ImmutableList.of(StaleCommunities.STALE_LLGR);
     private static final Attributes STALE_LLGR_ATTRIBUTES = new org.opendaylight.yang.gen.v1.urn.opendaylight.params
-            .xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder()
+            .xml.ns.yang.bgp.message.rev200120.path.attributes.AttributesBuilder()
             .setCommunities(STALE_LLGR_COMMUNUTIES)
             .build();
     private static final ChoiceNode EMPTY_ROUTES = Builders.choiceBuilder().withNodeIdentifier(ROUTES_NID).build();
@@ -138,26 +143,26 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
             final BGPTableTypeRegistryConsumer tableTypeRegistry,
             final List<RouteTarget> rtMemberships,
             final ClientRouteTargetContrainCache rtCache) {
-        this.registry = requireNonNull(rib.getRibSupportContext());
+        registry = requireNonNull(rib.getRibSupportContext());
         this.chain = requireNonNull(chain);
         this.peerIId = requireNonNull(peerIId);
-        this.effRibTables = this.peerIId.node(EFFRIBIN_NID);
-        this.prefixesInstalled = buildPrefixesTables(tables);
-        this.prefixesReceived = buildPrefixesTables(tables);
-        this.ribPolicies = requireNonNull(rib.getRibPolicies());
-        this.service = requireNonNull(rib.getService());
+        effRibTables = this.peerIId.node(EFFRIBIN_NID);
+        prefixesInstalled = buildPrefixesTables(tables);
+        prefixesReceived = buildPrefixesTables(tables);
+        ribPolicies = requireNonNull(rib.getRibPolicies());
+        service = requireNonNull(rib.getService());
         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
-        this.peerImportParameters = peer;
+        peerImportParameters = peer;
         this.rtMemberships = rtMemberships;
         this.rtCache = rtCache;
-        this.vpnTableRefresher = rib;
+        vpnTableRefresher = rib;
     }
 
     public void init() {
         final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
-            this.peerIId.node(ADJRIBIN_NID).node(TABLES_NID));
-        LOG.debug("Registered Effective RIB on {}", this.peerIId);
-        this.reg = requireNonNull(this.service).registerDataTreeChangeListener(treeId, this);
+            peerIId.node(ADJRIBIN_NID).node(TABLES_NID));
+        LOG.debug("Registered Effective RIB on {}", peerIId);
+        reg = requireNonNull(service).registerDataTreeChangeListener(treeId, this);
     }
 
     private static Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
@@ -167,20 +172,25 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
     }
 
     @Override
-    public synchronized void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
-        if (this.chain == null) {
+    public synchronized void onInitialData() {
+        // FIXME: update as if root was deleted
+    }
+
+    @Override
+    public synchronized void onDataTreeChanged(final List<DataTreeCandidate> changes) {
+        if (chain == null) {
             LOG.trace("Chain closed. Ignoring Changes : {}", changes);
             return;
         }
 
         LOG.trace("Data changed called to effective RIB. Change : {}", changes);
-        DOMDataWriteTransaction tx = null;
+        DOMDataTreeWriteTransaction tx = null;
         for (final DataTreeCandidate tc : changes) {
             final YangInstanceIdentifier rootPath = tc.getRootPath();
             final DataTreeCandidateNode root = tc.getRootNode();
             for (final DataTreeCandidateNode table : root.getChildNodes()) {
                 if (tx == null) {
-                    tx = this.chain.newWriteOnlyTransaction();
+                    tx = chain.newWriteOnlyTransaction();
                 }
                 changeDataTree(tx, rootPath, root, table);
             }
@@ -188,7 +198,7 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
 
         if (tx != null) {
             final FluentFuture<? extends CommitInfo> future = tx.commit();
-            this.submitted = future;
+            submitted = future;
             future.addCallback(new FutureCallback<CommitInfo>() {
                 @Override
                 public void onSuccess(final CommitInfo result) {
@@ -203,37 +213,37 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
         }
 
         //Refresh VPN Table if RT Memberships were updated
-        if (this.rtMembershipsUpdated) {
-            this.vpnTableRefresher.refreshTable(IVP4_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
-            this.vpnTableRefresher.refreshTable(IVP6_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
-            this.rtMembershipsUpdated = false;
+        if (rtMembershipsUpdated) {
+            vpnTableRefresher.refreshTable(IVP4_VPN_TABLE_KEY, peerImportParameters.getFromPeerId());
+            vpnTableRefresher.refreshTable(IVP6_VPN_TABLE_KEY, peerImportParameters.getFromPeerId());
+            rtMembershipsUpdated = false;
         }
     }
 
     @Override
     public synchronized void close() {
-        if (this.reg != null) {
-            this.reg.close();
-            this.reg = null;
+        if (reg != null) {
+            reg.close();
+            reg = null;
         }
-        if (this.submitted != null) {
+        if (submitted != null) {
             try {
-                this.submitted.get();
+                submitted.get();
             } catch (final InterruptedException | ExecutionException throwable) {
                 LOG.error("Write routes failed", throwable);
             }
         }
-        if (this.chain != null) {
-            this.chain.close();
-            this.chain = null;
+        if (chain != null) {
+            chain.close();
+            chain = null;
         }
-        this.prefixesReceived.values().forEach(LongAdder::reset);
-        this.prefixesInstalled.values().forEach(LongAdder::reset);
+        prefixesReceived.values().forEach(LongAdder::reset);
+        prefixesInstalled.values().forEach(LongAdder::reset);
     }
 
     @Override
     public long getPrefixedReceivedCount(final TablesKey tablesKey) {
-        final LongAdder counter = this.prefixesReceived.get(tablesKey);
+        final LongAdder counter = prefixesReceived.get(tablesKey);
         if (counter == null) {
             return 0;
         }
@@ -242,17 +252,17 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
 
     @Override
     public Set<TablesKey> getTableKeys() {
-        return ImmutableSet.copyOf(this.prefixesReceived.keySet());
+        return ImmutableSet.copyOf(prefixesReceived.keySet());
     }
 
     @Override
     public boolean isSupported(final TablesKey tablesKey) {
-        return this.prefixesReceived.containsKey(tablesKey);
+        return prefixesReceived.containsKey(tablesKey);
     }
 
     @Override
     public long getPrefixedInstalledCount(final TablesKey tablesKey) {
-        final LongAdder counter = this.prefixesInstalled.get(tablesKey);
+        final LongAdder counter = prefixesInstalled.get(tablesKey);
         if (counter == null) {
             return 0;
         }
@@ -261,17 +271,17 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
 
     @Override
     public long getTotalPrefixesInstalled() {
-        return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
+        return prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
     }
 
-    @GuardedBy("this")
-    private void changeDataTree(final DOMDataWriteTransaction tx, final YangInstanceIdentifier rootPath,
+    @Holding("this")
+    private void changeDataTree(final DOMDataTreeWriteTransaction tx, final YangInstanceIdentifier rootPath,
             final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
         final PathArgument lastArg = table.getIdentifier();
         verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(),
             rootPath);
         final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
-        final RIBSupportContext ribContext = this.registry.getRIBSupportContext(tableKey);
+        final RIBSupportContext ribContext = registry.getRIBSupportContext(tableKey);
         if (ribContext == null) {
             LOG.warn("Table {} is not supported, ignoring event", tableKey);
             return;
@@ -301,38 +311,46 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
         }
     }
 
-    private void deleteTable(final DOMDataWriteTransaction tx, final RIBSupportContext ribContext,
+    private void deleteTable(final DOMDataTreeWriteTransaction tx, final RIBSupportContext ribContext,
             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
         LOG.debug("Delete Effective Table {}", effectiveTablePath);
         onDeleteTable(ribContext.getRibSupport(), effectiveTablePath, table.getDataBefore());
         tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
     }
 
-    private void modifyTable(final DOMDataWriteTransaction tx, final RIBSupportContext ribContext,
+    private void modifyTable(final DOMDataTreeWriteTransaction tx, final RIBSupportContext ribContext,
             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
         LOG.debug("Modify Effective Table {}", effectiveTablePath);
 
-        final DataTreeCandidateNode modifiedAttrs = table.getModifiedChild(ATTRIBUTES_NID);
-        if (modifiedAttrs != null) {
-            final Optional<NormalizedNode<?, ?>> attrsAfter = modifiedAttrs.getDataAfter();
+        final boolean wasLongLivedStale = isLongLivedStaleTable(table.getDataBefore());
+        final boolean longLivedStale = isLongLivedStaleTable(table.getDataAfter());
+        if (wasLongLivedStale != longLivedStale) {
+            LOG.debug("LLGR_STALE flag flipped {}, overwriting table {}", longLivedStale ? "ON" : "OFF",
+                    effectiveTablePath);
+            writeTable(tx, ribContext, effectiveTablePath, table);
+            return;
+        }
+
+        table.getModifiedChild(ATTRIBUTES_NID).ifPresent(modifiedAttrs -> {
             final YangInstanceIdentifier effAttrsPath = effectiveTablePath.node(ATTRIBUTES_NID);
-            if (attrsAfter.isPresent()) {
-                tx.put(LogicalDatastoreType.OPERATIONAL, effAttrsPath, attrsAfter.get());
+            final Optional<NormalizedNode> optAttrsAfter = modifiedAttrs.getDataAfter();
+            if (optAttrsAfter.isPresent()) {
+                tx.put(LogicalDatastoreType.OPERATIONAL, effAttrsPath,
+                    effectiveAttributes(extractContainer(optAttrsAfter.orElseThrow())));
             } else {
                 tx.delete(LogicalDatastoreType.OPERATIONAL, effAttrsPath);
             }
-        }
+        });
 
-        final DataTreeCandidateNode modifiedRoutes = table.getModifiedChild(ROUTES_NID);
-        if (modifiedRoutes != null) {
-            final RIBSupport<?, ?, ?, ?> ribSupport = ribContext.getRibSupport();
+        table.getModifiedChild(ROUTES_NID).ifPresent(modifiedRoutes -> {
+            final RIBSupport<?, ?> ribSupport = ribContext.getRibSupport();
             switch (modifiedRoutes.getModificationType()) {
                 case APPEARED:
                 case WRITE:
                     deleteRoutesBefore(tx, ribSupport, effectiveTablePath, modifiedRoutes);
                     // XXX: YANG Tools seems to have an issue stacking DELETE with child WRITE
                     tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ROUTES_NID), EMPTY_ROUTES);
-                    writeRoutesAfter(tx, ribSupport, effectiveTablePath, modifiedRoutes.getDataAfter());
+                    writeRoutesAfter(tx, ribSupport, effectiveTablePath, modifiedRoutes.getDataAfter(), longLivedStale);
                     break;
                 case DELETE:
                 case DISAPPEARED:
@@ -341,7 +359,7 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
                     break;
                 case SUBTREE_MODIFIED:
                     for (DataTreeCandidateNode modifiedRoute : ribSupport.changedRoutes(modifiedRoutes)) {
-                        processRoute(tx, ribSupport, effectiveTablePath, modifiedRoute);
+                        processRoute(tx, ribSupport, effectiveTablePath, modifiedRoute, longLivedStale);
                     }
                     break;
                 case UNMODIFIED:
@@ -351,78 +369,87 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
                     LOG.warn("Ignoring modified routes {}", modifiedRoutes);
                     break;
             }
-        }
+        });
     }
 
-    private void writeTable(final DOMDataWriteTransaction tx, final RIBSupportContext ribContext,
+    private void writeTable(final DOMDataTreeWriteTransaction tx, final RIBSupportContext ribContext,
             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
         LOG.debug("Write Effective Table {}", effectiveTablePath);
         onDeleteTable(ribContext.getRibSupport(), effectiveTablePath, table.getDataBefore());
 
-        final Optional<NormalizedNode<?, ?>> maybeTableAfter = table.getDataAfter();
+        final Optional<NormalizedNode> maybeTableAfter = table.getDataAfter();
         if (maybeTableAfter.isPresent()) {
-            final MapEntryNode tableAfter = extractMapEntry(maybeTableAfter);
+            final NormalizedNode node = maybeTableAfter.orElseThrow();
+            verify(node instanceof MapEntryNode, "Expected MapEntryNode, got %s", node);
+            final MapEntryNode tableAfter = (MapEntryNode) node;
             ribContext.createEmptyTableStructure(tx, effectiveTablePath);
 
-            final Optional<DataContainerChild<?, ?>> maybeAttrsAfter = tableAfter.getChild(ATTRIBUTES_NID);
-            if (maybeAttrsAfter.isPresent()) {
+            final DataContainerChild maybeAttrsAfter = tableAfter.childByArg(ATTRIBUTES_NID);
+            final boolean longLivedStale;
+            if (maybeAttrsAfter != null) {
                 final ContainerNode attrsAfter = extractContainer(maybeAttrsAfter);
-                tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ATTRIBUTES_NID), attrsAfter);
+                longLivedStale = isLongLivedStale(attrsAfter);
+                tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ATTRIBUTES_NID),
+                    effectiveAttributes(attrsAfter));
+            } else {
+                longLivedStale = false;
             }
 
             writeRoutesAfter(tx, ribContext.getRibSupport(), effectiveTablePath,
-                NormalizedNodes.findNode(tableAfter, ROUTES_NID));
+                NormalizedNodes.findNode(tableAfter, ROUTES_NID), longLivedStale);
         }
     }
 
     // Performs house-keeping when the contents of a table is deleted
-    private void onDeleteTable(final RIBSupport<?, ?, ?, ?> ribSupport, final YangInstanceIdentifier effectiveTablePath,
-            final Optional<NormalizedNode<?, ?>> tableBefore) {
+    private void onDeleteTable(final RIBSupport<?, ?> ribSupport, final YangInstanceIdentifier effectiveTablePath,
+            final Optional<NormalizedNode> tableBefore) {
         // Routes are special in that we need to process the to keep our counters accurate
-        final Optional<NormalizedNode<?, ?>> maybeRoutesBefore = findRoutesMap(ribSupport,
+        final Optional<NormalizedNode> maybeRoutesBefore = findRoutesMap(ribSupport,
                 NormalizedNodes.findNode(tableBefore, ROUTES_NID));
         if (maybeRoutesBefore.isPresent()) {
-            onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).getValue());
+            onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).body());
         }
     }
 
-    private void deleteRoutesBefore(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
+    private void deleteRoutesBefore(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode modifiedRoutes) {
-        final Optional<NormalizedNode<?, ?>> maybeRoutesBefore = NormalizedNodes.findNode(
+        final Optional<NormalizedNode> maybeRoutesBefore = NormalizedNodes.findNode(
             modifiedRoutes.getDataBefore(), ribSupport.relativeRoutesPath());
         if (maybeRoutesBefore.isPresent()) {
-            onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).getValue());
+            onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).body());
         }
     }
 
-    private void writeRoutesAfter(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
-            final YangInstanceIdentifier effectiveTablePath, final Optional<NormalizedNode<?, ?>> routesAfter) {
-        final Optional<NormalizedNode<?, ?>> maybeRoutesAfter = NormalizedNodes.findNode(routesAfter,
+    private void writeRoutesAfter(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
+            final YangInstanceIdentifier effectiveTablePath, final Optional<NormalizedNode> routesAfter,
+            final boolean longLivedStale) {
+        final Optional<NormalizedNode> maybeRoutesAfter = NormalizedNodes.findNode(routesAfter,
             ribSupport.relativeRoutesPath());
         if (maybeRoutesAfter.isPresent()) {
             final YangInstanceIdentifier routesPath = routeMapPath(ribSupport, effectiveTablePath);
-            for (MapEntryNode routeAfter : extractMap(maybeRoutesAfter).getValue()) {
-                writeRoute(tx, ribSupport, routesPath.node(routeAfter.getIdentifier()), Optional.empty(), routeAfter);
+            for (MapEntryNode routeAfter : extractMap(maybeRoutesAfter).body()) {
+                writeRoute(tx, ribSupport, routesPath.node(routeAfter.getIdentifier()), Optional.empty(), routeAfter,
+                    longLivedStale);
             }
         }
     }
 
-    private void onRoutesDeleted(final RIBSupport<?, ?, ?, ?> ribSupport,
-            final YangInstanceIdentifier effectiveTablePath, final Collection<MapEntryNode> deletedRoutes) {
-        if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
+    private void onRoutesDeleted(final RIBSupport<?, ?> ribSupport, final YangInstanceIdentifier effectiveTablePath,
+            final Collection<MapEntryNode> deletedRoutes) {
+        if (RouteTargetConstrainSubsequentAddressFamily.VALUE.equals(ribSupport.getTablesKey().getSafi())) {
             final YangInstanceIdentifier routesPath = routeMapPath(ribSupport, effectiveTablePath);
             for (final MapEntryNode routeBefore : deletedRoutes) {
                 deleteRouteTarget(ribSupport, routesPath.node(routeBefore.getIdentifier()), routeBefore);
             }
-            this.rtMembershipsUpdated = true;
+            rtMembershipsUpdated = true;
         }
 
         final TablesKey tablesKey = ribSupport.getTablesKey();
         CountersUtil.add(prefixesInstalled.get(tablesKey), tablesKey, -deletedRoutes.size());
     }
 
-    private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
-            final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
+    private void processRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
+            final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route, final boolean longLivedStale) {
         LOG.debug("Process route {}", route.getIdentifier());
         final YangInstanceIdentifier routePath = ribSupport.routePath(routesPath, route.getIdentifier());
         switch (route.getModificationType()) {
@@ -436,27 +463,29 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
             case APPEARED:
             case SUBTREE_MODIFIED:
             case WRITE:
-                writeRoute(tx, ribSupport, routePath, route.getDataBefore(), route.getDataAfter().get());
+                writeRoute(tx, ribSupport, routePath, route.getDataBefore(), route.getDataAfter().orElseThrow(),
+                    longLivedStale);
+                break;
             default:
                 LOG.warn("Ignoring unhandled route {}", route);
                 break;
         }
     }
 
-    private void deleteRoute(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
-            final YangInstanceIdentifier routeIdPath, final NormalizedNode<?, ?> route) {
+    private void deleteRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
+            final YangInstanceIdentifier routeIdPath, final NormalizedNode route) {
         handleRouteTarget(ModificationType.DELETE, ribSupport, routeIdPath, route);
         tx.delete(LogicalDatastoreType.OPERATIONAL, routeIdPath);
         LOG.debug("Route deleted. routeId={}", routeIdPath);
         final TablesKey tablesKey = ribSupport.getTablesKey();
-        CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
+        CountersUtil.decrement(prefixesInstalled.get(tablesKey), tablesKey);
     }
 
-    private void writeRoute(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
-            final YangInstanceIdentifier routePath, final Optional<NormalizedNode<?, ?>> routeBefore,
-            final NormalizedNode<?, ?> routeAfter) {
+    private void writeRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
+            final YangInstanceIdentifier routePath, final Optional<NormalizedNode> routeBefore,
+            final NormalizedNode routeAfter, final boolean longLivedStale) {
         final TablesKey tablesKey = ribSupport.getTablesKey();
-        CountersUtil.increment(this.prefixesReceived.get(tablesKey), tablesKey);
+        CountersUtil.increment(prefixesReceived.get(tablesKey), tablesKey);
         // Lookup per-table attributes from RIBSupport
         final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(routeAfter,
             ribSupport.routeAttributesIdentifier()).orElse(null);
@@ -464,7 +493,6 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
         final Optional<Attributes> optEffAtt;
         // In case we want to add LLGR_STALE we do not process route through policies since it may be
         // considered as received with LLGR_STALE from peer which is not true.
-        final boolean longLivedStale = false;
         if (longLivedStale) {
             // LLGR procedures are in effect. If the route is tagged with NO_LLGR, it needs to be removed.
             final List<Communities> effCommunities = routeAttrs.getCommunities();
@@ -474,22 +502,20 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
             }
             optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs));
         } else {
-            final Class<? extends AfiSafiType> afiSafiType
-                = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get();
-            optEffAtt = this.ribPolicies
-                .applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType);
+            optEffAtt = ribPolicies.applyImportPolicies(peerImportParameters, routeAttrs,
+                verifyNotNull(tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey())));
         }
-        if (!optEffAtt.isPresent()) {
+        if (optEffAtt.isEmpty()) {
             deleteRoute(tx, ribSupport, routePath, routeBefore.orElse(null));
             return;
         }
         handleRouteTarget(ModificationType.WRITE, ribSupport, routePath, routeAfter);
         tx.put(LogicalDatastoreType.OPERATIONAL, routePath, routeAfter);
-        CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey);
+        CountersUtil.increment(prefixesInstalled.get(tablesKey), tablesKey);
 
-        final YangInstanceIdentifier attPath = routePath.node(ribSupport.routeAttributesIdentifier());
-        final Attributes attToStore = optEffAtt.get();
+        final Attributes attToStore = optEffAtt.orElseThrow();
         if (!attToStore.equals(routeAttrs)) {
+            final YangInstanceIdentifier attPath = routePath.node(ribSupport.routeAttributesIdentifier());
             final ContainerNode finalAttribute = ribSupport.attributeToContainerNode(attPath, attToStore);
             tx.put(LogicalDatastoreType.OPERATIONAL, attPath, finalAttribute);
         }
@@ -497,36 +523,35 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
 
     private void addRouteTarget(final RouteTargetConstrainRoute rtc) {
         final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
-        if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
-            this.rtCache.cacheRoute(rtc);
+        if (PeerRole.Ebgp != peerImportParameters.getFromPeerRole()) {
+            rtCache.cacheRoute(rtc);
         }
-        this.rtMemberships.add(rtMembership);
+        rtMemberships.add(rtMembership);
     }
 
-    private void deleteRouteTarget(final RIBSupport<?, ?, ?, ?> ribSupport, final YangInstanceIdentifier routeIdPath,
-            final NormalizedNode<?, ?> route) {
+    private void deleteRouteTarget(final RIBSupport<?, ?> ribSupport, final YangInstanceIdentifier routeIdPath,
+            final NormalizedNode route) {
         deleteRouteTarget((RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route));
     }
 
     private void deleteRouteTarget(final RouteTargetConstrainRoute rtc) {
         final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
-        if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
-            this.rtCache.uncacheRoute(rtc);
+        if (PeerRole.Ebgp != peerImportParameters.getFromPeerRole()) {
+            rtCache.uncacheRoute(rtc);
         }
-        this.rtMemberships.remove(rtMembership);
+        rtMemberships.remove(rtMembership);
     }
 
-    private void handleRouteTarget(final ModificationType modificationType, final RIBSupport<?, ?, ?, ?> ribSupport,
-            final YangInstanceIdentifier routeIdPath, final NormalizedNode<?, ?> route) {
-        if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
-            final RouteTargetConstrainRoute rtc =
-                (RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route);
+    private void handleRouteTarget(final ModificationType modificationType, final RIBSupport<?, ?> ribSupport,
+            final YangInstanceIdentifier routeIdPath, final NormalizedNode route) {
+        if (RouteTargetConstrainSubsequentAddressFamily.VALUE.equals(ribSupport.getTablesKey().getSafi())) {
+            final var rtc = (RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route);
             if (ModificationType.DELETE == modificationType) {
                 deleteRouteTarget(rtc);
             } else {
                 addRouteTarget(rtc);
             }
-            this.rtMembershipsUpdated = true;
+            rtMembershipsUpdated = true;
         }
     }
 
@@ -547,7 +572,7 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
             newCommunities = STALE_LLGR_COMMUNUTIES;
         }
 
-        return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329
+        return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120
                 .path.attributes.AttributesBuilder(attrs).setCommunities(newCommunities).build();
     }
 
@@ -561,34 +586,48 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn
     }
 
     private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
-        return this.effRibTables.node(TABLES_NID).node(tableKey);
+        return effRibTables.node(TABLES_NID).node(tableKey);
     }
 
-    private static YangInstanceIdentifier routeMapPath(final RIBSupport<?, ?, ?, ?> ribSupport,
+    private static YangInstanceIdentifier routeMapPath(final RIBSupport<?, ?> ribSupport,
             final YangInstanceIdentifier tablePath) {
         return concat(tablePath.node(ROUTES_NID), ribSupport.relativeRoutesPath());
     }
 
-    private static Optional<NormalizedNode<?, ?>> findRoutesMap(final RIBSupport<?, ?, ?, ?> ribSupport,
-            final Optional<NormalizedNode<?, ?>> optRoutes) {
+    private static Optional<NormalizedNode> findRoutesMap(final RIBSupport<?, ?> ribSupport,
+            final Optional<NormalizedNode> optRoutes) {
         return NormalizedNodes.findNode(optRoutes, ribSupport.relativeRoutesPath());
     }
 
-    private static ContainerNode extractContainer(final Optional<? extends NormalizedNode<?, ?>> optNode) {
-        final NormalizedNode<?, ?> node = optNode.get();
+    private static ContainerNode extractContainer(final NormalizedNode node) {
         verify(node instanceof ContainerNode, "Expected ContainerNode, got %s", node);
         return (ContainerNode) node;
     }
 
-    private static MapNode extractMap(final Optional<? extends NormalizedNode<?, ?>> optNode) {
-        final NormalizedNode<?, ?> node = optNode.get();
+    private static MapNode extractMap(final Optional<? extends NormalizedNode> optNode) {
+        final NormalizedNode node = optNode.orElseThrow();
         verify(node instanceof MapNode, "Expected MapNode, got %s", node);
         return (MapNode) node;
     }
 
-    private static MapEntryNode extractMapEntry(final Optional<? extends NormalizedNode<?, ?>> optNode) {
-        final NormalizedNode<?, ?> node = optNode.get();
-        verify(node instanceof MapEntryNode, "Expected MapEntryNode, got %s", node);
-        return (MapEntryNode) node;
+    private static boolean isLongLivedStale(final ContainerNode attributes) {
+        return NormalizedNodes.findNode(attributes, ADJRIBIN_ATTRIBUTES_AID, LLGR_STALE_NID).isPresent();
+    }
+
+    private static boolean isLongLivedStaleTable(final Optional<NormalizedNode> optTable) {
+        final Optional<NormalizedNode> optAttributes = NormalizedNodes.findNode(optTable, ATTRIBUTES_NID);
+        return optAttributes.isPresent() && isLongLivedStale(extractContainer(optAttributes.orElseThrow()));
+    }
+
+    private static ContainerNode effectiveAttributes(final ContainerNode attrs) {
+        final var upToDate = attrs.childByArg(UPTODATE_NID);
+        if (upToDate != null) {
+            final Object value = upToDate.body();
+            verify(value instanceof Boolean, "Expected boolean uptodate, got %s", value);
+            if ((Boolean) value) {
+                return RIBNormalizedNodes.UPTODATE_ATTRIBUTES;
+            }
+        }
+        return RIBNormalizedNodes.NOT_UPTODATE_ATTRIBUTES;
     }
 }