subnet-op-data and port-op-data is empty after cluster reboot
[netvirt.git] / vpnservice / neutronvpn / neutronvpn-impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronvpnManager.java
index 9a664dcc670f4b22925c8fa2e62dd1d1c45f00ed..c9433753c625d955a5b570d758b7a8e806039069 100644 (file)
@@ -7,15 +7,27 @@
  */
 package org.opendaylight.netvirt.neutronvpn;
 
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.SettableFuture;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
-import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.netvirt.elanmanager.api.IElanService;
+import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.VpnTargets;
@@ -30,13 +42,14 @@ import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev14081
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+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.yang.types.rev130715.Uuid;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.config.rev160806.NeutronvpnConfig;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksOutputBuilder;
@@ -65,19 +78,30 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev15060
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.RouterDisassociatedFromVpn;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.RouterDisassociatedFromVpnBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.RouterInterfacesMap;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.SubnetAddedToVpnBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.SubnetDeletedFromVpnBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.SubnetUpdatedInVpnBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.VpnMaps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.createl3vpn.input.L3vpn;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.getl3vpn.output.L3vpnInstances;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.getl3vpn.output.L3vpnInstancesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.RouterInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.RouterInterfacesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.RouterInterfacesKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.router.interfaces.Interfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.router.interfaces.InterfacesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.router.interfaces.InterfacesKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.ext.rev150712.NetworkL3Extension;
+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.SubnetmapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
+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.VpnMapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.AddStaticRouteInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.AddStaticRouteInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.AddStaticRouteOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveStaticRouteInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveStaticRouteInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.l3.attributes.Routes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
@@ -86,180 +110,278 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.por
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.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.Subnet;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.createl3vpn.input.L3vpn;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.getl3vpn.output.L3vpnInstances;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.getl3vpn.output.L3vpnInstancesBuilder;
-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.SubnetmapBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
-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.VpnMapBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-
-import java.util.EventListener;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
 
 public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, EventListener {
-
-    private static final Logger logger = LoggerFactory.getLogger(NeutronvpnManager.class);
-    private final DataBroker broker;
-    private LockManagerService lockManager;
-    private NeutronvpnNatManager nvpnNatManager;
-    IMdsalApiManager mdsalUtil;
-    private NotificationPublishService notificationPublishService;
-    private NotificationService notificationService;
+    private static final Logger LOG = LoggerFactory.getLogger(NeutronvpnManager.class);
+    private final DataBroker dataBroker;
+    private final NeutronvpnNatManager nvpnNatManager;
+    private final NotificationPublishService notificationPublishService;
+    private final VpnRpcService vpnRpcService;
+    private final NeutronFloatingToFixedIpMappingChangeListener floatingIpMapListener;
+    private final NeutronvpnConfig neutronvpnConfig;
+    private final IMdsalApiManager mdsalUtil;
+    private final IElanService elanService;
     Boolean isExternalVpn;
 
     /**
-     * @param db           - dataBroker reference
-     * @param mdsalManager - MDSAL Util API access
+     * @param dataBroker DataBroker reference
+     * @param mdsalManager MDSAL Util API access
+     * @param notiPublishService notificationPublishService
+     * @param vpnNatMgr VPN NAT manager service
+     * @param vpnRpcSrv VPN RPC service
+     * @param elanService ELAN service
+     * @param neutronFloatingToFixedIpMappingChangeListener FIP to FixedIP listener
+     * @param neutronvpnConfig Neutronvpn configuration service
      */
-    public NeutronvpnManager(final DataBroker db, IMdsalApiManager mdsalManager,NotificationPublishService notiPublishService,
-                             NotificationService notiService, NeutronvpnNatManager vpnNatMgr) {
-        broker = db;
+    public NeutronvpnManager(
+            final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
+            final NotificationPublishService notiPublishService, final NeutronvpnNatManager vpnNatMgr,
+            final VpnRpcService vpnRpcSrv, final IElanService elanService,
+            final NeutronFloatingToFixedIpMappingChangeListener neutronFloatingToFixedIpMappingChangeListener,
+            final NeutronvpnConfig neutronvpnConfig) {
+        this.dataBroker = dataBroker;
         mdsalUtil = mdsalManager;
         nvpnNatManager = vpnNatMgr;
         notificationPublishService = notiPublishService;
-        notificationService = notiService;
-    }
-
-    public void setLockManager(LockManagerService lockManager) {
-        this.lockManager = lockManager;
+        vpnRpcService = vpnRpcSrv;
+        this.elanService = elanService;
+        floatingIpMapListener = neutronFloatingToFixedIpMappingChangeListener;
+        LOG.info("neutronvpnConfig: {}", neutronvpnConfig);
+        this.neutronvpnConfig = neutronvpnConfig;
     }
 
     @Override
     public void close() throws Exception {
-        logger.info("Neutron VPN Manager Closed");
+        LOG.info("{} close", getClass().getSimpleName());
     }
 
-    protected Subnetmap updateSubnetNode(Uuid subnetId, String subnetIp, Uuid tenantId, Uuid networkId, Uuid routerId,
-                                         Uuid vpnId, Uuid portId) {
+    public NeutronvpnConfig getNeutronvpnConfig() {
+        return neutronvpnConfig;
+    }
+
+    protected void updateSubnetNodeWithFixedIps(Uuid subnetId, Uuid routerId,
+                                                Uuid routerInterfaceName, String fixedIp,
+                                                String routerIntfMacAddress) {
         Subnetmap subnetmap = null;
         SubnetmapBuilder builder = null;
-        boolean isLockAcquired = false;
         InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class).
                 child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
         try {
-            Optional<Subnetmap> sn = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION, id);
-            logger.debug("updating Subnet :read: ");
-            if (sn.isPresent()) {
-                builder = new SubnetmapBuilder(sn.get());
-                logger.debug("updating Subnet :existing: ");
-            } else {
-                builder = new SubnetmapBuilder().setKey(new SubnetmapKey(subnetId)).setId(subnetId);
-                logger.debug("updating Subnet :new: ");
-            }
+            synchronized (subnetId.getValue().intern()) {
+                Optional<Subnetmap> sn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+                if (sn.isPresent()) {
+                    builder = new SubnetmapBuilder(sn.get());
+                    LOG.debug("WithRouterFixedIPs: Updating existing subnetmap node for subnet ID {}", subnetId.getValue());
+                } else {
+                    builder = new SubnetmapBuilder().setKey(new SubnetmapKey(subnetId)).setId(subnetId);
+                    LOG.debug("WithRouterFixedIPs: creating new subnetmap node for subnet ID {}", subnetId.getValue());
+                }
 
-            if (subnetIp != null) {
-                builder.setSubnetIp(subnetIp);
-            }
-            if (routerId != null) {
                 builder.setRouterId(routerId);
-            }
-            if (networkId != null) {
-                builder.setNetworkId(networkId);
-            }
-            if (vpnId != null) {
-                builder.setVpnId(vpnId);
-            }
-            if (tenantId != null) {
-                builder.setTenantId(tenantId);
-            }
+                builder.setRouterInterfaceName(routerInterfaceName);
+                builder.setRouterIntfMacAddress(routerIntfMacAddress);
 
-            if (portId != null) {
-                List<Uuid> portList = builder.getPortList();
-                if (portList == null) {
-                    portList = new ArrayList<>();
+                if (fixedIp != null) {
+                    List<String> fixedIps = builder.getRouterInterfaceFixedIps();
+                    if (fixedIps == null) {
+                        fixedIps = new ArrayList<>();
+                    }
+                    fixedIps.add(fixedIp);
+                    builder.setRouterInterfaceFixedIps(fixedIps);
+                } else {
+                    builder.setRouterInterfaceFixedIps(null);
                 }
-                portList.add(portId);
-                builder.setPortList(portList);
+                subnetmap = builder.build();
+                LOG.debug("WithRouterFixedIPs Creating/Updating subnetMap node for Router FixedIps: {} ", subnetId.getValue());
+                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
             }
-
-            subnetmap = builder.build();
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, subnetId.getValue());
-            logger.debug("Creating/Updating subnetMap node: {} ", subnetId.getValue());
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
         } catch (Exception e) {
-            logger.error("Updation of subnetMap failed for node: {}", subnetId.getValue());
-        } finally {
-            if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, subnetId.getValue());
-            }
+            LOG.error("WithRouterFixedIPs: Updation of subnetMap for Router FixedIps failed for node: {}", subnetId.getValue());
         }
-        return subnetmap;
     }
 
-    protected Subnetmap removeFromSubnetNode(Uuid subnetId, Uuid networkId, Uuid routerId, Uuid vpnId, Uuid portId) {
+    protected Subnetmap updateSubnetNode(Uuid subnetId, String subnetIp, Uuid tenantId, Uuid networkId, Uuid routerId,
+                                         Uuid vpnId) {
         Subnetmap subnetmap = null;
-        boolean isLockAcquired = false;
-        InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class).
-                child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
+        SubnetmapBuilder builder = null;
+        InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class)
+                .child(Subnetmap.class, new SubnetmapKey(subnetId))
+                .build();
         try {
-            Optional<Subnetmap> sn = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION, id);
-            if (sn.isPresent()) {
-                SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
+            synchronized (subnetId.getValue().intern()) {
+                Optional<Subnetmap> sn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+                if (sn.isPresent()) {
+                    builder = new SubnetmapBuilder(sn.get());
+                    LOG.debug("updating existing subnetmap node for subnet ID {}", subnetId.getValue());
+                } else {
+                    builder = new SubnetmapBuilder().setKey(new SubnetmapKey(subnetId)).setId(subnetId);
+                    LOG.debug("creating new subnetmap node for subnet ID {}", subnetId.getValue());
+                }
+
+                if (subnetIp != null) {
+                    builder.setSubnetIp(subnetIp);
+                }
                 if (routerId != null) {
-                    builder.setRouterId(null);
+                    builder.setRouterId(routerId);
                 }
                 if (networkId != null) {
-                    builder.setNetworkId(null);
+                    builder.setNetworkId(networkId);
                 }
                 if (vpnId != null) {
-                    builder.setVpnId(null);
+                    builder.setVpnId(vpnId);
                 }
-                if (portId != null && builder.getPortList() != null) {
-                    List<Uuid> portList = builder.getPortList();
-                    portList.remove(portId);
-                    builder.setPortList(portList);
+                if (tenantId != null) {
+                    builder.setTenantId(tenantId);
                 }
 
                 subnetmap = builder.build();
-                isLockAcquired = NeutronvpnUtils.lock(lockManager, subnetId.getValue());
-                logger.debug("Removing from existing subnetmap node: {} ", subnetId.getValue());
-                MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
-            } else {
-                logger.warn("removing from non-existing subnetmap node: {} ", subnetId.getValue());
+                LOG.debug("Creating/Updating subnetMap node: {} ", subnetId.getValue());
+                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
             }
         } catch (Exception e) {
-            logger.error("Removal from subnetmap failed for node: {}", subnetId.getValue());
-        } finally {
-            if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, subnetId.getValue());
+            LOG.error("Updation of subnetMap failed for node: {}", subnetId.getValue());
+        }
+        return subnetmap;
+    }
+
+    protected Subnetmap removeFromSubnetNode(Uuid subnetId, Uuid networkId, Uuid routerId, Uuid vpnId, Uuid portId) {
+        Subnetmap subnetmap = null;
+        InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class)
+                .child(Subnetmap.class, new SubnetmapKey(subnetId))
+                .build();
+        try {
+            synchronized (subnetId.getValue().intern()) {
+                Optional<Subnetmap> sn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+                if (sn.isPresent()) {
+                    SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
+                    if (routerId != null) {
+                        builder.setRouterId(null);
+                    }
+                    if (networkId != null) {
+                        builder.setNetworkId(null);
+                    }
+                    if (vpnId != null) {
+                        builder.setVpnId(null);
+                    }
+                    if (portId != null && builder.getPortList() != null) {
+                        List<Uuid> portList = builder.getPortList();
+                        portList.remove(portId);
+                        builder.setPortList(portList);
+                    }
+
+                    subnetmap = builder.build();
+                    LOG.debug("Removing from existing subnetmap node: {} ", subnetId.getValue());
+                    MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
+                } else {
+                    LOG.warn("removing from non-existing subnetmap node: {} ", subnetId.getValue());
+                }
             }
+        } catch (Exception e) {
+            LOG.error("Removal from subnetmap failed for node: {}", subnetId.getValue());
         }
         return subnetmap;
     }
 
-    protected void deleteSubnetMapNode(Uuid subnetId) {
-        boolean isLockAcquired = false;
-        InstanceIdentifier<Subnetmap> subnetMapIdentifier = InstanceIdentifier.builder(Subnetmaps.class)
-                .child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
-        logger.debug("removing subnetMap node: {} ", subnetId.getValue());
+    protected Subnetmap updateSubnetmapNodeWithPorts(Uuid subnetId, Uuid portId, Uuid directPortId) {
+        Subnetmap subnetmap = null;
+        InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
+                new SubnetmapKey(subnetId)).build();
+        try {
+            synchronized (subnetId.getValue().intern()) {
+                Optional<Subnetmap> sn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+                if (sn.isPresent()) {
+                    SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
+                    if (null != portId) {
+                        List<Uuid> portList = builder.getPortList();
+                        if (null == portList) {
+                            portList = new ArrayList<Uuid>();
+                        }
+                        portList.add(portId);
+                        builder.setPortList(portList);
+                        LOG.debug("Updating existing subnetmap node {} with port {}", subnetId.getValue(),
+                                portId.getValue());
+                    }
+                    if (null != directPortId) {
+                        List<Uuid> directPortList = builder.getDirectPortList();
+                        if (null == directPortList) {
+                            directPortList = new ArrayList<Uuid>();
+                        }
+                        directPortList.add(directPortId);
+                        builder.setDirectPortList(directPortList);
+                        LOG.debug("Updating existing subnetmap node {} with port {}", subnetId.getValue(),
+                                directPortId.getValue());
+                    }
+                    subnetmap = builder.build();
+                    MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
+                } else {
+                    LOG.error("Trying to update non-existing subnetmap node {} ", subnetId.getValue());
+                }
+            }
+        } catch (Exception e) {
+            LOG.error("Updating port list of a given subnetMap failed for node: {} with exception{}",
+                    subnetId.getValue(), e);
+        }
+        return subnetmap;
+    }
+
+    protected Subnetmap removePortsFromSubnetmapNode(Uuid subnetId, Uuid portId, Uuid directPortId) {
+        Subnetmap subnetmap = null;
+        InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
+                new SubnetmapKey(subnetId)).build();
         try {
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, subnetId.getValue());
-            MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, subnetMapIdentifier);
+            synchronized (subnetId.getValue().intern()) {
+                Optional<Subnetmap> sn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+                if (sn.isPresent()) {
+                    SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
+                    if (null != portId && null != builder.getPortList()) {
+                        List<Uuid> portList = builder.getPortList();
+                        portList.remove(portId);
+                        builder.setPortList(portList);
+                        LOG.debug("Removing port {} from existing subnetmap node: {} ", portId.getValue(),
+                                subnetId.getValue());
+                    }
+                    if (null != directPortId && null != builder.getDirectPortList()) {
+                        List<Uuid> directPortList = builder.getDirectPortList();
+                        directPortList.remove(directPortId);
+                        builder.setDirectPortList(directPortList);
+                        LOG.debug("Removing direct port {} from existing subnetmap node: {} ", directPortId
+                                .getValue(), subnetId.getValue());
+                    }
+                    subnetmap = builder.build();
+                    MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
+                } else {
+                    LOG.error("Trying to remove port from non-existing subnetmap node {}", subnetId.getValue());
+                }
+            }
         } catch (Exception e) {
-            logger.error("Delete subnetMap node failed for subnet : {} ", subnetId.getValue());
-        } finally {
-            if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, subnetId.getValue());
+            LOG.error("Removing a port from port list of a subnetmap failed for node: {} with expection {}",
+                    subnetId.getValue(), e);
+        }
+        return subnetmap;
+    }
+
+    protected void deleteSubnetMapNode(Uuid subnetId) {
+        InstanceIdentifier<Subnetmap> subnetMapIdentifier =
+                InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,new SubnetmapKey(subnetId)).build();
+        LOG.debug("removing subnetMap node: {} ", subnetId.getValue());
+        try {
+            synchronized (subnetId.getValue().intern()) {
+                MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, subnetMapIdentifier);
             }
+        } catch (Exception e) {
+            LOG.error("Delete subnetMap node failed for subnet : {} ", subnetId.getValue());
         }
     }
 
@@ -268,15 +390,15 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         VpnInstanceBuilder builder = null;
         List<VpnTarget> vpnTargetList = new ArrayList<>();
         boolean isLockAcquired = false;
-        InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class).
-                child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
+        InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class).child
+                (VpnInstance.class, new VpnInstanceKey(vpnName)).build();
         try {
-            Optional<VpnInstance> optionalVpn = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
+            Optional<VpnInstance> optionalVpn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                     vpnIdentifier);
-            logger.debug("Creating/Updating a new vpn-instance node: {} ", vpnName);
+            LOG.debug("Creating/Updating a new vpn-instance node: {} ", vpnName);
             if (optionalVpn.isPresent()) {
                 builder = new VpnInstanceBuilder(optionalVpn.get());
-                logger.debug("updating existing vpninstance node");
+                LOG.debug("updating existing vpninstance node");
             } else {
                 builder = new VpnInstanceBuilder().setKey(new VpnInstanceKey(vpnName)).setVpnInstanceName(vpnName);
             }
@@ -288,22 +410,25 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                     for (String common : commonRT) {
                         irt.remove(common);
                         ert.remove(common);
-                        VpnTarget vpnTarget = new VpnTargetBuilder().setKey(new VpnTargetKey(common)).setVrfRTValue
-                                (common).setVrfRTType(VpnTarget.VrfRTType.Both).build();
+                        VpnTarget vpnTarget =
+                                new VpnTargetBuilder().setKey(new VpnTargetKey(common)).setVrfRTValue(common)
+                                        .setVrfRTType(VpnTarget.VrfRTType.Both).build();
                         vpnTargetList.add(vpnTarget);
                     }
                 }
                 for (String importRT : irt) {
-                    VpnTarget vpnTarget = new VpnTargetBuilder().setKey(new VpnTargetKey(importRT)).setVrfRTValue
-                            (importRT).setVrfRTType(VpnTarget.VrfRTType.ImportExtcommunity).build();
+                    VpnTarget vpnTarget =
+                            new VpnTargetBuilder().setKey(new VpnTargetKey(importRT)).setVrfRTValue(importRT)
+                                    .setVrfRTType(VpnTarget.VrfRTType.ImportExtcommunity).build();
                     vpnTargetList.add(vpnTarget);
                 }
             }
 
             if (ert != null && !ert.isEmpty()) {
                 for (String exportRT : ert) {
-                    VpnTarget vpnTarget = new VpnTargetBuilder().setKey(new VpnTargetKey(exportRT)).setVrfRTValue
-                            (exportRT).setVrfRTType(VpnTarget.VrfRTType.ExportExtcommunity).build();
+                    VpnTarget vpnTarget =
+                            new VpnTargetBuilder().setKey(new VpnTargetKey(exportRT)).setVrfRTValue(exportRT)
+                                    .setVrfRTType(VpnTarget.VrfRTType.ExportExtcommunity).build();
                     vpnTargetList.add(vpnTarget);
                 }
             }
@@ -317,14 +442,14 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             }
 
             VpnInstance newVpn = builder.setIpv4Family(ipv4vpnBuilder.build()).build();
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, vpnName);
-            logger.debug("Creating/Updating vpn-instance for {} ", vpnName);
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier, newVpn);
+            isLockAcquired = NeutronvpnUtils.lock(vpnName);
+            LOG.debug("Creating/Updating vpn-instance for {} ", vpnName);
+            MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier, newVpn);
         } catch (Exception e) {
-            logger.error("Update VPN Instance node failed for node: {} {} {} {}", vpnName, rd, irt, ert);
+            LOG.error("Update VPN Instance node failed for node: {} {} {} {}", vpnName, rd, irt, ert);
         } finally {
             if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, vpnName);
+                NeutronvpnUtils.unlock(vpnName);
             }
         }
     }
@@ -332,16 +457,17 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
     private void deleteVpnMapsNode(Uuid vpnid) {
         boolean isLockAcquired = false;
         InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
-                .child(VpnMap.class, new VpnMapKey(vpnid)).build();
-        logger.debug("removing vpnMaps node: {} ", vpnid.getValue());
+                .child(VpnMap.class, new VpnMapKey(vpnid))
+                .build();
+        LOG.debug("removing vpnMaps node: {} ", vpnid.getValue());
         try {
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, vpnid.getValue());
-            MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
+            isLockAcquired = NeutronvpnUtils.lock(vpnid.getValue());
+            MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
         } catch (Exception e) {
-            logger.error("Delete vpnMaps node failed for vpn : {} ", vpnid.getValue());
+            LOG.error("Delete vpnMaps node failed for vpn : {} ", vpnid.getValue());
         } finally {
             if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, vpnid.getValue());
+                NeutronvpnUtils.unlock(vpnid.getValue());
             }
         }
     }
@@ -350,9 +476,10 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         VpnMapBuilder builder;
         boolean isLockAcquired = false;
         InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
-                .child(VpnMap.class, new VpnMapKey(vpnId)).build();
+                .child(VpnMap.class, new VpnMapKey(vpnId))
+                .build();
         try {
-            Optional<VpnMap> optionalVpnMap = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
+            Optional<VpnMap> optionalVpnMap = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                     vpnMapIdentifier);
             if (optionalVpnMap.isPresent()) {
                 builder = new VpnMapBuilder(optionalVpnMap.get());
@@ -378,15 +505,15 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                 builder.setNetworkIds(nwList);
             }
 
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, vpnId.getValue());
-            logger.debug("Creating/Updating vpnMaps node: {} ", vpnId.getValue());
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier, builder.build());
-            logger.debug("VPNMaps DS updated for VPN {} ", vpnId.getValue());
+            isLockAcquired = NeutronvpnUtils.lock(vpnId.getValue());
+            LOG.debug("Creating/Updating vpnMaps node: {} ", vpnId.getValue());
+            MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier, builder.build());
+            LOG.debug("VPNMaps DS updated for VPN {} ", vpnId.getValue());
         } catch (Exception e) {
-            logger.error("UpdateVpnMaps failed for node: {} ", vpnId.getValue());
+            LOG.error("UpdateVpnMaps failed for node: {} ", vpnId.getValue());
         } finally {
             if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, vpnId.getValue());
+                NeutronvpnUtils.unlock(vpnId.getValue());
             }
         }
     }
@@ -394,8 +521,9 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
     private void clearFromVpnMaps(Uuid vpnId, Uuid routerId, List<Uuid> networkIds) {
         boolean isLockAcquired = false;
         InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
-                .child(VpnMap.class, new VpnMapKey(vpnId)).build();
-        Optional<VpnMap> optionalVpnMap = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
+                .child(VpnMap.class, new VpnMapKey(vpnId))
+                .build();
+        Optional<VpnMap> optionalVpnMap = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                 vpnMapIdentifier);
         if (optionalVpnMap.isPresent()) {
             VpnMap vpnMap = optionalVpnMap.get();
@@ -404,14 +532,14 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                 if (vpnMap.getNetworkIds() == null && routerId.equals(vpnMap.getVpnId())) {
                     try {
                         // remove entire node in case of internal VPN
-                        isLockAcquired = NeutronvpnUtils.lock(lockManager, vpnId.getValue());
-                        logger.debug("removing vpnMaps node: {} ", vpnId);
-                        MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
+                        isLockAcquired = NeutronvpnUtils.lock(vpnId.getValue());
+                        LOG.debug("removing vpnMaps node: {} ", vpnId);
+                        MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
                     } catch (Exception e) {
-                        logger.error("Deletion of vpnMaps node failed for vpn {}", vpnId.getValue());
+                        LOG.error("Deletion of vpnMaps node failed for vpn {}", vpnId.getValue());
                     } finally {
                         if (isLockAcquired) {
-                            NeutronvpnUtils.unlock(lockManager, vpnId.getValue());
+                            NeutronvpnUtils.unlock(vpnId.getValue());
                         }
                     }
                     return;
@@ -424,7 +552,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                     vpnNw.remove(nw);
                 }
                 if (vpnNw.isEmpty()) {
-                    logger.debug("setting networks null in vpnMaps node: {} ", vpnId.getValue());
+                    LOG.debug("setting networks null in vpnMaps node: {} ", vpnId.getValue());
                     vpnMapBuilder.setNetworkIds(null);
                 } else {
                     vpnMapBuilder.setNetworkIds(vpnNw);
@@ -432,115 +560,113 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             }
 
             try {
-                isLockAcquired = NeutronvpnUtils.lock(lockManager, vpnId.getValue());
-                logger.debug("clearing from vpnMaps node: {} ", vpnId.getValue());
-                MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier, vpnMapBuilder.build
-                        ());
+                isLockAcquired = NeutronvpnUtils.lock(vpnId.getValue());
+                LOG.debug("clearing from vpnMaps node: {} ", vpnId.getValue());
+                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier,
+                        vpnMapBuilder.build());
             } catch (Exception e) {
-                logger.error("Clearing from vpnMaps node failed for vpn {}", vpnId.getValue());
+                LOG.error("Clearing from vpnMaps node failed for vpn {}", vpnId.getValue());
             } finally {
                 if (isLockAcquired) {
-                    NeutronvpnUtils.unlock(lockManager, vpnId.getValue());
+                    NeutronvpnUtils.unlock(vpnId.getValue());
                 }
             }
         } else {
-            logger.error("VPN : {} not found", vpnId.getValue());
+            LOG.error("VPN : {} not found", vpnId.getValue());
         }
-        logger.debug("Clear from VPNMaps DS successful for VPN {} ", vpnId.getValue());
+        LOG.debug("Clear from VPNMaps DS successful for VPN {} ", vpnId.getValue());
     }
 
     private void deleteVpnInstance(Uuid vpnId) {
         boolean isLockAcquired = false;
-        InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class).
-                child(VpnInstance.class, new VpnInstanceKey(vpnId.getValue())).build();
+        InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
+                .child(VpnInstance.class,
+                        new VpnInstanceKey(vpnId.getValue()))
+                .build();
         try {
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, vpnId.getValue());
-            logger.debug("Deleting vpnInstance {}", vpnId.getValue());
-            MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier);
+            isLockAcquired = NeutronvpnUtils.lock(vpnId.getValue());
+            LOG.debug("Deleting vpnInstance {}", vpnId.getValue());
+            MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier);
         } catch (Exception e) {
-            logger.error("Deletion of VPNInstance node failed for VPN {}", vpnId.getValue());
+            LOG.error("Deletion of VPNInstance node failed for VPN {}", vpnId.getValue());
         } finally {
             if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, vpnId.getValue());
+                NeutronvpnUtils.unlock(vpnId.getValue());
             }
         }
     }
 
-    protected void createVpnInterface(Uuid vpnId, Port port) {
-        boolean isLockAcquired = false;
-        if (vpnId == null || port == null) {
-            return;
-        }
+    protected void createVpnInterface(Uuid vpnId, Uuid routerId, Port port,
+                                      WriteTransaction wrtConfigTxn) {
         String infName = port.getUuid().getValue();
         List<Adjacency> adjList = new ArrayList<>();
-        InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
-
-        // find router associated to vpn
-        Uuid routerId = NeutronvpnUtils.getRouterforVpn(broker, vpnId);
+        List<FixedIps> ips = port.getFixedIps();
+        Boolean isRouterInterface = false;
+        if (port.getDeviceOwner() != null) {
+            isRouterInterface = port.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF);
+        }
+        LOG.trace("createVpnInterface - isRouterInterface:{}", isRouterInterface);
         Router rtr = null;
         if (routerId != null) {
-            rtr = NeutronvpnUtils.getNeutronRouter(broker, routerId);
+            rtr = NeutronvpnUtils.getNeutronRouter(dataBroker, routerId);
         }
-        // find all subnets to which this port is associated
-        List<FixedIps> ips = port.getFixedIps();
         // create adjacency list
         for (FixedIps ip : ips) {
             // create vm adjacency
-            StringBuilder IpPrefixBuild = new StringBuilder(ip.getIpAddress().getIpv4Address().getValue());
-            String IpPrefix = IpPrefixBuild.append("/32").toString();
-            Adjacency vmAdj = new AdjacencyBuilder().setKey(new AdjacencyKey(IpPrefix)).setIpAddress(IpPrefix)
-                    .setMacAddress(port.getMacAddress().getValue()).build();
+            String ipValue = String.valueOf(ip.getIpAddress().getValue());
+            String ipPrefix = (ip.getIpAddress().getIpv4Address() != null) ? ipValue + "/32" : ipValue + "/128";
+            Adjacency vmAdj = new AdjacencyBuilder().setKey(new AdjacencyKey(ipPrefix)).setIpAddress(ipPrefix)
+                    .setMacAddress(port.getMacAddress().getValue()).setPrimaryAdjacency(true)
+                    .setSubnetId(ip.getSubnetId()).build();
             adjList.add(vmAdj);
             // create extra route adjacency
             if (rtr != null && rtr.getRoutes() != null) {
                 List<Routes> routeList = rtr.getRoutes();
-                List<Adjacency> erAdjList = addAdjacencyforExtraRoute(routeList, false, infName);
+                List<Adjacency> erAdjList = getAdjacencyforExtraRoute(vpnId, routeList, ipValue);
                 if (erAdjList != null && !erAdjList.isEmpty()) {
                     adjList.addAll(erAdjList);
                 }
             }
+            NeutronvpnUtils.createVpnPortFixedIpToPort(dataBroker, vpnId.getValue(), ipValue, infName, port
+                            .getMacAddress().getValue(), isRouterInterface, true, false);
         }
         // create vpn-interface on this neutron port
         Adjacencies adjs = new AdjacenciesBuilder().setAdjacency(adjList).build();
-        VpnInterfaceBuilder vpnb = new VpnInterfaceBuilder().setKey(new VpnInterfaceKey(infName)).
-                setName(infName).setVpnInstanceName(vpnId.getValue()).addAugmentation(Adjacencies.class, adjs);
-        VpnInterface vpnIf = vpnb.build();
-
-        try {
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, infName);
-            logger.debug("Creating vpn interface {}", vpnIf);
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier, vpnIf);
-        } catch (Exception ex) {
-            logger.error("Creation of vpninterface {} failed due to {}", infName, ex);
-        } finally {
-            if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, infName);
-            }
+        writeVpnInterfaceToDs(vpnId, infName, adjs, isRouterInterface, wrtConfigTxn);
+        if (routerId != null) {
+            addToNeutronRouterInterfacesMap(routerId, infName);
         }
     }
 
-    protected void deleteVpnInterface(Port port) {
-
-        if (port != null) {
-            boolean isLockAcquired = false;
-            String infName = port.getUuid().getValue();
-            InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
+    protected void deleteVpnInterface(Uuid vpnId, Uuid routerId, Port port, WriteTransaction wrtConfigTxn) {
+        Boolean wrtConfigTxnPresent = true;
+        if (wrtConfigTxn == null) {
+            wrtConfigTxnPresent = false;
+            wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
+        }
+        String infName = port.getUuid().getValue();
+        InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
+        try {
+            LOG.debug("Deleting vpn interface {}", infName);
+            wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier);
 
-            try {
-                isLockAcquired = NeutronvpnUtils.lock(lockManager, infName);
-                logger.debug("Deleting vpn interface {}", infName);
-                MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier);
-            } catch (Exception ex) {
-                logger.error("Deletion of vpninterface {} failed due to {}", infName, ex);
-            } finally {
-                if (isLockAcquired) {
-                    NeutronvpnUtils.unlock(lockManager, infName);
-                }
+            List<FixedIps> ips = port.getFixedIps();
+            for (FixedIps ip : ips) {
+                String ipValue = String.valueOf(ip.getIpAddress().getValue());
+                NeutronvpnUtils.removeVpnPortFixedIpToPort(dataBroker, vpnId.getValue(), ipValue);
             }
+        } catch (Exception ex) {
+            LOG.error("Deletion of vpninterface {} failed due to {}", infName, ex);
+        }
+        if (routerId != null) {
+            removeFromNeutronRouterInterfacesMap(routerId, infName);
+        }
+        if (!wrtConfigTxnPresent) {
+            wrtConfigTxn.submit();
         }
     }
 
-    protected void updateVpnInterface(Uuid vpnId, Port port) {
+    protected void updateVpnInterface(Uuid vpnId, Uuid oldVpnId, Port port, boolean  isBeingAssociated, boolean isSubnetIp) {
         if (vpnId == null || port == null) {
             return;
         }
@@ -548,28 +674,62 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         String infName = port.getUuid().getValue();
         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
         try {
-            Optional<VpnInterface> optionalVpnInterface = NeutronvpnUtils.read(broker, LogicalDatastoreType
+            isLockAcquired = NeutronvpnUtils.lock(infName);
+            Optional<VpnInterface> optionalVpnInterface = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType
                     .CONFIGURATION, vpnIfIdentifier);
             if (optionalVpnInterface.isPresent()) {
-                VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get());
-                VpnInterface vpnIf = vpnIfBuilder.setVpnInstanceName(vpnId.getValue()).build();
-                isLockAcquired = NeutronvpnUtils.lock(lockManager, infName);
-                logger.debug("Updating vpn interface {}", vpnIf);
-                MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier, vpnIf);
+                VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get())
+                        .setVpnInstanceName(vpnId.getValue());
+                LOG.debug("Updating vpn interface {}", infName);
+                if (!isBeingAssociated) {
+                    Adjacencies adjs = vpnIfBuilder.getAugmentation(Adjacencies.class);
+                    List<Adjacency> adjacencyList = (adjs != null) ? adjs.getAdjacency() : new ArrayList<Adjacency>();
+                    Iterator<Adjacency> adjacencyIter = adjacencyList.iterator();
+                    while (adjacencyIter.hasNext()) {
+                        Adjacency adjacency = adjacencyIter.next();
+                        String mipToQuery = adjacency.getIpAddress().split("/")[0];
+                        InstanceIdentifier<VpnPortipToPort> id = NeutronvpnUtils.buildVpnPortipToPortIdentifier
+                                (oldVpnId.getValue(), mipToQuery);
+                        Optional<VpnPortipToPort> optionalVpnPort = NeutronvpnUtils.read(dataBroker,
+                                LogicalDatastoreType
+                                .OPERATIONAL, id);
+                        if (!optionalVpnPort.isPresent() || optionalVpnPort.get().isLearnt()) {
+                            LOG.trace("Removing adjacencies from vpninterface {} upon dissociation of router {} " +
+                                    "from VPN " + "{}", infName, vpnId, oldVpnId);
+                            adjacencyIter.remove();
+                            NeutronvpnUtils.removeVpnPortFixedIpToPort(dataBroker, oldVpnId.getValue(), mipToQuery);
+                            LOG.trace("Entry for fixedIP {} for port {} on VPN removed from " +
+                                    "VpnPortFixedIPToPortData", mipToQuery, infName, vpnId.getValue());
+                        }
+                    }
+                    Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(adjacencyList).build();
+                    vpnIfBuilder.addAugmentation(Adjacencies.class, adjacencies);
+                }
+                List<FixedIps> ips = port.getFixedIps();
+                for (FixedIps ip : ips) {
+                    String ipValue = String.valueOf(ip.getIpAddress().getValue());
+                    if (oldVpnId != null) {
+                        NeutronvpnUtils.removeVpnPortFixedIpToPort(dataBroker, oldVpnId.getValue(), ipValue);
+                    }
+                    NeutronvpnUtils.createVpnPortFixedIpToPort(dataBroker, vpnId.getValue(), ipValue, infName, port
+                            .getMacAddress().getValue(), isSubnetIp, true, false);
+                }
+                MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier, vpnIfBuilder
+                        .build());
             } else {
-                logger.error("VPN Interface {} not found", infName);
+                LOG.error("VPN Interface {} not found", infName);
             }
         } catch (Exception ex) {
-            logger.error("Updation of vpninterface {} failed due to {}", infName, ex);
+            LOG.error("Updation of vpninterface {} failed due to {}", infName, ex);
         } finally {
             if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, infName);
+                NeutronvpnUtils.unlock(infName);
             }
         }
     }
 
-    public void createL3Vpn(Uuid vpn, String name, Uuid tenant, List<String> rd, List<String> irt, List<String> ert,
-                            Uuid router, List<Uuid> networks) {
+    public void createL3InternalVpn(Uuid vpn, String name, Uuid tenant, List<String> rd, List<String> irt,
+                                    List<String> ert, Uuid router, List<Uuid> networks) {
 
         // Update VPN Instance node
         updateVpnInstanceNode(vpn.getValue(), rd, irt, ert);
@@ -577,14 +737,68 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         // Update local vpn-subnet DS
         updateVpnMaps(vpn, name, router, tenant, networks);
 
+        if (router != null) {
+            Uuid existingVpn = NeutronvpnUtils.getVpnForRouter(dataBroker, router, true);
+            if (existingVpn != null) {
+                // use case when a cluster is rebooted and router add DCN is received, triggering #createL3InternalVpn
+
+                // if before reboot, router was already associated to VPN, should not proceed associating router to
+                // internal VPN. Adding to RouterInterfacesMap is also not needed since it's a config DS and will be
+                // preserved upon reboot.
+                // For a non-reboot case #associateRouterToInternalVPN already takes care of adding to
+                // RouterInterfacesMap via #createVPNInterface call.
+                LOG.info("Associating router to Internal VPN skipped for VPN {} due to router {} already associated " +
+                        "to external VPN {}", vpn.getValue(), router.getValue(), existingVpn.getValue());
+                return;
+            }
+            associateRouterToInternalVpn(vpn, router);
+        }
+    }
+
+    /**
+     * Performs the creation of a Neutron L3VPN, associating the new VPN to the
+     * specified Neutron Networks and Routers
+     *
+     * @param vpn Uuid of the VPN tp be created
+     * @param name Representative name of the new VPN
+     * @param tenant Uuid of the Tenant under which the VPN is going to be created
+     * @param rd Route-distinguisher for the VPN
+     * @param irt A list of Import Route Targets
+     * @param ert A list of Export Route Targets
+     * @param router UUID of the neutron router the VPN may be associated to
+     * @param networks UUID of the neutron network the VPN may be associated to
+     * @throws Exception if association of L3VPN failed
+     */
+    public void createL3Vpn(Uuid vpn, String name, Uuid tenant, List<String> rd, List<String> irt, List<String> ert,
+                            Uuid router, List<Uuid> networks) throws Exception {
+
+        // Update VPN Instance node
+        updateVpnInstanceNode(vpn.getValue(), rd, irt, ert);
+
+        // Please note that router and networks will be filled into VPNMaps
+        // by subsequent calls here to associateRouterToVpn and
+        // associateNetworksToVpn
+        updateVpnMaps(vpn, name, null, tenant, null);
+
         if (router != null) {
             associateRouterToVpn(vpn, router);
         }
         if (networks != null) {
-            associateNetworksToVpn(vpn, networks);
+            List<String> failStrings = associateNetworksToVpn(vpn, networks);
+            if (failStrings != null &&  !failStrings.isEmpty()) {
+                LOG.error("L3VPN {} association to networks failed with error message {}. ",
+                        vpn.getValue(), failStrings.get(0));
+                throw new Exception(failStrings.get(0));
+            }
         }
     }
 
+    /**
+     * It handles the invocations to the createL3VPN RPC method
+     *
+     * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService#createL3VPN
+     * (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNInput)
+     */
     @Override
     public Future<RpcResult<CreateL3VPNOutput>> createL3VPN(CreateL3VPNInput input) {
 
@@ -596,12 +810,13 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
 
         List<L3vpn> vpns = input.getL3vpn();
         for (L3vpn vpn : vpns) {
+            List<String> existingRDs = NeutronvpnUtils.getExistingRDs(dataBroker);
             RpcError error = null;
             String msg;
             if (vpn.getRouteDistinguisher() == null || vpn.getImportRT() == null || vpn.getExportRT() == null) {
                 msg = String.format("Creation of L3VPN failed for VPN %s due to absence of RD/iRT/eRT input",
                         vpn.getId().getValue());
-                logger.warn(msg);
+                LOG.warn(msg);
                 error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                 errorList.add(error);
                 warningcount++;
@@ -610,27 +825,37 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             if (vpn.getRouteDistinguisher().size() > 1) {
                 msg = String.format("Creation of L3VPN failed for VPN %s due to multiple RD input %s",
                         vpn.getId().getValue(), vpn.getRouteDistinguisher());
-                logger.warn(msg);
+                LOG.warn(msg);
+                error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
+                errorList.add(error);
+                warningcount++;
+                continue;
+            }
+            if (existingRDs.contains(vpn.getRouteDistinguisher().get(0))) {
+                msg = String.format("Creation of L3VPN failed for VPN %s as another VPN with the same RD %s is already configured",
+                        vpn.getId().getValue(), vpn.getRouteDistinguisher().get(0));
+                LOG.warn(msg);
                 error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                 errorList.add(error);
                 warningcount++;
                 continue;
             }
             if (vpn.getRouterId() != null) {
-                if (NeutronvpnUtils.getNeutronRouter(broker, vpn.getRouterId()) == null) {
+                if (NeutronvpnUtils.getNeutronRouter(dataBroker, vpn.getRouterId()) == null) {
                     msg = String.format("Creation of L3VPN failed for VPN %s due to router not found %s",
                             vpn.getId().getValue(), vpn.getRouterId().getValue());
-                    logger.warn(msg);
+                    LOG.warn(msg);
                     error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                     errorList.add(error);
                     warningcount++;
                     continue;
                 }
-                Uuid vpnId = NeutronvpnUtils.getVpnForRouter(broker, vpn.getRouterId(), true);
+                Uuid vpnId = NeutronvpnUtils.getVpnForRouter(dataBroker, vpn.getRouterId(), true);
                 if (vpnId != null) {
-                    msg = String.format("Creation of L3VPN failed for VPN %s due to router %s already associated to " +
-                            "another VPN %s", vpn.getId().getValue(), vpn.getRouterId().getValue(), vpnId.getValue());
-                    logger.warn(msg);
+                    msg = String.format("Creation of L3VPN failed for VPN %s due to router %s already associated to "
+                                    + "another VPN %s", vpn.getId().getValue(), vpn.getRouterId().getValue(),
+                            vpnId.getValue());
+                    LOG.warn(msg);
                     error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                     errorList.add(error);
                     warningcount++;
@@ -639,19 +864,20 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             }
             if (vpn.getNetworkIds() != null) {
                 for (Uuid nw : vpn.getNetworkIds()) {
-                    Network network = NeutronvpnUtils.getNeutronNetwork(broker, nw);
-                    Uuid vpnId = NeutronvpnUtils.getVpnForNetwork(broker, nw);
+                    Network network = NeutronvpnUtils.getNeutronNetwork(dataBroker, nw);
+                    Uuid vpnId = NeutronvpnUtils.getVpnForNetwork(dataBroker, nw);
                     if (network == null) {
                         msg = String.format("Creation of L3VPN failed for VPN %s due to network not found %s",
                                 vpn.getId().getValue(), nw.getValue());
-                        logger.warn(msg);
+                        LOG.warn(msg);
                         error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                         errorList.add(error);
                         warningcount++;
                     } else if (vpnId != null) {
-                        msg = String.format("Creation of L3VPN failed for VPN %s due to network %s already associated" +
-                                " to another VPN %s", vpn.getId().getValue(), nw.getValue(), vpnId.getValue());
-                        logger.warn(msg);
+                        msg = String.format("Creation of L3VPN failed for VPN %s due to network %s already associated"
+                                        + " to another VPN %s", vpn.getId().getValue(), nw.getValue(),
+                                vpnId.getValue());
+                        LOG.warn(msg);
                         error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                         errorList.add(error);
                         warningcount++;
@@ -666,7 +892,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                         vpn.getImportRT(), vpn.getExportRT(), vpn.getRouterId(), vpn.getNetworkIds());
             } catch (Exception ex) {
                 msg = String.format("Creation of L3VPN failed for VPN %s", vpn.getId().getValue());
-                logger.error(msg, ex);
+                LOG.error(msg, ex);
                 error = RpcResultBuilder.newError(ErrorType.APPLICATION, msg, ex.getMessage());
                 errorList.add(error);
                 failurecount++;
@@ -675,24 +901,30 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         // if at least one succeeds; result is success
         // if none succeeds; result is failure
         if (failurecount + warningcount == vpns.size()) {
-            result.set(RpcResultBuilder.<CreateL3VPNOutput>failed().withRpcErrors(errorList).build());
+            result.set(RpcResultBuilder.<CreateL3VPNOutput> failed().withRpcErrors(errorList).build());
         } else {
             List<String> errorResponseList = new ArrayList<>();
             if (!errorList.isEmpty()) {
                 for (RpcError rpcError : errorList) {
-                    String errorResponse = String.format("ErrorType: " + rpcError.getErrorType() + ", " + "ErrorTag: " +
-                            rpcError.getTag() + ", " + "ErrorMessage: " + rpcError.getMessage());
+                    String errorResponse = String.format("ErrorType: %s, ErrorTag: %s, ErrorMessage: %s", rpcError
+                            .getErrorType(), rpcError.getTag(), rpcError.getMessage());
                     errorResponseList.add(errorResponse);
                 }
             } else {
                 errorResponseList.add("Operation successful with no errors");
             }
             opBuilder.setResponse(errorResponseList);
-            result.set(RpcResultBuilder.<CreateL3VPNOutput>success().withResult(opBuilder.build()).build());
+            result.set(RpcResultBuilder.<CreateL3VPNOutput> success().withResult(opBuilder.build()).build());
         }
         return result;
     }
 
+    /**
+     * It handles the invocations to the neutronvpn:getL3VPN RPC method
+     *
+     * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService#getL3VPN
+     * (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNInput)
+     */
     @Override
     public Future<RpcResult<GetL3VPNOutput>> getL3VPN(GetL3VPNInput input) {
 
@@ -704,9 +936,10 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         try {
             if (inputVpnId == null) {
                 // get all vpns
-                InstanceIdentifier<VpnInstances> vpnsIdentifier =
-                        InstanceIdentifier.builder(VpnInstances.class).build();
-                Optional<VpnInstances> optionalVpns = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier<VpnInstances> vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class)
+                        .build();
+                Optional<VpnInstances> optionalVpns = NeutronvpnUtils.read(dataBroker,
+                        LogicalDatastoreType.CONFIGURATION,
                         vpnsIdentifier);
                 if (optionalVpns.isPresent() && optionalVpns.get().getVpnInstance() != null) {
                     for (VpnInstance vpn : optionalVpns.get().getVpnInstance()) {
@@ -717,25 +950,26 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                     }
                 } else {
                     // No VPN present
-                    result.set(RpcResultBuilder.<GetL3VPNOutput>failed()
-                            .withWarning(ErrorType.PROTOCOL, "", "No VPN is present").build());
+                    result.set(RpcResultBuilder.<GetL3VPNOutput>failed().withWarning(ErrorType.PROTOCOL, "", "No VPN " +
+                            "is present").build());
                     return result;
                 }
             } else {
                 String name = inputVpnId.getValue();
-                InstanceIdentifier<VpnInstance> vpnIdentifier =
-                        InstanceIdentifier.builder(VpnInstances.class)
-                                .child(VpnInstance.class, new VpnInstanceKey(name)).build();
+                InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
+                        .child(VpnInstance.class,
+                                new VpnInstanceKey(name))
+                        .build();
                 // read VpnInstance Info
-                Optional<VpnInstance> optionalVpn = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
+                Optional<VpnInstance> optionalVpn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                         vpnIdentifier);
                 if (optionalVpn.isPresent()) {
                     vpns.add(optionalVpn.get());
                 } else {
                     String message = String.format("GetL3VPN failed because VPN %s is not present", name);
-                    logger.error(message);
-                    result.set(RpcResultBuilder.<GetL3VPNOutput>failed()
-                            .withWarning(ErrorType.PROTOCOL, "invalid-value", message).build());
+                    LOG.error(message);
+                    result.set(RpcResultBuilder.<GetL3VPNOutput>failed().withWarning(ErrorType.PROTOCOL,
+                            "invalid-value", message).build());
                 }
             }
             List<L3vpnInstances> l3vpnList = new ArrayList<>();
@@ -766,7 +1000,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                 }
 
                 l3vpn.setId(vpnId).setRouteDistinguisher(rd).setImportRT(irtList).setExportRT(ertList);
-                Optional<VpnMap> optionalVpnMap = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
+                Optional<VpnMap> optionalVpnMap = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                         vpnMapIdentifier);
                 if (optionalVpnMap.isPresent()) {
                     VpnMap vpnMap = optionalVpnMap.get();
@@ -777,16 +1011,22 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             }
 
             opBuilder.setL3vpnInstances(l3vpnList);
-            result.set(RpcResultBuilder.<GetL3VPNOutput>success().withResult(opBuilder.build()).build());
+            result.set(RpcResultBuilder.<GetL3VPNOutput> success().withResult(opBuilder.build()).build());
 
         } catch (Exception ex) {
             String message = String.format("GetL3VPN failed due to %s", ex.getMessage());
-            logger.error(message, ex);
-            result.set(RpcResultBuilder.<GetL3VPNOutput>failed().withError(ErrorType.APPLICATION, message).build());
+            LOG.error(message, ex);
+            result.set(RpcResultBuilder.<GetL3VPNOutput> failed().withError(ErrorType.APPLICATION, message).build());
         }
         return result;
     }
 
+    /**
+     * It handles the invocations to the neutronvpn:deleteL3VPN RPC method
+     *
+     * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService#deleteL3VPN
+     * (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNInput)
+     */
     @Override
     public Future<RpcResult<DeleteL3VPNOutput>> deleteL3VPN(DeleteL3VPNInput input) {
 
@@ -802,22 +1042,22 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             String msg;
             try {
                 InstanceIdentifier<VpnInstance> vpnIdentifier =
-                        InstanceIdentifier.builder(VpnInstances.class)
-                                .child(VpnInstance.class, new VpnInstanceKey(vpn.getValue())).build();
-                Optional<VpnInstance> optionalVpn = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
-                        vpnIdentifier);
+                        InstanceIdentifier.builder(VpnInstances.class).child(VpnInstance.class, new VpnInstanceKey
+                                (vpn.getValue())).build();
+                Optional<VpnInstance> optionalVpn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType
+                        .CONFIGURATION, vpnIdentifier);
                 if (optionalVpn.isPresent()) {
                     removeL3Vpn(vpn);
                 } else {
                     msg = String.format("VPN with vpnid: %s does not exist", vpn.getValue());
-                    logger.warn(msg);
+                    LOG.warn(msg);
                     error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-value", msg);
                     errorList.add(error);
                     warningcount++;
                 }
             } catch (Exception ex) {
                 msg = String.format("Deletion of L3VPN failed when deleting for uuid %s", vpn.getValue());
-                logger.error(msg, ex);
+                LOG.error(msg, ex);
                 error = RpcResultBuilder.newError(ErrorType.APPLICATION, msg, ex.getMessage());
                 errorList.add(error);
                 failurecount++;
@@ -826,213 +1066,364 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         // if at least one succeeds; result is success
         // if none succeeds; result is failure
         if (failurecount + warningcount == vpns.size()) {
-            result.set(RpcResultBuilder.<DeleteL3VPNOutput>failed().withRpcErrors(errorList).build());
+            result.set(RpcResultBuilder.<DeleteL3VPNOutput> failed().withRpcErrors(errorList).build());
         } else {
             List<String> errorResponseList = new ArrayList<>();
             if (!errorList.isEmpty()) {
                 for (RpcError rpcError : errorList) {
-                    String errorResponse = String.format("ErrorType: " + rpcError.getErrorType() + ", " + "ErrorTag: " +
-                            rpcError.getTag() + ", " + "ErrorMessage: " + rpcError.getMessage());
+                    String errorResponse = String.format("ErrorType: %s, ErrorTag: %s, ErrorMessage: %s", rpcError
+                            .getErrorType(), rpcError.getTag(), rpcError.getMessage());
                     errorResponseList.add(errorResponse);
                 }
             } else {
                 errorResponseList.add("Operation successful with no errors");
             }
             opBuilder.setResponse(errorResponseList);
-            result.set(RpcResultBuilder.<DeleteL3VPNOutput>success().withResult(opBuilder.build()).build());
+            result.set(RpcResultBuilder.<DeleteL3VPNOutput> success().withResult(opBuilder.build()).build());
         }
         return result;
     }
 
-    protected void addSubnetToVpn(Uuid vpnId, Uuid subnet) {
-        logger.debug("Adding subnet {} to vpn {}", subnet.getValue(), vpnId.getValue());
-        Subnetmap sn = updateSubnetNode(subnet, null, null, null, null, vpnId, null);
-        boolean isLockAcquired = false;
-        String lockName = vpnId.getValue() + subnet.getValue();
-        String elanInstanceName = sn.getNetworkId().getValue();
-        InstanceIdentifier<ElanInstance> elanIdentifierId = InstanceIdentifier.builder(ElanInstances.class).child
-                (ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
-        Optional<ElanInstance> elanInstance = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
-                elanIdentifierId);
-        //TODO: Cache it in add so can reuse it in update and delete. Best would be to cache in some ElanUtils
-        long elanTag = elanInstance.get().getElanTag();
-        Uuid routerId = NeutronvpnUtils.getVpnMap(broker, vpnId).getRouterId();
-        if (vpnId.equals(routerId)) {
-            isExternalVpn = false;
-        } else {
-            isExternalVpn = true;
-        }
-        try {
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, lockName);
-            checkAndPublishSubnetAddNotification(subnet, sn.getSubnetIp(), vpnId.getValue(), isExternalVpn, elanTag);
-            logger.debug("Subnet added to Vpn notification sent");
-        }catch (Exception e){
-            logger.error("Subnet added to Vpn notification failed",e);
-        }finally {
-            if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, lockName);
-            }
-        }
-        // Check if there are ports on this subnet and add corresponding vpn-interfaces
+    protected void addSubnetToVpn(final Uuid vpnId, Uuid subnet) {
+        LOG.debug("Adding subnet {} to vpn {}", subnet.getValue(), vpnId.getValue());
+        Subnetmap sn = updateSubnetNode(subnet, null, null, null, null, vpnId);
+        final Uuid routerId = NeutronvpnUtils.getVpnMap(dataBroker, vpnId).getRouterId();
+        // Check if there are ports on this subnet and add corresponding
+        // vpn-interfaces
         List<Uuid> portList = sn.getPortList();
         if (portList != null) {
-            for (Uuid port : sn.getPortList()) {
-                logger.debug("adding vpn-interface for port {}", port.getValue());
-                createVpnInterface(vpnId, NeutronvpnUtils.getNeutronPort(broker, port));
+            for (final Uuid portId : sn.getPortList()) {
+                LOG.debug("adding vpn-interface for port {}", portId.getValue());
+                final DataStoreJobCoordinator portDataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                portDataStoreCoordinator.enqueueJob("PORT-" + portId.getValue(), new
+                        Callable<List<ListenableFuture<Void>>>() {
+                    @Override
+                    public List<ListenableFuture<Void>> call() throws Exception {
+                        WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
+                        List<ListenableFuture<Void>> futures = new ArrayList<>();
+                        createVpnInterface(vpnId, routerId, NeutronvpnUtils.getNeutronPort(dataBroker, portId),
+                                wrtConfigTxn);
+                        futures.add(wrtConfigTxn.submit());
+                        return futures;
+                    }
+                });
             }
         }
     }
 
     protected void updateVpnForSubnet(Uuid vpnId, Uuid subnet, boolean isBeingAssociated) {
-        logger.debug("Updating VPN {} for subnet {}", vpnId.getValue(), subnet.getValue());
-        Subnetmap sn = updateSubnetNode(subnet, null, null, null, null, vpnId, null);
-        boolean isLockAcquired = false;
-        String lockName = vpnId.getValue() + subnet.getValue();
-        String elanInstanceName = sn.getNetworkId().getValue();
-        InstanceIdentifier<ElanInstance> elanIdentifierId = InstanceIdentifier.builder(ElanInstances.class).child
-                (ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
-        Optional<ElanInstance> elanInstance = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
-                elanIdentifierId);
-        long elanTag = elanInstance.get().getElanTag();
-        try {
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, lockName);
-            checkAndPublishSubnetUpdNotification(subnet, sn.getSubnetIp(), vpnId.getValue(), isBeingAssociated,
-                    elanTag);
-            logger.debug("Subnet updated in Vpn notification sent");
-        } catch (Exception e) {
-            logger.error("Subnet updated in Vpn notification failed", e);
-        }finally {
-            if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, lockName);
+        LOG.debug("Updating VPN {} for subnet {}", vpnId.getValue(), subnet.getValue());
+        // Read the subnet first to see if its already associated to a VPN
+        Uuid oldVpnId = null;
+        InstanceIdentifier<Subnetmap> snId = InstanceIdentifier.builder(Subnetmaps.class).
+                child(Subnetmap.class, new SubnetmapKey(subnet)).build();
+        Subnetmap sn = null;
+        Optional<Subnetmap> optSn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, snId);
+        if (optSn.isPresent()) {
+            sn = optSn.get();
+            oldVpnId = sn.getVpnId();
+            List<String> ips = sn.getRouterInterfaceFixedIps();
+            for (String ipValue : ips) {
+                // Update the association of router-interface to external vpn
+                String PortName = NeutronvpnUtils.getNeutronPortNameFromVpnPortFixedIp(dataBroker, oldVpnId.getValue(), ipValue);
+                updateVpnInterface(vpnId, oldVpnId, NeutronvpnUtils.getNeutronPort(dataBroker, new Uuid(PortName)),
+                        isBeingAssociated, true);
             }
         }
-        // Check for ports on this subnet and update association of corresponding vpn-interfaces to external vpn
+        sn = updateSubnetNode(subnet, null, null, null, null, vpnId);
+        // Check for ports on this subnet and update association of
+        // corresponding vpn-interfaces to external vpn
         List<Uuid> portList = sn.getPortList();
         if (portList != null) {
             for (Uuid port : sn.getPortList()) {
-                logger.debug("Updating vpn-interface for port {}", port.getValue());
-                updateVpnInterface(vpnId, NeutronvpnUtils.getNeutronPort(broker, port));
+                LOG.debug("Updating vpn-interface for port {} isBeingAssociated {}", port.getValue(), isBeingAssociated);
+                updateVpnInterface(vpnId, oldVpnId, NeutronvpnUtils.getNeutronPort(dataBroker, port),
+                        isBeingAssociated, false);
             }
         }
     }
 
-    InstanceIdentifier<RouterInterfaces> getRouterInterfacesId(Uuid routerId) {
+    public InstanceIdentifier<RouterInterfaces> getRouterInterfacesId(Uuid routerId) {
         return InstanceIdentifier.builder(RouterInterfacesMap.class)
                 .child(RouterInterfaces.class, new RouterInterfacesKey(routerId)).build();
     }
-    void addToNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
-        InstanceIdentifier<RouterInterfaces> routerInterfacesId =  getRouterInterfacesId(routerId);
-        Optional<RouterInterfaces> optRouterInterfaces = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId);
-        Interfaces routerInterface = new InterfacesBuilder().setKey(new InterfacesKey(interfaceName)).setInterfaceId(interfaceName).build();
-        if(optRouterInterfaces.isPresent()) {
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)), routerInterface);
-        } else {
-            RouterInterfacesBuilder builder = new RouterInterfacesBuilder().setRouterId(routerId);
-            List<Interfaces> interfaces = new ArrayList<>();
-            interfaces.add(routerInterface);
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId, builder.setInterfaces(interfaces).build());
+
+    protected void addToNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
+        synchronized (routerId.getValue().intern()) {
+            InstanceIdentifier<RouterInterfaces> routerInterfacesId = getRouterInterfacesId(routerId);
+            Optional<RouterInterfaces> optRouterInterfaces = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType
+                    .CONFIGURATION, routerInterfacesId);
+            Interfaces routerInterface = new InterfacesBuilder().setKey(new InterfacesKey(interfaceName)).setInterfaceId
+                    (interfaceName).build();
+            if (optRouterInterfaces.isPresent()) {
+                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId.child(Interfaces
+                        .class, new InterfacesKey(interfaceName)), routerInterface);
+            } else {
+                RouterInterfacesBuilder builder = new RouterInterfacesBuilder().setRouterId(routerId);
+                List<Interfaces> interfaces = new ArrayList<>();
+                interfaces.add(routerInterface);
+                MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId.child(Interfaces
+                        .class, new InterfacesKey(interfaceName)), routerInterface);
+            }
         }
     }
-    
-    void removeFromNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
-        InstanceIdentifier<RouterInterfaces> routerInterfacesId =  getRouterInterfacesId(routerId);
-        Optional<RouterInterfaces> optRouterInterfaces = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId);
-        Interfaces routerInterface = new InterfacesBuilder().setKey(new InterfacesKey(interfaceName)).setInterfaceId(interfaceName).build();
-        if(optRouterInterfaces.isPresent()) {
-            RouterInterfaces routerInterfaces = optRouterInterfaces.get();
-            List<Interfaces> interfaces = routerInterfaces.getInterfaces();
-            if(interfaces != null && interfaces.remove(routerInterface)) {
-                if(interfaces.isEmpty()) {
-                    MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId);
-                } else {
-                    MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)));
+
+    protected void removeFromNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
+        synchronized (routerId.getValue().intern()) {
+            InstanceIdentifier<RouterInterfaces> routerInterfacesId = getRouterInterfacesId(routerId);
+            Optional<RouterInterfaces> optRouterInterfaces = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType
+                    .CONFIGURATION, routerInterfacesId);
+            Interfaces routerInterface = new InterfacesBuilder().setKey(new InterfacesKey(interfaceName)).setInterfaceId
+                    (interfaceName).build();
+            if (optRouterInterfaces.isPresent()) {
+                RouterInterfaces routerInterfaces = optRouterInterfaces.get();
+                List<Interfaces> interfaces = routerInterfaces.getInterfaces();
+                if (interfaces != null && interfaces.remove(routerInterface)) {
+                    if (interfaces.isEmpty()) {
+                        MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId);
+                    } else {
+                        MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                                routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)));
+                    }
                 }
             }
         }
     }
 
-    protected List<Adjacency> addAdjacencyforExtraRoute(List<Routes> routeList, boolean rtrUp, String vpnifname) {
+    /**
+     * Creates the corresponding static routes in the specified VPN. These static routes must be point to an
+     * InterVpnLink endpoint and the specified VPN must be the other end of the InterVpnLink. Otherwise the
+     * route will be ignored.
+     *
+     * @param vpnName the VPN identifier
+     * @param interVpnLinkRoutes The list of static routes
+     * @param nexthopsXinterVpnLinks A Map with the correspondence nextHop-InterVpnLink
+     */
+    public void addInterVpnRoutes(Uuid vpnName, List<Routes> interVpnLinkRoutes,
+                                  HashMap<String, InterVpnLink> nexthopsXinterVpnLinks) {
+        for ( Routes route : interVpnLinkRoutes ) {
+            String nexthop = String.valueOf(route.getNexthop().getValue());
+            String destination = String.valueOf(route.getDestination().getValue());
+            InterVpnLink interVpnLink = nexthopsXinterVpnLinks.get(nexthop);
+            if ( isNexthopTheOtherVpnLinkEndpoint(nexthop, vpnName.getValue(), interVpnLink) ) {
+                AddStaticRouteInput rpcInput =
+                        new AddStaticRouteInputBuilder().setDestination(destination).setNexthop(nexthop)
+                                .setVpnInstanceName(vpnName.getValue())
+                                .build();
+                Future<RpcResult<AddStaticRouteOutput>> labelOuputFtr = vpnRpcService.addStaticRoute(rpcInput);
+                RpcResult<AddStaticRouteOutput> rpcResult;
+                try {
+                    rpcResult = labelOuputFtr.get();
+                    if ( rpcResult.isSuccessful() ) {
+                        LOG.debug("Label generated for destination {} is: {}",
+                                destination, rpcResult.getResult().getLabel());
+                    } else {
+                        LOG.warn("RPC call to add a static Route to {} with nexthop {} returned with errors {}",
+                                destination, nexthop, rpcResult.getErrors());
+                    }
+                } catch (InterruptedException | ExecutionException e) {
+                    LOG.warn("Error happened while invoking addStaticRoute RPC: ", e);
+                }
+            } else {
+                // Any other case is a fault.
+                LOG.warn("route with destination {} and nexthop {} does not apply to any InterVpnLink",
+                        String.valueOf(route.getDestination().getValue()), nexthop );
+                continue;
+            }
+        }
+    }
+
+    /**
+     * Removes the corresponding static routes from the specified VPN. These static routes point to an
+     * InterVpnLink endpoint and the specified VPN must be the other end of the InterVpnLink.
+     *
+     * @param vpnName the VPN identifier
+     * @param interVpnLinkRoutes The list of static routes
+     * @param nexthopsXinterVpnLinks A Map with the correspondence nextHop-InterVpnLink
+     */
+    public void removeInterVpnRoutes(Uuid vpnName, List<Routes> interVpnLinkRoutes,
+                                     HashMap<String, InterVpnLink> nexthopsXinterVpnLinks) {
+        for ( Routes route : interVpnLinkRoutes ) {
+            String nexthop = String.valueOf(route.getNexthop().getValue());
+            String destination = String.valueOf(route.getDestination().getValue());
+            InterVpnLink interVpnLink = nexthopsXinterVpnLinks.get(nexthop);
+            if ( isNexthopTheOtherVpnLinkEndpoint(nexthop, vpnName.getValue(), interVpnLink) ) {
+                RemoveStaticRouteInput rpcInput =
+                        new RemoveStaticRouteInputBuilder().setDestination(destination).setNexthop(nexthop)
+                                .setVpnInstanceName(vpnName.getValue())
+                                .build();
+                vpnRpcService.removeStaticRoute(rpcInput);
+            } else {
+                // Any other case is a fault.
+                LOG.warn("route with destination {} and nexthop {} does not apply to any InterVpnLink",
+                        String.valueOf(route.getDestination().getValue()), nexthop );
+                continue;
+            }
+        }
+    }
+
+    /*
+     * Returns true if the specified nexthop is the other endpoint in an
+     * InterVpnLink, regarding one of the VPN's point of view.
+     */
+    private boolean isNexthopTheOtherVpnLinkEndpoint(String nexthop, String thisVpnUuid, InterVpnLink interVpnLink) {
+        return
+                interVpnLink != null
+                        && (   (interVpnLink.getFirstEndpoint().getVpnUuid().getValue().equals(thisVpnUuid)
+                        && interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(nexthop))
+                        || (interVpnLink.getSecondEndpoint().getVpnUuid().getValue().equals(thisVpnUuid )
+                        && interVpnLink.getFirstEndpoint().getIpAddress().getValue().equals(nexthop)) );
+    }
+
+    protected List<Adjacency> getAdjacencyforExtraRoute(Uuid vpnId, List<Routes> routeList, String fixedIp) {
         List<Adjacency> adjList = new ArrayList<>();
+        Map<String, List<String>> adjMap = new HashMap<>();
         for (Routes route : routeList) {
-            if (route != null && route.getNexthop() != null && route.getDestination() != null) {
-                boolean isLockAcquired = false;
+            if (route == null || route.getNexthop() == null || route.getDestination() == null) {
+                LOG.error("Incorrect input received for extra route. {}", route);
+            } else {
                 String nextHop = String.valueOf(route.getNexthop().getValue());
                 String destination = String.valueOf(route.getDestination().getValue());
-
-                String infName = NeutronvpnUtils.getNeutronPortNamefromPortFixedIp(broker, nextHop);
-                logger.trace("Adding extra route with nexthop {}, destination {}, infName {}", nextHop,
-                        destination, infName);
-                Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destination).setNextHopIp(nextHop).setKey
-                        (new AdjacencyKey(destination)).build();
-                if (!rtrUp) {
-                    if (infName.equals(vpnifname)) {
-                        adjList.add(erAdj);
-                    }
+                if (!nextHop.equals(fixedIp)) {
+                    LOG.trace("FixedIP {} is not extra route nexthop for destination {}", fixedIp, destination);
                     continue;
                 }
-                InstanceIdentifier<VpnInterface> vpnIfIdentifier = InstanceIdentifier.builder(VpnInterfaces.class).
-                        child(VpnInterface.class, new VpnInterfaceKey(infName)).build();
-                try {
-                    Optional<VpnInterface> optionalVpnInterface = NeutronvpnUtils.read(broker, LogicalDatastoreType
-                            .CONFIGURATION, vpnIfIdentifier);
-                    if (optionalVpnInterface.isPresent()) {
-                        Adjacencies erAdjs = new AdjacenciesBuilder().setAdjacency(Arrays.asList(erAdj)).build();
-                        VpnInterface vpnIf = new VpnInterfaceBuilder().setKey(new VpnInterfaceKey(infName))
-                                .addAugmentation(Adjacencies.class, erAdjs).build();
-                        isLockAcquired = NeutronvpnUtils.lock(lockManager, infName);
-                        logger.debug("Adding extra route {}", route);
-                        MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier, vpnIf);
-                    } else {
-                        logger.error("VM adjacency for interface {} not present ; cannot add extra route adjacency",
-                                infName);
-                    }
-                } catch (Exception e) {
-                    logger.error("exception in adding extra route: {}" + e);
-                } finally {
-                    if (isLockAcquired) {
-                        NeutronvpnUtils.unlock(lockManager, infName);
-                    }
+                LOG.trace("Adding extra route for destination {} onto vpn {} with nexthop {} ", destination,
+                        vpnId.getValue(), nextHop);
+                List<String> hops = adjMap.get(destination);
+                if (hops == null) {
+                    hops = new ArrayList<>();
+                    adjMap.put(destination, hops);
                 }
+                if (!hops.contains(nextHop)) {
+                    hops.add(nextHop);
+                }
+            }
+        }
+
+        for (String destination : adjMap.keySet()) {
+            Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destination).setNextHopIpList(adjMap.get
+                    (destination)).setKey(new AdjacencyKey(destination)).build();
+            adjList.add(erAdj);
+        }
+        return  adjList;
+    }
+
+    protected void updateVpnInterfaceWithExtraRouteAdjacency(Uuid vpnId, List<Routes> routeList) {
+        for (Routes route : routeList) {
+            if (route == null || route.getNexthop() == null || route.getDestination() == null) {
+                LOG.error("Incorrect input received for extra route. {}", route);
             } else {
-                logger.error("Incorrect input received for extra route. {}", route);
+                String nextHop = String.valueOf(route.getNexthop().getValue());
+                String destination = String.valueOf(route.getDestination().getValue());
+                String infName = NeutronvpnUtils.getNeutronPortNameFromVpnPortFixedIp(dataBroker, vpnId.getValue(),
+                        nextHop);
+                if (infName != null) {
+                    LOG.trace("Updating extra route for destination {} onto vpn {} with nexthop {} and infName {}", destination,
+                            vpnId.getValue(), nextHop, infName);
+                    boolean isLockAcquired = false;
+                    try {
+                        InstanceIdentifier<VpnInterface> identifier = InstanceIdentifier.builder(VpnInterfaces.class)
+                                .child(VpnInterface.class, new VpnInterfaceKey(infName)).build();
+                        InstanceIdentifier<Adjacency> path = identifier.augmentation(Adjacencies.class).
+                                child(Adjacency.class, new AdjacencyKey(destination));
+                        Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destination).setNextHopIpList(Arrays.asList(nextHop)).
+                                setKey(new AdjacencyKey(destination)).build();
+                        isLockAcquired = NeutronvpnUtils.lock(infName);
+                        MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, path, erAdj);
+                    } catch (Exception e) {
+                        LOG.error("exception in adding extra route with destination: {}, next hop: {}", destination, nextHop, e);
+                    } finally {
+                        if (isLockAcquired) {
+                            NeutronvpnUtils.unlock(infName);
+                        }
+                    }
+                } else {
+                    LOG.debug("Unable to find VPN NextHop interface to apply extra-route destination {} on VPN {} " +
+                            "with nexthop {}", destination, vpnId.getValue(), nextHop);
+                }
             }
         }
-        return adjList;
     }
 
-    protected void removeAdjacencyforExtraRoute(List<Routes> routeList) {
+    protected void removeAdjacencyforExtraRoute(Uuid vpnId, List<Routes> routeList) {
         for (Routes route : routeList) {
             if (route != null && route.getNexthop() != null && route.getDestination() != null) {
                 boolean isLockAcquired = false;
                 String nextHop = String.valueOf(route.getNexthop().getValue());
                 String destination = String.valueOf(route.getDestination().getValue());
+                String infName = NeutronvpnUtils.getNeutronPortNameFromVpnPortFixedIp(dataBroker, vpnId.getValue(),
+                        nextHop);
+                if (infName == null) {
+                    LOG.error("Unable to find VPN NextHop interface to remove extra-route destination {} on VPN {} " +
+                                    "with nexthop {}",
+                            destination, vpnId.getValue(), nextHop);
+                    // Proceed to remove the next extra-route
+                    continue;
+                }
+                LOG.trace("Removing extra route for destination {} on vpn {} with nexthop {} and infName {}",
+                        destination, vpnId.getValue(), nextHop, infName);
+
+                InstanceIdentifier<Adjacency> adjacencyIdentifier =
+                        InstanceIdentifier.builder(VpnInterfaces.class)
+                                .child(VpnInterface.class, new VpnInterfaceKey(infName))
+                                .augmentation(Adjacencies.class)
+                                .child(Adjacency.class, new AdjacencyKey(destination))
+                                .build();
+
+                // Looking for existing prefix in MDSAL database
+                Optional<Adjacency> adjacency = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                        adjacencyIdentifier);
+                boolean updateNextHops = false;
+                List<String> nextHopList = new ArrayList<>();
+                if (adjacency.isPresent()) {
+                    List<String> nhListRead = adjacency.get().getNextHopIpList();
+                    if (nhListRead.size() > 1) { // ECMP case
+                        for (String nextHopRead : nhListRead) {
+                            if (nextHopRead.equals(nextHop)) {
+                                updateNextHops = true;
+                            } else {
+                                nextHopList.add(nextHopRead);
+                            }
+                        }
+                    }
+                }
 
-                String infName = NeutronvpnUtils.getNeutronPortNamefromPortFixedIp(broker, nextHop);
-                logger.trace("Removing extra route with nexthop {}, destination {}, infName {}", nextHop,
-                        destination, infName);
-                InstanceIdentifier<Adjacency> adjacencyIdentifier = InstanceIdentifier.builder(VpnInterfaces.class).
-                        child(VpnInterface.class, new VpnInterfaceKey(infName)).augmentation(Adjacencies.class)
-                        .child(Adjacency.class, new AdjacencyKey(destination)).build();
                 try {
-                    isLockAcquired = NeutronvpnUtils.lock(lockManager, infName);
-                    MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier);
-                    logger.trace("extra route {} deleted successfully", route);
+                    isLockAcquired = NeutronvpnUtils.lock(infName);
+                    if (updateNextHops) {
+                        // An update must be done, not including the current next hop
+                        InstanceIdentifier<VpnInterface> vpnIfIdentifier = InstanceIdentifier.builder(
+                                VpnInterfaces.class).child(VpnInterface.class, new VpnInterfaceKey(infName)).build();
+                        Adjacency newAdj = new AdjacencyBuilder(adjacency.get()).setIpAddress(destination)
+                                .setNextHopIpList(nextHopList)
+                                .setKey(new AdjacencyKey(destination))
+                                .build();
+                        Adjacencies erAdjs = new AdjacenciesBuilder().setAdjacency(Arrays.asList(newAdj)).build();
+                        VpnInterface vpnIf = new VpnInterfaceBuilder().setKey(new VpnInterfaceKey(infName))
+                                .addAugmentation(Adjacencies.class, erAdjs).build();
+                        MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier, vpnIf);
+                    } else {
+                        // Remove the whole route
+                        MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier);
+                        LOG.trace("extra route {} deleted successfully", route);
+                    }
                 } catch (Exception e) {
-                    logger.error("exception in deleting extra route: {}" + e);
+                    LOG.error("exception in deleting extra route: {}" + e);
                 } finally {
                     if (isLockAcquired) {
-                        NeutronvpnUtils.unlock(lockManager, infName);
+                        NeutronvpnUtils.unlock(infName);
                     }
                 }
             } else {
-                logger.error("Incorrect input received for extra route. {}", route);
+                LOG.error("Incorrect input received for extra route. {}", route);
             }
         }
     }
 
     protected void removeL3Vpn(Uuid id) {
         // read VPNMaps
-        VpnMap vpnMap = NeutronvpnUtils.getVpnMap(broker, id);
+        VpnMap vpnMap = NeutronvpnUtils.getVpnMap(dataBroker, id);
         Uuid router = vpnMap.getRouterId();
         // dissociate router
         if (router != null) {
@@ -1049,120 +1440,101 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         deleteVpnInstance(id);
     }
 
-    protected void removeSubnetFromVpn(Uuid vpnId, Uuid subnet) {
-        logger.debug("Removing subnet {} from vpn {}", subnet.getValue(), vpnId.getValue());
-        Subnetmap sn = NeutronvpnUtils.getSubnetmap(broker, subnet);
-        boolean isLockAcquired = false;
-        String lockName = vpnId.getValue() + subnet.getValue();
-        String elanInstanceName = sn.getNetworkId().getValue();
-        InstanceIdentifier<ElanInstance> elanIdentifierId = InstanceIdentifier.builder(ElanInstances.class).child
-                (ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
-        Optional<ElanInstance> elanInstance = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
-                elanIdentifierId);
-        long elanTag = elanInstance.get().getElanTag();
-        Uuid routerId = NeutronvpnUtils.getVpnMap(broker, vpnId).getRouterId();
-        if (vpnId.equals(routerId)) {
-            isExternalVpn = false;
-        } else {
-            isExternalVpn = true;
-        }
-        try {
-            isLockAcquired = NeutronvpnUtils.lock(lockManager, lockName);
-            checkAndPublishSubnetDelNotification(subnet, sn.getSubnetIp(), vpnId.getValue(), isExternalVpn, elanTag);
-            logger.debug("Subnet removed from Vpn notification sent");
-        }catch (Exception e){
-            logger.error("Subnet removed from Vpn notification failed",e);
-        }finally {
-            if (isLockAcquired) {
-                NeutronvpnUtils.unlock(lockManager, lockName);
-            }
-        }
+    protected void removeSubnetFromVpn(final Uuid vpnId, Uuid subnet) {
+        LOG.debug("Removing subnet {} from vpn {}", subnet.getValue(), vpnId.getValue());
+        final Uuid routerId = NeutronvpnUtils.getVpnMap(dataBroker, vpnId).getRouterId();
+        Subnetmap sn = NeutronvpnUtils.getSubnetmap(dataBroker, subnet);
         if (sn != null) {
             // Check if there are ports on this subnet; remove corresponding vpn-interfaces
             List<Uuid> portList = sn.getPortList();
             if (portList != null) {
-                for (Uuid port : sn.getPortList()) {
-                    logger.debug("removing vpn-interface for port {}", port.getValue());
-                    deleteVpnInterface(NeutronvpnUtils.getNeutronPort(broker, port));
-                    if (routerId != null) {
-                        removeFromNeutronRouterInterfacesMap(routerId, port.getValue());
-                    }
+                for (final Uuid portId : sn.getPortList()) {
+                    LOG.debug("removing vpn-interface for port {}", portId.getValue());
+                    final DataStoreJobCoordinator portDataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                    portDataStoreCoordinator.enqueueJob("PORT-" + portId.getValue(), () -> {
+                        WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
+                        List<ListenableFuture<Void>> futures = new ArrayList<>();
+                        deleteVpnInterface(vpnId, routerId, NeutronvpnUtils.getNeutronPort(dataBroker, portId),
+                                wrtConfigTxn);
+                        futures.add(wrtConfigTxn.submit());
+                        return futures;
+                    });
                 }
             }
             // update subnet-vpn association
             removeFromSubnetNode(subnet, null, null, vpnId, null);
         } else {
-            logger.warn("Subnetmap for subnet {} not found", subnet.getValue());
+            LOG.warn("Subnetmap for subnet {} not found", subnet.getValue());
         }
     }
 
     protected void associateRouterToVpn(Uuid vpnId, Uuid routerId) {
         updateVpnMaps(vpnId, null, routerId, null, null);
-        List<Uuid> routerSubnets = NeutronvpnUtils.getNeutronRouterSubnetIds(broker, routerId);
-        if (!vpnId.equals(routerId)) {
-            logger.debug("Updating association of subnets to external vpn {}", vpnId.getValue());
-            if (routerSubnets != null) {
-                for (Uuid subnetId : routerSubnets) {
-                    updateVpnForSubnet(vpnId, subnetId, true);
-                }
-            }
-            try {
-                checkAndPublishRouterAssociatedtoVpnNotification(routerId, vpnId);
-                logger.debug("notification upon association of router {} to VPN {} published", routerId.getValue(),
-                        vpnId.getValue());
-            } catch (Exception e) {
-                logger.error("publishing of notification upon association of router {} to VPN {} failed : ", routerId
-                        .getValue(), vpnId.getValue(), e);
-            }
-        } else {
-            logger.debug("Adding subnets to internal vpn {}", vpnId.getValue());
-            for (Uuid subnet : routerSubnets) {
-                addSubnetToVpn(vpnId, subnet);
+        LOG.debug("Updating association of subnets to external vpn {}", vpnId.getValue());
+        List<Uuid> routerSubnets = NeutronvpnUtils.getNeutronRouterSubnetIds(dataBroker, routerId);
+        if (routerSubnets != null) {
+            for (Uuid subnetId : routerSubnets) {
+                updateVpnForSubnet(vpnId, subnetId, true);
             }
         }
+        try {
+            checkAndPublishRouterAssociatedtoVpnNotification(routerId, vpnId);
+            LOG.debug("notification upon association of router {} to VPN {} published", routerId.getValue(),
+                    vpnId.getValue());
+        } catch (Exception e) {
+            LOG.error("publishing of notification upon association of router {} to VPN {} failed : ", routerId
+                    .getValue(), vpnId.getValue(), e);
+        }
+    }
+
+    protected void associateRouterToInternalVpn(Uuid vpnId, Uuid routerId) {
+        List<Uuid> routerSubnets = NeutronvpnUtils.getNeutronRouterSubnetIds(dataBroker, routerId);
+        LOG.debug("Adding subnets to internal vpn {}", vpnId.getValue());
+        for (Uuid subnet : routerSubnets) {
+            addSubnetToVpn(vpnId, subnet);
+        }
     }
 
     protected void dissociateRouterFromVpn(Uuid vpnId, Uuid routerId) {
 
-        List<Uuid> routerSubnets = NeutronvpnUtils.getNeutronRouterSubnetIds(broker, routerId);
+        List<Uuid> routerSubnets = NeutronvpnUtils.getNeutronRouterSubnetIds(dataBroker, routerId);
         if (routerSubnets != null) {
             for (Uuid subnetId : routerSubnets) {
-                logger.debug("Updating association of subnets to internal vpn {}", routerId.getValue());
-                updateVpnForSubnet(routerId, subnetId,false);
+                LOG.debug("Updating association of subnets to internal vpn {}", routerId.getValue());
+                updateVpnForSubnet(routerId, subnetId, false);
             }
         }
         clearFromVpnMaps(vpnId, routerId, null);
         try {
             checkAndPublishRouterDisassociatedFromVpnNotification(routerId, vpnId);
-            logger.debug("notification upon disassociation of router {} from VPN {} published", routerId.getValue(),
+            LOG.debug("notification upon disassociation of router {} from VPN {} published", routerId.getValue(),
                     vpnId.getValue());
         } catch (Exception e) {
-            logger.error("publishing of notification upon disassociation of router {} from VPN {} failed : ", routerId
+            LOG.error("publishing of notification upon disassociation of router {} from VPN {} failed : ", routerId
                     .getValue(), vpnId.getValue(), e);
         }
     }
 
     protected List<String> associateNetworksToVpn(Uuid vpn, List<Uuid> networks) {
-
-        List<String> failedNwList = new ArrayList<String>();
+        List<String> failedNwList = new ArrayList<>();
         List<Uuid> passedNwList = new ArrayList<>();
         if (!networks.isEmpty()) {
             // process corresponding subnets for VPN
             for (Uuid nw : networks) {
-                Network network = NeutronvpnUtils.getNeutronNetwork(broker, nw);
-                Uuid vpnId = NeutronvpnUtils.getVpnForNetwork(broker, nw);
+                Network network = NeutronvpnUtils.getNeutronNetwork(dataBroker, nw);
+                Uuid vpnId = NeutronvpnUtils.getVpnForNetwork(dataBroker, nw);
                 if (network == null) {
                     failedNwList.add(String.format("network %s not found", nw.getValue()));
                 } else if (vpnId != null) {
                     failedNwList.add(String.format("network %s already associated to another VPN %s", nw.getValue(),
                             vpnId.getValue()));
                 } else {
-                    List<Uuid> networkSubnets = NeutronvpnUtils.getSubnetIdsFromNetworkId(broker, nw);
-                    logger.debug("Adding network subnets...{}", networkSubnets);
+                    List<Uuid> networkSubnets = NeutronvpnUtils.getSubnetIdsFromNetworkId(dataBroker, nw);
+                    LOG.debug("Adding network subnets...{}", networkSubnets);
                     if (networkSubnets != null) {
                         for (Uuid subnet : networkSubnets) {
                             // check if subnet added as router interface to some router
-                            Uuid subnetVpnId = NeutronvpnUtils.getVpnForSubnet(broker, subnet);
+                            Uuid subnetVpnId = NeutronvpnUtils.getVpnForSubnet(dataBroker, subnet);
                             if (subnetVpnId == null) {
                                 addSubnetToVpn(vpn, subnet);
                                 passedNwList.add(nw);
@@ -1172,8 +1544,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                             }
                         }
                     }
-                    if (network.getAugmentation(NetworkL3Extension.class) != null && network.getAugmentation
-                            (NetworkL3Extension.class).isExternal()) {
+                    if (NeutronvpnUtils.getIsExternal(network)) {
                         nvpnNatManager.addExternalNetworkToVpn(network, vpn);
                     }
                 }
@@ -1184,20 +1555,19 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
     }
 
     protected List<String> dissociateNetworksFromVpn(Uuid vpn, List<Uuid> networks) {
-
-        List<String> failedNwList = new ArrayList<String>();
+        List<String> failedNwList = new ArrayList<>();
         List<Uuid> passedNwList = new ArrayList<>();
         if (networks != null && !networks.isEmpty()) {
             // process corresponding subnets for VPN
             for (Uuid nw : networks) {
-                Network network = NeutronvpnUtils.getNeutronNetwork(broker, nw);
+                Network network = NeutronvpnUtils.getNeutronNetwork(dataBroker, nw);
                 if (network == null) {
                     failedNwList.add(String.format("network %s not found", nw.getValue()));
                 } else {
-                    Uuid vpnId = NeutronvpnUtils.getVpnForNetwork(broker, nw);
+                    Uuid vpnId = NeutronvpnUtils.getVpnForNetwork(dataBroker, nw);
                     if (vpn.equals(vpnId)) {
-                        List<Uuid> networkSubnets = NeutronvpnUtils.getSubnetIdsFromNetworkId(broker, nw);
-                        logger.debug("Removing network subnets...");
+                        List<Uuid> networkSubnets = NeutronvpnUtils.getSubnetIdsFromNetworkId(dataBroker, nw);
+                        LOG.debug("Removing network subnets...");
                         if (networkSubnets != null) {
                             for (Uuid subnet : networkSubnets) {
                                 removeSubnetFromVpn(vpn, subnet);
@@ -1213,7 +1583,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                                     "of the one given as input", nw.getValue(), vpnId.getValue()));
                         }
                     }
-                    if (network.getAugmentation(NetworkL3Extension.class).isExternal()) {
+                    if (NeutronvpnUtils.getIsExternal(network)) {
                         nvpnNatManager.removeExternalNetworkFromVpn(network);
                     }
                 }
@@ -1223,17 +1593,23 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         return failedNwList;
     }
 
+    /**
+     * It handles the invocations to the neutronvpn:associateNetworks RPC method
+     *
+     * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService#associateNetworks
+     * (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksInput)
+     */
     @Override
     public Future<RpcResult<AssociateNetworksOutput>> associateNetworks(AssociateNetworksInput input) {
 
         AssociateNetworksOutputBuilder opBuilder = new AssociateNetworksOutputBuilder();
         SettableFuture<RpcResult<AssociateNetworksOutput>> result = SettableFuture.create();
-        logger.debug("associateNetworks {}", input);
+        LOG.debug("associateNetworks {}", input);
         StringBuilder returnMsg = new StringBuilder();
         Uuid vpnId = input.getVpnId();
 
         try {
-            if (NeutronvpnUtils.getVpnMap(broker, vpnId) != null) {
+            if (NeutronvpnUtils.getVpnMap(dataBroker, vpnId) != null) {
                 List<Uuid> netIds = input.getNetworkId();
                 if (netIds != null && !netIds.isEmpty()) {
                     List<String> failed = associateNetworksToVpn(vpnId, netIds);
@@ -1245,41 +1621,47 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                 returnMsg.append("VPN not found : ").append(vpnId.getValue());
             }
             if (returnMsg.length() != 0) {
-                String message = String.format("associate Networks to vpn %s failed due to %s", vpnId.getValue(),
-                        returnMsg);
-                logger.error(message);
-                String errorResponse = String.format("ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: " +
+                String message = String.format("associate Networks to vpn %s failed due to %s",
+                        vpnId.getValue(), returnMsg);
+                LOG.error(message);
+                String errorResponse = String.format("ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: %s",
                         message);
                 opBuilder.setResponse(errorResponse);
-                result.set(RpcResultBuilder.<AssociateNetworksOutput>success().withResult(opBuilder.build()).build());
+                result.set(RpcResultBuilder.<AssociateNetworksOutput> success().withResult(opBuilder.build()).build());
             } else {
-                result.set(RpcResultBuilder.<AssociateNetworksOutput>success().build());
+                result.set(RpcResultBuilder.<AssociateNetworksOutput> success().build());
             }
         } catch (Exception ex) {
-            String message = String.format("associate Networks to vpn %s failed due to %s", input.getVpnId().getValue(),
-                    ex.getMessage());
-            logger.error(message, ex);
-            result.set(RpcResultBuilder.<AssociateNetworksOutput>failed().withError(ErrorType.APPLICATION, message)
+            String message = String.format("associate Networks to vpn %s failed due to %s",
+                    input.getVpnId().getValue(), ex.getMessage());
+            LOG.error(message, ex);
+            result.set(RpcResultBuilder.<AssociateNetworksOutput> failed().withError(ErrorType.APPLICATION, message)
                     .build());
         }
-        logger.debug("associateNetworks returns..");
+        LOG.debug("associateNetworks returns..");
         return result;
     }
 
+    /**
+     * It handles the invocations to the neutronvpn:associateRouter RPC method
+     *
+     * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService#associateRouter
+     * (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateRouterInput)
+     */
     @Override
     public Future<RpcResult<Void>> associateRouter(AssociateRouterInput input) {
 
         SettableFuture<RpcResult<Void>> result = SettableFuture.create();
-        logger.debug("associateRouter {}", input);
+        LOG.debug("associateRouter {}", input);
         StringBuilder returnMsg = new StringBuilder();
         Uuid vpnId = input.getVpnId();
         Uuid routerId = input.getRouterId();
         try {
-            if (routerId != null && vpnId != null) {
-                Router rtr = NeutronvpnUtils.getNeutronRouter(broker, routerId);
-                VpnMap vpnMap = NeutronvpnUtils.getVpnMap(broker, vpnId);
-                if (rtr != null && vpnMap != null) {
-                    Uuid extVpnId = NeutronvpnUtils.getVpnForRouter(broker, routerId, true);
+            VpnMap vpnMap = NeutronvpnUtils.getVpnMap(dataBroker, vpnId);
+            Router rtr = NeutronvpnUtils.getNeutronRouter(dataBroker, routerId);
+            if (vpnMap != null) {
+                if (rtr != null) {
+                    Uuid extVpnId = NeutronvpnUtils.getVpnForRouter(dataBroker, routerId, true);
                     if (vpnMap.getRouterId() != null) {
                         returnMsg.append("vpn ").append(vpnId.getValue()).append(" already associated to router ")
                                 .append(vpnMap.getRouterId().getValue());
@@ -1298,34 +1680,84 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             if (returnMsg.length() != 0) {
                 String message = String.format("associate router to vpn %s failed due to %s", routerId.getValue(),
                         returnMsg);
-                logger.error(message);
-                result.set(RpcResultBuilder.<Void>failed().withWarning(ErrorType.PROTOCOL, "invalid-value", message)
+                LOG.error(message);
+                result.set(RpcResultBuilder.<Void> failed().withWarning(ErrorType.PROTOCOL, "invalid-value", message)
                         .build());
             } else {
-                result.set(RpcResultBuilder.<Void>success().build());
+                result.set(RpcResultBuilder.<Void> success().build());
             }
         } catch (Exception ex) {
             String message = String.format("associate router %s to vpn %s failed due to %s", routerId.getValue(),
                     vpnId.getValue(), ex.getMessage());
-            logger.error(message, ex);
-            result.set(RpcResultBuilder.<Void>failed().withError(ErrorType.APPLICATION, message).build());
+            LOG.error(message, ex);
+            result.set(RpcResultBuilder.<Void> failed().withError(ErrorType.APPLICATION, message).build());
         }
-        logger.debug("associateRouter returns..");
+        LOG.debug("associateRouter returns..");
         return result;
     }
 
+    /** It handles the invocations to the neutronvpn:getFixedIPsForNeutronPort RPC method
+     *
+     * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService#getFixedIPsForNeutronPort
+     * (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortInput)
+     */
+    @Override
+    public Future<RpcResult<GetFixedIPsForNeutronPortOutput>> getFixedIPsForNeutronPort(GetFixedIPsForNeutronPortInput input) {
+        GetFixedIPsForNeutronPortOutputBuilder opBuilder = new GetFixedIPsForNeutronPortOutputBuilder();
+        SettableFuture<RpcResult<GetFixedIPsForNeutronPortOutput>> result = SettableFuture.create();
+        Uuid portId = input.getPortId();
+        StringBuilder returnMsg = new StringBuilder();
+        try {
+            List<String> fixedIPList = new ArrayList<>();
+            Port port = NeutronvpnUtils.getNeutronPort(dataBroker, portId);
+            if (port != null) {
+                List<FixedIps> fixedIPs = port.getFixedIps();
+                for (FixedIps ip : fixedIPs) {
+                    fixedIPList.add(String.valueOf(ip.getIpAddress().getValue()));
+                }
+            } else {
+                returnMsg.append("neutron port: ").append(portId.getValue()).append(" not found");
+            }
+            if (returnMsg.length() != 0) {
+                String message = String.format("Retrieval of FixedIPList for neutron port failed due to %s", returnMsg);
+                LOG.error(message);
+                result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput> failed()
+                        .withWarning(ErrorType.PROTOCOL, "invalid-value", message).build());
+            } else {
+                opBuilder.setFixedIPs(fixedIPList);
+                result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput> success().withResult(opBuilder.build())
+                        .build());
+                result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput> success().build());
+            }
+        } catch (Exception ex) {
+            String message = String.format("Retrieval of FixedIPList for neutron port %s failed due to %s",
+                    portId.getValue(), ex.getMessage());
+            LOG.error(message, ex);
+            result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput> failed()
+                    .withError(ErrorType.APPLICATION, message).build());
+        }
+        return result;
+    }
+
+    /**
+     * It handles the invocations to the neutronvpn:dissociateNetworks RPC method
+     *
+     * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn
+     * .rev150602.NeutronvpnService#dissociateNetworks(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt
+     * .neutronvpn.rev150602.DissociateNetworksInput)
+     */
     @Override
     public Future<RpcResult<DissociateNetworksOutput>> dissociateNetworks(DissociateNetworksInput input) {
 
         DissociateNetworksOutputBuilder opBuilder = new DissociateNetworksOutputBuilder();
         SettableFuture<RpcResult<DissociateNetworksOutput>> result = SettableFuture.create();
 
-        logger.debug("dissociateNetworks {}", input);
+        LOG.debug("dissociateNetworks {}", input);
         StringBuilder returnMsg = new StringBuilder();
         Uuid vpnId = input.getVpnId();
 
         try {
-            if (NeutronvpnUtils.getVpnMap(broker, vpnId) != null) {
+            if (NeutronvpnUtils.getVpnMap(dataBroker, vpnId) != null) {
                 List<Uuid> netIds = input.getNetworkId();
                 if (netIds != null && !netIds.isEmpty()) {
                     List<String> failed = dissociateNetworksFromVpn(vpnId, netIds);
@@ -1339,40 +1771,47 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             if (returnMsg.length() != 0) {
                 String message = String.format("dissociate Networks to vpn %s failed due to %s", vpnId.getValue(),
                         returnMsg);
-                logger.error(message);
-                String errorResponse = String.format("ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: " +
-                        message);
+                LOG.error(message);
+                String errorResponse = String.format("ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: "
+                        message);
                 opBuilder.setResponse(errorResponse);
-                result.set(RpcResultBuilder.<DissociateNetworksOutput>success().withResult(opBuilder.build()).build());
+                result.set(RpcResultBuilder.<DissociateNetworksOutput> success().withResult(opBuilder.build()).build());
             } else {
-                result.set(RpcResultBuilder.<DissociateNetworksOutput>success().build());
+                result.set(RpcResultBuilder.<DissociateNetworksOutput> success().build());
             }
         } catch (Exception ex) {
-            String message = String.format("dissociate Networks to vpn %s failed due to %s", input.getVpnId().
-                    getValue(), ex.getMessage());
-            logger.error(message, ex);
-            result.set(RpcResultBuilder.<DissociateNetworksOutput>failed().withError(ErrorType.APPLICATION, message)
+            String message = String.format("dissociate Networks to vpn %s failed due to %s",
+                    input.getVpnId().getValue(), ex.getMessage());
+            LOG.error(message, ex);
+            result.set(RpcResultBuilder.<DissociateNetworksOutput> failed().withError(ErrorType.APPLICATION, message)
                     .build());
         }
-        logger.debug("dissociateNetworks returns..");
+        LOG.debug("dissociateNetworks returns..");
         return result;
     }
 
+    /**
+     * It handles the invocations to the neutronvpn:dissociateRouter RPC method.
+     *
+     * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn
+     * .rev150602.NeutronvpnService#dissociateRouter(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn
+     * .rev150602.DissociateRouterInput)
+     */
     @Override
     public Future<RpcResult<Void>> dissociateRouter(DissociateRouterInput input) {
 
         SettableFuture<RpcResult<Void>> result = SettableFuture.create();
 
-        logger.debug("dissociateRouter {}", input);
+        LOG.debug("dissociateRouter {}", input);
         StringBuilder returnMsg = new StringBuilder();
         Uuid vpnId = input.getVpnId();
         Uuid routerId = input.getRouterId();
         try {
-            if (NeutronvpnUtils.getVpnMap(broker, vpnId) != null) {
+            if (NeutronvpnUtils.getVpnMap(dataBroker, vpnId) != null) {
                 if (routerId != null) {
-                    Router rtr = NeutronvpnUtils.getNeutronRouter(broker, routerId);
+                    Router rtr = NeutronvpnUtils.getNeutronRouter(dataBroker, routerId);
                     if (rtr != null) {
-                        Uuid routerVpnId = NeutronvpnUtils.getVpnForRouter(broker, routerId, true);
+                        Uuid routerVpnId = NeutronvpnUtils.getVpnForRouter(dataBroker, routerId, true);
                         if (vpnId.equals(routerVpnId)) {
                             dissociateRouterFromVpn(vpnId, routerId);
                         } else {
@@ -1395,67 +1834,28 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             if (returnMsg.length() != 0) {
                 String message = String.format("dissociate router %s to vpn %s failed due to %s", routerId.getValue(),
                         vpnId.getValue(), returnMsg);
-                logger.error(message);
-                String errorResponse = String.format("ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: " +
-                        message);
-                result.set(RpcResultBuilder.<Void>failed().withWarning(ErrorType.PROTOCOL, "invalid-value", message)
+                LOG.error(message);
+                String errorResponse = String.format("ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: "
+                        message);
+                result.set(RpcResultBuilder.<Void> failed().withWarning(ErrorType.PROTOCOL, "invalid-value", message)
                         .build());
             } else {
-                result.set(RpcResultBuilder.<Void>success().build());
+                result.set(RpcResultBuilder.<Void> success().build());
             }
         } catch (Exception ex) {
             String message = String.format("disssociate router %s to vpn %s failed due to %s", routerId.getValue(),
                     vpnId.getValue(), ex.getMessage());
-            logger.error(message, ex);
-            result.set(RpcResultBuilder.<Void>failed().withError(ErrorType.APPLICATION, message).build());
+            LOG.error(message, ex);
+            result.set(RpcResultBuilder.<Void> failed().withError(ErrorType.APPLICATION, message).build());
         }
-        logger.debug("dissociateRouter returns..");
+        LOG.debug("dissociateRouter returns..");
 
         return result;
     }
 
-    @Override
-    public Future<RpcResult<GetFixedIPsForNeutronPortOutput>> getFixedIPsForNeutronPort(GetFixedIPsForNeutronPortInput
-                                                                                                input) {
-        GetFixedIPsForNeutronPortOutputBuilder opBuilder = new GetFixedIPsForNeutronPortOutputBuilder();
-        SettableFuture<RpcResult<GetFixedIPsForNeutronPortOutput>> result = SettableFuture.create();
-        Uuid portId = input.getPortId();
-        StringBuilder returnMsg = new StringBuilder();
-        try {
-            List<String> fixedIPList = new ArrayList<>();
-            Port port = NeutronvpnUtils.getNeutronPort(broker, portId);
-            if (port != null) {
-                List<FixedIps> fixedIPs = port.getFixedIps();
-                for (FixedIps ip : fixedIPs) {
-                    fixedIPList.add(ip.getIpAddress().getIpv4Address().getValue());
-                }
-            } else {
-                returnMsg.append("neutron port: ").append(portId.getValue()).append(" not found");
-            }
-            if (returnMsg.length() != 0) {
-                String message = String.format("Retrieval of FixedIPList for neutron port failed due to %s", returnMsg);
-                logger.error(message);
-                result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>failed().withWarning(ErrorType.PROTOCOL,
-                        "invalid-value", message).build());
-            } else {
-                opBuilder.setFixedIPs(fixedIPList);
-                result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>success().withResult(opBuilder.build())
-                        .build());
-                result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>success().build());
-            }
-        } catch (Exception ex) {
-            String message = String.format("Retrieval of FixedIPList for neutron port %s failed due to %s", portId
-                    .getValue(), ex.getMessage());
-            logger.error(message, ex);
-            result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>failed().withError(ErrorType.APPLICATION,
-                    message).build());
-        }
-        return result;
-    }
-
     protected void handleNeutronRouterDeleted(Uuid routerId, List<Uuid> routerSubnetIds) {
         // check if the router is associated to some VPN
-        Uuid vpnId = NeutronvpnUtils.getVpnForRouter(broker, routerId, true);
+        Uuid vpnId = NeutronvpnUtils.getVpnForRouter(dataBroker, routerId, true);
         if (vpnId != null) {
             // remove existing external vpn interfaces
             for (Uuid subnetId : routerSubnetIds) {
@@ -1476,30 +1876,35 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
     }
 
     protected Subnet getNeutronSubnet(Uuid subnetId){
-        return NeutronvpnUtils.getNeutronSubnet(broker, subnetId);
+        return NeutronvpnUtils.getNeutronSubnet(dataBroker, subnetId);
     }
 
     protected IpAddress getNeutronSubnetGateway(Uuid subnetId) {
-        Subnet sn = NeutronvpnUtils.getNeutronSubnet(broker, subnetId);
+        Subnet sn = NeutronvpnUtils.getNeutronSubnet(dataBroker, subnetId);
         if (null != sn) {
             return sn.getGatewayIp();
         }
         return null;
     }
 
+
+    protected Network getNeutronNetwork(Uuid networkId) {
+        return NeutronvpnUtils.getNeutronNetwork(dataBroker, networkId);
+    }
+
     protected Port getNeutronPort(String name) {
-        return NeutronvpnUtils.getNeutronPort(broker, new Uuid(name));
+        return NeutronvpnUtils.getNeutronPort(dataBroker, new Uuid(name));
     }
 
     protected Port getNeutronPort(Uuid portId) {
-        return NeutronvpnUtils.getNeutronPort(broker, portId);
+        return NeutronvpnUtils.getNeutronPort(dataBroker, portId);
     }
 
     protected List<Uuid> getSubnetsforVpn(Uuid vpnid) {
         List<Uuid> subnets = new ArrayList<>();
-        //read subnetmaps
+        // read subnetmaps
         InstanceIdentifier<Subnetmaps> subnetmapsid = InstanceIdentifier.builder(Subnetmaps.class).build();
-        Optional<Subnetmaps> subnetmaps = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
+        Optional<Subnetmaps> subnetmaps = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                 subnetmapsid);
         if (subnetmaps.isPresent() && subnetmaps.get().getSubnetmap() != null) {
             List<Subnetmap> subnetMapList = subnetmaps.get().getSubnetmap();
@@ -1512,30 +1917,61 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         return subnets;
     }
 
+    /**
+     * Implementation of the "vpnservice:neutron-ports-show" Karaf CLI command
+     *
+     * @return a List of String to be printed on screen
+     */
     public List<String> showNeutronPortsCLI() {
         List<String> result = new ArrayList<>();
-        result.add(String.format(" %-34s  %-22s  %-22s  %-6s ", "PortName", "Mac Address", "IP Address",
-                "Prefix Length"));
-        result.add("---------------------------------------------------------------------------------------");
+        result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", "Port ID", "Mac Address", "Prefix Length", "IP " +
+                "Address"));
+        result.add("-------------------------------------------------------------------------------------------");
         InstanceIdentifier<Ports> portidentifier = InstanceIdentifier.create(Neutron.class).child(Ports.class);
         try {
-            Optional<Ports> ports = NeutronvpnUtils.read(broker, LogicalDatastoreType.CONFIGURATION, portidentifier);
+            Optional<Ports> ports = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, portidentifier);
             if (ports.isPresent() && ports.get().getPort() != null) {
                 for (Port port : ports.get().getPort()) {
-                    if (port.getFixedIps() != null && !port.getFixedIps().isEmpty()) {
-                        result.add(String.format(" %-34s  %-22s  %-22s  %-6s ", port.getUuid().getValue(), port.
-                                getMacAddress(), port.getFixedIps().get(0).getIpAddress().getIpv4Address().getValue(),
-                                NeutronvpnUtils.getIPPrefixFromPort(broker, port)));
+                    List<FixedIps> fixedIPs = port.getFixedIps();
+                    try {
+                        if (fixedIPs != null && !fixedIPs.isEmpty()) {
+                            List<String> ipList = new ArrayList<>();
+                            for (FixedIps fixedIp : fixedIPs) {
+                                IpAddress ipAddress = fixedIp.getIpAddress();
+                                if (ipAddress.getIpv4Address() != null) {
+                                    ipList.add(ipAddress.getIpv4Address().getValue());
+                                } else {
+                                    ipList.add((ipAddress.getIpv6Address().getValue()));
+                                }
+                            }
+                            result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", port.getUuid().getValue(), port
+                                    .getMacAddress().getValue(), NeutronvpnUtils.getIPPrefixFromPort(dataBroker, port),
+                                    ipList.toString()));
+                        } else {
+                            result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", port.getUuid().getValue(), port
+                                    .getMacAddress().getValue(), "Not Assigned", "Not " + "Assigned"));
+                        }
+                    } catch (Exception e) {
+                        LOG.error("Failed to retrieve neutronPorts info for port {}: ", port.getUuid().getValue(),
+                                e);
+                        System.out.println("Failed to retrieve neutronPorts info for port: " + port.getUuid()
+                                .getValue() + ": " + e.getMessage());
                     }
                 }
             }
         } catch (Exception e) {
-            logger.error("Failed to retrieve neutronPorts info : ", e);
+            LOG.error("Failed to retrieve neutronPorts info : ", e);
             System.out.println("Failed to retrieve neutronPorts info : " + e.getMessage());
         }
         return result;
     }
 
+    /**
+     * Implementation of the "vpnservice:l3vpn-config-show" karaf CLI command
+     *
+     * @param vpnuuid Uuid of the VPN whose config must be shown
+     * @return formatted output list
+     */
     public List<String> showVpnConfigCLI(Uuid vpnuuid) {
         List<String> result = new ArrayList<>();
         if (vpnuuid == null) {
@@ -1560,10 +1996,10 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                 result.add("");
                 List<L3vpnInstances> VpnList = rpcResult.getResult().getL3vpnInstances();
                 for (L3vpnInstance Vpn : VpnList) {
-                    String tenantId = Vpn.getTenantId() != null ? Vpn.getTenantId().getValue() : "\"                 " +
-                            "                  \"";
-                    result.add(String.format(" %-37s %-37s %-7s ", Vpn.getId().getValue(), tenantId, Vpn
-                            .getRouteDistinguisher()));
+                    String tenantId = Vpn.getTenantId() != null ? Vpn.getTenantId().getValue()
+                            : "\"                 " + "                  \"";
+                    result.add(String.format(" %-37s %-37s %-7s ", Vpn.getId().getValue(), tenantId,
+                            Vpn.getRouteDistinguisher()));
                     result.add("");
                     result.add(String.format(" %-80s ", Vpn.getImportRT()));
                     result.add("");
@@ -1597,68 +2033,102 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                 }
             }
         } catch (InterruptedException | ExecutionException e) {
-            logger.error("error getting VPN info : ", e);
+            LOG.error("error getting VPN info : ", e);
             System.out.println("error getting VPN info : " + e.getMessage());
         }
         return result;
     }
 
-    private String getshowVpnConfigCLIHelp() {
-        StringBuilder help = new StringBuilder("Usage:");
-        help.append("display vpn-config [-vid/--vpnid <id>]");
-        return help.toString();
-    }
-
-    private void checkAndPublishSubnetAddNotification(Uuid subnetId, String subnetIp, String vpnName,
-                                                      Boolean isExternalvpn, Long elanTag)throws InterruptedException {
-        SubnetAddedToVpnBuilder builder = new SubnetAddedToVpnBuilder();
-
-        logger.info("publish notification called");
+    protected void createExternalVpnInterfaces(Uuid extNetId) {
+        if (extNetId == null) {
+            LOG.trace("external network is null");
+            return;
+        }
 
-        builder.setSubnetId(subnetId);
-        builder.setSubnetIp(subnetIp);
-        builder.setVpnName(vpnName);
-        builder.setExternalVpn(isExternalvpn);
-        builder.setElanTag(elanTag);
+        Collection<String> extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue());
+        if (extElanInterfaces == null || extElanInterfaces.isEmpty()) {
+            LOG.trace("No external ports attached to external network {}", extNetId.getValue());
+            return;
+        }
 
-        notificationPublishService.putNotification(builder.build());
+        WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
+        for (String elanInterface : extElanInterfaces) {
+            createExternalVpnInterface(extNetId, elanInterface, wrtConfigTxn);
+        }
+        wrtConfigTxn.submit();
     }
 
-    private void checkAndPublishSubnetDelNotification(Uuid subnetId, String subnetIp, String vpnName,
-                                                      Boolean isExternalvpn, Long elanTag) throws InterruptedException {
-        SubnetDeletedFromVpnBuilder builder = new SubnetDeletedFromVpnBuilder();
+    protected void removeExternalVpnInterfaces(Uuid extNetId) {
+        Collection<String> extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue());
+        if (extElanInterfaces == null || extElanInterfaces.isEmpty()) {
+            LOG.trace("No external ports attached for external network {}", extNetId);
+            return;
+        }
+        try {
 
-        logger.info("publish notification called");
+            WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
+            for (String elanInterface : extElanInterfaces) {
+                InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils
+                        .buildVpnInterfaceIdentifier(elanInterface);
+                LOG.info("Removing vpn interface {}", elanInterface);
+                wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier);
+            }
+            wrtConfigTxn.submit();
 
-        builder.setSubnetId(subnetId);
-        builder.setSubnetIp(subnetIp);
-        builder.setVpnName(vpnName);
-        builder.setExternalVpn(isExternalvpn);
-        builder.setElanTag(elanTag);
+        } catch (Exception ex) {
+            LOG.error("Removal of vpninterfaces {} failed due to {}", extElanInterfaces, ex);
+        }
+    }
 
-        notificationPublishService.putNotification(builder.build());
+    private void createExternalVpnInterface(Uuid vpnId, String infName, WriteTransaction wrtConfigTxn) {
+        writeVpnInterfaceToDs(vpnId, infName, null, false /* not a router iface */, wrtConfigTxn);
     }
 
-    private void checkAndPublishSubnetUpdNotification(Uuid subnetId, String subnetIp, String vpnName,
-                                                      Boolean isExternalvpn, Long elanTag) throws InterruptedException {
-        SubnetUpdatedInVpnBuilder builder = new SubnetUpdatedInVpnBuilder();
+    private void writeVpnInterfaceToDs(Uuid vpnId, String infName, Adjacencies adjacencies,
+            Boolean isRouterInterface, WriteTransaction wrtConfigTxn) {
+        if (vpnId == null || infName == null) {
+            LOG.debug("vpn id or interface is null");
+            return;
+        }
 
-        logger.info("publish notification called");
+        Boolean wrtConfigTxnPresent = true;
+        if (wrtConfigTxn == null) {
+            wrtConfigTxnPresent = false;
+            wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
+        }
 
-        builder.setSubnetId(subnetId);
-        builder.setSubnetIp(subnetIp);
-        builder.setVpnName(vpnName);
-        builder.setExternalVpn(isExternalvpn);
-        builder.setElanTag(elanTag);
+        InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
+        VpnInterfaceBuilder vpnb = new VpnInterfaceBuilder().setKey(new VpnInterfaceKey(infName))
+                .setName(infName)
+                .setVpnInstanceName(vpnId.getValue())
+                .setIsRouterInterface(isRouterInterface);
+        if (adjacencies != null) {
+            vpnb.addAugmentation(Adjacencies.class, adjacencies);
+        }
+        VpnInterface vpnIf = vpnb.build();
+        try {
+            LOG.info("Creating vpn interface {}", vpnIf);
+            wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier, vpnIf);
+        } catch (Exception ex) {
+            LOG.error("Creation of vpninterface {} failed due to {}", infName, ex);
+        }
+
+        if (!wrtConfigTxnPresent) {
+            wrtConfigTxn.submit();
+        }
+    }
 
-        notificationPublishService.putNotification(builder.build());
+    private String getshowVpnConfigCLIHelp() {
+        StringBuilder help = new StringBuilder("Usage:");
+        help.append("display vpn-config [-vid/--vpnid <id>]");
+        return help.toString();
     }
 
     private void checkAndPublishRouterAssociatedtoVpnNotification(Uuid routerId, Uuid vpnId) throws
             InterruptedException {
         RouterAssociatedToVpn routerAssociatedToVpn = new RouterAssociatedToVpnBuilder().setRouterId(routerId)
                 .setVpnId(vpnId).build();
-        logger.info("publishing notification upon association of router to VPN");
+        LOG.info("publishing notification upon association of router to VPN");
         notificationPublishService.putNotification(routerAssociatedToVpn);
     }
 
@@ -1666,7 +2136,11 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             InterruptedException {
         RouterDisassociatedFromVpn routerDisassociatedFromVpn = new RouterDisassociatedFromVpnBuilder().setRouterId
                 (routerId).setVpnId(vpnId).build();
-        logger.info("publishing notification upon disassociation of router from VPN");
+        LOG.info("publishing notification upon disassociation of router from VPN");
         notificationPublishService.putNotification(routerDisassociatedFromVpn);
     }
+
+    protected void dissociatefixedIPFromFloatingIP(String fixedNeutronPortName) {
+        floatingIpMapListener.dissociatefixedIPFromFloatingIP(fixedNeutronPortName);
+    }
 }