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