org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayUtils.java

Source

/*
 * Copyright (c) 2016 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.vpnservice.elan.l2gw.utils;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
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.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
import org.opendaylight.vpnservice.elan.l2gw.jobs.DeleteL2GwDeviceMacsFromElanJob;
import org.opendaylight.vpnservice.datastoreutils.DataStoreJobCoordinator;
import org.opendaylight.vpnservice.elan.l2gw.jobs.LogicalSwitchDeletedJob;
import org.opendaylight.vpnservice.elan.l2gw.listeners.HwvtepPhysicalLocatorListener;
import org.opendaylight.vpnservice.elan.utils.ElanClusterUtils;
import org.opendaylight.vpnservice.elan.utils.ElanConstants;
import org.opendaylight.vpnservice.elan.utils.ElanUtils;
import org.opendaylight.vpnservice.interfacemgr.IfmUtil;
import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
import org.opendaylight.vpnservice.utils.SystemPropertyReader;
import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundConstants;
import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundUtils;
import org.opendaylight.vpnservice.utils.hwvtep.HwvtepUtils;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepLogicalSwitchRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LocalUcastMacs;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteUcastMacs;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.port.attributes.VlanBindings;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.forwarding.tables.MacTable;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.instances.ElanInstance;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.forwarding.entries.MacEntry;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.IfTunnel;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeBase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeVxlan;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.AddL2GwDeviceInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

/**
 * It gathers a set of utility methods that handle ELAN configuration in external Devices (where external means
 * "not-CSS". As of now: TORs).
 *
 * It makes use of HwvtepUtils class located under ovsdb/hwvtepsouthbound project for low-level mdsal operations
 *
 * @author eperefr
 *
 */
public class ElanL2GatewayUtils {
    private static DataBroker broker;
    private static ItmRpcService itmRpcService;
    private static DataStoreJobCoordinator dataStoreJobCoordinator;
    private static Timer LogicalSwitchDeleteJobTimer = new Timer();
    private static final int LOGICAL_SWITCH_DELETE_DELAY = 120000;
    private static ConcurrentMap<Pair<NodeId, String>, TimerTask> LogicalSwitchDeletedTasks = new ConcurrentHashMap<Pair<NodeId, String>, TimerTask>();

    private static final Logger LOG = LoggerFactory.getLogger(ElanL2GatewayUtils.class);

    /**
     * Sets the data broker.
     *
     * @param dataBroker
     *            the new data broker
     */
    public static void setDataBroker(DataBroker dataBroker) {
        broker = dataBroker;
    }

    /**
     * Sets the itm rpc service.
     *
     * @param itmRpc
     *            the new itm rpc service
     */
    public static void setItmRpcService(ItmRpcService itmRpc) {
        itmRpcService = itmRpc;
    }

    /**
     * Sets DataStoreJobCoordinator
     *
     * @param dsJobCoordinator
     *            the new dataStoreJobCoordinator
     */
    public static void setDataStoreJobCoordinator(DataStoreJobCoordinator dsJobCoordinator) {
        dataStoreJobCoordinator = dsJobCoordinator;
    }

    /**
     * Installs dpn macs in external device.
     * first it checks if the physical locator towards this dpn tep is present or not
     * if the physical locator is present go ahead and add the ucast macs
     * otherwise update the mcast mac entry to include this dpn tep ip
     * and schedule the job to put ucast macs once the physical locator is programmed in device
     * @param elanName
     *            the elan name
     * @param lstElanInterfaceNames
     *            the lst Elan interface names
     * @param dpnId
     *            the dpn id
     * @param externalNodeId
     *            the external node id
     */
    public static void installDpnMacsInL2gwDevice(String elanName, Set<String> lstElanInterfaceNames,
            BigInteger dpnId, NodeId externalNodeId) {
        L2GatewayDevice elanL2GwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName,
                externalNodeId.getValue());
        if (elanL2GwDevice == null) {
            LOG.debug("L2 gw device not found in elan cache for device name {}", externalNodeId);
            return;
        }
        IpAddress dpnTepIp = getSourceDpnTepIp(dpnId, externalNodeId);
        if (dpnTepIp == null) {
            LOG.warn("Could not install dpn macs in l2gw device , dpnTepIp not found dpn : {} , nodeid : {}", dpnId,
                    externalNodeId);
            return;
        }

        String logicalSwitchName = getLogicalSwitchFromElan(elanName);
        RemoteMcastMacs remoteMcastMac = readRemoteMcastMac(externalNodeId, logicalSwitchName,
                LogicalDatastoreType.OPERATIONAL);
        boolean phyLocAlreadyExists = checkIfPhyLocatorAlreadyExistsInRemoteMcastEntry(externalNodeId,
                remoteMcastMac, dpnTepIp);
        LOG.debug("phyLocAlreadyExists = {} for locator [{}] in remote mcast entry for elan [{}], nodeId [{}]",
                phyLocAlreadyExists, String.valueOf(dpnTepIp.getValue()), elanName, externalNodeId.getValue());
        List<PhysAddress> staticMacs = null;
        staticMacs = getElanDpnMacsFromInterfaces(lstElanInterfaceNames);

        if (phyLocAlreadyExists) {
            scheduleAddDpnMacsInExtDevice(elanName, dpnId, staticMacs, elanL2GwDevice);
            return;
        }
        ElanL2GatewayMulticastUtils.scheduleMcastMacUpdateJob(elanName, elanL2GwDevice);
        scheduleAddDpnMacsInExtDevice(elanName, dpnId, staticMacs, elanL2GwDevice);
    }

    /**
     * gets the macs addresses for elan interfaces
     *
     * @param lstElanInterfaceNames
     *            the lst elan interface names
     * @return the list
     */
    private static List<PhysAddress> getElanDpnMacsFromInterfaces(Set<String> lstElanInterfaceNames) {
        List<PhysAddress> result = new ArrayList<>();
        for (String interfaceName : lstElanInterfaceNames) {
            ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(interfaceName);
            if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) {
                for (MacEntry macEntry : elanInterfaceMac.getMacEntry()) {
                    result.add(macEntry.getMacAddress());
                }
            }
        }
        return result;
    }

    /**
     * Check if phy locator already exists in remote mcast entry.
     *
     * @param nodeId
     *            the node id
     * @param remoteMcastMac
     *            the remote mcast mac
     * @param expectedPhyLocatorIp
     *            the expected phy locator ip
     * @return true, if successful
     */
    public static boolean checkIfPhyLocatorAlreadyExistsInRemoteMcastEntry(NodeId nodeId,
            RemoteMcastMacs remoteMcastMac, IpAddress expectedPhyLocatorIp) {
        if (remoteMcastMac != null) {
            HwvtepPhysicalLocatorAugmentation expectedPhyLocatorAug = HwvtepSouthboundUtils
                    .createHwvtepPhysicalLocatorAugmentation(String.valueOf(expectedPhyLocatorIp.getValue()));
            HwvtepPhysicalLocatorRef expectedPhyLocRef = new HwvtepPhysicalLocatorRef(
                    HwvtepSouthboundUtils.createPhysicalLocatorInstanceIdentifier(nodeId, expectedPhyLocatorAug));
            if (remoteMcastMac.getLocatorSet() != null) {
                for (LocatorSet locatorSet : remoteMcastMac.getLocatorSet()) {
                    if (locatorSet.getLocatorRef().equals(expectedPhyLocRef)) {
                        LOG.trace("matched phyLocRef: {}", expectedPhyLocRef);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Gets the remote mcast mac.
     *
     * @param nodeId
     *            the node id
     * @param logicalSwitchName
     *            the logical switch name
     * @param datastoreType
     *            the datastore type
     * @return the remote mcast mac
     */
    public static RemoteMcastMacs readRemoteMcastMac(NodeId nodeId, String logicalSwitchName,
            LogicalDatastoreType datastoreType) {
        InstanceIdentifier<LogicalSwitches> logicalSwitch = HwvtepSouthboundUtils
                .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName));
        RemoteMcastMacsKey remoteMcastMacsKey = new RemoteMcastMacsKey(new HwvtepLogicalSwitchRef(logicalSwitch),
                new MacAddress(ElanConstants.UNKNOWN_DMAC));
        RemoteMcastMacs remoteMcastMac = HwvtepUtils.getRemoteMcastMac(broker, datastoreType, nodeId,
                remoteMcastMacsKey);
        return remoteMcastMac;
    }

    /**
     * Removes the given MAC Addresses from all the External Devices belonging
     * to the specified ELAN.
     *
     * @param elanInstance
     *            the elan instance
     * @param macAddresses
     *            the mac addresses
     */
    public static void removeMacsFromElanExternalDevices(ElanInstance elanInstance,
            List<PhysAddress> macAddresses) {
        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices = ElanL2GwCacheUtils
                .getInvolvedL2GwDevices(elanInstance.getElanInstanceName());
        for (L2GatewayDevice l2GatewayDevice : elanL2GwDevices.values()) {
            removeRemoteUcastMacsFromExternalDevice(l2GatewayDevice.getHwvtepNodeId(),
                    elanInstance.getElanInstanceName(), macAddresses);
        }
    }

    /**
     * Removes the given MAC Addresses from the specified External Device.
     *
     * @param deviceNodeId
     *            the device node id
     * @param logicalSwitchName
     * @param macAddresses
     *            the mac addresses
     * @return the listenable future
     */
    private static ListenableFuture<Void> removeRemoteUcastMacsFromExternalDevice(String deviceNodeId,
            String logicalSwitchName, List<PhysAddress> macAddresses) {
        NodeId nodeId = new NodeId(deviceNodeId);

        // TODO (eperefr)
        List<MacAddress> lstMac = Lists.transform(macAddresses, new Function<PhysAddress, MacAddress>() {
            @Override
            public MacAddress apply(PhysAddress physAddress) {
                return (physAddress != null) ? new MacAddress(physAddress.getValue()) : null;
            }
        });
        return HwvtepUtils.deleteRemoteUcastMacs(broker, nodeId, logicalSwitchName, lstMac);
    }

    public static ElanInstance getElanInstanceForUcastLocalMac(LocalUcastMacs localUcastMac) {
        Optional<LogicalSwitches> lsOpc = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL,
                (InstanceIdentifier<LogicalSwitches>) localUcastMac.getLogicalSwitchRef().getValue());
        if (lsOpc.isPresent()) {
            LogicalSwitches ls = lsOpc.get();
            if (ls != null) {
                // Logical switch name is Elan name
                String elanName = getElanFromLogicalSwitch(ls.getHwvtepNodeName().getValue());
                return ElanUtils.getElanInstanceByName(elanName);
            } else {
                String macAddress = localUcastMac.getMacEntryKey().getValue();
                LOG.error("Could not find logical_switch for {} being added/deleted", macAddress);
            }
        }
        return null;
    }

    /**
     * Install external device local macs in dpn.
     *
     * @param dpnId
     *            the dpn id
     * @param l2gwDeviceNodeId
     *            the l2gw device node id
     * @param elan
     *            the elan
     */
    public static void installL2gwDeviceMacsInDpn(BigInteger dpnId, NodeId l2gwDeviceNodeId, ElanInstance elan) {
        L2GatewayDevice l2gwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elan.getElanInstanceName(),
                l2gwDeviceNodeId.getValue());
        if (l2gwDevice == null) {
            LOG.debug("L2 gw device not found in elan cache for device name {}", l2gwDeviceNodeId.getValue());
            return;
        }

        installDmacFlowsOnDpn(dpnId, l2gwDevice, elan);
    }

    /**
     * Install dmac flows on dpn.
     *
     * @param dpnId
     *            the dpn id
     * @param l2gwDevice
     *            the l2gw device
     * @param elan
     *            the elan
     */
    public static void installDmacFlowsOnDpn(BigInteger dpnId, L2GatewayDevice l2gwDevice, ElanInstance elan) {
        String elanName = elan.getElanInstanceName();

        List<LocalUcastMacs> l2gwDeviceLocalMacs = l2gwDevice.getUcastLocalMacs();
        if (l2gwDeviceLocalMacs != null && !l2gwDeviceLocalMacs.isEmpty()) {
            for (LocalUcastMacs localUcastMac : l2gwDeviceLocalMacs) {
                //TODO batch these ops
                ElanUtils.installDmacFlowsToExternalRemoteMac(dpnId, l2gwDevice.getHwvtepNodeId(),
                        elan.getElanTag(), elan.getVni(), localUcastMac.getMacEntryKey().getValue(), elanName);
            }
            LOG.debug("Installing L2gw device [{}] local macs [size: {}] in dpn [{}] for elan [{}]",
                    l2gwDevice.getHwvtepNodeId(), l2gwDeviceLocalMacs.size(), dpnId, elanName);
        }
    }

    /**
     * Install elan l2gw devices local macs in dpn.
     *
     * @param dpnId
     *            the dpn id
     * @param elan
     *            the elan
     */
    public static void installElanL2gwDevicesLocalMacsInDpn(BigInteger dpnId, ElanInstance elan) {
        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevicesFromCache = ElanL2GwCacheUtils
                .getInvolvedL2GwDevices(elan.getElanInstanceName());
        if (elanL2GwDevicesFromCache != null) {
            for (L2GatewayDevice l2gwDevice : elanL2GwDevicesFromCache.values()) {
                installDmacFlowsOnDpn(dpnId, l2gwDevice, elan);
            }
        } else {
            LOG.debug("No Elan l2 gateway devices in cache for [{}] ", elan.getElanInstanceName());
        }
    }

    public static void installL2GwUcastMacInElan(final ElanInstance elan, final L2GatewayDevice extL2GwDevice,
            final String macToBeAdded) {
        final String extDeviceNodeId = extL2GwDevice.getHwvtepNodeId();
        final String elanInstanceName = elan.getElanInstanceName();

        // Retrieve all participating DPNs in this Elan. Populate this MAC in DMAC table.
        // Looping through all DPNs in order to add/remove mac flows in their DMAC table
        final List<DpnInterfaces> elanDpns = ElanUtils.getInvolvedDpnsInElan(elanInstanceName);
        if (elanDpns != null && elanDpns.size() > 0) {
            String jobKey = elan.getElanInstanceName() + ":" + macToBeAdded;
            ElanClusterUtils.runOnlyInLeaderNode(jobKey, "install l2gw mcas in dmac table",
                    new Callable<List<ListenableFuture<Void>>>() {
                        @Override
                        public List<ListenableFuture<Void>> call() throws Exception {
                            List<ListenableFuture<Void>> fts = Lists.newArrayList();
                            for (DpnInterfaces elanDpn : elanDpns) {
                                //TODO batch the below call
                                fts.addAll(ElanUtils.installDmacFlowsToExternalRemoteMac(elanDpn.getDpId(),
                                        extDeviceNodeId, elan.getElanTag(), elan.getVni(), macToBeAdded,
                                        elanInstanceName));
                            }
                            return fts;
                        }
                    });
        }
        final IpAddress extL2GwDeviceTepIp = extL2GwDevice.getTunnelIp();
        final List<PhysAddress> macList = new ArrayList<PhysAddress>();
        macList.add(new PhysAddress(macToBeAdded));

        String jobKey = "hwvtep:" + elan.getElanInstanceName() + ":" + macToBeAdded;
        ElanClusterUtils.runOnlyInLeaderNode(jobKey, "install remote ucast macs in l2gw device",
                new Callable<List<ListenableFuture<Void>>>() {
                    @Override
                    public List<ListenableFuture<Void>> call() throws Exception {
                        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices = ElanL2GwCacheUtils
                                .getInvolvedL2GwDevices(elanInstanceName);

                        List<ListenableFuture<Void>> fts = Lists.newArrayList();
                        for (L2GatewayDevice otherDevice : elanL2GwDevices.values()) {
                            if (!otherDevice.getHwvtepNodeId().equals(extDeviceNodeId)
                                    && !areMLAGDevices(extL2GwDevice, otherDevice)) {
                                final String hwvtepId = otherDevice.getHwvtepNodeId();
                                InstanceIdentifier<Node> iid = HwvtepSouthboundUtils
                                        .createInstanceIdentifier(new NodeId(hwvtepId));
                                final String logicalSwitchName = elanInstanceName;

                                ListenableFuture<Void> ft = HwvtepUtils.installUcastMacs(broker, hwvtepId, macList,
                                        logicalSwitchName, extL2GwDeviceTepIp);
                                //TODO batch the above call
                                Futures.addCallback(ft, new FutureCallback<Void>() {
                                    @Override
                                    public void onSuccess(Void noarg) {
                                        LOG.trace("Successful in initiating ucast_remote_macs addition"
                                                + "related to {} in {}", logicalSwitchName, hwvtepId);
                                    }

                                    @Override
                                    public void onFailure(Throwable error) {
                                        LOG.error(String.format(
                                                "Failed adding ucast_remote_macs related to " + "%s in %s",
                                                logicalSwitchName, hwvtepId), error);
                                    };
                                });
                                fts.add(ft);
                            }
                        }
                        return fts;
                    }
                });
    }

    /**
     * Un install l2 gw ucast mac from elan.
     *
     * @param elan
     *            the elan
     * @param l2GwDevice
     *            the l2 gw device
     * @param macAddresses
     *            the mac addresses
     */
    public static void unInstallL2GwUcastMacFromElan(final ElanInstance elan, final L2GatewayDevice l2GwDevice,
            final List<MacAddress> macAddresses) {
        if (macAddresses == null || macAddresses.isEmpty()) {
            return;
        }
        final String elanName = elan.getElanInstanceName();

        // Retrieve all participating DPNs in this Elan. Populate this MAC in
        // DMAC table. Looping through all DPNs in order to add/remove mac flows
        // in their DMAC table
        for (final MacAddress mac : macAddresses) {
            final List<DpnInterfaces> elanDpns = ElanUtils.getInvolvedDpnsInElan(elanName);
            if (elanDpns != null && !elanDpns.isEmpty()) {
                String jobKey = elanName + ":" + mac.getValue();
                ElanClusterUtils.runOnlyInLeaderNode(jobKey, "delete l2gw macs from dmac table",
                        new Callable<List<ListenableFuture<Void>>>() {
                            @Override
                            public List<ListenableFuture<Void>> call() {
                                List<ListenableFuture<Void>> fts = Lists.newArrayList();
                                for (DpnInterfaces elanDpn : elanDpns) {
                                    BigInteger dpnId = elanDpn.getDpId();
                                    // never batch deletes
                                    fts.addAll(ElanUtils.deleteDmacFlowsToExternalMac(elan.getElanTag(), dpnId,
                                            l2GwDevice.getHwvtepNodeId(), mac.getValue()));
                                }
                                return fts;
                            }
                        });
            }
        }

        DeleteL2GwDeviceMacsFromElanJob job = new DeleteL2GwDeviceMacsFromElanJob(broker, elanName, l2GwDevice,
                macAddresses);
        ElanClusterUtils.runOnlyInLeaderNode(job.getJobKey(), "delete remote ucast macs in l2gw devices", job);
    }

    /**
     * Delete elan l2 gateway devices ucast local macs from dpn.
     *
     * @param elanName
     *            the elan name
     * @param dpnId
     *            the dpn id
     */
    public static void deleteElanL2GwDevicesUcastLocalMacsFromDpn(final String elanName, final BigInteger dpnId) {
        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices = ElanL2GwCacheUtils
                .getInvolvedL2GwDevices(elanName);
        if (elanL2GwDevices == null || elanL2GwDevices.isEmpty()) {
            LOG.trace("No L2 gateway devices in Elan [{}] cache.", elanName);
            return;
        }
        final ElanInstance elan = ElanUtils.getElanInstanceByName(elanName);
        if (elan == null) {
            LOG.error("Could not find Elan by name: {}", elanName);
            return;
        }
        LOG.info("Deleting Elan [{}] L2GatewayDevices UcastLocalMacs from Dpn [{}]", elanName, dpnId);

        final Long elanTag = elan.getElanTag();
        for (final L2GatewayDevice l2GwDevice : elanL2GwDevices.values()) {
            List<MacAddress> localMacs = getL2GwDeviceLocalMacs(l2GwDevice);
            if (localMacs != null && !localMacs.isEmpty()) {
                for (final MacAddress mac : localMacs) {
                    String jobKey = elanName + ":" + mac.getValue();
                    ElanClusterUtils.runOnlyInLeaderNode(jobKey, "delete l2gw macs from dmac table",
                            new Callable<List<ListenableFuture<Void>>>() {
                                @Override
                                public List<ListenableFuture<Void>> call() {
                                    List<ListenableFuture<Void>> futures = Lists.newArrayList();

                                    futures.addAll(ElanUtils.deleteDmacFlowsToExternalMac(elanTag, dpnId,
                                            l2GwDevice.getHwvtepNodeId(), mac.getValue()));
                                    return futures;
                                }
                            });
                }
            }
        }
    }

    /**
     * Gets the l2 gw device local macs.
     *
     * @param l2gwDevice
     *            the l2gw device
     * @return the l2 gw device local macs
     */
    public static List<MacAddress> getL2GwDeviceLocalMacs(L2GatewayDevice l2gwDevice) {
        List<MacAddress> macs = new ArrayList<>();
        if (l2gwDevice == null) {
            return macs;
        }
        List<LocalUcastMacs> lstUcastLocalMacs = l2gwDevice.getUcastLocalMacs();
        if (lstUcastLocalMacs != null && !lstUcastLocalMacs.isEmpty()) {
            macs = Lists.transform(lstUcastLocalMacs, new Function<LocalUcastMacs, MacAddress>() {
                @Override
                public MacAddress apply(LocalUcastMacs localUcastMac) {
                    return (localUcastMac != null) ? localUcastMac.getMacEntryKey() : null;
                }
            });
        }
        return macs;
    }

    /**
     * Delete elan macs from L2 gateway device.<br>
     * This includes deleting ELAN mac table entries plus external device
     * UcastLocalMacs which are part of the same ELAN.
     *
     * @param l2GatewayDevice
     *            the l2 gateway device
     * @param elanName
     *            the elan name
     * @return the listenable future
     */
    public static ListenableFuture<Void> deleteElanMacsFromL2GatewayDevice(L2GatewayDevice l2GatewayDevice,
            String elanName) {
        List<MacAddress> elanMacTableEntries = getElanMacTableEntries(elanName);
        List<MacAddress> elanL2GatewayDevicesLocalMacs = getElanL2GatewayDevicesLocalMacs(l2GatewayDevice,
                elanName);

        List<MacAddress> lstElanLocalMacs = new ArrayList<>(elanMacTableEntries);
        lstElanLocalMacs.addAll(elanL2GatewayDevicesLocalMacs);

        return HwvtepUtils.deleteRemoteUcastMacs(broker, new NodeId(l2GatewayDevice.getHwvtepNodeId()), elanName,
                lstElanLocalMacs);
    }

    /**
     * Gets the elan mac table entries.
     *
     * @param elanName
     *            the elan name
     * @return the elan mac table entries as list
     */
    public static List<MacAddress> getElanMacTableEntries(String elanName) {
        MacTable macTable = ElanUtils.getElanMacTable(elanName);
        if (macTable == null || macTable.getMacEntry() == null || macTable.getMacEntry().isEmpty()) {
            LOG.trace("MacTable is empty for elan: {}", elanName);
            return Collections.emptyList();
        }
        List<MacAddress> lstMacs = Lists.transform(macTable.getMacEntry(), new Function<MacEntry, MacAddress>() {
            @Override
            public MacAddress apply(MacEntry macEntry) {
                return (macEntry != null) ? new MacAddress(macEntry.getMacAddress().getValue()) : null;
            }
        });
        return lstMacs;
    }

    /**
     * Gets the elan l2 gateway devices local macs.
     *
     * @param l2GwDeviceToBeExcluded
     *            the l2 gw device to be excluded
     * @param elanName
     *            the elan name
     * @return the elan l2 gateway devices local macs
     */
    public static List<MacAddress> getElanL2GatewayDevicesLocalMacs(L2GatewayDevice l2GwDeviceToBeExcluded,
            String elanName) {
        List<MacAddress> lstL2GatewayDeviceMacs = new ArrayList<>();

        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevicesFromCache = ElanL2GwCacheUtils
                .getInvolvedL2GwDevices(elanName);
        if (elanL2GwDevicesFromCache != null) {
            for (L2GatewayDevice otherDevice : elanL2GwDevicesFromCache.values()) {
                if (!otherDevice.getHwvtepNodeId().equals(l2GwDeviceToBeExcluded.getHwvtepNodeId())) {
                    List<LocalUcastMacs> lstUcastLocalMacs = otherDevice.getUcastLocalMacs();
                    if (lstUcastLocalMacs != null) {
                        List<MacAddress> l2GwDeviceMacs = Lists.transform(lstUcastLocalMacs,
                                new Function<LocalUcastMacs, MacAddress>() {
                                    @Override
                                    public MacAddress apply(LocalUcastMacs localUcastMac) {
                                        return (localUcastMac != null) ? localUcastMac.getMacEntryKey() : null;
                                    }
                                });
                        lstL2GatewayDeviceMacs.addAll(l2GwDeviceMacs);
                    }
                }
            }
        }
        return lstL2GatewayDeviceMacs;
    }

    /**
     * Install ELAN macs in L2 Gateway device.<br>
     * This includes installing ELAN mac table entries plus external device
     * UcastLocalMacs which are part of the same ELAN.
     *
     * @param elanName
     *            the elan name
     * @param l2GatewayDevice
     *            the l2 gateway device which has to be configured
     * @return the listenable future
     */
    public static ListenableFuture<Void> installElanMacsInL2GatewayDevice(String elanName,
            L2GatewayDevice l2GatewayDevice) {
        String logicalSwitchName = getLogicalSwitchFromElan(elanName);
        NodeId hwVtepNodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId());

        List<RemoteUcastMacs> lstL2GatewayDevicesMacs = getOtherDevicesMacs(elanName, l2GatewayDevice, hwVtepNodeId,
                logicalSwitchName);
        List<RemoteUcastMacs> lstElanMacTableEntries = getElanMacTableEntriesMacs(elanName, l2GatewayDevice,
                hwVtepNodeId, logicalSwitchName);

        List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<>(lstL2GatewayDevicesMacs);
        lstRemoteUcastMacs.addAll(lstElanMacTableEntries);

        ListenableFuture<Void> future = HwvtepUtils.addRemoteUcastMacs(broker, hwVtepNodeId, lstRemoteUcastMacs);

        LOG.info("Added RemoteUcastMacs entries [{}] in config DS. NodeID: {}, LogicalSwitch: {}",
                lstRemoteUcastMacs.size(), hwVtepNodeId.getValue(), logicalSwitchName);
        return future;
    }

    /**
     * Gets the l2 gateway devices ucast local macs as remote ucast macs.
     *
     * @param elanName
     *            the elan name
     * @param l2GatewayDeviceToBeConfigured
     *            the l2 gateway device to be configured
     * @param hwVtepNodeId
     *            the hw vtep node Id to be configured
     * @param logicalSwitchName
     *            the logical switch name
     * @return the l2 gateway devices macs as remote ucast macs
     */
    public static List<RemoteUcastMacs> getOtherDevicesMacs(String elanName,
            L2GatewayDevice l2GatewayDeviceToBeConfigured, NodeId hwVtepNodeId, String logicalSwitchName) {
        List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<RemoteUcastMacs>();
        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevicesFromCache = ElanL2GwCacheUtils
                .getInvolvedL2GwDevices(elanName);

        if (elanL2GwDevicesFromCache != null) {
            for (L2GatewayDevice otherDevice : elanL2GwDevicesFromCache.values()) {
                if (l2GatewayDeviceToBeConfigured.getHwvtepNodeId().equals(otherDevice.getHwvtepNodeId())) {
                    continue;
                }
                if (!areMLAGDevices(l2GatewayDeviceToBeConfigured, otherDevice)) {
                    List<LocalUcastMacs> lstUcastLocalMacs = otherDevice.getUcastLocalMacs();
                    if (lstUcastLocalMacs != null) {
                        for (LocalUcastMacs localUcastMac : lstUcastLocalMacs) {
                            HwvtepPhysicalLocatorAugmentation physLocatorAug = HwvtepSouthboundUtils
                                    .createHwvtepPhysicalLocatorAugmentation(
                                            String.valueOf(otherDevice.getTunnelIp().getValue()));
                            RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(
                                    hwVtepNodeId, localUcastMac.getMacEntryKey().getValue(),
                                    localUcastMac.getIpaddr(), logicalSwitchName, physLocatorAug);
                            lstRemoteUcastMacs.add(remoteUcastMac);
                        }
                    }
                }
            }
        }
        return lstRemoteUcastMacs;
    }

    /**
     * Are MLAG devices.
     *
     * @param l2GatewayDevice
     *            the l2 gateway device
     * @param otherL2GatewayDevice
     *            the other l2 gateway device
     * @return true, if both the specified l2 gateway devices are part of same
     *         MLAG
     */
    public static boolean areMLAGDevices(L2GatewayDevice l2GatewayDevice, L2GatewayDevice otherL2GatewayDevice) {
        // If tunnel IPs are same, then it is considered to be part of same MLAG
        return Objects.equals(l2GatewayDevice.getTunnelIp(), otherL2GatewayDevice.getTunnelIp());
    }

    /**
     * Gets the elan mac table entries as remote ucast macs. <br>
     * Note: ELAN MAC table only contains internal switches MAC's. It doesn't
     * contain external device MAC's.
     *
     * @param elanName
     *            the elan name
     * @param l2GatewayDeviceToBeConfigured
     *            the l2 gateway device to be configured
     * @param hwVtepNodeId
     *            the hw vtep node id
     * @param logicalSwitchName
     *            the logical switch name
     * @return the elan mac table entries as remote ucast macs
     */
    public static List<RemoteUcastMacs> getElanMacTableEntriesMacs(String elanName,
            L2GatewayDevice l2GatewayDeviceToBeConfigured, NodeId hwVtepNodeId, String logicalSwitchName) {
        List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<RemoteUcastMacs>();

        MacTable macTable = ElanUtils.getElanMacTable(elanName);
        if (macTable == null || macTable.getMacEntry() == null || macTable.getMacEntry().isEmpty()) {
            LOG.trace("MacTable is empty for elan: {}", elanName);
            return lstRemoteUcastMacs;
        }

        for (MacEntry macEntry : macTable.getMacEntry()) {
            BigInteger dpnId = ElanUtils.getDpidFromInterface(macEntry.getInterface());
            if (dpnId == null) {
                LOG.error("DPN ID not found for interface {}", macEntry.getInterface());
                continue;
            }

            IpAddress dpnTepIp = getSourceDpnTepIp(dpnId, hwVtepNodeId);
            LOG.trace("Dpn Tep IP: {} for dpnId: {} and nodeId: {}", dpnTepIp, dpnId, hwVtepNodeId.getValue());
            if (dpnTepIp == null) {
                LOG.error("TEP IP not found for dpnId {} and nodeId {}", dpnId, hwVtepNodeId.getValue());
                continue;
            }
            HwvtepPhysicalLocatorAugmentation physLocatorAug = HwvtepSouthboundUtils
                    .createHwvtepPhysicalLocatorAugmentation(String.valueOf(dpnTepIp.getValue()));
            // TODO: Query ARP cache to get IP address corresponding to the
            // MAC
            IpAddress ipAddress = null;
            RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(hwVtepNodeId,
                    macEntry.getMacAddress().getValue(), ipAddress, logicalSwitchName, physLocatorAug);
            lstRemoteUcastMacs.add(remoteUcastMac);
        }
        return lstRemoteUcastMacs;
    }

    /**
     * Gets the external tunnel interface name.
     *
     * @param sourceNode
     *            the source node
     * @param dstNode
     *            the dst node
     * @return the external tunnel interface name
     */
    public static String getExternalTunnelInterfaceName(String sourceNode, String dstNode) {
        Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
        String tunnelInterfaceName = null;
        try {
            Future<RpcResult<GetExternalTunnelInterfaceNameOutput>> output = itmRpcService
                    .getExternalTunnelInterfaceName(new GetExternalTunnelInterfaceNameInputBuilder()
                            .setSourceNode(sourceNode).setDestinationNode(dstNode).setTunnelType(tunType).build());

            RpcResult<GetExternalTunnelInterfaceNameOutput> rpcResult = output.get();
            if (rpcResult.isSuccessful()) {
                tunnelInterfaceName = rpcResult.getResult().getInterfaceName();
                LOG.debug("Tunnel interface name: {} for sourceNode: {} and dstNode: {}", tunnelInterfaceName,
                        sourceNode, dstNode);
            } else {
                LOG.warn("RPC call to ITM.GetExternalTunnelInterfaceName failed with error: {}",
                        rpcResult.getErrors());
            }
        } catch (NullPointerException | InterruptedException | ExecutionException e) {
            LOG.error("Failed to get external tunnel interface name for sourceNode: {} and dstNode: {}: {} ",
                    sourceNode, dstNode, e);
        }
        return tunnelInterfaceName;
    }

    /**
     * Gets the source dpn tep ip.
     *
     * @param srcDpnId
     *            the src dpn id
     * @param dstHwVtepNodeId
     *            the dst hw vtep node id
     * @return the dpn tep ip
     */
    public static IpAddress getSourceDpnTepIp(BigInteger srcDpnId, NodeId dstHwVtepNodeId) {
        IpAddress dpnTepIp = null;
        String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(srcDpnId),
                dstHwVtepNodeId.getValue());
        if (tunnelInterfaceName != null) {
            Interface tunnelInterface = getInterfaceFromConfigDS(new InterfaceKey(tunnelInterfaceName), broker);
            if (tunnelInterface != null) {
                dpnTepIp = tunnelInterface.getAugmentation(IfTunnel.class).getTunnelSource();
            } else {
                LOG.warn("Tunnel interface not found for tunnelInterfaceName {}", tunnelInterfaceName);
            }
        } else {
            LOG.warn("Tunnel interface name not found for srcDpnId {} and dstHwVtepNodeId {}", srcDpnId,
                    dstHwVtepNodeId);
        }
        return dpnTepIp;
    }

    /**
     * Update vlan bindings in l2 gateway device.
     *
     * @param nodeId
     *            the node id
     * @param logicalSwitchName
     *            the logical switch name
     * @param hwVtepDevice
     *            the hardware device
     * @param defaultVlanId
     *            the default vlan id
     * @return the listenable future
     */
    public static ListenableFuture<Void> updateVlanBindingsInL2GatewayDevice(NodeId nodeId,
            String logicalSwitchName, Devices hwVtepDevice, Integer defaultVlanId) {
        if (hwVtepDevice == null || hwVtepDevice.getInterfaces() == null
                || hwVtepDevice.getInterfaces().isEmpty()) {
            String errMsg = "HwVtepDevice is null or interfaces are empty.";
            LOG.error(errMsg);
            return Futures.immediateFailedFuture(new RuntimeException(errMsg));
        }

        WriteTransaction transaction = broker.newWriteOnlyTransaction();
        for (org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.devices.Interfaces deviceInterface : hwVtepDevice
                .getInterfaces()) {
            List<VlanBindings> vlanBindings = new ArrayList<>();
            if (deviceInterface.getSegmentationIds() != null && !deviceInterface.getSegmentationIds().isEmpty()) {
                for (Integer vlanId : deviceInterface.getSegmentationIds()) {
                    vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(nodeId, vlanId, logicalSwitchName));
                }
            } else {
                // Use defaultVlanId (specified in L2GatewayConnection) if Vlan
                // ID not specified at interface level.
                vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(nodeId, defaultVlanId, logicalSwitchName));
            }
            HwvtepUtils.mergeVlanBindings(transaction, nodeId, hwVtepDevice.getDeviceName(),
                    deviceInterface.getInterfaceName(), vlanBindings);
        }
        ListenableFuture<Void> future = transaction.submit();
        LOG.info("Updated Hwvtep VlanBindings in config DS. NodeID: {}, LogicalSwitch: {}", nodeId.getValue(),
                logicalSwitchName);
        return future;
    }

    /**
     * Delete vlan bindings from l2 gateway device.
     *
     * @param nodeId
     *            the node id
     * @param hwVtepDevice
     *            the hw vtep device
     * @param defaultVlanId
     *            the default vlan id
     * @return the listenable future
     */
    public static ListenableFuture<Void> deleteVlanBindingsFromL2GatewayDevice(NodeId nodeId, Devices hwVtepDevice,
            Integer defaultVlanId) {
        if (hwVtepDevice == null || hwVtepDevice.getInterfaces() == null
                || hwVtepDevice.getInterfaces().isEmpty()) {
            String errMsg = "HwVtepDevice is null or interfaces are empty.";
            LOG.error(errMsg);
            return Futures.immediateFailedFuture(new RuntimeException(errMsg));
        }
        NodeId physicalSwitchNodeId = HwvtepSouthboundUtils.createManagedNodeId(nodeId,
                hwVtepDevice.getDeviceName());

        WriteTransaction transaction = broker.newWriteOnlyTransaction();
        for (org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.devices.Interfaces deviceInterface : hwVtepDevice
                .getInterfaces()) {
            String phyPortName = deviceInterface.getInterfaceName();
            if (deviceInterface.getSegmentationIds() != null && !deviceInterface.getSegmentationIds().isEmpty()) {
                for (Integer vlanId : deviceInterface.getSegmentationIds()) {
                    HwvtepUtils.deleteVlanBinding(transaction, physicalSwitchNodeId, phyPortName, vlanId);
                }
            } else {
                // Use defaultVlanId (specified in L2GatewayConnection) if Vlan
                // ID not specified at interface level.
                HwvtepUtils.deleteVlanBinding(transaction, physicalSwitchNodeId, phyPortName, defaultVlanId);
            }
        }
        ListenableFuture<Void> future = transaction.submit();

        LOG.info("Deleted Hwvtep VlanBindings from config DS. NodeID: {}, hwVtepDevice: {}, defaultVlanId: {} ",
                nodeId.getValue(), hwVtepDevice, defaultVlanId);
        return future;
    }

    /**
     * Gets the elan name from logical switch name.
     *
     * @param logicalSwitchName
     *            the logical switch name
     * @return the elan name from logical switch name
     */
    public static String getElanFromLogicalSwitch(String logicalSwitchName) {
        // Assuming elan name is same as logical switch name
        String elanName = logicalSwitchName;
        return elanName;
    }

    /**
     * Gets the logical switch name from elan name.
     *
     * @param elanName
     *            the elan name
     * @return the logical switch from elan name
     */
    public static String getLogicalSwitchFromElan(String elanName) {
        // Assuming logical switch name is same as elan name
        String logicalSwitchName = elanName;
        return logicalSwitchName;
    }

    /**
     * Gets the l2 gateway connection job key.
     *
     * @param nodeId
     *            the node id
     * @param logicalSwitchName
     *            the logical switch name
     * @return the l2 gateway connection job key
     */
    public static String getL2GatewayConnectionJobKey(String nodeId, String logicalSwitchName) {
        return logicalSwitchName;
    }

    public static InstanceIdentifier<Interface> getInterfaceIdentifier(InterfaceKey interfaceKey) {
        InstanceIdentifier.InstanceIdentifierBuilder<Interface> interfaceInstanceIdentifierBuilder = InstanceIdentifier
                .builder(Interfaces.class).child(Interface.class, interfaceKey);
        return interfaceInstanceIdentifierBuilder.build();
    }

    public static Interface getInterfaceFromConfigDS(InterfaceKey interfaceKey, DataBroker dataBroker) {
        InstanceIdentifier<Interface> interfaceId = getInterfaceIdentifier(interfaceKey);
        Optional<Interface> interfaceOptional = IfmUtil.read(LogicalDatastoreType.CONFIGURATION, interfaceId,
                dataBroker);
        if (!interfaceOptional.isPresent()) {
            return null;
        }

        return interfaceOptional.get();
    }

    /**
     * Delete l2 gateway device ucast local macs from elan.<br>
     * Deletes macs from internal ELAN nodes and also on rest of external l2
     * gateway devices which are part of the ELAN.
     *
     * @param l2GatewayDevice
     *            the l2 gateway device whose ucast local macs to be deleted
     *            from elan
     * @param elanName
     *            the elan name
     * @return the listenable future
     */
    public static List<ListenableFuture<Void>> deleteL2GwDeviceUcastLocalMacsFromElan(
            L2GatewayDevice l2GatewayDevice, String elanName) {
        LOG.info("Deleting L2GatewayDevice [{}] UcastLocalMacs from elan [{}]", l2GatewayDevice.getHwvtepNodeId(),
                elanName);

        List<ListenableFuture<Void>> futures = new ArrayList<>();
        ElanInstance elan = ElanUtils.getElanInstanceByName(elanName);
        if (elan == null) {
            LOG.error("Could not find Elan by name: {}", elanName);
            return futures;
        }

        List<MacAddress> localMacs = getL2GwDeviceLocalMacs(l2GatewayDevice);
        unInstallL2GwUcastMacFromElan(elan, l2GatewayDevice, localMacs);
        return futures;
    }

    public static void createItmTunnels(ItmRpcService itmRpcService, String hwvtepId, String psName,
            IpAddress tunnelIp) {
        AddL2GwDeviceInputBuilder builder = new AddL2GwDeviceInputBuilder();
        builder.setTopologyId(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID.getValue());
        builder.setNodeId(HwvtepSouthboundUtils.createManagedNodeId(new NodeId(hwvtepId), psName).getValue());
        builder.setIpAddress(tunnelIp);
        try {
            Future<RpcResult<Void>> result = itmRpcService.addL2GwDevice(builder.build());
            RpcResult<Void> rpcResult = result.get();
            if (rpcResult.isSuccessful()) {
                LOG.info("Created ITM tunnels for {}", hwvtepId);
            } else {
                LOG.error("Failed to create ITM Tunnels: ", rpcResult.getErrors());
            }
        } catch (InterruptedException | ExecutionException e) {
            LOG.error("RPC to create ITM tunnels failed", e);
        }
    }

    public static String getNodeIdFromDpnId(BigInteger dpnId) {
        return MDSALUtil.NODE_PREFIX + MDSALUtil.SEPARATOR + dpnId.toString();
    }

    public static void scheduleAddDpnMacInExtDevices(String elanName, BigInteger dpId,
            List<PhysAddress> staticMacAddresses) {
        ConcurrentMap<String, L2GatewayDevice> elanDevices = ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName);
        for (final L2GatewayDevice externalDevice : elanDevices.values()) {
            scheduleAddDpnMacsInExtDevice(elanName, dpId, staticMacAddresses, externalDevice);
        }
    }

    public static void scheduleAddDpnMacsInExtDevice(final String elanName, BigInteger dpId,
            final List<PhysAddress> staticMacAddresses, final L2GatewayDevice externalDevice) {
        NodeId nodeId = new NodeId(externalDevice.getHwvtepNodeId());
        final IpAddress dpnTepIp = ElanL2GatewayUtils.getSourceDpnTepIp(dpId, nodeId);
        LOG.trace("Dpn Tep IP: {} for dpnId: {} and nodeId: {}", dpnTepIp, dpId, nodeId);
        if (dpnTepIp == null) {
            LOG.error("could not install dpn mac in l2gw TEP IP not found for dpnId {} and nodeId {}", dpId,
                    nodeId);
            return;
        }
        TerminationPointKey tpKey = HwvtepSouthboundUtils
                .getTerminationPointKey(dpnTepIp.getIpv4Address().getValue());
        InstanceIdentifier<TerminationPoint> tpPath = HwvtepSouthboundUtils.createTerminationPointId(nodeId, tpKey);

        HwvtepPhysicalLocatorListener.runJobAfterPhysicalLocatorIsAvialable(tpPath, new Runnable() {
            @Override
            public void run() {
                HwvtepUtils.installUcastMacs(broker, externalDevice.getHwvtepNodeId(), staticMacAddresses, elanName,
                        dpnTepIp);
            }
        });
    }

    public static void scheduleDeleteLogicalSwitch(final NodeId hwvtepNodeId, final String lsName) {
        TimerTask logicalSwitchDeleteTask = new TimerTask() {
            @Override
            public void run() {
                LogicalSwitchDeletedJob logicalSwitchDeletedJob = new LogicalSwitchDeletedJob(broker, hwvtepNodeId,
                        lsName);
                ElanL2GatewayUtils.dataStoreJobCoordinator.enqueueJob(logicalSwitchDeletedJob.getJobKey(),
                        logicalSwitchDeletedJob, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
            }
        };
        Pair<NodeId, String> nodeIdLogicalSwitchNamePair = new ImmutablePair<NodeId, String>(hwvtepNodeId, lsName);
        LogicalSwitchDeletedTasks.putIfAbsent(nodeIdLogicalSwitchNamePair, logicalSwitchDeleteTask);
        LogicalSwitchDeleteJobTimer.schedule(logicalSwitchDeleteTask, LOGICAL_SWITCH_DELETE_DELAY);
    }

    public static void cancelDeleteLogicalSwitch(final NodeId hwvtepNodeId, final String lsName) {
        Pair<NodeId, String> nodeIdLogicalSwitchNamePair = new ImmutablePair<NodeId, String>(hwvtepNodeId, lsName);
        TimerTask logicalSwitchDeleteTask = LogicalSwitchDeletedTasks.get(nodeIdLogicalSwitchNamePair);
        if (logicalSwitchDeleteTask != null) {
            LOG.debug("Delete logical switch {} action on node {} cancelled", lsName, hwvtepNodeId);
            logicalSwitchDeleteTask.cancel();
            LogicalSwitchDeletedTasks.remove(nodeIdLogicalSwitchNamePair);
        }
    }
}