Freeze upstream versions
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / utils / ElanL2GatewayMulticastUtils.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 java.util.Collections.emptyList;
11 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
12 import static org.opendaylight.netvirt.elan.utils.ElanUtils.isVxlanNetworkOrVxlanSegment;
13
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Objects;
22 import java.util.Set;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.TimeUnit;
26 import javax.annotation.PostConstruct;
27 import javax.inject.Inject;
28 import javax.inject.Singleton;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
32 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
33 import org.opendaylight.genius.mdsalutil.MDSALUtil;
34 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
35 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
36 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
37 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
38 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
39 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
40 import org.opendaylight.mdsal.binding.api.DataBroker;
41 import org.opendaylight.mdsal.binding.util.Datastore;
42 import org.opendaylight.mdsal.binding.util.Datastore.Configuration;
43 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
44 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
45 import org.opendaylight.mdsal.binding.util.TypedWriteTransaction;
46 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
47 import org.opendaylight.netvirt.elan.cache.ConfigMcastCache;
48 import org.opendaylight.netvirt.elan.cache.ElanInstanceDpnsCache;
49 import org.opendaylight.netvirt.elan.l2gw.jobs.McastUpdateJob;
50 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
51 import org.opendaylight.netvirt.elan.utils.ElanConstants;
52 import org.opendaylight.netvirt.elan.utils.ElanItmUtils;
53 import org.opendaylight.netvirt.elan.utils.ElanUtils;
54 import org.opendaylight.netvirt.elan.utils.Scheduler;
55 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
56 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.DesignatedSwitchesForExternalTunnels;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInstance;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ExternalTeps;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ExternalTepsBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ExternalTepsKey;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepLogicalSwitchRef;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorRef;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsKey;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSetBuilder;
85 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
86 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
87 import org.opendaylight.yangtools.yang.common.Uint64;
88 import org.slf4j.Logger;
89 import org.slf4j.LoggerFactory;
90
91 /**
92  * The utility class to handle ELAN L2 Gateway related to multicast.
93  */
94 @Singleton
95 public class ElanL2GatewayMulticastUtils {
96
97     /** The Constant LOG. */
98     private static final Logger LOG = LoggerFactory.getLogger(ElanL2GatewayMulticastUtils.class);
99     private static final Logger EVENT_LOGGER = LoggerFactory.getLogger(ElanL2GatewayMulticastUtils.class);
100
101     /** The broker. */
102     private final DataBroker broker;
103     private final ManagedNewTransactionRunner txRunner;
104
105     private final ElanItmUtils elanItmUtils;
106     private final ElanUtils elanUtils;
107     private final IMdsalApiManager mdsalManager;
108     private final IInterfaceManager interfaceManager;
109     private final ConfigMcastCache configMcastCache;
110     private final ElanInstanceDpnsCache elanInstanceDpnsCache;
111     private final Scheduler scheduler;
112     private final ElanRefUtil elanRefUtil;
113     private final ElanClusterUtils elanClusterUtils;
114     private final JobCoordinator jobCoordinator;
115
116     private volatile boolean immediatelyAfterClusterReboot = true;
117
118
119     @Inject
120     public ElanL2GatewayMulticastUtils(DataBroker broker, ElanItmUtils elanItmUtils, ElanUtils elanUtils,
121                                        IMdsalApiManager mdsalManager, IInterfaceManager interfaceManager,
122                                        ConfigMcastCache configMcastCache,
123                                        ElanInstanceDpnsCache elanInstanceDpnsCache,
124                                        ElanRefUtil elanRefUtil,
125                                        ElanClusterUtils elanClusterUtils,
126                                        JobCoordinator jobCoordinator,
127                                        Scheduler scheduler) {
128         this.broker = broker;
129         this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
130         this.elanItmUtils = elanItmUtils;
131         this.elanUtils = elanUtils;
132         this.mdsalManager = mdsalManager;
133         this.interfaceManager = interfaceManager;
134         this.configMcastCache = configMcastCache;
135         this.scheduler = scheduler;
136         this.elanInstanceDpnsCache = elanInstanceDpnsCache;
137         this.elanRefUtil = elanRefUtil;
138         this.elanClusterUtils = elanClusterUtils;
139         this.jobCoordinator = jobCoordinator;
140     }
141
142     @PostConstruct
143     public void init() {
144         scheduler.getScheduledExecutorService().schedule(() -> {
145             immediatelyAfterClusterReboot = false;
146         }, 60, TimeUnit.MINUTES);
147     }
148
149     public static InstanceIdentifier<ExternalTeps> buildExternalTepPath(String elan, IpAddress tepIp) {
150         return InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class, new ElanInstanceKey(elan))
151                 .child(ExternalTeps.class, new ExternalTepsKey(tepIp)).build();
152     }
153
154     protected ExternalTeps buildExternalTeps(L2GatewayDevice device) {
155         return new ExternalTepsBuilder().setTepIp(device.getTunnelIp()).setNodeid(device.getHwvtepNodeId()).build();
156     }
157
158     public IInterfaceManager getInterfaceManager() {
159         return interfaceManager;
160     }
161
162     /**
163      * Updates the remote mcast mac table for all the devices in this elan
164      * includes all the dpn tep ips and other devices tep ips in broadcast
165      * locator set.
166      *
167      * @param elanName
168      *            the elan to be updated
169      */
170     public void updateRemoteMcastMacOnElanL2GwDevices(String elanName) {
171         for (L2GatewayDevice device : ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName).values()) {
172             prepareRemoteMcastMacUpdateOnDevice(elanName, device, false, null);
173         }
174     }
175
176     /**
177      * Update remote mcast mac on elan l2 gw device.
178      *
179      * @param elanName
180      *            the elan name
181      * @param device
182      *            the device
183      */
184     public void updateRemoteMcastMacOnElanL2GwDevice(String elanName, L2GatewayDevice device) {
185         prepareRemoteMcastMacUpdateOnDevice(elanName, device, false, null);
186     }
187
188     public ListenableFuture<Void> prepareRemoteMcastMacUpdateOnDevice(String elanName,
189                                                     L2GatewayDevice dstDevice,
190                                                     boolean addCase,
191                                                     IpAddress removedDstTep) {
192         NodeId dstNodeId = new NodeId(dstDevice.getHwvtepNodeId());
193         RemoteMcastMacs existingMac = configMcastCache.getMac(HwvtepSouthboundUtils
194                 .createLogicalSwitchesInstanceIdentifier(dstNodeId, new HwvtepNodeName(elanName)));
195         if (!addCase && immediatelyAfterClusterReboot && removedDstTep != null) {
196             LOG.debug(" RemoteMcast update delete tep {} of elan {} ", removedDstTep.getIpv4Address().getValue(),
197                     elanName);
198             //incase of dpn flap immediately after cluster reboot just remove its tep alone
199             if (existingMac != null) {
200                 return deleteLocatorFromMcast(elanName, dstNodeId, removedDstTep, existingMac);
201             }
202         }
203         ConcurrentMap<String, L2GatewayDevice> elanL2gwDevices = ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName);
204         Collection<DpnInterfaces> elanDpns = elanInstanceDpnsCache.get(elanName);
205         List<IpAddress> dpnsTepIps = getAllTepIpsOfDpns(dstDevice, new ArrayList<DpnInterfaces>(elanDpns));
206         List<IpAddress> l2GwDevicesTepIps = getAllTepIpsOfL2GwDevices(elanL2gwDevices);
207
208         return preapareRemoteMcastMacEntry(elanName, dstDevice, dpnsTepIps, l2GwDevicesTepIps, addCase);
209     }
210
211     private ListenableFuture<Void> deleteLocatorFromMcast(String elanName, NodeId dstNodeId,
212                                                           IpAddress removedDstTep,
213                                                           RemoteMcastMacs existingMac) {
214         InstanceIdentifier<RemoteMcastMacs> macIid = HwvtepSouthboundUtils
215                 .createRemoteMcastMacsInstanceIdentifier(dstNodeId, existingMac.key());
216         LocatorSet tobeDeleted = buildLocatorSet(dstNodeId, removedDstTep);
217         RemoteMcastMacsBuilder newMacBuilder = new RemoteMcastMacsBuilder(existingMac);
218         List<LocatorSet> locatorList = new ArrayList<>(existingMac.nonnullLocatorSet());
219         locatorList.remove(tobeDeleted);
220         newMacBuilder.setLocatorSet(locatorList);
221         RemoteMcastMacs mac = newMacBuilder.build();
222         //configMcastCache.added(macIid, mac);
223         return ResourceBatchingManager.getInstance().put(
224                 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, macIid, mac);
225     }
226
227     LocatorSet buildLocatorSet(NodeId nodeId, IpAddress tepIp) {
228         HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
229                 .createHwvtepPhysicalLocatorAugmentation(tepIp.stringValue());
230         HwvtepPhysicalLocatorRef phyLocRef = new HwvtepPhysicalLocatorRef(
231                 HwvtepSouthboundUtils.createPhysicalLocatorInstanceIdentifier(nodeId, phyLocatorAug));
232         return new LocatorSetBuilder().setLocatorRef(phyLocRef).build();
233     }
234
235     /**
236      * Update mcast macs for this elan.
237      * for all dpns in this elan  recompute and update broadcast group
238      * for all l2gw devices in this elan recompute and update remote mcast mac entry
239      *
240      * @param elanName
241      *            the elan name
242      * @param device
243      *            the device
244      * @param createCase
245      *            the update this device
246      * @return the listenable future
247      */
248     public List<ListenableFuture<Void>> updateMcastMacsForAllElanDevices(String elanName, L2GatewayDevice device,
249                                                                          boolean createCase) {
250         /*BcGroupUpdateJob.updateAllBcGroups(elanName, createCase, null, device,
251                 elanRefUtil, this, mdsalManager,
252                 elanInstanceDpnsCache, elanItmUtils); */
253         if (createCase) {
254             McastUpdateJob.updateAllMcastsForConnectionAdd(elanName, this, elanClusterUtils, scheduler,
255                     jobCoordinator);
256         } else {
257             McastUpdateJob.updateAllMcastsForConnectionDelete(elanName, this, elanClusterUtils, scheduler,
258                     jobCoordinator, device);
259         }
260         return Collections.emptyList();
261     }
262
263     public void updateRemoteBroadcastGroupForAllElanDpns(ElanInstance elanInfo, boolean createCase,
264                                                          Uint64 addedDpn) {
265         //TODO cache this read
266         txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
267             //List<DpnInterfaces> dpns = elanUtils.getInvolvedDpnsInElan(elanInfo.getElanInstanceName());
268             Collection<DpnInterfaces> dpns = elanInstanceDpnsCache.get(elanInfo.getElanInstanceName());
269             LOG.trace("Invoking method ELAN Broadcast Groups for ELAN {}",
270                 elanInfo.getElanInstanceName());
271             if (createCase == true && addedDpn != null) {
272                 setupStandardElanBroadcastGroups(elanInfo, null, addedDpn, createCase,
273                     confTx);
274             }
275             for (DpnInterfaces dpn : dpns) {
276                 if (!dpn.getDpId().equals(addedDpn)) {
277                     setupStandardElanBroadcastGroups(elanInfo, null, dpn.getDpId(), createCase, confTx);
278                 }
279             }
280         });
281     }
282
283     public void setupElanBroadcastGroups(ElanInstance elanInfo, Uint64 dpnId,
284             TypedWriteTransaction<Configuration> confTx) {
285         LOG.debug("Setting up ELAN Broadcast Group for ELAN Instance {} for DPN {} ", elanInfo, dpnId);
286         setupElanBroadcastGroups(elanInfo, null, dpnId, confTx);
287     }
288
289     public void setupElanBroadcastGroups(ElanInstance elanInfo, DpnInterfaces dpnInterfaces,
290             Uint64 dpnId, TypedWriteTransaction<Datastore.Configuration> confTx) {
291         setupStandardElanBroadcastGroups(elanInfo, dpnInterfaces, dpnId, confTx);
292         setupLeavesEtreeBroadcastGroups(elanInfo, dpnInterfaces, dpnId, confTx);
293     }
294
295     public void setupStandardElanBroadcastGroups(ElanInstance elanInfo, DpnInterfaces dpnInterfaces,
296             Uint64 dpnId, TypedWriteTransaction<Datastore.Configuration> confTx) {
297         setupStandardElanBroadcastGroups(elanInfo, dpnInterfaces, dpnId, true, confTx);
298     }
299
300     public void setupStandardElanBroadcastGroups(ElanInstance elanInfo, @Nullable DpnInterfaces dpnInterfaces,
301             Uint64 dpnId, boolean createCase, TypedWriteTransaction<Datastore.Configuration> confTx) {
302         List<Bucket> listBucket = new ArrayList<>();
303         int bucketId = 0;
304         int actionKey = 0;
305         Long elanTag = elanInfo.getElanTag().toJava();
306         List<Action> listAction = new ArrayList<>();
307         listAction.add(new ActionGroup(ElanUtils.getElanLocalBCGId(elanTag)).buildAction(++actionKey));
308         listBucket.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT,
309                 MDSALUtil.WATCH_GROUP));
310         LOG.debug("Configured ELAN Broadcast Group with Action {} ", listAction);
311         bucketId++;
312         LOG.info("Constructing RemoteBCGroupBuckets for {} on dpn {} ", elanInfo.getElanInstanceName(), dpnId);
313         List<Bucket> listBucketInfoRemote = getRemoteBCGroupBuckets(elanInfo, dpnInterfaces, dpnId, bucketId, elanTag);
314         listBucket.addAll(listBucketInfoRemote);
315         long groupId = ElanUtils.getElanRemoteBCGId(elanTag);
316         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
317                 MDSALUtil.buildBucketLists(listBucket));
318         LOG.info("Installing the remote BroadCast Group:{}", group);
319         EVENT_LOGGER.debug("ELAN-RBG, ADD {} Elan Instance {} Dpn Id {}", group.getGroupId().getValue(),
320                 elanInfo.getElanInstanceName(), dpnId);
321         if (createCase) {
322             elanUtils.syncUpdateGroup(dpnId, group, ElanConstants.DELAY_TIME_IN_MILLISECOND, confTx);
323         } else {
324             mdsalManager.addGroup(confTx, dpnId, group);
325         }
326     }
327
328     public void setupLeavesEtreeBroadcastGroups(ElanInstance elanInfo, @Nullable DpnInterfaces dpnInterfaces,
329             Uint64 dpnId, TypedWriteTransaction<Configuration> confTx) {
330         EtreeInstance etreeInstance = elanInfo.augmentation(EtreeInstance.class);
331         if (etreeInstance != null) {
332             long etreeLeafTag = etreeInstance.getEtreeLeafTagVal().getValue().toJava();
333             List<Bucket> listBucket = new ArrayList<>();
334             int bucketId = 0;
335             int actionKey = 0;
336             List<Action> listAction = new ArrayList<>();
337             listAction.add(new ActionGroup(ElanUtils.getEtreeLeafLocalBCGId(etreeLeafTag)).buildAction(++actionKey));
338             listBucket.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT,
339                     MDSALUtil.WATCH_GROUP));
340             bucketId++;
341             List<Bucket> listBucketInfoRemote = getRemoteBCGroupBuckets(elanInfo, dpnInterfaces, dpnId, bucketId,
342                     etreeLeafTag);
343             listBucket.addAll(listBucketInfoRemote);
344             long groupId = ElanUtils.getEtreeLeafRemoteBCGId(etreeLeafTag);
345             Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
346                     MDSALUtil.buildBucketLists(listBucket));
347             LOG.trace("Installing the remote BroadCast Group:{}", group);
348             mdsalManager.addGroup(confTx, dpnId, group);
349         }
350     }
351
352     @Nullable
353     private DpnInterfaces getDpnInterfaces(ElanDpnInterfacesList elanDpns, Uint64 dpnId) {
354         if (elanDpns != null) {
355             for (DpnInterfaces dpnInterface : elanDpns.nonnullDpnInterfaces().values()) {
356                 LOG.trace("List of DpnInterfaces present in DS {} ", dpnInterface);
357                 if (dpnInterface.getDpId().equals(dpnId)) {
358                     return dpnInterface;
359                 }
360             }
361         }
362         LOG.debug("DPN {} missing in DpnInterfaces list {}", dpnId, elanDpns);
363         return null;
364     }
365
366     private List<Bucket> getRemoteBCGroupExternalPortBuckets(ElanDpnInterfacesList elanDpns,
367             DpnInterfaces dpnInterfaces, Uint64 dpnId, int bucketId) {
368         DpnInterfaces currDpnInterfaces = dpnInterfaces != null ? dpnInterfaces : getDpnInterfaces(elanDpns, dpnId);
369         if (currDpnInterfaces == null || !elanUtils.isDpnPresent(currDpnInterfaces.getDpId())
370                 || currDpnInterfaces.getInterfaces() == null || currDpnInterfaces.getInterfaces().isEmpty()) {
371             LOG.debug("Returning empty Bucket list for DPN {}", dpnId);
372             return emptyList();
373         }
374         List<Bucket> listBucketInfo = new ArrayList<>();
375         for (String interfaceName : currDpnInterfaces.getInterfaces()) {
376             if (interfaceManager.isExternalInterface(interfaceName)) {
377                 List<Action> listActionInfo = elanItmUtils.getExternalPortItmEgressAction(interfaceName);
378                 if (!listActionInfo.isEmpty()) {
379                     listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId,
380                             MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
381                     bucketId++;
382                 }
383             }
384         }
385         LOG.debug("Configured RemoteBCGroupExternalPortBuckets {} for DPN {}", listBucketInfo, dpnId);
386         return listBucketInfo;
387     }
388
389     @NonNull
390     public List<Bucket> getRemoteBCGroupBuckets(ElanInstance elanInfo, @Nullable DpnInterfaces dpnInterfaces,
391         Uint64 dpnId, int bucketId, long elanTag) {
392         List<Bucket> listBucketInfo = new ArrayList<>();
393         ElanDpnInterfacesList elanDpns = elanUtils.getElanDpnInterfacesList(elanInfo.getElanInstanceName());
394
395         if (isVxlanNetworkOrVxlanSegment(elanInfo)) {
396             // Adding 270000 to avoid collision between LPort and elan for broadcast group actions
397             listBucketInfo.addAll(getRemoteBCGroupTunnelBuckets(elanDpns, dpnId, bucketId,
398                     elanUtils.isOpenstackVniSemanticsEnforced()
399                             ? elanUtils.getVxlanSegmentationId(elanInfo).longValue()
400                         : elanTag + ElanConstants.ELAN_TAG_ADDEND));
401         }
402         listBucketInfo.addAll(getRemoteBCGroupExternalPortBuckets(elanDpns, dpnInterfaces, dpnId,
403                 getNextAvailableBucketId(listBucketInfo.size())));
404         listBucketInfo.addAll(getRemoteBCGroupBucketsOfElanExternalTeps(elanInfo, dpnId,
405                 getNextAvailableBucketId(listBucketInfo.size())));
406         listBucketInfo.addAll(getRemoteBCGroupBucketsOfL2gwDevices(elanInfo, dpnId,
407                 getNextAvailableBucketId(listBucketInfo.size())));
408         LOG.debug("Configured ELAN Remote BC Group with Bucket Info {}", listBucketInfo);
409         return listBucketInfo;
410     }
411
412     public List<Bucket> getRemoteBCGroupBucketsOfElanL2GwDevices(ElanInstance elanInfo, Uint64 dpnId,
413             int bucketId) {
414         List<Bucket> listBucketInfo = new ArrayList<>();
415         ConcurrentMap<String, L2GatewayDevice> map = ElanL2GwCacheUtils
416                 .getInvolvedL2GwDevices(elanInfo.getElanInstanceName());
417         for (L2GatewayDevice device : map.values()) {
418             String interfaceName = elanItmUtils.getExternalTunnelInterfaceName(String.valueOf(dpnId),
419                     device.getHwvtepNodeId());
420             if (interfaceName == null) {
421                 LOG.debug("RPC returned with empty response for getExternalTunnelInterfaceName {}"
422                         + " for DPN {}, bucketID {} ",elanInfo.getElanInstanceName() ,dpnId, bucketId);
423                 continue;
424             }
425             List<Action> listActionInfo = elanItmUtils.buildTunnelItmEgressActions(interfaceName,
426                     ElanUtils.getVxlanSegmentationId(elanInfo).longValue(), true);
427             if (listActionInfo.isEmpty()) {
428                 LOG.debug("Retrieved empty egress action for interface {} for elan {} on DPN {}",
429                         interfaceName, elanInfo.getElanInstanceName(), dpnId);
430                 continue;
431             }
432             listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId,
433                     MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
434             bucketId++;
435         }
436         LOG.debug("Configured RemoteBCGroupBucketsOfElanL2GwDevices {} for DPN {} of ELAN {}",
437                 listBucketInfo, dpnId, elanInfo.getElanInstanceName());
438         return listBucketInfo;
439     }
440
441     public List<Bucket> getRemoteBCGroupBucketsOfElanExternalTeps(ElanInstance elanInfo, Uint64 dpnId,
442             int bucketId) {
443         ElanInstance operElanInstance = null;
444         try {
445             operElanInstance = new SingleTransactionDataBroker(broker).syncReadOptional(
446                 LogicalDatastoreType.CONFIGURATION,
447                 InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class, elanInfo.key())
448                     .build()).orElse(null);
449         } catch (ExecutionException | InterruptedException e) {
450             LOG.error("Failed to read elan instance operational path {}", elanInfo, e);
451             return emptyList();
452         }
453         if (operElanInstance == null) {
454             return emptyList();
455         }
456         List<ExternalTeps> teps = new ArrayList<>(operElanInstance.nonnullExternalTeps().values());
457         if (teps == null || teps.isEmpty()) {
458             return emptyList();
459         }
460         List<Bucket> listBucketInfo = new ArrayList<>();
461         for (ExternalTeps tep : teps) {
462             String externalTep = tep.getNodeid() != null ? tep.getNodeid() : tep.getTepIp().toString();
463             String interfaceName = elanItmUtils.getExternalTunnelInterfaceName(String.valueOf(dpnId),
464                     externalTep);
465             if (interfaceName == null) {
466                 LOG.error("Could not get interface name to ext tunnel {} {}", dpnId, tep.getTepIp());
467                 continue;
468             }
469             List<Action> listActionInfo = elanItmUtils.buildTunnelItmEgressActions(interfaceName,
470                     elanUtils.getVxlanSegmentationId(elanInfo).longValue(), false);
471             listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId,
472                     MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
473             bucketId++;
474         }
475         return listBucketInfo;
476     }
477
478     public List<Bucket> getRemoteBCGroupBucketsOfL2gwDevices(ElanInstance elanInfo, Uint64 dpnId, int bucketId) {
479         ConcurrentMap<String, L2GatewayDevice> elanL2gwDevices = ElanL2GwCacheUtils
480                 .getInvolvedL2GwDevices(elanInfo.getElanInstanceName());
481         if (elanL2gwDevices == null || elanL2gwDevices.isEmpty()) {
482             return Collections.emptyList();
483         }
484         List<Bucket> listBucketInfo = new ArrayList<>();
485         for (L2GatewayDevice l2GatewayDevice : elanL2gwDevices.values()) {
486             if (l2GatewayDevice.getTunnelIp() == null) {
487                 continue;
488             }
489             String externalTep = l2GatewayDevice.getTunnelIp().toString();
490             String interfaceName = elanItmUtils.getExternalTunnelInterfaceName(String.valueOf(dpnId),
491                     externalTep);
492             if (interfaceName == null) {
493                 LOG.error("Could not get interface name to ext tunnel {} {}", dpnId, externalTep);
494                 continue;
495             }
496             List<Action> listActionInfo = elanItmUtils.buildTunnelItmEgressActions(interfaceName,
497                     elanUtils.getVxlanSegmentationId(elanInfo).longValue(), false);
498             if (!listActionInfo.isEmpty()) {
499                 LOG.debug("Adding Remote BC Group Bucket of tor - tunnel {} tun_id {}", interfaceName,
500                         elanUtils.getVxlanSegmentationId(elanInfo));
501             }
502             listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId,
503                     MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
504             bucketId++;
505         }
506         return listBucketInfo;
507     }
508
509     private int getNextAvailableBucketId(int bucketSize) {
510         return bucketSize + 1;
511     }
512
513     @SuppressWarnings("checkstyle:IllegalCatch")
514     public List<Bucket> getRemoteBCGroupTunnelBuckets(ElanDpnInterfacesList elanDpns, Uint64 dpnId, int bucketId,
515             long elanTagOrVni) {
516         List<Bucket> listBucketInfo = new ArrayList<>();
517         if (elanDpns != null) {
518             for (DpnInterfaces dpnInterface : elanDpns. nonnullDpnInterfaces().values()) {
519                 if (!Objects.equals(dpnInterface.getDpId(), dpnId) && dpnInterface.getInterfaces() != null
520                         && !dpnInterface.getInterfaces().isEmpty()) {
521                     try {
522                         List<Action> listActionInfo = elanItmUtils.getInternalTunnelItmEgressAction(dpnId,
523                                 dpnInterface.getDpId(), elanTagOrVni);
524                         LOG.trace("configuring broadcast group for elan {} for source DPN {} and destination DPN {} "
525                                 + "with actions {}", elanTagOrVni, dpnId, dpnInterface.getDpId(), listActionInfo);
526                         if (listActionInfo.isEmpty()) {
527                             LOG.debug("getInternalTunnelItmEgressAction for src DPN {} "
528                                             + "and dest DPN {} for ELAN Tag or VNI {} returned empty",
529                                     dpnId, dpnInterface.getDpId(), elanTagOrVni);
530                             continue;
531                         }
532                         listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId,
533                                 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
534                         bucketId++;
535                     } catch (Exception ex) {
536                         LOG.error("Logical Group Interface not found between source Dpn - {}, "
537                                         + "destination Dpn - {} with exception", dpnId, dpnInterface.getDpId(), ex);
538                     }
539                 }
540             }
541         }
542         LOG.debug("Configured RemoteBCGroupTunnelBuckets Info {} for DPN {} for ELAN Tag or VNI{}",
543                 listBucketInfo, dpnId, elanTagOrVni);
544         return listBucketInfo;
545     }
546
547     /**
548      * Update remote mcast mac.
549      *
550      * @param elanName
551      *            the elan name
552      * @param    device
553      *            the device
554      * @param dpnsTepIps
555      *            the dpns tep ips
556      * @param l2GwDevicesTepIps
557      *            the l2 gw devices tep ips
558      * @return the write transaction
559      */
560     private ListenableFuture<Void> preapareRemoteMcastMacEntry(String elanName,
561                                                                L2GatewayDevice device, List<IpAddress> dpnsTepIps,
562                                                                List<IpAddress> l2GwDevicesTepIps, boolean addCase) {
563         ArrayList<IpAddress> remoteTepIps = new ArrayList<>(l2GwDevicesTepIps);
564         remoteTepIps.remove(device.getTunnelIp());
565         remoteTepIps.addAll(dpnsTepIps);
566         IpAddress dhcpDesignatedSwitchTepIp = getTepIpOfDesignatedSwitchForExternalTunnel(device, elanName);
567         if (dhcpDesignatedSwitchTepIp != null && !remoteTepIps.contains(dhcpDesignatedSwitchTepIp)) {
568             remoteTepIps.add(dhcpDesignatedSwitchTepIp);
569         }
570         return putRemoteMcastMac(new NodeId(device.getHwvtepNodeId()), elanName, remoteTepIps, addCase);
571     }
572
573     /**
574      * Put remote mcast mac in config DS.
575      *
576      * @param nodeId
577      *            the node id
578      * @param logicalSwitchName
579      *            the logical switch name
580      * @param tepIps
581      *            the tep ips
582      */
583     private ListenableFuture<Void> putRemoteMcastMac(NodeId nodeId, String logicalSwitchName,
584                                                      ArrayList<IpAddress> tepIps, boolean addCase) {
585         List<LocatorSet> locators = new ArrayList<>();
586         for (IpAddress tepIp : tepIps) {
587             locators.add(buildLocatorSet(nodeId, tepIp));
588         }
589         HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils
590                 .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName)));
591         RemoteMcastMacs newMac = new RemoteMcastMacsBuilder()
592                 .setMacEntryKey(new MacAddress(ElanConstants.UNKNOWN_DMAC)).setLogicalSwitchRef(lsRef)
593                 .setLocatorSet(locators).build();
594         InstanceIdentifier<RemoteMcastMacs> iid = HwvtepSouthboundUtils.createRemoteMcastMacsInstanceIdentifier(nodeId,
595                 newMac.key());
596         RemoteMcastMacs existingMac = configMcastCache.getMac(newMac.getLogicalSwitchRef().getValue());
597
598         if (!addCase) {
599             //proactively update the cache for delete cases do not wait for batch manager to delete from cache
600             //while the delete is in progress from the batch manager the below skip may trigger
601             //by updating the cache upfront the skip wont be triggered
602             configMcastCache.added(iid, newMac);
603         }
604
605         if (addCase && existingMac != null && existingMac.getLocatorSet() != null) {
606             Set existingLocators = new HashSet<>(existingMac.getLocatorSet());
607             List newLocators = newMac.getLocatorSet();
608             if (existingLocators.containsAll(newLocators)) {
609                 return Futures.immediateFuture(null);
610             }
611         }
612
613         return ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
614                 iid, newMac);
615     }
616
617     /**
618      * Gets all the tep ips of dpns.
619      *
620      * @param l2GwDevice
621      *            the device
622      * @param dpns
623      *            the dpns
624      * @return the all tep ips of dpns and devices
625      */
626     private List<IpAddress> getAllTepIpsOfDpns(L2GatewayDevice l2GwDevice, List<DpnInterfaces> dpns) {
627         List<IpAddress> tepIps = new ArrayList<>();
628         for (DpnInterfaces dpn : dpns) {
629             IpAddress internalTunnelIp = elanItmUtils.getSourceDpnTepIp(dpn.getDpId(),
630                     new NodeId(l2GwDevice.getHwvtepNodeId()));
631             if (internalTunnelIp != null) {
632                 tepIps.add(internalTunnelIp);
633             }
634         }
635         return tepIps;
636     }
637
638     /**
639      * Gets the all tep ips of l2 gw devices.
640      *
641      * @param devices
642      *            the devices
643      * @return the all tep ips of l2 gw devices
644      */
645     private static List<IpAddress> getAllTepIpsOfL2GwDevices(ConcurrentMap<String, L2GatewayDevice> devices) {
646         List<IpAddress> tepIps = new ArrayList<>();
647         for (L2GatewayDevice otherDevice : devices.values()) {
648             // There is no need to add the same tep ip to the list.
649             if (!tepIps.contains(otherDevice.getTunnelIp())) {
650                 tepIps.add(otherDevice.getTunnelIp());
651             }
652         }
653         return tepIps;
654     }
655
656     /**
657      * Delete remote mcast mac from Hwvtep node.
658      *
659      * @param nodeId
660      *            the node id
661      * @param logicalSwitchName
662      *            the logical switch name
663      * @return the listenable future
664      */
665     public ListenableFuture<?> deleteRemoteMcastMac(NodeId nodeId, String logicalSwitchName) {
666         InstanceIdentifier<LogicalSwitches> logicalSwitch = HwvtepSouthboundUtils
667                 .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName));
668         RemoteMcastMacsKey remoteMcastMacsKey = new RemoteMcastMacsKey(new HwvtepLogicalSwitchRef(logicalSwitch),
669                 new MacAddress(ElanConstants.UNKNOWN_DMAC));
670
671         LOG.info("Deleting RemoteMcastMacs entry on node: {} for logical switch: {}", nodeId.getValue(),
672                 logicalSwitchName);
673         return HwvtepUtils.deleteRemoteMcastMac(broker, nodeId, remoteMcastMacsKey);
674     }
675
676     /**
677      * Gets the tep ip of designated switch for external tunnel.
678      *
679      * @param l2GwDevice
680      *            the l2 gw device
681      * @param elanInstanceName
682      *            the elan instance name
683      * @return the tep ip of designated switch for external tunnel
684      */
685     public IpAddress getTepIpOfDesignatedSwitchForExternalTunnel(L2GatewayDevice l2GwDevice,
686             String elanInstanceName) {
687         IpAddress tepIp = null;
688         if (l2GwDevice.getTunnelIp() == null) {
689             LOG.warn("Tunnel IP not found for {}", l2GwDevice.getDeviceName());
690             return tepIp;
691         }
692         DesignatedSwitchForTunnel desgSwitch = getDesignatedSwitchForExternalTunnel(l2GwDevice.getTunnelIp(),
693                 elanInstanceName);
694         if (desgSwitch != null) {
695             tepIp = elanItmUtils.getSourceDpnTepIp(Uint64.valueOf(desgSwitch.getDpId()),
696                     new NodeId(l2GwDevice.getHwvtepNodeId()));
697         }
698         return tepIp;
699     }
700
701     /**
702      * Gets the designated switch for external tunnel.
703      *
704      * @param tunnelIp
705      *            the tunnel ip
706      * @param elanInstanceName
707      *            the elan instance name
708      * @return the designated switch for external tunnel
709      */
710     public DesignatedSwitchForTunnel getDesignatedSwitchForExternalTunnel(IpAddress tunnelIp,
711             String elanInstanceName) {
712         try {
713             InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier = InstanceIdentifier
714                 .builder(DesignatedSwitchesForExternalTunnels.class)
715                 .child(DesignatedSwitchForTunnel.class,new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp))
716                 .build();
717             return new SingleTransactionDataBroker(broker).syncReadOptional(broker,
718                 LogicalDatastoreType.CONFIGURATION, instanceIdentifier).orElse(null);
719         } catch (ExecutionException e) {
720             LOG.error("Exception while retriving DesignatedSwitch for elan {} and tunnel {}",
721                 elanInstanceName, tunnelIp, e);
722         } catch (InterruptedException e) {
723             LOG.error("Exception while retriving DesignatedSwitch for elan {} and tunnel {}",
724                 elanInstanceName, tunnelIp, e);
725         }
726         return null;
727     }
728
729 }