NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronvpnUtils.java
index e70bc013778ad117e50f0565d38843fe44b7163d..3231943ad0d2adf0a681f10d317883a5249a9aa9 100644 (file)
@@ -1,17 +1,23 @@
 /*
- * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * Copyright (c) 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,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
 package org.opendaylight.netvirt.neutronvpn;
 
-import com.google.common.base.Optional;
+import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
+import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
+
+import com.google.common.base.Function;
 import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
-import java.math.BigInteger;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -19,36 +25,37 @@ import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
-import javax.annotation.Nonnull;
+import java.util.concurrent.locks.ReentrantLock;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.apache.commons.lang3.StringUtils;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-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.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
+import org.opendaylight.genius.infra.Datastore;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.genius.infra.TypedWriteTransaction;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
+import org.opendaylight.genius.utils.JvmGlobalLocks;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
+import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
+import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
-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.Ipv6Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
@@ -57,25 +64,20 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
 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.genius.idmanager.rev160406.AllocateIdInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.Dhcpv6Base;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAclBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpVersionBase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.PortSubnets;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.PortSubnet;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.PortSubnetBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.PortSubnetKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.port.subnet.SubnetInfo;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.port.subnet.SubnetInfoBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.port.subnet.SubnetInfoKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.SubnetInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.SubnetInfoBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.SubnetInfoKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeGre;
@@ -103,17 +105,29 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev16011
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.subnets.SubnetsKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMapping;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.VpnInstances;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.instances.VpnInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.instances.VpnInstanceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.instances.VpnInstanceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.interfaces.VpnInterfaceKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NetworkMaps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronVpnPortIdSubportData;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronVpnPortipPortData;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.VpnMaps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.port.id.subport.data.PortIdToSubport;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.port.id.subport.data.PortIdToSubportKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPortBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPortKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIds;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIdsBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMapKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.ext.rev150712.NetworkL3Extension;
@@ -135,7 +149,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.por
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.portsecurity.rev150712.PortSecurityExtension;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.provider.ext.rev150712.NetworkProviderExtension;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.qos.rev160613.qos.attributes.qos.policies.QosPolicy;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
@@ -148,6 +161,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.Uint64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -164,38 +178,50 @@ public class NeutronvpnUtils {
             .put(NetworkTypeVxlan.class, SegmentTypeVxlan.class)
             .build();
 
-    private static final Set<Class<? extends NetworkTypeBase>> SUPPORTED_NETWORK_TYPES = new HashSet<>();
-
-    static {
-        SUPPORTED_NETWORK_TYPES.add(NetworkTypeFlat.class);
-        SUPPORTED_NETWORK_TYPES.add(NetworkTypeVlan.class);
-        SUPPORTED_NETWORK_TYPES.add(NetworkTypeVxlan.class);
-        SUPPORTED_NETWORK_TYPES.add(NetworkTypeGre.class);
-    }
+    private static final ImmutableSet<Class<? extends NetworkTypeBase>> SUPPORTED_NETWORK_TYPES = ImmutableSet.of(
+        NetworkTypeFlat.class,
+        NetworkTypeVlan.class,
+        NetworkTypeVxlan.class,
+        NetworkTypeGre.class);
+
+
+    private static final InstanceIdentifier<VpnInstanceOpData> VPN_INSTANCE_OP_DATA_IID =
+            InstanceIdentifier.create(VpnInstanceOpData.class);
+    private static final InstanceIdentifier<VpnMaps> VPN_MAPS_IID = InstanceIdentifier.create(VpnMaps.class);
+    private static final InstanceIdentifier<Subnetmaps> SUBNETMAPS_IID = InstanceIdentifier.create(Subnetmaps.class);
+    private static final InstanceIdentifier<Networks> NEUTRON_NETWORKS_IID = InstanceIdentifier.builder(Neutron.class)
+            .child(Networks.class).build();
+    private static final InstanceIdentifier<Ports> NEUTRON_PORTS_IID = InstanceIdentifier.builder(Neutron.class)
+            .child(Ports.class).build();
+    private static final InstanceIdentifier<Routers> NEUTRON_ROUTERS_IID = InstanceIdentifier.builder(Neutron.class)
+            .child(Routers.class).build();
+    private static final InstanceIdentifier<Subnets> NEUTRON_SUBNETS_IID = InstanceIdentifier.builder(Neutron.class)
+            .child(Subnets.class).build();
 
     private final ConcurrentMap<Uuid, Network> networkMap = new ConcurrentHashMap<>();
     private final ConcurrentMap<Uuid, Router> routerMap = new ConcurrentHashMap<>();
     private final ConcurrentMap<Uuid, Port> portMap = new ConcurrentHashMap<>();
     private final ConcurrentMap<Uuid, Subnet> subnetMap = new ConcurrentHashMap<>();
     private final Map<IpAddress, Set<Uuid>> subnetGwIpMap = new ConcurrentHashMap<>();
-    private final ConcurrentMap<Uuid, QosPolicy> qosPolicyMap = new ConcurrentHashMap<>();
-    private final ConcurrentMap<Uuid, HashMap<Uuid, Port>> qosPortsMap = new ConcurrentHashMap<>();
-    private final ConcurrentMap<Uuid, HashMap<Uuid, Network>> qosNetworksMap = new ConcurrentHashMap<>();
 
     private final DataBroker dataBroker;
+    private final ManagedNewTransactionRunner txRunner;
     private final IdManagerService idManager;
     private final JobCoordinator jobCoordinator;
-    private IPV6InternetDefaultRouteProgrammer ipV6InternetDefRt;
+    private final IPV6InternetDefaultRouteProgrammer ipV6InternetDefRt;
+    private static final int JOB_MAX_RETRIES = 3;
 
     @Inject
     public NeutronvpnUtils(final DataBroker dataBroker, final IdManagerService idManager,
             final JobCoordinator jobCoordinator, final IPV6InternetDefaultRouteProgrammer ipV6InternetDefRt) {
         this.dataBroker = dataBroker;
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
         this.idManager = idManager;
         this.jobCoordinator = jobCoordinator;
         this.ipV6InternetDefRt = ipV6InternetDefRt;
     }
 
+    @Nullable
     protected Subnetmap getSubnetmap(Uuid subnetId) {
         InstanceIdentifier<Subnetmap> id = buildSubnetMapIdentifier(subnetId);
         Optional<Subnetmap> sn = read(LogicalDatastoreType.CONFIGURATION, id);
@@ -207,10 +233,9 @@ public class NeutronvpnUtils {
         return null;
     }
 
+    @Nullable
     public VpnMap getVpnMap(Uuid id) {
-        InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class).child(VpnMap.class,
-                new VpnMapKey(id)).build();
-        Optional<VpnMap> optionalVpnMap = read(LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
+        Optional<VpnMap> optionalVpnMap = read(LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier(id));
         if (optionalVpnMap.isPresent()) {
             return optionalVpnMap.get();
         }
@@ -218,12 +243,11 @@ public class NeutronvpnUtils {
         return null;
     }
 
+    @Nullable
     protected Uuid getVpnForNetwork(Uuid network) {
-        InstanceIdentifier<VpnMaps> vpnMapsIdentifier = InstanceIdentifier.builder(VpnMaps.class).build();
-        Optional<VpnMaps> optionalVpnMaps = read(LogicalDatastoreType.CONFIGURATION, vpnMapsIdentifier);
-        if (optionalVpnMaps.isPresent() && optionalVpnMaps.get().getVpnMap() != null) {
-            List<VpnMap> allMaps = optionalVpnMaps.get().getVpnMap();
-            for (VpnMap vpnMap : allMaps) {
+        Optional<VpnMaps> optionalVpnMaps = read(LogicalDatastoreType.CONFIGURATION, VPN_MAPS_IID);
+        if (optionalVpnMaps.isPresent() && optionalVpnMaps.get().nonnullVpnMap() != null) {
+            for (VpnMap vpnMap : new ArrayList<>(optionalVpnMaps.get().nonnullVpnMap())) {
                 List<Uuid> netIds = vpnMap.getNetworkIds();
                 if (netIds != null && netIds.contains(network)) {
                     return vpnMap.getVpnId();
@@ -234,6 +258,7 @@ public class NeutronvpnUtils {
         return null;
     }
 
+    @Nullable
     protected Uuid getVpnForSubnet(Uuid subnetId) {
         InstanceIdentifier<Subnetmap> subnetmapIdentifier = buildSubnetMapIdentifier(subnetId);
         Optional<Subnetmap> optionalSubnetMap = read(LogicalDatastoreType.CONFIGURATION,
@@ -245,6 +270,7 @@ public class NeutronvpnUtils {
         return null;
     }
 
+    @Nullable
     protected Uuid getNetworkForSubnet(Uuid subnetId) {
         InstanceIdentifier<Subnetmap> subnetmapIdentifier = buildSubnetMapIdentifier(subnetId);
         Optional<Subnetmap> optionalSubnetMap = read(LogicalDatastoreType.CONFIGURATION,
@@ -257,17 +283,26 @@ public class NeutronvpnUtils {
     }
 
     // @param external vpn - true if external vpn being fetched, false for internal vpn
-    protected Uuid getVpnForRouter(Uuid routerId, Boolean externalVpn) {
+    @Nullable
+    protected Uuid getVpnForRouter(@Nullable Uuid routerId, boolean externalVpn) {
         if (routerId == null) {
             return null;
         }
 
-        InstanceIdentifier<VpnMaps> vpnMapsIdentifier = InstanceIdentifier.builder(VpnMaps.class).build();
-        Optional<VpnMaps> optionalVpnMaps = read(LogicalDatastoreType.CONFIGURATION, vpnMapsIdentifier);
-        if (optionalVpnMaps.isPresent() && optionalVpnMaps.get().getVpnMap() != null) {
-            List<VpnMap> allMaps = optionalVpnMaps.get().getVpnMap();
-            for (VpnMap vpnMap : allMaps) {
-                if (routerId.equals(vpnMap.getRouterId())) {
+        Optional<VpnMaps> optionalVpnMaps = read(LogicalDatastoreType.CONFIGURATION, VPN_MAPS_IID);
+        if (optionalVpnMaps.isPresent() && optionalVpnMaps.get().nonnullVpnMap() != null) {
+            for (VpnMap vpnMap : new ArrayList<>(optionalVpnMaps.get().nonnullVpnMap())) {
+                List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.vpnmap
+                    .RouterIds> routerIdsList = vpnMap.getRouterIds();
+                if (routerIdsList == null || routerIdsList.isEmpty()) {
+                    continue;
+                }
+                // Skip router vpnId fetching from internet BGP-VPN
+                if (hasExternalNetwork(vpnMap.getNetworkIds())) {
+                    continue;
+                }
+                // FIXME: NETVIRT-1503: this check can be replaced by a ReadOnlyTransaction.exists()
+                if (routerIdsList.stream().anyMatch(routerIds -> routerId.equals(routerIds.getRouterId()))) {
                     if (externalVpn) {
                         if (!routerId.equals(vpnMap.getVpnId())) {
                             return vpnMap.getVpnId();
@@ -284,25 +319,47 @@ public class NeutronvpnUtils {
         return null;
     }
 
-    protected Uuid getRouterforVpn(Uuid vpnId) {
-        InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class).child(VpnMap.class,
-                new VpnMapKey(vpnId)).build();
-        Optional<VpnMap> optionalVpnMap = read(LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
+    // 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. Note that some UUIDs may point to unknown networks, in which
+    // case we check more  and assume false.
+    private boolean hasExternalNetwork(List<Uuid> uuids) {
+        if (uuids != null) {
+            for (Uuid uuid : uuids) {
+                final Network network = getNeutronNetwork(uuid);
+                if (network != null) {
+                    if (Boolean.TRUE.equals(getIsExternal(network))) {
+                        return true;
+                    }
+                } else {
+                    LOG.debug("hasExternalNetwork: cannot find network for {}", uuid);
+                }
+            }
+        }
+        return false;
+    }
+
+
+    @Nullable
+    protected List<Uuid> getRouterIdListforVpn(Uuid vpnId) {
+        Optional<VpnMap> optionalVpnMap = read(LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier(vpnId));
         if (optionalVpnMap.isPresent()) {
             VpnMap vpnMap = optionalVpnMap.get();
-            return vpnMap.getRouterId();
+            return NeutronUtils.getVpnMapRouterIdsListUuid(vpnMap.getRouterIds());
         }
-        LOG.error("getRouterforVpn: Failed as VPNMaps DS is absent for VPN {}", vpnId.getValue());
+        LOG.error("getRouterIdListforVpn: Failed as VPNMaps DS is absent for VPN {}", vpnId.getValue());
         return null;
     }
 
+    @Nullable
     protected List<Uuid> getNetworksForVpn(Uuid vpnId) {
-        InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class).child(VpnMap.class,
-                new VpnMapKey(vpnId)).build();
-        Optional<VpnMap> optionalVpnMap = read(LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
+        Optional<VpnMap> optionalVpnMap = read(LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier(vpnId));
         if (optionalVpnMap.isPresent()) {
             VpnMap vpnMap = optionalVpnMap.get();
-            return vpnMap.getNetworkIds();
+            if (vpnMap.getNetworkIds() != null && !vpnMap.getNetworkIds().isEmpty()) {
+                return new ArrayList<>(vpnMap.getNetworkIds());
+            } else {
+                return null;
+            }
         }
         LOG.error("getNetworksforVpn: Failed as VPNMaps DS is absent for VPN {}", vpnId.getValue());
         return null;
@@ -311,8 +368,7 @@ public class NeutronvpnUtils {
     protected List<Uuid> getSubnetsforVpn(Uuid vpnid) {
         List<Uuid> subnets = new ArrayList<>();
         // read subnetmaps
-        InstanceIdentifier<Subnetmaps> subnetmapsid = InstanceIdentifier.builder(Subnetmaps.class).build();
-        Optional<Subnetmaps> subnetmaps = read(LogicalDatastoreType.CONFIGURATION, subnetmapsid);
+        Optional<Subnetmaps> subnetmaps = read(LogicalDatastoreType.CONFIGURATION, SUBNETMAPS_IID);
         if (subnetmaps.isPresent() && subnetmaps.get().getSubnetmap() != null) {
             List<Subnetmap> subnetMapList = subnetmaps.get().getSubnetmap();
             for (Subnetmap candidateSubnetMap : subnetMapList) {
@@ -324,6 +380,7 @@ public class NeutronvpnUtils {
         return subnets;
     }
 
+    @Nullable
     protected String getNeutronPortNameFromVpnPortFixedIp(String vpnName, String fixedIp) {
         InstanceIdentifier<VpnPortipToPort> id = buildVpnPortipToPortIdentifier(vpnName, fixedIp);
         Optional<VpnPortipToPort> vpnPortipToPortData = read(LogicalDatastoreType.CONFIGURATION, id);
@@ -335,6 +392,7 @@ public class NeutronvpnUtils {
         return null;
     }
 
+    @Nullable
     protected List<Uuid> getSubnetIdsFromNetworkId(Uuid networkId) {
         InstanceIdentifier<NetworkMap> id = buildNetworkMapIdentifier(networkId);
         Optional<NetworkMap> optionalNetworkMap = read(LogicalDatastoreType.CONFIGURATION, id);
@@ -345,58 +403,65 @@ public class NeutronvpnUtils {
         return null;
     }
 
-    protected List<Uuid> getPortIdsFromSubnetId(Uuid subnetId) {
-        InstanceIdentifier<Subnetmap> id = buildSubnetMapIdentifier(subnetId);
-        Optional<Subnetmap> optionalSubnetmap = read(LogicalDatastoreType.CONFIGURATION, id);
-        if (optionalSubnetmap.isPresent()) {
-            return optionalSubnetmap.get().getPortList();
-        }
-        return null;
-    }
-
     protected Router getNeutronRouter(Uuid routerId) {
         Router router = routerMap.get(routerId);
         if (router != null) {
             return router;
         }
-        InstanceIdentifier<Router> inst = InstanceIdentifier.create(Neutron.class).child(Routers.class).child(Router
-                .class, new RouterKey(routerId));
-        Optional<Router> rtr = read(LogicalDatastoreType.CONFIGURATION, inst);
+        Optional<Router> rtr = read(LogicalDatastoreType.CONFIGURATION, getNeutronRouterIid(routerId));
         if (rtr.isPresent()) {
             router = rtr.get();
         }
         return router;
     }
 
-    protected Network getNeutronNetwork(Uuid networkId) {
-        Network network = null;
-        network = networkMap.get(networkId);
+    public InstanceIdentifier<Router> getNeutronRouterIid(Uuid routerId) {
+        return NEUTRON_ROUTERS_IID.child(Router.class, new RouterKey(routerId));
+    }
+
+    protected @Nullable Network getNeutronNetwork(Uuid networkId) {
+        Network network = networkMap.get(networkId);
         if (network != null) {
             return network;
         }
         LOG.debug("getNeutronNetwork for {}", networkId.getValue());
-        InstanceIdentifier<Network> inst = InstanceIdentifier.create(Neutron.class).child(Networks.class)
-            .child(Network.class, new NetworkKey(networkId));
-        Optional<Network> net = read(LogicalDatastoreType.CONFIGURATION, inst);
-        if (net.isPresent()) {
-            network = net.get();
-        }
-        return network;
+        InstanceIdentifier<Network> inst = NEUTRON_NETWORKS_IID.child(Network.class, new NetworkKey(networkId));
+        return read(LogicalDatastoreType.CONFIGURATION, inst).orElse(null);
     }
 
-    protected Port getNeutronPort(Uuid portId) {
+    protected @Nullable Port getNeutronPort(Uuid portId) {
         Port prt = portMap.get(portId);
         if (prt != null) {
             return prt;
         }
         LOG.debug("getNeutronPort for {}", portId.getValue());
-        InstanceIdentifier<Port> inst = InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class,
-                new PortKey(portId));
-        Optional<Port> port = read(LogicalDatastoreType.CONFIGURATION, inst);
-        if (port.isPresent()) {
-            prt = port.get();
+        InstanceIdentifier<Port> inst = NEUTRON_PORTS_IID.child(Port.class, new PortKey(portId));
+        return read(LogicalDatastoreType.CONFIGURATION, inst).orElse(null);
+    }
+
+    public PortIdToSubport getPortIdToSubport(Uuid portId) {
+        InstanceIdentifier<PortIdToSubport> portIdToSubportIdentifier = buildPortIdSubportMappingIdentifier(portId);
+        Optional<PortIdToSubport> optionalPortIdToSubport = read(LogicalDatastoreType.CONFIGURATION,
+                portIdToSubportIdentifier);
+        if (optionalPortIdToSubport.isPresent()) {
+            return optionalPortIdToSubport.get();
         }
-        return prt;
+        LOG.error("getPortIdToSubport failed, PortIdToSubport {} not present", portId.getValue());
+        return null;
+    }
+
+    protected static boolean isDhcpServerPort(Port port) {
+        return port.getDeviceOwner().equals("network:dhcp");
+    }
+
+    protected InterfaceAcl getDhcpInterfaceAcl(Port port) {
+        InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
+        interfaceAclBuilder.setPortSecurityEnabled(false);
+        interfaceAclBuilder.setInterfaceType(InterfaceAcl.InterfaceType.DhcpService);
+        List<AllowedAddressPairs> aclAllowedAddressPairs = NeutronvpnUtils.getAllowedAddressPairsForAclService(
+                port.getMacAddress(), port.getFixedIps());
+        interfaceAclBuilder.setAllowedAddressPairs(aclAllowedAddressPairs);
+        return interfaceAclBuilder.build();
     }
 
     /**
@@ -413,7 +478,7 @@ public class NeutronvpnUtils {
             // router interface, dhcp port and floating ip.
             return false;
         }
-        PortSecurityExtension portSecurity = port.getAugmentation(PortSecurityExtension.class);
+        PortSecurityExtension portSecurity = port.augmentation(PortSecurityExtension.class);
         if (portSecurity != null) {
             return portSecurity.isPortSecurityEnabled();
         }
@@ -427,8 +492,9 @@ public class NeutronvpnUtils {
      * @param port2SecurityGroups the port 2 security groups
      * @return the security groups delta
      */
-    protected static List<Uuid> getSecurityGroupsDelta(List<Uuid> port1SecurityGroups,
-            List<Uuid> port2SecurityGroups) {
+    @Nullable
+    protected static List<Uuid> getSecurityGroupsDelta(@Nullable List<Uuid> port1SecurityGroups,
+            @Nullable List<Uuid> port2SecurityGroups) {
         if (port1SecurityGroups == null) {
             return null;
         }
@@ -488,10 +554,11 @@ public class NeutronvpnUtils {
      * @param port2AllowedAddressPairs the port 2 allowed address pairs
      * @return the allowed address pairs delta
      */
+    @Nullable
     protected static List<AllowedAddressPairs> getAllowedAddressPairsDelta(
-        List<org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes
+        @Nullable List<org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes
             .AllowedAddressPairs> port1AllowedAddressPairs,
-        List<org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes
+        @Nullable List<org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes
             .AllowedAddressPairs> port2AllowedAddressPairs) {
         if (port1AllowedAddressPairs == null) {
             return null;
@@ -514,7 +581,7 @@ public class NeutronvpnUtils {
                 .AllowedAddressPairs allowedAddressPair1 = iterator.next();
             for (org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes
                      .AllowedAddressPairs allowedAddressPair2 : list2) {
-                if (allowedAddressPair1.getKey().equals(allowedAddressPair2.getKey())) {
+                if (allowedAddressPair1.key().equals(allowedAddressPair2.key())) {
                     iterator.remove();
                     break;
                 }
@@ -534,7 +601,7 @@ public class NeutronvpnUtils {
             org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.types.rev160517.IpPrefixOrAddress ipAddress) {
         AllowedAddressPairsBuilder aclAllowedAdressPairBuilder = new AllowedAddressPairsBuilder();
         aclAllowedAdressPairBuilder.setMacAddress(macAddress);
-        if (ipAddress != null && ipAddress.getValue() != null) {
+        if (ipAddress != null && ipAddress.stringValue() != null) {
             if (ipAddress.getIpPrefix() != null) {
                 aclAllowedAdressPairBuilder.setIpAddress(new IpPrefixOrAddress(ipAddress.getIpPrefix()));
             } else {
@@ -556,8 +623,8 @@ public class NeutronvpnUtils {
         List<AllowedAddressPairs> aclAllowedAddressPairs = new ArrayList<>();
         for (FixedIps fixedIp : fixedIps) {
             aclAllowedAddressPairs.add(getAclAllowedAddressPairs(macAddress,
-                    new org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.types.rev160517.IpPrefixOrAddress(
-                            fixedIp.getIpAddress().getValue())));
+                    org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.types.rev160517.IpPrefixOrAddressBuilder
+                    .getDefaultInstance(fixedIp.getIpAddress().stringValue())));
         }
         return aclAllowedAddressPairs;
     }
@@ -589,8 +656,9 @@ public class NeutronvpnUtils {
     protected static AllowedAddressPairs updateIPv6LinkLocalAddressForAclService(MacAddress macAddress) {
         IpAddress ipv6LinkLocalAddress = getIpv6LinkLocalAddressFromMac(macAddress);
         return getAclAllowedAddressPairs(macAddress,
-                new org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.types.rev160517.IpPrefixOrAddress(
-                        ipv6LinkLocalAddress.getValue()));
+                org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.types.rev160517.IpPrefixOrAddressBuilder
+                .getDefaultInstance(
+                        ipv6LinkLocalAddress.stringValue()));
     }
 
     /**
@@ -678,7 +746,7 @@ public class NeutronvpnUtils {
      * @param interfaceAclBuilder the interface acl builder
      * @param port the port
      */
-    protected static void populateInterfaceAclBuilder(InterfaceAclBuilder interfaceAclBuilder, Port port) {
+    protected void populateInterfaceAclBuilder(InterfaceAclBuilder interfaceAclBuilder, Port port) {
         // Handle security group enabled
         List<Uuid> securityGroups = port.getSecurityGroups();
         if (securityGroups != null) {
@@ -694,26 +762,18 @@ public class NeutronvpnUtils {
             aclAllowedAddressPairs.addAll(NeutronvpnUtils.getAllowedAddressPairsForAclService(portAllowedAddressPairs));
         }
         interfaceAclBuilder.setAllowedAddressPairs(aclAllowedAddressPairs);
+        interfaceAclBuilder.setInterfaceType(InterfaceAcl.InterfaceType.AccessPort);
+        populateSubnetInfo(interfaceAclBuilder, port);
     }
 
-    protected void populateSubnetInfo(Port port) {
+    protected void populateSubnetInfo(InterfaceAclBuilder interfaceAclBuilder, Port port) {
         List<SubnetInfo> portSubnetInfo = getSubnetInfo(port);
         if (portSubnetInfo != null) {
-            String portId = port.getUuid().getValue();
-            InstanceIdentifier<PortSubnet> portSubnetIdentifier = buildPortSubnetIdentifier(portId);
-
-            PortSubnetBuilder portSubnetBuilder = new PortSubnetBuilder().setKey(new PortSubnetKey(portId))
-                    .setPortId(portId).setSubnetInfo(portSubnetInfo);
-            try {
-                SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
-                        portSubnetIdentifier, portSubnetBuilder.build());
-            } catch (TransactionCommitFailedException e) {
-                LOG.error("Failed to populate subnet info for port={}", portId, e);
-            }
-            LOG.debug("Created Subnet info for port={}", portId);
+            interfaceAclBuilder.setSubnetInfo(portSubnetInfo);
         }
     }
 
+    @Nullable
     protected List<SubnetInfo> getSubnetInfo(Port port) {
         List<FixedIps> portFixedIps = port.getFixedIps();
         if (portFixedIps == null) {
@@ -726,10 +786,10 @@ public class NeutronvpnUtils {
             Subnet subnet = getNeutronSubnet(subnetId);
             if (subnet != null) {
                 Class<? extends IpVersionBase> ipVersion =
-                        NeutronSecurityRuleConstants.IP_VERSION_MAP.get(subnet.getIpVersion());
+                        NeutronSecurityGroupConstants.IP_VERSION_MAP.get(subnet.getIpVersion());
                 Class<? extends Dhcpv6Base> raMode = subnet.getIpv6RaMode() == null ? null
-                        : NeutronSecurityRuleConstants.RA_MODE_MAP.get(subnet.getIpv6RaMode());
-                SubnetInfo subnetInfo = new SubnetInfoBuilder().setKey(new SubnetInfoKey(subnetId))
+                        : NeutronSecurityGroupConstants.RA_MODE_MAP.get(subnet.getIpv6RaMode());
+                SubnetInfo subnetInfo = new SubnetInfoBuilder().withKey(new SubnetInfoKey(subnetId))
                         .setIpVersion(ipVersion).setIpPrefix(new IpPrefixOrAddress(subnet.getCidr()))
                         .setIpv6RaMode(raMode).setGatewayIp(subnet.getGatewayIp()).build();
                 subnetInfoList.add(subnetInfo);
@@ -743,22 +803,35 @@ public class NeutronvpnUtils {
         if (subnet != null) {
             return subnet;
         }
-        InstanceIdentifier<Subnet> inst = InstanceIdentifier.create(Neutron.class).child(Subnets.class).child(Subnet
-                .class, new SubnetKey(subnetId));
+        InstanceIdentifier<Subnet> inst = NEUTRON_SUBNETS_IID.child(Subnet.class, new SubnetKey(subnetId));
         Optional<Subnet> sn = read(LogicalDatastoreType.CONFIGURATION, inst);
 
         if (sn.isPresent()) {
             subnet = sn.get();
+            addToSubnetCache(subnet);
         }
         return subnet;
     }
 
-    @Nonnull
+    protected List<Subnetmap> getNeutronRouterSubnetMapList(Uuid routerId) {
+        List<Subnetmap> subnetMapList = new ArrayList<>();
+        Optional<Subnetmaps> subnetMaps = read(LogicalDatastoreType.CONFIGURATION, SUBNETMAPS_IID);
+        if (subnetMaps.isPresent() && subnetMaps.get().getSubnetmap() != null) {
+            for (Subnetmap subnetmap : subnetMaps.get().getSubnetmap()) {
+                if (routerId.equals(subnetmap.getRouterId())) {
+                    subnetMapList.add(subnetmap);
+                }
+            }
+        }
+        LOG.debug("getNeutronRouterSubnetMapList returns {}", subnetMapList);
+        return subnetMapList;
+    }
+
+    @NonNull
     protected List<Uuid> getNeutronRouterSubnetIds(Uuid routerId) {
         LOG.debug("getNeutronRouterSubnetIds for {}", routerId.getValue());
         List<Uuid> subnetIdList = new ArrayList<>();
-        Optional<Subnetmaps> subnetMaps = read(LogicalDatastoreType.CONFIGURATION,
-            InstanceIdentifier.builder(Subnetmaps.class).build());
+        Optional<Subnetmaps> subnetMaps = read(LogicalDatastoreType.CONFIGURATION, SUBNETMAPS_IID);
         if (subnetMaps.isPresent() && subnetMaps.get().getSubnetmap() != null) {
             for (Subnetmap subnetmap : subnetMaps.get().getSubnetmap()) {
                 if (routerId.equals(subnetmap.getRouterId())) {
@@ -772,15 +845,14 @@ public class NeutronvpnUtils {
 
     // TODO Clean up the exception handling and the console output
     @SuppressWarnings({"checkstyle:IllegalCatch", "checkstyle:RegexpSinglelineJava"})
+    @Nullable
     protected Short getIPPrefixFromPort(Port port) {
         try {
-            Uuid subnetUUID = port.getFixedIps().get(0).getSubnetId();
-            SubnetKey subnetkey = new SubnetKey(subnetUUID);
-            InstanceIdentifier<Subnet> subnetidentifier = InstanceIdentifier.create(Neutron.class).child(Subnets
-                    .class).child(Subnet.class, subnetkey);
-            Optional<Subnet> subnet = read(LogicalDatastoreType.CONFIGURATION, subnetidentifier);
+            // FIXME: why are we not using getNeutronSubnet() here? it does caching for us...
+            Optional<Subnet> subnet = read(LogicalDatastoreType.CONFIGURATION,
+                NEUTRON_SUBNETS_IID.child(Subnet.class, new SubnetKey(port.getFixedIps().get(0).getSubnetId())));
             if (subnet.isPresent()) {
-                String cidr = String.valueOf(subnet.get().getCidr().getValue());
+                String cidr = subnet.get().getCidr().stringValue();
                 // Extract the prefix length from cidr
                 String[] parts = cidr.split("/");
                 if (parts.length == 2) {
@@ -801,37 +873,38 @@ public class NeutronvpnUtils {
     // TODO Clean up the exception handling
     @SuppressWarnings("checkstyle:IllegalCatch")
     protected void createVpnPortFixedIpToPort(String vpnName, String fixedIp, String portName, String macAddress,
-            boolean isSubnetIp, WriteTransaction writeConfigTxn) {
+            boolean isSubnetIp, TypedWriteTransaction<Datastore.Configuration> writeConfigTxn) {
         InstanceIdentifier<VpnPortipToPort> id = NeutronvpnUtils.buildVpnPortipToPortIdentifier(vpnName, fixedIp);
         VpnPortipToPortBuilder builder = new VpnPortipToPortBuilder()
-            .setKey(new VpnPortipToPortKey(fixedIp, vpnName))
+            .withKey(new VpnPortipToPortKey(fixedIp, vpnName))
             .setVpnName(vpnName).setPortFixedip(fixedIp)
             .setPortName(portName).setMacAddress(macAddress).setSubnetIp(isSubnetIp);
         try {
             if (writeConfigTxn != null) {
-                writeConfigTxn.put(LogicalDatastoreType.CONFIGURATION, id, builder.build());
+                writeConfigTxn.put(id, builder.build());
             } else {
                 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, builder.build());
             }
             LOG.trace("Neutron port with fixedIp: {}, vpn {}, interface {}, mac {}, isSubnetIp {} added to "
                 + "VpnPortipToPort DS", fixedIp, vpnName, portName, macAddress, isSubnetIp);
         } catch (Exception e) {
-            LOG.error("Failure while creating VPNPortFixedIpToPort map for vpn {} - fixedIP {}", vpnName, fixedIp,
-                    e);
+            LOG.error("Failure while creating VPNPortFixedIpToPort map for vpn {} - fixedIP {} for port {} with "
+                    + "macAddress {}", vpnName, fixedIp, portName, macAddress, e);
         }
     }
 
     // TODO Clean up the exception handling
     @SuppressWarnings("checkstyle:IllegalCatch")
-    protected void removeVpnPortFixedIpToPort(String vpnName, String fixedIp, WriteTransaction writeConfigTxn) {
+    protected void removeVpnPortFixedIpToPort(String vpnName, String fixedIp,
+                                              TypedWriteTransaction<Datastore.Configuration> writeConfigTxn) {
         InstanceIdentifier<VpnPortipToPort> id = NeutronvpnUtils.buildVpnPortipToPortIdentifier(vpnName, fixedIp);
         try {
             if (writeConfigTxn != null) {
-                writeConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, id);
+                writeConfigTxn.delete(id);
             } else {
                 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
             }
-            LOG.trace("Neutron router port with fixedIp: {}, vpn {} removed from LearntVpnPortipToPort DS", fixedIp,
+            LOG.trace("Neutron router port with fixedIp: {}, vpn {} removed from VpnPortipToPort DS", fixedIp,
                     vpnName);
         } catch (Exception e) {
             LOG.error("Failure while removing VPNPortFixedIpToPort map for vpn {} - fixedIP {}", vpnName, fixedIp,
@@ -843,15 +916,18 @@ public class NeutronvpnUtils {
     @SuppressWarnings("checkstyle:IllegalCatch")
     protected void removeLearntVpnVipToPort(String vpnName, String fixedIp) {
         InstanceIdentifier<LearntVpnVipToPort> id = NeutronvpnUtils.buildLearntVpnVipToPortIdentifier(vpnName, fixedIp);
+        // FIXME: can we use 'id' as the lock name?
+        final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName + fixedIp);
+        lock.lock();
         try {
-            synchronized ((vpnName + fixedIp).intern()) {
-                MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
-            }
+            MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
             LOG.trace("Neutron router port with fixedIp: {}, vpn {} removed from LearntVpnPortipToPort DS", fixedIp,
                     vpnName);
         } catch (Exception e) {
             LOG.error("Failure while removing LearntVpnPortFixedIpToPort map for vpn {} - fixedIP {}",
                 vpnName, fixedIp, e);
+        } finally {
+            lock.unlock();
         }
     }
 
@@ -904,7 +980,7 @@ public class NeutronvpnUtils {
 
     public static String getSegmentationIdFromNeutronNetwork(Network network) {
         String segmentationId = null;
-        NetworkProviderExtension providerExtension = network.getAugmentation(NetworkProviderExtension.class);
+        NetworkProviderExtension providerExtension = network.augmentation(NetworkProviderExtension.class);
         if (providerExtension != null) {
             Class<? extends NetworkTypeBase> networkType = providerExtension.getNetworkType();
             segmentationId = NeutronUtils.getSegmentationIdFromNeutronNetwork(network, networkType);
@@ -914,12 +990,12 @@ public class NeutronvpnUtils {
     }
 
     public static Class<? extends SegmentTypeBase> getSegmentTypeFromNeutronNetwork(Network network) {
-        NetworkProviderExtension providerExtension = network.getAugmentation(NetworkProviderExtension.class);
+        NetworkProviderExtension providerExtension = network.augmentation(NetworkProviderExtension.class);
         return providerExtension != null ? NETWORK_MAP.get(providerExtension.getNetworkType()) : null;
     }
 
     public static String getPhysicalNetworkName(Network network) {
-        NetworkProviderExtension providerExtension = network.getAugmentation(NetworkProviderExtension.class);
+        NetworkProviderExtension providerExtension = network.augmentation(NetworkProviderExtension.class);
         return providerExtension != null ? providerExtension.getPhysicalNetwork() : null;
     }
 
@@ -942,52 +1018,8 @@ public class NeutronvpnUtils {
     }
 
     static Boolean getIsExternal(Network network) {
-        return network.getAugmentation(NetworkL3Extension.class) != null
-                && network.getAugmentation(NetworkL3Extension.class).isExternal();
-    }
-
-    public void addToQosPolicyCache(QosPolicy qosPolicy) {
-        qosPolicyMap.put(qosPolicy.getUuid(),qosPolicy);
-    }
-
-    public void removeFromQosPolicyCache(QosPolicy qosPolicy) {
-        qosPolicyMap.remove(qosPolicy.getUuid());
-    }
-
-    public void addToQosPortsCache(Uuid qosUuid, Port port) {
-        if (qosPortsMap.containsKey(qosUuid)) {
-            if (!qosPortsMap.get(qosUuid).containsKey(port.getUuid())) {
-                qosPortsMap.get(qosUuid).put(port.getUuid(), port);
-            }
-        } else {
-            HashMap<Uuid, Port> newPortMap = new HashMap<>();
-            newPortMap.put(port.getUuid(), port);
-            qosPortsMap.put(qosUuid, newPortMap);
-        }
-    }
-
-    public void removeFromQosPortsCache(Uuid qosUuid, Port port) {
-        if (qosPortsMap.containsKey(qosUuid) && qosPortsMap.get(qosUuid).containsKey(port.getUuid())) {
-            qosPortsMap.get(qosUuid).remove(port.getUuid(), port);
-        }
-    }
-
-    public void addToQosNetworksCache(Uuid qosUuid, Network network) {
-        if (qosNetworksMap.containsKey(qosUuid)) {
-            if (!qosNetworksMap.get(qosUuid).containsKey(network.getUuid())) {
-                qosNetworksMap.get(qosUuid).put(network.getUuid(), network);
-            }
-        } else {
-            HashMap<Uuid, Network> newNetworkMap = new HashMap<>();
-            newNetworkMap.put(network.getUuid(), network);
-            qosNetworksMap.put(qosUuid, newNetworkMap);
-        }
-    }
-
-    public void removeFromQosNetworksCache(Uuid qosUuid, Network network) {
-        if (qosNetworksMap.containsKey(qosUuid) && qosNetworksMap.get(qosUuid).containsKey(network.getUuid())) {
-            qosNetworksMap.get(qosUuid).remove(network.getUuid(), network);
-        }
+        NetworkL3Extension ext = network.augmentation(NetworkL3Extension.class);
+        return ext != null && ext.isExternal();
     }
 
     static InstanceIdentifier<NetworkMap> buildNetworkMapIdentifier(Uuid networkId) {
@@ -1003,9 +1035,7 @@ public class NeutronvpnUtils {
     }
 
     static InstanceIdentifier<Subnetmap> buildSubnetMapIdentifier(Uuid subnetId) {
-        InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class, new
-                SubnetmapKey(subnetId)).build();
-        return id;
+        return SUBNETMAPS_IID.child(Subnetmap.class, new SubnetmapKey(subnetId));
     }
 
     static InstanceIdentifier<Interface> buildVlanInterfaceIdentifier(String interfaceName) {
@@ -1014,6 +1044,12 @@ public class NeutronvpnUtils {
         return id;
     }
 
+    static InstanceIdentifier<PortIdToSubport> buildPortIdSubportMappingIdentifier(Uuid interfaceName) {
+        InstanceIdentifier<PortIdToSubport> id = InstanceIdentifier.builder(NeutronVpnPortIdSubportData.class)
+                .child(PortIdToSubport.class, new PortIdToSubportKey(interfaceName)).build();
+        return id;
+    }
+
     static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext
             .routers.Routers> buildExtRoutersIdentifier(Uuid routerId) {
         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers
@@ -1028,33 +1064,23 @@ public class NeutronvpnUtils {
                 FloatingIpIdToPortMappingKey(floatingIpId)).build();
     }
 
-    static InstanceIdentifier<PortSubnet> buildPortSubnetIdentifier(String portId) {
-        InstanceIdentifier<PortSubnet> id = InstanceIdentifier.builder(PortSubnets.class)
-                .child(PortSubnet.class, new PortSubnetKey(portId)).build();
-        return id;
-    }
-
     // TODO Remove this method entirely
     @SuppressWarnings("checkstyle:IllegalCatch")
     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
         try {
             return SingleTransactionDataBroker.syncReadOptional(dataBroker, datastoreType, path);
-        } catch (ReadFailedException e) {
+        } catch (ExecutionException | InterruptedException e) {
             throw new RuntimeException(e);
         }
     }
 
-    public static Class<? extends NetworkTypeBase> getNetworkType(Network network) {
-        NetworkProviderExtension providerExtension = network.getAugmentation(NetworkProviderExtension.class);
-        return providerExtension != null ? providerExtension.getNetworkType() : null;
-    }
-
+    @Nullable
     static ProviderTypes getProviderNetworkType(Network network) {
         if (network == null) {
             LOG.error("Error in getting provider network type since network is null");
             return null;
         }
-        NetworkProviderExtension npe = network.getAugmentation(NetworkProviderExtension.class);
+        NetworkProviderExtension npe = network.augmentation(NetworkProviderExtension.class);
         if (npe != null) {
             Class<? extends NetworkTypeBase> networkTypeBase = npe.getNetworkType();
             if (networkTypeBase != null) {
@@ -1075,23 +1101,24 @@ public class NeutronvpnUtils {
     }
 
     static boolean isNetworkTypeSupported(Network network) {
-        NetworkProviderExtension npe = network.getAugmentation(NetworkProviderExtension.class);
-        return npe != null && npe.getNetworkType() != null && SUPPORTED_NETWORK_TYPES.contains(npe.getNetworkType());
+        NetworkProviderExtension npe = network.augmentation(NetworkProviderExtension.class);
+        return npe != null && SUPPORTED_NETWORK_TYPES.contains(npe.getNetworkType());
     }
 
-    static boolean isNetworkOfType(Network network, Class<? extends NetworkTypeBase> type) {
-        NetworkProviderExtension npe = network.getAugmentation(NetworkProviderExtension.class);
-        if (npe != null && npe.getNetworkType() != null) {
-            return type.isAssignableFrom(npe.getNetworkType());
+    static boolean isFlatOrVlanNetwork(Network network) {
+        if (network != null) {
+            NetworkProviderExtension npe = network.augmentation(NetworkProviderExtension.class);
+            if (npe != null) {
+                Class<? extends NetworkTypeBase> npeType = npe.getNetworkType();
+                if (npeType != null) {
+                    return NetworkTypeVlan.class.isAssignableFrom(npeType)
+                            || NetworkTypeFlat.class.isAssignableFrom(npeType);
+                }
+            }
         }
         return false;
     }
 
-    static boolean isFlatOrVlanNetwork(Network network) {
-        return network != null
-                && (isNetworkOfType(network, NetworkTypeVlan.class) || isNetworkOfType(network, NetworkTypeFlat.class));
-    }
-
     static boolean isVlanOrVxlanNetwork(Class<? extends NetworkTypeBase> type) {
         return type.isAssignableFrom(NetworkTypeVxlan.class) || type.isAssignableFrom(NetworkTypeVlan.class);
     }
@@ -1116,74 +1143,40 @@ public class NeutronvpnUtils {
      */
     public Optional<InterVpnLink> getInterVpnLinkByEndpointIp(String endpointIp) {
         InstanceIdentifier<InterVpnLinks> interVpnLinksIid = InstanceIdentifier.builder(InterVpnLinks.class).build();
-        Optional<InterVpnLinks> interVpnLinksOpData = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
-                interVpnLinksIid);
-        if (interVpnLinksOpData.isPresent()) {
-            List<InterVpnLink> allInterVpnLinks = interVpnLinksOpData.get().getInterVpnLink();
-            for (InterVpnLink interVpnLink : allInterVpnLinks) {
-                if (interVpnLink.getFirstEndpoint().getIpAddress().getValue().equals(endpointIp)
-                        || interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(endpointIp)) {
-                    return Optional.of(interVpnLink);
-                }
-            }
-        }
-        return Optional.absent();
-    }
-
-    public Set<RouterDpnList> getAllRouterDpnList(BigInteger dpid) {
-        Set<RouterDpnList> ret = new HashSet<>();
-        InstanceIdentifier<NeutronRouterDpns> routerDpnId =
-                InstanceIdentifier.create(NeutronRouterDpns.class);
-        Optional<NeutronRouterDpns> neutronRouterDpnsOpt =
-            MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerDpnId);
-        if (neutronRouterDpnsOpt.isPresent()) {
-            NeutronRouterDpns neutronRouterDpns = neutronRouterDpnsOpt.get();
-            List<RouterDpnList> routerDpnLists = neutronRouterDpns.getRouterDpnList();
-            for (RouterDpnList routerDpnList : routerDpnLists) {
-                if (routerDpnList.getDpnVpninterfacesList() != null) {
-                    for (DpnVpninterfacesList dpnInterfaceList : routerDpnList.getDpnVpninterfacesList()) {
-                        if (dpnInterfaceList.getDpnId().equals(dpid)) {
-                            ret.add(routerDpnList);
-                        }
+        Optional<InterVpnLinks> interVpnLinksOpData = null;
+        try {
+            interVpnLinksOpData = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                    interVpnLinksIid);
+            if (interVpnLinksOpData.isPresent()) {
+                for (InterVpnLink interVpnLink : interVpnLinksOpData.get().nonnullInterVpnLink()) {
+                    if (interVpnLink.getFirstEndpoint().getIpAddress().getValue().equals(endpointIp)
+                            || interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(endpointIp)) {
+                        return Optional.of(interVpnLink);
                     }
                 }
             }
+        } catch (ExecutionException | InterruptedException e) {
+            LOG.error("getInterVpnLinkByEndpointIp: Exception when reading intervpn Links for endpoint Ip {} ",
+                    endpointIp, e);
         }
-        return ret;
+        return Optional.empty();
     }
 
-    protected Integer getUniqueRDId(String poolName, String idKey) {
-        AllocateIdInput getIdInput = new AllocateIdInputBuilder().setPoolName(poolName).setIdKey(idKey).build();
-        try {
-            Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
-            RpcResult<AllocateIdOutput> rpcResult = result.get();
-            if (rpcResult.isSuccessful()) {
-                return rpcResult.getResult().getIdValue().intValue();
-            } else {
-                LOG.error("RPC call to get unique ID for pool name {} with ID key {} returned with errors {}",
-                        poolName, idKey, rpcResult.getErrors());
-            }
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("Exception when getting Unique Id for poolname {} and ID Key {}", poolName, idKey, e);
-        }
-        LOG.error("getUniqueRdId: Failed to return ID for poolname {} and ID Key {}", poolName, idKey);
-        return null;
-    }
-
-    protected void releaseRDId(String poolName, String idKey) {
+    protected Integer releaseId(String poolName, String idKey) {
         ReleaseIdInput idInput = new ReleaseIdInputBuilder().setPoolName(poolName).setIdKey(idKey).build();
         try {
-            Future<RpcResult<Void>> result = idManager.releaseId(idInput);
-            RpcResult<Void> rpcResult = result.get();
-            if (!rpcResult.isSuccessful()) {
-                LOG.error("RPC Call to Get Unique Id returned with errors for poolname {} and ID Key {}: {}",
-                        poolName, idKey, rpcResult.getErrors());
+            Future<RpcResult<ReleaseIdOutput>> result = idManager.releaseId(idInput);
+            if (result == null || result.get() == null || !result.get().isSuccessful()) {
+                LOG.error("releaseId: RPC Call to release Id from pool {} with key {} returned with Errors {}",
+                        poolName, idKey, (result != null && result.get() != null) ? result.get().getErrors() :
+                                "RpcResult is null");
             } else {
-                LOG.info("ID {} for RD released successfully", idKey);
+                return result.get().getResult().getIdValues().get(0).intValue();
             }
         } catch (InterruptedException | ExecutionException e) {
-            LOG.error("Exception when trying to release ID for poolname {} and ID Key {}", poolName, idKey, e);
+            LOG.error("releaseId: Exception when releasing Id for key {} from pool {}", idKey, poolName, e);
         }
+        return NeutronConstants.INVALID_ID;
     }
 
     protected static IpAddress getIpv6LinkLocalAddressFromMac(MacAddress mac) {
@@ -1210,7 +1203,7 @@ public class NeutronvpnUtils {
         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[5]), 2, "0"));
 
         Ipv6Address ipv6LLA = new Ipv6Address("fe80:0:0:0:" + interfaceID.toString());
-        IpAddress ipAddress = new IpAddress(ipv6LLA.getValue().toCharArray());
+        IpAddress ipAddress = new IpAddress(ipv6LLA);
         return ipAddress;
     }
 
@@ -1234,10 +1227,7 @@ public class NeutronvpnUtils {
         Optional<VpnInstances> vpnInstancesOptional = read(LogicalDatastoreType.CONFIGURATION, path);
         if (vpnInstancesOptional.isPresent() && vpnInstancesOptional.get().getVpnInstance() != null) {
             for (VpnInstance vpnInstance : vpnInstancesOptional.get().getVpnInstance()) {
-                if (vpnInstance.getIpv4Family() == null) {
-                    continue;
-                }
-                List<String> rds = vpnInstance.getIpv4Family().getRouteDistinguisher();
+                List<String> rds = vpnInstance.getRouteDistinguisher();
                 if (rds != null) {
                     existingRDs.addAll(rds);
                 }
@@ -1247,9 +1237,7 @@ public class NeutronvpnUtils {
     }
 
     protected boolean doesVpnExist(Uuid vpnId) {
-        InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class).child(VpnMap.class,
-                new VpnMapKey(vpnId)).build();
-        return read(LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier).isPresent();
+        return read(LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier(vpnId)).isPresent();
     }
 
     protected Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external
@@ -1321,8 +1309,7 @@ public class NeutronvpnUtils {
      */
     protected List<Subnetmap> getNeutronRouterSubnetMaps(Uuid routerId) {
         List<Subnetmap> subnetIdList = new ArrayList<>();
-        Optional<Subnetmaps> subnetMaps = read(LogicalDatastoreType.CONFIGURATION,
-            InstanceIdentifier.builder(Subnetmaps.class).build());
+        Optional<Subnetmaps> subnetMaps = read(LogicalDatastoreType.CONFIGURATION, SUBNETMAPS_IID);
         if (subnetMaps.isPresent() && subnetMaps.get().getSubnetmap() != null) {
             for (Subnetmap subnetmap : subnetMaps.get().getSubnetmap()) {
                 if (routerId.equals(subnetmap.getRouterId())) {
@@ -1348,11 +1335,12 @@ public class NeutronvpnUtils {
      *
      * @return the route-distinguisher of the VPN
      */
+    @Nullable
     public String getVpnRd(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(dataBroker,
-                LogicalDatastoreType.CONFIGURATION, id).toJavaUtil().map(
+                LogicalDatastoreType.CONFIGURATION, id).map(
                 org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
                         .VpnInstance::getVrfId).orElse(null);
     }
@@ -1390,6 +1378,7 @@ public class NeutronvpnUtils {
         return IpVersionChoice.UNDEFINED;
     }
 
+    @Nullable
     public VpnInstanceOpDataEntry getVpnInstanceOpDataEntryFromVpnId(String vpnName) {
         String primaryRd = getVpnRd(vpnName);
         if (primaryRd == null) {
@@ -1407,55 +1396,42 @@ public class NeutronvpnUtils {
     }
 
     protected InstanceIdentifier<VpnInstanceOpDataEntry> getVpnOpDataIdentifier(String primaryRd) {
-        return InstanceIdentifier.builder(VpnInstanceOpData.class)
-                .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(primaryRd)).build();
+        return VPN_INSTANCE_OP_DATA_IID.child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(primaryRd));
     }
 
-    public boolean shouldVpnHandleIpVersionChangeToAdd(Subnetmap sm, Uuid vpnId) {
-        if (sm == null) {
-            return false;
-        }
-        IpVersionChoice ipVersion = getIpVersionFromString(sm.getSubnetIp());
-        return shouldVpnHandleIpVersionChoiceChangeToAdd(ipVersion, vpnId);
-    }
-
-    public boolean shouldVpnHandleIpVersionChoiceChangeToAdd(IpVersionChoice ipVersion, Uuid vpnId) {
-        VpnInstanceOpDataEntry vpnInstanceOpDataEntry = getVpnInstanceOpDataEntryFromVpnId(vpnId.getValue());
-        if (vpnInstanceOpDataEntry == null) {
-            return false;
-        }
-        if (vpnInstanceOpDataEntry.getType() == VpnInstanceOpDataEntry.Type.L2) {
-            LOG.error("shouldVpnHandleIpVersionChangeToAdd: {} "
-                    + "VpnInstanceOpDataEntry is L2 instance. Do nothing.", vpnId.getValue());
+    public boolean shouldVpnHandleIpVersionChoiceChange(IpVersionChoice ipVersion, Uuid routerId, boolean add) {
+        int subnetCount = -1;
+        if (ipVersion.isIpVersionChosen(IpVersionChoice.IPV4)) {
+            subnetCount = getSubnetCountFromRouter(routerId, ipVersion);
+        } else if (ipVersion.isIpVersionChosen(IpVersionChoice.IPV6)) {
+            subnetCount = getSubnetCountFromRouter(routerId, ipVersion);
+        } else {
+            //Possible value of ipversion choice is either V4 or V6 only. Not accepted V4andV6 and Undefined
             return false;
         }
-        boolean isIpv4Configured = vpnInstanceOpDataEntry.isIpv4Configured();
-        boolean isVpnInstanceIpv4Changed = false;
-        if (ipVersion.isIpVersionChosen(IpVersionChoice.IPV4) && !isIpv4Configured) {
-            isVpnInstanceIpv4Changed = true;
-        }
-        boolean isIpv6Configured = vpnInstanceOpDataEntry.isIpv6Configured();
-        boolean isVpnInstanceIpv6Changed = false;
-        if (ipVersion.isIpVersionChosen(IpVersionChoice.IPV6) && !isIpv6Configured) {
-            isVpnInstanceIpv6Changed = true;
-        }
-        if (!isVpnInstanceIpv4Changed && !isVpnInstanceIpv6Changed) {
-            LOG.debug("shouldVpnHandleIpVersionChangeToAdd: VPN {} did not change with IpFamily {}",
-                  vpnId.getValue(), ipVersion.toString());
+        /* ADD: Update vpnInstanceOpDataEntry with address family only on first IPv4/IPv6 subnet
+         * for the VPN Instance.
+         *
+         * REMOVE: Update vpnInstanceOpDataEntry with address family only on last IPv4/IPv6 subnet
+         * for the VPN Instance.
+         */
+        if (add && subnetCount == 1) {
+            return true;
+        } else if (!add && subnetCount == 0) {
+            return true;
+        } else {
             return false;
         }
-        return true;
     }
 
     public boolean shouldVpnHandleIpVersionChangeToRemove(Subnetmap sm, Uuid vpnId) {
         if (sm == null) {
             return false;
         }
-        InstanceIdentifier<Subnetmaps> subnetMapsId = InstanceIdentifier.builder(Subnetmaps.class).build();
-        Optional<Subnetmaps> allSubnetMaps = read(LogicalDatastoreType.CONFIGURATION, subnetMapsId);
+        Optional<Subnetmaps> allSubnetMaps = read(LogicalDatastoreType.CONFIGURATION, SUBNETMAPS_IID);
         // calculate and store in list IpVersion for each subnetMap, belonging to current VpnInstance
         List<IpVersionChoice> snIpVersions = new ArrayList<>();
-        for (Subnetmap snMap: allSubnetMaps.get().getSubnetmap()) {
+        for (Subnetmap snMap : allSubnetMaps.get().nonnullSubnetmap()) {
             if (snMap.getId().equals(sm.getId())) {
                 continue;
             }
@@ -1473,40 +1449,61 @@ public class NeutronvpnUtils {
         return false;
     }
 
-    public void updateVpnInstanceWithIpFamily(String vpnName, IpVersionChoice ipVersion, boolean add) {
-        VpnInstanceOpDataEntry vpnInstanceOpDataEntry = getVpnInstanceOpDataEntryFromVpnId(vpnName);
-        if (vpnInstanceOpDataEntry == null) {
-            return;
-        }
-        if (vpnInstanceOpDataEntry.getType() == VpnInstanceOpDataEntry.Type.L2) {
-            LOG.debug("updateVpnInstanceWithIpFamily: Update VpnInstance {} with ipFamily {}."
-                            + "VpnInstanceOpDataEntry is L2 instance. Do nothing.", vpnName,
-                    ipVersion.toString());
-            return;
+    public int getSubnetCountFromRouter(Uuid routerId, IpVersionChoice ipVer) {
+        List<Subnetmap> subnetMapList = getNeutronRouterSubnetMapList(routerId);
+        int subnetCount = 0;
+        for (Subnetmap subMap : subnetMapList) {
+            IpVersionChoice ipVersion = getIpVersionFromString(subMap.getSubnetIp());
+            if (ipVersion.isIpVersionChosen(ipVer)) {
+                subnetCount++;
+            }
+            if (subnetCount > 1) {
+                break;
+            }
         }
-        final boolean isFinalVpnInstanceIpv6Changed = ipVersion
-                .isIpVersionChosen(IpVersionChoice.IPV6) ? true : false;
-        final boolean isFinalVpnInstanceIpv4Changed = ipVersion
-                .isIpVersionChosen(IpVersionChoice.IPV4) ? true : false;
-        final boolean finalIsIpv4Configured = ipVersion.isIpVersionChosen(IpVersionChoice.IPV4) ? add : false;
-        final boolean finalIsIpv6Configured = ipVersion.isIpVersionChosen(IpVersionChoice.IPV6) ? add : false;
+        return subnetCount;
+    }
+
+    public void updateVpnInstanceWithIpFamily(String vpnName, IpVersionChoice ipVersion, boolean add) {
         jobCoordinator.enqueueJob("VPN-" + vpnName, () -> {
-            VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder(vpnInstanceOpDataEntry);
-            if (isFinalVpnInstanceIpv4Changed) {
-                builder.setIpv4Configured(finalIsIpv4Configured);
+            VpnInstance vpnInstance = getVpnInstance(dataBroker, new Uuid(vpnName));
+            if (vpnInstance == null) {
+                return Collections.emptyList();
+            }
+            if (vpnInstance.isL2vpn()) {
+                LOG.debug("updateVpnInstanceWithIpFamily: Update VpnInstance {} with ipFamily {}."
+                        + "VpnInstance is L2 instance. Do nothing.", vpnName, ipVersion);
+                return Collections.emptyList();
+            }
+            if (ipVersion == IpVersionChoice.UNDEFINED) {
+                LOG.debug("updateVpnInstanceWithIpFamily: Update VpnInstance {} with Undefined address family"
+                        + "is not allowed. Do nothing", vpnName);
+                return Collections.emptyList();
             }
-            if (isFinalVpnInstanceIpv6Changed) {
-                builder.setIpv6Configured(finalIsIpv6Configured);
+            VpnInstanceBuilder builder = new VpnInstanceBuilder(vpnInstance);
+            boolean ipConfigured = add;
+
+            int originalValue = vpnInstance.getIpAddressFamilyConfigured().getIntValue();
+            int updatedValue = ipVersion.choice;
+
+            if (originalValue != updatedValue) {
+                if (ipConfigured) {
+                    originalValue = originalValue == 0 ? updatedValue : updatedValue + originalValue;
+                } else {
+                    originalValue = 10 - updatedValue;
+                }
+            } else if (!ipConfigured) {
+                originalValue = 0;
             }
-            WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
-            InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.builder(VpnInstanceOpData.class)
-                    .child(VpnInstanceOpDataEntry.class,
-                            new VpnInstanceOpDataEntryKey(vpnInstanceOpDataEntry.getVrfId())).build();
-            writeTxn.merge(LogicalDatastoreType.OPERATIONAL, id, builder.build(), false);
-            LOG.info("updateVpnInstanceWithIpFamily: Successfully {} {} to Vpn {}",
-                    add ? "added" : "removed",
-                    ipVersion.toString(), vpnName);
-            return Collections.singletonList(writeTxn.submit());
+
+            builder.setIpAddressFamilyConfigured(VpnInstance.IpAddressFamilyConfigured.forValue(originalValue));
+
+            InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
+                    .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
+            LOG.info("updateVpnInstanceWithIpFamily: Successfully {} IP family {} to Vpn {}",
+                    add == true ? "added" : "removed", ipVersion, vpnName);
+            return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
+                    CONFIGURATION, tx -> tx.merge(vpnIdentifier, builder.build(), false)));
         });
         return;
     }
@@ -1518,6 +1515,7 @@ public class NeutronvpnUtils {
      * @param vpnId the Uuid of the VPN
      * @return the VpnInstance or null if unfindable
      */
+    @Nullable
     public VpnInstance getVpnInstance(DataBroker broker, Uuid vpnId) {
         if (broker == null || vpnId == null) {
             return null;
@@ -1525,7 +1523,7 @@ public class NeutronvpnUtils {
         InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class).child(VpnInstance.class,
                 new VpnInstanceKey(vpnId.getValue())).build();
         Optional<VpnInstance> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
-        return (vpnInstance.isPresent()) ? vpnInstance.get() : null;
+        return vpnInstance.isPresent() ? vpnInstance.get() : null;
     }
 
     /**
@@ -1533,7 +1531,7 @@ public class NeutronvpnUtils {
      * @param routerId the Uuid of the router which you try to reach the external network
      * @return Uuid of externalNetwork or null if is not exist
      */
-    protected Uuid getExternalNetworkUuidAttachedFromRouterUuid(@Nonnull Uuid routerId) {
+    protected Uuid getExternalNetworkUuidAttachedFromRouterUuid(@NonNull Uuid routerId) {
         LOG.debug("getExternalNetworkUuidAttachedFromRouterUuid for {}", routerId.getValue());
         Uuid externalNetworkUuid = null;
         Router router = getNeutronRouter(routerId);
@@ -1543,7 +1541,7 @@ public class NeutronvpnUtils {
         return externalNetworkUuid;
     }
 
-    public Uuid getInternetvpnUuidBoundToRouterId(@Nonnull Uuid routerId) {
+    public Uuid getInternetvpnUuidBoundToRouterId(@NonNull Uuid routerId) {
         Uuid netId = getExternalNetworkUuidAttachedFromRouterUuid(routerId);
         if (netId == null) {
             return netId;
@@ -1558,7 +1556,8 @@ public class NeutronvpnUtils {
      * @param subnetUuid Uuid of subnet where you are finding a link to an external network
      * @return Uuid of externalVpn or null if it is not found
      */
-    public Uuid getInternetvpnUuidBoundToSubnetRouter(@Nonnull Uuid subnetUuid) {
+    @Nullable
+    public Uuid getInternetvpnUuidBoundToSubnetRouter(@NonNull Uuid subnetUuid) {
         Subnetmap subnetmap = getSubnetmap(subnetUuid);
         Uuid routerUuid = subnetmap.getRouterId();
         LOG.debug("getInternetvpnUuidBoundToSubnetRouter for subnetUuid {}", subnetUuid.getValue());
@@ -1574,64 +1573,99 @@ public class NeutronvpnUtils {
      * @param extNet Provider Network, which has a port attached as external network gateway to router
      * @return a list of Private Subnetmap Ids of the router with external network gateway
      */
-    public @Nonnull List<Uuid> getPrivateSubnetsToExport(@Nonnull Network extNet) {
+    public @NonNull List<Uuid> getPrivateSubnetsToExport(@NonNull Network extNet, Uuid internetVpnId) {
         List<Uuid> subList = new ArrayList<>();
-        Uuid extNetVpnId = getVpnForNetwork(extNet.getUuid());
-        if (extNetVpnId == null) {
-            return subList;
+        List<Uuid> rtrList = new ArrayList<>();
+        if (internetVpnId != null) {
+            rtrList.addAll(getRouterIdListforVpn(internetVpnId));
+        } else {
+            Uuid extNwVpnId = getVpnForNetwork(extNet.getUuid());
+            rtrList.addAll(getRouterIdListforVpn(extNwVpnId));
         }
-        Router router = getNeutronRouter(getRouterforVpn(extNetVpnId));
-        ExternalGatewayInfo info = router.getExternalGatewayInfo();
-        if (info == null) {
-            LOG.error("getPrivateSubnetsToExport: can not get info about external gateway for router {}",
-                      router.getUuid().getValue());
+        if (rtrList.isEmpty()) {
             return subList;
         }
-        // check that router really has given provider network as its external gateway port
-        if (!extNet.getUuid().equals(info.getExternalNetworkId())) {
-            LOG.error("getPrivateSubnetsToExport: router {} is not attached to given provider network {}",
-                      router.getUuid().getValue(), extNet.getUuid().getValue());
-            return subList;
+        for (Uuid rtrId: rtrList) {
+            Router router = getNeutronRouter(rtrId);
+            ExternalGatewayInfo info = router.getExternalGatewayInfo();
+            if (info == null) {
+                LOG.error("getPrivateSubnetsToExport: can not get info about external gateway for router {}",
+                          router.getUuid().getValue());
+                continue;
+            }
+            // check that router really has given provider network as its external gateway port
+            if (!extNet.getUuid().equals(info.getExternalNetworkId())) {
+                LOG.error("getPrivateSubnetsToExport: router {} is not attached to given provider network {}",
+                          router.getUuid().getValue(), extNet.getUuid().getValue());
+                continue;
+            }
+            subList.addAll(getNeutronRouterSubnetIds(rtrId));
         }
-        return getSubnetsforVpn(router.getUuid());
+        return subList;
     }
 
-    public void updateVpnInstanceWithFallback(String vpnName, boolean add) {
-        VpnInstanceOpDataEntry vpnInstanceOpDataEntry = getVpnInstanceOpDataEntryFromVpnId(vpnName);
+    public void updateVpnInstanceWithFallback(Uuid routerId, Uuid vpnName, boolean add) {
+        VpnInstanceOpDataEntry vpnInstanceOpDataEntry = getVpnInstanceOpDataEntryFromVpnId(vpnName.getValue());
         if (vpnInstanceOpDataEntry == null) {
-            // BGPVPN context not found
+            LOG.error("updateVpnInstanceWithFallback: vpnInstanceOpDataEntry not found for vpn {}", vpnName);
             return;
         }
-        String routerIdUuid = getRouterIdfromVpnInstance(vpnInstanceOpDataEntry.getVrfId());
-        if (routerIdUuid != null) {
-            List<BigInteger> dpnIds = getDpnsForRouter(routerIdUuid);
-            if (!dpnIds.isEmpty()) {
-                Long vpnId = vpnInstanceOpDataEntry.getVpnId();
-                VpnInstanceOpDataEntry vpnOpDataEntry = getVpnInstanceOpDataEntryFromVpnId(routerIdUuid);
-                Long routerIdAsLong = vpnOpDataEntry.getVpnId();
-                if (routerIdAsLong == null) {
-                    return;
-                }
-                for (BigInteger dpnId : dpnIds) {
-                    if (add) {
-                        ipV6InternetDefRt.installDefaultRoute(dpnId, vpnId, routerIdAsLong);
-                    } else {
-                        ipV6InternetDefRt.removeDefaultRoute(dpnId, vpnId, routerIdAsLong);
-                    }
+        Long internetBgpVpnId = vpnInstanceOpDataEntry.getVpnId().toJava();
+        List<Uuid> routerIds = new ArrayList<>();
+        //Handle router specific V6 internet fallback flow else handle all V6 external routers
+        if (routerId != null) {
+            routerIds.add(routerId);
+        } else {
+            //This block will execute for ext-nw to Internet VPN association/disassociation event.
+            routerIds = getRouterIdListforVpn(vpnName);
+        }
+        if (routerIds == null || routerIds.isEmpty()) {
+            LOG.error("updateVpnInstanceWithFallback: router not found for vpn {}", vpnName);
+            return;
+        }
+        for (Uuid rtrId: routerIds) {
+            if (rtrId == null) {
+                continue;
+            }
+            List<Uint64> dpnIds = getDpnsForRouter(rtrId.getValue());
+            if (dpnIds.isEmpty()) {
+                continue;
+            }
+            VpnInstanceOpDataEntry vpnOpDataEntry = getVpnInstanceOpDataEntryFromVpnId(rtrId.getValue());
+            Long routerIdAsLong = vpnOpDataEntry.getVpnId().toJava();
+            long vpnId;
+            Uuid rtrVpnId = getVpnForRouter(rtrId, true);
+            if (rtrVpnId == null) {
+                //If external BGP-VPN is not associated with router then routerId is same as routerVpnId
+                vpnId = routerIdAsLong;
+            } else {
+                vpnId = getVpnId(rtrVpnId.getValue());
+            }
+            for (Uint64 dpnId : dpnIds) {
+                if (add) {
+                    LoggingFutures.addErrorLogging(
+                        txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
+                            tx -> ipV6InternetDefRt.installDefaultRoute(tx, dpnId, rtrId.getValue(),
+                                internetBgpVpnId, vpnId)), LOG, "Error adding default route");
+                } else {
+                    LoggingFutures.addErrorLogging(
+                        txRunner.callWithNewReadWriteTransactionAndSubmit(Datastore.CONFIGURATION,
+                            tx -> ipV6InternetDefRt.removeDefaultRoute(tx, dpnId, rtrId.getValue(),
+                                internetBgpVpnId, vpnId)), LOG,
+                        "Error removing default route");
                 }
             }
         }
     }
 
-    public void updateVpnInstanceOpWithType(VpnInstanceOpDataEntry.BgpvpnType choice, @Nonnull Uuid vpn) {
+    public void updateVpnInstanceOpWithType(VpnInstanceOpDataEntry.BgpvpnType choice, @NonNull Uuid vpn) {
         String primaryRd = getVpnRd(vpn.getValue());
         if (primaryRd == null) {
             LOG.debug("updateVpnInstanceOpWithType: Update BgpvpnType {} for {}."
                     + "Primary RD not found", choice, vpn.getValue());
             return;
         }
-        InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.builder(VpnInstanceOpData.class)
-              .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(primaryRd)).build();
+        InstanceIdentifier<VpnInstanceOpDataEntry> id = getVpnOpDataIdentifier(primaryRd);
 
         Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
             read(LogicalDatastoreType.OPERATIONAL, id);
@@ -1648,56 +1682,130 @@ public class NeutronvpnUtils {
         }
         VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder(vpnInstanceOpDataEntry);
         builder.setBgpvpnType(choice);
-        WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
-        writeTxn.merge(LogicalDatastoreType.OPERATIONAL, id, builder.build(), false);
-        LOG.debug("updateVpnInstanceOpWithType: sent merge to operDS BgpvpnType {} for {}", choice, vpn.getValue());
-        try {
-            writeTxn.submit().get();
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("updateVpnInstanceOpWithType: on merge execution, error:  {}", e);
+        ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
+            tx.merge(id, builder.build(), false);
+            LOG.debug("updateVpnInstanceOpWithType: sent merge to operDS BgpvpnType {} for {}", choice, vpn.getValue());
+        }), LOG, "Error updating VPN instance op {} with type {}", vpn, choice);
+    }
+
+    public static RouterIds getvpnInstanceRouterIds(Uuid routerId) {
+        return new RouterIdsBuilder().setRouterId(routerId).build();
+    }
+
+    public static List<RouterIds> getVpnInstanceRouterIdsList(List<Uuid> routerIds) {
+        List<RouterIds> listRouterIds = new ArrayList<>();
+        for (Uuid routerId : routerIds) {
+            final RouterIds routerIdInstance = getvpnInstanceRouterIds(routerId);
+            listRouterIds.add(routerIdInstance);
         }
-        return;
+        return listRouterIds;
     }
 
-    @Nonnull
-    public List<BigInteger> getDpnsForRouter(String routerUuid) {
+    @NonNull
+    public List<Uint64> getDpnsForRouter(String routerUuid) {
         InstanceIdentifier id = InstanceIdentifier.builder(NeutronRouterDpns.class)
             .child(RouterDpnList.class, new RouterDpnListKey(routerUuid)).build();
         Optional<RouterDpnList> routerDpnListData =
                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
                         LogicalDatastoreType.OPERATIONAL, id);
-        List<BigInteger> dpns = new ArrayList<>();
+        List<Uint64> dpns = new ArrayList<>();
         if (routerDpnListData.isPresent()) {
-            List<DpnVpninterfacesList> dpnVpninterfacesList = routerDpnListData.get().getDpnVpninterfacesList();
-            for (DpnVpninterfacesList dpnVpnInterface : dpnVpninterfacesList) {
+            for (DpnVpninterfacesList dpnVpnInterface : routerDpnListData.get().nonnullDpnVpninterfacesList()) {
                 dpns.add(dpnVpnInterface.getDpnId());
             }
         }
         return dpns;
     }
 
-    public String getRouterIdfromVpnInstance(String vpnName) {
-        // returns only router, attached to IPv4 networks
-        InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
-            .child(VpnMap.class, new VpnMapKey(new Uuid(vpnName))).build();
-        Optional<VpnMap> optionalVpnMap = SingleTransactionDataBroker
-                .syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
-                        LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
-        if (!optionalVpnMap.isPresent()) {
-            LOG.error("getRouterIdfromVpnInstance : Router not found for vpn : {}", vpnName);
-            return null;
-        }
-        Uuid routerId = optionalVpnMap.get().getRouterId();
-        if (routerId != null) {
-            return routerId.getValue();
+    @Nullable
+    List<Subnetmap> getSubnetmapListFromNetworkId(Uuid networkId) {
+        List<Uuid> subnetIdList = getSubnetIdsFromNetworkId(networkId);
+        if (subnetIdList != null) {
+            List<Subnetmap> subnetmapList = new ArrayList<>();
+            for (Uuid subnetId : subnetIdList) {
+                Subnetmap subnetmap = getSubnetmap(subnetId);
+                if (subnetmap != null) {
+                    subnetmapList.add(subnetmap);
+                } else {
+                    LOG.error("getSubnetmapListFromNetworkId: subnetmap is null for subnet {} belonging to network {}",
+                            subnetId.getValue(), networkId.getValue());
+                }
+            }
+            return subnetmapList;
         }
-        LOG.info("getRouterIdfromVpnInstance : Router not found for vpn : {}", vpnName);
+        LOG.error("getSubnetmapListFromNetworkId: Failed as subnetIdList is null for network {}",
+                networkId.getValue());
         return null;
     }
 
-    public InstanceIdentifier<Router> buildNeutronRouterIdentifier(Uuid routerUuid) {
-        InstanceIdentifier<Router> routerInstanceIdentifier = InstanceIdentifier.create(Neutron.class)
-             .child(Routers.class).child(Router.class, new RouterKey(routerUuid));
-        return routerInstanceIdentifier;
+    @Nullable
+    public long getVpnId(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(dataBroker,
+                LogicalDatastoreType.CONFIGURATION, id).map(
+                org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
+                        .VpnInstance::getVpnId).orElse(null).toJava();
+    }
+
+    protected boolean isV6SubnetPartOfRouter(Uuid routerId) {
+        List<Subnetmap> subnetList = getNeutronRouterSubnetMapList(routerId);
+        for (Subnetmap sm : subnetList) {
+            if (sm == null) {
+                continue;
+            }
+            IpVersionChoice ipVers = getIpVersionFromString(sm.getSubnetIp());
+            //skip further subnet processing once found first V6 subnet for the router
+            if (ipVers.isIpVersionChosen(IpVersionChoice.IPV6)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T extends DataObject> void asyncReadAndExecute(final LogicalDatastoreType datastoreType,
+                                                           final InstanceIdentifier<T> iid, final String jobKey,
+                                                           final Function<Optional<T>, Void> function) {
+        jobCoordinator.enqueueJob(jobKey, () -> {
+            SettableFuture<Optional<T>> settableFuture = SettableFuture.create();
+            List futures = Collections.singletonList(settableFuture);
+            try (ReadTransaction tx = dataBroker.newReadOnlyTransaction()) {
+                Futures.addCallback(tx.read(datastoreType, iid),
+                        new SettableFutureCallback<Optional<T>>(settableFuture) {
+                            @Override
+                            public void onSuccess(Optional<T> data) {
+                                function.apply(data);
+                                super.onSuccess(data);
+                            }
+                        }, MoreExecutors.directExecutor());
+
+                return futures;
+            }
+        }, JOB_MAX_RETRIES);
+    }
+
+    private static InstanceIdentifier<VpnMap> vpnMapIdentifier(Uuid uuid) {
+        return VPN_MAPS_IID.child(VpnMap.class, new VpnMapKey(uuid));
+    }
+
+    private class SettableFutureCallback<T> implements FutureCallback<T> {
+
+        private final SettableFuture<T> settableFuture;
+
+        SettableFutureCallback(SettableFuture<T> settableFuture) {
+            this.settableFuture = settableFuture;
+        }
+
+        @Override
+        public void onSuccess(T objT) {
+            settableFuture.set(objT);
+        }
+
+        @Override
+        public void onFailure(Throwable throwable) {
+            settableFuture.setException(throwable);
+        }
     }
 }
+