All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.netvirt.neutronvpn.NeutronvpnManager Maven / Gradle / Ivy

There is a newer version: 0.11.4
Show newest version
/*
 * Copyright (c) 2015, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.netvirt.neutronvpn;

import 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.Objects;
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.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.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;
import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.VpnTargetsBuilder;
import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget;
import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTargetBuilder;
import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTargetKey;
import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceBuilder;
import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.vpn.instance.Ipv4FamilyBuilder;
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.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.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.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
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;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateRouterInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateNetworksInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateNetworksOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateNetworksOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateRouterInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.L3vpnInstance;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.RouterAssociatedToVpn;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.RouterAssociatedToVpnBuilder;
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.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.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;
import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
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.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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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 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 IElanService elanService;
    Boolean isExternalVpn;

    /**
     * @param dataBroker DataBroker reference
     * @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 dataBroker, final NotificationPublishService notiPublishService,
            final NeutronvpnNatManager vpnNatMgr, final VpnRpcService vpnRpcSrv, final IElanService elanService,
            final NeutronFloatingToFixedIpMappingChangeListener neutronFloatingToFixedIpMappingChangeListener,
            final NeutronvpnConfig neutronvpnConfig) {
        this.dataBroker = dataBroker;
        nvpnNatManager = vpnNatMgr;
        notificationPublishService = notiPublishService;
        vpnRpcService = vpnRpcSrv;
        this.elanService = elanService;
        floatingIpMapListener = neutronFloatingToFixedIpMappingChangeListener;
        LOG.info("neutronvpnConfig: {}", neutronvpnConfig);
        this.neutronvpnConfig = neutronvpnConfig;
    }

    @Override
    public void close() throws Exception {
        LOG.info("{} close", getClass().getSimpleName());
    }

    public NeutronvpnConfig getNeutronvpnConfig() {
        return neutronvpnConfig;
    }

    // TODO Clean up the exception handling
    @SuppressWarnings("checkstyle:IllegalCatch")
    protected void createSubnetmapNode(Uuid subnetId, String subnetIp, Uuid tenantId, Uuid networkId) {
        try {
            InstanceIdentifier subnetMapIdentifier = NeutronvpnUtils.buildSubnetMapIdentifier(subnetId);
            synchronized (subnetId.getValue().intern()) {
                Optional sn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                        subnetMapIdentifier);
                SubnetmapBuilder subnetmapBuilder = null;
                if (sn.isPresent()) {
                    LOG.error("subnetmap node for subnet ID {} already exists, returning", subnetId.getValue());
                    return;
                } else {
                    subnetmapBuilder = new SubnetmapBuilder().setKey(new SubnetmapKey(subnetId)).setId(subnetId)
                            .setSubnetIp(subnetIp).setTenantId(tenantId).setNetworkId(networkId);
                    LOG.debug("Adding a new subnet node in Subnetmaps DS for subnet {}", subnetId.getValue());
                }
                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
                        subnetMapIdentifier, subnetmapBuilder.build());
            }
        } catch (Exception e) {
            LOG.error("Creating subnetmap node failed for subnet {}", subnetId.getValue());
        }
    }

    // TODO Clean up the exception handling
    @SuppressWarnings("checkstyle:IllegalCatch")
    private Subnetmap updateSubnetNode(Uuid subnetId, Uuid routerId, Uuid vpnId) {
        Subnetmap subnetmap = null;
        SubnetmapBuilder builder = null;
        InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class)
                .child(Subnetmap.class, new SubnetmapKey(subnetId))
                .build();
        try {
            synchronized (subnetId.getValue().intern()) {
                Optional 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 {
                    LOG.error("subnetmap node for subnet {} does not exist, returning", subnetId.getValue());
                    return null;
                }
                if (routerId != null) {
                    builder.setRouterId(routerId);
                }
                if (vpnId != null) {
                    builder.setVpnId(vpnId);
                }

                subnetmap = builder.build();
                LOG.debug("Creating/Updating subnetMap node: {} ", subnetId.getValue());
                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
            }
        } catch (Exception e) {
            LOG.error("Updation of subnetMap failed for node: {}", subnetId.getValue());
        }
        return subnetmap;
    }

    // TODO Clean up the exception handling
    @SuppressWarnings("checkstyle:IllegalCatch")
    protected void updateSubnetNodeWithFixedIp(Uuid subnetId, Uuid routerId,
                                               Uuid routerInterfacePortId, String fixedIp,
                                               String routerIntfMacAddress) {
        Subnetmap subnetmap = null;
        SubnetmapBuilder builder = null;
        InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class).
                child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
        try {
            synchronized (subnetId.getValue().intern()) {
                Optional sn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
                if (sn.isPresent()) {
                    builder = new SubnetmapBuilder(sn.get());
                    LOG.debug("WithRouterFixedIP: Updating existing subnetmap node for subnet ID {}", subnetId.getValue());
                } else {
                    LOG.error("WithRouterFixedIP: subnetmap node for subnet {} does not exist, returning", subnetId.getValue());
                    return;
                }
                builder.setRouterId(routerId);
                builder.setRouterInterfacePortId(routerInterfacePortId);
                builder.setRouterIntfMacAddress(routerIntfMacAddress);
                builder.setRouterInterfaceFixedIp(fixedIp);
                subnetmap = builder.build();
                LOG.debug("WithRouterFixedIP Creating/Updating subnetMap node for Router FixedIp: {} ", subnetId.getValue());
                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
            }
        } catch (Exception e) {
            LOG.error("WithRouterFixedIP: Updation of subnetMap for Router FixedIp failed for node: {}", subnetId.getValue());
        }
    }

    protected Subnetmap updateSubnetmapNodeWithPorts(Uuid subnetId, Uuid portId, Uuid directPortId) {
        Subnetmap subnetmap = null;
        InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
                new SubnetmapKey(subnetId)).build();
        try {
            synchronized (subnetId.getValue().intern()) {
                Optional sn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
                if (sn.isPresent()) {
                    SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
                    if (null != portId) {
                        List portList = builder.getPortList();
                        if (null == portList) {
                            portList = new ArrayList();
                        }
                        portList.add(portId);
                        builder.setPortList(portList);
                        LOG.debug("Updating existing subnetmap node {} with port {}", subnetId.getValue(),
                                portId.getValue());
                    }
                    if (null != directPortId) {
                        List directPortList = builder.getDirectPortList();
                        if (null == directPortList) {
                            directPortList = new ArrayList();
                        }
                        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;
    }

    // TODO Clean up the exception handling
    @SuppressWarnings("checkstyle:IllegalCatch")
    protected Subnetmap removeFromSubnetNode(Uuid subnetId, Uuid networkId, Uuid routerId, Uuid vpnId, Uuid portId) {
        Subnetmap subnetmap = null;
        InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class)
                .child(Subnetmap.class, new SubnetmapKey(subnetId))
                .build();
        try {
            synchronized (subnetId.getValue().intern()) {
                Optional 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 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;
    }

    // TODO Clean up the exception handling
    @SuppressWarnings("checkstyle:IllegalCatch")
    protected Subnetmap removePortsFromSubnetmapNode(Uuid subnetId, Uuid portId, Uuid directPortId) {
        Subnetmap subnetmap = null;
        InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
                new SubnetmapKey(subnetId)).build();
        try {
            synchronized (subnetId.getValue().intern()) {
                Optional sn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
                if (sn.isPresent()) {
                    SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
                    if (null != portId && null != builder.getPortList()) {
                        List 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 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) {
            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 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());
        }
    }

    private void updateVpnInstanceNode(String vpnName, List rd, List irt, List ert) {

        VpnInstanceBuilder builder = null;
        List vpnTargetList = new ArrayList<>();
        boolean isLockAcquired = false;
        InstanceIdentifier vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class).child
                (VpnInstance.class, new VpnInstanceKey(vpnName)).build();
        try {
            Optional optionalVpn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                    vpnIdentifier);
            LOG.debug("Creating/Updating a new vpn-instance node: {} ", vpnName);
            if (optionalVpn.isPresent()) {
                builder = new VpnInstanceBuilder(optionalVpn.get());
                LOG.debug("updating existing vpninstance node");
            } else {
                builder = new VpnInstanceBuilder().setKey(new VpnInstanceKey(vpnName)).setVpnInstanceName(vpnName);
            }
            if (irt != null && !irt.isEmpty()) {
                if (ert != null && !ert.isEmpty()) {
                    List commonRT = new ArrayList<>(irt);
                    commonRT.retainAll(ert);

                    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();
                        vpnTargetList.add(vpnTarget);
                    }
                }
                for (String importRT : irt) {
                    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();
                    vpnTargetList.add(vpnTarget);
                }
            }

            VpnTargets vpnTargets = new VpnTargetsBuilder().setVpnTarget(vpnTargetList).build();

            Ipv4FamilyBuilder ipv4vpnBuilder = new Ipv4FamilyBuilder().setVpnTargets(vpnTargets);

            if (rd != null && !rd.isEmpty()) {
                ipv4vpnBuilder.setRouteDistinguisher(rd.get(0));
            }

            VpnInstance newVpn = builder.setIpv4Family(ipv4vpnBuilder.build()).build();
            isLockAcquired = NeutronvpnUtils.lock(vpnName);
            LOG.debug("Creating/Updating vpn-instance for {} ", vpnName);
            MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier, newVpn);
        } catch (Exception e) {
            LOG.error("Update VPN Instance node failed for node: {} {} {} {}", vpnName, rd, irt, ert);
        } finally {
            if (isLockAcquired) {
                NeutronvpnUtils.unlock(vpnName);
            }
        }
    }

    private void deleteVpnMapsNode(Uuid vpnid) {
        boolean isLockAcquired = false;
        InstanceIdentifier vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
                .child(VpnMap.class, new VpnMapKey(vpnid))
                .build();
        LOG.debug("removing vpnMaps node: {} ", vpnid.getValue());
        try {
            isLockAcquired = NeutronvpnUtils.lock(vpnid.getValue());
            MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
        } catch (Exception e) {
            LOG.error("Delete vpnMaps node failed for vpn : {} ", vpnid.getValue());
        } finally {
            if (isLockAcquired) {
                NeutronvpnUtils.unlock(vpnid.getValue());
            }
        }
    }

    private void updateVpnMaps(Uuid vpnId, String name, Uuid router, Uuid tenantId, List networks) {
        VpnMapBuilder builder;
        boolean isLockAcquired = false;
        InstanceIdentifier vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
                .child(VpnMap.class, new VpnMapKey(vpnId))
                .build();
        try {
            Optional optionalVpnMap = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                    vpnMapIdentifier);
            if (optionalVpnMap.isPresent()) {
                builder = new VpnMapBuilder(optionalVpnMap.get());
            } else {
                builder = new VpnMapBuilder().setKey(new VpnMapKey(vpnId)).setVpnId(vpnId);
            }

            if (name != null) {
                builder.setName(name);
            }
            if (tenantId != null) {
                builder.setTenantId(tenantId);
            }
            if (router != null) {
                builder.setRouterId(router);
            }
            if (networks != null) {
                List nwList = builder.getNetworkIds();
                if (nwList == null) {
                    nwList = new ArrayList<>();
                }
                nwList.addAll(networks);
                builder.setNetworkIds(nwList);
            }

            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) {
            LOG.error("UpdateVpnMaps failed for node: {} ", vpnId.getValue());
        } finally {
            if (isLockAcquired) {
                NeutronvpnUtils.unlock(vpnId.getValue());
            }
        }
    }

    private void clearFromVpnMaps(Uuid vpnId, Uuid routerId, List networkIds) {
        boolean isLockAcquired = false;
        InstanceIdentifier vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
                .child(VpnMap.class, new VpnMapKey(vpnId))
                .build();
        Optional optionalVpnMap = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                vpnMapIdentifier);
        if (optionalVpnMap.isPresent()) {
            VpnMap vpnMap = optionalVpnMap.get();
            VpnMapBuilder vpnMapBuilder = new VpnMapBuilder(vpnMap);
            if (routerId != null) {
                if (vpnMap.getNetworkIds() == null && routerId.equals(vpnMap.getVpnId())) {
                    try {
                        // remove entire node in case of internal VPN
                        isLockAcquired = NeutronvpnUtils.lock(vpnId.getValue());
                        LOG.debug("removing vpnMaps node: {} ", vpnId);
                        MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
                    } catch (Exception e) {
                        LOG.error("Deletion of vpnMaps node failed for vpn {}", vpnId.getValue());
                    } finally {
                        if (isLockAcquired) {
                            NeutronvpnUtils.unlock(vpnId.getValue());
                        }
                    }
                    return;
                }
                vpnMapBuilder.setRouterId(null);
            }
            if (networkIds != null) {
                List vpnNw = vpnMap.getNetworkIds();
                for (Uuid nw : networkIds) {
                    vpnNw.remove(nw);
                }
                if (vpnNw.isEmpty()) {
                    LOG.debug("setting networks null in vpnMaps node: {} ", vpnId.getValue());
                    vpnMapBuilder.setNetworkIds(null);
                } else {
                    vpnMapBuilder.setNetworkIds(vpnNw);
                }
            }

            try {
                isLockAcquired = NeutronvpnUtils.lock(vpnId.getValue());
                LOG.debug("clearing from vpnMaps node: {} ", vpnId.getValue());
                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier,
                        vpnMapBuilder.build());
            } catch (Exception e) {
                LOG.error("Clearing from vpnMaps node failed for vpn {}", vpnId.getValue());
            } finally {
                if (isLockAcquired) {
                    NeutronvpnUtils.unlock(vpnId.getValue());
                }
            }
        } else {
            LOG.error("VPN : {} not found", vpnId.getValue());
        }
        LOG.debug("Clear from VPNMaps DS successful for VPN {} ", vpnId.getValue());
    }

    private void deleteVpnInstance(Uuid vpnId) {
        boolean isLockAcquired = false;
        InstanceIdentifier vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
                .child(VpnInstance.class,
                        new VpnInstanceKey(vpnId.getValue()))
                .build();
        try {
            isLockAcquired = NeutronvpnUtils.lock(vpnId.getValue());
            LOG.debug("Deleting vpnInstance {}", vpnId.getValue());
            MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier);
        } catch (Exception e) {
            LOG.error("Deletion of VPNInstance node failed for VPN {}", vpnId.getValue());
        } finally {
            if (isLockAcquired) {
                NeutronvpnUtils.unlock(vpnId.getValue());
            }
        }
    }

    protected void createVpnInterface(Uuid vpnId, Uuid routerId, Port port,
                                      WriteTransaction wrtConfigTxn) {
        String infName = port.getUuid().getValue();
        List adjList = new ArrayList<>();
        List 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(dataBroker, routerId);
        }
        // create adjacency list
        for (FixedIps ip : ips) {
            // create vm adjacency
            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 routeList = rtr.getRoutes();
                List erAdjList = getAdjacencyforExtraRoute(vpnId, routeList, ipValue);
                if (erAdjList != null && !erAdjList.isEmpty()) {
                    adjList.addAll(erAdjList);
                }
            }
            NeutronvpnUtils.createVpnPortFixedIpToPort(dataBroker, vpnId.getValue(), ipValue, infName, port
                            .getMacAddress().getValue(), isRouterInterface, wrtConfigTxn);
        }
        // create vpn-interface on this neutron port
        Adjacencies adjs = new AdjacenciesBuilder().setAdjacency(adjList).build();
        writeVpnInterfaceToDs(vpnId, infName, adjs, isRouterInterface, wrtConfigTxn);
        if (routerId != null) {
            addToNeutronRouterInterfacesMap(routerId, 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 vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
        try {
            LOG.debug("Deleting vpn interface {}", infName);
            wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier);

            List ips = port.getFixedIps();
            for (FixedIps ip : ips) {
                String ipValue = String.valueOf(ip.getIpAddress().getValue());
                NeutronvpnUtils.removeVpnPortFixedIpToPort(dataBroker, vpnId.getValue(),
                        ipValue, wrtConfigTxn);
            }
        } 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, Uuid oldVpnId, Port port, boolean  isBeingAssociated,
                                      boolean isSubnetIp, WriteTransaction writeConfigTxn) {
        if (vpnId == null || port == null) {
            return;
        }
        boolean isLockAcquired = false;
        String infName = port.getUuid().getValue();
        InstanceIdentifier vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);

        try {
            isLockAcquired = NeutronvpnUtils.lock(infName);
            Optional optionalVpnInterface = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType
                    .CONFIGURATION, vpnIfIdentifier);
            if (optionalVpnInterface.isPresent()) {
                VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get())
                        .setVpnInstanceName(vpnId.getValue());
                LOG.debug("Updating vpn interface {}", infName);
                if (!isBeingAssociated) {
                    Adjacencies adjs = vpnIfBuilder.getAugmentation(Adjacencies.class);
                    List adjacencyList = (adjs != null) ? adjs.getAdjacency() : new ArrayList();
                    Iterator adjacencyIter = adjacencyList.iterator();
                    while (adjacencyIter.hasNext()) {
                        Adjacency adjacency = adjacencyIter.next();
                        String mipToQuery = adjacency.getIpAddress().split("/")[0];
                        InstanceIdentifier id = NeutronvpnUtils.buildLearntVpnVipToPortIdentifier
                                (oldVpnId.getValue(), mipToQuery);
                        Optional optionalVpnVipToPort = NeutronvpnUtils.read(dataBroker,
                                LogicalDatastoreType
                                .OPERATIONAL, id);
                        if (optionalVpnVipToPort.isPresent()) {
                            LOG.trace("Removing adjacencies from vpninterface {} upon dissociation of router {} " +
                                    "from VPN " + "{}", infName, vpnId, oldVpnId);
                            adjacencyIter.remove();
                            NeutronvpnUtils.removeLearntVpnVipToPort(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 ips = port.getFixedIps();
                for (FixedIps ip : ips) {
                    String ipValue = String.valueOf(ip.getIpAddress().getValue());
                    if (oldVpnId != null) {
                        NeutronvpnUtils.removeVpnPortFixedIpToPort(dataBroker, oldVpnId.getValue(),
                                ipValue, writeConfigTxn);
                    }
                    NeutronvpnUtils.createVpnPortFixedIpToPort(dataBroker, vpnId.getValue(), ipValue, infName, port
                            .getMacAddress().getValue(), isSubnetIp, writeConfigTxn);
                }
                writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier, vpnIfBuilder
                        .build());
            } else {
                LOG.error("VPN Interface {} not found", infName);
            }
        } catch (Exception ex) {
            LOG.error("Updation of vpninterface {} failed due to {}", infName, ex);
        } finally {
            if (isLockAcquired) {
                NeutronvpnUtils.unlock(infName);
            }
        }
    }

    public void createL3InternalVpn(Uuid vpn, String name, Uuid tenant, List rd, List irt,
                                    List ert, Uuid router, List networks) {

        // Update VPN Instance node
        updateVpnInstanceNode(vpn.getValue(), rd, irt, ert);

        // 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 rd, List irt, List ert,
                            Uuid router, List 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) {
            List 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> createL3VPN(CreateL3VPNInput input) {

        CreateL3VPNOutputBuilder opBuilder = new CreateL3VPNOutputBuilder();
        SettableFuture> result = SettableFuture.create();
        List errorList = new ArrayList<>();
        int failurecount = 0;
        int warningcount = 0;

        List vpns = input.getL3vpn();
        for (L3vpn vpn : vpns) {
            RpcError error = null;
            String msg;
            if (NeutronvpnUtils.doesVpnExist(dataBroker, vpn.getId())) {
                msg = String.format("Creation of L3VPN failed for VPN %s due to VPN with the same ID already present",
                                vpn.getId().getValue());
                LOG.warn(msg);
                error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                errorList.add(error);
                warningcount++;
                continue;
            }
            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());
                LOG.warn(msg);
                error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                errorList.add(error);
                warningcount++;
                continue;
            }
            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());
                LOG.warn(msg);
                error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                errorList.add(error);
                warningcount++;
                continue;
            }
            List existingRDs = NeutronvpnUtils.getExistingRDs(dataBroker);
            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(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());
                    LOG.warn(msg);
                    error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                    errorList.add(error);
                    warningcount++;
                    continue;
                }
                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());
                    LOG.warn(msg);
                    error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                    errorList.add(error);
                    warningcount++;
                    continue;
                }
            }
            if (vpn.getNetworkIds() != null) {
                for (Uuid nw : vpn.getNetworkIds()) {
                    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());
                        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());
                        LOG.warn(msg);
                        error = RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", msg);
                        errorList.add(error);
                        warningcount++;
                    }
                }
                if (error != null) {
                    continue;
                }
            }
            try {
                createL3Vpn(vpn.getId(), vpn.getName(), vpn.getTenantId(), vpn.getRouteDistinguisher(),
                        vpn.getImportRT(), vpn.getExportRT(), vpn.getRouterId(), vpn.getNetworkIds());
            } catch (Exception ex) {
                msg = String.format("Creation of L3VPN failed for VPN %s", vpn.getId().getValue());
                LOG.error(msg, ex);
                error = RpcResultBuilder.newError(ErrorType.APPLICATION, msg, ex.getMessage());
                errorList.add(error);
                failurecount++;
            }
        }
        // if at least one succeeds; result is success
        // if none succeeds; result is failure
        if (failurecount + warningcount == vpns.size()) {
            result.set(RpcResultBuilder. failed().withRpcErrors(errorList).build());
        } else {
            List errorResponseList = new ArrayList<>();
            if (!errorList.isEmpty()) {
                for (RpcError rpcError : errorList) {
                    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. 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> getL3VPN(GetL3VPNInput input) {

        GetL3VPNOutputBuilder opBuilder = new GetL3VPNOutputBuilder();
        SettableFuture> result = SettableFuture.create();
        Uuid inputVpnId = input.getId();
        List vpns = new ArrayList<>();

        try {
            if (inputVpnId == null) {
                // get all vpns
                InstanceIdentifier vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class)
                        .build();
                Optional optionalVpns = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType
                        .CONFIGURATION, vpnsIdentifier);
                if (optionalVpns.isPresent() && !optionalVpns.get().getVpnInstance().isEmpty()) {
                    for (VpnInstance vpn : optionalVpns.get().getVpnInstance()) {
                        // eliminating implicitly created (router and VLAN provider external network specific) VPNs
                        // from getL3VPN output
                        if (vpn.getIpv4Family().getRouteDistinguisher() != null) {
                            vpns.add(vpn);
                        }
                    }
                } else {
                    // No VPN present
                    result.set(RpcResultBuilder. success().withResult(opBuilder.build()).build());
                    return result;
                }
            } else {
                String name = inputVpnId.getValue();
                InstanceIdentifier vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
                        .child(VpnInstance.class, new VpnInstanceKey(name)).build();
                // read VpnInstance Info
                Optional optionalVpn = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                        vpnIdentifier);
                // eliminating implicitly created (router or VLAN provider external network specific) VPN from
                // getL3VPN output
                if (optionalVpn.isPresent() && optionalVpn.get().getIpv4Family().getRouteDistinguisher() != null) {
                    vpns.add(optionalVpn.get());
                } else {
                    String message = String.format("GetL3VPN failed because VPN %s is not present", name);
                    LOG.error(message);
                    result.set(RpcResultBuilder.failed().withWarning(ErrorType.PROTOCOL,
                            "invalid-value", message).build());
                }
            }
            List l3vpnList = new ArrayList<>();
            for (VpnInstance vpnInstance : vpns) {
                Uuid vpnId = new Uuid(vpnInstance.getVpnInstanceName());
                // create VpnMaps id
                InstanceIdentifier vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class).child(VpnMap
                        .class, new VpnMapKey(vpnId)).build();
                L3vpnInstancesBuilder l3vpn = new L3vpnInstancesBuilder();

                List rd = Arrays.asList(vpnInstance.getIpv4Family().getRouteDistinguisher().split(","));
                List vpnTargetList = vpnInstance.getIpv4Family().getVpnTargets().getVpnTarget();

                List ertList = new ArrayList<>();
                List irtList = new ArrayList<>();

                for (VpnTarget vpnTarget : vpnTargetList) {
                    if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
                        ertList.add(vpnTarget.getVrfRTValue());
                    }
                    if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
                        irtList.add(vpnTarget.getVrfRTValue());
                    }
                    if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
                        ertList.add(vpnTarget.getVrfRTValue());
                        irtList.add(vpnTarget.getVrfRTValue());
                    }
                }

                l3vpn.setId(vpnId).setRouteDistinguisher(rd).setImportRT(irtList).setExportRT(ertList);
                Optional optionalVpnMap = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                        vpnMapIdentifier);
                if (optionalVpnMap.isPresent()) {
                    VpnMap vpnMap = optionalVpnMap.get();
                    l3vpn.setRouterId(vpnMap.getRouterId()).setNetworkIds(vpnMap.getNetworkIds())
                            .setTenantId(vpnMap.getTenantId()).setName(vpnMap.getName());
                }
                l3vpnList.add(l3vpn.build());
            }

            opBuilder.setL3vpnInstances(l3vpnList);
            result.set(RpcResultBuilder. success().withResult(opBuilder.build()).build());

        } catch (Exception ex) {
            String message = String.format("GetL3VPN failed due to %s", ex.getMessage());
            LOG.error(message, ex);
            result.set(RpcResultBuilder. 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> deleteL3VPN(DeleteL3VPNInput input) {

        DeleteL3VPNOutputBuilder opBuilder = new DeleteL3VPNOutputBuilder();
        SettableFuture> result = SettableFuture.create();
        List errorList = new ArrayList<>();

        int failurecount = 0;
        int warningcount = 0;
        List vpns = input.getId();
        for (Uuid vpn : vpns) {
            RpcError error;
            String msg;
            try {
                InstanceIdentifier vpnIdentifier =
                        InstanceIdentifier.builder(VpnInstances.class).child(VpnInstance.class, new VpnInstanceKey
                                (vpn.getValue())).build();
                Optional 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());
                    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());
                LOG.error(msg, ex);
                error = RpcResultBuilder.newError(ErrorType.APPLICATION, msg, ex.getMessage());
                errorList.add(error);
                failurecount++;
            }
        }
        // if at least one succeeds; result is success
        // if none succeeds; result is failure
        if (failurecount + warningcount == vpns.size()) {
            result.set(RpcResultBuilder. failed().withRpcErrors(errorList).build());
        } else {
            List errorResponseList = new ArrayList<>();
            if (!errorList.isEmpty()) {
                for (RpcError rpcError : errorList) {
                    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. success().withResult(opBuilder.build()).build());
        }
        return result;
    }

    protected void addSubnetToVpn(final Uuid vpnId, Uuid subnet) {
        LOG.debug("Adding subnet {} to vpn {}", subnet.getValue(), vpnId.getValue());
        Subnetmap sn = updateSubnetNode(subnet, null, vpnId);
        if (sn == null) {
            LOG.error("subnetmap is null, cannot add subnet {} to VPN {}", subnet.getValue(), vpnId.getValue());
            return;
        }
        VpnMap vpnMap = NeutronvpnUtils.getVpnMap(dataBroker, vpnId);
        if (vpnMap == null) {
            LOG.error("No vpnMap for vpnId {}, cannot add subnet {} to VPN", vpnId.getValue(), subnet.getValue());
            return;
        }

        final Uuid routerId = NeutronvpnUtils.getVpnMap(dataBroker, vpnId).getRouterId();
        // Check if there are ports on this subnet and add corresponding
        // vpn-interfaces
        List portList = sn.getPortList();
        if (portList != null) {
            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>>() {
                    @Override
                    public List> call() throws Exception {
                        WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
                        List> futures = new ArrayList<>();
                        createVpnInterface(vpnId, routerId, NeutronvpnUtils.getNeutronPort(dataBroker, portId),
                                wrtConfigTxn);
                        futures.add(wrtConfigTxn.submit());
                        return futures;
                    }
                });
            }
        }
    }

    private void updateVpnForSubnet(Uuid oldVpnId, Uuid newVpnId, Uuid subnet, boolean isBeingAssociated) {
        LOG.debug("Moving subnet {} from oldVpn {} to newVpn {} ", subnet.getValue(),
                oldVpnId.getValue(), newVpnId.getValue());
        Subnetmap sn = updateSubnetNode(subnet, null, newVpnId);
        if (sn == null) {
            LOG.error("Updating subnet {} with newVpn {} failed", subnet.getValue(), newVpnId.getValue());
            return;
        }

        final DataStoreJobCoordinator portDataStoreCoordinator = DataStoreJobCoordinator.getInstance();
        if (sn.getRouterInterfacePortId() != null) {
            portDataStoreCoordinator.enqueueJob("PORT-" + sn.getRouterInterfacePortId().getValue(), () -> {
                WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
                List> futures = new ArrayList<>();
                updateVpnInterface(newVpnId, oldVpnId,
                        NeutronvpnUtils.getNeutronPort(dataBroker, sn.getRouterInterfacePortId()),
                        isBeingAssociated, true, wrtConfigTxn);
                futures.add(wrtConfigTxn.submit());
                return futures;
            });
        }

        // Check for ports on this subnet and update association of
        // corresponding vpn-interfaces to external vpn
        List portList = sn.getPortList();
        if (portList != null) {
            for (Uuid port : portList) {
                LOG.debug("Updating vpn-interface for port {} isBeingAssociated {}",
                        port.getValue(), isBeingAssociated);
                portDataStoreCoordinator.enqueueJob("PORT-" + port.getValue(), () -> {
                    WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
                    List> futures = new ArrayList<>();
                    updateVpnInterface(newVpnId, oldVpnId, NeutronvpnUtils.getNeutronPort(dataBroker, port),
                            isBeingAssociated, false, wrtConfigTxn);
                    futures.add(wrtConfigTxn.submit());
                    return futures;
                });
            }
        }
    }

    public InstanceIdentifier getRouterInterfacesId(Uuid routerId) {
        return InstanceIdentifier.builder(RouterInterfacesMap.class)
                .child(RouterInterfaces.class, new RouterInterfacesKey(routerId)).build();
    }

    protected void addToNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
        synchronized (routerId.getValue().intern()) {
            InstanceIdentifier routerInterfacesId = getRouterInterfacesId(routerId);
            Optional 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 = new ArrayList<>();
                interfaces.add(routerInterface);
                MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId.child(Interfaces
                        .class, new InterfacesKey(interfaceName)), routerInterface);
            }
        }
    }

    protected void removeFromNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
        synchronized (routerId.getValue().intern()) {
            InstanceIdentifier routerInterfacesId = getRouterInterfacesId(routerId);
            Optional 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 = 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)));
                    }
                }
            }
        }
    }

    /**
     * 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 interVpnLinkRoutes,
                                  HashMap 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> labelOuputFtr = vpnRpcService.addStaticRoute(rpcInput);
                RpcResult 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 interVpnLinkRoutes,
                                     HashMap 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 getAdjacencyforExtraRoute(Uuid vpnId, List routeList, String fixedIp) {
        List adjList = new ArrayList<>();
        Map> adjMap = new HashMap<>();
        for (Routes route : routeList) {
            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());
                if (!nextHop.equals(fixedIp)) {
                    LOG.trace("FixedIP {} is not extra route nexthop for destination {}", fixedIp, destination);
                    continue;
                }
                LOG.trace("Adding extra route for destination {} onto vpn {} with nexthop {} ", destination,
                        vpnId.getValue(), nextHop);
                List 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 routeList) {
        for (Routes route : routeList) {
            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.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 identifier = InstanceIdentifier.builder(VpnInterfaces.class)
                                .child(VpnInterface.class, new VpnInterfaceKey(infName)).build();
                        InstanceIdentifier 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);
                }
            }
        }
    }

    protected void removeAdjacencyforExtraRoute(Uuid vpnId, List 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 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 = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                        adjacencyIdentifier);
                boolean updateNextHops = false;
                List nextHopList = new ArrayList<>();
                if (adjacency.isPresent()) {
                    List nhListRead = adjacency.get().getNextHopIpList();
                    if (nhListRead.size() > 1) { // ECMP case
                        for (String nextHopRead : nhListRead) {
                            if (nextHopRead.equals(nextHop)) {
                                updateNextHops = true;
                            } else {
                                nextHopList.add(nextHopRead);
                            }
                        }
                    }
                }

                try {
                    isLockAcquired = NeutronvpnUtils.lock(infName);
                    if (updateNextHops) {
                        // An update must be done, not including the current next hop
                        InstanceIdentifier 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) {
                    LOG.error("exception in deleting extra route: {}" + e);
                } finally {
                    if (isLockAcquired) {
                        NeutronvpnUtils.unlock(infName);
                    }
                }
            } else {
                LOG.error("Incorrect input received for extra route. {}", route);
            }
        }
    }

    protected void removeL3Vpn(Uuid id) {
        // read VPNMaps
        VpnMap vpnMap = NeutronvpnUtils.getVpnMap(dataBroker, id);
        Uuid router = (vpnMap != null) ? vpnMap.getRouterId() : null;
        // dissociate router
        if (router != null) {
            dissociateRouterFromVpn(id, router);
        }
        // dissociate networks
        if (!id.equals(router)) {
            dissociateNetworksFromVpn(id, vpnMap.getNetworkIds());
        }
        // remove entire vpnMaps node
        deleteVpnMapsNode(id);

        // remove vpn-instance
        deleteVpnInstance(id);
    }

    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 portList = sn.getPortList();
            if (portList != null) {
                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> futures = new ArrayList<>();
                        Port port = NeutronvpnUtils.getNeutronPort(dataBroker, portId);
                        if (port != null) {
                            deleteVpnInterface(vpnId, routerId, port, wrtConfigTxn);
                        } else {
                            LOG.error("Cannot proceed with deleteVpnInterface for port {} in subnet {} since port is " +
                                    "absent in Neutron config DS", portId.getValue(), subnet.getValue());
                        }
                        futures.add(wrtConfigTxn.submit());
                        return futures;
                    });
                }
            }
            // update subnet-vpn association
            removeFromSubnetNode(subnet, null, null, vpnId, null);
        } else {
            LOG.warn("Subnetmap for subnet {} not found", subnet.getValue());
        }
    }

    protected void associateRouterToVpn(Uuid vpnId, Uuid routerId) {
        updateVpnMaps(vpnId, null, routerId, null, null);
        LOG.debug("Updating association of subnets to external vpn {}", vpnId.getValue());
        List routerSubnets = NeutronvpnUtils.getNeutronRouterSubnetIds(dataBroker, routerId);
        if (routerSubnets != null) {
            for (Uuid subnetId : routerSubnets) {
                updateVpnForSubnet(routerId, 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 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 routerSubnets = NeutronvpnUtils.getNeutronRouterSubnetIds(dataBroker, routerId);
        if (routerSubnets != null) {
            for (Uuid subnetId : routerSubnets) {
                LOG.debug("Updating association of subnets to internal vpn {}", routerId.getValue());
                updateVpnForSubnet(vpnId, routerId, subnetId, false);
            }
        }
        clearFromVpnMaps(vpnId, routerId, null);
        try {
            checkAndPublishRouterDisassociatedFromVpnNotification(routerId, vpnId);
            LOG.debug("notification upon disassociation of router {} from VPN {} published", routerId.getValue(),
                    vpnId.getValue());
        } catch (Exception e) {
            LOG.error("publishing of notification upon disassociation of router {} from VPN {} failed : ", routerId
                    .getValue(), vpnId.getValue(), e);
        }
    }

    protected List associateNetworksToVpn(Uuid vpn, List networks) {
        List failedNwList = new ArrayList<>();
        List passedNwList = new ArrayList<>();
        if (!networks.isEmpty()) {
            // process corresponding subnets for VPN
            for (Uuid nw : networks) {
                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 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(dataBroker, subnet);
                            if (subnetVpnId == null) {
                                addSubnetToVpn(vpn, subnet);
                                passedNwList.add(nw);
                            } else {
                                failedNwList.add(String.format("subnet %s already added as router interface bound to " +
                                        "internal/external VPN %s", subnet.getValue (), subnetVpnId.getValue()));
                            }
                        }
                    }
                    if (NeutronvpnUtils.getIsExternal(network)) {
                        nvpnNatManager.addExternalNetworkToVpn(network, vpn);
                    }
                }
            }
            updateVpnMaps(vpn, null, null, null, passedNwList);
        }
        return failedNwList;
    }

    protected List dissociateNetworksFromVpn(Uuid vpn, List networks) {
        List failedNwList = new ArrayList<>();
        List passedNwList = new ArrayList<>();
        if (networks != null && !networks.isEmpty()) {
            // process corresponding subnets for VPN
            for (Uuid nw : networks) {
                Network network = NeutronvpnUtils.getNeutronNetwork(dataBroker, nw);
                if (network == null) {
                    failedNwList.add(String.format("network %s not found", nw.getValue()));
                } else {
                    Uuid vpnId = NeutronvpnUtils.getVpnForNetwork(dataBroker, nw);
                    if (vpn.equals(vpnId)) {
                        List networkSubnets = NeutronvpnUtils.getSubnetIdsFromNetworkId(dataBroker, nw);
                        LOG.debug("Removing network subnets...");
                        if (networkSubnets != null) {
                            for (Uuid subnet : networkSubnets) {
                                removeSubnetFromVpn(vpn, subnet);
                                passedNwList.add(nw);
                            }
                        }
                    } else {
                        if (vpnId == null) {
                            failedNwList.add(String.format("input network %s not associated to any vpn yet", nw
                                    .getValue()));
                        } else {
                            failedNwList.add(String.format("input network %s associated to a another vpn %s instead " +
                                    "of the one given as input", nw.getValue(), vpnId.getValue()));
                        }
                    }
                    if (NeutronvpnUtils.getIsExternal(network)) {
                        nvpnNatManager.removeExternalNetworkFromVpn(network);
                    }
                }
            }
            clearFromVpnMaps(vpn, null, passedNwList);
        }
        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> associateNetworks(AssociateNetworksInput input) {

        AssociateNetworksOutputBuilder opBuilder = new AssociateNetworksOutputBuilder();
        SettableFuture> result = SettableFuture.create();
        LOG.debug("associateNetworks {}", input);
        StringBuilder returnMsg = new StringBuilder();
        Uuid vpnId = input.getVpnId();

        try {
            if (NeutronvpnUtils.getVpnMap(dataBroker, vpnId) != null) {
                List netIds = input.getNetworkId();
                if (netIds != null && !netIds.isEmpty()) {
                    List failed = associateNetworksToVpn(vpnId, netIds);
                    if (!failed.isEmpty()) {
                        returnMsg.append(failed);
                    }
                }
            } else {
                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);
                LOG.error(message);
                String errorResponse = String.format("ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: %s",
                        message);
                opBuilder.setResponse(errorResponse);
                result.set(RpcResultBuilder. success().withResult(opBuilder.build()).build());
            } else {
                result.set(RpcResultBuilder. success().build());
            }
        } catch (Exception ex) {
            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. failed().withError(ErrorType.APPLICATION, message)
                    .build());
        }
        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> associateRouter(AssociateRouterInput input) {

        SettableFuture> result = SettableFuture.create();
        LOG.debug("associateRouter {}", input);
        StringBuilder returnMsg = new StringBuilder();
        Uuid vpnId = input.getVpnId();
        Uuid routerId = input.getRouterId();
        try {
            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());
                    } else if (extVpnId != null) {
                        returnMsg.append("router ").append(routerId.getValue()).append(" already associated to " +
                                "another VPN ").append(extVpnId.getValue());
                    } else {
                        associateRouterToVpn(vpnId, routerId);
                    }
                } else {
                    returnMsg.append("router not found : ").append(routerId.getValue());
                }
            } else {
                returnMsg.append("VPN not found : ").append(vpnId.getValue());
            }
            if (returnMsg.length() != 0) {
                String message = String.format("associate router to vpn %s failed due to %s", routerId.getValue(),
                        returnMsg);
                LOG.error(message);
                result.set(RpcResultBuilder. failed().withWarning(ErrorType.PROTOCOL, "invalid-value", message)
                        .build());
            } else {
                result.set(RpcResultBuilder. 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());
            LOG.error(message, ex);
            result.set(RpcResultBuilder. failed().withError(ErrorType.APPLICATION, message).build());
        }
        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> getFixedIPsForNeutronPort(GetFixedIPsForNeutronPortInput input) {
        GetFixedIPsForNeutronPortOutputBuilder opBuilder = new GetFixedIPsForNeutronPortOutputBuilder();
        SettableFuture> result = SettableFuture.create();
        Uuid portId = input.getPortId();
        StringBuilder returnMsg = new StringBuilder();
        try {
            List fixedIPList = new ArrayList<>();
            Port port = NeutronvpnUtils.getNeutronPort(dataBroker, portId);
            if (port != null) {
                List 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. failed()
                        .withWarning(ErrorType.PROTOCOL, "invalid-value", message).build());
            } else {
                opBuilder.setFixedIPs(fixedIPList);
                result.set(RpcResultBuilder. success().withResult(opBuilder.build())
                        .build());
                result.set(RpcResultBuilder. 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. 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> dissociateNetworks(DissociateNetworksInput input) {

        DissociateNetworksOutputBuilder opBuilder = new DissociateNetworksOutputBuilder();
        SettableFuture> result = SettableFuture.create();

        LOG.debug("dissociateNetworks {}", input);
        StringBuilder returnMsg = new StringBuilder();
        Uuid vpnId = input.getVpnId();

        try {
            if (NeutronvpnUtils.getVpnMap(dataBroker, vpnId) != null) {
                List netIds = input.getNetworkId();
                if (netIds != null && !netIds.isEmpty()) {
                    List failed = dissociateNetworksFromVpn(vpnId, netIds);
                    if (!failed.isEmpty()) {
                        returnMsg.append(failed);
                    }
                }
            } else {
                returnMsg.append("VPN not found : ").append(vpnId.getValue());
            }
            if (returnMsg.length() != 0) {
                String message = String.format("dissociate Networks to vpn %s failed due to %s", vpnId.getValue(),
                        returnMsg);
                LOG.error(message);
                String errorResponse = String.format("ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: "
                        + message);
                opBuilder.setResponse(errorResponse);
                result.set(RpcResultBuilder. success().withResult(opBuilder.build()).build());
            } else {
                result.set(RpcResultBuilder. success().build());
            }
        } catch (Exception ex) {
            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. failed().withError(ErrorType.APPLICATION, message)
                    .build());
        }
        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> dissociateRouter(DissociateRouterInput input) {

        SettableFuture> result = SettableFuture.create();

        LOG.debug("dissociateRouter {}", input);
        StringBuilder returnMsg = new StringBuilder();
        Uuid vpnId = input.getVpnId();
        Uuid routerId = input.getRouterId();
        try {
            if (NeutronvpnUtils.getVpnMap(dataBroker, vpnId) != null) {
                if (routerId != null) {
                    Router rtr = NeutronvpnUtils.getNeutronRouter(dataBroker, routerId);
                    if (rtr != null) {
                        Uuid routerVpnId = NeutronvpnUtils.getVpnForRouter(dataBroker, routerId, true);
                        if (vpnId.equals(routerVpnId)) {
                            dissociateRouterFromVpn(vpnId, routerId);
                        } else {
                            if (routerVpnId == null) {
                                returnMsg.append("input router ").append(routerId.getValue()).append(" not associated" +
                                        " to any vpn yet");
                            } else {
                                returnMsg.append("input router ").append(routerId.getValue()).append(" associated to " +
                                        "vpn ").append(routerVpnId.getValue()).append("instead of the vpn given as " +
                                        "input");
                            }
                        }
                    } else {
                        returnMsg.append("router not found : ").append(routerId.getValue());
                    }
                }
            } else {
                returnMsg.append("VPN not found : ").append(vpnId.getValue());
            }
            if (returnMsg.length() != 0) {
                String message = String.format("dissociate router %s to vpn %s failed due to %s", routerId.getValue(),
                        vpnId.getValue(), returnMsg);
                LOG.error(message);
                String errorResponse = String.format("ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: "
                        + message);
                result.set(RpcResultBuilder. failed().withWarning(ErrorType.PROTOCOL, "invalid-value", message)
                        .build());
            } else {
                result.set(RpcResultBuilder. 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());
            LOG.error(message, ex);
            result.set(RpcResultBuilder. failed().withError(ErrorType.APPLICATION, message).build());
        }
        LOG.debug("dissociateRouter returns..");

        return result;
    }

    protected void handleNeutronRouterDeleted(Uuid routerId, List routerSubnetIds) {
        // check if the router is associated to some VPN
        Uuid vpnId = NeutronvpnUtils.getVpnForRouter(dataBroker, routerId, true);
        if (vpnId != null) {
            // remove existing external vpn interfaces
            for (Uuid subnetId : routerSubnetIds) {
                removeSubnetFromVpn(vpnId, subnetId);
            }
            clearFromVpnMaps(vpnId, routerId, null);
        } else {
            // remove existing internal vpn interfaces
            for (Uuid subnetId : routerSubnetIds) {
                removeSubnetFromVpn(routerId, subnetId);
            }
        }
        // delete entire vpnMaps node for internal VPN
        deleteVpnMapsNode(routerId);

        // delete vpn-instance for internal VPN
        deleteVpnInstance(routerId);
    }

    protected Subnet getNeutronSubnet(Uuid subnetId){
        return NeutronvpnUtils.getNeutronSubnet(dataBroker, subnetId);
    }

    protected IpAddress getNeutronSubnetGateway(Uuid 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(dataBroker, new Uuid(name));
    }

    protected Port getNeutronPort(Uuid portId) {
        return NeutronvpnUtils.getNeutronPort(dataBroker, portId);
    }

    protected Uuid getNetworkForSubnet(Uuid subnetId) {
        return NeutronvpnUtils.getNetworkForSubnet(dataBroker, subnetId);
    }

    protected List getNetworksForVpn(Uuid vpnId) {
        return NeutronvpnUtils.getNetworksforVpn(dataBroker, vpnId);
    }

    /**
     * Implementation of the "vpnservice:neutron-ports-show" Karaf CLI command
     *
     * @return a List of String to be printed on screen
     */
    public List showNeutronPortsCLI() {
        List result = new ArrayList<>();
        result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", "Port ID", "Mac Address", "Prefix Length", "IP " +
                "Address"));
        result.add("-------------------------------------------------------------------------------------------");
        InstanceIdentifier portidentifier = InstanceIdentifier.create(Neutron.class).child(Ports.class);
        try {
            Optional ports = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, portidentifier);
            if (ports.isPresent() && ports.get().getPort() != null) {
                for (Port port : ports.get().getPort()) {
                    List fixedIPs = port.getFixedIps();
                    try {
                        if (fixedIPs != null && !fixedIPs.isEmpty()) {
                            List 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) {
            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 showVpnConfigCLI(Uuid vpnuuid) {
        List result = new ArrayList<>();
        if (vpnuuid == null) {
            System.out.println("");
            System.out.println("Displaying VPN config for all VPNs");
            System.out.println("To display VPN config for a particular VPN, use the following syntax");
            System.out.println(getshowVpnConfigCLIHelp());
        }
        try {
            RpcResult rpcResult = getL3VPN(new GetL3VPNInputBuilder().setId(vpnuuid).build()).get();
            if (rpcResult.isSuccessful()) {
                result.add("");
                result.add(String.format(" %-37s %-37s %-7s ", "VPN ID", "Tenant ID", "RD"));
                result.add("");
                result.add(String.format(" %-80s ", "Import-RTs"));
                result.add("");
                result.add(String.format(" %-80s ", "Export-RTs"));
                result.add("");
                result.add(String.format(" %-76s ", "Subnet IDs"));
                result.add("");
                result.add("------------------------------------------------------------------------------------");
                result.add("");
                List 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()));
                    result.add("");
                    result.add(String.format(" %-80s ", Vpn.getImportRT()));
                    result.add("");
                    result.add(String.format(" %-80s ", Vpn.getExportRT()));
                    result.add("");

                    Uuid vpnid = Vpn.getId();
                    List subnetList = NeutronvpnUtils.getSubnetsforVpn(dataBroker, vpnid);
                    if (!subnetList.isEmpty()) {
                        for (Uuid subnetuuid : subnetList) {
                            result.add(String.format(" %-76s ", subnetuuid.getValue()));
                        }
                    } else {
                        result.add(String.format(" %-76s ", "\"                                    \""));
                    }
                    result.add("");
                    result.add("----------------------------------------");
                    result.add("");
                }
            } else {
                String errortag = rpcResult.getErrors().iterator().next().getTag();
                if (Objects.equals(errortag, "")) {
                    System.out.println("");
                    System.out.println("No VPN has been configured yet");
                } else if (Objects.equals(errortag, "invalid-value")) {
                    System.out.println("");
                    System.out.println("VPN " + vpnuuid.getValue() + " is not present");
                } else {
                    System.out.println("error getting VPN info : " + rpcResult.getErrors());
                    System.out.println(getshowVpnConfigCLIHelp());
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            LOG.error("error getting VPN info : ", e);
            System.out.println("error getting VPN info : " + e.getMessage());
        }
        return result;
    }

    protected void createExternalVpnInterfaces(Uuid extNetId) {
        if (extNetId == null) {
            LOG.trace("external network is null");
            return;
        }

        Collection extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue());
        if (extElanInterfaces == null || extElanInterfaces.isEmpty()) {
            LOG.trace("No external ports attached to external network {}", extNetId.getValue());
            return;
        }

        WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
        for (String elanInterface : extElanInterfaces) {
            createExternalVpnInterface(extNetId, elanInterface, wrtConfigTxn);
        }
        wrtConfigTxn.submit();
    }

    protected void removeExternalVpnInterfaces(Uuid extNetId) {
        Collection extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue());
        if (extElanInterfaces == null || extElanInterfaces.isEmpty()) {
            LOG.trace("No external ports attached for external network {}", extNetId);
            return;
        }
        try {

            WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
            for (String elanInterface : extElanInterfaces) {
                InstanceIdentifier vpnIfIdentifier = NeutronvpnUtils
                        .buildVpnInterfaceIdentifier(elanInterface);
                LOG.info("Removing vpn interface {}", elanInterface);
                wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier);
            }
            wrtConfigTxn.submit();

        } catch (Exception ex) {
            LOG.error("Removal of vpninterfaces {} failed due to {}", extElanInterfaces, ex);
        }
    }

    private void createExternalVpnInterface(Uuid vpnId, String infName, WriteTransaction wrtConfigTxn) {
        writeVpnInterfaceToDs(vpnId, infName, null, false /* not a router iface */, wrtConfigTxn);
    }

    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;
        }

        Boolean wrtConfigTxnPresent = true;
        if (wrtConfigTxn == null) {
            wrtConfigTxnPresent = false;
            wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
        }

        InstanceIdentifier 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();
        }
    }

    private String getshowVpnConfigCLIHelp() {
        StringBuilder help = new StringBuilder("Usage:");
        help.append("display vpn-config [-vid/--vpnid ]");
        return help.toString();
    }

    private void checkAndPublishRouterAssociatedtoVpnNotification(Uuid routerId, Uuid vpnId) throws
            InterruptedException {
        RouterAssociatedToVpn routerAssociatedToVpn = new RouterAssociatedToVpnBuilder().setRouterId(routerId)
                .setVpnId(vpnId).build();
        LOG.info("publishing notification upon association of router to VPN");
        notificationPublishService.putNotification(routerAssociatedToVpn);
    }

    private void checkAndPublishRouterDisassociatedFromVpnNotification(Uuid routerId, Uuid vpnId) throws
            InterruptedException {
        RouterDisassociatedFromVpn routerDisassociatedFromVpn = new RouterDisassociatedFromVpnBuilder().setRouterId
                (routerId).setVpnId(vpnId).build();
        LOG.info("publishing notification upon disassociation of router from VPN");
        notificationPublishService.putNotification(routerDisassociatedFromVpn);
    }

    protected void dissociatefixedIPFromFloatingIP(String fixedNeutronPortName) {
        floatingIpMapListener.dissociatefixedIPFromFloatingIP(fixedNeutronPortName);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy