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