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