Refactor Ipv6Service module.
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnUtil.java
old mode 100755 (executable)
new mode 100644 (file)
index 081cc5e..1cf07c8
@@ -11,9 +11,7 @@ package org.opendaylight.netvirt.vpnmanager;
 import com.google.common.base.Optional;
 import com.google.common.collect.Iterators;
 import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
 import java.math.BigInteger;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
@@ -38,10 +36,13 @@ import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
 import org.opendaylight.genius.mdsalutil.FlowEntity;
 import org.opendaylight.genius.mdsalutil.FlowEntityBuilder;
@@ -56,10 +57,12 @@ import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetDestination;
+import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
 import org.opendaylight.genius.utils.ServiceIndex;
 import org.opendaylight.genius.utils.SystemPropertyReader;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
 import org.opendaylight.netvirt.elanmanager.api.ElanHelper;
 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
@@ -68,6 +71,7 @@ import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
 import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
+import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
 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;
@@ -80,6 +84,7 @@ import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev14081
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames.AssociatedSubnetType;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNamesBuilder;
 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;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
@@ -94,6 +99,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdPools;
 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.genius.idmanager.rev160406.id.pools.IdPool;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPoolKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.IfIndexesInterfaceMap;
@@ -104,12 +110,18 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpc
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.Ipv6NdUtilService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationToOfGroupInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationToOfGroupInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationToOfGroupOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.TimeUnits;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.TryLockInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.TryLockInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.TryLockOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.UnlockInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.UnlockInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.UnlockOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanTagNameMap;
@@ -140,6 +152,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adj
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOpBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.LearntVpnVipToPortData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.LearntVpnVipToPortEventAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.LearntVpnVipToPortEventData;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.PrefixToInterface;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.RouterInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.SubnetOpData;
@@ -154,6 +168,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adj
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPortBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPortKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.event.data.LearntVpnVipToPortEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.event.data.LearntVpnVipToPortEventBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.event.data.LearntVpnVipToPortEventKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.VpnIds;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.VpnIdsBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.VpnIdsKey;
@@ -207,6 +224,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.ext.rev150712.NetworkL3Extension;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.NetworkKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
 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;
@@ -214,26 +232,49 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.s
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
 import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.impl.schema.tree.SchemaValidationFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public final class VpnUtil {
+
     private static final Logger LOG = LoggerFactory.getLogger(VpnUtil.class);
-    private static final int DEFAULT_PREFIX_LENGTH = 32;
-    private static final String PREFIX_SEPARATOR = "/";
+
+    static final int SINGLE_TRANSACTION_BROKER_NO_RETRY = 1;
+
+    /**
+     * Class to generate timestamps with microsecond precision.
+     * For example: MicroTimestamp.INSTANCE.get() = "2012-10-21 19:13:45.267128"
+     */
+    public enum MicroTimestamp {
+        INSTANCE ;
+
+        private long              startDate ;
+        private long              startNanoseconds ;
+        private SimpleDateFormat  dateFormat ;
+
+        MicroTimestamp() {
+            this.startDate = System.currentTimeMillis() ;
+            this.startNanoseconds = System.nanoTime() ;
+            this.dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") ;
+        }
+
+        public String get() {
+            long microSeconds = (System.nanoTime() - this.startNanoseconds) / 1000 ;
+            long date = this.startDate + microSeconds / 1000 ;
+            return this.dateFormat.format(date) + String.format("%03d", microSeconds % 1000) ;
+        }
+    }
 
     private VpnUtil() {
     }
 
-    static InstanceIdentifier<VpnInterface> getVpnInterfaceIdentifier(String vpnInterfaceName) {
+    public static InstanceIdentifier<VpnInterface> getVpnInterfaceIdentifier(String vpnInterfaceName) {
         return InstanceIdentifier.builder(VpnInterfaces.class)
             .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).build();
     }
 
-    static InstanceIdentifier<VpnInterfaceOpDataEntry> getVpnInterfaceOpDataEntryIdentifier(
+    public static InstanceIdentifier<VpnInterfaceOpDataEntry> getVpnInterfaceOpDataEntryIdentifier(
                                                              String vpnInterfaceName, String vpnName) {
         return InstanceIdentifier.builder(VpnInterfaceOpData.class)
             .child(VpnInterfaceOpDataEntry.class,
@@ -254,7 +295,7 @@ public final class VpnUtil {
     static VpnInterfaceOpDataEntry getVpnInterfaceOpDataEntry(String intfName, String vpnName,
                                         AdjacenciesOp aug, BigInteger dpnId,
                                         Boolean isSheduledForRemove, long lportTag, String gwMac) {
-        return new VpnInterfaceOpDataEntryBuilder().setKey(new VpnInterfaceOpDataEntryKey(intfName, vpnName))
+        return new VpnInterfaceOpDataEntryBuilder().withKey(new VpnInterfaceOpDataEntryKey(intfName, vpnName))
             .setDpnId(dpnId).setScheduledForRemove(isSheduledForRemove).addAugmentation(AdjacenciesOp.class, aug)
                 .setLportTag(lportTag).setGatewayMacAddress(gwMac).build();
     }
@@ -284,7 +325,7 @@ public final class VpnUtil {
     }
 
     static VpnIds getPrefixToInterface(long vpnId) {
-        return new VpnIdsBuilder().setKey(new VpnIdsKey(vpnId)).setVpnId(vpnId).build();
+        return new VpnIdsBuilder().withKey(new VpnIdsKey(vpnId)).setVpnId(vpnId).build();
     }
 
     static Prefixes getPrefixToInterface(BigInteger dpId, String vpnInterfaceName, String ipPrefix, Uuid subnetId,
@@ -377,7 +418,7 @@ public final class VpnUtil {
         .instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces> getDpnVpnInterfaces(DataBroker broker,
         VpnInstance vpnInstance, BigInteger dpnId) {
         String primaryRd = getPrimaryRd(vpnInstance);
-        InstanceIdentifier<VpnToDpnList> dpnToVpnId = getVpnToDpnListIdentifier(primaryRd, dpnId);
+        InstanceIdentifier<VpnToDpnList> dpnToVpnId = VpnHelper.getVpnToDpnListIdentifier(primaryRd, dpnId);
         Optional<VpnToDpnList> dpnInVpn = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, dpnToVpnId);
         return dpnInVpn.isPresent() ? dpnInVpn.get().getVpnInterfaces() : Collections.emptyList();
     }
@@ -405,7 +446,7 @@ public final class VpnUtil {
         return null;
     }
 
-    static List<Adjacency> getAdjacenciesForVpnInterfaceFromConfig(DataBroker broker, String intfName) {
+    public static List<Adjacency> getAdjacenciesForVpnInterfaceFromConfig(DataBroker broker, String intfName) {
         final InstanceIdentifier<VpnInterface> identifier = getVpnInterfaceIdentifier(intfName);
         InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
         Optional<Adjacencies> adjacencies = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, path);
@@ -439,10 +480,10 @@ public final class VpnUtil {
     }
 
     static AllocatedRdsBuilder getRdsBuilder(String nexthop, String rd) {
-        return new AllocatedRdsBuilder().setKey(new AllocatedRdsKey(nexthop)).setNexthop(nexthop).setRd(rd);
+        return new AllocatedRdsBuilder().withKey(new AllocatedRdsKey(nexthop)).setNexthop(nexthop).setRd(rd);
     }
 
-    static Adjacencies getVpnInterfaceAugmentation(List<Adjacency> nextHopList) {
+    public static Adjacencies getVpnInterfaceAugmentation(List<Adjacency> nextHopList) {
         return new AdjacenciesBuilder().setAdjacency(nextHopList).build();
     }
 
@@ -465,12 +506,6 @@ public final class VpnUtil {
             .child(Interface.class, new InterfaceKey(interfaceName)).build();
     }
 
-    static InstanceIdentifier<VpnToDpnList> getVpnToDpnListIdentifier(String rd, BigInteger dpnId) {
-        return InstanceIdentifier.builder(VpnInstanceOpData.class)
-            .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd))
-            .child(VpnToDpnList.class, new VpnToDpnListKey(dpnId)).build();
-    }
-
     public static BigInteger getCookieArpFlow(int interfaceTag) {
         return VpnConstants.COOKIE_L3_BASE.add(new BigInteger("0110000", 16)).add(
             BigInteger.valueOf(interfaceTag));
@@ -507,8 +542,7 @@ public final class VpnUtil {
     public static void releaseId(IdManagerService idManager, 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();
+            RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
             if (!rpcResult.isSuccessful()) {
                 LOG.error("releaseId: RPC Call to release Id for key {} from pool {} returned with Errors {}",
                         idKey, poolName, rpcResult.getErrors());
@@ -606,14 +640,15 @@ public final class VpnUtil {
         Optional<VrfTables> vrfTablesOpc = read(broker, LogicalDatastoreType.CONFIGURATION, vpnVrfTableIid);
         if (vrfTablesOpc.isPresent()) {
             VrfTables vrfTables = vrfTablesOpc.get();
-            WriteTransaction tx = broker.newWriteOnlyTransaction();
-            for (VrfEntry vrfEntry : vrfTables.getVrfEntry()) {
-                if (origin == RouteOrigin.value(vrfEntry.getOrigin())) {
-                    tx.delete(LogicalDatastoreType.CONFIGURATION,
-                            vpnVrfTableIid.child(VrfEntry.class, vrfEntry.getKey()));
-                }
-            }
-            tx.submit();
+            ListenableFutures.addErrorLogging(
+                    new ManagedNewTransactionRunnerImpl(broker).callWithNewWriteOnlyTransactionAndSubmit(tx -> {
+                        for (VrfEntry vrfEntry : vrfTables.getVrfEntry()) {
+                            if (origin == RouteOrigin.value(vrfEntry.getOrigin())) {
+                                tx.delete(LogicalDatastoreType.CONFIGURATION,
+                                        vpnVrfTableIid.child(VrfEntry.class, vrfEntry.key()));
+                            }
+                        }
+                    }), LOG, "Error removing VRF entries by origin");
         }
     }
 
@@ -638,11 +673,13 @@ public final class VpnUtil {
     public static void removeVrfEntries(DataBroker broker, String rd, List<VrfEntry> vrfEntries) {
         InstanceIdentifier<VrfTables> vpnVrfTableIid =
             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd)).build();
-        WriteTransaction tx = broker.newWriteOnlyTransaction();
-        for (VrfEntry vrfEntry : vrfEntries) {
-            tx.delete(LogicalDatastoreType.CONFIGURATION, vpnVrfTableIid.child(VrfEntry.class, vrfEntry.getKey()));
-        }
-        tx.submit();
+        ListenableFutures.addErrorLogging(
+                new ManagedNewTransactionRunnerImpl(broker).callWithNewWriteOnlyTransactionAndSubmit(tx -> {
+                    for (VrfEntry vrfEntry : vrfEntries) {
+                        tx.delete(LogicalDatastoreType.CONFIGURATION,
+                                vpnVrfTableIid.child(VrfEntry.class, vrfEntry.key()));
+                    }
+                }), LOG, "Error removing VRF entries");
     }
 
     // TODO Clean up the exception handling
@@ -727,7 +764,7 @@ public final class VpnUtil {
     }
 
     static RouterInterface getRouterInterface(String interfaceName, String routerName) {
-        return new RouterInterfaceBuilder().setKey(new RouterInterfaceKey(interfaceName))
+        return new RouterInterfaceBuilder().withKey(new RouterInterfaceKey(interfaceName))
             .setInterfaceName(interfaceName).setRouterName(routerName).build();
     }
 
@@ -777,7 +814,8 @@ public final class VpnUtil {
         return read(broker, LogicalDatastoreType.CONFIGURATION, interfaceId).isPresent();
     }
 
-    static Optional<List<String>> getVpnHandlingIpv4AssociatedWithInterface(DataBroker broker, String interfaceName) {
+    public static Optional<List<String>> getVpnHandlingAssociatedWithInterface(DataBroker broker,
+            String interfaceName) {
         InstanceIdentifier<VpnInterface> interfaceId = getVpnInterfaceIdentifier(interfaceName);
         Optional<List<String>> vpnOptional = Optional.absent();
         Optional<VpnInterface> optConfiguredVpnInterface = read(broker, LogicalDatastoreType.CONFIGURATION,
@@ -789,9 +827,6 @@ public final class VpnUtil {
             if (optVpnInstanceList.isPresent()) {
                 List<String> vpnList = new ArrayList<>();
                 for (VpnInstanceNames vpnInstance : optVpnInstanceList.get()) {
-                    if (vpnInstance.getAssociatedSubnetType().equals(AssociatedSubnetType.V6Subnet)) {
-                        continue;
-                    }
                     vpnList.add(vpnInstance.getVpnName());
                 }
                 vpnOptional = Optional.of(vpnList);
@@ -803,7 +838,7 @@ public final class VpnUtil {
     public static String getIpPrefix(String prefix) {
         String[] prefixValues = prefix.split("/");
         if (prefixValues.length == 1) {
-            prefix = prefix + PREFIX_SEPARATOR + DEFAULT_PREFIX_LENGTH;
+            prefix = NWUtil.toIpPrefix(prefix);
         }
         return prefix;
     }
@@ -822,87 +857,34 @@ public final class VpnUtil {
 
         };
 
-    public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
+    @Deprecated
+    private static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
                                                           InstanceIdentifier<T> path) {
-        try (ReadOnlyTransaction tx = broker.newReadOnlyTransaction()) {
-            return tx.read(datastoreType, path).get();
-        } catch (InterruptedException | ExecutionException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public static <T extends DataObject> void asyncUpdate(DataBroker broker, LogicalDatastoreType datastoreType,
-        InstanceIdentifier<T> path, T data) {
-        asyncUpdate(broker, datastoreType, path, data, DEFAULT_CALLBACK);
-    }
-
-    public static <T extends DataObject> void asyncUpdate(DataBroker broker, LogicalDatastoreType datastoreType,
-        InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
-        WriteTransaction tx = broker.newWriteOnlyTransaction();
-        tx.merge(datastoreType, path, data, true);
-        Futures.addCallback(tx.submit(), callback, MoreExecutors.directExecutor());
-    }
-
-    public static <T extends DataObject> void asyncWrite(DataBroker broker, LogicalDatastoreType datastoreType,
-        InstanceIdentifier<T> path, T data) {
-        asyncWrite(broker, datastoreType, path, data, DEFAULT_CALLBACK);
-    }
-
-    public static <T extends DataObject> void asyncWrite(DataBroker broker, LogicalDatastoreType datastoreType,
-        InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
-        WriteTransaction tx = broker.newWriteOnlyTransaction();
-        tx.put(datastoreType, path, data, WriteTransaction.CREATE_MISSING_PARENTS);
-        Futures.addCallback(tx.submit(), callback, MoreExecutors.directExecutor());
-    }
-
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    public static <T extends DataObject> void tryDelete(DataBroker broker, LogicalDatastoreType datastoreType,
-        InstanceIdentifier<T> path) {
         try {
-            delete(broker, datastoreType, path, DEFAULT_CALLBACK);
-        } catch (SchemaValidationFailedException sve) {
-            LOG.info("tryDelete: Could not delete {}. SchemaValidationFailedException: {}", path, sve.getMessage());
-        } catch (Exception e) {
-            LOG.info("tryDelete: Could not delete {}. Unhandled error: {}", path, e.getMessage());
+            return SingleTransactionDataBroker.syncReadOptional(broker, datastoreType, path);
+        } catch (ReadFailedException e) {
+            throw new RuntimeException(e);
         }
     }
 
-    public static <T extends DataObject> void delete(DataBroker broker, LogicalDatastoreType datastoreType,
-        InstanceIdentifier<T> path) {
-        delete(broker, datastoreType, path, DEFAULT_CALLBACK);
-    }
-
-
-    public static <T extends DataObject> void delete(DataBroker broker, LogicalDatastoreType datastoreType,
-        InstanceIdentifier<T> path, FutureCallback<Void> callback) {
-        WriteTransaction tx = broker.newWriteOnlyTransaction();
-        tx.delete(datastoreType, path);
-        Futures.addCallback(tx.submit(), callback, MoreExecutors.directExecutor());
-    }
-
+    @Deprecated
     public static <T extends DataObject> void syncWrite(DataBroker broker, LogicalDatastoreType datastoreType,
         InstanceIdentifier<T> path, T data) {
-        WriteTransaction tx = broker.newWriteOnlyTransaction();
-        tx.put(datastoreType, path, data, WriteTransaction.CREATE_MISSING_PARENTS);
-
         try {
-            tx.submit().get();
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("syncWrite: Error writing to datastore (path, data) : ({}, {})", path, data);
+            SingleTransactionDataBroker.syncWrite(broker, datastoreType, path, data);
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("syncWrite: Error writing to datastore (path, data) : ({}, {})", path, data, e);
             throw new RuntimeException(e.getMessage(), e);
         }
     }
 
+    @Deprecated
     public static <T extends DataObject> void syncUpdate(DataBroker broker, LogicalDatastoreType datastoreType,
         InstanceIdentifier<T> path, T data) {
-        WriteTransaction tx = broker.newWriteOnlyTransaction();
-        tx.merge(datastoreType, path, data, true);
-
         try {
-            tx.submit().get();
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("syncUpdate: Error writing to datastore (path, data) : ({}, {})", path, data);
+            SingleTransactionDataBroker.syncUpdate(broker, datastoreType, path, data);
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("syncUpdate: Error writing to datastore (path, data) : ({}, {})", path, data, e);
             throw new RuntimeException(e.getMessage(), e);
         }
     }
@@ -964,138 +946,37 @@ public final class VpnUtil {
                 .build();
     }
 
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    public static void removePrefixToInterfaceForVpnId(DataBroker broker, long vpnId, WriteTransaction writeTxn) {
-        try {
-            // Clean up PrefixToInterface Operational DS
-            if (writeTxn != null) {
-                writeTxn.delete(LogicalDatastoreType.OPERATIONAL,
-                    InstanceIdentifier.builder(PrefixToInterface.class).child(
-                        VpnIds.class, new VpnIdsKey(vpnId)).build());
-            } else {
-                delete(broker, LogicalDatastoreType.OPERATIONAL,
-                    InstanceIdentifier.builder(PrefixToInterface.class).child(VpnIds.class,
-                        new VpnIdsKey(vpnId)).build(),
-                    DEFAULT_CALLBACK);
-            }
-        } catch (Exception e) {
-            LOG.error("removePrefixToInterfaceForVpnId: Exception during cleanup of PrefixToInterface for VPN ID {}",
-                    vpnId, e);
-        }
+    public static void removePrefixToInterfaceForVpnId(long vpnId, @Nonnull WriteTransaction operTx) {
+        // Clean up PrefixToInterface Operational DS
+        operTx.delete(LogicalDatastoreType.OPERATIONAL,
+            InstanceIdentifier.builder(PrefixToInterface.class).child(VpnIds.class, new VpnIdsKey(vpnId)).build());
     }
 
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    public static void removeVpnExtraRouteForVpn(DataBroker broker, String vpnName, WriteTransaction writeTxn) {
-        try {
-            // Clean up VPNExtraRoutes Operational DS
-            if (writeTxn != null) {
-                writeTxn.delete(LogicalDatastoreType.OPERATIONAL,
-                        InstanceIdentifier.builder(VpnToExtraroutes.class)
-                                .child(Vpn.class, new VpnKey(vpnName)).build());
-            } else {
-                delete(broker, LogicalDatastoreType.OPERATIONAL,
-                        InstanceIdentifier.builder(VpnToExtraroutes.class)
-                                .child(Vpn.class, new VpnKey(vpnName)).build(),
-                        DEFAULT_CALLBACK);
-            }
-        } catch (Exception e) {
-            LOG.error("removeVpnExtraRouteForVpna: Exception during cleanup of VPNToExtraRoute for VPN {}",
-                    vpnName, e);
-        }
+    public static void removeVpnExtraRouteForVpn(String vpnName, @Nonnull WriteTransaction operTx) {
+        // Clean up VPNExtraRoutes Operational DS
+        operTx.delete(LogicalDatastoreType.OPERATIONAL,
+                InstanceIdentifier.builder(VpnToExtraroutes.class).child(Vpn.class, new VpnKey(vpnName)).build());
     }
 
     // TODO Clean up the exception handling
     @SuppressWarnings("checkstyle:IllegalCatch")
-    public static void removeVpnOpInstance(DataBroker broker, String vpnName, WriteTransaction writeTxn) {
-        try {
-            // Clean up VPNInstanceOpDataEntry
-            if (writeTxn != null) {
-                writeTxn.delete(LogicalDatastoreType.OPERATIONAL, getVpnInstanceOpDataIdentifier(vpnName));
-            } else {
-                delete(broker, LogicalDatastoreType.OPERATIONAL, getVpnInstanceOpDataIdentifier(vpnName),
-                    DEFAULT_CALLBACK);
-            }
-        } catch (Exception e) {
-            LOG.error("removeVpnOpInstance: Exception during cleanup of VPNInstanceOpDataEntry for VPN {}",
-                    vpnName, e);
-        }
+    public static void removeVpnOpInstance(String vpnName, @Nonnull WriteTransaction operTx) {
+        // Clean up VPNInstanceOpDataEntry
+        operTx.delete(LogicalDatastoreType.OPERATIONAL, getVpnInstanceOpDataIdentifier(vpnName));
     }
 
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    public static void removeVpnInstanceToVpnId(DataBroker broker, String vpnName, WriteTransaction writeTxn) {
-        try {
-            if (writeTxn != null) {
-                writeTxn.delete(LogicalDatastoreType.CONFIGURATION,
-                                VpnOperDsUtils.getVpnInstanceToVpnIdIdentifier(vpnName));
-            } else {
-                delete(broker, LogicalDatastoreType.CONFIGURATION,
-                       VpnOperDsUtils.getVpnInstanceToVpnIdIdentifier(vpnName),
-                    DEFAULT_CALLBACK);
-            }
-        } catch (Exception e) {
-            LOG.error("removeVpnInstanceToVpnId: Exception during clean up of VpnInstanceToVpnId for VPN {}",
-                    vpnName, e);
-        }
+    public static void removeVpnInstanceToVpnId(String vpnName, @Nonnull WriteTransaction confTx) {
+        confTx.delete(LogicalDatastoreType.CONFIGURATION, VpnOperDsUtils.getVpnInstanceToVpnIdIdentifier(vpnName));
     }
 
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    public static void removeVpnIdToVpnInstance(DataBroker broker, long vpnId, WriteTransaction writeTxn) {
-        try {
-            if (writeTxn != null) {
-                writeTxn.delete(LogicalDatastoreType.CONFIGURATION, getVpnIdToVpnInstanceIdentifier(vpnId));
-            } else {
-                delete(broker, LogicalDatastoreType.CONFIGURATION, getVpnIdToVpnInstanceIdentifier(vpnId),
-                    DEFAULT_CALLBACK);
-            }
-        } catch (Exception e) {
-            LOG.error("removeVpnIdToVpnInstance: Exception during clean up of VpnIdToVpnInstance for VPNID {}",
-                    vpnId, e);
-        }
+    public static void removeVpnIdToVpnInstance(long vpnId, @Nonnull WriteTransaction confTx) {
+        confTx.delete(LogicalDatastoreType.CONFIGURATION, getVpnIdToVpnInstanceIdentifier(vpnId));
     }
 
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    public static void removeVrfTableForVpn(DataBroker broker, String vpnName, WriteTransaction writeTxn) {
-        // Clean up FIB Entries Config DS
-        try {
-            if (writeTxn != null) {
-                writeTxn.delete(LogicalDatastoreType.CONFIGURATION,
-                    InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class,
-                        new VrfTablesKey(vpnName)).build());
-            } else {
-                delete(broker, LogicalDatastoreType.CONFIGURATION,
-                    InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class,
-                        new VrfTablesKey(vpnName)).build(),
-                    DEFAULT_CALLBACK);
-            }
-        } catch (Exception e) {
-            LOG.error("removeVrfTableForVpn: Exception during clean up of VrfTable from FIB for VPN {}",
-                    vpnName, e);
-        }
-    }
-
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    public static void removeL3nexthopForVpnId(DataBroker broker, long vpnId, WriteTransaction writeTxn) {
-        try {
-            // Clean up L3NextHop Operational DS
-            if (writeTxn != null) {
-                writeTxn.delete(LogicalDatastoreType.OPERATIONAL,
-                    InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
-                        new VpnNexthopsKey(vpnId)).build());
-            } else {
-                delete(broker, LogicalDatastoreType.OPERATIONAL,
-                    InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
-                        new VpnNexthopsKey(vpnId)).build(),
-                    DEFAULT_CALLBACK);
-            }
-        } catch (Exception e) {
-            LOG.error("removeL3nexthopForVpnId: Exception during cleanup of L3NextHop for VPN ID {}", vpnId, e);
-        }
+    public static void removeL3nexthopForVpnId(long vpnId, @Nonnull WriteTransaction operTx) {
+        // Clean up L3NextHop Operational DS
+        operTx.delete(LogicalDatastoreType.OPERATIONAL,
+            InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class, new VpnNexthopsKey(vpnId)).build());
     }
 
     public static void scheduleVpnInterfaceForRemoval(DataBroker broker,String interfaceName, BigInteger dpnId,
@@ -1104,7 +985,7 @@ public final class VpnUtil {
         InstanceIdentifier<VpnInterfaceOpDataEntry> interfaceId =
             VpnUtil.getVpnInterfaceOpDataEntryIdentifier(interfaceName, vpnInstanceName);
         VpnInterfaceOpDataEntry interfaceToUpdate =
-            new VpnInterfaceOpDataEntryBuilder().setKey(new VpnInterfaceOpDataEntryKey(interfaceName,
+            new VpnInterfaceOpDataEntryBuilder().withKey(new VpnInterfaceOpDataEntryKey(interfaceName,
             vpnInstanceName)).setName(interfaceName).setDpnId(dpnId).setVpnInstanceName(vpnInstanceName)
             .setScheduledForRemove(isScheduledToRemove).build();
         if (writeOperTxn != null) {
@@ -1114,18 +995,22 @@ public final class VpnUtil {
         }
     }
 
-    protected static void createLearntVpnVipToPort(DataBroker broker, String vpnName, String fixedIp, String
-            portName, String macAddress) {
+    public static void createLearntVpnVipToPort(DataBroker broker, String vpnName, String fixedIp, String
+            portName, String macAddress, WriteTransaction writeOperTxn) {
         synchronized ((vpnName + fixedIp).intern()) {
             InstanceIdentifier<LearntVpnVipToPort> id = buildLearntVpnVipToPortIdentifier(vpnName, fixedIp);
             LearntVpnVipToPortBuilder builder =
-                    new LearntVpnVipToPortBuilder().setKey(new LearntVpnVipToPortKey(fixedIp, vpnName)).setVpnName(
+                    new LearntVpnVipToPortBuilder().withKey(new LearntVpnVipToPortKey(fixedIp, vpnName)).setVpnName(
                             vpnName).setPortFixedip(fixedIp).setPortName(portName)
                             .setMacAddress(macAddress.toLowerCase(Locale.getDefault()))
                             .setCreationTime(new SimpleDateFormat("MM/dd/yyyy h:mm:ss a").format(new Date()));
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, id, builder.build());
-            LOG.debug("createLearntVpnVipToPort: ARP learned for fixedIp: {}, vpn {}, interface {}, mac {},"
-                    + " added to VpnPortipToPort DS", fixedIp, vpnName, portName, macAddress);
+            if (writeOperTxn != null) {
+                writeOperTxn.put(LogicalDatastoreType.OPERATIONAL, id, builder.build(), true);
+            } else {
+                MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, id, builder.build());
+            }
+            LOG.debug("createLearntVpnVipToPort: ARP/NA learned for fixedIp: {}, vpn {}, interface {}, mac {},"
+                    + " added to LearntVpnVipToPort DS", fixedIp, vpnName, portName, macAddress);
         }
     }
 
@@ -1135,12 +1020,111 @@ public final class VpnUtil {
                 new LearntVpnVipToPortKey(fixedIp, vpnName)).build();
     }
 
-    protected static void removeLearntVpnVipToPort(DataBroker broker, String vpnName, String fixedIp) {
+    public static void removeLearntVpnVipToPort(DataBroker broker, String vpnName, String fixedIp,
+            WriteTransaction writeOperTxn) {
         synchronized ((vpnName + fixedIp).intern()) {
             InstanceIdentifier<LearntVpnVipToPort> id = buildLearntVpnVipToPortIdentifier(vpnName, fixedIp);
+            if (writeOperTxn != null) {
+                writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, id);
+            } else {
+                MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, id);
+            }
+            LOG.debug("removeLearntVpnVipToPort: Deleted LearntVpnVipToPort entry for fixedIp: {}, vpn {}",
+                fixedIp, vpnName);
+        }
+    }
+
+    protected static void removeVpnPortFixedIpToPort(DataBroker broker, String vpnName, String fixedIp,
+                                                     WriteTransaction writeConfigTxn) {
+        synchronized ((vpnName + fixedIp).intern()) {
+            InstanceIdentifier<VpnPortipToPort> id = buildVpnPortipToPortIdentifier(vpnName, fixedIp);
+            if (writeConfigTxn != null) {
+                writeConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, id);
+            } else {
+                MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, id);
+            }
+            LOG.debug("removeVpnPortFixedIpToPort: Deleted VpnPortipToPort entry for fixedIp: {}, vpn {}",
+                fixedIp, vpnName);
+        }
+    }
+
+    public static void createLearntVpnVipToPortEvent(DataBroker broker, String vpnName, String srcIp, String destIP,
+                                                        String portName, String macAddress,
+                                                        LearntVpnVipToPortEventAction action,
+                                                        WriteTransaction writeOperTxn) {
+        String eventId = MicroTimestamp.INSTANCE.get();
+
+        InstanceIdentifier<LearntVpnVipToPortEvent> id = buildLearntVpnVipToPortEventIdentifier(eventId);
+        LearntVpnVipToPortEventBuilder builder = new LearntVpnVipToPortEventBuilder().withKey(
+                new LearntVpnVipToPortEventKey(eventId)).setVpnName(vpnName).setSrcFixedip(srcIp)
+                .setDestFixedip(destIP).setPortName(portName)
+                .setMacAddress(macAddress.toLowerCase(Locale.getDefault())).setEventAction(action);
+        if (writeOperTxn != null) {
+            writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, id);
+        } else {
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, id, builder.build());
+        }
+        LOG.info("createLearntVpnVipToPortEvent: ARP learn event created for fixedIp: {}, vpn {}, interface {},"
+                + " mac {} action {} eventId {}", srcIp, vpnName, portName, macAddress, action, eventId);
+    }
+
+    private static InstanceIdentifier<LearntVpnVipToPortEvent> buildLearntVpnVipToPortEventIdentifier(String eventId) {
+        InstanceIdentifier<LearntVpnVipToPortEvent> id = InstanceIdentifier.builder(LearntVpnVipToPortEventData.class)
+                .child(LearntVpnVipToPortEvent.class, new LearntVpnVipToPortEventKey(eventId)).build();
+        return id;
+    }
+
+    protected static void removeLearntVpnVipToPortEvent(DataBroker broker, String eventId,
+                                                        WriteTransaction writeOperTxn) {
+        InstanceIdentifier<LearntVpnVipToPortEvent> id = buildLearntVpnVipToPortEventIdentifier(eventId);
+        if (writeOperTxn != null) {
+            writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, id);
+        } else {
             MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, id);
-            LOG.debug("removeLearntVpnVipToPort: Delete learned ARP for fixedIp: {}, vpn {} removed from"
-                    + " VpnPortipToPort DS", fixedIp, vpnName);
+        }
+        LOG.info("removeLearntVpnVipToPortEvent: Deleted Event {}", eventId);
+
+    }
+
+    public static void removeMipAdjacency(DataBroker dataBroker, String vpnName, String vpnInterface, String prefix,
+                                          WriteTransaction writeConfigTxn) {
+        String ip = VpnUtil.getIpPrefix(prefix);
+        LOG.trace("Removing {} adjacency from Old VPN Interface {} ", ip, vpnInterface);
+        InstanceIdentifier<VpnInterface> vpnIfId = VpnUtil.getVpnInterfaceIdentifier(vpnInterface);
+        InstanceIdentifier<Adjacencies> path = vpnIfId.augmentation(Adjacencies.class);
+        //TODO: Remove synchronized?
+
+        Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
+        if (adjacencies.isPresent()) {
+            InstanceIdentifier<Adjacency> adjacencyIdentifier = InstanceIdentifier.builder(VpnInterfaces.class)
+                    .child(VpnInterface.class, new VpnInterfaceKey(vpnInterface)).augmentation(Adjacencies.class)
+                    .child(Adjacency.class, new AdjacencyKey(ip)).build();
+            writeConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier);
+            LOG.error("removeMipAdjacency: Successfully Deleted Adjacency {} from interface {} vpn {}", ip,
+                    vpnInterface, vpnName);
+        }
+    }
+
+    public static void removeMipAdjAndLearntIp(DataBroker dataBroker, String vpnName,
+                                               String vpnInterface, String prefix) {
+        synchronized ((vpnName + prefix).intern()) {
+            InstanceIdentifier<LearntVpnVipToPort> id = buildLearntVpnVipToPortIdentifier(vpnName, prefix);
+            MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+            LOG.info("removeMipAdjAndLearntIp: Delete learned ARP for fixedIp: {}, vpn {} removed from"
+                            + "VpnPortipToPort DS", prefix, vpnName);
+            String ip = VpnUtil.getIpPrefix(prefix);
+            InstanceIdentifier<VpnInterfaceOpDataEntry> vpnInterfaceOpId = VpnUtil
+                    .getVpnInterfaceOpDataEntryIdentifier(vpnInterface, vpnName);
+            InstanceIdentifier<AdjacenciesOp> path = vpnInterfaceOpId.augmentation(AdjacenciesOp.class);
+            Optional<AdjacenciesOp> adjacenciesOp = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
+            if (adjacenciesOp.isPresent()) {
+                InstanceIdentifier<Adjacency> adjacencyIdentifier = InstanceIdentifier.builder(VpnInterfaces.class)
+                        .child(VpnInterface.class, new VpnInterfaceKey(vpnInterface)).augmentation(Adjacencies.class)
+                        .child(Adjacency.class, new AdjacencyKey(ip)).build();
+                MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier);
+                LOG.info("removeMipAdjAndLearntIp: Successfully Deleted Adjacency {} from interface {} vpn {}", ip,
+                        vpnInterface, vpnName);
+            }
         }
     }
 
@@ -1168,7 +1152,7 @@ public final class VpnUtil {
         return null;
     }
 
-    static LearntVpnVipToPort getLearntVpnVipToPort(DataBroker broker, String vpnName, String fixedIp) {
+    public static LearntVpnVipToPort getLearntVpnVipToPort(DataBroker broker, String vpnName, String fixedIp) {
         InstanceIdentifier<LearntVpnVipToPort> id = buildLearntVpnVipToPortIdentifier(vpnName, fixedIp);
         Optional<LearntVpnVipToPort> learntVpnVipToPort = read(broker, LogicalDatastoreType.OPERATIONAL, id);
         if (learntVpnVipToPort.isPresent()) {
@@ -1249,7 +1233,7 @@ public final class VpnUtil {
         return extNetwork != null ? extNetwork.getVpnid() : null;
     }
 
-    static List<Uuid> getExternalNetworkRouterIds(DataBroker dataBroker, Uuid networkId) {
+    public static List<Uuid> getExternalNetworkRouterIds(DataBroker dataBroker, Uuid networkId) {
         Networks extNetwork = getExternalNetwork(dataBroker, networkId);
         return extNetwork != null ? extNetwork.getRouterIds() : Collections.emptyList();
     }
@@ -1285,33 +1269,48 @@ public final class VpnUtil {
         return InstanceIdentifier.create(Subnetmaps.class);
     }
 
-    public static FlowEntity buildL3vpnGatewayFlow(BigInteger dpId, String gwMacAddress, long vpnId,
+    public static FlowEntity buildL3vpnGatewayFlow(DataBroker broker, BigInteger dpId, String gwMacAddress, long vpnId,
             long subnetVpnId) {
         List<MatchInfo> mkMatches = new ArrayList<>();
+        Subnetmap smap = null;
         mkMatches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
         mkMatches.add(new MatchEthernetDestination(new MacAddress(gwMacAddress)));
         List<InstructionInfo> mkInstructions = new ArrayList<>();
         mkInstructions.add(new InstructionGotoTable(NwConstants.L3_FIB_TABLE));
         if (subnetVpnId != VpnConstants.INVALID_ID) {
+            String vpnName = getVpnName(broker, subnetVpnId);
+            if (vpnName != null) {
+                smap = getSubnetmapFromItsUuid(broker, Uuid.getDefaultInstance(vpnName));
+                if (smap != null && smap.getSubnetIp() != null) {
+                    IpVersionChoice ipVersionChoice = getIpVersionFromString(smap.getSubnetIp());
+                    if (ipVersionChoice == IpVersionChoice.IPV4) {
+                        mkMatches.add(MatchEthernetType.IPV4);
+                    } else {
+                        mkMatches.add(MatchEthernetType.IPV6);
+                    }
+                }
+            }
+
             BigInteger subnetIdMetaData = MetaDataUtil.getVpnIdMetadata(subnetVpnId);
             mkInstructions.add(new InstructionWriteMetadata(subnetIdMetaData, MetaDataUtil.METADATA_MASK_VRFID));
         }
 
-        String flowId = getL3VpnGatewayFlowRef(NwConstants.L3_GW_MAC_TABLE, dpId, vpnId, gwMacAddress);
+        String flowId = getL3VpnGatewayFlowRef(NwConstants.L3_GW_MAC_TABLE, dpId, vpnId, gwMacAddress, subnetVpnId);
 
         return MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_GW_MAC_TABLE,
                 flowId, 20, flowId, 0, 0, NwConstants.COOKIE_L3_GW_MAC_TABLE, mkMatches, mkInstructions);
     }
 
-    private static String getL3VpnGatewayFlowRef(short l3GwMacTable, BigInteger dpId, long vpnId, String gwMacAddress) {
+    private static String getL3VpnGatewayFlowRef(short l3GwMacTable, BigInteger dpId, long vpnId,
+                                                 String gwMacAddress, long subnetVpnId) {
         return gwMacAddress + NwConstants.FLOWID_SEPARATOR + vpnId + NwConstants.FLOWID_SEPARATOR + dpId
-            + NwConstants.FLOWID_SEPARATOR + l3GwMacTable;
+            + NwConstants.FLOWID_SEPARATOR + l3GwMacTable + NwConstants.FLOWID_SEPARATOR + subnetVpnId;
     }
 
     public static void lockSubnet(LockManagerService lockManager, String subnetId) {
         TryLockInput input =
             new TryLockInputBuilder().setLockName(subnetId).setTime(3000L).setTimeUnit(TimeUnits.Milliseconds).build();
-        Future<RpcResult<Void>> result = lockManager.tryLock(input);
+        Future<RpcResult<TryLockOutput>> result = lockManager.tryLock(input);
         try {
             if (result != null && result.get().isSuccessful()) {
                 LOG.debug("lockSubnet: Acquired lock for {}", subnetId);
@@ -1329,7 +1328,7 @@ public final class VpnUtil {
     @SuppressWarnings("checkstyle:AvoidHidingCauseException")
     public static void unlockSubnet(LockManagerService lockManager, String subnetId) {
         UnlockInput input = new UnlockInputBuilder().setLockName(subnetId).build();
-        Future<RpcResult<Void>> result = lockManager.unlock(input);
+        Future<RpcResult<UnlockOutput>> result = lockManager.unlock(input);
         try {
             if (result != null && result.get().isSuccessful()) {
                 LOG.debug("unlockSubnet: Unlocked {}", subnetId);
@@ -1342,16 +1341,21 @@ public final class VpnUtil {
         }
     }
 
-    static Optional<IpAddress> getGatewayIpAddressFromInterface(String srcInterface,
+    static Optional<IpAddress> getIpv4GatewayAddressFromInterface(String srcInterface,
             INeutronVpnManager neutronVpnService) {
         Optional<IpAddress> gatewayIp = Optional.absent();
         if (neutronVpnService != null) {
             //TODO(Gobinath): Need to fix this as assuming port will belong to only one Subnet would be incorrect"
             Port port = neutronVpnService.getNeutronPort(srcInterface);
-            if (port != null && port.getFixedIps() != null && port.getFixedIps().get(0) != null
-                && port.getFixedIps().get(0).getSubnetId() != null) {
-                gatewayIp = Optional.of(
-                    neutronVpnService.getNeutronSubnet(port.getFixedIps().get(0).getSubnetId()).getGatewayIp());
+            if (port != null && port.getFixedIps() != null) {
+                for (FixedIps portIp: port.getFixedIps()) {
+                    if (portIp.getIpAddress().getIpv6Address() != null) {
+                        // Skip IPv6 address
+                        continue;
+                    }
+                    gatewayIp = Optional.of(
+                            neutronVpnService.getNeutronSubnet(portIp.getSubnetId()).getGatewayIp());
+                }
             }
         } else {
             LOG.error("getGatewayIpAddressFromInterface: neutron vpn service is not configured."
@@ -1401,7 +1405,8 @@ public final class VpnUtil {
                         interfaceName, dpnId.toString(), vpnIdsOptional.get().getVpnInstanceName());
                 return;
             }
-            FlowEntity flowEntity = VpnUtil.buildL3vpnGatewayFlow(dpnId, gwMac, vpnId, VpnConstants.INVALID_ID);
+            FlowEntity flowEntity = VpnUtil.buildL3vpnGatewayFlow(dataBroker, dpnId, gwMac, vpnId,
+                    VpnConstants.INVALID_ID);
             if (addOrRemove == NwConstants.ADD_FLOW) {
                 mdsalManager.addFlowToTx(flowEntity, writeInvTxn);
             } else if (addOrRemove == NwConstants.DEL_FLOW) {
@@ -1434,12 +1439,20 @@ public final class VpnUtil {
         if (subnet.isPresent()) {
             Class<? extends IpVersionBase> ipVersionBase = subnet.get().getIpVersion();
             if (ipVersionBase.equals(IpVersionV4.class)) {
-                LOG.trace("getVpnSubnetGatewayIp: Obtained subnet {} for vpn interface",
-                        subnet.get().getUuid().getValue());
-                IpAddress gwIp = subnet.get().getGatewayIp();
-                if (gwIp != null && gwIp.getIpv4Address() != null) {
-                    gwIpAddress = Optional.of(gwIp.getIpv4Address().getValue());
+                Subnetmap subnetmap = getSubnetmapFromItsUuid(dataBroker, subnetUuid);
+                if (subnetmap != null && subnetmap.getRouterInterfaceFixedIp() != null) {
+                    LOG.trace("getVpnSubnetGatewayIp: Obtained subnetMap {} for vpn interface",
+                            subnetmap.getId().getValue());
+                    gwIpAddress = Optional.of(subnetmap.getRouterInterfaceFixedIp());
+                } else {
+                    //For direct L3VPN to network association (no router) continue to use subnet-gateway IP
+                    IpAddress gwIp = subnet.get().getGatewayIp();
+                    if (gwIp != null && gwIp.getIpv4Address() != null) {
+                        gwIpAddress = Optional.of(gwIp.getIpv4Address().getValue());
+                    }
                 }
+                LOG.trace("getVpnSubnetGatewayIp: Obtained subnet-gw ip {} for vpn interface",
+                        gwIpAddress.get());
             }
         }
         return gwIpAddress;
@@ -1524,7 +1537,7 @@ public final class VpnUtil {
         java.util.Optional<String> allocatedRd = VpnExtraRouteHelper
                 .getRdAllocatedForExtraRoute(dataBroker, vpnId, prefix, nextHop);
         if (allocatedRd.isPresent()) {
-            return java.util.Optional.of(allocatedRd.get());
+            return allocatedRd;
         }
 
         //Check if rd is already allocated for this extraroute behind the same CSS. If yes, reuse it
@@ -1589,16 +1602,16 @@ public final class VpnUtil {
     static void bindService(final String vpnInstanceName, final String interfaceName, DataBroker dataBroker,
                             boolean isTunnelInterface, JobCoordinator jobCoordinator) {
         jobCoordinator.enqueueJob(interfaceName,
-            () -> {
-                WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
-                BoundServices serviceInfo = isTunnelInterface
-                        ? VpnUtil.getBoundServicesForTunnelInterface(vpnInstanceName, interfaceName)
-                        : getBoundServicesForVpnInterface(dataBroker, vpnInstanceName, interfaceName);
-                writeTxn.put(LogicalDatastoreType.CONFIGURATION, InterfaceUtils.buildServiceId(interfaceName,
-                        ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants.L3VPN_SERVICE_INDEX)),
-                        serviceInfo, WriteTransaction.CREATE_MISSING_PARENTS);
-                return Collections.singletonList(writeTxn.submit());
-            }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+            () -> Collections.singletonList(
+                    new ManagedNewTransactionRunnerImpl(dataBroker).callWithNewReadWriteTransactionAndSubmit(tx -> {
+                        BoundServices serviceInfo = isTunnelInterface
+                                ? VpnUtil.getBoundServicesForTunnelInterface(vpnInstanceName, interfaceName)
+                                : getBoundServicesForVpnInterface(dataBroker, vpnInstanceName, interfaceName);
+                        tx.put(LogicalDatastoreType.CONFIGURATION, InterfaceUtils.buildServiceId(interfaceName,
+                                ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
+                                        NwConstants.L3VPN_SERVICE_INDEX)),
+                                serviceInfo, WriteTransaction.CREATE_MISSING_PARENTS);
+                    })), SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
     }
 
     static BoundServices getBoundServicesForVpnInterface(DataBroker broker, String vpnName, String interfaceName) {
@@ -1637,17 +1650,13 @@ public final class VpnUtil {
             JobCoordinator jobCoordinator) {
         if (!isInterfaceStateDown) {
             jobCoordinator.enqueueJob(vpnInterfaceName,
-                () -> {
-                    WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
-                    writeTxn.delete(LogicalDatastoreType.CONFIGURATION,
-                            InterfaceUtils.buildServiceId(vpnInterfaceName,
-                                    ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
-                                            NwConstants.L3VPN_SERVICE_INDEX)));
-
-                    List<ListenableFuture<Void>> futures = new ArrayList<>();
-                    futures.add(writeTxn.submit());
-                    return futures;
-                }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+                () -> Collections.singletonList(new ManagedNewTransactionRunnerImpl(
+                        dataBroker).callWithNewWriteOnlyTransactionAndSubmit(tx ->
+                        tx.delete(LogicalDatastoreType.CONFIGURATION,
+                                InterfaceUtils.buildServiceId(vpnInterfaceName,
+                                        ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
+                                                NwConstants.L3VPN_SERVICE_INDEX))))),
+                SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
         }
     }
 
@@ -1674,7 +1683,7 @@ public final class VpnUtil {
         return optionalSubnets.isPresent() ? optionalSubnets.get() : null;
     }
 
-    static Uuid getSubnetFromExternalRouterByIp(DataBroker dataBroker, Uuid routerId, String ip) {
+    public static Uuid getSubnetFromExternalRouterByIp(DataBroker dataBroker, Uuid routerId, String ip) {
         Routers externalRouter = VpnUtil.getExternalRouter(dataBroker, routerId.getValue());
         if (externalRouter != null && externalRouter.getExternalIps() != null) {
             for (ExternalIps externalIp : externalRouter.getExternalIps()) {
@@ -1691,8 +1700,8 @@ public final class VpnUtil {
     }
 
     static Boolean getIsExternal(Network network) {
-        return network.getAugmentation(NetworkL3Extension.class) != null
-                && network.getAugmentation(NetworkL3Extension.class).isExternal();
+        return network.augmentation(NetworkL3Extension.class) != null
+                && network.augmentation(NetworkL3Extension.class).isExternal();
     }
 
     @SuppressWarnings("checkstyle:linelength")
@@ -1946,7 +1955,7 @@ public final class VpnUtil {
 
     public static ListenableFuture<Void> unsetScheduledToRemoveForVpnInterface(ManagedNewTransactionRunner txRunner,
             String interfaceName) {
-        VpnInterfaceBuilder builder = new VpnInterfaceBuilder().setKey(new VpnInterfaceKey(interfaceName))
+        VpnInterfaceBuilder builder = new VpnInterfaceBuilder().withKey(new VpnInterfaceKey(interfaceName))
                 .setScheduledForRemove(false);
         return txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.merge(LogicalDatastoreType.OPERATIONAL,
                 VpnUtil.getVpnInterfaceIdentifier(interfaceName), builder.build(),
@@ -2026,7 +2035,7 @@ public final class VpnUtil {
     }
 
     public static Set<BigInteger> getDpnInElan(DataBroker dataBroker,  Map<String,String> elanInstanceRouterPortMap) {
-        Set<BigInteger> dpnIdSet = new HashSet<BigInteger>();
+        Set<BigInteger> dpnIdSet = new HashSet<>();
         for (String elanInstanceName : elanInstanceRouterPortMap.keySet()) {
             InstanceIdentifier<ElanDpnInterfacesList> elanDpnInterfaceId = getElanDpnOperationalDataPath(
                     elanInstanceName);
@@ -2046,22 +2055,24 @@ public final class VpnUtil {
             BigInteger dpnId, DataBroker dataBroker) {
         InstanceIdentifier<DpnInterfaces> elanDpnInterfaceId = getElanDpnInterfaceOperationalDataPath(
                 elanInstanceName,dpnId);
-        Optional<DpnInterfaces> dpnInElanInterfaces = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
-                elanDpnInterfaceId);
-        List<String> elanInterfaceList;
-        DpnInterfaces dpnInterface;
-        if (!dpnInElanInterfaces.isPresent()) {
-            elanInterfaceList = new ArrayList<>();
-        } else {
-            dpnInterface = dpnInElanInterfaces.get();
-            elanInterfaceList = dpnInterface.getInterfaces();
-        }
-        if (!elanInterfaceList.contains(routerInterfacePortId)) {
-            elanInterfaceList.add(routerInterfacePortId);
-            dpnInterface = new DpnInterfacesBuilder().setDpId(dpnId).setInterfaces(elanInterfaceList)
-                    .setKey(new DpnInterfacesKey(dpnId)).build();
-            VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
-                    elanDpnInterfaceId, dpnInterface);
+        synchronized (elanInstanceName.intern()) {
+            Optional<DpnInterfaces> dpnInElanInterfaces = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                    elanDpnInterfaceId);
+            List<String> elanInterfaceList;
+            DpnInterfaces dpnInterface;
+            if (!dpnInElanInterfaces.isPresent()) {
+                elanInterfaceList = new ArrayList<>();
+            } else {
+                dpnInterface = dpnInElanInterfaces.get();
+                elanInterfaceList = dpnInterface.getInterfaces();
+            }
+            if (!elanInterfaceList.contains(routerInterfacePortId)) {
+                elanInterfaceList.add(routerInterfacePortId);
+                dpnInterface = new DpnInterfacesBuilder().setDpId(dpnId).setInterfaces(elanInterfaceList)
+                        .withKey(new DpnInterfacesKey(dpnId)).build();
+                VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                        elanDpnInterfaceId, dpnInterface);
+            }
         }
 
     }
@@ -2070,26 +2081,28 @@ public final class VpnUtil {
             String vpnName, BigInteger dpnId, DataBroker dataBroker) {
         InstanceIdentifier<DpnInterfaces> elanDpnInterfaceId = getElanDpnInterfaceOperationalDataPath(
                 elanInstanceName,dpnId);
-        Optional<DpnInterfaces> dpnInElanInterfaces = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
-                elanDpnInterfaceId);
-        List<String> elanInterfaceList;
-        DpnInterfaces dpnInterface;
-        if (!dpnInElanInterfaces.isPresent()) {
-            LOG.info("No interface in any dpn for {}", vpnName);
-            return;
-        } else {
-            dpnInterface = dpnInElanInterfaces.get();
-            elanInterfaceList = dpnInterface.getInterfaces();
-        }
-        if (!elanInterfaceList.contains(routerInterfacePortId)) {
-            LOG.info("Router port not present in DPN {} for VPN {}", dpnId, vpnName);
-            return;
+        synchronized (elanInstanceName.intern()) {
+            Optional<DpnInterfaces> dpnInElanInterfaces = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                    elanDpnInterfaceId);
+            List<String> elanInterfaceList;
+            DpnInterfaces dpnInterface;
+            if (!dpnInElanInterfaces.isPresent()) {
+                LOG.info("No interface in any dpn for {}", vpnName);
+                return;
+            } else {
+                dpnInterface = dpnInElanInterfaces.get();
+                elanInterfaceList = dpnInterface.getInterfaces();
+            }
+            if (!elanInterfaceList.contains(routerInterfacePortId)) {
+                LOG.info("Router port not present in DPN {} for VPN {}", dpnId, vpnName);
+                return;
+            }
+            elanInterfaceList.remove(routerInterfacePortId);
+            dpnInterface = new DpnInterfacesBuilder().setDpId(dpnId).setInterfaces(elanInterfaceList)
+                    .withKey(new DpnInterfacesKey(dpnId)).build();
+            VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                    elanDpnInterfaceId, dpnInterface);
         }
-        elanInterfaceList.remove(routerInterfacePortId);
-        dpnInterface = new DpnInterfacesBuilder().setDpId(dpnId).setInterfaces(elanInterfaceList)
-                .setKey(new DpnInterfacesKey(dpnId)).build();
-        VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
-                elanDpnInterfaceId, dpnInterface);
 
     }
 
@@ -2170,7 +2183,7 @@ public final class VpnUtil {
     }
 
     public static Map<String, String> getElanInstanceRouterPortMap(DataBroker dataBroker, String vpnName) {
-        Map<String, String> elanInstanceRouterPortMap = new HashMap<String, String>();
+        Map<String, String> elanInstanceRouterPortMap = new HashMap<>();
         Optional<Subnetmaps> subnetMapsData =
                 read(dataBroker, LogicalDatastoreType.CONFIGURATION, buildSubnetMapsWildCardPath());
         if (subnetMapsData.isPresent()) {
@@ -2249,4 +2262,74 @@ public final class VpnUtil {
                 .child(ElanDpnInterfacesList.class, new ElanDpnInterfacesListKey(elanInstanceName))
                 .build();
     }
+
+    public static boolean isMatchedPrefixToInterface(Prefixes prefix, VpnInterfaceOpDataEntry vpnInterface) {
+        if (prefix != null && vpnInterface != null) {
+            if (prefix.getDpnId() != null && vpnInterface.getDpnId() != null) {
+                if (prefix.getVpnInterfaceName() != null && vpnInterface.getName() != null) {
+                    return prefix.getDpnId().equals(vpnInterface.getDpnId())
+                            && prefix.getVpnInterfaceName().equalsIgnoreCase(vpnInterface.getName());
+                }
+            }
+        }
+        return false;
+    }
+
+    public static void removePrefixToInterfaceAdj(DataBroker dataBroker, Adjacency adj, long vpnId,
+                                                  VpnInterfaceOpDataEntry vpnInterfaceOpDataEntry,
+                                                  WriteTransaction writeOperTxn) {
+        if (writeOperTxn == null) {
+            ListenableFutures.addErrorLogging(
+                    new ManagedNewTransactionRunnerImpl(dataBroker).callWithNewWriteOnlyTransactionAndSubmit(tx ->
+                        removePrefixToInterfaceAdj(dataBroker, adj, vpnId, vpnInterfaceOpDataEntry, tx)), LOG,
+                    "Error removing prefix");
+            return;
+        }
+
+        Optional<Prefixes> prefix = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                VpnUtil.getPrefixToInterfaceIdentifier(vpnId,
+                        VpnUtil.getIpPrefix(adj.getIpAddress())));
+        List<Prefixes> prefixToInterface = new ArrayList<>();
+        List<Prefixes> prefixToInterfaceLocal = new ArrayList<>();
+        if (prefix.isPresent()) {
+            prefixToInterfaceLocal.add(prefix.get());
+        }
+        if (prefixToInterfaceLocal.isEmpty()) {
+            for (String nh : adj.getNextHopIpList()) {
+                prefix = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                        VpnUtil.getPrefixToInterfaceIdentifier(vpnId,
+                                VpnUtil.getIpPrefix(nh)));
+                if (prefix.isPresent()) {
+                    prefixToInterfaceLocal.add(prefix.get());
+                }
+            }
+        }
+        if (!prefixToInterfaceLocal.isEmpty()) {
+            prefixToInterface.addAll(prefixToInterfaceLocal);
+        }
+        for (Prefixes pref : prefixToInterface) {
+            if (VpnUtil.isMatchedPrefixToInterface(pref, vpnInterfaceOpDataEntry)) {
+                writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL,
+                        VpnUtil.getPrefixToInterfaceIdentifier(vpnId, pref.getIpAddress()));
+            }
+        }
+    }
+
+    public static void sendNeighborSolicationToOfGroup(Ipv6NdUtilService ipv6NdUtilService, Ipv6Address srcIpv6Address,
+            MacAddress srcMac, Ipv6Address dstIpv6Address, Long ofGroupId, BigInteger dpId) {
+        SendNeighborSolicitationToOfGroupInput input = new SendNeighborSolicitationToOfGroupInputBuilder()
+                .setSourceIpv6(srcIpv6Address).setSourceLlAddress(srcMac).setTargetIpAddress(dstIpv6Address)
+                .setOfGroupId(ofGroupId).setDpId(dpId).build();
+        try {
+            Future<RpcResult<SendNeighborSolicitationToOfGroupOutput>> result = ipv6NdUtilService
+                    .sendNeighborSolicitationToOfGroup(input);
+            RpcResult<SendNeighborSolicitationToOfGroupOutput> rpcResult = result.get();
+            if (!rpcResult.isSuccessful()) {
+                LOG.error("sendNeighborSolicitationToOfGroup: RPC Call failed for input={} and Errors={}", input,
+                        rpcResult.getErrors());
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Failed to send NS packet to ELAN group, input={}", input, e);
+        }
+    }
 }