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