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