2 * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.dhcpservice;
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.HashSet;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.Locale;
20 import java.util.Map.Entry;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.concurrent.CopyOnWriteArraySet;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.Future;
27 import javax.annotation.PostConstruct;
28 import javax.inject.Inject;
29 import javax.inject.Named;
30 import javax.inject.Singleton;
31 import org.apache.commons.lang3.tuple.ImmutablePair;
32 import org.apache.commons.lang3.tuple.Pair;
33 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
34 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
35 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
36 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
37 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
38 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
39 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
40 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
41 import org.opendaylight.genius.mdsalutil.MDSALUtil;
42 import org.opendaylight.genius.mdsalutil.NwConstants;
43 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
44 import org.opendaylight.genius.utils.clustering.EntityOwnershipUtils;
45 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
46 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
47 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
48 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
49 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
50 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService;
51 import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
52 import org.opendaylight.netvirt.dhcpservice.api.IDhcpExternalTunnelManager;
53 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput;
54 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
55 import org.opendaylight.netvirt.elanmanager.api.IElanService;
56 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
57 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayCache;
58 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
59 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetExternalTunnelInterfaceNameInputBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetExternalTunnelInterfaceNameOutput;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.DesignatedSwitchesForExternalTunnels;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelKey;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPort;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepLogicalSwitchRef;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorRef;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsBuilder;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSetBuilder;
89 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
90 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
91 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
92 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
93 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
94 import org.opendaylight.yangtools.yang.common.RpcResult;
95 import org.slf4j.Logger;
96 import org.slf4j.LoggerFactory;
99 public class DhcpExternalTunnelManager implements IDhcpExternalTunnelManager {
101 private static final Logger LOG = LoggerFactory.getLogger(DhcpExternalTunnelManager.class);
102 public static final String UNKNOWN_DMAC = "00:00:00:00:00:00";
104 private final DataBroker broker;
105 private final ManagedNewTransactionRunner txRunner;
106 private final IMdsalApiManager mdsalUtil;
107 private final ItmRpcService itmRpcService;
108 private final EntityOwnershipUtils entityOwnershipUtils;
109 private final IInterfaceManager interfaceManager;
110 private final JobCoordinator jobCoordinator;
111 private final L2GatewayCache l2GatewayCache;
112 private IElanService elanService;
114 private final ConcurrentMap<BigInteger, Set<Pair<IpAddress, String>>> designatedDpnsToTunnelIpElanNameCache =
115 new ConcurrentHashMap<>();
116 private final ConcurrentMap<Pair<IpAddress, String>, Set<String>> tunnelIpElanNameToVmMacCache =
117 new ConcurrentHashMap<>();
118 private final ConcurrentMap<Pair<IpAddress, String>, Set<String>> availableVMCache = new ConcurrentHashMap<>();
119 private final ConcurrentMap<Pair<BigInteger, String>, Port> vniMacAddressToPortCache = new ConcurrentHashMap<>();
122 public ConcurrentMap<BigInteger, Set<Pair<IpAddress, String>>> getDesignatedDpnsToTunnelIpElanNameCache() {
123 return designatedDpnsToTunnelIpElanNameCache;
127 public ConcurrentMap<Pair<IpAddress, String>, Set<String>> getTunnelIpElanNameToVmMacCache() {
128 return tunnelIpElanNameToVmMacCache;
132 public ConcurrentMap<Pair<IpAddress, String>, Set<String>> getAvailableVMCache() {
133 return availableVMCache;
137 public ConcurrentMap<Pair<BigInteger, String>, Port> getVniMacAddressToPortCache() {
138 return vniMacAddressToPortCache;
142 public DhcpExternalTunnelManager(final DataBroker broker,
143 final IMdsalApiManager mdsalUtil, final ItmRpcService itmRpcService,
144 final EntityOwnershipService entityOwnershipService, final IInterfaceManager interfaceManager,
145 final JobCoordinator jobCoordinator, final L2GatewayCache l2GatewayCache,
146 @Named("elanService") IElanService ielanService) {
147 this.broker = broker;
148 this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
149 this.mdsalUtil = mdsalUtil;
150 this.itmRpcService = itmRpcService;
151 this.entityOwnershipUtils = new EntityOwnershipUtils(entityOwnershipService);
152 this.interfaceManager = interfaceManager;
153 this.jobCoordinator = jobCoordinator;
154 this.l2GatewayCache = l2GatewayCache;
155 this.elanService = ielanService;
163 private void initilizeCaches() {
164 LOG.trace("Loading designatedDpnsToTunnelIpElanNameCache");
165 InstanceIdentifier<DesignatedSwitchesForExternalTunnels> instanceIdentifier =
166 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).build();
167 Optional<DesignatedSwitchesForExternalTunnels> designatedSwitchForTunnelOptional =
168 MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
169 if (designatedSwitchForTunnelOptional.isPresent()) {
170 List<DesignatedSwitchForTunnel> list =
171 designatedSwitchForTunnelOptional.get().getDesignatedSwitchForTunnel();
172 for (DesignatedSwitchForTunnel designatedSwitchForTunnel : list) {
173 Set<Pair<IpAddress, String>> setOfTunnelIpElanNamePair =
174 designatedDpnsToTunnelIpElanNameCache
175 .get(BigInteger.valueOf(designatedSwitchForTunnel.getDpId()));
176 if (setOfTunnelIpElanNamePair == null) {
177 setOfTunnelIpElanNamePair = new CopyOnWriteArraySet<>();
179 Pair<IpAddress, String> tunnelIpElanNamePair =
180 new ImmutablePair<>(designatedSwitchForTunnel.getTunnelRemoteIpAddress(),
181 designatedSwitchForTunnel.getElanInstanceName());
182 setOfTunnelIpElanNamePair.add(tunnelIpElanNamePair);
183 designatedDpnsToTunnelIpElanNameCache.put(BigInteger.valueOf(designatedSwitchForTunnel.getDpId()),
184 setOfTunnelIpElanNamePair);
187 LOG.trace("Loading vniMacAddressToPortCache");
188 InstanceIdentifier<Ports> inst = InstanceIdentifier.builder(Neutron.class).child(Ports.class).build();
189 Optional<Ports> optionalPorts = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst);
190 if (optionalPorts.isPresent()) {
191 List<Port> list = optionalPorts.get().getPort();
192 for (Port port : list) {
193 if (NeutronUtils.isPortVnicTypeNormal(port)) {
196 String macAddress = port.getMacAddress().getValue();
197 Uuid networkId = port.getNetworkId();
198 String segmentationId = DhcpServiceUtils.getSegmentationId(networkId, broker);
199 if (segmentationId == null) {
202 updateVniMacToPortCache(new BigInteger(segmentationId), macAddress, port);
207 public BigInteger designateDpnId(IpAddress tunnelIp, String elanInstanceName, List<BigInteger> dpns) {
208 BigInteger designatedDpnId = readDesignatedSwitchesForExternalTunnel(tunnelIp, elanInstanceName);
209 if (designatedDpnId != null && !designatedDpnId.equals(DhcpMConstants.INVALID_DPID)) {
210 LOG.trace("Dpn {} already designated for tunnelIp - elan : {} - {}", designatedDpnId, tunnelIp,
212 return designatedDpnId;
214 return chooseDpn(tunnelIp, elanInstanceName, dpns);
217 public void installDhcpFlowsForVms(final IpAddress tunnelIp, String elanInstanceName, final List<BigInteger> dpns,
218 final BigInteger designatedDpnId, final String vmMacAddress) {
219 LOG.trace("In installDhcpFlowsForVms ipAddress {}, elanInstanceName {}, dpn {}, vmMacAddress {}", tunnelIp,
220 elanInstanceName, designatedDpnId, vmMacAddress);
222 String tunnelIpDpnKey = getTunnelIpDpnKey(tunnelIp, designatedDpnId);
223 jobCoordinator.enqueueJob(getJobKey(tunnelIpDpnKey), () -> {
224 if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
225 HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
226 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
227 dpns.remove(designatedDpnId);
228 for (BigInteger dpn : dpns) {
229 installDhcpDropAction(dpn, vmMacAddress, tx);
231 installDhcpEntries(designatedDpnId, vmMacAddress, tx);
234 LOG.trace("Exiting installDhcpEntries since this cluster node is not the owner for dpn");
237 return Collections.emptyList();
240 updateLocalCache(tunnelIp, elanInstanceName, vmMacAddress);
243 public void installDhcpFlowsForVms(BigInteger designatedDpnId, Set<String> listVmMacAddress, WriteTransaction tx) {
244 for (String vmMacAddress : listVmMacAddress) {
245 installDhcpEntries(designatedDpnId, vmMacAddress, tx);
249 public void unInstallDhcpFlowsForVms(String elanInstanceName, List<BigInteger> dpns, String vmMacAddress) {
250 unInstallDhcpEntriesOnDpns(dpns, vmMacAddress);
251 removeFromLocalCache(elanInstanceName, vmMacAddress);
254 public void unInstallDhcpFlowsForVms(String elanInstanceName, IpAddress tunnelIp, List<BigInteger> dpns) {
255 Pair<IpAddress, String> tunnelIpElanNamePair = new ImmutablePair<>(tunnelIp, elanInstanceName);
256 Set<String> vmMacs = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair);
257 LOG.trace("In unInstallFlowsForVms elanInstanceName {}, tunnelIp {}, dpns {}, vmMacs {}",
258 elanInstanceName, tunnelIp, dpns, vmMacs);
259 if (vmMacs == null) {
262 for (String vmMacAddress : vmMacs) {
263 unInstallDhcpEntriesOnDpns(dpns, vmMacAddress);
265 tunnelIpElanNameToVmMacCache.remove(tunnelIpElanNamePair);
268 public BigInteger readDesignatedSwitchesForExternalTunnel(IpAddress tunnelIp, String elanInstanceName) {
269 if (tunnelIp == null || elanInstanceName == null || elanInstanceName.isEmpty()) {
272 InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier =
273 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class)
274 .child(DesignatedSwitchForTunnel.class,
275 new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp)).build();
276 Optional<DesignatedSwitchForTunnel> designatedSwitchForTunnelOptional =
277 MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
278 if (designatedSwitchForTunnelOptional.isPresent()) {
279 return BigInteger.valueOf(designatedSwitchForTunnelOptional.get().getDpId());
284 public void writeDesignatedSwitchForExternalTunnel(BigInteger dpnId, IpAddress tunnelIp,
285 String elanInstanceName) {
286 DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey =
287 new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp);
288 InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier =
289 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class)
290 .child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build();
291 DesignatedSwitchForTunnel designatedSwitchForTunnel =
292 new DesignatedSwitchForTunnelBuilder().setDpId(dpnId.longValue())
293 .setElanInstanceName(elanInstanceName).setTunnelRemoteIpAddress(tunnelIp)
294 .setKey(designatedSwitchForTunnelKey).build();
295 LOG.trace("Writing into CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp, elanInstanceName,
297 MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier, designatedSwitchForTunnel);
298 updateLocalCache(dpnId, tunnelIp, elanInstanceName);
301 public void removeDesignatedSwitchForExternalTunnel(BigInteger dpnId, IpAddress tunnelIp,
302 String elanInstanceName) {
303 DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey =
304 new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp);
305 InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier =
306 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class)
307 .child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build();
308 LOG.trace("Removing from CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp,
309 elanInstanceName, dpnId);
310 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
311 removeFromLocalCache(dpnId, tunnelIp, elanInstanceName);
314 // This method is called whenever new OVS Switch is added.
315 public void installDhcpDropActionOnDpn(BigInteger dpId) {
316 // During controller restart we'll get add for designatedDpns as well and we
317 // need not install drop flows for those dpns
318 if (designatedDpnsToTunnelIpElanNameCache.get(dpId) != null) {
319 LOG.trace("The dpn {} is designated DPN need not install drop flows");
322 // Read from DS since the cache may not get loaded completely in restart scenario
323 if (isDpnDesignatedDpn(dpId)) {
324 LOG.trace("The dpn {} is designated DPN need not install drop flows");
327 List<String> vmMacs = getAllVmMacs();
328 LOG.trace("Installing drop actions to this new DPN {} VMs {}", dpId, vmMacs);
329 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
330 for (String vmMacAddress : vmMacs) {
331 installDhcpDropAction(dpId, vmMacAddress, tx);
333 }), LOG, "Error writing to the datastore");
336 private boolean isDpnDesignatedDpn(BigInteger dpId) {
337 InstanceIdentifier<DesignatedSwitchesForExternalTunnels> instanceIdentifier =
338 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).build();
339 Optional<DesignatedSwitchesForExternalTunnels> designatedSwitchForTunnelOptional =
340 MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
341 if (designatedSwitchForTunnelOptional.isPresent()) {
342 List<DesignatedSwitchForTunnel> list =
343 designatedSwitchForTunnelOptional.get().getDesignatedSwitchForTunnel();
344 for (DesignatedSwitchForTunnel designatedSwitchForTunnel : list) {
345 if (dpId.equals(BigInteger.valueOf(designatedSwitchForTunnel.getDpId()))) {
353 private List<String> getAllVmMacs() {
354 List<String> vmMacs = new LinkedList<>();
355 Collection<Set<String>> listOfVmMacs = tunnelIpElanNameToVmMacCache.values();
356 for (Set<String> list : listOfVmMacs) {
362 public void updateLocalCache(BigInteger designatedDpnId, IpAddress tunnelIp, String elanInstanceName) {
363 Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
364 Set<Pair<IpAddress, String>> tunnelIpElanNameSet;
365 tunnelIpElanNameSet = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId);
366 if (tunnelIpElanNameSet == null) {
367 tunnelIpElanNameSet = new CopyOnWriteArraySet<>();
369 tunnelIpElanNameSet.add(tunnelIpElanName);
370 LOG.trace("Updating designatedDpnsToTunnelIpElanNameCache for designatedDpn {} value {}", designatedDpnId,
371 tunnelIpElanNameSet);
372 designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameSet);
375 public void updateLocalCache(IpAddress tunnelIp, String elanInstanceName, String vmMacAddress) {
376 Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
377 Set<String> setOfExistingVmMacAddress;
378 setOfExistingVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanName);
379 if (setOfExistingVmMacAddress == null) {
380 setOfExistingVmMacAddress = new CopyOnWriteArraySet<>();
382 setOfExistingVmMacAddress.add(vmMacAddress);
383 LOG.trace("Updating tunnelIpElanNameToVmMacCache for tunnelIpElanName {} value {}", tunnelIpElanName,
384 setOfExistingVmMacAddress);
385 tunnelIpElanNameToVmMacCache.put(tunnelIpElanName, setOfExistingVmMacAddress);
386 updateExistingVMTunnelIPCache(tunnelIp, elanInstanceName, vmMacAddress);
389 public void updateExistingVMTunnelIPCache(IpAddress tunnelIp, String elanInstanceName, String vmMacAddress) {
390 Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
391 Set<String> listExistingVmMacAddress;
392 listExistingVmMacAddress = availableVMCache.get(tunnelIpElanName);
393 if (listExistingVmMacAddress == null) {
394 listExistingVmMacAddress = new CopyOnWriteArraySet<>();
396 listExistingVmMacAddress.add(vmMacAddress);
397 LOG.trace("Updating availableVMCache for tunnelIpElanName {} value {}", tunnelIpElanName,
398 listExistingVmMacAddress);
399 availableVMCache.put(tunnelIpElanName, listExistingVmMacAddress);
402 public void handleDesignatedDpnDown(BigInteger dpnId, List<BigInteger> listOfDpns) {
403 LOG.trace("In handleDesignatedDpnDown dpnId {}, listOfDpns {}", dpnId, listOfDpns);
404 Set<Pair<IpAddress, String>> setOfTunnelIpElanNamePairs = designatedDpnsToTunnelIpElanNameCache.get(dpnId);
405 if (setOfTunnelIpElanNamePairs == null || setOfTunnelIpElanNamePairs.isEmpty()) {
406 LOG.trace("No tunnelIpElanName to handle for dpn {}. Returning", dpnId);
408 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
409 if (!dpnId.equals(DhcpMConstants.INVALID_DPID)) {
410 List<String> listOfVms = getAllVmMacs();
411 for (String vmMacAddress : listOfVms) {
412 unInstallDhcpEntries(dpnId, vmMacAddress, tx);
415 for (Pair<IpAddress, String> pair : setOfTunnelIpElanNamePairs) {
416 updateCacheAndInstallNewFlows(listOfDpns, pair, tx);
418 }), LOG, "Error writing to datastore");
422 public void updateCacheAndInstallNewFlows(List<BigInteger> listOfDpns, Pair<IpAddress, String> pair,
423 WriteTransaction tx) {
424 BigInteger newDesignatedDpn = chooseDpn(pair.getLeft(), pair.getRight(), listOfDpns);
425 if (newDesignatedDpn.equals(DhcpMConstants.INVALID_DPID)) {
428 Set<String> setOfVmMacs = tunnelIpElanNameToVmMacCache.get(pair);
429 if (setOfVmMacs != null && !setOfVmMacs.isEmpty()) {
430 LOG.trace("Updating DHCP flows for VMs {} with new designated DPN {}", setOfVmMacs, newDesignatedDpn);
431 installDhcpFlowsForVms(newDesignatedDpn, setOfVmMacs, tx);
433 java.util.Optional<SubnetToDhcpPort> subnetDhcpData = getSubnetDhcpPortData(pair.getRight());
434 if (subnetDhcpData.isPresent()) {
435 configureDhcpArpRequestResponseFlow(newDesignatedDpn, pair.getRight(), true,
436 pair.getLeft(), subnetDhcpData.get().getPortFixedip(), subnetDhcpData.get().getPortMacaddress());
440 private void changeExistingFlowToDrop(Pair<IpAddress, String> tunnelIpElanNamePair, BigInteger dpnId,
441 WriteTransaction tx) {
442 Set<String> setOfVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair);
443 if (setOfVmMacAddress == null || setOfVmMacAddress.isEmpty()) {
446 for (String vmMacAddress : setOfVmMacAddress) {
447 installDhcpDropAction(dpnId, vmMacAddress, tx);
452 * Choose a dpn among the list of elanDpns such that it has lowest count of being the designated dpn.
453 * @param tunnelIp The tunnel Ip address
454 * @param elanInstanceName The elan instance name
455 * @param dpns The data path nodes
456 * @return The designated dpn
458 private BigInteger chooseDpn(IpAddress tunnelIp, String elanInstanceName,
459 List<BigInteger> dpns) {
460 BigInteger designatedDpnId = DhcpMConstants.INVALID_DPID;
461 if (dpns != null && dpns.size() != 0) {
462 List<BigInteger> candidateDpns = DhcpServiceUtils.getDpnsForElan(elanInstanceName, broker);
463 candidateDpns.retainAll(dpns);
464 LOG.trace("Choosing new dpn for tunnelIp {}, elanInstanceName {}, among elanDpns {}",
465 tunnelIp, elanInstanceName, candidateDpns);
466 boolean elanDpnAvailableFlag = true;
467 if (candidateDpns.isEmpty()) {
468 candidateDpns = dpns;
469 elanDpnAvailableFlag = false;
472 L2GatewayDevice device = getDeviceFromTunnelIp(tunnelIp);
473 if (device == null) {
474 LOG.trace("Could not find any device for elanInstanceName {} and tunnelIp {}",
475 elanInstanceName, tunnelIp);
476 handleUnableToDesignateDpn(tunnelIp, elanInstanceName);
477 return designatedDpnId;
479 for (BigInteger dpn : candidateDpns) {
480 String hwvtepNodeId = device.getHwvtepNodeId();
481 if (!elanDpnAvailableFlag) {
482 if (!isTunnelConfigured(dpn, hwvtepNodeId)) {
483 LOG.trace("Tunnel is not configured on dpn {} to TOR {}", dpn, hwvtepNodeId);
486 } else if (!isTunnelUp(hwvtepNodeId, dpn)) {
487 LOG.trace("Tunnel is not up between dpn {} and TOR {}", dpn, hwvtepNodeId);
490 Set<Pair<IpAddress, String>> tunnelIpElanNameSet = designatedDpnsToTunnelIpElanNameCache.get(dpn);
491 if (tunnelIpElanNameSet == null) {
492 designatedDpnId = dpn;
495 if (size == 0 || tunnelIpElanNameSet.size() < size) {
496 size = tunnelIpElanNameSet.size();
497 designatedDpnId = dpn;
500 writeDesignatedSwitchForExternalTunnel(designatedDpnId, tunnelIp, elanInstanceName);
501 return designatedDpnId;
503 handleUnableToDesignateDpn(tunnelIp, elanInstanceName);
504 return designatedDpnId;
507 private void handleUnableToDesignateDpn(IpAddress tunnelIp, String elanInstanceName) {
508 writeDesignatedSwitchForExternalTunnel(DhcpMConstants.INVALID_DPID, tunnelIp, elanInstanceName);
511 private void installDhcpEntries(BigInteger dpnId, String vmMacAddress, WriteTransaction tx) {
512 DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL,
513 vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil, tx);
516 public void addOrRemoveDhcpArpFlowforElan(String elanInstanceName, boolean addFlow, String dhcpIpAddress,
517 String dhcpMacAddress) {
518 LOG.trace("Configure DHCP SR-IOV Arp flows for Elan {} dpns .", elanInstanceName);
519 for (Entry<BigInteger, Set<Pair<IpAddress,String>>> entry : designatedDpnsToTunnelIpElanNameCache.entrySet()) {
520 BigInteger dpn = entry.getKey();
521 Set<Pair<IpAddress,String>> tunnelIpElanNameSet = entry.getValue();
522 for (Pair<IpAddress, String> pair : tunnelIpElanNameSet) {
523 if (pair.getRight().equalsIgnoreCase(elanInstanceName)) {
525 LOG.trace("Adding SR-IOV DHCP Arp Flows for Elan {} and tunnelIp {}",
526 elanInstanceName, pair.getLeft());
527 configureDhcpArpRequestResponseFlow(dpn, elanInstanceName, true,
528 pair.getLeft(), dhcpIpAddress, dhcpMacAddress);
530 LOG.trace("Deleting SR-IOV DHCP Arp Flows for Elan {} and tunnelIp {}",
531 elanInstanceName, pair.getLeft());
532 configureDhcpArpRequestResponseFlow(dpn, elanInstanceName, false,
533 pair.getLeft(), dhcpIpAddress, dhcpMacAddress);
541 public void configureDhcpArpRequestResponseFlow(BigInteger dpnId, String elanInstanceName, boolean addFlow,
542 IpAddress tunnelIp, String dhcpIpAddress, String dhcpMacAddress) {
543 L2GatewayDevice device = getDeviceFromTunnelIp(tunnelIp);
544 if (device == null) {
545 LOG.error("Unable to get L2Device for tunnelIp {} and elanInstanceName {}", tunnelIp,
548 jobCoordinator.enqueueJob(getJobKey(elanInstanceName), () -> {
549 if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
550 HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
551 String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpnId),
552 device.getHwvtepNodeId());
553 int lportTag = interfaceManager.getInterfaceInfo(tunnelInterfaceName).getInterfaceTag();
554 InstanceIdentifier<ElanInstance> elanIdentifier = InstanceIdentifier.builder(ElanInstances.class)
555 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
556 Optional<ElanInstance> optElan = MDSALUtil.read(broker,
557 LogicalDatastoreType.CONFIGURATION, elanIdentifier);
558 if (optElan.isPresent()) {
559 LOG.trace("Configuring the SR-IOV Arp request/response flows for LPort {} ElanTag {}.",
560 lportTag, optElan.get().getElanTag());
561 Uuid nwUuid = new Uuid(elanInstanceName);
562 String strVni = DhcpServiceUtils.getSegmentationId(nwUuid, broker);
563 BigInteger vni = strVni != null ? new BigInteger(strVni) : BigInteger.ZERO;
564 if (!vni.equals(BigInteger.ZERO)) {
566 LOG.trace("Installing the SR-IOV DHCP Arp flow for DPN {} Port Ip {}, Lport {}.",
567 dpnId, dhcpIpAddress, lportTag);
568 installDhcpArpRequestFlows(dpnId, vni, dhcpIpAddress, lportTag,
569 optElan.get().getElanTag());
570 installDhcpArpResponderFlows(dpnId, tunnelInterfaceName, lportTag, elanInstanceName,
571 dhcpIpAddress, dhcpMacAddress);
573 LOG.trace("Uninstalling the SR-IOV DHCP Arp flows for DPN {} Port Ip {}, Lport {}.",
574 dpnId, dhcpIpAddress, lportTag);
575 uninstallDhcpArpRequestFlows(dpnId, vni, dhcpIpAddress, lportTag);
576 uninstallDhcpArpResponderFlows(dpnId, tunnelInterfaceName, lportTag, dhcpIpAddress);
585 public java.util.Optional<SubnetToDhcpPort> getSubnetDhcpPortData(String elanInstanceName) {
586 java.util.Optional<SubnetToDhcpPort> optSubnetDhcp = java.util.Optional.empty();
587 Uuid nwUuid = new Uuid(elanInstanceName);
588 List<Uuid> subnets = DhcpServiceUtils.getSubnetIdsFromNetworkId(broker, nwUuid);
589 for (Uuid subnet : subnets) {
590 if (DhcpServiceUtils.isIpv4Subnet(broker, subnet)) {
591 optSubnetDhcp = DhcpServiceUtils.getSubnetDhcpPortData(broker, subnet.getValue());
592 return optSubnetDhcp;
595 return optSubnetDhcp;
598 private void installDhcpArpRequestFlows(BigInteger dpnId, BigInteger vni, String dhcpIpAddress,
599 int lportTag, Long elanTag) {
600 DhcpServiceUtils.setupDhcpArpRequest(dpnId, NwConstants.EXTERNAL_TUNNEL_TABLE, vni, dhcpIpAddress,
601 lportTag, elanTag, true, mdsalUtil);
604 private void installDhcpArpResponderFlows(BigInteger dpnId, String interfaceName, int lportTag,
605 String elanInstanceName, String dhcpIpAddress, String dhcpMacAddress) {
606 LOG.trace("Adding SR-IOV DHCP ArpResponder for elan {} Lport {} Port Ip {}.",
607 elanInstanceName, lportTag, dhcpIpAddress);
608 ArpResponderInput.ArpReponderInputBuilder builder = new ArpResponderInput.ArpReponderInputBuilder();
609 builder.setDpId(dpnId).setInterfaceName(interfaceName).setSpa(dhcpIpAddress).setSha(dhcpMacAddress)
610 .setLportTag(lportTag);
611 builder.setInstructions(ArpResponderUtil.getInterfaceInstructions(interfaceManager, interfaceName,
612 dhcpIpAddress, dhcpMacAddress));
613 elanService.addExternalTunnelArpResponderFlow(builder.buildForInstallFlow(), elanInstanceName);
616 private void uninstallDhcpArpResponderFlows(BigInteger dpnId, String interfaceName, int lportTag,
617 String dhcpIpAddress) {
618 LOG.trace("Removing SR-IOV DHCP ArpResponder flow for interface {} on DPN {}", interfaceName, dpnId);
619 ArpResponderInput arpInput = new ArpResponderInput.ArpReponderInputBuilder().setDpId(dpnId)
620 .setInterfaceName(interfaceName).setSpa(dhcpIpAddress)
621 .setLportTag(lportTag).buildForRemoveFlow();
622 elanService.removeArpResponderFlow(arpInput);
625 private void uninstallDhcpArpRequestFlows(BigInteger dpnId, BigInteger vni, String dhcpIpAddress,
627 DhcpServiceUtils.setupDhcpArpRequest(dpnId, NwConstants.EXTERNAL_TUNNEL_TABLE, vni, dhcpIpAddress,
628 lportTag, null, false, mdsalUtil);
632 public void unInstallDhcpEntries(BigInteger dpnId, String vmMacAddress, WriteTransaction tx) {
633 DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL,
634 vmMacAddress, NwConstants.DEL_FLOW, mdsalUtil, tx);
637 private void installDhcpDropAction(BigInteger dpn, String vmMacAddress, WriteTransaction tx) {
638 DhcpServiceUtils.setupDhcpDropAction(dpn, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL,
639 vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil, tx);
642 public List<ListenableFuture<Void>> handleTunnelStateDown(IpAddress tunnelIp, BigInteger interfaceDpn) {
643 LOG.trace("In handleTunnelStateDown tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn);
644 if (interfaceDpn == null) {
645 return Collections.emptyList();
647 synchronized (getTunnelIpDpnKey(tunnelIp, interfaceDpn)) {
648 Set<Pair<IpAddress, String>> tunnelElanPairSet =
649 designatedDpnsToTunnelIpElanNameCache.get(interfaceDpn);
650 if (tunnelElanPairSet == null || tunnelElanPairSet.isEmpty()) {
651 return Collections.emptyList();
653 for (Pair<IpAddress, String> tunnelElanPair : tunnelElanPairSet) {
654 IpAddress tunnelIpInDpn = tunnelElanPair.getLeft();
655 if (tunnelIpInDpn.equals(tunnelIp)) {
656 if (!checkL2GatewayConnection(tunnelElanPair)) {
657 LOG.trace("Couldn't find device for given tunnelIpElanPair {} in L2GwConnCache",
659 return Collections.emptyList();
663 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
664 for (Pair<IpAddress, String> tunnelElanPair : tunnelElanPairSet) {
665 IpAddress tunnelIpInDpn = tunnelElanPair.getLeft();
666 String elanInstanceName = tunnelElanPair.getRight();
667 if (tunnelIpInDpn.equals(tunnelIp)) {
668 if (!checkL2GatewayConnection(tunnelElanPair)) {
669 LOG.trace("Couldn't find device for given tunnelIpElanPair {} in L2GwConnCache",
672 List<BigInteger> dpns = DhcpServiceUtils.getListOfDpns(broker);
673 dpns.remove(interfaceDpn);
674 changeExistingFlowToDrop(tunnelElanPair, interfaceDpn, tx);
675 java.util.Optional<SubnetToDhcpPort> subnetDhcpData = getSubnetDhcpPortData(elanInstanceName);
676 if (subnetDhcpData.isPresent()) {
677 configureDhcpArpRequestResponseFlow(interfaceDpn, elanInstanceName, false,
678 tunnelIpInDpn, subnetDhcpData.get().getPortFixedip(),
679 subnetDhcpData.get().getPortMacaddress());
681 updateCacheAndInstallNewFlows(dpns, tunnelElanPair, tx);
688 private boolean checkL2GatewayConnection(Pair<IpAddress, String> tunnelElanPair) {
689 ConcurrentMap<String, L2GatewayDevice> l2GwDevices =
690 ElanL2GwCacheUtils.getInvolvedL2GwDevices(tunnelElanPair.getRight());
691 for (L2GatewayDevice device : l2GwDevices.values()) {
692 if (device.getTunnelIp().equals(tunnelElanPair.getLeft())) {
699 private String getTunnelIpDpnKey(IpAddress tunnelIp, BigInteger interfaceDpn) {
700 return tunnelIp.toString() + interfaceDpn;
703 private void removeFromLocalCache(String elanInstanceName, String vmMacAddress) {
704 for (Entry<Pair<IpAddress, String>, Set<String>> entry : tunnelIpElanNameToVmMacCache.entrySet()) {
705 Pair<IpAddress, String> pair = entry.getKey();
706 if (pair.getRight().trim().equalsIgnoreCase(elanInstanceName.trim())) {
707 Set<String> setOfExistingVmMacAddress = entry.getValue();
708 if (setOfExistingVmMacAddress == null || setOfExistingVmMacAddress.isEmpty()) {
711 LOG.trace("Removing vmMacAddress {} from listOfMacs {} for elanInstanceName {}", vmMacAddress,
712 setOfExistingVmMacAddress, elanInstanceName);
713 setOfExistingVmMacAddress.remove(vmMacAddress);
714 if (setOfExistingVmMacAddress.size() > 0) {
715 tunnelIpElanNameToVmMacCache.put(pair, setOfExistingVmMacAddress);
718 tunnelIpElanNameToVmMacCache.remove(pair);
723 public void removeFromLocalCache(BigInteger designatedDpnId, IpAddress tunnelIp, String elanInstanceName) {
724 Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
725 Set<Pair<IpAddress, String>> tunnelIpElanNameSet;
726 tunnelIpElanNameSet = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId);
727 if (tunnelIpElanNameSet != null) {
728 LOG.trace("Removing tunnelIpElan {} from designatedDpnsToTunnelIpElanNameCache. Existing list {} for "
729 + "designatedDpnId {}",
730 tunnelIpElanName, tunnelIpElanNameSet, designatedDpnId);
731 tunnelIpElanNameSet.remove(tunnelIpElanName);
732 if (tunnelIpElanNameSet.size() != 0) {
733 designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameSet);
735 designatedDpnsToTunnelIpElanNameCache.remove(designatedDpnId);
740 public void updateVniMacToPortCache(BigInteger vni, String macAddress, Port port) {
741 if (macAddress == null) {
744 Pair<BigInteger, String> vniMacAddressPair = new ImmutablePair<>(
745 vni, macAddress.toUpperCase(Locale.getDefault()));
746 LOG.trace("Updating vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}", vni,
747 macAddress.toUpperCase(Locale.getDefault()), vniMacAddressPair, port);
748 vniMacAddressToPortCache.put(vniMacAddressPair, port);
751 public void removeVniMacToPortCache(BigInteger vni, String macAddress) {
752 if (macAddress == null) {
755 Pair<BigInteger, String> vniMacAddressPair = new ImmutablePair<>(
756 vni, macAddress.toUpperCase(Locale.getDefault()));
757 vniMacAddressToPortCache.remove(vniMacAddressPair);
760 public Port readVniMacToPortCache(BigInteger vni, String macAddress) {
761 if (macAddress == null) {
764 Pair<BigInteger, String> vniMacAddressPair = new ImmutablePair<>(
765 vni, macAddress.toUpperCase(Locale.getDefault()));
766 LOG.trace("Reading vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}",
767 vni, macAddress.toUpperCase(Locale.getDefault()), vniMacAddressPair,
768 vniMacAddressToPortCache.get(vniMacAddressPair));
769 return vniMacAddressToPortCache.get(vniMacAddressPair);
772 public String getExternalTunnelInterfaceName(String sourceNode, String dstNode) {
773 String tunnelInterfaceName = null;
774 Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
776 Future<RpcResult<GetExternalTunnelInterfaceNameOutput>> output = itmRpcService
777 .getExternalTunnelInterfaceName(new GetExternalTunnelInterfaceNameInputBuilder()
778 .setSourceNode(sourceNode).setDestinationNode(dstNode).setTunnelType(tunType).build());
780 RpcResult<GetExternalTunnelInterfaceNameOutput> rpcResult = output.get();
781 if (rpcResult.isSuccessful()) {
782 tunnelInterfaceName = rpcResult.getResult().getInterfaceName();
783 LOG.trace("Tunnel interface name: {}", tunnelInterfaceName);
785 LOG.warn("RPC call to ITM.GetExternalTunnelInterfaceName failed with error: {}", rpcResult.getErrors());
787 } catch (NullPointerException | InterruptedException | ExecutionException e) {
788 LOG.error("Failed to get external tunnel interface name for sourceNode: {} and dstNode: {}",
789 sourceNode, dstNode, e);
791 return tunnelInterfaceName;
794 public static Optional<Node> getNode(DataBroker dataBroker, String physicalSwitchNodeId) {
795 InstanceIdentifier<Node> psNodeId = HwvtepSouthboundUtils
796 .createInstanceIdentifier(new NodeId(physicalSwitchNodeId));
797 return MDSALUtil.read(LogicalDatastoreType.CONFIGURATION, psNodeId, dataBroker);
800 public RemoteMcastMacs createRemoteMcastMac(Node dstDevice, String logicalSwitchName, IpAddress internalTunnelIp) {
801 Set<LocatorSet> locators = new HashSet<>();
802 TerminationPointKey terminationPointKey = HwvtepSouthboundUtils.getTerminationPointKey(
803 internalTunnelIp.getIpv4Address().getValue());
804 HwvtepPhysicalLocatorRef phyLocRef = new HwvtepPhysicalLocatorRef(
805 HwvtepSouthboundUtils.createInstanceIdentifier(dstDevice.getNodeId()).child(TerminationPoint.class,
806 terminationPointKey));
807 locators.add(new LocatorSetBuilder().setLocatorRef(phyLocRef).build());
809 HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils
810 .createLogicalSwitchesInstanceIdentifier(dstDevice.getNodeId(), new HwvtepNodeName(logicalSwitchName)));
812 RemoteMcastMacs remoteMcastMacs = new RemoteMcastMacsBuilder()
813 .setMacEntryKey(new MacAddress(UNKNOWN_DMAC))
814 .setLogicalSwitchRef(lsRef).build();
815 InstanceIdentifier<RemoteMcastMacs> iid = HwvtepSouthboundUtils.createRemoteMcastMacsInstanceIdentifier(
816 dstDevice.getNodeId(), remoteMcastMacs.getKey());
817 ReadOnlyTransaction transaction = broker.newReadOnlyTransaction();
819 //TODO do async mdsal read
820 remoteMcastMacs = transaction.read(LogicalDatastoreType.CONFIGURATION, iid).checkedGet().get();
821 locators.addAll(remoteMcastMacs.getLocatorSet());
822 return new RemoteMcastMacsBuilder(remoteMcastMacs).setLocatorSet(new ArrayList<>(locators)).build();
823 } catch (ReadFailedException e) {
824 LOG.error("Failed to read the macs {}", iid);
829 private WriteTransaction putRemoteMcastMac(WriteTransaction transaction, String elanName,
830 L2GatewayDevice device, IpAddress internalTunnelIp) {
831 Optional<Node> optionalNode = getNode(broker, device.getHwvtepNodeId());
832 Node dstNode = optionalNode.get();
833 if (dstNode == null) {
834 LOG.trace("could not get device node {} ", device.getHwvtepNodeId());
837 RemoteMcastMacs macs = createRemoteMcastMac(dstNode, elanName, internalTunnelIp);
838 HwvtepUtils.putRemoteMcastMac(transaction, dstNode.getNodeId(), macs);
842 public void installRemoteMcastMac(final BigInteger designatedDpnId, final IpAddress tunnelIp,
843 final String elanInstanceName) {
844 if (designatedDpnId.equals(DhcpMConstants.INVALID_DPID)) {
848 jobCoordinator.enqueueJob(getJobKey(elanInstanceName), () -> {
849 if (!entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
850 HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
851 LOG.info("Installing remote McastMac is not executed for this node.");
852 return Collections.emptyList();
855 LOG.info("Installing remote McastMac");
856 L2GatewayDevice device = getDeviceFromTunnelIp(tunnelIp);
857 if (device == null) {
858 LOG.error("Unable to get L2Device for tunnelIp {} and elanInstanceName {}", tunnelIp,
860 return Collections.emptyList();
862 String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(designatedDpnId),
863 device.getHwvtepNodeId());
864 if (tunnelInterfaceName != null) {
865 Interface tunnelInterface =
866 interfaceManager.getInterfaceInfoFromConfigDataStore(tunnelInterfaceName);
867 if (tunnelInterface == null) {
868 LOG.trace("Tunnel Interface is not present {}", tunnelInterfaceName);
869 return Collections.emptyList();
871 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(
872 tx -> putRemoteMcastMac(tx, elanInstanceName, device,
873 tunnelInterface.getAugmentation(IfTunnel.class).getTunnelSource())));
875 return Collections.emptyList();
879 private L2GatewayDevice getDeviceFromTunnelIp(IpAddress tunnelIp) {
880 Collection<L2GatewayDevice> devices = l2GatewayCache.getAll();
881 LOG.trace("In getDeviceFromTunnelIp devices {}", devices);
882 for (L2GatewayDevice device : devices) {
883 if (tunnelIp.equals(device.getTunnelIp())) {
890 private boolean isTunnelUp(String nodeName, BigInteger dpn) {
891 String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), nodeName);
892 if (tunnelInterfaceName == null) {
893 LOG.trace("Tunnel Interface is not present on node {} with dpn {}", nodeName, dpn);
896 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
897 .Interface tunnelInterface =
898 DhcpServiceUtils.getInterfaceFromOperationalDS(tunnelInterfaceName, broker);
899 if (tunnelInterface == null) {
900 LOG.trace("Interface {} is not present in interface state", tunnelInterfaceName);
903 return tunnelInterface.getOperStatus() == OperStatus.Up;
906 public List<ListenableFuture<Void>> handleTunnelStateUp(IpAddress tunnelIp, BigInteger interfaceDpn) {
907 LOG.trace("In handleTunnelStateUp tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn);
908 synchronized (getTunnelIpDpnKey(tunnelIp, interfaceDpn)) {
909 Set<Pair<IpAddress, String>> tunnelIpElanPair =
910 designatedDpnsToTunnelIpElanNameCache.get(DhcpMConstants.INVALID_DPID);
911 List<BigInteger> dpns = DhcpServiceUtils.getListOfDpns(broker);
912 if (tunnelIpElanPair == null || tunnelIpElanPair.isEmpty()) {
913 LOG.trace("There are no undesignated DPNs");
914 return Collections.emptyList();
916 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
917 for (Pair<IpAddress, String> pair : tunnelIpElanPair) {
918 if (tunnelIp.equals(pair.getLeft())) {
919 String elanInstanceName = pair.getRight();
920 BigInteger newDesignatedDpn = designateDpnId(tunnelIp, elanInstanceName, dpns);
921 if (newDesignatedDpn != null && !newDesignatedDpn.equals(DhcpMConstants.INVALID_DPID)) {
922 Set<String> vmMacAddress = tunnelIpElanNameToVmMacCache.get(pair);
923 if (vmMacAddress != null && !vmMacAddress.isEmpty()) {
924 LOG.trace("Updating DHCP flow for macAddress {} with newDpn {}",
925 vmMacAddress, newDesignatedDpn);
926 installDhcpFlowsForVms(newDesignatedDpn, vmMacAddress, tx);
929 java.util.Optional<SubnetToDhcpPort> subnetDhcpData = getSubnetDhcpPortData(elanInstanceName);
930 if (subnetDhcpData.isPresent()) {
931 configureDhcpArpRequestResponseFlow(newDesignatedDpn, elanInstanceName,
932 true, tunnelIp, subnetDhcpData.get().getPortFixedip(),
933 subnetDhcpData.get().getPortMacaddress());
941 private boolean isTunnelConfigured(BigInteger dpn, String hwVtepNodeId) {
942 String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), hwVtepNodeId);
943 if (tunnelInterfaceName == null) {
946 Interface tunnelInterface = interfaceManager.getInterfaceInfoFromConfigDataStore(tunnelInterfaceName);
947 if (tunnelInterface == null) {
948 LOG.trace("Tunnel Interface is not present {}", tunnelInterfaceName);
954 public void removeFromAvailableCache(Pair<IpAddress, String> tunnelIpElanName) {
955 availableVMCache.remove(tunnelIpElanName);
958 private void unInstallDhcpEntriesOnDpns(final List<BigInteger> dpns, final String vmMacAddress) {
959 jobCoordinator.enqueueJob(getJobKey(vmMacAddress), () -> {
960 if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
961 HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
962 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
963 for (final BigInteger dpn : dpns) {
964 unInstallDhcpEntries(dpn, vmMacAddress, tx);
968 LOG.trace("Exiting unInstallDhcpEntries since this cluster node is not the owner for dpn");
971 return Collections.emptyList();
975 public IpAddress getTunnelIpBasedOnElan(String elanInstanceName, String vmMacAddress) {
976 LOG.trace("DhcpExternalTunnelManager getTunnelIpBasedOnElan elanInstanceName {}", elanInstanceName);
977 IpAddress tunnelIp = null;
978 for (Entry<Pair<IpAddress, String>, Set<String>> entry : availableVMCache.entrySet()) {
979 Pair<IpAddress, String> pair = entry.getKey();
980 LOG.trace("DhcpExternalTunnelManager getTunnelIpBasedOnElan left {} right {}", pair.getLeft(),
982 if (pair.getRight().trim().equalsIgnoreCase(elanInstanceName.trim())) {
983 Set<String> listExistingVmMacAddress = entry.getValue();
984 if (listExistingVmMacAddress != null && !listExistingVmMacAddress.isEmpty()
985 && listExistingVmMacAddress.contains(vmMacAddress)) {
986 tunnelIp = pair.getLeft();
991 LOG.trace("DhcpExternalTunnelManager getTunnelIpBasedOnElan returned tunnelIP {}", tunnelIp);
995 private String getJobKey(final String jobKeySuffix) {
996 return DhcpMConstants.DHCP_JOB_KEY_PREFIX + jobKeySuffix;