MRI version bumpup for Aluminium
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / utils / ElanL2GatewayUtils.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netvirt.elan.l2gw.utils;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11
12 import com.google.common.collect.Lists;
13 import com.google.common.util.concurrent.FluentFuture;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Locale;
24 import java.util.Map;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.ScheduledFuture;
33 import java.util.concurrent.TimeUnit;
34 import java.util.function.Function;
35 import java.util.stream.Collectors;
36 import javax.annotation.PreDestroy;
37 import javax.inject.Inject;
38 import javax.inject.Singleton;
39 import org.apache.commons.lang3.tuple.ImmutablePair;
40 import org.apache.commons.lang3.tuple.Pair;
41 import org.eclipse.jdt.annotation.NonNull;
42 import org.eclipse.jdt.annotation.Nullable;
43 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
44 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
45 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
46 import org.opendaylight.genius.utils.SystemPropertyReader;
47 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
48 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
49 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
50 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
51 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
52 import org.opendaylight.mdsal.binding.api.DataBroker;
53 import org.opendaylight.mdsal.common.api.CommitInfo;
54 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
55 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
56 import org.opendaylight.netvirt.elan.cache.ElanInstanceDpnsCache;
57 import org.opendaylight.netvirt.elan.l2gw.ha.HwvtepHAUtil;
58 import org.opendaylight.netvirt.elan.l2gw.jobs.DeleteL2GwDeviceMacsFromElanJob;
59 import org.opendaylight.netvirt.elan.l2gw.jobs.DeleteLogicalSwitchJob;
60 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
61 import org.opendaylight.netvirt.elan.utils.ElanConstants;
62 import org.opendaylight.netvirt.elan.utils.ElanDmacUtils;
63 import org.opendaylight.netvirt.elan.utils.ElanItmUtils;
64 import org.opendaylight.netvirt.elan.utils.ElanUtils;
65 import org.opendaylight.netvirt.elan.utils.Scheduler;
66 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
67 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
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;
70 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
71 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
72 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.IetfYangUtil;
73 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
74 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInputBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceOutput;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.DeviceVteps;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.AddL2GwDeviceInputBuilder;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.AddL2GwDeviceOutput;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.config.rev150710.ElanConfig;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.forwarding.tables.MacTable;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ExternalTeps;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntry;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepLogicalSwitchRef;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepMacTableGenericAttributes;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorRef;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LocalUcastMacs;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsKey;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteUcastMacs;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteUcastMacsKey;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.port.attributes.VlanBindings;
106 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
107 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
108 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
109 import org.opendaylight.yangtools.yang.common.RpcResult;
110 import org.opendaylight.yangtools.yang.common.Uint64;
111 import org.slf4j.Logger;
112 import org.slf4j.LoggerFactory;
113
114 /**
115  * It gathers a set of utility methods that handle ELAN configuration in
116  * external Devices (where external means "not-CSS". As of now: TORs).
117  *
118  * <p>It makes use of HwvtepUtils class located under ovsdb/hwvtepsouthbound
119  * project for low-level mdsal operations
120  *
121  * @author eperefr
122  */
123 @Singleton
124 public class ElanL2GatewayUtils {
125     private static final Logger LOG = LoggerFactory.getLogger(ElanL2GatewayUtils.class);
126     private static final int DEFAULT_LOGICAL_SWITCH_DELETE_DELAY_SECS = 20;
127
128     private final DataBroker broker;
129     private final ManagedNewTransactionRunner txRunner;
130     private final ElanDmacUtils elanDmacUtils;
131     private final ElanItmUtils elanItmUtils;
132     private final ElanClusterUtils elanClusterUtils;
133     private final OdlInterfaceRpcService interfaceManagerRpcService;
134     private final JobCoordinator jobCoordinator;
135     private final ElanUtils elanUtils;
136     private final ElanInstanceCache elanInstanceCache;
137     private final ElanInstanceDpnsCache elanInstanceDpnsCache;
138
139     private final ConcurrentMap<Pair<NodeId, String>, ScheduledFuture> logicalSwitchDeletedTasks
140             = new ConcurrentHashMap<>();
141     private final ConcurrentMap<Pair<NodeId, String>, DeleteLogicalSwitchJob> deleteJobs = new ConcurrentHashMap<>();
142     private final Scheduler scheduler;
143     private final ElanConfig elanConfig;
144
145     @Inject
146     public ElanL2GatewayUtils(DataBroker broker, ElanDmacUtils elanDmacUtils, ElanItmUtils elanItmUtils,
147             ElanClusterUtils elanClusterUtils, OdlInterfaceRpcService interfaceManagerRpcService,
148             JobCoordinator jobCoordinator, ElanUtils elanUtils,
149             Scheduler scheduler, ElanConfig elanConfig, ElanInstanceCache elanInstanceCache,
150             ElanInstanceDpnsCache elanInstanceDpnsCache) {
151         this.broker = broker;
152         this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
153         this.elanDmacUtils = elanDmacUtils;
154         this.elanItmUtils = elanItmUtils;
155         this.elanClusterUtils = elanClusterUtils;
156         this.interfaceManagerRpcService = interfaceManagerRpcService;
157         this.jobCoordinator = jobCoordinator;
158         this.elanUtils = elanUtils;
159         this.scheduler = scheduler;
160         this.elanConfig = elanConfig;
161         this.elanInstanceCache = elanInstanceCache;
162         this.elanInstanceDpnsCache = elanInstanceDpnsCache;
163     }
164
165     @PreDestroy
166     public void close() {
167     }
168
169     public long getLogicalSwitchDeleteDelaySecs() {
170         return elanConfig.getL2gwLogicalSwitchDelaySecs() != null
171                 ? elanConfig.getL2gwLogicalSwitchDelaySecs().toJava() : DEFAULT_LOGICAL_SWITCH_DELETE_DELAY_SECS;
172     }
173
174     /**
175      * gets the macs addresses for elan interfaces.
176      *
177      * @param lstElanInterfaceNames
178      *            the lst elan interface names
179      * @return the list
180      */
181     public List<PhysAddress> getElanDpnMacsFromInterfaces(Set<String> lstElanInterfaceNames) {
182         List<PhysAddress> result = new ArrayList<>();
183         for (String interfaceName : lstElanInterfaceNames) {
184             ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(broker, interfaceName);
185             if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) {
186                 for (MacEntry macEntry : new ArrayList<MacEntry>(elanInterfaceMac.getMacEntry().values())) {
187                     result.add(macEntry.getMacAddress());
188                 }
189             }
190         }
191         return result;
192     }
193
194     /**
195      * Check if phy locator already exists in remote mcast entry.
196      *
197      * @param nodeId
198      *            the node id
199      * @param remoteMcastMac
200      *            the remote mcast mac
201      * @param expectedPhyLocatorIp
202      *            the expected phy locator ip
203      * @return true, if successful
204      */
205     public static boolean checkIfPhyLocatorAlreadyExistsInRemoteMcastEntry(NodeId nodeId,
206             RemoteMcastMacs remoteMcastMac, IpAddress expectedPhyLocatorIp) {
207         if (remoteMcastMac != null) {
208             HwvtepPhysicalLocatorAugmentation expectedPhyLocatorAug = HwvtepSouthboundUtils
209                     .createHwvtepPhysicalLocatorAugmentation(expectedPhyLocatorIp);
210             HwvtepPhysicalLocatorRef expectedPhyLocRef = new HwvtepPhysicalLocatorRef(
211                     HwvtepSouthboundUtils.createPhysicalLocatorInstanceIdentifier(nodeId, expectedPhyLocatorAug));
212             if (remoteMcastMac.getLocatorSet() != null) {
213                 for (LocatorSet locatorSet : remoteMcastMac.getLocatorSet()) {
214                     if (Objects.equals(locatorSet.getLocatorRef(), expectedPhyLocRef)) {
215                         LOG.trace("matched phyLocRef: {}", expectedPhyLocRef);
216                         return true;
217                     }
218                 }
219             }
220         }
221         return false;
222     }
223
224     /**
225      * Gets the remote mcast mac.
226      *
227      * @param nodeId
228      *            the node id
229      * @param logicalSwitchName
230      *            the logical switch name
231      * @param datastoreType
232      *            the datastore type
233      * @return the remote mcast mac
234      */
235     public RemoteMcastMacs readRemoteMcastMac(NodeId nodeId, String logicalSwitchName,
236             LogicalDatastoreType datastoreType) {
237         InstanceIdentifier<LogicalSwitches> logicalSwitch = HwvtepSouthboundUtils
238                 .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName));
239         RemoteMcastMacsKey remoteMcastMacsKey = new RemoteMcastMacsKey(new HwvtepLogicalSwitchRef(logicalSwitch),
240                 new MacAddress(ElanConstants.UNKNOWN_DMAC));
241         try {
242             return HwvtepUtils.getRemoteMcastMac(broker, datastoreType, nodeId, remoteMcastMacsKey);
243         } catch (ExecutionException | InterruptedException e) {
244             LOG.error("readRemoteMcastMac: Exception while reading LogicalSwitches DS for the nodeId {}, "
245                     + "logicalSwitchName {}", nodeId.getValue(), logicalSwitch, e);
246         }
247         return null;
248     }
249
250     /**
251      * Removes the given MAC Addresses from all the External Devices belonging
252      * to the specified ELAN.
253      *
254      * @param elanInstance
255      *            the elan instance
256      * @param macAddresses
257      *            the mac addresses
258      */
259     public void removeMacsFromElanExternalDevices(ElanInstance elanInstance, List<PhysAddress> macAddresses) {
260         String elanName = elanInstance.getElanInstanceName();
261         for (L2GatewayDevice l2GatewayDevice : ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName)) {
262             removeRemoteUcastMacsFromExternalDevice(l2GatewayDevice.getHwvtepNodeId(), elanName, macAddresses);
263         }
264     }
265
266     /**
267      * Removes the given MAC Addresses from the specified External Device.
268      *
269      * @param deviceNodeId
270      *            the device node id
271      * @param macAddresses
272      *            the mac addresses
273      * @return the listenable future
274      */
275     private FluentFuture<? extends CommitInfo> removeRemoteUcastMacsFromExternalDevice(String deviceNodeId,
276                                                                                        String logicalSwitchName,
277                                                                                        List<PhysAddress> macAddresses) {
278         NodeId nodeId = new NodeId(deviceNodeId);
279
280         // TODO (eperefr)
281         List<MacAddress> lstMac = macAddresses.stream().filter(Objects::nonNull).map(
282             physAddress -> new MacAddress(physAddress.getValue())).collect(Collectors.toList());
283         return HwvtepUtils.deleteRemoteUcastMacs(broker, nodeId, logicalSwitchName, lstMac);
284     }
285
286     /**
287      * Install external device local macs in dpn.
288      *
289      * @param dpnId
290      *            the dpn id
291      * @param l2gwDeviceNodeId
292      *            the l2gw device node id
293      * @param elan
294      *            the elan
295      * @param interfaceName
296      *            the interface name
297      */
298     public void installL2gwDeviceMacsInDpn(Uint64 dpnId, NodeId l2gwDeviceNodeId, ElanInstance elan,
299             String interfaceName) {
300         L2GatewayDevice l2gwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elan.getElanInstanceName(),
301                 l2gwDeviceNodeId.getValue());
302         if (l2gwDevice == null) {
303             LOG.debug("L2 gw device not found in elan cache for device name {}", l2gwDeviceNodeId.getValue());
304             return;
305         }
306
307         installDmacFlowsOnDpn(dpnId, l2gwDevice, elan, interfaceName);
308     }
309
310     /**
311      * Install dmac flows on dpn.
312      *
313      * @param dpnId
314      *            the dpn id
315      * @param l2gwDevice
316      *            the l2gw device
317      * @param elan
318      *            the elan
319      * @param interfaceName
320      *            the interface name
321      */
322     public List<ListenableFuture<Void>> installDmacFlowsOnDpn(Uint64 dpnId, L2GatewayDevice l2gwDevice,
323                                                               ElanInstance elan, String interfaceName) {
324         String elanName = elan.getElanInstanceName();
325         List<ListenableFuture<Void>> fts = new ArrayList<>();
326         Collection<LocalUcastMacs> l2gwDeviceLocalMacs = l2gwDevice.getUcastLocalMacs();
327         if (!l2gwDeviceLocalMacs.isEmpty()) {
328             for (LocalUcastMacs localUcastMac : l2gwDeviceLocalMacs) {
329                 fts.addAll(elanDmacUtils.installDmacFlowsToExternalRemoteMacInBatch(dpnId, l2gwDevice.getHwvtepNodeId(),
330                         elan.getElanTag().toJava(), ElanUtils.getVxlanSegmentationId(elan).longValue(),
331                         localUcastMac.getMacEntryKey().getValue(), elanName, interfaceName));
332             }
333             LOG.debug("Installing L2gw device [{}] local macs [size: {}] in dpn [{}] for elan [{}]",
334                     l2gwDevice.getHwvtepNodeId(), l2gwDeviceLocalMacs.size(), dpnId, elanName);
335         }
336         return fts;
337     }
338
339     /**
340      * Install elan l2gw devices local macs in dpn.
341      *
342      * @param dpnId
343      *            the dpn id
344      * @param elan
345      *            the elan
346      * @param interfaceName
347      *            the interface name
348      */
349     public void installElanL2gwDevicesLocalMacsInDpn(Uint64 dpnId, ElanInstance elan, String interfaceName) {
350         for (L2GatewayDevice l2gwDevice : ElanL2GwCacheUtils.getInvolvedL2GwDevices(elan.getElanInstanceName())) {
351             installDmacFlowsOnDpn(dpnId, l2gwDevice, elan, interfaceName);
352         }
353     }
354
355     public void installL2GwUcastMacInElan(final ElanInstance elan, final L2GatewayDevice extL2GwDevice,
356             final String macToBeAdded, final LocalUcastMacs localUcastMacs, @Nullable String interfaceName) {
357         final String extDeviceNodeId = extL2GwDevice.getHwvtepNodeId();
358         final String elanInstanceName = elan.getElanInstanceName();
359         final Collection<DpnInterfaces> elanDpns = getElanDpns(elanInstanceName);
360         Collection<L2GatewayDevice> elanL2GwDevices = ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanInstanceName);
361
362         // Retrieve all participating DPNs in this Elan. Populate this MAC in
363         // DMAC table.
364         // Looping through all DPNs in order to add/remove mac flows in their
365         // DMAC table
366         if (elanDpns.size() > 0 || elanL2GwDevices.size() > 0) {
367             String jobKey = elanInstanceName + ":" + macToBeAdded;
368             IpAddress extL2GwDeviceTepIp = extL2GwDevice.getTunnelIp();
369             List<PhysAddress> macList = Lists.newArrayList(new PhysAddress(macToBeAdded));
370
371             elanClusterUtils.runOnlyInOwnerNode(jobKey, "install l2gw macs in dmac table", () -> {
372                 if (doesLocalUcastMacExistsInCache(extL2GwDevice, localUcastMacs)) {
373                     List<ListenableFuture<Void>> futures = new ArrayList<>();
374                     for (DpnInterfaces elanDpn : elanDpns) {
375                         futures.addAll(elanDmacUtils.installDmacFlowsToExternalRemoteMacInBatch(
376                                 elanDpn.getDpId(),
377                                 extDeviceNodeId, elan.getElanTag().toJava(),
378                                 ElanUtils.getVxlanSegmentationId(elan).longValue(),
379                                 macToBeAdded, elanInstanceName, interfaceName));
380                     }
381                     for (L2GatewayDevice otherDevice : elanL2GwDevices) {
382                         if (!otherDevice.getHwvtepNodeId().equals(extDeviceNodeId)
383                                 && !areMLAGDevices(extL2GwDevice, otherDevice)) {
384                             final String hwvtepId = otherDevice.getHwvtepNodeId();
385                             final String logicalSwitchName = elanInstanceName;
386                             HwvtepUtils.installUcastMacs(broker, hwvtepId, macList, logicalSwitchName,
387                                     extL2GwDeviceTepIp);
388                         }
389                     }
390                     return futures;
391                 } else {
392                     LOG.trace("Skipping install of dmac flows for mac {} as it is not found in cache",
393                             macToBeAdded);
394                 }
395                 return Collections.emptyList();
396             });
397         }
398     }
399
400     /**
401      * Does local ucast mac exists in cache.
402      *
403      * @param elanL2GwDevice
404      *            the elan L2 Gw device
405      * @param macAddress
406      *            the mac address to be verified
407      * @return true, if successful
408      */
409     private static boolean doesLocalUcastMacExistsInCache(L2GatewayDevice elanL2GwDevice, LocalUcastMacs macAddress) {
410         return elanL2GwDevice.containsUcastMac(macAddress);
411     }
412
413     /**
414      * Uninstall l2gw macs from other l2gw devices in the elanName provided.
415      * @param elanName - Elan Name for which other l2gw devices will be scanned.
416      * @param l2GwDevice - l2gwDevice whose macs are required to be cleared from other devices.
417      * @param macAddresses - Mac address to be cleared.
418      */
419     public void unInstallL2GwUcastMacFromL2gwDevices(final String elanName,
420                                                      final L2GatewayDevice l2GwDevice,
421                                                      final Collection<MacAddress> macAddresses) {
422         if (macAddresses == null || macAddresses.isEmpty()) {
423             return;
424         }
425
426         if (elanName == null) {
427             return;
428         }
429
430         DeleteL2GwDeviceMacsFromElanJob job = new DeleteL2GwDeviceMacsFromElanJob(elanName, l2GwDevice,
431                 macAddresses);
432         elanClusterUtils.runOnlyInOwnerNode(job.getJobKey(), "delete remote ucast macs in l2gw devices", job);
433     }
434
435     /**
436      * Uninstall l2gw macs from other DPNs in the elan instance provided.
437      * @param elan - Elan Instance for which other DPNs will be scanned.
438      * @param l2GwDevice - l2gwDevice whose macs are required to be cleared from other devices.
439      * @param macAddresses - Mac address to be cleared.
440      */
441     public void unInstallL2GwUcastMacFromElanDpns(final ElanInstance elan, final L2GatewayDevice l2GwDevice,
442                                                   final Collection<MacAddress> macAddresses) {
443         if (macAddresses == null || macAddresses.isEmpty()) {
444             return;
445         }
446         if (elan == null || elan.getElanInstanceName() == null) {
447             LOG.error("Could not delete l2gw ucast macs, Failed to find the elan for device {}",
448                     l2GwDevice.getHwvtepNodeId());
449             return;
450         }
451
452         final Collection<DpnInterfaces> elanDpns = getElanDpns(elan.getElanInstanceName());
453
454         // Retrieve all participating DPNs in this Elan. Populate this MAC in
455         // DMAC table. Looping through all DPNs in order to add/remove mac flows
456         // in their DMAC table
457         List<ListenableFuture<Void>> result = new ArrayList<>();
458         for (final MacAddress mac : macAddresses) {
459             elanClusterUtils.runOnlyInOwnerNode(elan.getElanInstanceName() + ":" + mac.getValue(),
460                     "delete remote ucast macs in elan DPNs", () -> {
461                     for (DpnInterfaces elanDpn : elanDpns) {
462                         Uint64 dpnId = elanDpn.getDpId();
463                         result.addAll(elanDmacUtils.deleteDmacFlowsToExternalMac(elan.getElanTag().toJava(), dpnId,
464                                 l2GwDevice.getHwvtepNodeId(),
465                                 IetfYangUtil.INSTANCE.canonizeMacAddress(mac).getValue()));
466                     }
467                     return result;
468                 });
469         }
470     }
471
472     /**
473      * Delete elan l2 gateway devices ucast local macs from dpn.
474      *
475      * @param elanName
476      *            the elan name
477      * @param dpnId
478      *            the dpn id
479      */
480     public void deleteElanL2GwDevicesUcastLocalMacsFromDpn(final String elanName, final Uint64 dpnId) {
481         Collection<L2GatewayDevice> elanL2GwDevices = ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName);
482         if (elanL2GwDevices.isEmpty()) {
483             LOG.trace("No L2 gateway devices in Elan [{}] cache.", elanName);
484             return;
485         }
486         final ElanInstance elan = elanInstanceCache.get(elanName).orElse(null);
487         if (elan == null) {
488             LOG.error("Could not find Elan by name: {}", elanName);
489             return;
490         }
491         LOG.info("Deleting Elan [{}] L2GatewayDevices UcastLocalMacs from Dpn [{}]", elanName, dpnId);
492
493         final Long elanTag = elan.getElanTag().toJava();
494         for (final L2GatewayDevice l2GwDevice : elanL2GwDevices) {
495             getL2GwDeviceLocalMacsAndRunCallback(elan.getElanInstanceName(), l2GwDevice, (localMacs) -> {
496                 for (MacAddress mac : localMacs) {
497                     String jobKey = elanName + ":" + mac.getValue();
498                     elanClusterUtils.runOnlyInOwnerNode(jobKey,
499                         () -> elanDmacUtils.deleteDmacFlowsToExternalMac(elanTag, dpnId,
500                                 l2GwDevice.getHwvtepNodeId(), mac.getValue()));
501                 }
502                 return null;
503             });
504         }
505     }
506
507     public void getL2GwDeviceLocalMacsAndRunCallback(String elanName, L2GatewayDevice l2gwDevice,
508                                                      Function<Collection<MacAddress>, Void> function) {
509         if (l2gwDevice == null) {
510             return;
511         }
512         Set<MacAddress> macs = new HashSet<>();
513         Collection<LocalUcastMacs> lstUcastLocalMacs = l2gwDevice.getUcastLocalMacs();
514         if (!lstUcastLocalMacs.isEmpty()) {
515             macs.addAll(lstUcastLocalMacs.stream().filter(Objects::nonNull)
516                     .map(mac -> new MacAddress(mac.getMacEntryKey().getValue().toLowerCase(Locale.ENGLISH)))
517                     .collect(Collectors.toList()));
518         }
519
520         InstanceIdentifier<Node> nodeIid = HwvtepSouthboundUtils.createInstanceIdentifier(
521                 new NodeId(l2gwDevice.getHwvtepNodeId()));
522         Futures.addCallback(broker.newReadOnlyTransaction().read(LogicalDatastoreType.CONFIGURATION, nodeIid),
523                 new FutureCallback<Optional<Node>>() {
524                     @Override
525                     public void onSuccess(Optional<Node> configNode) {
526                         if (configNode != null && configNode.isPresent()) {
527                             HwvtepGlobalAugmentation augmentation = configNode.get().augmentation(
528                                     HwvtepGlobalAugmentation.class);
529                             if (augmentation != null && augmentation.getLocalUcastMacs() != null) {
530                                 macs.addAll(new ArrayList<LocalUcastMacs>(augmentation
531                                         .getLocalUcastMacs().values()).stream()
532                                         .filter(mac -> getLogicalSwitchName(mac).equals(elanName))
533                                         .map(HwvtepMacTableGenericAttributes::getMacEntryKey)
534                                         .collect(Collectors.toSet()));
535                             }
536                             function.apply(macs);
537                         }
538                     }
539
540                     @Override
541                     public void onFailure(Throwable throwable) {
542                         LOG.error("Failed to read config topology node {}", nodeIid);
543                     }
544                 }, MoreExecutors.directExecutor());
545     }
546
547     private static String getLogicalSwitchName(LocalUcastMacs mac) {
548         return ((InstanceIdentifier<LogicalSwitches>)mac.getLogicalSwitchRef().getValue())
549                 .firstKeyOf(LogicalSwitches.class).getHwvtepNodeName().getValue();
550     }
551
552     /**
553      * Delete elan macs from L2 gateway device.<br>
554      * This includes deleting ELAN mac table entries plus external device
555      * UcastLocalMacs which are part of the same ELAN.
556      *
557      * @param hwvtepNodeId
558      *            the hwvtepNodeId
559      * @param elanName
560      *            the elan name
561      * @return the listenable future
562      */
563     public FluentFuture<? extends CommitInfo> deleteElanMacsFromL2GatewayDevice(String hwvtepNodeId, String elanName) {
564         String logicalSwitch = getLogicalSwitchFromElan(elanName);
565
566         List<MacAddress> lstElanMacs = getRemoteUcastMacs(new NodeId(hwvtepNodeId), logicalSwitch,
567                 LogicalDatastoreType.CONFIGURATION);
568         FluentFuture<? extends CommitInfo> future = HwvtepUtils.deleteRemoteUcastMacs(broker, new NodeId(hwvtepNodeId),
569                 logicalSwitch, lstElanMacs);
570
571         Futures.addCallback(future, new FutureCallback<CommitInfo>() {
572             @Override
573             public void onSuccess(CommitInfo noarg) {
574                 LOG.trace("Successful in batch deletion of elan [{}] macs from l2gw device [{}]", elanName,
575                         hwvtepNodeId);
576             }
577
578             @Override
579             public void onFailure(Throwable error) {
580                 LOG.warn("Failed during batch delete of elan {} macs from l2gw device {}. "
581                         + "Retrying with sequential deletes.", elanName, hwvtepNodeId, error);
582                 if (lstElanMacs != null && !lstElanMacs.isEmpty()) {
583                     for (MacAddress mac : lstElanMacs) {
584                         HwvtepUtils.deleteRemoteUcastMac(broker, new NodeId(hwvtepNodeId), logicalSwitch, mac);
585                     }
586                 }
587             }
588         }, MoreExecutors.directExecutor());
589
590         if (LOG.isDebugEnabled()) {
591             List<String> elanMacs = lstElanMacs.stream().map(MacAddress::getValue).collect(Collectors.toList());
592             LOG.debug("Deleting elan [{}] macs from node [{}]. Deleted macs = {}", elanName, hwvtepNodeId, elanMacs);
593         }
594         return future;
595     }
596
597     /**
598      * Gets the remote ucast macs from hwvtep node filtering based on logical
599      * switch.
600      *
601      * @param hwvtepNodeId
602      *            the hwvtep node id
603      * @param logicalSwitch
604      *            the logical switch
605      * @param datastoreType
606      *            the datastore type
607      * @return the remote ucast macs
608      */
609     public List<MacAddress> getRemoteUcastMacs(NodeId hwvtepNodeId, String logicalSwitch,
610             LogicalDatastoreType datastoreType) {
611         List<MacAddress> lstMacs = Collections.emptyList();
612         Node hwvtepNode;
613         try {
614             hwvtepNode = HwvtepUtils.getHwVtepNode(broker, datastoreType, hwvtepNodeId);
615         } catch (ExecutionException | InterruptedException e) {
616             LOG.error("getRemoteUcastMacs: Exception while reading hwvtepNodeId DS for the hwvtepNodeId {}",
617                     hwvtepNodeId.getValue(), e);
618             return Collections.emptyList();
619         }
620         if (hwvtepNode != null) {
621             Map<RemoteUcastMacsKey, RemoteUcastMacs> keyRemoteUcastMacsMap
622                     = hwvtepNode.augmentation(HwvtepGlobalAugmentation.class)
623                     .getRemoteUcastMacs();
624             if (keyRemoteUcastMacsMap != null && !keyRemoteUcastMacsMap.isEmpty()) {
625                 // Filtering keyRemoteUcastMacsMap based on the logical switch and
626                 // forming a list of MacAddress
627                 lstMacs = keyRemoteUcastMacsMap.values().stream()
628                         .filter(mac -> logicalSwitch.equals(mac.getLogicalSwitchRef().getValue()
629                                 .firstKeyOf(LogicalSwitches.class).getHwvtepNodeName().getValue()))
630                         .map(HwvtepMacTableGenericAttributes::getMacEntryKey).collect(Collectors.toList());
631             }
632         }
633         return lstMacs;
634     }
635
636     /**
637      * Install ELAN macs in L2 Gateway device.<br>
638      * This includes installing ELAN mac table entries plus external device
639      * UcastLocalMacs which are part of the same ELAN.
640      *
641      * @param elanName
642      *            the elan name
643      * @param l2GatewayDevice
644      *            the l2 gateway device which has to be configured
645      * @return the listenable future
646      */
647     public FluentFuture<? extends CommitInfo> installElanMacsInL2GatewayDevice(String elanName,
648                                                                                L2GatewayDevice l2GatewayDevice) {
649         String logicalSwitchName = getLogicalSwitchFromElan(elanName);
650         NodeId hwVtepNodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId());
651
652         List<RemoteUcastMacs> lstL2GatewayDevicesMacs = getOtherDevicesMacs(elanName, l2GatewayDevice, hwVtepNodeId,
653                 logicalSwitchName);
654         List<RemoteUcastMacs> lstElanMacTableEntries = getElanMacTableEntriesMacs(elanName,
655                 hwVtepNodeId, logicalSwitchName);
656
657         List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<>(lstL2GatewayDevicesMacs);
658         lstRemoteUcastMacs.addAll(lstElanMacTableEntries);
659
660         FluentFuture<? extends CommitInfo> future = HwvtepUtils.addRemoteUcastMacs(broker, hwVtepNodeId,
661                 lstRemoteUcastMacs);
662
663         LOG.info("Added RemoteUcastMacs entries [{}] in config DS. NodeID: {}, LogicalSwitch: {}",
664                 lstRemoteUcastMacs.size(), hwVtepNodeId.getValue(), logicalSwitchName);
665         return future;
666     }
667
668     /**
669      * Gets the l2 gateway devices ucast local macs as remote ucast macs.
670      *
671      * @param elanName
672      *            the elan name
673      * @param l2GatewayDeviceToBeConfigured
674      *            the l2 gateway device to be configured
675      * @param hwVtepNodeId
676      *            the hw vtep node Id to be configured
677      * @param logicalSwitchName
678      *            the logical switch name
679      * @return the l2 gateway devices macs as remote ucast macs
680      */
681     public static List<RemoteUcastMacs> getOtherDevicesMacs(String elanName,
682             L2GatewayDevice l2GatewayDeviceToBeConfigured, NodeId hwVtepNodeId, String logicalSwitchName) {
683         List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<>();
684
685         for (L2GatewayDevice otherDevice : ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName)) {
686             if (l2GatewayDeviceToBeConfigured.getHwvtepNodeId().equals(otherDevice.getHwvtepNodeId())) {
687                 continue;
688             }
689             if (!areMLAGDevices(l2GatewayDeviceToBeConfigured, otherDevice)) {
690                 for (LocalUcastMacs localUcastMac : otherDevice.getUcastLocalMacs()) {
691                     HwvtepPhysicalLocatorAugmentation physLocatorAug = HwvtepSouthboundUtils
692                             .createHwvtepPhysicalLocatorAugmentation(otherDevice.getTunnelIp());
693                     RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(hwVtepNodeId,
694                         IetfYangUtil.INSTANCE.canonizeMacAddress(localUcastMac.getMacEntryKey()).getValue(),
695                         localUcastMac.getIpaddr(), logicalSwitchName, physLocatorAug);
696                     lstRemoteUcastMacs.add(remoteUcastMac);
697                 }
698             }
699         }
700         return lstRemoteUcastMacs;
701     }
702
703     /**
704      * Are MLAG devices.
705      *
706      * @param l2GatewayDevice
707      *            the l2 gateway device
708      * @param otherL2GatewayDevice
709      *            the other l2 gateway device
710      * @return true, if both the specified l2 gateway devices are part of same
711      *         MLAG
712      */
713     public static boolean areMLAGDevices(L2GatewayDevice l2GatewayDevice, L2GatewayDevice otherL2GatewayDevice) {
714         // If tunnel IPs are same, then it is considered to be part of same MLAG
715         return Objects.equals(l2GatewayDevice.getTunnelIp(), otherL2GatewayDevice.getTunnelIp());
716     }
717
718     /**
719      * Gets the elan mac table entries as remote ucast macs. <br>
720      * Note: ELAN MAC table only contains internal switches MAC's. It doesn't
721      * contain external device MAC's.
722      *
723      * @param elanName
724      *            the elan name
725      * @param hwVtepNodeId
726      *            the hw vtep node id
727      * @param logicalSwitchName
728      *            the logical switch name
729      * @return the elan mac table entries as remote ucast macs
730      */
731     public List<RemoteUcastMacs> getElanMacTableEntriesMacs(String elanName,
732             NodeId hwVtepNodeId, String logicalSwitchName) {
733         List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<>();
734
735         MacTable macTable = ElanUtils.getElanMacTable(broker, elanName);
736         if (macTable == null || macTable.getMacEntry() == null || macTable.getMacEntry().isEmpty()) {
737             LOG.trace("MacTable is empty for elan: {}", elanName);
738             return lstRemoteUcastMacs;
739         }
740
741         for (MacEntry macEntry : new ArrayList<MacEntry>(macTable.getMacEntry().values())) {
742             Uint64 dpnId = getDpidFromInterface(macEntry.getInterface());
743             if (dpnId == null) {
744                 LOG.error("DPN ID not found for interface {}", macEntry.getInterface());
745                 continue;
746             }
747
748             IpAddress dpnTepIp = elanItmUtils.getSourceDpnTepIp(dpnId, hwVtepNodeId);
749             LOG.trace("Dpn Tep IP: {} for dpnId: {} and nodeId: {}", dpnTepIp, dpnId, hwVtepNodeId.getValue());
750             if (dpnTepIp == null) {
751                 LOG.error("TEP IP not found for dpnId {} and nodeId {}", dpnId, hwVtepNodeId.getValue());
752                 continue;
753             }
754             HwvtepPhysicalLocatorAugmentation physLocatorAug = HwvtepSouthboundUtils
755                     .createHwvtepPhysicalLocatorAugmentation(dpnTepIp);
756             // TODO: Query ARP cache to get IP address corresponding to the
757             // MAC
758             RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(hwVtepNodeId,
759                 IetfYangUtil.INSTANCE.canonizePhysAddress(macEntry.getMacAddress()).getValue(), null /*IpAddress*/,
760                     logicalSwitchName, physLocatorAug);
761             lstRemoteUcastMacs.add(remoteUcastMac);
762         }
763         return lstRemoteUcastMacs;
764     }
765
766     /**
767      * Gets the dpid from interface.
768      *
769      * @param interfaceName
770      *            the interface name
771      * @return the dpid from interface
772      */
773     @Nullable
774     public Uint64 getDpidFromInterface(String interfaceName) {
775         Uint64 dpId = null;
776         Future<RpcResult<GetDpidFromInterfaceOutput>> output = interfaceManagerRpcService
777                 .getDpidFromInterface(new GetDpidFromInterfaceInputBuilder().setIntfName(interfaceName).build());
778         try {
779             RpcResult<GetDpidFromInterfaceOutput> rpcResult = output.get();
780             if (rpcResult != null && rpcResult.isSuccessful()) {
781                 dpId = rpcResult.getResult().getDpid();
782             }
783         } catch (InterruptedException | ExecutionException e) {
784             LOG.error("Failed to get the DPN ID for interface {}", interfaceName, e);
785         }
786         return dpId;
787     }
788
789     /**
790      * Update vlan bindings in l2 gateway device.
791      *
792      * @param nodeId
793      *            the node id
794      * @param logicalSwitchName
795      *            the logical switch name
796      * @param hwVtepDevice
797      *            the hardware device
798      * @param defaultVlanId
799      *            the default vlan id
800      * @return the listenable future
801      */
802     public ListenableFuture<Void> updateVlanBindingsInL2GatewayDevice(NodeId nodeId, String logicalSwitchName,
803             Devices hwVtepDevice, Integer defaultVlanId) {
804         if (hwVtepDevice == null || hwVtepDevice.getInterfaces() == null || hwVtepDevice.getInterfaces().isEmpty()) {
805             String errMsg = "HwVtepDevice is null or interfaces are empty.";
806             LOG.error(errMsg);
807             return Futures.immediateFailedFuture(new RuntimeException(errMsg));
808         }
809
810         return txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
811             for (org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712
812                     .l2gateway.attributes.devices.Interfaces deviceInterface : new ArrayList<>(hwVtepDevice
813                     .getInterfaces().values())) {
814                 //Removed the check for checking terminationPoint present in OP or not
815                 //for coniguring vlan bindings
816                 //As we are not any more dependent on it , plugin takes care of this
817                 // with port reconcilation.
818                 List<VlanBindings> vlanBindings = new ArrayList<>();
819                 if (deviceInterface.getSegmentationIds() != null && !deviceInterface.getSegmentationIds().isEmpty()) {
820                     for (Integer vlanId : deviceInterface.getSegmentationIds()) {
821                         vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(nodeId, vlanId, logicalSwitchName));
822                     }
823                 } else {
824                     // Use defaultVlanId (specified in L2GatewayConnection) if Vlan
825                     // ID not specified at interface level.
826                     vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(nodeId, defaultVlanId, logicalSwitchName));
827                 }
828                 HwvtepUtils.mergeVlanBindings(tx, nodeId, hwVtepDevice.getDeviceName(),
829                         deviceInterface.getInterfaceName(), vlanBindings);
830             }
831             LOG.info("Updated Hwvtep VlanBindings in config DS. NodeID: {}, LogicalSwitch: {}", nodeId.getValue(),
832                     logicalSwitchName);
833         });
834     }
835
836     /**
837      * Update vlan bindings in l2 gateway device.
838      *
839      * @param nodeId
840      *            the node id
841      * @param psName
842      *            the physical switch name
843      * @param interfaceName
844      *            the interface in physical switch
845      * @param vlanBindings
846      *            the vlan bindings to be configured
847      * @return the listenable future
848      */
849     public ListenableFuture<Void> updateVlanBindingsInL2GatewayDevice(NodeId nodeId, String psName,
850             String interfaceName, List<VlanBindings> vlanBindings) {
851         return txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
852             HwvtepUtils.mergeVlanBindings(tx, nodeId, psName, interfaceName, vlanBindings);
853             LOG.info("Updated Hwvtep VlanBindings in config DS. NodeID: {}", nodeId.getValue());
854         });
855     }
856
857     /**
858      * Delete vlan bindings from l2 gateway device.
859      *
860      * @param nodeId
861      *            the node id
862      * @param hwVtepDevice
863      *            the hw vtep device
864      * @param defaultVlanId
865      *            the default vlan id
866      * @return the listenable future
867      */
868     public ListenableFuture<Void> deleteVlanBindingsFromL2GatewayDevice(NodeId nodeId, Devices hwVtepDevice,
869             Integer defaultVlanId) {
870         if (hwVtepDevice == null || hwVtepDevice.getInterfaces() == null || hwVtepDevice.getInterfaces().isEmpty()) {
871             String errMsg = "HwVtepDevice is null or interfaces are empty.";
872             LOG.error(errMsg);
873             return Futures.immediateFailedFuture(new RuntimeException(errMsg));
874         }
875         NodeId physicalSwitchNodeId = HwvtepSouthboundUtils.createManagedNodeId(nodeId, hwVtepDevice.getDeviceName());
876
877         return txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
878             for (org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712
879                     .l2gateway.attributes.devices.Interfaces deviceInterface : new ArrayList<>(hwVtepDevice
880                     .getInterfaces().values())) {
881                 String phyPortName = deviceInterface.getInterfaceName();
882                 if (deviceInterface.getSegmentationIds() != null && !deviceInterface.getSegmentationIds().isEmpty()) {
883                     for (Integer vlanId : deviceInterface.getSegmentationIds()) {
884                         HwvtepUtils.deleteVlanBinding(tx, physicalSwitchNodeId, phyPortName, vlanId);
885                     }
886                 } else {
887                     // Use defaultVlanId (specified in L2GatewayConnection) if Vlan
888                     // ID not specified at interface level.
889                     HwvtepUtils.deleteVlanBinding(tx, physicalSwitchNodeId, phyPortName, defaultVlanId);
890                 }
891             }
892             LOG.info("Deleted Hwvtep VlanBindings from config DS. NodeID: {}, hwVtepDevice: {}, defaultVlanId: {} ",
893                     nodeId.getValue(), hwVtepDevice, defaultVlanId);
894         });
895     }
896
897     /**
898      * Gets the logical switch name from elan name.
899      *
900      * @param elanName
901      *            the elan name
902      * @return the logical switch from elan name
903      */
904     public static String getLogicalSwitchFromElan(String elanName) {
905         // Assuming logical switch name is same as elan name
906         String logicalSwitchName = elanName;
907         return logicalSwitchName;
908     }
909
910     /**
911      * Gets the l2 gateway connection job key.
912      *
913      * @param logicalSwitchName
914      *            the logical switch name
915      * @return the l2 gateway connection job key
916      */
917     public static String getL2GatewayConnectionJobKey(String logicalSwitchName) {
918         return logicalSwitchName;
919     }
920
921     public static InstanceIdentifier<Interface> getInterfaceIdentifier(InterfaceKey interfaceKey) {
922         InstanceIdentifier.InstanceIdentifierBuilder<Interface> interfaceInstanceIdentifierBuilder = InstanceIdentifier
923                 .builder(Interfaces.class).child(Interface.class, interfaceKey);
924         return interfaceInstanceIdentifierBuilder.build();
925     }
926
927     @Nullable
928     public static Interface getInterfaceFromConfigDS(InterfaceKey interfaceKey, DataBroker dataBroker) {
929         InstanceIdentifier<Interface> interfaceId = getInterfaceIdentifier(interfaceKey);
930         try {
931             return SingleTransactionDataBroker
932                     .syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, interfaceId).orElse(null);
933         } catch (InterruptedException | ExecutionException e) {
934             // TODO remove this, and propagate ReadFailedException instead of re-throw RuntimeException
935             LOG.error("getInterfaceFromConfigDS({}) failed", interfaceKey, e);
936             throw new RuntimeException(e);
937         }
938     }
939
940     /**
941      * Delete l2 gateway device ucast local macs from elan.<br>
942      * Deletes macs from internal ELAN nodes and also on rest of external l2
943      * gateway devices which are part of the ELAN.
944      *
945      * @param l2GatewayDevice
946      *            the l2 gateway device whose ucast local macs to be deleted
947      *            from elan
948      * @param elanName
949      *            the elan name
950      */
951     public void deleteL2GwDeviceUcastLocalMacsFromElan(L2GatewayDevice l2GatewayDevice,
952             String elanName) {
953         LOG.info("Deleting L2GatewayDevice [{}] UcastLocalMacs from elan [{}]", l2GatewayDevice.getHwvtepNodeId(),
954                 elanName);
955
956         ElanInstance elan = elanInstanceCache.get(elanName).orElse(null);
957         if (elan == null) {
958             LOG.error("Could not find Elan by name: {}", elanName);
959             return;
960         }
961
962         Collection<MacAddress> localMacs = getL2GwDeviceLocalMacs(elanName, l2GatewayDevice);
963         unInstallL2GwUcastMacFromL2gwDevices(elanName, l2GatewayDevice, localMacs);
964         unInstallL2GwUcastMacFromElanDpns(elan, l2GatewayDevice, localMacs);
965     }
966
967     public static void createItmTunnels(DataBroker dataBroker, ItmRpcService itmRpcService,
968                                         String hwvtepId, String psName, IpAddress tunnelIp) {
969         AddL2GwDeviceInputBuilder builder = new AddL2GwDeviceInputBuilder();
970         builder.setTopologyId(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID.getValue());
971         builder.setNodeId(HwvtepSouthboundUtils.createManagedNodeId(new NodeId(hwvtepId), psName).getValue());
972         builder.setIpAddress(tunnelIp);
973         try {
974             deleteStaleTunnelsOfHwvtepInITM(dataBroker, itmRpcService, hwvtepId, psName, tunnelIp);
975             RpcResult<AddL2GwDeviceOutput> rpcResult = itmRpcService.addL2GwDevice(builder.build()).get();
976             if (rpcResult.isSuccessful()) {
977                 LOG.info("Created ITM tunnels for {}", hwvtepId);
978             } else {
979                 LOG.error("Failed to create ITM Tunnels: {}", rpcResult.getErrors());
980             }
981         } catch (InterruptedException | ExecutionException e) {
982             LOG.error("RPC to create ITM tunnels failed", e);
983         }
984     }
985
986     private static void deleteStaleTunnelsOfHwvtepInITM(DataBroker dataBroker,
987                                                         ItmRpcService itmRpcService,
988                                                         String globalNodeId,
989                                                         String psName,
990                                                         IpAddress tunnelIp) {
991         try {
992             Optional<TransportZones> tzonesoptional = readTransportZone(dataBroker);
993             if (!tzonesoptional.isPresent() || tzonesoptional.get().getTransportZone() == null) {
994                 return;
995             }
996             String psNodeId = globalNodeId + HwvtepHAUtil.PHYSICALSWITCH + psName;
997             tzonesoptional.get().nonnullTransportZone().stream()
998                 .filter(zone -> zone.getDeviceVteps() != null)
999                 .flatMap(zone -> new ArrayList<DeviceVteps>(zone.getDeviceVteps().values()).stream())
1000                 .filter(deviceVteps -> Objects.equals(getPsName(deviceVteps), psName)) //get device with same ps name
1001                 .filter(deviceVteps -> !Objects.equals(psNodeId, deviceVteps.getNodeId())
1002                         || !Objects.equals(tunnelIp, deviceVteps.getIpAddress()))//node id or tunnel ip is changed
1003                 .forEach(deviceVteps -> deleteStaleL2gwTep(dataBroker, itmRpcService, deviceVteps));
1004         } catch (ExecutionException | InterruptedException e) {
1005             LOG.error("Failed delete stale tunnels for {}", globalNodeId);
1006         }
1007     }
1008
1009     private static Optional<TransportZones> readTransportZone(DataBroker dataBroker) throws ExecutionException,
1010             InterruptedException {
1011         return new SingleTransactionDataBroker(dataBroker).syncReadOptional(LogicalDatastoreType.CONFIGURATION,
1012                 InstanceIdentifier.builder(TransportZones.class).build());
1013     }
1014
1015     private static Optional<ElanInstances> readElanInstances(DataBroker dataBroker) throws ExecutionException,
1016             InterruptedException {
1017         return new SingleTransactionDataBroker(dataBroker).syncReadOptional(LogicalDatastoreType.CONFIGURATION,
1018                 InstanceIdentifier.builder(ElanInstances.class).build());
1019     }
1020
1021     private static String getPsName(DeviceVteps deviceVteps) {
1022         return HwvtepHAUtil.getPsName(HwvtepHAUtil.convertToInstanceIdentifier(deviceVteps.getNodeId()));
1023     }
1024
1025     private static void deleteStaleL2gwTep(DataBroker dataBroker,
1026                                            ItmRpcService itmRpcService,
1027                                            DeviceVteps deviceVteps) {
1028         String psName = HwvtepHAUtil.getPsName(HwvtepHAUtil.convertToInstanceIdentifier(deviceVteps.getNodeId()));
1029         String globalNodeId = HwvtepHAUtil.convertToGlobalNodeId(deviceVteps.getNodeId());
1030         try {
1031             LOG.info("Deleting stale tep {} ", deviceVteps);
1032             L2GatewayUtils.deleteItmTunnels(itmRpcService, globalNodeId, psName, deviceVteps.getIpAddress());
1033             Optional<ElanInstances> optionalElan = readElanInstances(dataBroker);
1034             if (!optionalElan.isPresent()) {
1035                 return;
1036             }
1037             LoggingFutures.addErrorLogging(
1038                 new ManagedNewTransactionRunnerImpl(dataBroker).callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
1039                     tx -> optionalElan.get().nonnullElanInstance().values().stream()
1040                         .flatMap(elan -> elan.nonnullExternalTeps().values().stream()
1041                             .map(externalTep -> ElanL2GatewayMulticastUtils.buildExternalTepPath(
1042                                 elan.getElanInstanceName(), externalTep.getTepIp())))
1043                         .filter(externalTepIid -> Objects.equals(
1044                             deviceVteps.getIpAddress(), externalTepIid.firstKeyOf(ExternalTeps.class).getTepIp()))
1045                         .peek(externalTepIid -> LOG.info("Deleting stale external tep {}", externalTepIid))
1046                         .forEach(tx::delete)), LOG,
1047                 "Failed to delete stale external teps {}", deviceVteps);
1048             Thread.sleep(10000);//TODO remove the sleep currently it waits for interfacemgr to finish the cleanup
1049         } catch (ExecutionException | InterruptedException e) {
1050             LOG.error("Failed to delete stale l2gw tep {}", deviceVteps, e);
1051         }
1052     }
1053
1054     public void scheduleAddDpnMacInExtDevices(String elanName, Uint64 dpId,
1055             List<PhysAddress> staticMacAddresses) {
1056         for (final L2GatewayDevice externalDevice : ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName)) {
1057             scheduleAddDpnMacsInExtDevice(elanName, dpId, staticMacAddresses, externalDevice);
1058         }
1059     }
1060
1061     public void scheduleAddDpnMacsInExtDevice(final String elanName, Uint64 dpId,
1062             final List<PhysAddress> staticMacAddresses, final L2GatewayDevice externalDevice) {
1063         NodeId nodeId = new NodeId(externalDevice.getHwvtepNodeId());
1064         final IpAddress dpnTepIp = elanItmUtils.getSourceDpnTepIp(dpId, nodeId);
1065         LOG.trace("Dpn Tep IP: {} for dpnId: {} and nodeId: {}", dpnTepIp, dpId, nodeId);
1066         if (dpnTepIp == null) {
1067             LOG.error("could not install dpn mac in l2gw TEP IP not found for dpnId {} and nodeId {}", dpId, nodeId);
1068             return;
1069         }
1070
1071         //TODO: to  be batched in genius
1072         HwvtepUtils.installUcastMacs(broker, externalDevice.getHwvtepNodeId(), staticMacAddresses, elanName, dpnTepIp);
1073     }
1074
1075     public void scheduleDeleteLogicalSwitch(NodeId hwvtepNodeId, String lsName) {
1076         scheduleDeleteLogicalSwitch(hwvtepNodeId, lsName, false);
1077     }
1078
1079     public void scheduleDeleteLogicalSwitch(final NodeId hwvtepNodeId, final String lsName, final boolean clearUcast) {
1080         final Pair<NodeId, String> nodeIdLogicalSwitchNamePair = new ImmutablePair<>(hwvtepNodeId, lsName);
1081         logicalSwitchDeletedTasks.computeIfAbsent(nodeIdLogicalSwitchNamePair,
1082             (key) -> scheduler.getScheduledExecutorService().schedule(() -> {
1083                 DeleteLogicalSwitchJob deleteLsJob = new DeleteLogicalSwitchJob(broker,
1084                         ElanL2GatewayUtils.this, hwvtepNodeId, lsName, clearUcast);
1085                 jobCoordinator.enqueueJob(deleteLsJob.getJobKey(), deleteLsJob,
1086                         SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
1087                 deleteJobs.put(nodeIdLogicalSwitchNamePair, deleteLsJob);
1088                 logicalSwitchDeletedTasks.remove(nodeIdLogicalSwitchNamePair);
1089             }, getLogicalSwitchDeleteDelaySecs(), TimeUnit.SECONDS));
1090     }
1091
1092     public void cancelDeleteLogicalSwitch(final NodeId hwvtepNodeId, final String lsName) {
1093         Pair<NodeId, String> nodeIdLogicalSwitchNamePair = new ImmutablePair<>(hwvtepNodeId, lsName);
1094         ScheduledFuture logicalSwitchDeleteTask = logicalSwitchDeletedTasks.remove(nodeIdLogicalSwitchNamePair);
1095         if (logicalSwitchDeleteTask != null) {
1096             LOG.debug("Delete logical switch {} action on node {} cancelled", lsName, hwvtepNodeId);
1097             logicalSwitchDeleteTask.cancel(true);
1098             DeleteLogicalSwitchJob deleteLogicalSwitchJob = deleteJobs.remove(nodeIdLogicalSwitchNamePair);
1099             if (deleteLogicalSwitchJob != null) {
1100                 deleteLogicalSwitchJob.cancel();
1101             }
1102         }
1103     }
1104
1105     @NonNull
1106     public Collection<DpnInterfaces> getElanDpns(String elanName) {
1107         Collection<DpnInterfaces> dpnInterfaces = elanInstanceDpnsCache.get(elanName);
1108         if (!dpnInterfaces.isEmpty()) {
1109             return dpnInterfaces;
1110         }
1111
1112         return elanUtils.getElanDPNByName(elanName);
1113     }
1114
1115     /**
1116      * Gets the l2 gw device local macs.
1117      * @param elanName
1118      *            name of the elan
1119      * @param l2gwDevice
1120      *            the l2gw device
1121      * @return the l2 gw device local macs
1122      */
1123     public Collection<MacAddress> getL2GwDeviceLocalMacs(String elanName, L2GatewayDevice l2gwDevice) {
1124         if (l2gwDevice == null) {
1125             return Collections.emptyList();
1126         }
1127         Collection<LocalUcastMacs> lstUcastLocalMacs = l2gwDevice.getUcastLocalMacs();
1128         Set<MacAddress> macs = new HashSet<>();
1129         if (!lstUcastLocalMacs.isEmpty()) {
1130             macs.addAll(lstUcastLocalMacs.stream().filter(Objects::nonNull)
1131                     .map(mac -> new MacAddress(mac.getMacEntryKey().getValue().toLowerCase(Locale.ENGLISH)))
1132                     .collect(Collectors.toList()));
1133         }
1134         Optional<Node> configNode;
1135         try {
1136             configNode = SingleTransactionDataBroker.syncReadOptional(broker, LogicalDatastoreType.CONFIGURATION,
1137                     HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(l2gwDevice.getHwvtepNodeId())));
1138         } catch (ExecutionException | InterruptedException e) {
1139             LOG.error("getL2GwDeviceLocalMacs: Exception while reading l2gwDevice DS for the elan {}, l2gwDevice {}",
1140                     elanName, l2gwDevice, e);
1141             return Collections.emptyList();
1142         }
1143         if (configNode.isPresent()) {
1144             HwvtepGlobalAugmentation augmentation = configNode.get().augmentation(HwvtepGlobalAugmentation.class);
1145             if (augmentation != null && augmentation.getLocalUcastMacs() != null) {
1146                 macs.addAll(augmentation.getLocalUcastMacs().values().stream()
1147                         .filter(mac -> getLogicalSwitchName(mac).equals(elanName))
1148                         .map(HwvtepMacTableGenericAttributes::getMacEntryKey)
1149                         .collect(Collectors.toSet()));
1150             }
1151         }
1152         return macs;
1153     }
1154 }