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