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.vpnservice.elan.l2gw.utils;
10 import java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.Objects;
16 import java.util.concurrent.Callable;
17 import java.util.concurrent.ConcurrentMap;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.Future;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
26 import org.opendaylight.vpnservice.datastoreutils.DataStoreJobCoordinator;
27 import org.opendaylight.vpnservice.elan.utils.ElanUtils;
28 import org.opendaylight.vpnservice.interfacemgr.IfmUtil;
29 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
30 import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
31 import org.opendaylight.vpnservice.utils.SystemPropertyReader;
32 import org.opendaylight.vpnservice.utils.clustering.ClusteringUtils;
33 import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundConstants;
34 import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundUtils;
35 import org.opendaylight.vpnservice.utils.hwvtep.HwvtepUtils;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LocalUcastMacs;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteUcastMacs;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.port.attributes.VlanBindings;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.forwarding.tables.MacTable;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.instances.ElanInstance;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.forwarding.entries.MacEntry;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.IfTunnel;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.AddL2GwDeviceInputBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameInputBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameOutput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
60 import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.opendaylight.yangtools.yang.common.RpcResult;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
66 import com.google.common.base.Function;
67 import com.google.common.base.Optional;
68 import com.google.common.collect.Lists;
69 import com.google.common.util.concurrent.FutureCallback;
70 import com.google.common.util.concurrent.Futures;
71 import com.google.common.util.concurrent.ListenableFuture;
72 import com.google.common.util.concurrent.SettableFuture;
75 * It gathers a set of utility methods that handle ELAN configuration in external Devices (where external means
76 * "not-CSS". As of now: TORs).
78 * It makes use of HwvtepUtils class located under ovsdb/hwvtepsouthbound project for low-level mdsal operations
83 public class ElanL2GatewayUtils {
85 private static DataBroker broker;
86 private static ItmRpcService itmRpcService;
88 private static final Logger LOG = LoggerFactory.getLogger(ElanL2GatewayUtils.class);
91 * Sets the data broker.
96 public static void setDataBroker(DataBroker dataBroker) {
101 * Sets the itm rpc service.
104 * the new itm rpc service
106 public static void setItmRpcService(ItmRpcService itmRpc) {
107 itmRpcService = itmRpc;
111 * Installs the given MAC as a remote mac in all external devices (as of
112 * now, TORs) that participate in the given Elan.
114 * @param elanInstance
115 * Elan to which the interface belongs to
117 * Id of the DPN where the macs are located. Needed for selecting
119 * @param macAddresses
122 public static void installMacsInElanExternalDevices(ElanInstance elanInstance, BigInteger dpId,
123 List<PhysAddress> macAddresses) {
124 String logicalSwitchName = getElanFromLogicalSwitch(elanInstance.getElanInstanceName());
125 ConcurrentMap<String, L2GatewayDevice> elanDevices = ElanL2GwCacheUtils
126 .getAllElanL2GatewayDevicesFromCache(elanInstance.getElanInstanceName());
127 for (L2GatewayDevice externalDevice : elanDevices.values()) {
128 NodeId nodeId = new NodeId(externalDevice.getHwvtepNodeId());
129 IpAddress dpnTepIp = getSourceDpnTepIp(dpId, nodeId);
130 LOG.trace("Dpn Tep IP: {} for dpnId: {} and nodeId: {}", dpnTepIp, dpId, nodeId);
131 if (dpnTepIp == null) {
132 LOG.error("TEP IP not found for dpnId {} and nodeId {}", dpId, nodeId);
135 installMacsInExternalDeviceAsRemoteUcastMacs(externalDevice.getHwvtepNodeId(), macAddresses,
136 logicalSwitchName, dpnTepIp);
141 * Installs a list of Mac Addresses as remote Ucast address in an external
142 * device using the hwvtep-southbound.
144 * @param deviceNodeId
145 * NodeId if the ExternalDevice where the macs must be installed
147 * @param macAddresses
148 * List of Mac addresses to be installed in the external device.
149 * @param logicalSwitchName
150 * the logical switch name
151 * @param remoteVtepIp
152 * VTEP's IP in this CSS used for the tunnel with external
155 private static ListenableFuture<Void> installMacsInExternalDeviceAsRemoteUcastMacs(String deviceNodeId,
156 List<PhysAddress> macAddresses, String logicalSwitchName, IpAddress remoteVtepIp) {
157 NodeId nodeId = new NodeId(deviceNodeId);
158 HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
159 .createHwvtepPhysicalLocatorAugmentation(String.valueOf(remoteVtepIp.getValue()));
160 List<RemoteUcastMacs> macs = new ArrayList<RemoteUcastMacs>();
161 for (PhysAddress mac : macAddresses) {
162 // TODO: Query ARP cache to get IP address corresponding to
164 IpAddress ipAddress = null;
165 macs.add(HwvtepSouthboundUtils.createRemoteUcastMac(nodeId, mac.getValue(), ipAddress, logicalSwitchName,
168 return HwvtepUtils.addRemoteUcastMacs(broker, nodeId, macs);
172 * Install macs in external device as remote ucast macs.
176 * @param lstElanInterfaceNames
177 * the lst Elan interface names
180 * @param externalNodeId
181 * the external node id
182 * @return the listenable future
184 public static ListenableFuture<Void> installMacsInExternalDeviceAsRemoteUcastMacs(String elanName,
185 Set<String> lstElanInterfaceNames, BigInteger dpnId, NodeId externalNodeId) {
186 SettableFuture<Void> future = SettableFuture.create();
188 if (lstElanInterfaceNames == null || lstElanInterfaceNames.isEmpty()) {
192 IpAddress dpnTepIp = getSourceDpnTepIp(dpnId, externalNodeId);
193 if (dpnTepIp == null) {
197 WriteTransaction transaction = broker.newWriteOnlyTransaction();
198 HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepUtils.getPhysicalLocator(broker,
199 LogicalDatastoreType.CONFIGURATION, externalNodeId, dpnTepIp);
200 if (phyLocatorAug == null) {
201 phyLocatorAug = HwvtepSouthboundUtils
202 .createHwvtepPhysicalLocatorAugmentation(String.valueOf(dpnTepIp.getValue()));
203 HwvtepUtils.putPhysicalLocator(transaction, externalNodeId, phyLocatorAug);
206 String logicalSwitchName = getLogicalSwitchFromElan(elanName);
207 for (String interfaceName : lstElanInterfaceNames) {
208 ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(interfaceName);
209 if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) {
210 for (MacEntry macEntry : elanInterfaceMac.getMacEntry()) {
211 // TODO: Query ARP cache to get IP address corresponding to
213 IpAddress ipAddress = null;
214 RemoteUcastMacs mac = HwvtepSouthboundUtils.createRemoteUcastMac(externalNodeId,
215 macEntry.getMacAddress().getValue(), ipAddress, logicalSwitchName, phyLocatorAug);
216 HwvtepUtils.putRemoteUcastMac(transaction, externalNodeId, mac);
220 LOG.debug("Installing macs in external device [{}] for dpn [{}], elan [{}], no of interfaces [{}]",
221 externalNodeId.getValue(), dpnId, elanName, lstElanInterfaceNames.size());
222 return transaction.submit();
226 * Removes the given MAC Addresses from all the External Devices belonging
227 * to the specified ELAN.
229 * @param elanInstance
231 * @param macAddresses
234 public static void removeMacsFromElanExternalDevices(ElanInstance elanInstance, List<PhysAddress> macAddresses) {
235 ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices = ElanL2GwCacheUtils
236 .getAllElanL2GatewayDevicesFromCache(elanInstance.getElanInstanceName());
237 for (L2GatewayDevice l2GatewayDevice : elanL2GwDevices.values()) {
238 removeRemoteUcastMacsFromExternalDevice(l2GatewayDevice.getHwvtepNodeId(),
239 elanInstance.getElanInstanceName(), macAddresses);
244 * Removes the given MAC Addresses from the specified External Device.
246 * @param deviceNodeId
248 * @param logicalSwitchName
249 * @param macAddresses
251 * @return the listenable future
253 private static ListenableFuture<Void> removeRemoteUcastMacsFromExternalDevice(String deviceNodeId,
254 String logicalSwitchName, List<PhysAddress> macAddresses) {
255 NodeId nodeId = new NodeId(deviceNodeId);
258 List<MacAddress> lstMac = Lists.transform(macAddresses, new Function<PhysAddress, MacAddress>() {
260 public MacAddress apply(PhysAddress physAddress) {
261 return (physAddress != null) ? new MacAddress(physAddress.getValue()) : null;
264 return HwvtepUtils.deleteRemoteUcastMacs(broker, nodeId, logicalSwitchName, lstMac);
267 public static ElanInstance getElanInstanceForUcastLocalMac(LocalUcastMacs localUcastMac) {
268 Optional<LogicalSwitches> lsOpc = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL,
269 (InstanceIdentifier<LogicalSwitches>) localUcastMac.getLogicalSwitchRef().getValue());
270 if (lsOpc.isPresent()) {
271 LogicalSwitches ls = lsOpc.get();
273 // Logical switch name is Elan name
274 String elanName = getElanFromLogicalSwitch(ls.getHwvtepNodeName().getValue());
275 return ElanUtils.getElanInstanceByName(elanName);
277 String macAddress = localUcastMac.getMacEntryKey().getValue();
278 LOG.error("Could not find logical_switch for {} being added/deleted", macAddress);
285 * Install external device local macs in dpn.
289 * @param l2gwDeviceNodeId
290 * the l2gw device node id
294 public static void installL2gwDeviceLocalMacsInDpn(BigInteger dpnId, NodeId l2gwDeviceNodeId, ElanInstance elan) {
295 String elanName = elan.getElanInstanceName();
296 L2GatewayDevice l2gwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName,
297 l2gwDeviceNodeId.getValue());
298 if (l2gwDevice == null) {
299 LOG.debug("L2 gw device not found in elan cache for device name {}", l2gwDeviceNodeId.getValue());
303 List<LocalUcastMacs> l2gwDeviceLocalMacs = l2gwDevice.getUcastLocalMacs();
304 if (l2gwDeviceLocalMacs != null && !l2gwDeviceLocalMacs.isEmpty()) {
305 for (LocalUcastMacs localUcastMac : l2gwDeviceLocalMacs) {
306 ElanUtils.installDmacFlowsToExternalRemoteMac(dpnId, l2gwDeviceNodeId.getValue(), elan.getElanTag(),
307 elan.getVni(), localUcastMac.getMacEntryKey().getValue(), elanName);
310 LOG.debug("Installing L2gw device [{}] local macs [size: {}] in dpn [{}] for elan [{}]",
311 l2gwDeviceNodeId.getValue(), l2gwDeviceLocalMacs.size(), dpnId, elanName);
314 public static void installL2GwUcastMacInElan(EntityOwnershipService entityOwnershipService,
315 BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, final ElanInstance elan,
316 L2GatewayDevice extL2GwDevice, final String macToBeAdded) {
317 final String extDeviceNodeId = extL2GwDevice.getHwvtepNodeId();
318 final String elanInstanceName = elan.getElanInstanceName();
320 // Retrieve all participating DPNs in this Elan. Populate this MAC in DMAC table.
321 // Looping through all DPNs in order to add/remove mac flows in their DMAC table
322 List<DpnInterfaces> elanDpns = ElanUtils.getInvolvedDpnsInElan(elanInstanceName);
323 for (DpnInterfaces elanDpn : elanDpns) {
324 final BigInteger dpnId = elanDpn.getDpId();
325 final String nodeId = getNodeIdFromDpnId(dpnId);
327 ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
328 entityOwnershipService, MDSALUtil.NODE_PREFIX, nodeId);
329 Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
331 public void onSuccess(Boolean isOwner) {
333 LOG.info("Installing DMAC flows in {} connected to cluster node owner", dpnId.toString());
335 DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
336 dataStoreCoordinator.enqueueJob(nodeId, new Callable<List<ListenableFuture<Void>>>() {
338 public List<ListenableFuture<Void>> call() throws Exception {
339 return ElanUtils.installDmacFlowsToExternalRemoteMac(dpnId, extDeviceNodeId,
340 elan.getElanTag(), elan.getVni(), macToBeAdded, elanInstanceName);
342 }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
344 LOG.info("Install DMAC flows is not executed on the cluster node as this is not owner " +
345 "for the DPN {}", dpnId.toString());
350 public void onFailure(Throwable error) {
351 LOG.error("Failed to install DMAC flows", error);
356 final IpAddress extL2GwDeviceTepIp = extL2GwDevice.getTunnelIp();
357 final List<PhysAddress> macList = new ArrayList<PhysAddress>();
358 macList.add(new PhysAddress(macToBeAdded));
360 ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices =
361 ElanL2GwCacheUtils.getAllElanL2GatewayDevicesFromCache(elanInstanceName);
362 for (L2GatewayDevice otherDevice : elanL2GwDevices.values()) {
363 if (!otherDevice.getHwvtepNodeId().equals(extDeviceNodeId) && !areMLAGDevices(extL2GwDevice, otherDevice)) {
364 final String hwvtepId = otherDevice.getHwvtepNodeId();
365 InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId));
366 ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
367 entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE,
368 bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid));
369 Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
371 public void onSuccess(Boolean isOwner) {
373 LOG.info("Adding DMAC entry in {} connected to cluster node owner", hwvtepId);
375 DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
376 dataStoreCoordinator.enqueueJob(hwvtepId, new Callable<List<ListenableFuture<Void>>>() {
378 public List<ListenableFuture<Void>> call() throws Exception {
379 final String logicalSwitchName = getLogicalSwitchFromElan(elanInstanceName);
380 ListenableFuture<Void> installFuture = installMacsInExternalDeviceAsRemoteUcastMacs(
381 hwvtepId, macList, logicalSwitchName, extL2GwDeviceTepIp);
383 Futures.addCallback(installFuture, new FutureCallback<Void>() {
385 public void onSuccess(Void noarg) {
386 if (LOG.isTraceEnabled()) {
387 LOG.trace("Successful in initiating ucast_remote_macs addition" +
388 "related to {} in {}", logicalSwitchName, hwvtepId);
393 public void onFailure(Throwable error) {
394 LOG.error(String.format("Failed adding ucast_remote_macs related to " +
395 "%s in %s", logicalSwitchName, hwvtepId), error);
399 return Lists.newArrayList(installFuture);
401 }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
403 LOG.info("DMAC entry addition is not executed on the cluster node as this is not owner for " +
404 "the Hwvtep {}", hwvtepId);
409 public void onFailure(Throwable error) {
410 LOG.error("Failed to install DMAC entry", error);
417 public static void unInstallL2GwUcastMacFromElan(EntityOwnershipService entityOwnershipService,
418 BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, final ElanInstance elan,
419 L2GatewayDevice extL2GwDevice, final LocalUcastMacs macToBeRemoved) {
420 final String extDeviceNodeId = extL2GwDevice.getHwvtepNodeId();
421 final String elanInstanceName = elan.getElanInstanceName();
423 // Retrieve all participating DPNs in this Elan. Populate this MAC in DMAC table.
424 // Looping through all DPNs in order to add/remove mac flows in their DMAC table
425 List<DpnInterfaces> elanDpns = ElanUtils.getInvolvedDpnsInElan(elanInstanceName);
426 for (DpnInterfaces elanDpn : elanDpns) {
427 final BigInteger dpnId = elanDpn.getDpId();
428 final String nodeId = getNodeIdFromDpnId(dpnId);
430 ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
431 entityOwnershipService, MDSALUtil.NODE_PREFIX, nodeId);
432 Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
434 public void onSuccess(Boolean isOwner) {
436 LOG.info("Uninstalling DMAC flows from {} connected to cluster node owner",
439 DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
440 dataStoreCoordinator.enqueueJob(nodeId, new Callable<List<ListenableFuture<Void>>>() {
442 public List<ListenableFuture<Void>> call() throws Exception {
443 return ElanUtils.deleteDmacFlowsToExternalMac(elan.getElanTag(), dpnId,
444 extDeviceNodeId, macToBeRemoved.getMacEntryKey().getValue());
446 }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
448 LOG.info("Uninstall DMAC flows is not executed on the cluster node as this is not owner " +
449 "for the DPN {}", dpnId.toString());
454 public void onFailure(Throwable error) {
455 LOG.error("Failed to uninstall DMAC flows", error);
460 ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices =
461 ElanL2GwCacheUtils.getAllElanL2GatewayDevicesFromCache(elanInstanceName);
462 for (L2GatewayDevice otherDevice : elanL2GwDevices.values()) {
463 if (!otherDevice.getHwvtepNodeId().equals(extDeviceNodeId) && !areMLAGDevices(extL2GwDevice, otherDevice)) {
464 final String hwvtepId = otherDevice.getHwvtepNodeId();
465 final NodeId hwvtepNodeId = new NodeId(hwvtepId);
466 InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(hwvtepNodeId);
467 ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
468 entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE,
469 bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid));
470 Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
472 public void onSuccess(Boolean isOwner) {
474 LOG.info("Removing DMAC entry from {} connected to cluster node owner", hwvtepId);
476 DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
477 dataStoreCoordinator.enqueueJob(hwvtepId, new Callable<List<ListenableFuture<Void>>>() {
479 public List<ListenableFuture<Void>> call() throws Exception {
480 final String logicalSwitchName = getLogicalSwitchFromElan(elanInstanceName);
481 ListenableFuture<Void> uninstallFuture = HwvtepUtils.deleteRemoteUcastMac(broker,
482 hwvtepNodeId, logicalSwitchName, macToBeRemoved.getMacEntryKey());
484 Futures.addCallback(uninstallFuture, new FutureCallback<Void>() {
486 public void onSuccess(Void noarg) {
487 if (LOG.isTraceEnabled()) {
488 LOG.trace("Successful in initiating ucast_remote_macs deletion " +
489 "related to {} in {}", logicalSwitchName, hwvtepId);
494 public void onFailure(Throwable error) {
495 LOG.error(String.format("Failed removing ucast_remote_macs related " +
496 "to %s in %s", logicalSwitchName, hwvtepId), error);
500 return Lists.newArrayList(uninstallFuture);
502 }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
504 LOG.info("DMAC entry removal is not executed on the cluster node as this is not owner for " +
505 "the Hwvtep {}", hwvtepId);
510 public void onFailure(Throwable error) {
511 LOG.error("Failed to uninstall DMAC entry", error);
519 * Delete elan macs from L2 gateway device.<br>
520 * This includes deleting ELAN mac table entries plus external device
521 * UcastLocalMacs which are part of the same ELAN.
523 * @param l2GatewayDevice
524 * the l2 gateway device
527 * @return the listenable future
529 public static ListenableFuture<Void> deleteElanMacsFromL2GatewayDevice(L2GatewayDevice l2GatewayDevice,
531 List<MacAddress> elanMacTableEntries = getElanMacTableEntries(elanName);
532 List<MacAddress> elanL2GatewayDevicesLocalMacs = getElanL2GatewayDevicesLocalMacs(l2GatewayDevice, elanName);
534 List<MacAddress> lstElanLocalMacs = new ArrayList<>(elanMacTableEntries);
535 lstElanLocalMacs.addAll(elanL2GatewayDevicesLocalMacs);
537 return HwvtepUtils.deleteRemoteUcastMacs(broker, new NodeId(l2GatewayDevice.getHwvtepNodeId()),
538 elanName, lstElanLocalMacs);
542 * Gets the elan mac table entries.
546 * @return the elan mac table entries as list
548 public static List<MacAddress> getElanMacTableEntries(String elanName) {
549 MacTable macTable = ElanUtils.getElanMacTable(elanName);
550 if (macTable == null || macTable.getMacEntry() == null || macTable.getMacEntry().isEmpty()) {
551 LOG.trace("MacTable is empty for elan: {}", elanName);
552 return Collections.emptyList();
554 List<MacAddress> lstMacs = Lists.transform(macTable.getMacEntry(), new Function<MacEntry, MacAddress>() {
556 public MacAddress apply(MacEntry macEntry) {
557 return (macEntry != null) ? new MacAddress(macEntry.getMacAddress().getValue()) : null;
564 * Gets the elan l2 gateway devices local macs.
566 * @param l2GwDeviceToBeExcluded
567 * the l2 gw device to be excluded
570 * @return the elan l2 gateway devices local macs
572 public static List<MacAddress> getElanL2GatewayDevicesLocalMacs(L2GatewayDevice l2GwDeviceToBeExcluded,
574 List<MacAddress> lstL2GatewayDeviceMacs = new ArrayList<>();
576 ConcurrentMap<String, L2GatewayDevice> elanL2GwDevicesFromCache = ElanL2GwCacheUtils
577 .getAllElanL2GatewayDevicesFromCache(elanName);
578 if (elanL2GwDevicesFromCache != null) {
579 for (L2GatewayDevice otherDevice : elanL2GwDevicesFromCache.values()) {
580 if (!otherDevice.getHwvtepNodeId().equals(l2GwDeviceToBeExcluded.getHwvtepNodeId())) {
581 List<LocalUcastMacs> lstUcastLocalMacs = otherDevice.getUcastLocalMacs();
582 if (lstUcastLocalMacs != null) {
583 List<MacAddress> l2GwDeviceMacs = Lists.transform(lstUcastLocalMacs,
584 new Function<LocalUcastMacs, MacAddress>() {
586 public MacAddress apply(LocalUcastMacs localUcastMac) {
587 return (localUcastMac != null) ? localUcastMac.getMacEntryKey() : null;
590 lstL2GatewayDeviceMacs.addAll(l2GwDeviceMacs);
595 return lstL2GatewayDeviceMacs;
599 * Install ELAN macs in L2 Gateway device.<br>
600 * This includes installing ELAN mac table entries plus external device
601 * UcastLocalMacs which are part of the same ELAN.
605 * @param l2GatewayDevice
606 * the l2 gateway device which has to be configured
607 * @return the listenable future
609 public static ListenableFuture<Void> installElanMacsInL2GatewayDevice(String elanName,
610 L2GatewayDevice l2GatewayDevice) {
611 String logicalSwitchName = getLogicalSwitchFromElan(elanName);
612 NodeId hwVtepNodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId());
614 List<RemoteUcastMacs> lstL2GatewayDevicesMacs = getL2GatewayDevicesUcastLocalMacsAsRemoteUcastMacs(elanName,
615 l2GatewayDevice, hwVtepNodeId, logicalSwitchName);
616 List<RemoteUcastMacs> lstElanMacTableEntries = getElanMacTableEntriesAsRemoteUcastMacs(elanName,
617 l2GatewayDevice, hwVtepNodeId, logicalSwitchName);
619 List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<>(lstL2GatewayDevicesMacs);
620 lstRemoteUcastMacs.addAll(lstElanMacTableEntries);
622 ListenableFuture<Void> future = HwvtepUtils.addRemoteUcastMacs(broker, hwVtepNodeId, lstRemoteUcastMacs);
624 LOG.info("Added RemoteUcastMacs entries [{}] in config DS. NodeID: {}, LogicalSwitch: {}",
625 lstRemoteUcastMacs.size(), hwVtepNodeId.getValue(), logicalSwitchName);
630 * Gets the l2 gateway devices ucast local macs as remote ucast macs.
634 * @param l2GatewayDeviceToBeConfigured
635 * the l2 gateway device to be configured
636 * @param hwVtepNodeId
637 * the hw vtep node Id to be configured
638 * @param logicalSwitchName
639 * the logical switch name
640 * @return the l2 gateway devices macs as remote ucast macs
642 public static List<RemoteUcastMacs> getL2GatewayDevicesUcastLocalMacsAsRemoteUcastMacs(String elanName,
643 L2GatewayDevice l2GatewayDeviceToBeConfigured, NodeId hwVtepNodeId, String logicalSwitchName) {
644 List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<RemoteUcastMacs>();
645 ConcurrentMap<String, L2GatewayDevice> elanL2GwDevicesFromCache = ElanL2GwCacheUtils
646 .getAllElanL2GatewayDevicesFromCache(elanName);
648 if (elanL2GwDevicesFromCache != null) {
649 for (L2GatewayDevice otherDevice : elanL2GwDevicesFromCache.values()) {
650 if (l2GatewayDeviceToBeConfigured.getHwvtepNodeId().equals(otherDevice.getHwvtepNodeId())) {
653 if (!areMLAGDevices(l2GatewayDeviceToBeConfigured, otherDevice)) {
654 List<LocalUcastMacs> lstUcastLocalMacs = otherDevice.getUcastLocalMacs();
655 if (lstUcastLocalMacs != null) {
656 for (LocalUcastMacs localUcastMac : lstUcastLocalMacs) {
657 HwvtepPhysicalLocatorAugmentation physLocatorAug = HwvtepSouthboundUtils
658 .createHwvtepPhysicalLocatorAugmentation(
659 String.valueOf(otherDevice.getTunnelIp().getValue()));
660 RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(hwVtepNodeId,
661 localUcastMac.getMacEntryKey().getValue(), localUcastMac.getIpaddr(),
662 logicalSwitchName, physLocatorAug);
663 lstRemoteUcastMacs.add(remoteUcastMac);
669 return lstRemoteUcastMacs;
675 * @param l2GatewayDevice
676 * the l2 gateway device
677 * @param otherL2GatewayDevice
678 * the other l2 gateway device
679 * @return true, if both the specified l2 gateway devices are part of same
682 public static boolean areMLAGDevices(L2GatewayDevice l2GatewayDevice, L2GatewayDevice otherL2GatewayDevice) {
683 // If tunnel IPs are same, then it is considered to be part of same MLAG
684 return Objects.equals(l2GatewayDevice.getTunnelIp(), otherL2GatewayDevice.getTunnelIp());
688 * Gets the elan mac table entries as remote ucast macs. <br>
689 * Note: ELAN MAC table only contains internal switches MAC's. It doesn't
690 * contain external device MAC's.
694 * @param l2GatewayDeviceToBeConfigured
695 * the l2 gateway device to be configured
696 * @param hwVtepNodeId
697 * the hw vtep node id
698 * @param logicalSwitchName
699 * the logical switch name
700 * @return the elan mac table entries as remote ucast macs
702 public static List<RemoteUcastMacs> getElanMacTableEntriesAsRemoteUcastMacs(String elanName,
703 L2GatewayDevice l2GatewayDeviceToBeConfigured, NodeId hwVtepNodeId, String logicalSwitchName) {
704 List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<RemoteUcastMacs>();
706 MacTable macTable = ElanUtils.getElanMacTable(elanName);
707 if (macTable == null || macTable.getMacEntry() == null || macTable.getMacEntry().isEmpty()) {
708 LOG.trace("MacTable is empty for elan: {}", elanName);
709 return lstRemoteUcastMacs;
712 for (MacEntry macEntry : macTable.getMacEntry()) {
713 BigInteger dpnId = ElanUtils.getDpidFromInterface(macEntry.getInterface());
715 LOG.error("DPN ID not found for interface {}", macEntry.getInterface());
719 IpAddress dpnTepIp = getSourceDpnTepIp(dpnId, hwVtepNodeId);
720 LOG.trace("Dpn Tep IP: {} for dpnId: {} and nodeId: {}", dpnTepIp, dpnId, hwVtepNodeId.getValue());
721 if (dpnTepIp == null) {
722 LOG.error("TEP IP not found for dpnId {} and nodeId {}", dpnId, hwVtepNodeId.getValue());
725 HwvtepPhysicalLocatorAugmentation physLocatorAug = HwvtepSouthboundUtils
726 .createHwvtepPhysicalLocatorAugmentation(String.valueOf(dpnTepIp.getValue()));
727 // TODO: Query ARP cache to get IP address corresponding to the
729 IpAddress ipAddress = null;
730 RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(hwVtepNodeId,
731 macEntry.getMacAddress().getValue(), ipAddress, logicalSwitchName, physLocatorAug);
732 lstRemoteUcastMacs.add(remoteUcastMac);
734 return lstRemoteUcastMacs;
738 * Gets the external tunnel interface name.
744 * @return the external tunnel interface name
746 public static String getExternalTunnelInterfaceName(String sourceNode, String dstNode) {
747 String tunnelInterfaceName = null;
749 Future<RpcResult<GetExternalTunnelInterfaceNameOutput>> output = itmRpcService
750 .getExternalTunnelInterfaceName(new GetExternalTunnelInterfaceNameInputBuilder()
751 .setSourceNode(sourceNode).setDestinationNode(dstNode).build());
753 RpcResult<GetExternalTunnelInterfaceNameOutput> rpcResult = output.get();
754 if (rpcResult.isSuccessful()) {
755 tunnelInterfaceName = rpcResult.getResult().getInterfaceName();
756 LOG.debug("Tunnel interface name: {} for sourceNode: {} and dstNode: {}", tunnelInterfaceName,
757 sourceNode, dstNode);
759 LOG.warn("RPC call to ITM.GetExternalTunnelInterfaceName failed with error: {}",
760 rpcResult.getErrors());
762 } catch (NullPointerException | InterruptedException | ExecutionException e) {
763 LOG.error("Failed to get external tunnel interface name for sourceNode: {} and dstNode: {}: {} ",
764 sourceNode, dstNode, e);
766 return tunnelInterfaceName;
770 * Gets the source dpn tep ip.
774 * @param dstHwVtepNodeId
775 * the dst hw vtep node id
776 * @return the dpn tep ip
778 public static IpAddress getSourceDpnTepIp(BigInteger srcDpnId, NodeId dstHwVtepNodeId) {
779 IpAddress dpnTepIp = null;
780 String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(srcDpnId),
781 dstHwVtepNodeId.getValue());
782 if (tunnelInterfaceName != null) {
783 Interface tunnelInterface = getInterfaceFromConfigDS(new InterfaceKey(tunnelInterfaceName), broker);
784 if (tunnelInterface != null) {
785 dpnTepIp = tunnelInterface.getAugmentation(IfTunnel.class).getTunnelSource();
787 LOG.warn("Tunnel interface not found for tunnelInterfaceName {}", tunnelInterfaceName);
790 LOG.warn("Tunnel interface name not found for srcDpnId {} and dstHwVtepNodeId {}", srcDpnId,
797 * Update vlan bindings in l2 gateway device.
801 * @param logicalSwitchName
802 * the logical switch name
803 * @param hwVtepDevice
804 * the hardware device
805 * @param defaultVlanId
806 * the default vlan id
807 * @return the listenable future
809 public static ListenableFuture<Void> updateVlanBindingsInL2GatewayDevice(NodeId nodeId, String logicalSwitchName,
810 Devices hwVtepDevice, Integer defaultVlanId) {
811 if (hwVtepDevice == null || hwVtepDevice.getInterfaces() == null || hwVtepDevice.getInterfaces().isEmpty()) {
812 String errMsg = "HwVtepDevice is null or interfaces are empty.";
814 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
817 WriteTransaction transaction = broker.newWriteOnlyTransaction();
818 for (org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.devices.Interfaces deviceInterface : hwVtepDevice
820 List<VlanBindings> vlanBindings = new ArrayList<>();
821 if (deviceInterface.getSegmentationIds() != null && !deviceInterface.getSegmentationIds().isEmpty()) {
822 for (Integer vlanId : deviceInterface.getSegmentationIds()) {
823 vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(nodeId, vlanId, logicalSwitchName));
826 // Use defaultVlanId (specified in L2GatewayConnection) if Vlan
827 // ID not specified at interface level.
828 vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(nodeId, defaultVlanId, logicalSwitchName));
830 HwvtepUtils.mergeVlanBindings(transaction, nodeId, hwVtepDevice.getDeviceName(),
831 deviceInterface.getInterfaceName(), vlanBindings);
833 ListenableFuture<Void> future = transaction.submit();
834 LOG.info("Updated Hwvtep VlanBindings in config DS. NodeID: {}, LogicalSwitch: {}", nodeId.getValue(),
840 * Delete vlan bindings from l2 gateway device.
844 * @param hwVtepDevice
846 * @param defaultVlanId
847 * the default vlan id
848 * @return the listenable future
850 public static ListenableFuture<Void> deleteVlanBindingsFromL2GatewayDevice(NodeId nodeId, Devices hwVtepDevice,
851 Integer defaultVlanId) {
852 if (hwVtepDevice == null || hwVtepDevice.getInterfaces() == null || hwVtepDevice.getInterfaces().isEmpty()) {
853 String errMsg = "HwVtepDevice is null or interfaces are empty.";
855 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
857 NodeId physicalSwitchNodeId = HwvtepSouthboundUtils.createManagedNodeId(nodeId, hwVtepDevice.getDeviceName());
859 WriteTransaction transaction = broker.newWriteOnlyTransaction();
860 for (org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.devices.Interfaces deviceInterface : hwVtepDevice
862 String phyPortName = deviceInterface.getInterfaceName();
863 if (deviceInterface.getSegmentationIds() != null && !deviceInterface.getSegmentationIds().isEmpty()) {
864 for (Integer vlanId : deviceInterface.getSegmentationIds()) {
865 HwvtepUtils.deleteVlanBinding(transaction, physicalSwitchNodeId, phyPortName, vlanId);
868 // Use defaultVlanId (specified in L2GatewayConnection) if Vlan
869 // ID not specified at interface level.
870 HwvtepUtils.deleteVlanBinding(transaction, physicalSwitchNodeId, phyPortName, defaultVlanId);
873 ListenableFuture<Void> future = transaction.submit();
875 LOG.info("Deleted Hwvtep VlanBindings from config DS. NodeID: {}, hwVtepDevice: {}, defaultVlanId: {} ",
876 nodeId.getValue(), hwVtepDevice, defaultVlanId);
881 * Gets the elan name from logical switch name.
883 * @param logicalSwitchName
884 * the logical switch name
885 * @return the elan name from logical switch name
887 public static String getElanFromLogicalSwitch(String logicalSwitchName) {
888 // Assuming elan name is same as logical switch name
889 String elanName = logicalSwitchName;
894 * Gets the logical switch name from elan name.
898 * @return the logical switch from elan name
900 public static String getLogicalSwitchFromElan(String elanName) {
901 // Assuming logical switch name is same as elan name
902 String logicalSwitchName = elanName;
903 return logicalSwitchName;
907 * Gets the l2 gateway connection job key.
911 * @param logicalSwitchName
912 * the logical switch name
913 * @return the l2 gateway connection job key
915 public static String getL2GatewayConnectionJobKey(String nodeId, String logicalSwitchName) {
916 return new StringBuilder(nodeId).append(logicalSwitchName).toString();
919 public static InstanceIdentifier<Interface> getInterfaceIdentifier(InterfaceKey interfaceKey) {
920 InstanceIdentifier.InstanceIdentifierBuilder<Interface> interfaceInstanceIdentifierBuilder =
921 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, interfaceKey);
922 return interfaceInstanceIdentifierBuilder.build();
925 public static Interface getInterfaceFromConfigDS(InterfaceKey interfaceKey, DataBroker dataBroker) {
926 InstanceIdentifier<Interface> interfaceId = getInterfaceIdentifier(interfaceKey);
927 Optional<Interface> interfaceOptional = IfmUtil.read(LogicalDatastoreType.CONFIGURATION, interfaceId, dataBroker);
928 if (!interfaceOptional.isPresent()) {
932 return interfaceOptional.get();
936 * Delete l2 gateway device ucast local macs from elan.<br>
937 * Deletes macs from internal ELAN nodes and also on rest of external l2
938 * gateway devices which are part of the ELAN.
940 * @param l2GatewayDevice
941 * the l2 gateway device whose ucast local macs to be deleted
945 * @return the listenable future
947 public static List<ListenableFuture<Void>> deleteL2GatewayDeviceUcastLocalMacsFromElan(
948 L2GatewayDevice l2GatewayDevice, String elanName) {
949 List<ListenableFuture<Void>> futures = new ArrayList<>();
951 ElanInstance elan = ElanUtils.getElanInstanceByName(elanName);
953 LOG.error("Could not find Elan by name: {}", elanName);
957 List<LocalUcastMacs> lstLocalUcastMacs = l2GatewayDevice.getUcastLocalMacs();
958 if (lstLocalUcastMacs != null) {
959 for (LocalUcastMacs localUcastMac : lstLocalUcastMacs) {
960 List<DpnInterfaces> dpnInterfaces = ElanUtils.getInvolvedDpnsInElan(elanName);
961 if (dpnInterfaces != null) {
962 // TODO: Need to check if it can be optimized
963 for (DpnInterfaces elanDpn : dpnInterfaces) {
964 ElanUtils.deleteDmacFlowsToExternalMac(elan.getElanTag(), elanDpn.getDpId(),
965 l2GatewayDevice.getHwvtepNodeId(), localUcastMac.getMacEntryKey().getValue());
970 List<MacAddress> lstMac = Lists.transform(lstLocalUcastMacs, new Function<LocalUcastMacs, MacAddress>() {
972 public MacAddress apply(LocalUcastMacs mac) {
973 return (mac != null) ? mac.getMacEntryKey() : null;
977 ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices = ElanL2GwCacheUtils
978 .getAllElanL2GatewayDevicesFromCache(elanName);
979 for (L2GatewayDevice otherDevice : elanL2GwDevices.values()) {
980 if (!otherDevice.getHwvtepNodeId().equals(l2GatewayDevice.getHwvtepNodeId())) {
981 futures.add(HwvtepUtils.deleteRemoteUcastMacs(broker, new NodeId(otherDevice.getHwvtepNodeId()),
989 public static void createItmTunnels(ItmRpcService itmRpcService, String hwvtepId, String psName,
990 IpAddress tunnelIp) {
991 AddL2GwDeviceInputBuilder builder = new AddL2GwDeviceInputBuilder();
992 builder.setTopologyId(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID.getValue());
993 builder.setNodeId(HwvtepSouthboundUtils.createManagedNodeId(new NodeId(hwvtepId), psName).getValue());
994 builder.setIpAddress(tunnelIp);
996 Future<RpcResult<Void>> result = itmRpcService.addL2GwDevice(builder.build());
997 RpcResult<Void> rpcResult = result.get();
998 if (rpcResult.isSuccessful()) {
999 LOG.info("Created ITM tunnels for {}", hwvtepId);
1001 LOG.error("Failed to create ITM Tunnels: ", rpcResult.getErrors());
1003 } catch (InterruptedException | ExecutionException e) {
1004 LOG.error("RPC to create ITM tunnels failed", e);
1008 public static String getNodeIdFromDpnId(BigInteger dpnId) {
1009 return MDSALUtil.NODE_PREFIX + MDSALUtil.SEPARATOR + dpnId.toString();