Use JvmGlobalLocks in neutronmanager
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronvpnUtils.java
index 2c629a0aa1a8b1ebccfcc0d5153bb0c9f9afb985..3dbf3856a94274294d15b6c39ab389d840aece41 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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,
@@ -8,9 +8,18 @@
 
 package org.opendaylight.netvirt.neutronvpn;
 
+import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
+import static org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils.requireNonNullElse;
+
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableBiMap;
 import com.google.common.collect.Sets;
+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.math.BigInteger;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
@@ -29,19 +38,26 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 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.binding.api.ReadOnlyTransaction;
 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;
 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.netvirt.neutronvpn.api.enums.IpVersionChoice;
@@ -118,6 +134,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev15060
 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;
@@ -191,6 +209,7 @@ public class NeutronvpnUtils {
     private final IdManagerService idManager;
     private final JobCoordinator jobCoordinator;
     private final IPV6InternetDefaultRouteProgrammer ipV6InternetDefRt;
+    private static final int JOB_MAX_RETRIES = 3;
 
     @Inject
     public NeutronvpnUtils(final DataBroker dataBroker, final IdManagerService idManager,
@@ -202,6 +221,7 @@ public class NeutronvpnUtils {
         this.ipV6InternetDefRt = ipV6InternetDefRt;
     }
 
+    @Nullable
     protected Subnetmap getSubnetmap(Uuid subnetId) {
         InstanceIdentifier<Subnetmap> id = buildSubnetMapIdentifier(subnetId);
         Optional<Subnetmap> sn = read(LogicalDatastoreType.CONFIGURATION, id);
@@ -213,6 +233,7 @@ 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();
@@ -224,12 +245,13 @@ 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) {
+            for (VpnMap vpnMap : requireNonNullElse(optionalVpnMaps.get().getVpnMap(),
+                    Collections.<VpnMap>emptyList())) {
                 List<Uuid> netIds = vpnMap.getNetworkIds();
                 if (netIds != null && netIds.contains(network)) {
                     return vpnMap.getVpnId();
@@ -240,6 +262,7 @@ public class NeutronvpnUtils {
         return null;
     }
 
+    @Nullable
     protected Uuid getVpnForSubnet(Uuid subnetId) {
         InstanceIdentifier<Subnetmap> subnetmapIdentifier = buildSubnetMapIdentifier(subnetId);
         Optional<Subnetmap> optionalSubnetMap = read(LogicalDatastoreType.CONFIGURATION,
@@ -251,6 +274,7 @@ public class NeutronvpnUtils {
         return null;
     }
 
+    @Nullable
     protected Uuid getNetworkForSubnet(Uuid subnetId) {
         InstanceIdentifier<Subnetmap> subnetmapIdentifier = buildSubnetMapIdentifier(subnetId);
         Optional<Subnetmap> optionalSubnetMap = read(LogicalDatastoreType.CONFIGURATION,
@@ -263,7 +287,8 @@ 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;
         }
@@ -271,9 +296,23 @@ public class NeutronvpnUtils {
         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())) {
+            for (VpnMap vpnMap : requireNonNullElse(optionalVpnMaps.get().getVpnMap(),
+                    Collections.<VpnMap>emptyList())) {
+                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 (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 (getIsExternal(getNeutronNetwork(vpnMap.getNetworkIds().iterator().next()))) {
+                        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();
@@ -290,18 +329,20 @@ public class NeutronvpnUtils {
         return null;
     }
 
-    protected Uuid getRouterforVpn(Uuid vpnId) {
+    @Nullable
+    protected List<Uuid> getRouterIdListforVpn(Uuid vpnId) {
         InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class).child(VpnMap.class,
                 new VpnMapKey(vpnId)).build();
         Optional<VpnMap> optionalVpnMap = read(LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
         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();
@@ -330,6 +371,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);
@@ -341,6 +383,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);
@@ -351,6 +394,7 @@ public class NeutronvpnUtils {
         return null;
     }
 
+    @Nullable
     protected List<Uuid> getPortIdsFromSubnetId(Uuid subnetId) {
         InstanceIdentifier<Subnetmap> id = buildSubnetMapIdentifier(subnetId);
         Optional<Subnetmap> optionalSubnetmap = read(LogicalDatastoreType.CONFIGURATION, id);
@@ -439,8 +483,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;
         }
@@ -500,10 +545,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;
@@ -546,7 +592,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 {
@@ -568,8 +614,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;
     }
@@ -601,8 +647,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()));
     }
 
     /**
@@ -726,6 +773,7 @@ public class NeutronvpnUtils {
         }
     }
 
+    @Nullable
     protected List<SubnetInfo> getSubnetInfo(Port port) {
         List<FixedIps> portFixedIps = port.getFixedIps();
         if (portFixedIps == null) {
@@ -761,10 +809,26 @@ public class NeutronvpnUtils {
 
         if (sn.isPresent()) {
             subnet = sn.get();
+            addToSubnetCache(subnet);
         }
         return subnet;
     }
 
+    protected List<Subnetmap> getNeutronRouterSubnetMapList(Uuid routerId) {
+        List<Subnetmap> subnetMapList = new ArrayList<>();
+        Optional<Subnetmaps> subnetMaps = read(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.builder(Subnetmaps.class).build());
+        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());
@@ -784,6 +848,7 @@ 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();
@@ -792,7 +857,7 @@ public class NeutronvpnUtils {
                     .class).child(Subnet.class, subnetkey);
             Optional<Subnet> subnet = read(LogicalDatastoreType.CONFIGURATION, subnetidentifier);
             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) {
@@ -813,7 +878,7 @@ 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()
             .withKey(new VpnPortipToPortKey(fixedIp, vpnName))
@@ -821,7 +886,7 @@ public class NeutronvpnUtils {
             .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());
             }
@@ -835,11 +900,12 @@ public class NeutronvpnUtils {
 
     // 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);
             }
@@ -855,15 +921,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();
         }
     }
 
@@ -1061,6 +1130,7 @@ public class NeutronvpnUtils {
         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");
@@ -1131,8 +1201,8 @@ public class NeutronvpnUtils {
         Optional<InterVpnLinks> interVpnLinksOpData = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                 interVpnLinksIid);
         if (interVpnLinksOpData.isPresent()) {
-            List<InterVpnLink> allInterVpnLinks = interVpnLinksOpData.get().getInterVpnLink();
-            for (InterVpnLink interVpnLink : allInterVpnLinks) {
+            for (InterVpnLink interVpnLink : requireNonNullElse(interVpnLinksOpData.get().getInterVpnLink(),
+                    Collections.<InterVpnLink>emptyList())) {
                 if (interVpnLink.getFirstEndpoint().getIpAddress().getValue().equals(endpointIp)
                         || interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(endpointIp)) {
                     return Optional.of(interVpnLink);
@@ -1150,8 +1220,8 @@ public class NeutronvpnUtils {
             MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerDpnId);
         if (neutronRouterDpnsOpt.isPresent()) {
             NeutronRouterDpns neutronRouterDpns = neutronRouterDpnsOpt.get();
-            List<RouterDpnList> routerDpnLists = neutronRouterDpns.getRouterDpnList();
-            for (RouterDpnList routerDpnList : routerDpnLists) {
+            for (RouterDpnList routerDpnList : requireNonNullElse(neutronRouterDpns.getRouterDpnList(),
+                    Collections.<RouterDpnList>emptyList())) {
                 if (routerDpnList.getDpnVpninterfacesList() != null) {
                     for (DpnVpninterfacesList dpnInterfaceList : routerDpnList.getDpnVpninterfacesList()) {
                         if (dpnInterfaceList.getDpnId().equals(dpid)) {
@@ -1164,6 +1234,7 @@ public class NeutronvpnUtils {
         return ret;
     }
 
+    @Nullable
     protected Integer getUniqueRDId(String poolName, String idKey) {
         AllocateIdInput getIdInput = new AllocateIdInputBuilder().setPoolName(poolName).setIdKey(idKey).build();
         try {
@@ -1221,7 +1292,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;
     }
 
@@ -1359,6 +1430,7 @@ 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);
@@ -1401,6 +1473,7 @@ public class NeutronvpnUtils {
         return IpVersionChoice.UNDEFINED;
     }
 
+    @Nullable
     public VpnInstanceOpDataEntry getVpnInstanceOpDataEntryFromVpnId(String vpnName) {
         String primaryRd = getVpnRd(vpnName);
         if (primaryRd == null) {
@@ -1422,40 +1495,29 @@ public class NeutronvpnUtils {
                 .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(primaryRd)).build();
     }
 
-    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) {
@@ -1466,7 +1528,8 @@ public class NeutronvpnUtils {
         Optional<Subnetmaps> allSubnetMaps = read(LogicalDatastoreType.CONFIGURATION, subnetMapsId);
         // 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 : requireNonNullElse(allSubnetMaps.get().getSubnetmap(),
+                Collections.<Subnetmap>emptyList())) {
             if (snMap.getId().equals(sm.getId())) {
                 continue;
             }
@@ -1484,40 +1547,56 @@ 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);
+            VpnInstanceOpDataEntry vpnInstanceOpDataEntry = getVpnInstanceOpDataEntryFromVpnId(vpnName);
+            if (vpnInstanceOpDataEntry == null) {
+                return Collections.emptyList();
+            }
+            if (vpnInstanceOpDataEntry.getType() == VpnInstanceOpDataEntry.Type.L2) {
+                LOG.debug("updateVpnInstanceWithIpFamily: Update VpnInstance {} with ipFamily {}."
+                        + "VpnInstanceOpDataEntry is L2 instance. Do nothing.", vpnName, ipVersion);
+                return Collections.emptyList();
             }
-            if (isFinalVpnInstanceIpv6Changed) {
-                builder.setIpv6Configured(finalIsIpv6Configured);
+            if (ipVersion == IpVersionChoice.UNDEFINED) {
+                LOG.debug("updateVpnInstanceWithIpFamily: Update VpnInstance {} with Undefined address family"
+                        + "is not allowed. Do nothing", vpnName);
+                return Collections.emptyList();
             }
-            return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
-                InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.builder(VpnInstanceOpData.class)
-                        .child(VpnInstanceOpDataEntry.class,
-                                new VpnInstanceOpDataEntryKey(vpnInstanceOpDataEntry.getVrfId())).build();
-                tx.merge(LogicalDatastoreType.OPERATIONAL, id, builder.build(), false);
-                LOG.info("updateVpnInstanceWithIpFamily: Successfully {} {} to Vpn {}",
-                        add ? "added" : "removed",
-                        ipVersion.toString(), vpnName);
-            }));
+            VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder(vpnInstanceOpDataEntry);
+            boolean ipConfigured = add;
+            if (ipVersion.isIpVersionChosen(IpVersionChoice.IPV4AND6)) {
+                builder.setIpv4Configured(ipConfigured);
+                builder.setIpv6Configured(ipConfigured);
+            } else if (ipVersion.isIpVersionChosen(IpVersionChoice.IPV4)) {
+                builder.setIpv4Configured(ipConfigured);
+            } else if (ipVersion.isIpVersionChosen(IpVersionChoice.IPV6)) {
+                builder.setIpv6Configured(ipConfigured);
+            }
+            return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
+                    OPERATIONAL, tx -> {
+                    InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier
+                            .builder(VpnInstanceOpData.class).child(VpnInstanceOpDataEntry.class,
+                                        new VpnInstanceOpDataEntryKey(vpnInstanceOpDataEntry.getVrfId())).build();
+                    tx.merge(id, builder.build(), false);
+                    LOG.info("updateVpnInstanceWithIpFamily: Successfully {} {} to Vpn {}",
+                            add == true ? "added" : "removed", ipVersion, vpnName);
+                }));
         });
     }
 
@@ -1528,6 +1607,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;
@@ -1568,6 +1648,7 @@ 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
      */
+    @Nullable
     public Uuid getInternetvpnUuidBoundToSubnetRouter(@Nonnull Uuid subnetUuid) {
         Subnetmap subnetmap = getSubnetmap(subnetUuid);
         Uuid routerUuid = subnetmap.getRouterId();
@@ -1584,50 +1665,79 @@ 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();
+        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<BigInteger> dpnIds = getDpnsForRouter(rtrId.getValue());
+            if (dpnIds.isEmpty()) {
+                continue;
+            }
+            VpnInstanceOpDataEntry vpnOpDataEntry = getVpnInstanceOpDataEntryFromVpnId(rtrId.getValue());
+            Long routerIdAsLong = vpnOpDataEntry.getVpnId();
+            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 (BigInteger dpnId : dpnIds) {
+                if (add) {
+                    ipV6InternetDefRt.installDefaultRoute(dpnId, rtrId.getValue(), internetBgpVpnId, vpnId);
+                } else {
+                    ipV6InternetDefRt.removeDefaultRoute(dpnId, rtrId.getValue(), internetBgpVpnId, vpnId);
                 }
             }
         }
@@ -1658,12 +1768,73 @@ public class NeutronvpnUtils {
         }
         VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder(vpnInstanceOpDataEntry);
         builder.setBgpvpnType(choice);
-        ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
-            tx.merge(LogicalDatastoreType.OPERATIONAL, id, builder.build(), false);
+        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 List<Uuid> getAssociateRouterInputRouterIdsListUuid(List<RouterIds> routerIds) {
+        if (routerIds == null) {
+            return Collections.emptyList();
+        }
+        return routerIds.stream().map(
+            routerId -> routerId.getRouterId()).collect(Collectors.toList());
+    }
+
+    public List<Uuid> getDisassociateRouterInputRouterIdsListUuid(List<RouterIds> routerIds) {
+        if (routerIds == null) {
+            return Collections.emptyList();
+        }
+        return routerIds.stream().map(
+            routerId -> routerId.getRouterId()).collect(Collectors.toList());
+    }
+
+    public RouterIds getvpnMapRouterIds(Uuid routerId) {
+        return new RouterIdsBuilder().setRouterId(routerId).build();
+    }
+
+    public void removeVpnMapRouterIdsFromList(Uuid routerId, List<RouterIds> vpnRouterIds) {
+        Iterator<RouterIds> vpnRouterIdIter = vpnRouterIds.iterator();
+        while (vpnRouterIdIter.hasNext()) {
+            RouterIds vpnRouterId = vpnRouterIdIter.next();
+            if (vpnRouterId.getRouterId().getValue().equals(routerId.getValue())) {
+                vpnRouterIdIter.remove();
+                return;
+            }
+        }
+        return;
+    }
+
+    public boolean vpnMapRouterIdsContainsRouterId(Uuid routerId, List<RouterIds> vpnRouterIds) {
+        if (routerId == null) {
+            return false;
+        }
+        return vpnRouterIds.stream().anyMatch(vpnRouterId ->
+              vpnRouterId.getRouterId().getValue().equals(routerId.getValue()));
+    }
+
+    public List<Uuid> getVpnInstanceRouterIdsListUuid(List<RouterIds> routerIds) {
+        if (routerIds == null) {
+            return Collections.emptyList();
+        }
+        return routerIds.stream().map(
+            routerId -> routerId.getRouterId()).collect(Collectors.toList());
+    }
+
+    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 listRouterIds;
+    }
+
     @Nonnull
     public List<BigInteger> getDpnsForRouter(String routerUuid) {
         InstanceIdentifier id = InstanceIdentifier.builder(NeutronRouterDpns.class)
@@ -1673,15 +1844,16 @@ public class NeutronvpnUtils {
                         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());
             }
         }
         return dpns;
     }
 
-    public String getRouterIdfromVpnInstance(String vpnName) {
+    @Nullable
+    public List<Uuid> getRouterIdsfromVpnInstance(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();
@@ -1689,15 +1861,13 @@ public class NeutronvpnUtils {
                 .syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
                         LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
         if (!optionalVpnMap.isPresent()) {
-            LOG.error("getRouterIdfromVpnInstance : Router not found for vpn : {}", vpnName);
+            LOG.error("getRouterIdsfromVpnInstance : Router not found for vpn : {}", vpnName);
             return null;
         }
-        Uuid routerId = optionalVpnMap.get().getRouterId();
-        if (routerId != null) {
-            return routerId.getValue();
-        }
-        LOG.info("getRouterIdfromVpnInstance : Router not found for vpn : {}", vpnName);
-        return null;
+        List<Uuid> rtrIds = optionalVpnMap.get().getRouterIds().stream().map(routerIds -> routerIds.getRouterId())
+                .collect(Collectors.toList());
+        return rtrIds;
+
     }
 
     public InstanceIdentifier<Router> buildNeutronRouterIdentifier(Uuid routerUuid) {
@@ -1705,4 +1875,92 @@ public class NeutronvpnUtils {
              .child(Routers.class).child(Router.class, new RouterKey(routerUuid));
         return routerInstanceIdentifier;
     }
+
+    @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.error("getSubnetmapListFromNetworkId: Failed as subnetIdList is null for network {}",
+                networkId.getValue());
+        return null;
+    }
+
+    @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).toJavaUtil().map(
+                org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
+                        .VpnInstance::getVpnId).orElse(null);
+    }
+
+    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 (ReadOnlyTransaction 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 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);
+        }
+    }
 }
+