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

org.opendaylight.netvirt.dhcpservice.DhcpExternalTunnelManager Maven / Gradle / Ivy

There is a newer version: 0.11.4
Show newest version
/*
 * 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.netvirt.dhcpservice;

import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;

import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.genius.infra.Datastore;
import org.opendaylight.genius.infra.Datastore.Configuration;
import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
import org.opendaylight.genius.infra.TypedReadWriteTransaction;
import org.opendaylight.genius.infra.TypedWriteTransaction;
import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
import org.opendaylight.genius.mdsalutil.MDSALUtil;
import org.opendaylight.genius.mdsalutil.NwConstants;
import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
import org.opendaylight.genius.utils.clustering.EntityOwnershipUtils;
import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService;
import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
import org.opendaylight.netvirt.dhcpservice.api.IDhcpExternalTunnelManager;
import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput;
import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
import org.opendaylight.netvirt.elanmanager.api.IElanService;
import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayCache;
import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
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.interfaces.rev140508.interfaces.Interface;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
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.Uuid;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetExternalTunnelInterfaceNameInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetExternalTunnelInterfaceNameOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.DesignatedSwitchesForExternalTunnels;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.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.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPort;
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.HwvtepPhysicalLocatorRef;
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.RemoteMcastMacsBuilder;
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.locator.set.attributes.LocatorSetBuilder;
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.opendaylight.yangtools.yang.common.Uint64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
public class DhcpExternalTunnelManager implements IDhcpExternalTunnelManager {

    private static final Logger LOG = LoggerFactory.getLogger(DhcpExternalTunnelManager.class);
    public static final String UNKNOWN_DMAC = "00:00:00:00:00:00";

    private final DataBroker broker;
    private final ManagedNewTransactionRunner txRunner;
    private final IMdsalApiManager mdsalUtil;
    private final ItmRpcService itmRpcService;
    private final EntityOwnershipUtils entityOwnershipUtils;
    private final IInterfaceManager interfaceManager;
    private final JobCoordinator jobCoordinator;
    private final L2GatewayCache l2GatewayCache;
    private IElanService elanService;
    private final DhcpServiceCounters dhcpServiceCounters;

    private final ConcurrentMap>> designatedDpnsToTunnelIpElanNameCache =
            new ConcurrentHashMap<>();
    private final ConcurrentMap, Set> tunnelIpElanNameToVmMacCache =
            new ConcurrentHashMap<>();
    private final ConcurrentMap, Set> availableVMCache = new ConcurrentHashMap<>();
    private final ConcurrentMap, Port> vniMacAddressToPortCache = new ConcurrentHashMap<>();

    @Override
    public ConcurrentMap>> getDesignatedDpnsToTunnelIpElanNameCache() {
        return designatedDpnsToTunnelIpElanNameCache;
    }

    @Override
    public ConcurrentMap, Set> getTunnelIpElanNameToVmMacCache() {
        return tunnelIpElanNameToVmMacCache;
    }

    @Override
    public ConcurrentMap, Set> getAvailableVMCache() {
        return availableVMCache;
    }

    @Override
    public ConcurrentMap, Port> getVniMacAddressToPortCache() {
        return vniMacAddressToPortCache;
    }

    @Inject
    public DhcpExternalTunnelManager(final DataBroker broker,
            final IMdsalApiManager mdsalUtil, final ItmRpcService itmRpcService,
            final EntityOwnershipService entityOwnershipService, final IInterfaceManager interfaceManager,
            final JobCoordinator jobCoordinator, final L2GatewayCache l2GatewayCache,
            @Named("elanService") IElanService ielanService, DhcpServiceCounters dhcpServiceCounters) {
        this.broker = broker;
        this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
        this.mdsalUtil = mdsalUtil;
        this.itmRpcService = itmRpcService;
        this.entityOwnershipUtils = new EntityOwnershipUtils(entityOwnershipService);
        this.interfaceManager = interfaceManager;
        this.jobCoordinator = jobCoordinator;
        this.l2GatewayCache = l2GatewayCache;
        this.elanService = ielanService;
        this.dhcpServiceCounters = dhcpServiceCounters;
    }

    @PostConstruct
    public void init() {
        initilizeCaches();
    }

    private void initilizeCaches() {
        LOG.trace("Loading designatedDpnsToTunnelIpElanNameCache");
        InstanceIdentifier instanceIdentifier =
                InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).build();
        Optional designatedSwitchForTunnelOptional =
                MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
        if (designatedSwitchForTunnelOptional.isPresent()) {
            List list =
                designatedSwitchForTunnelOptional.get().nonnullDesignatedSwitchForTunnel();
            for (DesignatedSwitchForTunnel designatedSwitchForTunnel : list) {
                Set> setOfTunnelIpElanNamePair =
                        designatedDpnsToTunnelIpElanNameCache
                                .get(Uint64.valueOf(designatedSwitchForTunnel.getDpId()));
                if (setOfTunnelIpElanNamePair == null) {
                    setOfTunnelIpElanNamePair = new CopyOnWriteArraySet<>();
                }
                Pair tunnelIpElanNamePair =
                        new ImmutablePair<>(designatedSwitchForTunnel.getTunnelRemoteIpAddress(),
                                designatedSwitchForTunnel.getElanInstanceName());
                setOfTunnelIpElanNamePair.add(tunnelIpElanNamePair);
                designatedDpnsToTunnelIpElanNameCache.put(Uint64.valueOf(designatedSwitchForTunnel.getDpId()),
                        setOfTunnelIpElanNamePair);
            }
        }
        LOG.trace("Loading vniMacAddressToPortCache");
        InstanceIdentifier inst = InstanceIdentifier.builder(Neutron.class).child(Ports.class).build();
        Optional optionalPorts = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst);
        if (optionalPorts.isPresent()) {
            List list = optionalPorts.get().nonnullPort();
            for (Port port : list) {
                if (NeutronUtils.isPortVnicTypeNormal(port)) {
                    continue;
                }
                String macAddress = port.getMacAddress().getValue();
                Uuid networkId = port.getNetworkId();
                String segmentationId = DhcpServiceUtils.getSegmentationId(networkId, broker);
                if (segmentationId == null) {
                    return;
                }
                updateVniMacToPortCache(Uint64.valueOf(new BigInteger(segmentationId)), macAddress, port);
            }
        }
    }

    public Uint64 designateDpnId(IpAddress tunnelIp, String elanInstanceName, List dpns) {
        Uint64 designatedDpnId = readDesignatedSwitchesForExternalTunnel(tunnelIp, elanInstanceName);
        if (designatedDpnId != null && !designatedDpnId.equals(DhcpMConstants.INVALID_DPID)) {
            LOG.trace("Dpn {} already designated for tunnelIp - elan : {} - {}", designatedDpnId, tunnelIp,
                    elanInstanceName);
            return designatedDpnId;
        }
        return chooseDpn(tunnelIp, elanInstanceName, dpns);
    }

    public void installDhcpFlowsForVms(final IpAddress tunnelIp, String elanInstanceName, final List dpns,
            final Uint64 designatedDpnId, final String vmMacAddress) {
        LOG.trace("In installDhcpFlowsForVms ipAddress {}, elanInstanceName {}, dpn {}, vmMacAddress {}", tunnelIp,
                elanInstanceName, designatedDpnId, vmMacAddress);

        String tunnelIpDpnKey = getTunnelIpDpnKey(tunnelIp, designatedDpnId);
        jobCoordinator.enqueueJob(getJobKey(tunnelIpDpnKey), () -> {
            if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
                    HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
                return Collections.singletonList(
                    txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
                        dpns.remove(designatedDpnId);
                        for (Uint64 dpn : dpns) {
                            installDhcpDropAction(dpn, vmMacAddress, tx);
                        }
                        installDhcpEntries(designatedDpnId, vmMacAddress, tx);
                    }));
            } else {
                LOG.trace("Exiting installDhcpEntries since this cluster node is not the owner for dpn");
            }

            return Collections.emptyList();
        });

        updateLocalCache(tunnelIp, elanInstanceName, vmMacAddress);
    }

    public void installDhcpFlowsForVms(Uint64 designatedDpnId, Set listVmMacAddress,
            TypedReadWriteTransaction tx) throws ExecutionException, InterruptedException {
        for (String vmMacAddress : listVmMacAddress) {
            installDhcpEntries(designatedDpnId, vmMacAddress, tx);
        }
    }

    public void unInstallDhcpFlowsForVms(String elanInstanceName, List dpns, String vmMacAddress) {
        unInstallDhcpEntriesOnDpns(dpns, vmMacAddress);
        removeFromLocalCache(elanInstanceName, vmMacAddress);
    }

    public void unInstallDhcpFlowsForVms(String elanInstanceName, IpAddress tunnelIp, List dpns) {
        Pair tunnelIpElanNamePair = new ImmutablePair<>(tunnelIp, elanInstanceName);
        Set vmMacs = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair);
        LOG.trace("In unInstallFlowsForVms elanInstanceName {}, tunnelIp {}, dpns {}, vmMacs {}",
                elanInstanceName, tunnelIp, dpns, vmMacs);
        if (vmMacs == null) {
            return;
        }
        for (String vmMacAddress : vmMacs) {
            unInstallDhcpEntriesOnDpns(dpns, vmMacAddress);
        }
        tunnelIpElanNameToVmMacCache.remove(tunnelIpElanNamePair);
    }

    @NonNull
    public Uint64 readDesignatedSwitchesForExternalTunnel(IpAddress tunnelIp, String elanInstanceName) {
        if (tunnelIp == null || elanInstanceName == null || elanInstanceName.isEmpty()) {
            return Uint64.ZERO;
        }
        InstanceIdentifier instanceIdentifier =
                InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class)
                        .child(DesignatedSwitchForTunnel.class,
                                new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp)).build();
        Optional designatedSwitchForTunnelOptional =
                MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
        if (designatedSwitchForTunnelOptional.isPresent()) {
            return Uint64.valueOf(designatedSwitchForTunnelOptional.get().getDpId());
        }
        return Uint64.ZERO;
    }

    public void writeDesignatedSwitchForExternalTunnel(Uint64 dpnId, IpAddress tunnelIp,
                                                       String elanInstanceName) {
        DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey =
                new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp);
        InstanceIdentifier instanceIdentifier =
                InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class)
                        .child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build();
        DesignatedSwitchForTunnel designatedSwitchForTunnel =
                new DesignatedSwitchForTunnelBuilder().setDpId(dpnId.longValue())
                        .setElanInstanceName(elanInstanceName).setTunnelRemoteIpAddress(tunnelIp)
                        .withKey(designatedSwitchForTunnelKey).build();
        LOG.trace("Writing into CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp, elanInstanceName,
                dpnId);
        MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier, designatedSwitchForTunnel);
        updateLocalCache(dpnId, tunnelIp, elanInstanceName);
    }

    public void removeDesignatedSwitchForExternalTunnel(Uint64 dpnId, IpAddress tunnelIp,
                                                        String elanInstanceName) {
        DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey =
                new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp);
        InstanceIdentifier instanceIdentifier =
                InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class)
                        .child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build();
        LOG.trace("Removing from CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp,
                elanInstanceName, dpnId);
        MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
        removeFromLocalCache(dpnId, tunnelIp, elanInstanceName);
    }

    // This method is called whenever new OVS Switch is added.
    public void installDhcpDropActionOnDpn(Uint64 dpId) {
        // During controller restart we'll get add for designatedDpns as well and we
        // need not install drop flows for those dpns
        if (designatedDpnsToTunnelIpElanNameCache.get(dpId) != null) {
            LOG.trace("The dpn {} is designated DPN need not install drop flows", dpId);
            return;
        }
        // Read from DS since the cache may not get loaded completely in restart scenario
        if (isDpnDesignatedDpn(dpId)) {
            LOG.trace("The dpn {} is designated DPN need not install drop flows", dpId);
            return;
        }
        List vmMacs = getAllVmMacs();
        LOG.trace("Installing drop actions to this new DPN {} VMs {}", dpId, vmMacs);
        ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
            for (String vmMacAddress : vmMacs) {
                installDhcpDropAction(dpId, vmMacAddress, tx);
            }
        }), LOG, "Error writing to the datastore");
    }

    private boolean isDpnDesignatedDpn(Uint64 dpId) {
        InstanceIdentifier instanceIdentifier =
                InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).build();
        Optional designatedSwitchForTunnelOptional =
                MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
        if (designatedSwitchForTunnelOptional.isPresent()) {
            List list =
                    designatedSwitchForTunnelOptional.get().nonnullDesignatedSwitchForTunnel();
            for (DesignatedSwitchForTunnel designatedSwitchForTunnel : list) {
                if (dpId.equals(Uint64.valueOf(designatedSwitchForTunnel.getDpId()))) {
                    return true;
                }
            }
        }
        return false;
    }

    private List getAllVmMacs() {
        List vmMacs = new LinkedList<>();
        Collection> listOfVmMacs = tunnelIpElanNameToVmMacCache.values();
        for (Set list : listOfVmMacs) {
            vmMacs.addAll(list);
        }
        return vmMacs;
    }

    public void updateLocalCache(Uint64 designatedDpnId, IpAddress tunnelIp, String elanInstanceName) {
        Pair tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
        Set> tunnelIpElanNameSet;
        tunnelIpElanNameSet = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId);
        if (tunnelIpElanNameSet == null) {
            tunnelIpElanNameSet = new CopyOnWriteArraySet<>();
        }
        tunnelIpElanNameSet.add(tunnelIpElanName);
        LOG.trace("Updating designatedDpnsToTunnelIpElanNameCache for designatedDpn {} value {}", designatedDpnId,
                tunnelIpElanNameSet);
        designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameSet);
    }

    public void updateLocalCache(IpAddress tunnelIp, String elanInstanceName, String vmMacAddress) {
        Pair tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
        Set setOfExistingVmMacAddress;
        setOfExistingVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanName);
        if (setOfExistingVmMacAddress == null) {
            setOfExistingVmMacAddress = new CopyOnWriteArraySet<>();
        }
        setOfExistingVmMacAddress.add(vmMacAddress);
        LOG.trace("Updating tunnelIpElanNameToVmMacCache for tunnelIpElanName {} value {}", tunnelIpElanName,
                setOfExistingVmMacAddress);
        tunnelIpElanNameToVmMacCache.put(tunnelIpElanName, setOfExistingVmMacAddress);
        updateExistingVMTunnelIPCache(tunnelIp, elanInstanceName, vmMacAddress);
    }

    public void updateExistingVMTunnelIPCache(IpAddress tunnelIp, String elanInstanceName, String vmMacAddress) {
        Pair tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
        Set listExistingVmMacAddress;
        listExistingVmMacAddress = availableVMCache.get(tunnelIpElanName);
        if (listExistingVmMacAddress == null) {
            listExistingVmMacAddress = new CopyOnWriteArraySet<>();
        }
        listExistingVmMacAddress.add(vmMacAddress);
        LOG.trace("Updating availableVMCache for tunnelIpElanName {} value {}", tunnelIpElanName,
                listExistingVmMacAddress);
        availableVMCache.put(tunnelIpElanName, listExistingVmMacAddress);
    }

    public void handleDesignatedDpnDown(Uint64 dpnId, List listOfDpns) {
        LOG.trace("In handleDesignatedDpnDown dpnId {}, listOfDpns {}", dpnId, listOfDpns);
        Set> setOfTunnelIpElanNamePairs = designatedDpnsToTunnelIpElanNameCache.get(dpnId);
        if (setOfTunnelIpElanNamePairs == null || setOfTunnelIpElanNamePairs.isEmpty()) {
            LOG.trace("No tunnelIpElanName to handle for dpn {}. Returning", dpnId);
        } else {
            ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
                if (!dpnId.equals(DhcpMConstants.INVALID_DPID)) {
                    List listOfVms = getAllVmMacs();
                    for (String vmMacAddress : listOfVms) {
                        unInstallDhcpEntries(dpnId, vmMacAddress, tx);
                    }
                }
                for (Pair pair : setOfTunnelIpElanNamePairs) {
                    updateCacheAndInstallNewFlows(listOfDpns, pair, tx);
                }
            }), LOG, "Error writing to datastore");
        }
    }

    public void updateCacheAndInstallNewFlows(List listOfDpns, Pair pair,
            TypedReadWriteTransaction tx) throws ExecutionException, InterruptedException {
        Uint64 newDesignatedDpn = chooseDpn(pair.getLeft(), pair.getRight(), listOfDpns);
        if (newDesignatedDpn.equals(DhcpMConstants.INVALID_DPID)) {
            return;
        }
        Set setOfVmMacs = tunnelIpElanNameToVmMacCache.get(pair);
        if (setOfVmMacs != null && !setOfVmMacs.isEmpty()) {
            LOG.trace("Updating DHCP flows for VMs {} with new designated DPN {}", setOfVmMacs, newDesignatedDpn);
            installDhcpFlowsForVms(newDesignatedDpn, setOfVmMacs, tx);
        }
        java.util.Optional subnetDhcpData = getSubnetDhcpPortData(pair.getRight());
        if (subnetDhcpData.isPresent()) {
            configureDhcpArpRequestResponseFlow(newDesignatedDpn, pair.getRight(), true,
                    pair.getLeft(), subnetDhcpData.get().getPortFixedip(), subnetDhcpData.get().getPortMacaddress());
        }
    }

    private void changeExistingFlowToDrop(Pair tunnelIpElanNamePair, Uint64 dpnId,
                                          TypedReadWriteTransaction tx)
            throws ExecutionException, InterruptedException {
        Set setOfVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair);
        if (setOfVmMacAddress == null || setOfVmMacAddress.isEmpty()) {
            return;
        }
        for (String vmMacAddress : setOfVmMacAddress) {
            installDhcpDropAction(dpnId, vmMacAddress, tx);
        }
    }

    /**
     * Choose a dpn among the list of elanDpns such that it has lowest count of being the designated dpn.
     * @param tunnelIp The tunnel Ip address
     * @param elanInstanceName The elan instance name
     * @param dpns The data path nodes
     * @return The designated dpn
     */
    private Uint64 chooseDpn(IpAddress tunnelIp, String elanInstanceName,
            List dpns) {
        Uint64 designatedDpnId = DhcpMConstants.INVALID_DPID;
        if (dpns != null && dpns.size() != 0) {
            List candidateDpns = DhcpServiceUtils.getDpnsForElan(elanInstanceName, broker);
            candidateDpns.retainAll(dpns);
            LOG.trace("Choosing new dpn for tunnelIp {}, elanInstanceName {}, among elanDpns {}",
                    tunnelIp, elanInstanceName, candidateDpns);
            boolean elanDpnAvailableFlag = true;
            if (candidateDpns.isEmpty()) {
                candidateDpns = dpns;
                elanDpnAvailableFlag = false;
            }
            int size = 0;
            L2GatewayDevice device = getDeviceFromTunnelIp(tunnelIp);
            if (device == null) {
                LOG.trace("Could not find any device for elanInstanceName {} and tunnelIp {}",
                        elanInstanceName, tunnelIp);
                handleUnableToDesignateDpn(tunnelIp, elanInstanceName);
                return designatedDpnId;
            }
            for (Uint64 dpn : candidateDpns) {
                String hwvtepNodeId = device.getHwvtepNodeId();
                if (!elanDpnAvailableFlag) {
                    if (!isTunnelConfigured(dpn, hwvtepNodeId)) {
                        LOG.trace("Tunnel is not configured on dpn {} to TOR {}", dpn, hwvtepNodeId);
                        continue;
                    }
                } else if (!isTunnelUp(hwvtepNodeId, dpn)) {
                    LOG.trace("Tunnel is not up between dpn {} and TOR {}", dpn, hwvtepNodeId);
                    continue;
                }
                Set> tunnelIpElanNameSet = designatedDpnsToTunnelIpElanNameCache.get(dpn);
                if (tunnelIpElanNameSet == null) {
                    designatedDpnId = dpn;
                    break;
                }
                if (size == 0 || tunnelIpElanNameSet.size() < size) {
                    size = tunnelIpElanNameSet.size();
                    designatedDpnId = dpn;
                }
            }
            writeDesignatedSwitchForExternalTunnel(designatedDpnId, tunnelIp, elanInstanceName);
            return designatedDpnId;
        }
        handleUnableToDesignateDpn(tunnelIp, elanInstanceName);
        return designatedDpnId;
    }

    private void handleUnableToDesignateDpn(IpAddress tunnelIp, String elanInstanceName) {
        writeDesignatedSwitchForExternalTunnel(DhcpMConstants.INVALID_DPID, tunnelIp, elanInstanceName);
    }

    private void installDhcpEntries(Uint64 dpnId, String vmMacAddress,
            TypedReadWriteTransaction tx) throws ExecutionException, InterruptedException {
        DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL,
                vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil, dhcpServiceCounters, tx);
    }

    public void addOrRemoveDhcpArpFlowforElan(String elanInstanceName, boolean addFlow, String dhcpIpAddress,
                                              String dhcpMacAddress) {
        LOG.trace("Configure DHCP SR-IOV Arp flows for Elan {} dpns .", elanInstanceName);
        for (Entry>> entry : designatedDpnsToTunnelIpElanNameCache.entrySet()) {
            Uint64 dpn = entry.getKey();
            Set> tunnelIpElanNameSet = entry.getValue();
            for (Pair pair : tunnelIpElanNameSet) {
                if (pair.getRight().equalsIgnoreCase(elanInstanceName)) {
                    if (addFlow) {
                        LOG.trace("Adding SR-IOV DHCP Arp Flows for Elan {} and tunnelIp {}",
                                elanInstanceName, pair.getLeft());
                        configureDhcpArpRequestResponseFlow(dpn, elanInstanceName, true,
                                pair.getLeft(), dhcpIpAddress, dhcpMacAddress);
                    } else {
                        LOG.trace("Deleting SR-IOV DHCP Arp Flows for Elan {} and tunnelIp {}",
                                elanInstanceName, pair.getLeft());
                        configureDhcpArpRequestResponseFlow(dpn, elanInstanceName, false,
                                pair.getLeft(), dhcpIpAddress, dhcpMacAddress);
                    }
                }
            }
        }
    }


    public void configureDhcpArpRequestResponseFlow(Uint64 dpnId, String elanInstanceName, boolean addFlow,
                                            IpAddress tunnelIp, String dhcpIpAddress, String dhcpMacAddress) {
        L2GatewayDevice device = getDeviceFromTunnelIp(tunnelIp);
        if (device == null) {
            LOG.error("Unable to get L2Device for tunnelIp {} and elanInstanceName {}", tunnelIp,
                    elanInstanceName);
        }
        jobCoordinator.enqueueJob(getJobKey(elanInstanceName), () -> {
            if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
                    HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
                String tunnelInterfaceName = getExternalTunnelInterfaceName(dpnId.toString(),
                        device.getHwvtepNodeId());
                int lportTag = interfaceManager.getInterfaceInfo(tunnelInterfaceName).getInterfaceTag();
                InstanceIdentifier elanIdentifier = InstanceIdentifier.builder(ElanInstances.class)
                        .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
                Optional optElan = MDSALUtil.read(broker,
                        LogicalDatastoreType.CONFIGURATION, elanIdentifier);
                if (optElan.isPresent()) {
                    LOG.trace("Configuring the SR-IOV Arp request/response flows for LPort {} ElanTag {}.",
                            lportTag, optElan.get().getElanTag());
                    Uuid nwUuid = new Uuid(elanInstanceName);
                    String strVni = DhcpServiceUtils.getSegmentationId(nwUuid, broker);
                    Uint64 vni = strVni != null ? Uint64.valueOf(strVni) : Uint64.valueOf(0);
                    if (!vni.equals(Uint64.ZERO)) {
                        return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(
                            Datastore.CONFIGURATION, tx -> {
                                if (addFlow) {
                                    LOG.trace("Installing the SR-IOV DHCP Arp flow for DPN {} Port Ip {}, Lport {}.",
                                        dpnId, dhcpIpAddress, lportTag);
                                    installDhcpArpRequestFlows(tx, dpnId, vni, dhcpIpAddress, lportTag,
                                        optElan.get().getElanTag().toJava());
                                    installDhcpArpResponderFlows(dpnId, tunnelInterfaceName, lportTag, elanInstanceName,
                                        dhcpIpAddress, dhcpMacAddress);
                                } else {
                                    LOG.trace("Uninstalling the SR-IOV DHCP Arp flows for DPN {} Port Ip {}, Lport {}.",
                                        dpnId, dhcpIpAddress, lportTag);
                                    uninstallDhcpArpRequestFlows(tx, dpnId, vni, dhcpIpAddress, lportTag);
                                    uninstallDhcpArpResponderFlows(dpnId, tunnelInterfaceName, lportTag, dhcpIpAddress);
                                }
                            }));
                    }
                }
            }
            return Collections.emptyList();
        });
    }

    public  java.util.Optional getSubnetDhcpPortData(String elanInstanceName) {
        java.util.Optional optSubnetDhcp = java.util.Optional.empty();
        Uuid nwUuid = new Uuid(elanInstanceName);
        List subnets = DhcpServiceUtils.getSubnetIdsFromNetworkId(broker, nwUuid);
        for (Uuid subnet : subnets) {
            if (DhcpServiceUtils.isIpv4Subnet(broker, subnet)) {
                optSubnetDhcp = DhcpServiceUtils.getSubnetDhcpPortData(broker, subnet.getValue());
                return optSubnetDhcp;
            }
        }
        return optSubnetDhcp;
    }

    private void installDhcpArpRequestFlows(TypedReadWriteTransaction tx, Uint64 dpnId,
                                            Uint64 vni, String dhcpIpAddress, int lportTag, Long elanTag)
            throws ExecutionException, InterruptedException {
        DhcpServiceUtils.setupDhcpArpRequest(dpnId, NwConstants.EXTERNAL_TUNNEL_TABLE, vni, dhcpIpAddress,
                lportTag, elanTag, true, mdsalUtil, tx);
    }

    private void installDhcpArpResponderFlows(Uint64 dpnId, String interfaceName, int lportTag,
                                              String elanInstanceName, String dhcpIpAddress, String dhcpMacAddress) {
        LOG.trace("Adding SR-IOV DHCP ArpResponder for elan {} Lport {} Port Ip {}.",
                elanInstanceName, lportTag, dhcpIpAddress);
        ArpResponderInput.ArpReponderInputBuilder builder = new ArpResponderInput.ArpReponderInputBuilder();
        builder.setDpId(dpnId.toJava()).setInterfaceName(interfaceName).setSpa(dhcpIpAddress).setSha(dhcpMacAddress)
                .setLportTag(lportTag);
        builder.setInstructions(ArpResponderUtil.getInterfaceInstructions(interfaceManager, interfaceName,
                dhcpIpAddress, dhcpMacAddress, itmRpcService));
        elanService.addExternalTunnelArpResponderFlow(builder.buildForInstallFlow(), elanInstanceName);
    }

    private void uninstallDhcpArpResponderFlows(Uint64 dpnId, String interfaceName, int lportTag,
                                                String dhcpIpAddress) {
        LOG.trace("Removing SR-IOV DHCP ArpResponder flow for interface {} on DPN {}", interfaceName, dpnId);
        ArpResponderInput arpInput = new ArpResponderInput.ArpReponderInputBuilder().setDpId(dpnId.toJava())
                .setInterfaceName(interfaceName).setSpa(dhcpIpAddress)
                .setLportTag(lportTag).buildForRemoveFlow();
        elanService.removeArpResponderFlow(arpInput);
    }

    private void uninstallDhcpArpRequestFlows(TypedReadWriteTransaction tx, Uint64 dpnId,
                                              Uint64 vni, String dhcpIpAddress, int lportTag)
            throws ExecutionException, InterruptedException {
        DhcpServiceUtils.setupDhcpArpRequest(dpnId, NwConstants.EXTERNAL_TUNNEL_TABLE, vni, dhcpIpAddress,
                lportTag, null, false, mdsalUtil, tx);
    }


    public void unInstallDhcpEntries(Uint64 dpnId, String vmMacAddress,
            TypedReadWriteTransaction tx) throws ExecutionException, InterruptedException {
        DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL,
                vmMacAddress, NwConstants.DEL_FLOW, mdsalUtil, dhcpServiceCounters, tx);
    }

    private void installDhcpDropAction(Uint64 dpn, String vmMacAddress,
            TypedReadWriteTransaction tx) throws ExecutionException, InterruptedException {
        DhcpServiceUtils.setupDhcpDropAction(dpn, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL,
                vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil, dhcpServiceCounters, tx);
    }

    public List> handleTunnelStateDown(IpAddress tunnelIp, Uint64 interfaceDpn) {
        LOG.trace("In handleTunnelStateDown tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn);
        if (interfaceDpn == null) {
            return Collections.emptyList();
        }
        synchronized (getTunnelIpDpnKey(tunnelIp, interfaceDpn)) {
            Set> tunnelElanPairSet =
                    designatedDpnsToTunnelIpElanNameCache.get(interfaceDpn);
            if (tunnelElanPairSet == null || tunnelElanPairSet.isEmpty()) {
                return Collections.emptyList();
            }
            for (Pair tunnelElanPair : tunnelElanPairSet) {
                IpAddress tunnelIpInDpn = tunnelElanPair.getLeft();
                if (tunnelIpInDpn.equals(tunnelIp)) {
                    if (!checkL2GatewayConnection(tunnelElanPair)) {
                        LOG.trace("Couldn't find device for given tunnelIpElanPair {} in L2GwConnCache",
                                tunnelElanPair);
                        return Collections.emptyList();
                    }
                }
            }
            return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
                for (Pair tunnelElanPair : tunnelElanPairSet) {
                    IpAddress tunnelIpInDpn = tunnelElanPair.getLeft();
                    String elanInstanceName = tunnelElanPair.getRight();
                    if (tunnelIpInDpn.equals(tunnelIp)) {
                        if (!checkL2GatewayConnection(tunnelElanPair)) {
                            LOG.trace("Couldn't find device for given tunnelIpElanPair {} in L2GwConnCache",
                                    tunnelElanPair);
                        }
                        List dpns = DhcpServiceUtils.getListOfDpns(broker);
                        dpns.remove(interfaceDpn);
                        changeExistingFlowToDrop(tunnelElanPair, interfaceDpn, tx);
                        java.util.Optional subnetDhcpData = getSubnetDhcpPortData(elanInstanceName);
                        if (subnetDhcpData.isPresent()) {
                            configureDhcpArpRequestResponseFlow(interfaceDpn, elanInstanceName, false,
                                    tunnelIpInDpn, subnetDhcpData.get().getPortFixedip(),
                                    subnetDhcpData.get().getPortMacaddress());
                        }
                        updateCacheAndInstallNewFlows(dpns, tunnelElanPair, tx);
                    }
                }
            }));
        }
    }

    private boolean checkL2GatewayConnection(Pair tunnelElanPair) {
        for (L2GatewayDevice device : ElanL2GwCacheUtils.getInvolvedL2GwDevices(tunnelElanPair.getRight())) {
            if (Objects.equals(device.getTunnelIp(), tunnelElanPair.getLeft())) {
                return true;
            }
        }
        return false;
    }

    private String getTunnelIpDpnKey(IpAddress tunnelIp, Uint64 interfaceDpn) {
        return tunnelIp.toString() + interfaceDpn;
    }

    private void removeFromLocalCache(String elanInstanceName, String vmMacAddress) {
        for (Entry, Set> entry : tunnelIpElanNameToVmMacCache.entrySet()) {
            Pair pair = entry.getKey();
            if (pair.getRight().trim().equalsIgnoreCase(elanInstanceName.trim())) {
                Set setOfExistingVmMacAddress = entry.getValue();
                if (setOfExistingVmMacAddress == null || setOfExistingVmMacAddress.isEmpty()) {
                    continue;
                }
                LOG.trace("Removing vmMacAddress {} from listOfMacs {} for elanInstanceName {}", vmMacAddress,
                        setOfExistingVmMacAddress, elanInstanceName);
                setOfExistingVmMacAddress.remove(vmMacAddress);
                if (setOfExistingVmMacAddress.size() > 0) {
                    tunnelIpElanNameToVmMacCache.put(pair, setOfExistingVmMacAddress);
                    return;
                }
                tunnelIpElanNameToVmMacCache.remove(pair);
            }
        }
    }

    public void removeFromLocalCache(Uint64 designatedDpnId, IpAddress tunnelIp, String elanInstanceName) {
        Pair tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
        Set> tunnelIpElanNameSet;
        tunnelIpElanNameSet = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId);
        if (tunnelIpElanNameSet != null) {
            LOG.trace("Removing tunnelIpElan {} from designatedDpnsToTunnelIpElanNameCache. Existing list {} for "
                            + "designatedDpnId {}",
                    tunnelIpElanName, tunnelIpElanNameSet, designatedDpnId);
            tunnelIpElanNameSet.remove(tunnelIpElanName);
            if (tunnelIpElanNameSet.size() != 0) {
                designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameSet);
            } else {
                designatedDpnsToTunnelIpElanNameCache.remove(designatedDpnId);
            }
        }
    }

    public void updateVniMacToPortCache(Uint64 vni, String macAddress, Port port) {
        if (macAddress == null) {
            return;
        }
        Pair vniMacAddressPair = new ImmutablePair<>(
                vni, macAddress.toUpperCase(Locale.getDefault()));
        LOG.trace("Updating vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}", vni,
                macAddress.toUpperCase(Locale.getDefault()), vniMacAddressPair, port);
        vniMacAddressToPortCache.put(vniMacAddressPair, port);
    }

    public void removeVniMacToPortCache(Uint64 vni, String macAddress) {
        if (macAddress == null) {
            return;
        }
        Pair vniMacAddressPair = new ImmutablePair<>(
                vni, macAddress.toUpperCase(Locale.getDefault()));
        vniMacAddressToPortCache.remove(vniMacAddressPair);
    }

    @Nullable
    public Port readVniMacToPortCache(Uint64 vni, String macAddress) {
        if (macAddress == null) {
            return null;
        }
        Pair vniMacAddressPair = new ImmutablePair<>(
                vni, macAddress.toUpperCase(Locale.getDefault()));
        LOG.trace("Reading vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}",
                vni, macAddress.toUpperCase(Locale.getDefault()), vniMacAddressPair,
                vniMacAddressToPortCache.get(vniMacAddressPair));
        return vniMacAddressToPortCache.get(vniMacAddressPair);
    }

    public String getExternalTunnelInterfaceName(String sourceNode, String dstNode) {
        String tunnelInterfaceName = null;
        Class tunType = TunnelTypeVxlan.class;
        try {
            Future> output = itmRpcService
                    .getExternalTunnelInterfaceName(new GetExternalTunnelInterfaceNameInputBuilder()
                            .setSourceNode(sourceNode).setDestinationNode(dstNode).setTunnelType(tunType).build());

            RpcResult rpcResult = output.get();
            if (rpcResult.isSuccessful()) {
                tunnelInterfaceName = rpcResult.getResult().getInterfaceName();
                LOG.trace("Tunnel interface name: {}", tunnelInterfaceName);
            } 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;
    }

    public static Optional getNode(DataBroker dataBroker, String physicalSwitchNodeId) {
        InstanceIdentifier psNodeId = HwvtepSouthboundUtils
                .createInstanceIdentifier(new NodeId(physicalSwitchNodeId));
        return MDSALUtil.read(LogicalDatastoreType.CONFIGURATION, psNodeId, dataBroker);
    }

    @Nullable
    public RemoteMcastMacs createRemoteMcastMac(Node dstDevice, String logicalSwitchName, IpAddress internalTunnelIp) {
        Set locators = new HashSet<>();
        TerminationPointKey terminationPointKey = HwvtepSouthboundUtils.getTerminationPointKey(
                internalTunnelIp.getIpv4Address().getValue());
        HwvtepPhysicalLocatorRef phyLocRef = new HwvtepPhysicalLocatorRef(
                HwvtepSouthboundUtils.createInstanceIdentifier(dstDevice.getNodeId()).child(TerminationPoint.class,
                        terminationPointKey));
        locators.add(new LocatorSetBuilder().setLocatorRef(phyLocRef).build());

        HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils
                .createLogicalSwitchesInstanceIdentifier(dstDevice.getNodeId(), new HwvtepNodeName(logicalSwitchName)));

        RemoteMcastMacs remoteMcastMacs = new RemoteMcastMacsBuilder()
                .setMacEntryKey(new MacAddress(UNKNOWN_DMAC))
                .setLogicalSwitchRef(lsRef).build();
        InstanceIdentifier iid = HwvtepSouthboundUtils.createRemoteMcastMacsInstanceIdentifier(
                dstDevice.getNodeId(), remoteMcastMacs.key());
        ReadOnlyTransaction transaction = broker.newReadOnlyTransaction();
        try {
            //TODO do async mdsal read
            remoteMcastMacs = transaction.read(LogicalDatastoreType.CONFIGURATION, iid).checkedGet().get();
            locators.addAll(remoteMcastMacs.getLocatorSet());
            return new RemoteMcastMacsBuilder(remoteMcastMacs).setLocatorSet(new ArrayList<>(locators)).build();
        } catch (ReadFailedException e) {
            LOG.error("Failed to read the macs {}", iid);
        } finally {
            transaction.close();
        }
        return null;
    }

    private void putRemoteMcastMac(TypedWriteTransaction transaction, String elanName,
                                   L2GatewayDevice device, IpAddress internalTunnelIp) {
        Optional optionalNode = getNode(broker, device.getHwvtepNodeId());
        if (!optionalNode.isPresent()) {
            LOG.trace("could not get device node {} ", device.getHwvtepNodeId());
            return;
        }
        Node dstNode = optionalNode.get();
        RemoteMcastMacs macs = createRemoteMcastMac(dstNode, elanName, internalTunnelIp);
        HwvtepUtils.addRemoteMcastMac(transaction, dstNode.getNodeId(), macs);
    }

    public void installRemoteMcastMac(final Uint64 designatedDpnId, final IpAddress tunnelIp,
                                      final String elanInstanceName) {
        if (designatedDpnId.equals(DhcpMConstants.INVALID_DPID)) {
            return;
        }

        jobCoordinator.enqueueJob(getJobKey(elanInstanceName), () -> {
            if (!entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
                    HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
                LOG.info("Installing remote McastMac is not executed for this node.");
                return Collections.emptyList();
            }

            LOG.info("Installing remote McastMac");
            L2GatewayDevice device = getDeviceFromTunnelIp(tunnelIp);
            if (device == null) {
                LOG.error("Unable to get L2Device for tunnelIp {} and elanInstanceName {}", tunnelIp,
                    elanInstanceName);
                return Collections.emptyList();
            }
            String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(designatedDpnId),
                    device.getHwvtepNodeId());
            if (tunnelInterfaceName != null) {
                Interface tunnelInterface =
                        interfaceManager.getInterfaceInfoFromConfigDataStore(tunnelInterfaceName);
                if (tunnelInterface == null) {
                    LOG.trace("Tunnel Interface is not present {}", tunnelInterfaceName);
                    return Collections.emptyList();
                }
                return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
                    tx -> putRemoteMcastMac(tx, elanInstanceName, device,
                            tunnelInterface.augmentation(IfTunnel.class).getTunnelSource())));
            }
            return Collections.emptyList();
        });
    }

    @Nullable
    private L2GatewayDevice getDeviceFromTunnelIp(IpAddress tunnelIp) {
        Collection devices = l2GatewayCache.getAll();
        LOG.trace("In getDeviceFromTunnelIp devices {}", devices);
        for (L2GatewayDevice device : devices) {
            if (tunnelIp.equals(device.getTunnelIp())) {
                return device;
            }
        }
        return null;
    }

    private boolean isTunnelUp(String nodeName, Uint64 dpn) {
        String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), nodeName);
        if (tunnelInterfaceName == null) {
            LOG.trace("Tunnel Interface is not present on node {} with dpn {}", nodeName, dpn);
            return false;
        }
        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
                .Interface tunnelInterface =
                DhcpServiceUtils.getInterfaceFromOperationalDS(tunnelInterfaceName, broker);
        if (tunnelInterface == null) {
            LOG.trace("Interface {} is not present in interface state", tunnelInterfaceName);
            return false;
        }
        return tunnelInterface.getOperStatus() == OperStatus.Up;
    }

    public List> handleTunnelStateUp(IpAddress tunnelIp, Uint64 interfaceDpn) {
        LOG.trace("In handleTunnelStateUp tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn);
        synchronized (getTunnelIpDpnKey(tunnelIp, interfaceDpn)) {
            Set> tunnelIpElanPair =
                    designatedDpnsToTunnelIpElanNameCache.get(DhcpMConstants.INVALID_DPID);
            List dpns = DhcpServiceUtils.getListOfDpns(broker);
            if (tunnelIpElanPair == null || tunnelIpElanPair.isEmpty()) {
                LOG.trace("There are no undesignated DPNs");
                return Collections.emptyList();
            }
            return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
                for (Pair pair : tunnelIpElanPair) {
                    if (tunnelIp.equals(pair.getLeft())) {
                        String elanInstanceName = pair.getRight();
                        Uint64 newDesignatedDpn = designateDpnId(tunnelIp, elanInstanceName, dpns);
                        if (newDesignatedDpn != null && !newDesignatedDpn.equals(DhcpMConstants.INVALID_DPID)) {
                            Set vmMacAddress = tunnelIpElanNameToVmMacCache.get(pair);
                            if (vmMacAddress != null && !vmMacAddress.isEmpty()) {
                                LOG.trace("Updating DHCP flow for macAddress {} with newDpn {}",
                                        vmMacAddress, newDesignatedDpn);
                                installDhcpFlowsForVms(newDesignatedDpn, vmMacAddress, tx);
                            }
                        }
                        java.util.Optional subnetDhcpData = getSubnetDhcpPortData(elanInstanceName);
                        if (subnetDhcpData.isPresent()) {
                            configureDhcpArpRequestResponseFlow(newDesignatedDpn, elanInstanceName,
                                    true, tunnelIp, subnetDhcpData.get().getPortFixedip(),
                                    subnetDhcpData.get().getPortMacaddress());
                        }
                    }
                }
            }));
        }
    }

    private boolean isTunnelConfigured(Uint64 dpn, String hwVtepNodeId) {
        String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), hwVtepNodeId);
        if (tunnelInterfaceName == null) {
            return false;
        }
        Interface tunnelInterface = interfaceManager.getInterfaceInfoFromConfigDataStore(tunnelInterfaceName);
        if (tunnelInterface == null) {
            LOG.trace("Tunnel Interface is not present {}", tunnelInterfaceName);
            return false;
        }
        return true;
    }

    public void removeFromAvailableCache(Pair tunnelIpElanName) {
        availableVMCache.remove(tunnelIpElanName);
    }

    private void unInstallDhcpEntriesOnDpns(final List dpns, final String vmMacAddress) {
        jobCoordinator.enqueueJob(getJobKey(vmMacAddress), () -> {
            if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
                    HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
                return Collections.singletonList(
                    txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
                        for (final Uint64 dpn : dpns) {
                            unInstallDhcpEntries(dpn, vmMacAddress, tx);
                        }
                    }));
            } else {
                LOG.trace("Exiting unInstallDhcpEntries since this cluster node is not the owner for dpn");
            }

            return Collections.emptyList();
        });
    }

    @Nullable
    public IpAddress getTunnelIpBasedOnElan(String elanInstanceName, String vmMacAddress) {
        LOG.trace("DhcpExternalTunnelManager getTunnelIpBasedOnElan elanInstanceName {}", elanInstanceName);
        IpAddress tunnelIp = null;
        for (Entry, Set> entry : availableVMCache.entrySet()) {
            Pair pair = entry.getKey();
            LOG.trace("DhcpExternalTunnelManager getTunnelIpBasedOnElan left {} right {}", pair.getLeft(),
                    pair.getRight());
            if (pair.getRight().trim().equalsIgnoreCase(elanInstanceName.trim())) {
                Set listExistingVmMacAddress = entry.getValue();
                if (listExistingVmMacAddress != null && !listExistingVmMacAddress.isEmpty()
                        && listExistingVmMacAddress.contains(vmMacAddress)) {
                    tunnelIp = pair.getLeft();
                    break;
                }
            }
        }
        LOG.trace("DhcpExternalTunnelManager getTunnelIpBasedOnElan returned tunnelIP {}", tunnelIp);
        return tunnelIp;
    }

    private String getJobKey(final String jobKeySuffix) {
        return DhcpMConstants.DHCP_JOB_KEY_PREFIX + jobKeySuffix;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy