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