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 static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.HashSet;
18 import java.util.LinkedList;
19 import java.util.List;
20 import java.util.Locale;
21 import java.util.Map.Entry;
22 import java.util.Objects;
23 import java.util.Optional;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentMap;
27 import java.util.concurrent.CopyOnWriteArraySet;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.Future;
30 import javax.annotation.PostConstruct;
31 import javax.inject.Inject;
32 import javax.inject.Named;
33 import javax.inject.Singleton;
34 import org.apache.commons.lang3.tuple.ImmutablePair;
35 import org.apache.commons.lang3.tuple.Pair;
36 import org.eclipse.jdt.annotation.NonNull;
37 import org.eclipse.jdt.annotation.Nullable;
38 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
39 import org.opendaylight.genius.infra.Datastore;
40 import org.opendaylight.genius.infra.Datastore.Configuration;
41 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
42 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
43 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
44 import org.opendaylight.genius.infra.TypedWriteTransaction;
45 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
46 import org.opendaylight.genius.mdsalutil.MDSALUtil;
47 import org.opendaylight.genius.mdsalutil.NwConstants;
48 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
49 import org.opendaylight.genius.utils.clustering.EntityOwnershipUtils;
50 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
51 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
52 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
53 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
54 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
55 import org.opendaylight.mdsal.binding.api.DataBroker;
56 import org.opendaylight.mdsal.binding.api.ReadTransaction;
57 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
58 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService;
59 import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
60 import org.opendaylight.netvirt.dhcpservice.api.IDhcpExternalTunnelManager;
61 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput;
62 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
63 import org.opendaylight.netvirt.elanmanager.api.IElanService;
64 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
65 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayCache;
66 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
67 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
69 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
70 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
71 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
72 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetExternalTunnelInterfaceNameInputBuilder;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetExternalTunnelInterfaceNameOutput;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.DesignatedSwitchesForExternalTunnels;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelKey;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPort;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepLogicalSwitchRef;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorRef;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsBuilder;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSetBuilder;
97 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
98 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
99 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
100 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
101 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
102 import org.opendaylight.yangtools.yang.common.RpcResult;
103 import org.opendaylight.yangtools.yang.common.Uint64;
104 import org.slf4j.Logger;
105 import org.slf4j.LoggerFactory;
108 public class DhcpExternalTunnelManager implements IDhcpExternalTunnelManager {
110 private static final Logger LOG = LoggerFactory.getLogger(DhcpExternalTunnelManager.class);
111 public static final String UNKNOWN_DMAC = "00:00:00:00:00:00";
113 private final DataBroker broker;
114 private final ManagedNewTransactionRunner txRunner;
115 private final IMdsalApiManager mdsalUtil;
116 private final ItmRpcService itmRpcService;
117 private final EntityOwnershipUtils entityOwnershipUtils;
118 private final IInterfaceManager interfaceManager;
119 private final JobCoordinator jobCoordinator;
120 private final L2GatewayCache l2GatewayCache;
121 private IElanService elanService;
122 private final DhcpServiceCounters dhcpServiceCounters;
124 private final ConcurrentMap<Uint64, Set<Pair<IpAddress, String>>> designatedDpnsToTunnelIpElanNameCache =
125 new ConcurrentHashMap<>();
126 private final ConcurrentMap<Pair<IpAddress, String>, Set<String>> tunnelIpElanNameToVmMacCache =
127 new ConcurrentHashMap<>();
128 private final ConcurrentMap<Pair<IpAddress, String>, Set<String>> availableVMCache = new ConcurrentHashMap<>();
129 private final ConcurrentMap<Pair<Uint64, String>, Port> vniMacAddressToPortCache = new ConcurrentHashMap<>();
132 public ConcurrentMap<Uint64, Set<Pair<IpAddress, String>>> getDesignatedDpnsToTunnelIpElanNameCache() {
133 return designatedDpnsToTunnelIpElanNameCache;
137 public ConcurrentMap<Pair<IpAddress, String>, Set<String>> getTunnelIpElanNameToVmMacCache() {
138 return tunnelIpElanNameToVmMacCache;
142 public ConcurrentMap<Pair<IpAddress, String>, Set<String>> getAvailableVMCache() {
143 return availableVMCache;
147 public ConcurrentMap<Pair<Uint64, String>, Port> getVniMacAddressToPortCache() {
148 return vniMacAddressToPortCache;
152 public DhcpExternalTunnelManager(final DataBroker broker,
153 final IMdsalApiManager mdsalUtil, final ItmRpcService itmRpcService,
154 final EntityOwnershipService entityOwnershipService, final IInterfaceManager interfaceManager,
155 final JobCoordinator jobCoordinator, final L2GatewayCache l2GatewayCache,
156 @Named("elanService") IElanService ielanService, DhcpServiceCounters dhcpServiceCounters) {
157 this.broker = broker;
158 this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
159 this.mdsalUtil = mdsalUtil;
160 this.itmRpcService = itmRpcService;
161 this.entityOwnershipUtils = new EntityOwnershipUtils(entityOwnershipService);
162 this.interfaceManager = interfaceManager;
163 this.jobCoordinator = jobCoordinator;
164 this.l2GatewayCache = l2GatewayCache;
165 this.elanService = ielanService;
166 this.dhcpServiceCounters = dhcpServiceCounters;
174 private void initilizeCaches() {
175 LOG.trace("Loading designatedDpnsToTunnelIpElanNameCache");
176 InstanceIdentifier<DesignatedSwitchesForExternalTunnels> instanceIdentifier =
177 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).build();
178 Optional<DesignatedSwitchesForExternalTunnels> designatedSwitchForTunnelOptional;
180 designatedSwitchForTunnelOptional = SingleTransactionDataBroker.syncReadOptional(broker,
181 LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
182 } catch (ExecutionException | InterruptedException e) {
183 LOG.error("initilizeCaches: Exception while reading the DesignatedSwitchesForExternalTunnels DS", e);
186 if (designatedSwitchForTunnelOptional.isPresent()) {
187 List<DesignatedSwitchForTunnel> list =
188 designatedSwitchForTunnelOptional.get().nonnullDesignatedSwitchForTunnel();
189 for (DesignatedSwitchForTunnel designatedSwitchForTunnel : list) {
190 Set<Pair<IpAddress, String>> setOfTunnelIpElanNamePair =
191 designatedDpnsToTunnelIpElanNameCache
192 .get(Uint64.valueOf(designatedSwitchForTunnel.getDpId()));
193 if (setOfTunnelIpElanNamePair == null) {
194 setOfTunnelIpElanNamePair = new CopyOnWriteArraySet<>();
196 Pair<IpAddress, String> tunnelIpElanNamePair =
197 new ImmutablePair<>(designatedSwitchForTunnel.getTunnelRemoteIpAddress(),
198 designatedSwitchForTunnel.getElanInstanceName());
199 setOfTunnelIpElanNamePair.add(tunnelIpElanNamePair);
200 designatedDpnsToTunnelIpElanNameCache.put(Uint64.valueOf(designatedSwitchForTunnel.getDpId()),
201 setOfTunnelIpElanNamePair);
204 LOG.trace("Loading vniMacAddressToPortCache");
205 InstanceIdentifier<Ports> inst = InstanceIdentifier.builder(Neutron.class).child(Ports.class).build();
206 Optional<Ports> optionalPorts;
208 optionalPorts = SingleTransactionDataBroker.syncReadOptional(broker, LogicalDatastoreType.CONFIGURATION,
210 } catch (ExecutionException | InterruptedException e) {
211 LOG.error("initilizeCaches: Exception while reading the Ports DS", e);
214 if (optionalPorts.isPresent()) {
215 List<Port> list = optionalPorts.get().nonnullPort();
216 for (Port port : list) {
217 if (NeutronUtils.isPortVnicTypeNormal(port)) {
220 String macAddress = port.getMacAddress().getValue();
221 Uuid networkId = port.getNetworkId();
222 String segmentationId = DhcpServiceUtils.getSegmentationId(networkId, broker);
223 if (segmentationId == null) {
226 updateVniMacToPortCache(Uint64.valueOf(new BigInteger(segmentationId)), macAddress, port);
231 public Uint64 designateDpnId(IpAddress tunnelIp, String elanInstanceName, List<Uint64> dpns) {
232 Uint64 designatedDpnId = readDesignatedSwitchesForExternalTunnel(tunnelIp, elanInstanceName);
233 if (designatedDpnId != null && !designatedDpnId.equals(DhcpMConstants.INVALID_DPID)) {
234 LOG.trace("Dpn {} already designated for tunnelIp - elan : {} - {}", designatedDpnId, tunnelIp,
236 return designatedDpnId;
238 return chooseDpn(tunnelIp, elanInstanceName, dpns);
241 public void installDhcpFlowsForVms(final IpAddress tunnelIp, String elanInstanceName, final List<Uint64> dpns,
242 final Uint64 designatedDpnId, final String vmMacAddress) {
243 LOG.trace("In installDhcpFlowsForVms ipAddress {}, elanInstanceName {}, dpn {}, vmMacAddress {}", tunnelIp,
244 elanInstanceName, designatedDpnId, vmMacAddress);
246 String tunnelIpDpnKey = getTunnelIpDpnKey(tunnelIp, designatedDpnId);
247 jobCoordinator.enqueueJob(getJobKey(tunnelIpDpnKey), () -> {
248 if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
249 HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
250 return Collections.singletonList(
251 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
252 dpns.remove(designatedDpnId);
253 for (Uint64 dpn : dpns) {
254 installDhcpDropAction(dpn, vmMacAddress, tx);
256 installDhcpEntries(designatedDpnId, vmMacAddress, tx);
259 LOG.trace("Exiting installDhcpEntries since this cluster node is not the owner for dpn");
262 return Collections.emptyList();
265 updateLocalCache(tunnelIp, elanInstanceName, vmMacAddress);
268 public void installDhcpFlowsForVms(Uint64 designatedDpnId, Set<String> listVmMacAddress,
269 TypedReadWriteTransaction<Configuration> tx) throws ExecutionException, InterruptedException {
270 for (String vmMacAddress : listVmMacAddress) {
271 installDhcpEntries(designatedDpnId, vmMacAddress, tx);
275 public void unInstallDhcpFlowsForVms(String elanInstanceName, List<Uint64> dpns, String vmMacAddress) {
276 unInstallDhcpEntriesOnDpns(dpns, vmMacAddress);
277 removeFromLocalCache(elanInstanceName, vmMacAddress);
280 public void unInstallDhcpFlowsForVms(String elanInstanceName, IpAddress tunnelIp, List<Uint64> dpns) {
281 Pair<IpAddress, String> tunnelIpElanNamePair = new ImmutablePair<>(tunnelIp, elanInstanceName);
282 Set<String> vmMacs = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair);
283 LOG.trace("In unInstallFlowsForVms elanInstanceName {}, tunnelIp {}, dpns {}, vmMacs {}",
284 elanInstanceName, tunnelIp, dpns, vmMacs);
285 if (vmMacs == null) {
288 for (String vmMacAddress : vmMacs) {
289 unInstallDhcpEntriesOnDpns(dpns, vmMacAddress);
291 tunnelIpElanNameToVmMacCache.remove(tunnelIpElanNamePair);
295 public Uint64 readDesignatedSwitchesForExternalTunnel(IpAddress tunnelIp, String elanInstanceName) {
296 if (tunnelIp == null || elanInstanceName == null || elanInstanceName.isEmpty()) {
299 InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier =
300 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class)
301 .child(DesignatedSwitchForTunnel.class,
302 new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp)).build();
303 Optional<DesignatedSwitchForTunnel> designatedSwitchForTunnelOptional;
305 designatedSwitchForTunnelOptional = SingleTransactionDataBroker.syncReadOptional(broker,
306 LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
307 } catch (ExecutionException | InterruptedException e) {
308 LOG.error("readDesignatedSwitchesForExternalTunnel: Exception while reading the DesignatedSwitchForTunnel "
309 + "DS for the elan-instance {} tunnelIp {}", elanInstanceName, tunnelIp, e);
312 if (designatedSwitchForTunnelOptional.isPresent()) {
313 return Uint64.valueOf(designatedSwitchForTunnelOptional.get().getDpId());
318 public void writeDesignatedSwitchForExternalTunnel(Uint64 dpnId, IpAddress tunnelIp,
319 String elanInstanceName) {
320 DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey =
321 new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp);
322 InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier =
323 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class)
324 .child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build();
325 DesignatedSwitchForTunnel designatedSwitchForTunnel =
326 new DesignatedSwitchForTunnelBuilder().setDpId(dpnId.longValue())
327 .setElanInstanceName(elanInstanceName).setTunnelRemoteIpAddress(tunnelIp)
328 .withKey(designatedSwitchForTunnelKey).build();
329 LOG.trace("Writing into CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp, elanInstanceName,
331 MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier, designatedSwitchForTunnel);
332 updateLocalCache(dpnId, tunnelIp, elanInstanceName);
335 public void removeDesignatedSwitchForExternalTunnel(Uint64 dpnId, IpAddress tunnelIp,
336 String elanInstanceName) {
337 DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey =
338 new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp);
339 InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier =
340 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class)
341 .child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build();
342 LOG.trace("Removing from CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp,
343 elanInstanceName, dpnId);
344 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
345 removeFromLocalCache(dpnId, tunnelIp, elanInstanceName);
348 // This method is called whenever new OVS Switch is added.
349 public void installDhcpDropActionOnDpn(Uint64 dpId) {
350 // During controller restart we'll get add for designatedDpns as well and we
351 // need not install drop flows for those dpns
352 if (designatedDpnsToTunnelIpElanNameCache.get(dpId) != null) {
353 LOG.trace("The dpn {} is designated DPN need not install drop flows", dpId);
356 // Read from DS since the cache may not get loaded completely in restart scenario
357 if (isDpnDesignatedDpn(dpId)) {
358 LOG.trace("The dpn {} is designated DPN need not install drop flows", dpId);
361 List<String> vmMacs = getAllVmMacs();
362 LOG.trace("Installing drop actions to this new DPN {} VMs {}", dpId, vmMacs);
363 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
364 for (String vmMacAddress : vmMacs) {
365 installDhcpDropAction(dpId, vmMacAddress, tx);
367 }), LOG, "Error writing to the datastore");
370 private boolean isDpnDesignatedDpn(Uint64 dpId) {
371 InstanceIdentifier<DesignatedSwitchesForExternalTunnels> instanceIdentifier =
372 InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).build();
373 Optional<DesignatedSwitchesForExternalTunnels> designatedSwitchForTunnelOptional;
375 designatedSwitchForTunnelOptional = SingleTransactionDataBroker.syncReadOptional(broker,
376 LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
377 } catch (ExecutionException | InterruptedException e) {
378 LOG.error("isDpnDesignatedDpn: Exception while reading the DesignatedSwitchesForExternalTunnels "
379 + "DS for the dpId {}", dpId, e);
382 if (designatedSwitchForTunnelOptional.isPresent()) {
383 List<DesignatedSwitchForTunnel> list =
384 designatedSwitchForTunnelOptional.get().nonnullDesignatedSwitchForTunnel();
385 for (DesignatedSwitchForTunnel designatedSwitchForTunnel : list) {
386 if (dpId.equals(Uint64.valueOf(designatedSwitchForTunnel.getDpId()))) {
394 private List<String> getAllVmMacs() {
395 List<String> vmMacs = new LinkedList<>();
396 Collection<Set<String>> listOfVmMacs = tunnelIpElanNameToVmMacCache.values();
397 for (Set<String> list : listOfVmMacs) {
403 public void updateLocalCache(Uint64 designatedDpnId, IpAddress tunnelIp, String elanInstanceName) {
404 Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
405 Set<Pair<IpAddress, String>> tunnelIpElanNameSet;
406 tunnelIpElanNameSet = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId);
407 if (tunnelIpElanNameSet == null) {
408 tunnelIpElanNameSet = new CopyOnWriteArraySet<>();
410 tunnelIpElanNameSet.add(tunnelIpElanName);
411 LOG.trace("Updating designatedDpnsToTunnelIpElanNameCache for designatedDpn {} value {}", designatedDpnId,
412 tunnelIpElanNameSet);
413 designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameSet);
416 public void updateLocalCache(IpAddress tunnelIp, String elanInstanceName, String vmMacAddress) {
417 Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
418 Set<String> setOfExistingVmMacAddress;
419 setOfExistingVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanName);
420 if (setOfExistingVmMacAddress == null) {
421 setOfExistingVmMacAddress = new CopyOnWriteArraySet<>();
423 setOfExistingVmMacAddress.add(vmMacAddress);
424 LOG.trace("Updating tunnelIpElanNameToVmMacCache for tunnelIpElanName {} value {}", tunnelIpElanName,
425 setOfExistingVmMacAddress);
426 tunnelIpElanNameToVmMacCache.put(tunnelIpElanName, setOfExistingVmMacAddress);
427 updateExistingVMTunnelIPCache(tunnelIp, elanInstanceName, vmMacAddress);
430 public void updateExistingVMTunnelIPCache(IpAddress tunnelIp, String elanInstanceName, String vmMacAddress) {
431 Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
432 Set<String> listExistingVmMacAddress;
433 listExistingVmMacAddress = availableVMCache.get(tunnelIpElanName);
434 if (listExistingVmMacAddress == null) {
435 listExistingVmMacAddress = new CopyOnWriteArraySet<>();
437 listExistingVmMacAddress.add(vmMacAddress);
438 LOG.trace("Updating availableVMCache for tunnelIpElanName {} value {}", tunnelIpElanName,
439 listExistingVmMacAddress);
440 availableVMCache.put(tunnelIpElanName, listExistingVmMacAddress);
443 public void handleDesignatedDpnDown(Uint64 dpnId, List<Uint64> listOfDpns) {
444 LOG.trace("In handleDesignatedDpnDown dpnId {}, listOfDpns {}", dpnId, listOfDpns);
445 Set<Pair<IpAddress, String>> setOfTunnelIpElanNamePairs = designatedDpnsToTunnelIpElanNameCache.get(dpnId);
446 if (setOfTunnelIpElanNamePairs == null || setOfTunnelIpElanNamePairs.isEmpty()) {
447 LOG.trace("No tunnelIpElanName to handle for dpn {}. Returning", dpnId);
449 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
450 if (!dpnId.equals(DhcpMConstants.INVALID_DPID)) {
451 List<String> listOfVms = getAllVmMacs();
452 for (String vmMacAddress : listOfVms) {
453 unInstallDhcpEntries(dpnId, vmMacAddress, tx);
456 for (Pair<IpAddress, String> pair : setOfTunnelIpElanNamePairs) {
457 updateCacheAndInstallNewFlows(listOfDpns, pair, tx);
459 }), LOG, "Error writing to datastore");
463 public void updateCacheAndInstallNewFlows(List<Uint64> listOfDpns, Pair<IpAddress, String> pair,
464 TypedReadWriteTransaction<Configuration> tx) throws ExecutionException, InterruptedException {
465 Uint64 newDesignatedDpn = chooseDpn(pair.getLeft(), pair.getRight(), listOfDpns);
466 if (newDesignatedDpn.equals(DhcpMConstants.INVALID_DPID)) {
469 Set<String> setOfVmMacs = tunnelIpElanNameToVmMacCache.get(pair);
470 if (setOfVmMacs != null && !setOfVmMacs.isEmpty()) {
471 LOG.trace("Updating DHCP flows for VMs {} with new designated DPN {}", setOfVmMacs, newDesignatedDpn);
472 installDhcpFlowsForVms(newDesignatedDpn, setOfVmMacs, tx);
474 java.util.Optional<SubnetToDhcpPort> subnetDhcpData = getSubnetDhcpPortData(pair.getRight());
475 if (subnetDhcpData.isPresent()) {
476 configureDhcpArpRequestResponseFlow(newDesignatedDpn, pair.getRight(), true,
477 pair.getLeft(), subnetDhcpData.get().getPortFixedip(), subnetDhcpData.get().getPortMacaddress());
481 private void changeExistingFlowToDrop(Pair<IpAddress, String> tunnelIpElanNamePair, Uint64 dpnId,
482 TypedReadWriteTransaction<Configuration> tx)
483 throws ExecutionException, InterruptedException {
484 Set<String> setOfVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair);
485 if (setOfVmMacAddress == null || setOfVmMacAddress.isEmpty()) {
488 for (String vmMacAddress : setOfVmMacAddress) {
489 installDhcpDropAction(dpnId, vmMacAddress, tx);
494 * Choose a dpn among the list of elanDpns such that it has lowest count of being the designated dpn.
495 * @param tunnelIp The tunnel Ip address
496 * @param elanInstanceName The elan instance name
497 * @param dpns The data path nodes
498 * @return The designated dpn
500 private Uint64 chooseDpn(IpAddress tunnelIp, String elanInstanceName,
502 Uint64 designatedDpnId = DhcpMConstants.INVALID_DPID;
503 if (dpns != null && dpns.size() != 0) {
504 List<Uint64> candidateDpns = DhcpServiceUtils.getDpnsForElan(elanInstanceName, broker);
505 candidateDpns.retainAll(dpns);
506 LOG.trace("Choosing new dpn for tunnelIp {}, elanInstanceName {}, among elanDpns {}",
507 tunnelIp, elanInstanceName, candidateDpns);
508 boolean elanDpnAvailableFlag = true;
509 if (candidateDpns.isEmpty()) {
510 candidateDpns = dpns;
511 elanDpnAvailableFlag = false;
514 L2GatewayDevice device = getDeviceFromTunnelIp(tunnelIp);
515 if (device == null) {
516 LOG.trace("Could not find any device for elanInstanceName {} and tunnelIp {}",
517 elanInstanceName, tunnelIp);
518 handleUnableToDesignateDpn(tunnelIp, elanInstanceName);
519 return designatedDpnId;
521 for (Uint64 dpn : candidateDpns) {
522 String hwvtepNodeId = device.getHwvtepNodeId();
523 if (!elanDpnAvailableFlag) {
524 if (!isTunnelConfigured(dpn, hwvtepNodeId)) {
525 LOG.trace("Tunnel is not configured on dpn {} to TOR {}", dpn, hwvtepNodeId);
528 } else if (!isTunnelUp(hwvtepNodeId, dpn)) {
529 LOG.trace("Tunnel is not up between dpn {} and TOR {}", dpn, hwvtepNodeId);
532 Set<Pair<IpAddress, String>> tunnelIpElanNameSet = designatedDpnsToTunnelIpElanNameCache.get(dpn);
533 if (tunnelIpElanNameSet == null) {
534 designatedDpnId = dpn;
537 if (size == 0 || tunnelIpElanNameSet.size() < size) {
538 size = tunnelIpElanNameSet.size();
539 designatedDpnId = dpn;
542 writeDesignatedSwitchForExternalTunnel(designatedDpnId, tunnelIp, elanInstanceName);
543 return designatedDpnId;
545 handleUnableToDesignateDpn(tunnelIp, elanInstanceName);
546 return designatedDpnId;
549 private void handleUnableToDesignateDpn(IpAddress tunnelIp, String elanInstanceName) {
550 writeDesignatedSwitchForExternalTunnel(DhcpMConstants.INVALID_DPID, tunnelIp, elanInstanceName);
553 private void installDhcpEntries(Uint64 dpnId, String vmMacAddress,
554 TypedReadWriteTransaction<Configuration> tx) throws ExecutionException, InterruptedException {
555 DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL,
556 vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil, dhcpServiceCounters, tx);
559 public void addOrRemoveDhcpArpFlowforElan(String elanInstanceName, boolean addFlow, String dhcpIpAddress,
560 String dhcpMacAddress) {
561 LOG.trace("Configure DHCP SR-IOV Arp flows for Elan {} dpns .", elanInstanceName);
562 for (Entry<Uint64, Set<Pair<IpAddress,String>>> entry : designatedDpnsToTunnelIpElanNameCache.entrySet()) {
563 Uint64 dpn = entry.getKey();
564 Set<Pair<IpAddress,String>> tunnelIpElanNameSet = entry.getValue();
565 for (Pair<IpAddress, String> pair : tunnelIpElanNameSet) {
566 if (pair.getRight().equalsIgnoreCase(elanInstanceName)) {
568 LOG.trace("Adding SR-IOV DHCP Arp Flows for Elan {} and tunnelIp {}",
569 elanInstanceName, pair.getLeft());
570 configureDhcpArpRequestResponseFlow(dpn, elanInstanceName, true,
571 pair.getLeft(), dhcpIpAddress, dhcpMacAddress);
573 LOG.trace("Deleting SR-IOV DHCP Arp Flows for Elan {} and tunnelIp {}",
574 elanInstanceName, pair.getLeft());
575 configureDhcpArpRequestResponseFlow(dpn, elanInstanceName, false,
576 pair.getLeft(), dhcpIpAddress, dhcpMacAddress);
584 public void configureDhcpArpRequestResponseFlow(Uint64 dpnId, String elanInstanceName, boolean addFlow,
585 IpAddress tunnelIp, String dhcpIpAddress, String dhcpMacAddress) {
586 L2GatewayDevice device = getDeviceFromTunnelIp(tunnelIp);
587 if (device == null) {
588 LOG.error("Unable to get L2Device for tunnelIp {} and elanInstanceName {}", tunnelIp,
591 jobCoordinator.enqueueJob(getJobKey(elanInstanceName), () -> {
592 if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
593 HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
594 String tunnelInterfaceName = getExternalTunnelInterfaceName(dpnId.toString(),
595 device.getHwvtepNodeId());
596 int lportTag = interfaceManager.getInterfaceInfo(tunnelInterfaceName).getInterfaceTag();
597 InstanceIdentifier<ElanInstance> elanIdentifier = InstanceIdentifier.builder(ElanInstances.class)
598 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
599 Optional<ElanInstance> optElan = MDSALUtil.read(broker,
600 LogicalDatastoreType.CONFIGURATION, elanIdentifier);
601 if (optElan.isPresent()) {
602 LOG.trace("Configuring the SR-IOV Arp request/response flows for LPort {} ElanTag {}.",
603 lportTag, optElan.get().getElanTag());
604 Uuid nwUuid = new Uuid(elanInstanceName);
605 String strVni = DhcpServiceUtils.getSegmentationId(nwUuid, broker);
606 Uint64 vni = strVni != null ? Uint64.valueOf(strVni) : Uint64.valueOf(0);
607 if (!vni.equals(Uint64.ZERO)) {
608 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(
609 Datastore.CONFIGURATION, tx -> {
611 LOG.trace("Installing the SR-IOV DHCP Arp flow for DPN {} Port Ip {}, Lport {}.",
612 dpnId, dhcpIpAddress, lportTag);
613 installDhcpArpRequestFlows(tx, dpnId, vni, dhcpIpAddress, lportTag,
614 optElan.get().getElanTag().toJava());
615 installDhcpArpResponderFlows(dpnId, tunnelInterfaceName, lportTag, elanInstanceName,
616 dhcpIpAddress, dhcpMacAddress);
618 LOG.trace("Uninstalling the SR-IOV DHCP Arp flows for DPN {} Port Ip {}, Lport {}.",
619 dpnId, dhcpIpAddress, lportTag);
620 uninstallDhcpArpRequestFlows(tx, dpnId, vni, dhcpIpAddress, lportTag);
621 uninstallDhcpArpResponderFlows(dpnId, tunnelInterfaceName, lportTag, dhcpIpAddress);
627 return Collections.emptyList();
631 public java.util.Optional<SubnetToDhcpPort> getSubnetDhcpPortData(String elanInstanceName) {
632 java.util.Optional<SubnetToDhcpPort> optSubnetDhcp = java.util.Optional.empty();
633 Uuid nwUuid = new Uuid(elanInstanceName);
634 List<Uuid> subnets = DhcpServiceUtils.getSubnetIdsFromNetworkId(broker, nwUuid);
635 for (Uuid subnet : subnets) {
636 if (DhcpServiceUtils.isIpv4Subnet(broker, subnet)) {
637 optSubnetDhcp = DhcpServiceUtils.getSubnetDhcpPortData(broker, subnet.getValue());
638 return optSubnetDhcp;
641 return optSubnetDhcp;
644 private void installDhcpArpRequestFlows(TypedReadWriteTransaction<Configuration> tx, Uint64 dpnId,
645 Uint64 vni, String dhcpIpAddress, int lportTag, Long elanTag)
646 throws ExecutionException, InterruptedException {
647 DhcpServiceUtils.setupDhcpArpRequest(dpnId, NwConstants.EXTERNAL_TUNNEL_TABLE, vni, dhcpIpAddress,
648 lportTag, elanTag, true, mdsalUtil, tx);
651 private void installDhcpArpResponderFlows(Uint64 dpnId, String interfaceName, int lportTag,
652 String elanInstanceName, String dhcpIpAddress, String dhcpMacAddress) {
653 LOG.trace("Adding SR-IOV DHCP ArpResponder for elan {} Lport {} Port Ip {}.",
654 elanInstanceName, lportTag, dhcpIpAddress);
655 ArpResponderInput.ArpReponderInputBuilder builder = new ArpResponderInput.ArpReponderInputBuilder();
656 builder.setDpId(dpnId.toJava()).setInterfaceName(interfaceName).setSpa(dhcpIpAddress).setSha(dhcpMacAddress)
657 .setLportTag(lportTag);
658 builder.setInstructions(ArpResponderUtil.getInterfaceInstructions(interfaceManager, interfaceName,
659 dhcpIpAddress, dhcpMacAddress, itmRpcService));
660 elanService.addExternalTunnelArpResponderFlow(builder.buildForInstallFlow(), elanInstanceName);
663 private void uninstallDhcpArpResponderFlows(Uint64 dpnId, String interfaceName, int lportTag,
664 String dhcpIpAddress) {
665 LOG.trace("Removing SR-IOV DHCP ArpResponder flow for interface {} on DPN {}", interfaceName, dpnId);
666 ArpResponderInput arpInput = new ArpResponderInput.ArpReponderInputBuilder().setDpId(dpnId.toJava())
667 .setInterfaceName(interfaceName).setSpa(dhcpIpAddress)
668 .setLportTag(lportTag).buildForRemoveFlow();
669 elanService.removeArpResponderFlow(arpInput);
672 private void uninstallDhcpArpRequestFlows(TypedReadWriteTransaction<Configuration> tx, Uint64 dpnId,
673 Uint64 vni, String dhcpIpAddress, int lportTag)
674 throws ExecutionException, InterruptedException {
675 DhcpServiceUtils.setupDhcpArpRequest(dpnId, NwConstants.EXTERNAL_TUNNEL_TABLE, vni, dhcpIpAddress,
676 lportTag, null, false, mdsalUtil, tx);
680 public void unInstallDhcpEntries(Uint64 dpnId, String vmMacAddress,
681 TypedReadWriteTransaction<Configuration> tx) throws ExecutionException, InterruptedException {
682 DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL,
683 vmMacAddress, NwConstants.DEL_FLOW, mdsalUtil, dhcpServiceCounters, tx);
686 private void installDhcpDropAction(Uint64 dpn, String vmMacAddress,
687 TypedReadWriteTransaction<Configuration> tx) throws ExecutionException, InterruptedException {
688 DhcpServiceUtils.setupDhcpDropAction(dpn, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL,
689 vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil, dhcpServiceCounters, tx);
692 public List<ListenableFuture<Void>> handleTunnelStateDown(IpAddress tunnelIp, Uint64 interfaceDpn) {
693 LOG.trace("In handleTunnelStateDown tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn);
694 if (interfaceDpn == null) {
695 return Collections.emptyList();
697 synchronized (getTunnelIpDpnKey(tunnelIp, interfaceDpn)) {
698 Set<Pair<IpAddress, String>> tunnelElanPairSet =
699 designatedDpnsToTunnelIpElanNameCache.get(interfaceDpn);
700 if (tunnelElanPairSet == null || tunnelElanPairSet.isEmpty()) {
701 return Collections.emptyList();
703 for (Pair<IpAddress, String> tunnelElanPair : tunnelElanPairSet) {
704 IpAddress tunnelIpInDpn = tunnelElanPair.getLeft();
705 if (tunnelIpInDpn.equals(tunnelIp)) {
706 if (!checkL2GatewayConnection(tunnelElanPair)) {
707 LOG.trace("Couldn't find device for given tunnelIpElanPair {} in L2GwConnCache",
709 return Collections.emptyList();
713 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
714 for (Pair<IpAddress, String> tunnelElanPair : tunnelElanPairSet) {
715 IpAddress tunnelIpInDpn = tunnelElanPair.getLeft();
716 String elanInstanceName = tunnelElanPair.getRight();
717 if (tunnelIpInDpn.equals(tunnelIp)) {
718 if (!checkL2GatewayConnection(tunnelElanPair)) {
719 LOG.trace("Couldn't find device for given tunnelIpElanPair {} in L2GwConnCache",
722 List<Uint64> dpns = DhcpServiceUtils.getListOfDpns(broker);
723 dpns.remove(interfaceDpn);
724 changeExistingFlowToDrop(tunnelElanPair, interfaceDpn, tx);
725 java.util.Optional<SubnetToDhcpPort> subnetDhcpData = getSubnetDhcpPortData(elanInstanceName);
726 if (subnetDhcpData.isPresent()) {
727 configureDhcpArpRequestResponseFlow(interfaceDpn, elanInstanceName, false,
728 tunnelIpInDpn, subnetDhcpData.get().getPortFixedip(),
729 subnetDhcpData.get().getPortMacaddress());
731 updateCacheAndInstallNewFlows(dpns, tunnelElanPair, tx);
738 private boolean checkL2GatewayConnection(Pair<IpAddress, String> tunnelElanPair) {
739 for (L2GatewayDevice device : ElanL2GwCacheUtils.getInvolvedL2GwDevices(tunnelElanPair.getRight())) {
740 if (Objects.equals(device.getTunnelIp(), tunnelElanPair.getLeft())) {
747 private String getTunnelIpDpnKey(IpAddress tunnelIp, Uint64 interfaceDpn) {
748 return tunnelIp.toString() + interfaceDpn;
751 private void removeFromLocalCache(String elanInstanceName, String vmMacAddress) {
752 for (Entry<Pair<IpAddress, String>, Set<String>> entry : tunnelIpElanNameToVmMacCache.entrySet()) {
753 Pair<IpAddress, String> pair = entry.getKey();
754 if (pair.getRight().trim().equalsIgnoreCase(elanInstanceName.trim())) {
755 Set<String> setOfExistingVmMacAddress = entry.getValue();
756 if (setOfExistingVmMacAddress == null || setOfExistingVmMacAddress.isEmpty()) {
759 LOG.trace("Removing vmMacAddress {} from listOfMacs {} for elanInstanceName {}", vmMacAddress,
760 setOfExistingVmMacAddress, elanInstanceName);
761 setOfExistingVmMacAddress.remove(vmMacAddress);
762 if (setOfExistingVmMacAddress.size() > 0) {
763 tunnelIpElanNameToVmMacCache.put(pair, setOfExistingVmMacAddress);
766 tunnelIpElanNameToVmMacCache.remove(pair);
771 public void removeFromLocalCache(Uint64 designatedDpnId, IpAddress tunnelIp, String elanInstanceName) {
772 Pair<IpAddress, String> tunnelIpElanName = new ImmutablePair<>(tunnelIp, elanInstanceName);
773 Set<Pair<IpAddress, String>> tunnelIpElanNameSet;
774 tunnelIpElanNameSet = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId);
775 if (tunnelIpElanNameSet != null) {
776 LOG.trace("Removing tunnelIpElan {} from designatedDpnsToTunnelIpElanNameCache. Existing list {} for "
777 + "designatedDpnId {}",
778 tunnelIpElanName, tunnelIpElanNameSet, designatedDpnId);
779 tunnelIpElanNameSet.remove(tunnelIpElanName);
780 if (tunnelIpElanNameSet.size() != 0) {
781 designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameSet);
783 designatedDpnsToTunnelIpElanNameCache.remove(designatedDpnId);
788 public void updateVniMacToPortCache(Uint64 vni, String macAddress, Port port) {
789 if (macAddress == null) {
792 Pair<Uint64, String> vniMacAddressPair = new ImmutablePair<>(
793 vni, macAddress.toUpperCase(Locale.getDefault()));
794 LOG.trace("Updating vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}", vni,
795 macAddress.toUpperCase(Locale.getDefault()), vniMacAddressPair, port);
796 vniMacAddressToPortCache.put(vniMacAddressPair, port);
799 public void removeVniMacToPortCache(Uint64 vni, String macAddress) {
800 if (macAddress == null) {
803 Pair<Uint64, String> vniMacAddressPair = new ImmutablePair<>(
804 vni, macAddress.toUpperCase(Locale.getDefault()));
805 vniMacAddressToPortCache.remove(vniMacAddressPair);
809 public Port readVniMacToPortCache(Uint64 vni, String macAddress) {
810 if (macAddress == null) {
813 Pair<Uint64, String> vniMacAddressPair = new ImmutablePair<>(
814 vni, macAddress.toUpperCase(Locale.getDefault()));
815 LOG.trace("Reading vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}",
816 vni, macAddress.toUpperCase(Locale.getDefault()), vniMacAddressPair,
817 vniMacAddressToPortCache.get(vniMacAddressPair));
818 return vniMacAddressToPortCache.get(vniMacAddressPair);
821 public String getExternalTunnelInterfaceName(String sourceNode, String dstNode) {
822 String tunnelInterfaceName = null;
823 Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
825 Future<RpcResult<GetExternalTunnelInterfaceNameOutput>> output = itmRpcService
826 .getExternalTunnelInterfaceName(new GetExternalTunnelInterfaceNameInputBuilder()
827 .setSourceNode(sourceNode).setDestinationNode(dstNode).setTunnelType(tunType).build());
829 RpcResult<GetExternalTunnelInterfaceNameOutput> rpcResult = output.get();
830 if (rpcResult.isSuccessful()) {
831 tunnelInterfaceName = rpcResult.getResult().getInterfaceName();
832 LOG.trace("Tunnel interface name: {}", tunnelInterfaceName);
834 LOG.warn("RPC call to ITM.GetExternalTunnelInterfaceName failed with error: {}", rpcResult.getErrors());
836 } catch (NullPointerException | InterruptedException | ExecutionException e) {
837 LOG.error("Failed to get external tunnel interface name for sourceNode: {} and dstNode: {}",
838 sourceNode, dstNode, e);
840 return tunnelInterfaceName;
843 public static Optional<Node> getNode(DataBroker dataBroker, String physicalSwitchNodeId) {
844 InstanceIdentifier<Node> psNodeId = HwvtepSouthboundUtils
845 .createInstanceIdentifier(new NodeId(physicalSwitchNodeId));
846 return MDSALUtil.read(LogicalDatastoreType.CONFIGURATION, psNodeId, dataBroker);
850 public RemoteMcastMacs createRemoteMcastMac(Node dstDevice, String logicalSwitchName, IpAddress internalTunnelIp) {
851 Set<LocatorSet> locators = new HashSet<>();
852 TerminationPointKey terminationPointKey = HwvtepSouthboundUtils.getTerminationPointKey(
853 internalTunnelIp.getIpv4Address().getValue());
854 HwvtepPhysicalLocatorRef phyLocRef = new HwvtepPhysicalLocatorRef(
855 HwvtepSouthboundUtils.createInstanceIdentifier(dstDevice.getNodeId()).child(TerminationPoint.class,
856 terminationPointKey));
857 locators.add(new LocatorSetBuilder().setLocatorRef(phyLocRef).build());
859 HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils
860 .createLogicalSwitchesInstanceIdentifier(dstDevice.getNodeId(), new HwvtepNodeName(logicalSwitchName)));
862 RemoteMcastMacs remoteMcastMacs = new RemoteMcastMacsBuilder()
863 .setMacEntryKey(new MacAddress(UNKNOWN_DMAC))
864 .setLogicalSwitchRef(lsRef).build();
865 InstanceIdentifier<RemoteMcastMacs> iid = HwvtepSouthboundUtils.createRemoteMcastMacsInstanceIdentifier(
866 dstDevice.getNodeId(), remoteMcastMacs.key());
867 ReadTransaction transaction = broker.newReadOnlyTransaction();
869 //TODO do async mdsal read
870 remoteMcastMacs = transaction.read(LogicalDatastoreType.CONFIGURATION, iid).get().get();
871 locators.addAll(remoteMcastMacs.getLocatorSet());
872 return new RemoteMcastMacsBuilder(remoteMcastMacs).setLocatorSet(new ArrayList<>(locators)).build();
873 } catch (InterruptedException | ExecutionException e) {
874 LOG.error("Failed to read the macs {}", iid);
881 private void putRemoteMcastMac(TypedWriteTransaction<Configuration> transaction, String elanName,
882 L2GatewayDevice device, IpAddress internalTunnelIp) {
883 Optional<Node> optionalNode = getNode(broker, device.getHwvtepNodeId());
884 if (!optionalNode.isPresent()) {
885 LOG.trace("could not get device node {} ", device.getHwvtepNodeId());
888 Node dstNode = optionalNode.get();
889 RemoteMcastMacs macs = createRemoteMcastMac(dstNode, elanName, internalTunnelIp);
890 HwvtepUtils.addRemoteMcastMac(transaction, dstNode.getNodeId(), macs);
893 public void installRemoteMcastMac(final Uint64 designatedDpnId, final IpAddress tunnelIp,
894 final String elanInstanceName) {
895 if (designatedDpnId.equals(DhcpMConstants.INVALID_DPID)) {
899 jobCoordinator.enqueueJob(getJobKey(elanInstanceName), () -> {
900 if (!entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
901 HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
902 LOG.info("Installing remote McastMac is not executed for this node.");
903 return Collections.emptyList();
906 LOG.info("Installing remote McastMac");
907 L2GatewayDevice device = getDeviceFromTunnelIp(tunnelIp);
908 if (device == null) {
909 LOG.error("Unable to get L2Device for tunnelIp {} and elanInstanceName {}", tunnelIp,
911 return Collections.emptyList();
913 String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(designatedDpnId),
914 device.getHwvtepNodeId());
915 if (tunnelInterfaceName != null) {
916 Interface tunnelInterface =
917 interfaceManager.getInterfaceInfoFromConfigDataStore(tunnelInterfaceName);
918 if (tunnelInterface == null) {
919 LOG.trace("Tunnel Interface is not present {}", tunnelInterfaceName);
920 return Collections.emptyList();
922 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
923 tx -> putRemoteMcastMac(tx, elanInstanceName, device,
924 tunnelInterface.augmentation(IfTunnel.class).getTunnelSource())));
926 return Collections.emptyList();
931 private L2GatewayDevice getDeviceFromTunnelIp(IpAddress tunnelIp) {
932 Collection<L2GatewayDevice> devices = l2GatewayCache.getAll();
933 LOG.trace("In getDeviceFromTunnelIp devices {}", devices);
934 for (L2GatewayDevice device : devices) {
935 if (tunnelIp.equals(device.getTunnelIp())) {
942 private boolean isTunnelUp(String nodeName, Uint64 dpn) {
943 String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), nodeName);
944 if (tunnelInterfaceName == null) {
945 LOG.trace("Tunnel Interface is not present on node {} with dpn {}", nodeName, dpn);
948 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
949 .Interface tunnelInterface =
950 DhcpServiceUtils.getInterfaceFromOperationalDS(tunnelInterfaceName, broker);
951 if (tunnelInterface == null) {
952 LOG.trace("Interface {} is not present in interface state", tunnelInterfaceName);
955 return tunnelInterface.getOperStatus() == OperStatus.Up;
958 public List<ListenableFuture<Void>> handleTunnelStateUp(IpAddress tunnelIp, Uint64 interfaceDpn) {
959 LOG.trace("In handleTunnelStateUp tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn);
960 synchronized (getTunnelIpDpnKey(tunnelIp, interfaceDpn)) {
961 Set<Pair<IpAddress, String>> tunnelIpElanPair =
962 designatedDpnsToTunnelIpElanNameCache.get(DhcpMConstants.INVALID_DPID);
963 List<Uint64> dpns = DhcpServiceUtils.getListOfDpns(broker);
964 if (tunnelIpElanPair == null || tunnelIpElanPair.isEmpty()) {
965 LOG.trace("There are no undesignated DPNs");
966 return Collections.emptyList();
968 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
969 for (Pair<IpAddress, String> pair : tunnelIpElanPair) {
970 if (tunnelIp.equals(pair.getLeft())) {
971 String elanInstanceName = pair.getRight();
972 Uint64 newDesignatedDpn = designateDpnId(tunnelIp, elanInstanceName, dpns);
973 if (newDesignatedDpn != null && !newDesignatedDpn.equals(DhcpMConstants.INVALID_DPID)) {
974 Set<String> vmMacAddress = tunnelIpElanNameToVmMacCache.get(pair);
975 if (vmMacAddress != null && !vmMacAddress.isEmpty()) {
976 LOG.trace("Updating DHCP flow for macAddress {} with newDpn {}",
977 vmMacAddress, newDesignatedDpn);
978 installDhcpFlowsForVms(newDesignatedDpn, vmMacAddress, tx);
981 java.util.Optional<SubnetToDhcpPort> subnetDhcpData = getSubnetDhcpPortData(elanInstanceName);
982 if (subnetDhcpData.isPresent()) {
983 configureDhcpArpRequestResponseFlow(newDesignatedDpn, elanInstanceName,
984 true, tunnelIp, subnetDhcpData.get().getPortFixedip(),
985 subnetDhcpData.get().getPortMacaddress());
993 private boolean isTunnelConfigured(Uint64 dpn, String hwVtepNodeId) {
994 String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), hwVtepNodeId);
995 if (tunnelInterfaceName == null) {
998 Interface tunnelInterface = interfaceManager.getInterfaceInfoFromConfigDataStore(tunnelInterfaceName);
999 if (tunnelInterface == null) {
1000 LOG.trace("Tunnel Interface is not present {}", tunnelInterfaceName);
1006 public void removeFromAvailableCache(Pair<IpAddress, String> tunnelIpElanName) {
1007 availableVMCache.remove(tunnelIpElanName);
1010 private void unInstallDhcpEntriesOnDpns(final List<Uint64> dpns, final String vmMacAddress) {
1011 jobCoordinator.enqueueJob(getJobKey(vmMacAddress), () -> {
1012 if (entityOwnershipUtils.isEntityOwner(HwvtepSouthboundConstants.ELAN_ENTITY_TYPE,
1013 HwvtepSouthboundConstants.ELAN_ENTITY_NAME)) {
1014 return Collections.singletonList(
1015 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
1016 for (final Uint64 dpn : dpns) {
1017 unInstallDhcpEntries(dpn, vmMacAddress, tx);
1021 LOG.trace("Exiting unInstallDhcpEntries since this cluster node is not the owner for dpn");
1024 return Collections.emptyList();
1029 public IpAddress getTunnelIpBasedOnElan(String elanInstanceName, String vmMacAddress) {
1030 LOG.trace("DhcpExternalTunnelManager getTunnelIpBasedOnElan elanInstanceName {}", elanInstanceName);
1031 IpAddress tunnelIp = null;
1032 for (Entry<Pair<IpAddress, String>, Set<String>> entry : availableVMCache.entrySet()) {
1033 Pair<IpAddress, String> pair = entry.getKey();
1034 LOG.trace("DhcpExternalTunnelManager getTunnelIpBasedOnElan left {} right {}", pair.getLeft(),
1036 if (pair.getRight().trim().equalsIgnoreCase(elanInstanceName.trim())) {
1037 Set<String> listExistingVmMacAddress = entry.getValue();
1038 if (listExistingVmMacAddress != null && !listExistingVmMacAddress.isEmpty()
1039 && listExistingVmMacAddress.contains(vmMacAddress)) {
1040 tunnelIp = pair.getLeft();
1045 LOG.trace("DhcpExternalTunnelManager getTunnelIpBasedOnElan returned tunnelIP {}", tunnelIp);
1049 private String getJobKey(final String jobKeySuffix) {
1050 return DhcpMConstants.DHCP_JOB_KEY_PREFIX + jobKeySuffix;