IPv6 CVR North-South Support
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NatUtil.java
index ab8ecfbfb636fdb93745b6747d25d1c0454592bb..d9708410cb36eaa96a0588892ca177b2ac5cbed5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * Copyright © 2016, 2018 Ericsson India Global Services Pvt Ltd. 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,
@@ -8,9 +8,16 @@
 
 package org.opendaylight.netvirt.natservice.internal;
 
+import static java.util.Collections.emptyList;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.controller.md.sal.binding.api.WriteTransaction.CREATE_MISSING_PARENTS;
+import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
+
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.math.BigInteger;
 import java.net.InetAddress;
@@ -31,20 +38,25 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
-import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
-import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
+import org.opendaylight.genius.infra.Datastore.Configuration;
+import org.opendaylight.genius.infra.Datastore.Operational;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.TypedReadTransaction;
+import org.opendaylight.genius.infra.TypedReadWriteTransaction;
+import org.opendaylight.genius.infra.TypedWriteTransaction;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
 import org.opendaylight.genius.mdsalutil.ActionInfo;
 import org.opendaylight.genius.mdsalutil.FlowEntity;
 import org.opendaylight.genius.mdsalutil.FlowEntityBuilder;
+import org.opendaylight.genius.mdsalutil.GroupEntity;
 import org.opendaylight.genius.mdsalutil.InstructionInfo;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.mdsalutil.MatchInfo;
+import org.opendaylight.genius.mdsalutil.MatchInfoBase;
 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
@@ -75,13 +87,14 @@ import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev14081
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefixBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
@@ -91,6 +104,12 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.BridgeRefInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntryKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceOutput;
@@ -103,8 +122,17 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
@@ -228,9 +256,12 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev14
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionRegLoadNodesNodeTableFlowApplyActionsCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigs;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -239,6 +270,9 @@ public final class NatUtil {
 
     private static String OF_URI_SEPARATOR = ":";
     private static final Logger LOG = LoggerFactory.getLogger(NatUtil.class);
+    private static final String OTHER_CONFIG_PARAMETERS_DELIMITER = ",";
+    private static final String OTHER_CONFIG_KEY_VALUE_DELIMITER = ":";
+    private static final String PROVIDER_MAPPINGS = "provider_mappings";
 
     private NatUtil() { }
 
@@ -263,7 +297,7 @@ public final class NatUtil {
     /*
         getVpnId() returns the VPN ID from the VPN name
      */
-    public static long getVpnId(DataBroker broker, String vpnName) {
+    public static long getVpnId(DataBroker broker, @Nullable String vpnName) {
         if (vpnName == null) {
             return NatConstants.INVALID_ID;
         }
@@ -285,6 +319,22 @@ public final class NatUtil {
         return vpnId;
     }
 
+    public static long getVpnId(TypedReadTransaction<Configuration> confTx, String vpnName) {
+        if (vpnName == null) {
+            return NatConstants.INVALID_ID;
+        }
+
+        try {
+            return confTx.read(getVpnInstanceToVpnIdIdentifier(vpnName)).get().toJavaUtil().map(
+                org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
+                    .VpnInstance::getVpnId).orElse(NatConstants.INVALID_ID);
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error retrieving VPN id for {}", vpnName, e);
+        }
+
+        return NatConstants.INVALID_ID;
+    }
+
     public static Long getNetworkVpnIdFromRouterId(DataBroker broker, long routerId) {
         //Get the external network ID from the ExternalRouter model
         Uuid networkId = NatUtil.getNetworkIdFromRouterId(broker, routerId);
@@ -325,7 +375,7 @@ public final class NatUtil {
             .child(InternalToExternalPortMap.class, new InternalToExternalPortMapKey(internalIp)).build();
     }
 
-    static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn
+    public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn
         .instance.to.vpn.id.VpnInstance> getVpnInstanceToVpnIdIdentifier(String vpnName) {
         return InstanceIdentifier.builder(VpnInstanceToVpnId.class)
             .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn
@@ -334,6 +384,7 @@ public final class NatUtil {
                     .instance.to.vpn.id.VpnInstanceKey(vpnName)).build();
     }
 
+    @Nullable
     static String getVpnInstanceFromVpnIdentifier(DataBroker broker, long vpnId) {
         InstanceIdentifier<VpnIds> id = InstanceIdentifier.builder(VpnIdToVpnInstance.class)
             .child(VpnIds.class, new VpnIdsKey(vpnId)).build();
@@ -360,6 +411,7 @@ public final class NatUtil {
                 + port;
     }
 
+    @Nullable
     static Uuid getNetworkIdFromRouterId(DataBroker broker, long routerId) {
         String routerName = getRouterName(broker, routerId);
         if (routerName == null) {
@@ -369,6 +421,7 @@ public final class NatUtil {
         return getNetworkIdFromRouterName(broker, routerName);
     }
 
+    @Nullable
     static Uuid getNetworkIdFromRouterName(DataBroker broker, String routerName) {
         if (routerName == null) {
             LOG.error("getNetworkIdFromRouterName - empty routerName received");
@@ -404,26 +457,50 @@ public final class NatUtil {
                 LogicalDatastoreType.CONFIGURATION, id).toJavaUtil().map(Routers::isEnableSnat).orElse(false);
     }
 
+    @Nullable
     public static Uuid getVpnIdfromNetworkId(DataBroker broker, Uuid networkId) {
         InstanceIdentifier<Networks> id = buildNetworkIdentifier(networkId);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                 LogicalDatastoreType.CONFIGURATION, id).toJavaUtil().map(Networks::getVpnid).orElse(null);
     }
 
+    @Nullable
+    public static Uuid getVpnIdfromNetworkId(TypedReadTransaction<Configuration> tx, Uuid networkId) {
+        try {
+            return tx.read(buildNetworkIdentifier(networkId)).get().toJavaUtil().map(Networks::getVpnid).orElse(null);
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error reading network VPN id for {}", networkId, e);
+            return null;
+        }
+    }
+
+    @Nullable
     public static ProviderTypes getProviderTypefromNetworkId(DataBroker broker, Uuid networkId) {
         InstanceIdentifier<Networks> id = buildNetworkIdentifier(networkId);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                 LogicalDatastoreType.CONFIGURATION, id).toJavaUtil().map(Networks::getProviderNetworkType).orElse(null);
     }
 
+    @Nullable
+    public static ProviderTypes getProviderTypefromNetworkId(TypedReadTransaction<Configuration> tx, Uuid networkId) {
+        InstanceIdentifier<Networks> id = buildNetworkIdentifier(networkId);
+        try {
+            return tx.read(id).get().toJavaUtil().map(Networks::getProviderNetworkType).orElse(null);
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error retrieving provider type for {}", networkId, e);
+            return null;
+        }
+    }
+
     @Nonnull
     public static List<Uuid> getRouterIdsfromNetworkId(DataBroker broker, Uuid networkId) {
         InstanceIdentifier<Networks> id = buildNetworkIdentifier(networkId);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                 LogicalDatastoreType.CONFIGURATION, id).toJavaUtil().map(Networks::getRouterIds).orElse(
-                Collections.emptyList());
+                emptyList());
     }
 
+    @Nullable
     static String getAssociatedExternalNetwork(DataBroker dataBroker, String routerId) {
         InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
         Optional<Routers> routerData =
@@ -444,6 +521,7 @@ public final class NatUtil {
             .child(Networks.class, new NetworksKey(networkId)).build();
     }
 
+    @Nullable
     public static BigInteger getPrimaryNaptfromRouterId(DataBroker broker, Long routerId) {
         // convert routerId to Name
         String routerName = getRouterName(broker, routerId);
@@ -454,6 +532,7 @@ public final class NatUtil {
         return getPrimaryNaptfromRouterName(broker, routerName);
     }
 
+    @Nullable
     public static BigInteger getPrimaryNaptfromRouterName(DataBroker broker, String routerName) {
         if (routerName == null) {
             LOG.error("getPrimaryNaptfromRouterName - empty routerName received");
@@ -466,15 +545,18 @@ public final class NatUtil {
     }
 
     public static InstanceIdentifier<RouterToNaptSwitch> buildNaptSwitchIdentifier(String routerId) {
-        InstanceIdentifier<RouterToNaptSwitch> rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class)
-            .child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerId)).build();
-        return rtrNaptSw;
+        return InstanceIdentifier.builder(NaptSwitches.class).child(RouterToNaptSwitch.class,
+            new RouterToNaptSwitchKey(routerId)).build();
     }
 
-    public static String getRouterName(DataBroker broker, Long routerId) {
-        InstanceIdentifier<RouterIds> id = buildRouterIdentifier(routerId);
+    public static Optional<NaptSwitches> getAllPrimaryNaptSwitches(DataBroker broker) {
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
-                LogicalDatastoreType.CONFIGURATION, id).toJavaUtil().map(RouterIds::getRouterName).orElse(null);
+                LogicalDatastoreType.CONFIGURATION, getNaptSwitchesIdentifier());
+    }
+
+    @Nullable
+    public static String getRouterName(DataBroker broker, Long routerId) {
+        return getVpnInstanceFromVpnIdentifier(broker, routerId);
     }
 
     static InstanceIdentifier<VpnInstanceOpDataEntry> getVpnInstanceOpDataIdentifier(String vrfId) {
@@ -504,6 +586,7 @@ public final class NatUtil {
             + ((rawIpAddress[2] & 0xFF) << 1 * 8) + (rawIpAddress[3] & 0xFF) & 0xffffffffL;
     }
 
+    @Nullable
     public static String getEndpointIpAddressForDPN(DataBroker broker, BigInteger dpnId) {
         String nextHopIp = null;
         InstanceIdentifier<DPNTEPsInfo> tunnelInfoId =
@@ -515,14 +598,14 @@ public final class NatUtil {
         if (tunnelInfo.isPresent()) {
             List<TunnelEndPoints> nexthopIpList = tunnelInfo.get().getTunnelEndPoints();
             if (nexthopIpList != null && !nexthopIpList.isEmpty()) {
-                nextHopIp = nexthopIpList.get(0).getIpAddress().getIpv4Address().getValue();
+                nextHopIp = nexthopIpList.get(0).getIpAddress().stringValue();
             }
         }
         return nextHopIp;
     }
 
+    @Nullable
     public static String getVpnRd(DataBroker broker, String vpnName) {
-
         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn
             .instance.to.vpn.id.VpnInstance> id = getVpnInstanceToVpnIdIdentifier(vpnName);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
@@ -531,6 +614,19 @@ public final class NatUtil {
                         .VpnInstance::getVrfId).orElse(null);
     }
 
+    @Nullable
+    public static String getVpnRd(TypedReadTransaction<Configuration> tx, String vpnName) {
+        try {
+            return tx.read(getVpnInstanceToVpnIdIdentifier(vpnName)).get().toJavaUtil().map(
+                org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
+                    .VpnInstance::getVrfId).orElse(null);
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error reading the VPN VRF id for {}", vpnName, e);
+            return null;
+        }
+    }
+
+    @Nullable
     public static IpPortExternal getExternalIpPortMap(DataBroker broker, Long routerId, String internalIpAddress,
                                                       String internalPort, NAPTEntryEvent.Protocol protocol) {
         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
@@ -555,12 +651,14 @@ public final class NatUtil {
             .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).build();
     }
 
+    @Nullable
     static VpnInterface getConfiguredVpnInterface(DataBroker broker, String interfaceName) {
         InstanceIdentifier<VpnInterface> interfaceId = getVpnInterfaceIdentifier(interfaceName);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                 LogicalDatastoreType.CONFIGURATION, interfaceId).orNull();
     }
 
+    @Nullable
     public static String getDpnFromNodeConnectorId(NodeConnectorId portId) {
         /*
          * NodeConnectorId is of form 'openflow:dpnid:portnum'
@@ -581,6 +679,7 @@ public final class NatUtil {
         return new BigInteger(getDpnFromNodeConnectorId(nodeConnectorId));
     }
 
+    @Nullable
     public static String getRouterIdfromVpnInstance(DataBroker broker, String vpnName) {
         // returns only router, attached to IPv4 networks
         InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
@@ -617,6 +716,7 @@ public final class NatUtil {
         return null;
     }
 
+    @Nullable
     static Uuid getVpnForRouter(DataBroker broker, String routerId) {
         Preconditions.checkNotNull(routerId, "dissociateRouter: routerId not found!");
         InstanceIdentifier<VpnMaps> vpnMapsIdentifier = InstanceIdentifier.builder(VpnMaps.class).build();
@@ -624,15 +724,23 @@ public final class NatUtil {
                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                         LogicalDatastoreType.CONFIGURATION, vpnMapsIdentifier);
         if (optionalVpnMaps.isPresent() && optionalVpnMaps.get().getVpnMap() != null) {
-            List<VpnMap> allMaps = optionalVpnMaps.get().getVpnMap();
-            for (VpnMap vpnMap: allMaps) {
+            for (VpnMap vpnMap : requireNonNullElse(optionalVpnMaps.get().getVpnMap(),
+                    Collections.<VpnMap>emptyList())) {
                 if (routerId.equals(vpnMap.getVpnId().getValue())) {
                     continue;
                 }
                 List<Uuid> routerIdsList = NeutronUtils.getVpnMapRouterIdsListUuid(vpnMap.getRouterIds());
-                if (routerIdsList == null || routerIdsList.isEmpty()) {
+                if (routerIdsList.isEmpty()) {
                     return null;
                 }
+                // Skip router vpnId fetching from internet BGP-VPN
+                if (vpnMap.getNetworkIds() != null && !vpnMap.getNetworkIds().isEmpty()) {
+                    // We only need to check the first network; if it’s not an external network there’s no
+                    // need to check the rest of the VPN’s network list
+                    if (isExternalNetwork(broker, vpnMap.getNetworkIds().iterator().next())) {
+                        continue;
+                    }
+                }
                 if (routerIdsList.contains(new Uuid(routerId))) {
                     return vpnMap.getVpnId();
                 }
@@ -649,6 +757,7 @@ public final class NatUtil {
                 NatConstants.INVALID_ID);
     }
 
+    @Nullable
     public static String getAssociatedVPN(DataBroker dataBroker, Uuid networkId) {
         Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
         if (vpnUuid == null) {
@@ -658,6 +767,16 @@ public final class NatUtil {
         return vpnUuid.getValue();
     }
 
+    @Nullable
+    public static String getAssociatedVPN(TypedReadTransaction<Configuration> tx, Uuid networkId) {
+        Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(tx, networkId);
+        if (vpnUuid == null) {
+            LOG.error("getAssociatedVPN : No VPN instance associated with ext network {}", networkId);
+            return null;
+        }
+        return vpnUuid.getValue();
+    }
+
     // TODO Clean up the exception handling
     @SuppressWarnings("checkstyle:IllegalCatch")
     public static void addPrefixToBGP(DataBroker broker,
@@ -665,11 +784,10 @@ public final class NatUtil {
                                       IFibManager fibManager,
                                       String vpnName,
                                       String rd,
-                                      Uuid subnetId,
                                       String prefix,
                                       String nextHopIp,
-                                      String parentVpnRd,
-                                      String macAddress,
+                                      @Nullable String parentVpnRd,
+                                      @Nullable String macAddress,
                                       long label,
                                       long l3vni,
                                       RouteOrigin origin,
@@ -683,8 +801,8 @@ public final class NatUtil {
                 return;
             }
 
-            addPrefixToInterface(broker, getVpnId(broker, vpnName), null /*interfaceName*/,prefix, dpId,
-                    subnetId, Prefixes.PrefixCue.Nat);
+            addPrefixToInterface(broker, getVpnId(broker, vpnName), null /*interfaceName*/,prefix, parentVpnRd,
+                dpId, Prefixes.PrefixCue.Nat);
             fibManager.addOrUpdateFibEntry(rd, macAddress, prefix,
                     Collections.singletonList(nextHopIp), VrfEntry.EncapType.Mplsgre, (int)label, l3vni /*l3vni*/,
                     null /*gatewayMacAddress*/, parentVpnRd, origin, null /*writeTxn*/);
@@ -702,15 +820,16 @@ public final class NatUtil {
         }
     }
 
-    static void addPrefixToInterface(DataBroker broker, long vpnId, String interfaceName, String ipPrefix,
-                                     BigInteger dpId, Uuid subnetId, Prefixes.PrefixCue prefixCue) {
+    static void addPrefixToInterface(DataBroker broker, long vpnId, @Nullable String interfaceName, String ipPrefix,
+                                     String networkId, BigInteger dpId, Prefixes.PrefixCue prefixCue) {
         InstanceIdentifier<Prefixes> prefixId = InstanceIdentifier.builder(PrefixToInterface.class)
                 .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface
                         .VpnIds.class, new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix
                         .to._interface.VpnIdsKey(vpnId))
                 .child(Prefixes.class, new PrefixesKey(ipPrefix)).build();
         PrefixesBuilder prefixBuilder = new PrefixesBuilder().setDpnId(dpId).setIpAddress(ipPrefix);
-        prefixBuilder.setVpnInterfaceName(interfaceName).setSubnetId(subnetId).setPrefixCue(prefixCue);
+        prefixBuilder.setVpnInterfaceName(interfaceName).setPrefixCue(prefixCue);
+        prefixBuilder.setNetworkId(new Uuid(networkId));
         try {
             SingleTransactionDataBroker.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, prefixId,
                     prefixBuilder.build());
@@ -752,7 +871,7 @@ public final class NatUtil {
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
                 LogicalDatastoreType.CONFIGURATION,
                 buildSnatIntIpPortIdentifier(routerId, internalIpAddress, protocolType)).toJavaUtil().map(
-                IntIpProtoType::getPorts).orElse(Collections.emptyList());
+                IntIpProtoType::getPorts).orElse(emptyList());
     }
 
     public static InstanceIdentifier<IntIpProtoType> buildSnatIntIpPortIdentifier(Long routerId,
@@ -833,6 +952,7 @@ public final class NatUtil {
         }
     }
 
+    @Nullable
     public static IpPortMapping getIportMapping(DataBroker broker, long routerId) {
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                 LogicalDatastoreType.CONFIGURATION, getIportMappingIdentifier(routerId)).orNull();
@@ -845,12 +965,11 @@ public final class NatUtil {
 
     public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt
         .natservice.rev160111.intext.ip.map.IpMapping> getIpMappingBuilder(Long routerId) {
-        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111
-            .intext.ip.map.IpMapping> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
+        return InstanceIdentifier.builder(IntextIpMap.class)
             .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map
                 .IpMapping.class, new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111
-                .intext.ip.map.IpMappingKey(routerId)).build();
-        return idBuilder;
+                .intext.ip.map.IpMappingKey(routerId))
+            .build();
     }
 
     @Nonnull
@@ -862,8 +981,7 @@ public final class NatUtil {
         // Ensure there are no duplicates
         Collection<String> externalIps = new HashSet<>();
         if (ipMappingOptional.isPresent()) {
-            List<IpMap> ipMaps = ipMappingOptional.get().getIpMap();
-            for (IpMap ipMap : ipMaps) {
+            for (IpMap ipMap : requireNonNullElse(ipMappingOptional.get().getIpMap(), Collections.<IpMap>emptyList())) {
                 externalIps.add(ipMap.getExternalIp());
             }
         }
@@ -877,7 +995,7 @@ public final class NatUtil {
             return NatUtil.getIpsListFromExternalIps(routerData.getExternalIps());
         }
 
-        return Collections.emptyList();
+        return emptyList();
     }
 
     @Nonnull
@@ -888,14 +1006,14 @@ public final class NatUtil {
                         LogicalDatastoreType.OPERATIONAL, getIpMappingBuilder(routerId));
         Map<String, Long> externalIpsLabel = new HashMap<>();
         if (ipMappingOptional.isPresent()) {
-            List<IpMap> ipMaps = ipMappingOptional.get().getIpMap();
-            for (IpMap ipMap : ipMaps) {
+            for (IpMap ipMap : requireNonNullElse(ipMappingOptional.get().getIpMap(), Collections.<IpMap>emptyList())) {
                 externalIpsLabel.put(ipMap.getExternalIp(), ipMap.getLabel());
             }
         }
         return externalIpsLabel;
     }
 
+    @Nullable
     public static String getLeastLoadedExternalIp(DataBroker dataBroker, long segmentId) {
         String leastLoadedExternalIp = null;
         InstanceIdentifier<ExternalCounters> id =
@@ -905,9 +1023,9 @@ public final class NatUtil {
             MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
         if (externalCountersData.isPresent()) {
             ExternalCounters externalCounter = externalCountersData.get();
-            List<ExternalIpCounter> externalIpCounterList = externalCounter.getExternalIpCounter();
             short countOfLstLoadExtIp = 32767;
-            for (ExternalIpCounter externalIpCounter : externalIpCounterList) {
+            for (ExternalIpCounter externalIpCounter : requireNonNullElse(externalCounter.getExternalIpCounter(),
+                    Collections.<ExternalIpCounter>emptyList())) {
                 String curExternalIp = externalIpCounter.getExternalIp();
                 short countOfCurExtIp = externalIpCounter.getCounter();
                 if (countOfCurExtIp < countOfLstLoadExtIp) {
@@ -920,6 +1038,7 @@ public final class NatUtil {
     }
 
     @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
+    @Nullable
     public static String[] getSubnetIpAndPrefix(DataBroker dataBroker, Uuid subnetId) {
         String subnetIP = getSubnetIp(dataBroker, subnetId);
         if (subnetIP != null) {
@@ -929,6 +1048,7 @@ public final class NatUtil {
         return null;
     }
 
+    @Nonnull
     public static String[] getSubnetIpAndPrefix(String subnetString) {
         String[] subnetSplit = subnetString.split("/");
         String subnetIp = subnetSplit[0];
@@ -939,6 +1059,7 @@ public final class NatUtil {
         return new String[] {subnetIp, subnetPrefix};
     }
 
+    @Nullable
     public static String getSubnetIp(DataBroker dataBroker, Uuid subnetId) {
         InstanceIdentifier<Subnetmap> subnetmapId = InstanceIdentifier
             .builder(Subnetmaps.class)
@@ -946,7 +1067,6 @@ public final class NatUtil {
             .build();
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
                 LogicalDatastoreType.CONFIGURATION, subnetmapId).toJavaUtil().map(Subnetmap::getSubnetIp).orElse(null);
-
     }
 
     public static String[] getExternalIpAndPrefix(String leastLoadedExtIpAddr) {
@@ -968,8 +1088,8 @@ public final class NatUtil {
                         LogicalDatastoreType.OPERATIONAL, id);
         List<BigInteger> dpns = new ArrayList<>();
         if (routerDpnListData.isPresent()) {
-            List<DpnVpninterfacesList> dpnVpninterfacesList = routerDpnListData.get().getDpnVpninterfacesList();
-            for (DpnVpninterfacesList dpnVpnInterface : dpnVpninterfacesList) {
+            for (DpnVpninterfacesList dpnVpnInterface : requireNonNullElse(
+                    routerDpnListData.get().getDpnVpninterfacesList(), Collections.<DpnVpninterfacesList>emptyList())) {
                 dpns.add(dpnVpnInterface.getDpnId());
             }
         }
@@ -985,6 +1105,7 @@ public final class NatUtil {
         return bgpVpnId;
     }
 
+    @Nullable
     static org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.router.interfaces
         .RouterInterface getConfiguredRouterInterface(DataBroker broker, String interfaceName) {
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
@@ -1001,8 +1122,8 @@ public final class NatUtil {
                     .RouterInterfaceKey(interfaceName)).build();
     }
 
-    public static void addToNeutronRouterDpnsMap(DataBroker broker, String routerName, String interfaceName,
-            BigInteger dpId , WriteTransaction writeOperTxn) {
+    public static void addToNeutronRouterDpnsMap(String routerName, String interfaceName, BigInteger dpId,
+        TypedReadWriteTransaction<Operational> operTx) throws ExecutionException, InterruptedException {
 
         if (dpId.equals(BigInteger.ZERO)) {
             LOG.warn("addToNeutronRouterDpnsMap : Could not retrieve dp id for interface {} "
@@ -1014,9 +1135,7 @@ public final class NatUtil {
                 + "ODL-L3VPN : NeutronRouterDpn map", routerName, dpId, interfaceName);
         InstanceIdentifier<DpnVpninterfacesList> dpnVpnInterfacesListIdentifier = getRouterDpnId(routerName, dpId);
 
-        Optional<DpnVpninterfacesList> optionalDpnVpninterfacesList =
-                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
-                        LogicalDatastoreType.OPERATIONAL, dpnVpnInterfacesListIdentifier);
+        Optional<DpnVpninterfacesList> optionalDpnVpninterfacesList = operTx.read(dpnVpnInterfacesListIdentifier).get();
         org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns
             .router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces routerInterface =
             new RouterInterfacesBuilder().withKey(new RouterInterfacesKey(interfaceName))
@@ -1024,10 +1143,10 @@ public final class NatUtil {
         if (optionalDpnVpninterfacesList.isPresent()) {
             LOG.debug("addToNeutronRouterDpnsMap : RouterDpnList already present for the Router {} and DPN {} for the "
                     + "Interface {} in the ODL-L3VPN : NeutronRouterDpn map", routerName, dpId, interfaceName);
-            writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL, dpnVpnInterfacesListIdentifier
+            operTx.merge(dpnVpnInterfacesListIdentifier
                     .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router
                             .dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces.class,
-                            new RouterInterfacesKey(interfaceName)), routerInterface, true);
+                            new RouterInterfacesKey(interfaceName)), routerInterface, CREATE_MISSING_PARENTS);
         } else {
             LOG.debug("addToNeutronRouterDpnsMap : Building new RouterDpnList for the Router {} and DPN {} for the "
                     + "Interface {} in the ODL-L3VPN : NeutronRouterDpn map", routerName, dpId, interfaceName);
@@ -1039,14 +1158,12 @@ public final class NatUtil {
             routerInterfaces.add(routerInterface);
             dpnVpnList.setRouterInterfaces(routerInterfaces);
             routerDpnListBuilder.setDpnVpninterfacesList(Collections.singletonList(dpnVpnList.build()));
-            writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL,
-                    getRouterId(routerName),
-                    routerDpnListBuilder.build(), true);
+            operTx.merge(getRouterId(routerName), routerDpnListBuilder.build(), CREATE_MISSING_PARENTS);
         }
     }
 
-    public static void addToDpnRoutersMap(DataBroker broker, String routerName, String interfaceName,
-            BigInteger dpId, WriteTransaction writeOperTxn) {
+    public static void addToDpnRoutersMap(String routerName, String interfaceName, BigInteger dpId,
+        TypedReadWriteTransaction<Operational> operTx) throws ExecutionException, InterruptedException {
         if (dpId.equals(BigInteger.ZERO)) {
             LOG.error("addToDpnRoutersMap : Could not retrieve dp id for interface {} to handle router {} "
                     + "association model", interfaceName, routerName);
@@ -1057,20 +1174,18 @@ public final class NatUtil {
                 + "DPNRouters map", dpId, routerName, interfaceName);
         InstanceIdentifier<DpnRoutersList> dpnRoutersListIdentifier = getDpnRoutersId(dpId);
 
-        Optional<DpnRoutersList> optionalDpnRoutersList =
-                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
-                        LogicalDatastoreType.OPERATIONAL, dpnRoutersListIdentifier);
+        Optional<DpnRoutersList> optionalDpnRoutersList = operTx.read(dpnRoutersListIdentifier).get();
 
         if (optionalDpnRoutersList.isPresent()) {
             RoutersList routersList = new RoutersListBuilder().withKey(new RoutersListKey(routerName))
                     .setRouter(routerName).build();
-            List<RoutersList> routersListFromDs = optionalDpnRoutersList.get().getRoutersList();
+            List<RoutersList> routersListFromDs = requireNonNullElse(optionalDpnRoutersList.get().getRoutersList(),
+                emptyList());
             if (!routersListFromDs.contains(routersList)) {
                 LOG.debug("addToDpnRoutersMap : Router {} not present for the DPN {}"
                         + " in the ODL-L3VPN : DPNRouters map", routerName, dpId);
-                writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL,
-                        dpnRoutersListIdentifier
-                        .child(RoutersList.class, new RoutersListKey(routerName)), routersList, true);
+                operTx.merge(dpnRoutersListIdentifier
+                        .child(RoutersList.class, new RoutersListKey(routerName)), routersList, CREATE_MISSING_PARENTS);
             } else {
                 LOG.debug("addToDpnRoutersMap : Router {} already mapped to the DPN {} in the ODL-L3VPN : "
                         + "DPNRouters map", routerName, dpId);
@@ -1083,127 +1198,64 @@ public final class NatUtil {
             RoutersListBuilder routersListBuilder = new RoutersListBuilder();
             routersListBuilder.setRouter(routerName);
             dpnRoutersListBuilder.setRoutersList(Collections.singletonList(routersListBuilder.build()));
-            writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL,
-                    getDpnRoutersId(dpId),
-                    dpnRoutersListBuilder.build(), true);
+            operTx.merge(getDpnRoutersId(dpId), dpnRoutersListBuilder.build(), CREATE_MISSING_PARENTS);
         }
     }
 
-
-    public static void removeFromNeutronRouterDpnsMap(DataBroker broker, String routerName, String interfaceName,
-                                               BigInteger dpId, WriteTransaction writeOperTxn) {
-        if (dpId.equals(BigInteger.ZERO)) {
-            LOG.error("removeFromNeutronRouterDpnsMap : Could not retrieve dp id for interface {} to handle router {} "
-                    + "dissociation model", interfaceName, routerName);
-            return;
-        }
-        InstanceIdentifier<DpnVpninterfacesList> routerDpnListIdentifier = getRouterDpnId(routerName, dpId);
-        Optional<DpnVpninterfacesList> optionalRouterDpnList =
-                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
-                        LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
-        if (optionalRouterDpnList.isPresent()) {
-            List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router
-                .dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces> routerInterfaces =
-                optionalRouterDpnList.get().getRouterInterfaces();
-            org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router
-                .dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces routerInterface =
-                new RouterInterfacesBuilder().withKey(new RouterInterfacesKey(interfaceName))
-                    .setInterface(interfaceName).build();
-            if (routerInterfaces != null && routerInterfaces.remove(routerInterface)) {
-                if (routerInterfaces.isEmpty()) {
-                    writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
-                } else {
-                    writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
-                        org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router
-                            .dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces.class,
-                        new RouterInterfacesKey(interfaceName)));
-                }
-            }
-        }
-    }
-
-    public static void removeFromNeutronRouterDpnsMap(DataBroker broker, String routerName,
-                                               BigInteger dpId, WriteTransaction writeOperTxn) {
+    public static void removeFromNeutronRouterDpnsMap(String routerName, BigInteger dpId,
+        TypedReadWriteTransaction<Operational> operTx) throws ExecutionException, InterruptedException {
         if (dpId.equals(BigInteger.ZERO)) {
             LOG.warn("removeFromNeutronRouterDpnsMap : DPN ID is invalid for the router {} ", routerName);
             return;
         }
 
         InstanceIdentifier<DpnVpninterfacesList> routerDpnListIdentifier = getRouterDpnId(routerName, dpId);
-        Optional<DpnVpninterfacesList> optionalRouterDpnList =
-                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
-                        LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
+        Optional<DpnVpninterfacesList> optionalRouterDpnList = operTx.read(routerDpnListIdentifier).get();
         if (optionalRouterDpnList.isPresent()) {
             LOG.debug("removeFromNeutronRouterDpnsMap : Removing the dpn-vpninterfaces-list from the "
                     + "odl-l3vpn:neutron-router-dpns model for the router {}", routerName);
-            writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
+            operTx.delete(routerDpnListIdentifier);
         } else {
             LOG.debug("removeFromNeutronRouterDpnsMap : dpn-vpninterfaces-list does not exist in the "
                     + "odl-l3vpn:neutron-router-dpns model for the router {}", routerName);
         }
     }
 
-    public static void removeFromNeutronRouterDpnsMap(DataBroker broker, String routerName, String vpnInterfaceName,
-                                               OdlInterfaceRpcService ifaceMgrRpcService,
-                                               WriteTransaction writeOperTxn) {
-        BigInteger dpId = getDpnForInterface(ifaceMgrRpcService, vpnInterfaceName);
-        if (dpId.equals(BigInteger.ZERO)) {
-            LOG.debug("removeFromNeutronRouterDpnsMap : Could not retrieve dp id for interface {} to handle router {}"
-                    + " dissociation model", vpnInterfaceName, routerName);
-            return;
-        }
+    public static void removeFromNeutronRouterDpnsMap(String routerName, String vpnInterfaceName,
+         BigInteger dpId, @Nonnull TypedReadWriteTransaction<Operational> operTx) {
         InstanceIdentifier<DpnVpninterfacesList> routerDpnListIdentifier = getRouterDpnId(routerName, dpId);
-        Optional<DpnVpninterfacesList> optionalRouterDpnList =
-                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
-                        LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
+        Optional<DpnVpninterfacesList> optionalRouterDpnList;
+        try {
+            optionalRouterDpnList = operTx.read(routerDpnListIdentifier).get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error reading the router DPN list for {}", routerDpnListIdentifier, e);
+            optionalRouterDpnList = Optional.absent();
+        }
         if (optionalRouterDpnList.isPresent()) {
             List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns
-                .router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces> routerInterfaces =
-                optionalRouterDpnList.get().getRouterInterfaces();
+                    .router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces> routerInterfaces =
+                    optionalRouterDpnList.get().getRouterInterfaces();
             org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn
-                .list.dpn.vpninterfaces.list.RouterInterfaces routerInterface =
-                new RouterInterfacesBuilder().withKey(new RouterInterfacesKey(vpnInterfaceName))
-                    .setInterface(vpnInterfaceName).build();
+                    .list.dpn.vpninterfaces.list.RouterInterfaces routerInterface =
+                    new RouterInterfacesBuilder().withKey(new RouterInterfacesKey(vpnInterfaceName))
+                            .setInterface(vpnInterfaceName).build();
 
             if (routerInterfaces != null && routerInterfaces.remove(routerInterface)) {
                 if (routerInterfaces.isEmpty()) {
-                    if (writeOperTxn != null) {
-                        writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
-                    } else {
-                        MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
-                    }
+                    operTx.delete(routerDpnListIdentifier);
                 } else {
-                    if (writeOperTxn != null) {
-                        writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
+                    operTx.delete(routerDpnListIdentifier.child(
                             org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router
-                                .dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces.class,
-                            new RouterInterfacesKey(vpnInterfaceName)));
-                    } else {
-                        MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
-                            org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron
-                                .router.dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces.class,
+                                    .dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces.class,
                             new RouterInterfacesKey(vpnInterfaceName)));
-                    }
                 }
             }
         }
     }
 
     public static void removeFromDpnRoutersMap(DataBroker broker, String routerName, String vpnInterfaceName,
-                                        OdlInterfaceRpcService ifaceMgrRpcService, WriteTransaction writeOperTxn) {
-        BigInteger dpId = getDpnForInterface(ifaceMgrRpcService, vpnInterfaceName);
-        if (dpId.equals(BigInteger.ZERO)) {
-            LOG.debug("removeFromDpnRoutersMap : removeFromDpnRoutersMap() : "
-                + "Could not retrieve DPN ID for interface {} to handle router {} dissociation model",
-                vpnInterfaceName, routerName);
-            return;
-        }
-        removeFromDpnRoutersMap(broker, routerName, vpnInterfaceName, dpId, ifaceMgrRpcService, writeOperTxn);
-    }
-
-    static void removeFromDpnRoutersMap(DataBroker broker, String routerName, String vpnInterfaceName,
-                                        BigInteger curDpnId,
-                                        OdlInterfaceRpcService ifaceMgrRpcService, WriteTransaction writeOperTxn) {
+        BigInteger curDpnId, OdlInterfaceRpcService ifaceMgrRpcService, TypedReadWriteTransaction<Operational> operTx)
+        throws ExecutionException, InterruptedException {
         /*
             1) Get the DpnRoutersList for the DPN.
             2) Get the RoutersList identifier for the DPN and router.
@@ -1217,9 +1269,7 @@ public final class NatUtil {
 
         //Get the dpn-routers-list instance for the current DPN.
         InstanceIdentifier<DpnRoutersList> dpnRoutersListIdentifier = getDpnRoutersId(curDpnId);
-        Optional<DpnRoutersList> dpnRoutersListData =
-                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
-                        LogicalDatastoreType.OPERATIONAL, dpnRoutersListIdentifier);
+        Optional<DpnRoutersList> dpnRoutersListData = operTx.read(dpnRoutersListIdentifier).get();
 
         if (dpnRoutersListData == null || !dpnRoutersListData.isPresent()) {
             LOG.error("removeFromDpnRoutersMap : dpn-routers-list is not present for DPN {} "
@@ -1229,9 +1279,7 @@ public final class NatUtil {
 
         //Get the routers-list instance for the router on the current DPN only
         InstanceIdentifier<RoutersList> routersListIdentifier = getRoutersList(curDpnId, routerName);
-        Optional<RoutersList> routersListData =
-                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
-                        LogicalDatastoreType.OPERATIONAL, routersListIdentifier);
+        Optional<RoutersList> routersListData = operTx.read(routersListIdentifier).get();
 
         if (routersListData == null || !routersListData.isPresent()) {
             LOG.error("removeFromDpnRoutersMap : routers-list is not present for the DPN {} "
@@ -1253,7 +1301,7 @@ public final class NatUtil {
             LOG.debug("removeFromDpnRoutersMap : Unable to get the routers list for the DPN {}. Possibly all subnets "
                     + "removed from router {} OR Router {} has been deleted. Hence DPN router model WILL be cleared ",
                 curDpnId, routerName, routerName);
-            writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routersListIdentifier);
+            operTx.delete(routersListIdentifier);
             return;
         }
 
@@ -1287,7 +1335,7 @@ public final class NatUtil {
         LOG.debug("removeFromDpnRoutersMap : Router {} is present in the DPN {} only through the interface {} "
             + "Hence DPN router model WILL be cleared. Possibly last VM for the router "
             + "deleted in the DPN", routerName, curDpnId, vpnInterfaceName);
-        writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routersListIdentifier);
+        operTx.delete(routersListIdentifier);
     }
 
     private static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn
@@ -1319,10 +1367,10 @@ public final class NatUtil {
             if (dpIdResult.isSuccessful()) {
                 nodeId = dpIdResult.getResult().getDpid();
             } else {
-                LOG.debug("removeFromDpnRoutersMap : Could not retrieve DPN Id for interface {}", ifName);
+                LOG.debug("getDpnForInterface : Could not retrieve DPN Id for interface {}", ifName);
             }
         } catch (NullPointerException | InterruptedException | ExecutionException e) {
-            LOG.error("removeFromDpnRoutersMap : Exception when getting dpn for interface {}", ifName, e);
+            LOG.error("getDpnForInterface : Exception when getting dpn for interface {}", ifName, e);
         }
         return nodeId;
     }
@@ -1340,7 +1388,7 @@ public final class NatUtil {
     public static List<ActionInfo> getEgressActionsForInterface(OdlInterfaceRpcService odlInterfaceRpcService,
                                                                 ItmRpcService itmRpcService,
                                                                 IInterfaceManager interfaceManager,
-                                                                String ifName, Long tunnelKey, int pos,
+                                                                String ifName, @Nullable Long tunnelKey, int pos,
                                                                 boolean internalTunnelInterface) {
         LOG.debug("getEgressActionsForInterface : called for interface {}", ifName);
         GetEgressActionsForInterfaceInputBuilder egressActionsIfmBuilder =
@@ -1353,7 +1401,7 @@ public final class NatUtil {
         } //init builders, ITM/IFM rpc can be called based on type of interface
 
         try {
-            List<Action> actions = Collections.emptyList();
+            List<Action> actions = emptyList();
             if (interfaceManager.isItmDirectTunnelsEnabled() && internalTunnelInterface) {
                 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
                         itmRpcService.getEgressActionsForTunnel(egressActionsItmBuilder.build()).get();
@@ -1374,7 +1422,7 @@ public final class NatUtil {
                 }
             }
             List<ActionInfo> listActionInfo = new ArrayList<>();
-            for (Action action : actions) {
+            for (Action action : requireNonNullElse(actions, Collections.<Action>emptyList())) {
                 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
                     actionClass = action.getAction();
                 if (actionClass instanceof OutputActionCase) {
@@ -1403,9 +1451,10 @@ public final class NatUtil {
             LOG.error("Exception when egress actions for interface {}", ifName, e);
         }
         LOG.error("Error when getting egress actions for interface {}", ifName);
-        return Collections.emptyList();
+        return emptyList();
     }
 
+    @Nullable
     public static Port getNeutronPortForRouterGetewayIp(DataBroker broker, IpAddress targetIP) {
         return getNeutronPortForIp(broker, targetIP, NeutronConstants.DEVICE_OWNER_GATEWAY_INF);
     }
@@ -1422,14 +1471,14 @@ public final class NatUtil {
 
         if (!portsOptional.isPresent() || portsOptional.get().getPort() == null) {
             LOG.error("getNeutronPorts : No neutron ports found");
-            return Collections.emptyList();
+            return emptyList();
         }
 
         return portsOptional.get().getPort();
     }
 
-    public static Port getNeutronPortForIp(DataBroker broker,
-                                           IpAddress targetIP, String deviceType) {
+    @Nullable
+    public static Port getNeutronPortForIp(DataBroker broker, IpAddress targetIP, String deviceType) {
         List<Port> ports = getNeutronPorts(
             broker);
 
@@ -1446,12 +1495,13 @@ public final class NatUtil {
         return null;
     }
 
+    @Nullable
     public static Uuid getSubnetIdForFloatingIp(Port port, IpAddress targetIP) {
         if (port == null) {
             LOG.error("getSubnetIdForFloatingIp : port is null");
             return null;
         }
-        for (FixedIps ip : port.getFixedIps()) {
+        for (FixedIps ip : requireNonNullElse(port.getFixedIps(), Collections.<FixedIps>emptyList())) {
             if (Objects.equals(ip.getIpAddress(), targetIP)) {
                 return ip.getSubnetId();
             }
@@ -1460,6 +1510,7 @@ public final class NatUtil {
         return null;
     }
 
+    @Nullable
     public static Subnetmap getSubnetMap(DataBroker broker, Uuid subnetId) {
         InstanceIdentifier<Subnetmap> subnetmapId = InstanceIdentifier.builder(Subnetmaps.class)
             .child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
@@ -1473,9 +1524,10 @@ public final class NatUtil {
             .child(NetworkMap.class, new NetworkMapKey(networkId)).build();
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                 LogicalDatastoreType.CONFIGURATION, id).toJavaUtil().map(NetworkMap::getSubnetIdList).orElse(
-                Collections.emptyList());
+                emptyList());
     }
 
+    @Nullable
     public static String getSubnetGwMac(DataBroker broker, Uuid subnetId, String vpnName) {
         if (subnetId == null) {
             LOG.error("getSubnetGwMac : subnetID is null");
@@ -1527,7 +1579,7 @@ public final class NatUtil {
     }
 
     public static boolean isIPv6Subnet(String prefix) {
-        return new IpPrefix(prefix.toCharArray()).getIpv6Prefix() != null;
+        return IpPrefixBuilder.getDefaultInstance(prefix).getIpv6Prefix() != null;
     }
 
     static InstanceIdentifier<DpnRoutersList> getDpnRoutersId(BigInteger dpnId) {
@@ -1546,6 +1598,7 @@ public final class NatUtil {
             .child(RouterDpnList.class, new RouterDpnListKey(routerName)).build();
     }
 
+    @Nullable
     protected static String getFloatingIpPortMacFromFloatingIpId(DataBroker broker, Uuid floatingIpId) {
         InstanceIdentifier<FloatingIpIdToPortMapping> id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
@@ -1553,6 +1606,19 @@ public final class NatUtil {
                 FloatingIpIdToPortMapping::getFloatingIpPortMacAddress).orElse(null);
     }
 
+    @Nullable
+    protected static String getFloatingIpPortMacFromFloatingIpId(TypedReadTransaction<Configuration> confTx,
+        Uuid floatingIpId) {
+        try {
+            return confTx.read(buildfloatingIpIdToPortMappingIdentifier(floatingIpId)).get().toJavaUtil().map(
+                FloatingIpIdToPortMapping::getFloatingIpPortMacAddress).orElse(null);
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error reading the floating IP port MAC for {}", floatingIpId, e);
+            return null;
+        }
+    }
+
+    @Nullable
     protected static Uuid getFloatingIpPortSubnetIdFromFloatingIpId(DataBroker broker, Uuid floatingIpId) {
         InstanceIdentifier<FloatingIpIdToPortMapping> id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
@@ -1560,11 +1626,24 @@ public final class NatUtil {
                 FloatingIpIdToPortMapping::getFloatingIpPortSubnetId).orElse(null);
     }
 
+    @Nullable
+    protected static Uuid getFloatingIpPortSubnetIdFromFloatingIpId(TypedReadTransaction<Configuration> confTx,
+        Uuid floatingIpId) {
+        try {
+            return confTx.read(buildfloatingIpIdToPortMappingIdentifier(floatingIpId)).get().toJavaUtil().map(
+                FloatingIpIdToPortMapping::getFloatingIpPortSubnetId).orElse(null);
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error reading the floating IP port subnet for {}", floatingIpId, e);
+            return null;
+        }
+    }
+
     static InstanceIdentifier<FloatingIpIdToPortMapping> buildfloatingIpIdToPortMappingIdentifier(Uuid floatingIpId) {
         return InstanceIdentifier.builder(FloatingIpPortInfo.class).child(FloatingIpIdToPortMapping.class, new
             FloatingIpIdToPortMappingKey(floatingIpId)).build();
     }
 
+    @Nullable
     static Interface getInterfaceStateFromOperDS(DataBroker dataBroker, String interfaceName) {
         InstanceIdentifier<Interface> ifStateId =
             buildStateInterfaceId(interfaceName);
@@ -1579,16 +1658,26 @@ public final class NatUtil {
                 .child(Interface.class,
                     new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
                         .interfaces.state.InterfaceKey(interfaceName));
-        InstanceIdentifier<Interface> id = idBuilder.build();
-        return id;
+        return idBuilder.build();
     }
 
+    @Nullable
     public static Routers getRoutersFromConfigDS(DataBroker dataBroker, String routerName) {
         InstanceIdentifier<Routers> routerIdentifier = NatUtil.buildRouterIdentifier(routerName);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
                 LogicalDatastoreType.CONFIGURATION, routerIdentifier).orNull();
     }
 
+    @Nullable
+    public static Routers getRoutersFromConfigDS(TypedReadTransaction<Configuration> confTx, String routerName) {
+        try {
+            return confTx.read(NatUtil.buildRouterIdentifier(routerName)).get().orNull();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error reading router {}", routerName, e);
+            return null;
+        }
+    }
+
     static void createRouterIdsConfigDS(DataBroker dataBroker, long routerId, String routerName) {
         if (routerId == NatConstants.INVALID_ID) {
             LOG.error("createRouterIdsConfigDS : invalid routerId for routerName {}", routerName);
@@ -1599,6 +1688,7 @@ public final class NatUtil {
         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, buildRouterIdentifier(routerId), rtrs);
     }
 
+    @Nullable
     static FlowEntity buildDefaultNATFlowEntityForExternalSubnet(BigInteger dpId, long vpnId, String subnetId,
             IdManagerService idManager) {
         InetAddress defaultIP = null;
@@ -1621,12 +1711,12 @@ public final class NatUtil {
         actionsInfo.add(new ActionGroup(groupId));
         String flowRef = getFlowRef(dpId, NwConstants.L3_FIB_TABLE, defaultIP, vpnId);
         instructions.add(new InstructionApplyActions(actionsInfo));
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_FIB_TABLE, flowRef,
+        return MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_FIB_TABLE, flowRef,
                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
                 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
-        return flowEntity;
     }
 
+    @Nullable
     static String getExtGwMacAddFromRouterId(DataBroker broker, long routerId) {
         String routerName = getRouterName(broker, routerId);
         if (routerName == null) {
@@ -1636,12 +1726,23 @@ public final class NatUtil {
         return getExtGwMacAddFromRouterName(broker, routerName);
     }
 
+    @Nullable
     static String getExtGwMacAddFromRouterName(DataBroker broker, String routerName) {
         InstanceIdentifier<Routers> id = buildRouterIdentifier(routerName);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                 LogicalDatastoreType.CONFIGURATION, id).toJavaUtil().map(Routers::getExtGwMacAddress).orElse(null);
     }
 
+    @Nullable
+    static String getExtGwMacAddFromRouterName(TypedReadTransaction<Configuration> tx, String routerName) {
+        try {
+            return tx.read(buildRouterIdentifier(routerName)).get().toJavaUtil().map(
+                Routers::getExtGwMacAddress).orElse(null);
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error retrieving external gateway MAC address for router {}", routerName, e);
+            return null;
+        }
+    }
 
     static InstanceIdentifier<Router> buildNeutronRouterIdentifier(Uuid routerUuid) {
         InstanceIdentifier<Router> routerInstanceIdentifier = InstanceIdentifier.create(Neutron.class)
@@ -1650,6 +1751,7 @@ public final class NatUtil {
         return routerInstanceIdentifier;
     }
 
+    @Nullable
     public static String getNeutronRouterNamebyUuid(DataBroker broker, Uuid routerUuid) {
         InstanceIdentifier<Router> neutronRouterIdentifier = NatUtil.buildNeutronRouterIdentifier(routerUuid);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
@@ -1662,7 +1764,7 @@ public final class NatUtil {
         InstanceIdentifier<RouterPorts> routerPortsIdentifier = getRouterPortsId(routerUuid.getValue());
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                 LogicalDatastoreType.CONFIGURATION,
-                routerPortsIdentifier).toJavaUtil().map(RouterPorts::getPorts).orElse(Collections.emptyList());
+                routerPortsIdentifier).toJavaUtil().map(RouterPorts::getPorts).orElse(emptyList());
     }
 
     @Nonnull
@@ -1672,13 +1774,14 @@ public final class NatUtil {
                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                         LogicalDatastoreType.CONFIGURATION, externalNwIdentifier);
         if (externalNwData.isPresent()) {
-            for (Networks externalNw : externalNwData.get().getNetworks()) {
+            for (Networks externalNw : requireNonNullElse(externalNwData.get().getNetworks(),
+                    Collections.<Networks>emptyList())) {
                 if (externalNw.getVpnid() != null && externalNw.getVpnid().equals(vpnUuid)) {
-                    return externalNw.getRouterIds();
+                    return requireNonNullElse(externalNw.getRouterIds(), emptyList());
                 }
             }
         }
-        return Collections.emptyList();
+        return emptyList();
     }
 
     public static boolean isIpInSubnet(String ipAddress, String start, String end) {
@@ -1695,7 +1798,7 @@ public final class NatUtil {
     }
 
     @Nonnull
-    public static Collection<Uuid> getExternalSubnetIdsFromExternalIps(List<ExternalIps> externalIps) {
+    public static Collection<Uuid> getExternalSubnetIdsFromExternalIps(@Nullable List<ExternalIps> externalIps) {
         if (externalIps == null) {
             return Collections.emptySet();
         }
@@ -1704,7 +1807,7 @@ public final class NatUtil {
     }
 
     @Nonnull
-    public static Collection<Uuid> getExternalSubnetIdsForRouter(DataBroker dataBroker, String routerName) {
+    public static Collection<Uuid> getExternalSubnetIdsForRouter(DataBroker dataBroker, @Nullable String routerName) {
         if (routerName == null) {
             LOG.error("getExternalSubnetIdsForRouter : empty routerName received");
             return Collections.emptySet();
@@ -1739,6 +1842,27 @@ public final class NatUtil {
                 LogicalDatastoreType.CONFIGURATION, subnetsIdentifier);
     }
 
+    @Nonnull
+    protected static Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external
+        .subnets.Subnets> getOptionalExternalSubnets(TypedReadTransaction<Configuration> tx, Uuid subnetId) {
+        if (subnetId == null) {
+            LOG.warn("getOptionalExternalSubnets : subnetId is null");
+            return Optional.absent();
+        }
+
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice
+            .rev160111.external.subnets.Subnets> subnetsIdentifier =
+            InstanceIdentifier.builder(ExternalSubnets.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice
+                    .rev160111.external.subnets.Subnets.class, new SubnetsKey(subnetId)).build();
+        try {
+            return tx.read(subnetsIdentifier).get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error retrieving external subnets on {}", subnetId, e);
+            return Optional.absent();
+        }
+    }
+
     protected static long getExternalSubnetVpnId(DataBroker dataBroker, Uuid subnetId) {
         Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external
             .subnets.Subnets> optionalExternalSubnets = NatUtil.getOptionalExternalSubnets(dataBroker,
@@ -1750,6 +1874,17 @@ public final class NatUtil {
         return NatConstants.INVALID_ID;
     }
 
+    protected static long getExternalSubnetVpnId(TypedReadTransaction<Configuration> tx, Uuid subnetId) {
+        Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external
+            .subnets.Subnets> optionalExternalSubnets = NatUtil.getOptionalExternalSubnets(tx,
+            subnetId);
+        if (optionalExternalSubnets.isPresent()) {
+            return NatUtil.getVpnId(tx, subnetId.getValue());
+        }
+
+        return NatConstants.INVALID_ID;
+    }
+
     protected static long getExternalSubnetVpnIdForRouterExternalIp(DataBroker dataBroker, String externalIpAddress,
             Routers router) {
         Uuid externalSubnetId = NatUtil.getExternalSubnetForRouterExternalIp(externalIpAddress, router);
@@ -1760,10 +1895,10 @@ public final class NatUtil {
         return NatConstants.INVALID_ID;
     }
 
+    @Nullable
     protected static Uuid getExternalSubnetForRouterExternalIp(String externalIpAddress, Routers router) {
         externalIpAddress = validateAndAddNetworkMask(externalIpAddress);
-        List<ExternalIps> externalIps = router.getExternalIps();
-        for (ExternalIps extIp : externalIps) {
+        for (ExternalIps extIp : requireNonNullElse(router.getExternalIps(), Collections.<ExternalIps>emptyList())) {
             String extIpString = validateAndAddNetworkMask(extIp.getIpAddress());
             if (extIpString.equals(externalIpAddress)) {
                 return extIp.getSubnetId();
@@ -1786,36 +1921,48 @@ public final class NatUtil {
     @Nonnull
     static List<String> getIpsListFromExternalIps(@Nullable List<ExternalIps> externalIps) {
         if (externalIps == null) {
-            return Collections.emptyList();
+            return emptyList();
         }
 
         return externalIps.stream().map(ExternalIps::getIpAddress).collect(Collectors.toList());
     }
 
     // elan-instances config container
+    @Nullable
     public static ElanInstance getElanInstanceByName(String elanInstanceName, DataBroker broker) {
         InstanceIdentifier<ElanInstance> elanIdentifierId = getElanInstanceConfigurationDataPath(elanInstanceName);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
                 LogicalDatastoreType.CONFIGURATION, elanIdentifierId).orNull();
     }
 
+    @Nullable
+    public static ElanInstance getElanInstanceByName(TypedReadTransaction<Configuration> tx, String elanInstanceName) {
+        try {
+            return tx.read(getElanInstanceConfigurationDataPath(elanInstanceName)).get().orNull();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error retrieving ELAN instance by name {}", elanInstanceName, e);
+            return null;
+        }
+    }
+
     public static InstanceIdentifier<ElanInstance> getElanInstanceConfigurationDataPath(String elanInstanceName) {
         return InstanceIdentifier.builder(ElanInstances.class)
                 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
     }
 
-    public static long getTunnelIdForNonNaptToNaptFlow(DataBroker dataBroker, IElanService elanManager,
-            IdManagerService idManager, long routerId, String routerName) {
+    public static long getTunnelIdForNonNaptToNaptFlow(DataBroker dataBroker, NatOverVxlanUtil natOverVxlanUtil,
+                                                       IElanService elanManager, IdManagerService idManager,
+                                                       long routerId, String routerName) {
         if (elanManager.isOpenStackVniSemanticsEnforced()) {
             // Router VNI will be set as tun_id if OpenStackSemantics is enabled
-            return NatOverVxlanUtil.getRouterVni(idManager, routerName, routerId).longValue();
+            return natOverVxlanUtil.getRouterVni(routerName, routerId).longValue();
         } else {
             return NatEvpnUtil.getTunnelIdForRouter(idManager, dataBroker, routerName, routerId);
         }
     }
 
     public static void makePreDnatToSnatTableEntry(IMdsalApiManager mdsalManager, BigInteger naptDpnId,
-            short tableId, WriteTransaction writeFlowTx) {
+            short tableId, TypedWriteTransaction<Configuration> confTx) {
         LOG.debug("makePreDnatToSnatTableEntry : Create Pre-DNAT table {} --> table {} flow on NAPT DpnId {} ",
                 NwConstants.PDNAT_TABLE, tableId, naptDpnId);
 
@@ -1828,21 +1975,19 @@ public final class NatUtil {
                 5, flowRef, 0, 0,  NwConstants.COOKIE_DNAT_TABLE,
                 matches, preDnatToSnatInstructions);
 
-        mdsalManager.addFlowToTx(naptDpnId, preDnatToSnatTableFlowEntity, writeFlowTx);
+        mdsalManager.addFlow(confTx, naptDpnId, preDnatToSnatTableFlowEntity);
         LOG.debug("makePreDnatToSnatTableEntry : Successfully installed Pre-DNAT flow {} on NAPT DpnId {} ",
                 preDnatToSnatTableFlowEntity,  naptDpnId);
     }
 
-    public static void removePreDnatToSnatTableEntry(IMdsalApiManager mdsalManager, BigInteger naptDpnId,
-                                                     WriteTransaction removeFlowInvTx) {
+    public static void removePreDnatToSnatTableEntry(TypedReadWriteTransaction<Configuration> confTx,
+            IMdsalApiManager mdsalManager, BigInteger naptDpnId) throws ExecutionException, InterruptedException {
         LOG.debug("removePreDnatToSnatTableEntry : Remove Pre-DNAT table {} --> table {} flow on NAPT DpnId {} ",
                 NwConstants.PDNAT_TABLE, NwConstants.INBOUND_NAPT_TABLE, naptDpnId);
         String flowRef = getFlowRefPreDnatToSnat(naptDpnId, NwConstants.PDNAT_TABLE, "PreDNATToSNAT");
-        Flow preDnatToSnatTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.PDNAT_TABLE,flowRef,
-                5, flowRef, 0, 0,  NwConstants.COOKIE_DNAT_TABLE, null, null);
-        mdsalManager.removeFlowToTx(naptDpnId, preDnatToSnatTableFlowEntity, removeFlowInvTx);
+        mdsalManager.removeFlow(confTx, naptDpnId, flowRef, NwConstants.PDNAT_TABLE);
         LOG.debug("removePreDnatToSnatTableEntry: Successfully removed Pre-DNAT flow {} on NAPT DpnId = {}",
-                preDnatToSnatTableFlowEntity, naptDpnId);
+                flowRef, naptDpnId);
     }
 
     private static String getFlowRefPreDnatToSnat(BigInteger dpnId, short tableId, String uniqueId) {
@@ -1850,7 +1995,7 @@ public final class NatUtil {
                 + NwConstants.FLOWID_SEPARATOR + uniqueId;
     }
 
-    public static Boolean isFloatingIpPresentForDpn(DataBroker dataBroker, BigInteger dpnId, String rd,
+    public static boolean isFloatingIpPresentForDpn(DataBroker dataBroker, BigInteger dpnId, String rd,
                                                     String vpnName, String externalIp,
                                                     Boolean isMoreThanOneFipCheckOnDpn) {
         InstanceIdentifier<VpnToDpnList> id = getVpnToDpnListIdentifier(rd, dpnId);
@@ -1863,31 +2008,31 @@ public final class NatUtil {
                 if (ipAddressList != null && !ipAddressList.isEmpty()) {
                     int floatingIpPresentCount = 0;
                     for (IpAddresses ipAddress: ipAddressList) {
-                        if (!ipAddress.getIpAddress().equals(externalIp)
+                        if (!Objects.equals(ipAddress.getIpAddress(), externalIp)
                                 && IpAddresses.IpAddressSource.FloatingIP.equals(ipAddress.getIpAddressSource())) {
                             floatingIpPresentCount++;
                             //Add tunnel table check
                             if (isMoreThanOneFipCheckOnDpn && floatingIpPresentCount > 1) {
-                                return Boolean.TRUE;
+                                return true;
                             }
                             //Remove tunnel table check
                             if (!isMoreThanOneFipCheckOnDpn) {
-                                return Boolean.TRUE;
+                                return true;
                             }
                         }
                     }
                 } else {
                     LOG.debug("isFloatingIpPresentForDpn : vpn-to-dpn-list does not contain any floating IP for DPN {}",
                            dpnId);
-                    return Boolean.FALSE;
+                    return false;
                 }
             } catch (NullPointerException e) {
                 LOG.error("isFloatingIpPresentForDpn: Exception occurred on getting external IP address from "
                         + "vpn-to-dpn-list on Dpn {}", dpnId, e);
-                return Boolean.FALSE;
+                return false;
             }
         }
-        return Boolean.FALSE;
+        return false;
     }
 
     private static InstanceIdentifier<VpnToDpnList> getVpnToDpnListIdentifier(String rd, BigInteger dpnId) {
@@ -1897,9 +2042,9 @@ public final class NatUtil {
     }
 
     @Nullable
-    public static String getPrimaryRd(String vpnName, ReadTransaction tx) throws ReadFailedException {
-        return tx.read(LogicalDatastoreType.CONFIGURATION,
-                getVpnInstanceIdentifier(vpnName)).checkedGet().toJavaUtil().map(NatUtil::getPrimaryRd).orElse(null);
+    public static String getPrimaryRd(String vpnName, TypedReadTransaction<Configuration> tx)
+        throws ExecutionException, InterruptedException {
+        return tx.read(getVpnInstanceIdentifier(vpnName)).get().toJavaUtil().map(NatUtil::getPrimaryRd).orElse(null);
     }
 
     public static String getPrimaryRd(DataBroker dataBroker, String vpnName) {
@@ -1913,12 +2058,13 @@ public final class NatUtil {
         return vpnName;
     }
 
-    public static String getPrimaryRd(VpnInstance vpnInstance) {
-        List<String> rds = null;
-        if (vpnInstance != null) {
-            rds = getListOfRdsFromVpnInstance(vpnInstance);
+    @Nullable
+    public static String getPrimaryRd(@Nullable VpnInstance vpnInstance) {
+        if (vpnInstance == null) {
+            return null;
         }
-        return rds == null || rds.isEmpty() ? vpnInstance.getVpnInstanceName() : rds.get(0);
+        List<String> rds = getListOfRdsFromVpnInstance(vpnInstance);
+        return rds.isEmpty() ? vpnInstance.getVpnInstanceName() : rds.get(0);
     }
 
     public static InstanceIdentifier<VpnInstance> getVpnInstanceIdentifier(String vpnName) {
@@ -1954,6 +2100,7 @@ public final class NatUtil {
         new VpnInterfaceOpDataEntryKey(vpnInterfaceName, vpnName)).build();
     }
 
+    @Nullable
     public static VpnInstanceOpDataEntry getVpnInstanceOpData(DataBroker broker, String rd) {
         InstanceIdentifier<VpnInstanceOpDataEntry> id = NatUtil.getVpnInstanceOpDataIdentifier(rd);
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
@@ -1971,13 +2118,11 @@ public final class NatUtil {
                 for (Uuid routerUuid : routerUuidList) {
                     String sharedRouterName = routerUuid.getValue();
                     if (!routerName.equals(sharedRouterName)) {
-                        BigInteger swtichDpnId = NatUtil.getPrimaryNaptfromRouterName(broker, sharedRouterName);
-                        if (swtichDpnId == null) {
-                            continue;
-                        } else if (swtichDpnId.equals(dpnId)) {
+                        BigInteger switchDpnId = NatUtil.getPrimaryNaptfromRouterName(broker, sharedRouterName);
+                        if (switchDpnId != null && switchDpnId.equals(dpnId)) {
                             LOG.debug("checkForRoutersWithSameExtNetAndNaptSwitch: external-network {} is "
                                     + "associated with other active router {} on NAPT switch {}", networkId,
-                                    sharedRouterName, swtichDpnId);
+                                    sharedRouterName, switchDpnId);
                             return true;
                         }
                     }
@@ -1987,13 +2132,35 @@ public final class NatUtil {
         return false;
     }
 
+    public static boolean checkForRoutersWithSameExtSubnetAndNaptSwitch(DataBroker broker, Uuid externalSubnetId,
+                                                                        String routerName, BigInteger dpnId) {
+        List<Uuid> routerUuidList = getOptionalExternalSubnets(broker, externalSubnetId).toJavaUtil()
+            .map(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external
+                .subnets.Subnets::getRouterIds).orElse(emptyList());
+        if (!routerUuidList.isEmpty()) {
+            for (Uuid routerUuid : routerUuidList) {
+                String sharedRouterName = routerUuid.getValue();
+                if (!routerName.equals(sharedRouterName)) {
+                    BigInteger switchDpnId = NatUtil.getPrimaryNaptfromRouterName(broker, sharedRouterName);
+                    if (switchDpnId != null && switchDpnId.equals(dpnId)) {
+                        LOG.debug("checkForRoutersWithSameExtSubnetAndNaptSwitch: external-subnetwork {} is "
+                                  + "associated with other active router {} on NAPT switch {}", externalSubnetId,
+                            sharedRouterName, switchDpnId);
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
     public static void installRouterGwFlows(ManagedNewTransactionRunner txRunner, IVpnManager vpnManager,
             Routers router, BigInteger primarySwitchId, int addOrRemove) {
-        ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
+        ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
             List<ExternalIps> externalIps = router.getExternalIps();
             List<String> externalIpsSting = new ArrayList<>();
 
-            if (externalIps.isEmpty()) {
+            if (externalIps == null || externalIps.isEmpty()) {
                 LOG.error("installRouterGwFlows: setupRouterGwFlows no externalIP present");
                 return;
             }
@@ -2006,7 +2173,7 @@ public final class NatUtil {
                         router.getNetworkId(), subnetVpnName.getValue(), tx);
                 vpnManager.addArpResponderFlowsToExternalNetworkIps(router.getRouterName(), externalIpsSting,
                         router.getExtGwMacAddress(), primarySwitchId,
-                        router.getNetworkId(), tx);
+                        router.getNetworkId());
             } else {
                 vpnManager.removeRouterGwMacFlow(router.getRouterName(), router.getExtGwMacAddress(), primarySwitchId,
                         router.getNetworkId(), subnetVpnName.getValue(), tx);
@@ -2017,15 +2184,99 @@ public final class NatUtil {
         }), LOG, "Error installing router gateway flows");
     }
 
-    public static CheckedFuture<Void, TransactionCommitFailedException> waitForTransactionToComplete(
-            WriteTransaction tx) {
-        CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
-        try {
-            futures.get();
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("Error writing to datastore {}", e);
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public static void removeSNATFromDPN(DataBroker dataBroker, IMdsalApiManager mdsalManager,
+            IdManagerService idManager, NaptSwitchHA naptSwitchHA, BigInteger dpnId,
+            String routerName, long routerId, long routerVpnId,
+            ProviderTypes extNwProvType, TypedReadWriteTransaction<Configuration> confTx)
+                    throws ExecutionException, InterruptedException {
+        //irrespective of naptswitch or non-naptswitch, SNAT default miss entry need to be removed
+        //remove miss entry to NAPT switch
+        //if naptswitch elect new switch and install Snat flows and remove those flows in oldnaptswitch
+        if (extNwProvType == null) {
+            return;
+        }
+        //Get the external IP labels other than VXLAN provider type. Since label is not applicable for VXLAN
+        Map<String, Long> externalIpLabel;
+        if (extNwProvType == ProviderTypes.VXLAN) {
+            externalIpLabel = null;
+        } else {
+            externalIpLabel = NatUtil.getExternalIpsLabelForRouter(dataBroker, routerId);
+        }
+        BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
+        if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
+            LOG.error("removeSNATFromDPN : No naptSwitch is selected for router {}", routerName);
+            return;
+        }
+        Collection<String> externalIpCache = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
+        boolean naptStatus =
+            naptSwitchHA.isNaptSwitchDown(routerName, routerId, dpnId, naptSwitch, routerVpnId,
+                    externalIpCache, confTx);
+        if (!naptStatus) {
+            LOG.debug("removeSNATFromDPN: Switch with DpnId {} is not naptSwitch for router {}",
+                dpnId, routerName);
+            long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
+            FlowEntity flowEntity = null;
+            try {
+                flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId,
+                    NatConstants.DEL_FLOW);
+                if (flowEntity == null) {
+                    LOG.error("removeSNATFromDPN : Failed to populate flowentity for router:{} "
+                            + "with dpnId:{} groupId:{}", routerName, dpnId, groupId);
+                    return;
+                }
+                LOG.debug("removeSNATFromDPN : Removing default SNAT miss entry flow entity {}", flowEntity);
+                mdsalManager.removeFlow(confTx, flowEntity);
+
+            } catch (Exception ex) {
+                LOG.error("removeSNATFromDPN : Failed to remove default SNAT miss entry flow entity {}",
+                    flowEntity, ex);
+                return;
+            }
+            LOG.debug("removeSNATFromDPN : Removed default SNAT miss entry flow for dpnID {} with routername {}",
+                dpnId, routerName);
+
+            //remove group
+            GroupEntity groupEntity = null;
+            try {
+                groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
+                    GroupTypes.GroupAll, emptyList() /*listBucketInfo*/);
+                LOG.info("removeSNATFromDPN : Removing NAPT GroupEntity:{}", groupEntity);
+                mdsalManager.removeGroup(groupEntity);
+            } catch (Exception ex) {
+                LOG.error("removeSNATFromDPN : Failed to remove group entity {}", groupEntity, ex);
+                return;
+            }
+            LOG.debug("removeSNATFromDPN : Removed default SNAT miss entry flow for dpnID {} with routerName {}",
+                dpnId, routerName);
+        } else {
+            naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, routerId, naptSwitch,
+                    externalIpLabel, confTx);
+            //remove table 26 flow ppointing to table46
+            FlowEntity flowEntity = null;
+            try {
+                flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName, routerVpnId,
+                    NatConstants.DEL_FLOW);
+                if (flowEntity == null) {
+                    LOG.error("removeSNATFromDPN : Failed to populate flowentity for router {} with dpnId {}",
+                            routerName, dpnId);
+                    return;
+                }
+                LOG.debug("removeSNATFromDPN : Removing default SNAT miss entry flow entity for router {} with "
+                    + "dpnId {} in napt switch {}", routerName, dpnId, naptSwitch);
+                mdsalManager.removeFlow(confTx, flowEntity);
+
+            } catch (Exception ex) {
+                LOG.error("removeSNATFromDPN : Failed to remove default SNAT miss entry flow entity {}",
+                    flowEntity, ex);
+                return;
+            }
+            LOG.debug("removeSNATFromDPN : Removed default SNAT miss entry flow for dpnID {} with routername {}",
+                dpnId, routerName);
+
+            //best effort to check IntExt model
+            naptSwitchHA.bestEffortDeletion(routerId, routerName, externalIpLabel, confTx);
         }
-        return futures;
     }
 
     public static Boolean isOpenStackVniSemanticsEnforcedForGreAndVxlan(IElanService elanManager,
@@ -2144,17 +2395,16 @@ public final class NatUtil {
         return extRouterInstanceIndentifier;
     }
 
+    @Nullable
     public static LearntVpnVipToPortData getLearntVpnVipToPortData(DataBroker dataBroker) {
-        InstanceIdentifier<LearntVpnVipToPortData> learntVpnVipToPortDataId = getLearntVpnVipToPortDataId();
-        LearntVpnVipToPortData learntVpnVipToPortData = null;
         try {
-            learntVpnVipToPortData = SingleTransactionDataBroker.syncRead(dataBroker,
-                    LogicalDatastoreType.OPERATIONAL, learntVpnVipToPortDataId);
+            return SingleTransactionDataBroker.syncRead(dataBroker,
+                    LogicalDatastoreType.OPERATIONAL, getLearntVpnVipToPortDataId());
         }
         catch (ReadFailedException e) {
             LOG.warn("Failed to read LearntVpnVipToPortData with error {}", e.getMessage());
+            return null;
         }
-        return learntVpnVipToPortData;
     }
 
     public static InstanceIdentifier<LearntVpnVipToPortData> getLearntVpnVipToPortDataId() {
@@ -2170,6 +2420,12 @@ public final class NatUtil {
                 .child(DpnInterfaces.class, new DpnInterfacesKey(dpId)).build();
     }
 
+    public static InstanceIdentifier<Group> getGroupInstanceId(BigInteger dpnId, long groupId) {
+        return InstanceIdentifier.builder(Nodes.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight
+                .inventory.rev130819.nodes.Node.class, new NodeKey(new NodeId("openflow:" + dpnId)))
+                .augmentation(FlowCapableNode.class).child(Group.class, new GroupKey(new GroupId(groupId))).build();
+    }
+
     public static void createGroupIdPool(IdManagerService idManager) {
         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
                 .setPoolName(NatConstants.SNAT_IDPOOL_NAME)
@@ -2187,4 +2443,215 @@ public final class NatUtil {
             LOG.error("createGroupIdPool : Failed to create PortPool for NAPT Service", e);
         }
     }
+
+    public static boolean getSwitchStatus(DataBroker broker, BigInteger switchId) {
+        NodeId nodeId = new NodeId("openflow:" + switchId);
+        LOG.debug("getSwitchStatus : Querying switch with dpnId {} is up/down", nodeId);
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> nodeInstanceId
+            = InstanceIdentifier.builder(Nodes.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight
+                    .inventory.rev130819.nodes.Node.class, new NodeKey(nodeId)).build();
+        Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> nodeOptional =
+                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
+                        LogicalDatastoreType.OPERATIONAL, nodeInstanceId);
+        if (nodeOptional.isPresent()) {
+            LOG.debug("getSwitchStatus : Switch {} is up", nodeId);
+            return true;
+        }
+        LOG.debug("getSwitchStatus : Switch {} is down", nodeId);
+        return false;
+    }
+
+    public static boolean isExternalNetwork(DataBroker broker, Uuid networkId) {
+        InstanceIdentifier<Networks> id = buildNetworkIdentifier(networkId);
+        Optional<Networks> networkData =
+                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
+                        broker, LogicalDatastoreType.CONFIGURATION, id);
+        return networkData.isPresent();
+    }
+
+    @Nullable
+    public static String getElanInstancePhysicalNetwok(String elanInstanceName, DataBroker broker) {
+        ElanInstance elanInstance =  getElanInstanceByName(elanInstanceName, broker);
+        if (null != elanInstance) {
+            return elanInstance.getPhysicalNetworkName();
+        }
+        return null;
+
+    }
+
+    public static Map<String, String> getOpenvswitchOtherConfigMap(BigInteger dpnId, DataBroker dataBroker) {
+        String otherConfigVal = getProviderMappings(dpnId, dataBroker);
+        return getMultiValueMap(otherConfigVal);
+    }
+
+    public static Map<String, String> getMultiValueMap(String multiKeyValueStr) {
+        if (Strings.isNullOrEmpty(multiKeyValueStr)) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, String> valueMap = new HashMap<>();
+        Splitter splitter = Splitter.on(OTHER_CONFIG_PARAMETERS_DELIMITER);
+        for (String keyValue : splitter.split(multiKeyValueStr)) {
+            String[] split = keyValue.split(OTHER_CONFIG_KEY_VALUE_DELIMITER, 2);
+            if (split.length == 2) {
+                valueMap.put(split[0], split[1]);
+            }
+        }
+
+        return valueMap;
+    }
+
+    public static Optional<Node> getBridgeRefInfo(BigInteger dpnId, DataBroker dataBroker) {
+        InstanceIdentifier<BridgeRefEntry> bridgeRefInfoPath = InstanceIdentifier.create(BridgeRefInfo.class)
+                .child(BridgeRefEntry.class, new BridgeRefEntryKey(dpnId));
+
+        Optional<BridgeRefEntry> bridgeRefEntry =
+                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
+                        LogicalDatastoreType.OPERATIONAL, bridgeRefInfoPath);
+        if (!bridgeRefEntry.isPresent()) {
+            LOG.info("getBridgeRefInfo : bridgeRefEntry is not present for {}", dpnId);
+            return Optional.absent();
+        }
+
+        InstanceIdentifier<Node> nodeId =
+                bridgeRefEntry.get().getBridgeReference().getValue().firstIdentifierOf(Node.class);
+
+        return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
+                LogicalDatastoreType.OPERATIONAL, nodeId);
+    }
+
+    @Nullable
+    public static String getProviderMappings(BigInteger dpId, DataBroker dataBroker) {
+        return getBridgeRefInfo(dpId, dataBroker).toJavaUtil().map(node -> getOpenvswitchOtherConfigs(node,
+                PROVIDER_MAPPINGS, dataBroker)).orElse(null);
+    }
+
+    @Nullable
+    public static String getOpenvswitchOtherConfigs(Node node, String key, DataBroker dataBroker) {
+        OvsdbNodeAugmentation ovsdbNode = node.augmentation(OvsdbNodeAugmentation.class);
+        if (ovsdbNode == null) {
+            Optional<Node> nodeFromReadOvsdbNode = readOvsdbNode(node, dataBroker);
+            if (nodeFromReadOvsdbNode.isPresent()) {
+                ovsdbNode = nodeFromReadOvsdbNode.get().augmentation(OvsdbNodeAugmentation.class);
+            }
+        }
+
+        if (ovsdbNode != null && ovsdbNode.getOpenvswitchOtherConfigs() != null) {
+            for (OpenvswitchOtherConfigs openvswitchOtherConfigs : ovsdbNode.getOpenvswitchOtherConfigs()) {
+                if (Objects.equals(openvswitchOtherConfigs.getOtherConfigKey(), key)) {
+                    return openvswitchOtherConfigs.getOtherConfigValue();
+                }
+            }
+        }
+        LOG.info("getOpenvswitchOtherConfigs : OtherConfigs is not present for ovsdbNode {}", node.getNodeId());
+        return null;
+    }
+
+    @Nonnull
+    public static Optional<Node> readOvsdbNode(Node bridgeNode, DataBroker dataBroker) {
+        OvsdbBridgeAugmentation bridgeAugmentation = extractBridgeAugmentation(bridgeNode);
+        if (bridgeAugmentation != null) {
+            InstanceIdentifier<Node> ovsdbNodeIid =
+                    (InstanceIdentifier<Node>) bridgeAugmentation.getManagedBy().getValue();
+            return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
+                    LogicalDatastoreType.OPERATIONAL, ovsdbNodeIid);
+        }
+        return Optional.absent();
+
+    }
+
+    @Nullable
+    public static OvsdbBridgeAugmentation extractBridgeAugmentation(Node node) {
+        if (node == null) {
+            return null;
+        }
+        return node.augmentation(OvsdbBridgeAugmentation.class);
+    }
+
+    // Use Objects.requireNonNullElse instead with JDK9+
+    @Nonnull
+    public static <T> T requireNonNullElse(@Nullable T obj, @Nonnull T defaultObj) {
+        return obj != null ? obj : requireNonNull(defaultObj);
+    }
+
+    public static String getDefaultFibRouteToSNATForSubnetJobKey(String subnetName, BigInteger dpnId) {
+        return NatConstants.NAT_DJC_PREFIX + subnetName + dpnId;
+    }
+
+    public static ExternalSubnets getExternalSubnets(DataBroker dataBroker) {
+        InstanceIdentifier<ExternalSubnets> subnetsIdentifier =
+                InstanceIdentifier.builder(ExternalSubnets.class)
+                .build();
+        try {
+            Optional<ExternalSubnets> optionalExternalSubnets  = SingleTransactionDataBroker
+                    .syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, subnetsIdentifier);
+            if (optionalExternalSubnets.isPresent()) {
+                return optionalExternalSubnets.get();
+            }
+        } catch (ReadFailedException e) {
+            LOG.error("Failed to read the subnets from the datastore.");
+        }
+        return null;
+
+    }
+
+    public static void addFlow(TypedWriteTransaction<Configuration> confTx, IMdsalApiManager mdsalManager,
+            BigInteger dpId, short tableId, String flowId, int priority, String flowName, BigInteger cookie,
+            List<? extends MatchInfoBase> matches, List<InstructionInfo> instructions) {
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
+                NatConstants.DEFAULT_IDLE_TIMEOUT, NatConstants.DEFAULT_IDLE_TIMEOUT, cookie, matches,
+                instructions);
+        LOG.trace("syncFlow : Installing DpnId {}, flowId {}", dpId, flowId);
+        mdsalManager.addFlow(confTx, flowEntity);
+    }
+
+    public static void removeFlow(TypedReadWriteTransaction<Configuration> confTx, IMdsalApiManager mdsalManager,
+            BigInteger dpId, short tableId, String flowId) throws ExecutionException, InterruptedException {
+        LOG.trace("syncFlow : Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
+        mdsalManager.removeFlow(confTx, dpId, flowId, tableId);
+    }
+
+    public static String getIpv6FlowRef(BigInteger dpnId, short tableId, long routerID) {
+        return new StringBuilder().append(NatConstants.IPV6_FLOWID_PREFIX).append(dpnId).append(NatConstants
+                .FLOWID_SEPARATOR).append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
+    }
+
+    public static String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId,
+                                                ItmRpcService itmManager) {
+        Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
+        RpcResult<GetTunnelInterfaceNameOutput> rpcResult;
+        try {
+            Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager
+                    .getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId)
+                            .setDestinationDpid(dstDpId).setTunnelType(tunType).build());
+            rpcResult = result.get();
+            if (!rpcResult.isSuccessful()) {
+                tunType = TunnelTypeGre.class ;
+                result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
+                        .setSourceDpid(srcDpId)
+                        .setDestinationDpid(dstDpId)
+                        .setTunnelType(tunType)
+                        .build());
+                rpcResult = result.get();
+                if (!rpcResult.isSuccessful()) {
+                    LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
+                            rpcResult.getErrors());
+                } else {
+                    return rpcResult.getResult().getInterfaceName();
+                }
+                LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
+                        rpcResult.getErrors());
+            } else {
+                return rpcResult.getResult().getInterfaceName();
+            }
+        } catch (InterruptedException | ExecutionException | NullPointerException e) {
+            LOG.error("getTunnelInterfaceName : Exception when getting tunnel interface Id for tunnel "
+                    + "between {} and {}", srcDpId, dstDpId);
+        }
+        return null;
+    }
+
+    public static String getIpv6JobKey(String routerName) {
+        return "Ipv6." + routerName;
+    }
 }