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