DHCP Handling for TOR VM
[vpnservice.git] / dhcpservice / dhcpservice-impl / src / main / java / org / opendaylight / vpnservice / dhcpservice / DhcpExternalTunnelManager.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.vpnservice.dhcpservice;
9
10 import java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.HashSet;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.Set;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ConcurrentMap;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.Future;
21
22 import org.apache.commons.lang3.tuple.ImmutablePair;
23 import org.apache.commons.lang3.tuple.Pair;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
26 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.vpnservice.dhcpservice.api.DHCPMConstants;
29 import org.opendaylight.vpnservice.mdsalutil.MDSALDataStoreUtils;
30 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
31 import org.opendaylight.vpnservice.mdsalutil.NwConstants;
32 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
33 import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
34 import org.opendaylight.vpnservice.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils;
35 import org.opendaylight.vpnservice.neutronvpn.api.utils.NeutronUtils;
36 import org.opendaylight.vpnservice.utils.clustering.ClusteringUtils;
37 import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundUtils;
38 import org.opendaylight.vpnservice.utils.hwvtep.HwvtepUtils;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepLogicalSwitchRef;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorRef;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSetBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev160428.DesignatedSwitchesForExternalTunnels;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.IfTunnel;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameInputBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameOutput;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
65 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.opendaylight.yangtools.yang.common.RpcResult;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71 import com.google.common.base.Optional;
72 import com.google.common.util.concurrent.FutureCallback;
73 import com.google.common.util.concurrent.Futures;
74 import com.google.common.util.concurrent.ListenableFuture;
75
76 public class DhcpExternalTunnelManager {
77
78     private static final Logger logger = LoggerFactory.getLogger(DhcpExternalTunnelManager.class);
79     public static final String UNKNOWN_DMAC = "00:00:00:00:00:00";
80     private static final FutureCallback<Void> DEFAULT_CALLBACK =
81             new FutureCallback<Void>() {
82                 @Override
83                 public void onSuccess(Void result) {
84                     logger.debug("Success in Datastore write operation");
85                 }
86                 @Override
87                 public void onFailure(Throwable error) {
88                     logger.error("Error in Datastore write operation", error);
89                 };
90             };
91     private final DataBroker broker;
92     private IMdsalApiManager mdsalUtil;
93
94     private ConcurrentMap<BigInteger, List<Pair<IpAddress, String>>> designatedDpnsToTunnelIpElanNameCache = new ConcurrentHashMap<BigInteger, List<Pair<IpAddress, String>>>();
95     private ConcurrentMap<Pair<IpAddress, String>, Set<String>> tunnelIpElanNameToVmMacCache = new ConcurrentHashMap<Pair<IpAddress, String>, Set<String>>();
96     private ConcurrentMap<Pair<BigInteger, String>, Port> vniMacAddressToPortCache = new ConcurrentHashMap<Pair<BigInteger, String>, Port>();
97     private ItmRpcService itmRpcService;
98     private EntityOwnershipService entityOwnershipService;
99
100     public DhcpExternalTunnelManager(DataBroker broker, IMdsalApiManager mdsalUtil, ItmRpcService itmRpcService, EntityOwnershipService entityOwnershipService) {
101         this.broker = broker;
102         this.mdsalUtil = mdsalUtil;
103         this.itmRpcService = itmRpcService;
104         this.entityOwnershipService = entityOwnershipService;
105         initilizeCaches();
106     }
107
108     private void initilizeCaches() {
109         logger.trace("Loading designatedDpnsToTunnelIpElanNameCache");
110         InstanceIdentifier<DesignatedSwitchesForExternalTunnels> instanceIdentifier = InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).build();
111         Optional<DesignatedSwitchesForExternalTunnels> designatedSwitchForTunnelOptional = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
112         if (designatedSwitchForTunnelOptional.isPresent()) {
113             List<DesignatedSwitchForTunnel> list = designatedSwitchForTunnelOptional.get().getDesignatedSwitchForTunnel();
114             for (DesignatedSwitchForTunnel designatedSwitchForTunnel : list) {
115                 List<Pair<IpAddress, String>> listOfTunnelIpElanNamePair = designatedDpnsToTunnelIpElanNameCache.get(designatedSwitchForTunnel.getDpId());
116                 if (listOfTunnelIpElanNamePair == null) {
117                     listOfTunnelIpElanNamePair = new LinkedList<Pair<IpAddress, String>>();
118                 }
119                 Pair<IpAddress, String> tunnelIpElanNamePair = new ImmutablePair<IpAddress, String>(designatedSwitchForTunnel.getTunnelRemoteIpAddress(), designatedSwitchForTunnel.getElanInstanceName());
120                 listOfTunnelIpElanNamePair.add(tunnelIpElanNamePair);
121                 designatedDpnsToTunnelIpElanNameCache.put(BigInteger.valueOf(designatedSwitchForTunnel.getDpId()), listOfTunnelIpElanNamePair);
122             }
123         }
124         logger.trace("Loading vniMacAddressToPortCache");
125         InstanceIdentifier<Ports> inst = InstanceIdentifier.builder(Neutron.class).child(Ports.class).build();
126         Optional<Ports> optionalPorts = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst);
127         if (optionalPorts.isPresent()) {
128             List<Port> list = optionalPorts.get().getPort();
129             for (Port port : list) {
130                 if(NeutronUtils.isPortVnicTypeNormal(port)) {
131                     continue;
132                 }
133                 String macAddress = port.getMacAddress();
134                 Uuid networkId = port.getNetworkId();
135                 String segmentationId = DhcpServiceUtils.getSegmentationId(networkId, broker);
136                 if (segmentationId == null) {
137                     return;
138                 }
139                 updateVniMacToPortCache(new BigInteger(segmentationId), macAddress, port);
140             }
141         }
142
143     }
144
145
146     public BigInteger designateDpnId(IpAddress tunnelIp,
147             String elanInstanceName, List<BigInteger> dpns) {
148         BigInteger designatedDpnId = readDesignatedSwitchesForExternalTunnel(tunnelIp, elanInstanceName);
149         if (designatedDpnId != null && !designatedDpnId.equals(DHCPMConstants.INVALID_DPID)) {
150             logger.trace("Dpn {} already designated for tunnelIp - elan : {} - {}", designatedDpnId, tunnelIp, elanInstanceName);
151             return designatedDpnId;
152         }
153         return chooseDpn(tunnelIp, elanInstanceName, dpns);
154     }
155
156     public void installDhcpFlowsForVms(IpAddress tunnelIp, String elanInstanceName, List<BigInteger> dpns,
157             BigInteger designatedDpnId, String vmMacAddress) {
158         installDhcpEntries(designatedDpnId, vmMacAddress, entityOwnershipService);
159         dpns.remove(designatedDpnId);
160         for (BigInteger dpn : dpns) {
161             installDhcpDropAction(dpn, vmMacAddress, entityOwnershipService);
162         }
163         updateLocalCache(tunnelIp, elanInstanceName, vmMacAddress);
164     }
165
166     public void installDhcpFlowsForVms(IpAddress tunnelIp,
167             String elanInstanceName, BigInteger designatedDpnId, Set<String> listVmMacAddress) {
168         for (String vmMacAddress : listVmMacAddress) {
169             installDhcpEntries(designatedDpnId, vmMacAddress);
170         }
171     }
172
173     public void unInstallDhcpFlowsForVms(String elanInstanceName, List<BigInteger> dpns, String vmMacAddress) {
174         for (BigInteger dpn : dpns) {
175             unInstallDhcpEntries(dpn, vmMacAddress, entityOwnershipService);
176         }
177         removeFromLocalCache(elanInstanceName, vmMacAddress);
178     }
179
180     public BigInteger readDesignatedSwitchesForExternalTunnel(IpAddress tunnelIp, String elanInstanceName) {
181         InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier = InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).child(DesignatedSwitchForTunnel.class, new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp)).build();
182         Optional<DesignatedSwitchForTunnel> designatedSwitchForTunnelOptional = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
183         if (designatedSwitchForTunnelOptional.isPresent()) {
184             return BigInteger.valueOf(designatedSwitchForTunnelOptional.get().getDpId());
185         }
186         return null;
187     }
188
189     public void writeDesignatedSwitchForExternalTunnel(BigInteger dpnId, IpAddress tunnelIp, String elanInstanceName) {
190         DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey = new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp);
191         InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier = InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build();
192         DesignatedSwitchForTunnel designatedSwitchForTunnel = new DesignatedSwitchForTunnelBuilder().setDpId(dpnId.longValue()).setElanInstanceName(elanInstanceName).setTunnelRemoteIpAddress(tunnelIp).setKey(designatedSwitchForTunnelKey).build();
193         logger.trace("Writing into CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp, elanInstanceName, dpnId);
194         MDSALDataStoreUtils.asyncUpdate(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier, designatedSwitchForTunnel, DEFAULT_CALLBACK);
195     }
196
197     public void removeDesignatedSwitchForExternalTunnel(BigInteger dpnId, IpAddress tunnelIp, String elanInstanceName) {
198         DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey = new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp);
199         InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier = InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build();
200         logger.trace("Writing into CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp, elanInstanceName, dpnId);
201         MDSALDataStoreUtils.asyncRemove(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier, DEFAULT_CALLBACK);
202     }
203
204     public void installDhcpDropActionOnDpn(BigInteger dpId) {
205         List<String> vmMacs = getAllVmMacs();
206         logger.trace("Installing drop actions to this new DPN {} VMs {}", dpId, vmMacs);
207         for (String vmMacAddress : vmMacs) {
208             installDhcpDropAction(dpId, vmMacAddress);
209         }
210     }
211
212     private List<String> getAllVmMacs() {
213         List<String> vmMacs = new LinkedList<String>();
214         Collection<Set<String>> listOfVmMacs = tunnelIpElanNameToVmMacCache.values();
215         for (Set<String> list : listOfVmMacs) {
216             vmMacs.addAll(list);
217         }
218         return vmMacs;
219     }
220
221     public void updateLocalCache(BigInteger designatedDpnId, IpAddress tunnelIp, String elanInstanceName) {
222         Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<IpAddress, String>(tunnelIp, elanInstanceName);
223         List<Pair<IpAddress, String>> tunnelIpElanNameList;
224         tunnelIpElanNameList = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId);
225         if (tunnelIpElanNameList == null) {
226             tunnelIpElanNameList = new LinkedList<Pair<IpAddress, String>>();
227         }
228         tunnelIpElanNameList.add(tunnelIpElanName);
229         designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameList);
230     }
231
232     private void updateLocalCache(IpAddress tunnelIp, String elanInstanceName, String vmMacAddress) {
233         Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<IpAddress, String>(tunnelIp, elanInstanceName);
234         Set<String> listExistingVmMacAddress;
235         listExistingVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanName);
236         if (listExistingVmMacAddress == null) {
237             listExistingVmMacAddress = new HashSet<String>();
238         }
239         listExistingVmMacAddress.add(vmMacAddress);
240         tunnelIpElanNameToVmMacCache.put(tunnelIpElanName, listExistingVmMacAddress);
241     }
242
243     public void handleDesignatedDpnDown(BigInteger dpnId, List<BigInteger> listOfDpns) {
244         logger.trace("In handleDesignatedDpnDown dpnId {}, listOfDpns {}", dpnId, listOfDpns);
245         try {
246             List<Pair<IpAddress, String>> listOfTunnelIpElanNamePairs = designatedDpnsToTunnelIpElanNameCache.get(dpnId);
247             if (!dpnId.equals(DHCPMConstants.INVALID_DPID)) {
248                 List<String> listOfVms = getAllVmMacs();
249                 for (String vmMacAddress : listOfVms) {
250                     unInstallDhcpEntries(dpnId, vmMacAddress);
251                 }
252             }
253             if (listOfTunnelIpElanNamePairs == null || listOfTunnelIpElanNamePairs.isEmpty()) {
254                 logger.trace("No tunnelIpElanName to handle for dpn {}. Returning", dpnId);
255                 return;
256             }
257             for (Pair<IpAddress, String> pair : listOfTunnelIpElanNamePairs) {
258                 updateCacheAndInstallNewFlows(dpnId, listOfDpns, pair);
259             }
260         } catch (Exception e) {
261             logger.error("Error in handleDesignatedDpnDown {}", e);
262         }
263     }
264
265     public void updateCacheAndInstallNewFlows(BigInteger dpnId,
266             List<BigInteger> listOfDpns, Pair<IpAddress, String> pair)
267             throws ExecutionException {
268         BigInteger newDesignatedDpn = chooseDpn(pair.getLeft(), pair.getRight(), listOfDpns);
269         if (newDesignatedDpn.equals(DHCPMConstants.INVALID_DPID)) {
270             return;
271         }
272         Set<String> listVmMacs = tunnelIpElanNameToVmMacCache.get(pair);
273         if (listVmMacs != null && !listVmMacs.isEmpty()) {
274             installDhcpFlowsForVms(pair.getLeft(), pair.getRight(), newDesignatedDpn, listVmMacs);
275         }
276     }
277
278     private void changeExistingFlowToDrop(Pair<IpAddress, String> tunnelIpElanNamePair, BigInteger dpnId) {
279         try {
280             Set<String> listVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair);
281             if (listVmMacAddress == null || listVmMacAddress.isEmpty()) {
282                 return;
283             }
284             for (String vmMacAddress : listVmMacAddress) {
285                 installDhcpDropAction(dpnId, vmMacAddress);
286             }
287         } catch (Exception e) {
288             logger.error("Error in uninstallExistingFlows {}", e);
289         }
290     }
291
292     /**
293      * Choose a dpn among the list of elanDpns such that it has lowest count of being the designated dpn.
294      * @param tunnelIp
295      * @param elanInstanceName
296      * @param dpns
297      * @return
298      */
299     private BigInteger chooseDpn(IpAddress tunnelIp, String elanInstanceName,
300             List<BigInteger> dpns) {
301         BigInteger designatedDpnId = DHCPMConstants.INVALID_DPID;
302         if (dpns != null && dpns.size() != 0) {
303             List<BigInteger> candidateDpns = DhcpServiceUtils.getDpnsForElan(elanInstanceName, broker);
304             candidateDpns.retainAll(dpns);
305             logger.trace("Choosing new dpn for tunnelIp {}, elanInstanceName {}, among elanDpns {}", tunnelIp, elanInstanceName, candidateDpns);
306             boolean elanDpnAvailableFlag = true;
307             if (candidateDpns == null || candidateDpns.isEmpty()) {
308                 candidateDpns = dpns;
309                 elanDpnAvailableFlag = false;
310             }
311             int size = 0;
312             L2GatewayDevice device = getDeviceFromTunnelIp(elanInstanceName, tunnelIp);
313             if (device == null) {
314                 logger.trace("Could not find any device for elanInstanceName {} and tunnelIp {}", elanInstanceName, tunnelIp);
315                 handleUnableToDesignateDpn(tunnelIp, elanInstanceName);
316                 return designatedDpnId;
317             }
318             for (BigInteger dpn : candidateDpns) {
319                 String hwvtepNodeId = device.getHwvtepNodeId();
320                 if (!elanDpnAvailableFlag) {
321                     if (!isTunnelConfigured(dpn, hwvtepNodeId)) {
322                         logger.trace("Tunnel is not configured on dpn {} to TOR {}", dpn, hwvtepNodeId);
323                         continue;
324                     }
325                 } else if (!isTunnelUp(hwvtepNodeId, dpn)) {
326                     logger.trace("Tunnel is not up between dpn {} and TOR {}", dpn, hwvtepNodeId);
327                     continue;
328                 }
329                 List<Pair<IpAddress, String>> tunnelIpElanNameList = designatedDpnsToTunnelIpElanNameCache.get(dpn);
330                 if (tunnelIpElanNameList == null) {
331                     designatedDpnId = dpn;
332                     break;
333                 }
334                 if (size == 0 || tunnelIpElanNameList.size() < size) {
335                     size = tunnelIpElanNameList.size();
336                     designatedDpnId = dpn;
337                 }
338             }
339             if (!elanDpnAvailableFlag) {
340                 installRemoteMcastMac(designatedDpnId, device, elanInstanceName);
341             }
342             writeDesignatedSwitchForExternalTunnel(designatedDpnId, tunnelIp, elanInstanceName);
343             return designatedDpnId;
344         }
345         handleUnableToDesignateDpn(tunnelIp, elanInstanceName);
346         return designatedDpnId;
347     }
348
349     private void handleUnableToDesignateDpn(IpAddress tunnelIp, String elanInstanceName) {
350         writeDesignatedSwitchForExternalTunnel(DHCPMConstants.INVALID_DPID, tunnelIp, elanInstanceName);
351     }
352
353     public void installDhcpEntries(BigInteger dpnId, String vmMacAddress) {
354         DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL, vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil);
355     }
356
357     public void installDhcpEntries(final BigInteger dpnId, final String vmMacAddress, EntityOwnershipService eos) {
358         final String nodeId = DhcpServiceUtils.getNodeIdFromDpnId(dpnId);
359         ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
360                 eos, MDSALUtil.NODE_PREFIX, nodeId);
361         Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
362             @Override
363             public void onSuccess(Boolean isOwner) {
364                 if (isOwner) {
365                     installDhcpEntries(dpnId, vmMacAddress);
366                 } else {
367                     logger.trace("Exiting installDhcpEntries since this cluster node is not the owner for dpn {}", nodeId);
368                 }
369             }
370
371             @Override
372             public void onFailure(Throwable error) {
373                 logger.error("Error while fetching checkNodeEntityOwner", error);
374             }
375         });
376     }
377
378     public void unInstallDhcpEntries(BigInteger dpnId, String vmMacAddress) {
379         DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL, vmMacAddress, NwConstants.DEL_FLOW, mdsalUtil);
380     }
381
382     public void unInstallDhcpEntries(final BigInteger dpnId, final String vmMacAddress, EntityOwnershipService eos) {
383         final String nodeId = DhcpServiceUtils.getNodeIdFromDpnId(dpnId);
384         ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
385                 eos, MDSALUtil.NODE_PREFIX, nodeId);
386         Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
387             @Override
388             public void onSuccess(Boolean isOwner) {
389                 if (isOwner) {
390                     unInstallDhcpEntries(dpnId, vmMacAddress);
391                 } else {
392                     logger.trace("Exiting unInstallDhcpEntries since this cluster node is not the owner for dpn {}", nodeId);
393                 }
394             }
395
396             @Override
397             public void onFailure(Throwable error) {
398                 logger.error("Error while fetching checkNodeEntityOwner", error);
399             }
400         });
401     }
402
403     public void installDhcpDropAction(BigInteger dpn, String vmMacAddress) {
404         DhcpServiceUtils.setupDhcpDropAction(dpn, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL, vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil);
405     }
406
407     public void installDhcpDropAction(final BigInteger dpnId, final String vmMacAddress, EntityOwnershipService eos) {
408         final String nodeId = DhcpServiceUtils.getNodeIdFromDpnId(dpnId);
409         ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
410                 eos, MDSALUtil.NODE_PREFIX, nodeId);
411         Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
412             @Override
413             public void onSuccess(Boolean isOwner) {
414                 if (isOwner) {
415                     installDhcpDropAction(dpnId, vmMacAddress);
416                 } else {
417                     logger.trace("Exiting installDhcpDropAction since this cluster node is not the owner for dpn {}", nodeId);
418                 }
419             }
420
421             @Override
422             public void onFailure(Throwable error) {
423                 logger.error("Error while fetching checkNodeEntityOwner", error);
424             }
425         });
426     }
427
428     public void handleTunnelStateDown(IpAddress tunnelIp, BigInteger interfaceDpn) {
429         logger.trace("In handleTunnelStateDown tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn);
430         if (interfaceDpn == null) {
431             return;
432         }
433         try {
434             List<Pair<IpAddress, String>> tunnelElanPairList = designatedDpnsToTunnelIpElanNameCache.get(interfaceDpn);
435             if (tunnelElanPairList == null || tunnelElanPairList.isEmpty()) {
436                 return;
437             }
438             for (Pair<IpAddress, String> tunnelElanPair : tunnelElanPairList) {
439                 IpAddress tunnelIpInDpn = tunnelElanPair.getLeft();
440                 if (tunnelIpInDpn.equals(tunnelIp)) {
441                     List<BigInteger> dpns = DhcpServiceUtils.getListOfDpns(broker);
442                     dpns.remove(interfaceDpn);
443                     changeExistingFlowToDrop(tunnelElanPair, interfaceDpn);
444                     updateCacheAndInstallNewFlows(interfaceDpn, dpns, tunnelElanPair);
445                 }
446             }
447         } catch (Exception e) {
448             logger.error("Error in handleTunnelStateDown {}", e.getMessage());
449             logger.trace("Exception details {}", e);
450         }
451     }
452
453     private void removeFromLocalCache(String elanInstanceName, String vmMacAddress) {
454         Set<Pair<IpAddress, String>> tunnelIpElanNameKeySet = tunnelIpElanNameToVmMacCache.keySet();
455         for (Pair<IpAddress, String> pair : tunnelIpElanNameKeySet) {
456             if (pair.getRight().trim().equalsIgnoreCase(elanInstanceName.trim())) {
457                 Set<String> listExistingVmMacAddress;
458                 listExistingVmMacAddress = tunnelIpElanNameToVmMacCache.get(pair);
459                 if (listExistingVmMacAddress == null || listExistingVmMacAddress.isEmpty()) {
460                     continue;
461                 }
462                 listExistingVmMacAddress.remove(vmMacAddress);
463                 if (listExistingVmMacAddress.size() > 0) {
464                     tunnelIpElanNameToVmMacCache.put(pair, listExistingVmMacAddress);
465                     return;
466                 }
467                 tunnelIpElanNameToVmMacCache.remove(pair);
468             }
469         }
470     }
471
472     public void removeFromLocalCache(BigInteger designatedDpnId, IpAddress tunnelIp, String elanInstanceName) {
473         Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<IpAddress, String>(tunnelIp, elanInstanceName);
474         List<Pair<IpAddress, String>> tunnelIpElanNameList;
475         tunnelIpElanNameList = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId);
476         if (tunnelIpElanNameList != null) {
477             tunnelIpElanNameList.remove(tunnelIpElanName);
478             if (tunnelIpElanNameList.size() != 0) {
479                 designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameList);
480             } else {
481                 designatedDpnsToTunnelIpElanNameCache.remove(designatedDpnId);
482             }
483         }
484     }
485
486     public void updateVniMacToPortCache(BigInteger vni, String macAddress, Port port) {
487         if (macAddress == null) {
488             return;
489         }
490         Pair<BigInteger, String> vniMacAddressPair = new ImmutablePair<BigInteger, String>(vni, macAddress.toUpperCase());
491         logger.trace("Updating vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}", vni, macAddress.toUpperCase(), vniMacAddressPair, port);
492         vniMacAddressToPortCache.put(vniMacAddressPair, port);
493     }
494
495     public void removeVniMacToPortCache(BigInteger vni, String macAddress) {
496         if (macAddress == null) {
497             return;
498         }
499         Pair<BigInteger, String> vniMacAddressPair = new ImmutablePair<BigInteger, String>(vni, macAddress.toUpperCase());
500         vniMacAddressToPortCache.remove(vniMacAddressPair);
501     }
502
503     public Port readVniMacToPortCache(BigInteger vni, String macAddress) {
504         if (macAddress == null) {
505             return null;
506         }
507         Pair<BigInteger, String> vniMacAddressPair = new ImmutablePair<BigInteger, String>(vni, macAddress.toUpperCase());
508         logger.trace("Reading vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}", vni, macAddress.toUpperCase(), vniMacAddressPair, vniMacAddressToPortCache.get(vniMacAddressPair));
509         return vniMacAddressToPortCache.get(vniMacAddressPair);
510     }
511
512     public String getExternalTunnelInterfaceName(String sourceNode, String dstNode) {
513         String tunnelInterfaceName = null;
514         try {
515             Future<RpcResult<GetExternalTunnelInterfaceNameOutput>> output = itmRpcService
516                     .getExternalTunnelInterfaceName(new GetExternalTunnelInterfaceNameInputBuilder()
517                             .setSourceNode(sourceNode).setDestinationNode(dstNode).build());
518
519             RpcResult<GetExternalTunnelInterfaceNameOutput> rpcResult = output.get();
520             if (rpcResult.isSuccessful()) {
521                 tunnelInterfaceName = rpcResult.getResult().getInterfaceName();
522                 logger.debug("Tunnel interface name: {}", tunnelInterfaceName);
523             } else {
524                 logger.warn("RPC call to ITM.GetExternalTunnelInterfaceName failed with error: {}",
525                         rpcResult.getErrors());
526             }
527         } catch (NullPointerException | InterruptedException | ExecutionException e) {
528             logger.error("Failed to get external tunnel interface name for sourceNode: {} and dstNode: {}: {} ",
529                     sourceNode, dstNode, e);
530         }
531         return tunnelInterfaceName;
532     }
533
534     public static Optional<Node> getNode(DataBroker dataBroker, String physicalSwitchNodeId) {
535         InstanceIdentifier<Node> psNodeId = HwvtepSouthboundUtils
536                 .createInstanceIdentifier(new NodeId(physicalSwitchNodeId));
537         Optional<Node> physicalSwitchOptional = MDSALUtil.read(LogicalDatastoreType.CONFIGURATION, psNodeId, dataBroker);
538         return physicalSwitchOptional;
539     }
540
541     public RemoteMcastMacs createRemoteMcastMac(Node dstDevice, String logicalSwitchName, IpAddress internalTunnelIp) {
542         List<LocatorSet> locators = new ArrayList<>();
543         for (TerminationPoint tp : dstDevice.getTerminationPoint()) {
544             HwvtepPhysicalLocatorAugmentation aug = tp.getAugmentation(HwvtepPhysicalLocatorAugmentation.class);
545             if (internalTunnelIp.getIpv4Address().equals(aug.getDstIp().getIpv4Address())) {
546                 HwvtepPhysicalLocatorRef phyLocRef = new HwvtepPhysicalLocatorRef(
547                         HwvtepSouthboundUtils.createPhysicalLocatorInstanceIdentifier(dstDevice.getNodeId(), aug));
548                 locators.add(new LocatorSetBuilder().setLocatorRef(phyLocRef).build());
549             }
550         }
551         HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils
552                 .createLogicalSwitchesInstanceIdentifier(dstDevice.getNodeId(), new HwvtepNodeName(logicalSwitchName)));
553
554         RemoteMcastMacs remoteUcastMacs = new RemoteMcastMacsBuilder()
555                 .setMacEntryKey(new MacAddress(UNKNOWN_DMAC))
556                 .setLogicalSwitchRef(lsRef).setLocatorSet(locators).build();
557         return remoteUcastMacs;
558     }
559
560     private WriteTransaction putRemoteMcastMac(WriteTransaction transaction, String elanName, L2GatewayDevice device, IpAddress internalTunnelIp) {
561         Optional<Node> optionalNode = getNode(broker, device.getHwvtepNodeId());
562         Node dstNode = optionalNode.get();
563         if (dstNode == null) {
564             logger.debug("could not get device node {} ", device.getHwvtepNodeId());
565             return null;
566         }
567         RemoteMcastMacs macs = createRemoteMcastMac(dstNode, elanName, internalTunnelIp);
568         HwvtepUtils.putRemoteMcastMac(transaction, dstNode.getNodeId(), macs);
569         return transaction;
570     }
571
572     private void installRemoteMcastMac(BigInteger designatedDpnId, L2GatewayDevice device, String elanInstanceName) {
573         String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(designatedDpnId), device.getHwvtepNodeId());
574         IpAddress internalTunnelIp = null;
575         if (tunnelInterfaceName != null) {
576             Interface tunnelInterface = DhcpServiceUtils.getInterfaceFromConfigDS(tunnelInterfaceName, broker);
577             if (tunnelInterface == null) {
578                 logger.debug("Tunnel Interface is not present {}", tunnelInterfaceName);
579                 return;
580             }
581             internalTunnelIp = tunnelInterface.getAugmentation(IfTunnel.class).getTunnelSource();
582             WriteTransaction transaction = broker.newWriteOnlyTransaction();
583             putRemoteMcastMac(transaction, elanInstanceName, device, internalTunnelIp);
584             if (transaction != null) {
585                 transaction.submit();
586             }
587         }
588     }
589
590     private L2GatewayDevice getDeviceFromTunnelIp(String elanInstanceName, IpAddress tunnelIp) {
591         ConcurrentMap<String, L2GatewayDevice> devices = L2GatewayCacheUtils.getCache();
592         for (L2GatewayDevice device : devices.values()) {
593             if (device.getTunnelIp().equals(tunnelIp)) {
594                 return device;
595             }
596         }
597         return null;
598     }
599
600     private boolean isTunnelUp(String nodeName, BigInteger dpn) {
601         boolean isTunnelUp = false;
602         String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), nodeName);
603         if (tunnelInterfaceName == null) {
604             logger.debug("Tunnel Interface is not present {}", tunnelInterfaceName);
605             return isTunnelUp;
606         }
607         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface tunnelInterface = DhcpServiceUtils.getInterfaceFromOperationalDS(tunnelInterfaceName, broker);
608         if (tunnelInterface == null) {
609             logger.debug("Interface {} is not present in interface state", tunnelInterfaceName);
610             return isTunnelUp;
611         }
612         isTunnelUp = (tunnelInterface.getOperStatus() == OperStatus.Up) ? true :false;
613         return isTunnelUp;
614     }
615
616     public void handleTunnelStateUp(IpAddress tunnelIp, BigInteger interfaceDpn) {
617         logger.trace("In handleTunnelStateUp tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn);
618         try {
619             List<Pair<IpAddress, String>> tunnelIpElanPair = designatedDpnsToTunnelIpElanNameCache.get(DHCPMConstants.INVALID_DPID);
620             List<BigInteger> dpns = DhcpServiceUtils.getListOfDpns(broker);
621             if (tunnelIpElanPair == null || tunnelIpElanPair.isEmpty()) {
622                 return;
623             }
624             for (Pair<IpAddress, String> pair : tunnelIpElanPair) {
625                 if (tunnelIp.equals(pair.getLeft())) {
626                     designateDpnId(tunnelIp, pair.getRight(), dpns);
627                 }
628             }
629         } catch (Exception e) {
630             logger.error("Error in handleTunnelStateUp {}", e.getMessage());
631             logger.trace("Exception details {}", e);
632         }
633     }
634
635     private boolean isTunnelConfigured(BigInteger dpn, String hwVtepNodeId) {
636         String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), hwVtepNodeId);
637         if (tunnelInterfaceName == null) {
638             return false;
639         }
640         Interface tunnelInterface = DhcpServiceUtils.getInterfaceFromConfigDS(tunnelInterfaceName, broker);
641         if (tunnelInterface == null) {
642             logger.debug("Tunnel Interface is not present {}", tunnelInterfaceName);
643             return false;
644         }
645         return true;
646     }
647
648     public void unInstallDhcpFlowsForVms(String elanInstanceName, IpAddress tunnelIp, List<BigInteger> dpns) {
649         Pair<IpAddress, String> tunnelIpElanNamePair = new ImmutablePair<IpAddress, String>(tunnelIp, elanInstanceName);
650         Set<String> vmMacs = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair);
651         logger.trace("In unInstallFlowsForVms elanInstanceName {}, tunnelIp {}, dpns {}, vmMacs {}", elanInstanceName, tunnelIp, dpns, vmMacs);
652         if (vmMacs == null) {
653             return;
654         }
655         for (String vmMacAddress : vmMacs) {
656             for (BigInteger dpn : dpns) {
657                 unInstallDhcpEntries(dpn, vmMacAddress, entityOwnershipService);
658             }
659         }
660         tunnelIpElanNameToVmMacCache.remove(tunnelIpElanNamePair);
661     }
662 }