Merge "Exception in SubnetRoute Feature"
[netvirt.git] / vpnservice / elanmanager / 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 java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.concurrent.ConcurrentMap;
14
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.netvirt.elan.internal.ElanInterfaceManager;
19 import org.opendaylight.netvirt.elan.utils.ElanUtils;
20 import org.opendaylight.netvirt.elan.internal.ElanInstanceManager;
21 import org.opendaylight.netvirt.elan.utils.ElanConstants;
22 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
23 import org.opendaylight.netvirt.elan.l2gw.jobs.HwvtepDeviceMcastMacUpdateJob;
24 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
25 import org.opendaylight.genius.mdsalutil.MDSALUtil;
26 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
27 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
28 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepLogicalSwitchRef;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorRef;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSetBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.DesignatedSwitchesForExternalTunnels;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import com.google.common.base.Optional;
52 import com.google.common.collect.Lists;
53 import com.google.common.util.concurrent.ListenableFuture;
54 import com.google.common.util.concurrent.SettableFuture;
55
56 /**
57  * The utility class to handle ELAN L2 Gateway related to multicast.
58  */
59 public class ElanL2GatewayMulticastUtils {
60
61     /** The Constant LOG. */
62     private static final Logger LOG = LoggerFactory.getLogger(ElanL2GatewayMulticastUtils.class);
63
64     /** The broker. */
65     private final DataBroker broker;
66
67     /** The elan instance manager. */
68     private final ElanInstanceManager elanInstanceManager;
69
70     /** The elan interface manager. */
71     private final ElanInterfaceManager elanInterfaceManager;
72
73     private final ElanL2GatewayUtils elanL2GatewayUtils;
74
75     private final ElanUtils elanUtils;
76
77     public ElanL2GatewayMulticastUtils(DataBroker broker, ElanInstanceManager elanInstanceManager,
78                                        ElanInterfaceManager elanInterfaceManager, ElanL2GatewayUtils elanL2GatewayUtils,
79                                        ElanUtils elanUtils) {
80         this.broker = broker;
81         this.elanInstanceManager = elanInstanceManager;
82         this.elanInterfaceManager = elanInterfaceManager;
83         this.elanL2GatewayUtils = elanL2GatewayUtils;
84         this.elanUtils = elanUtils;
85     }
86
87     /**
88      * Handle mcast for elan l2 gw device add.
89      *
90      * @param elanName
91      *            the elan name
92      * @param device
93      *            the device
94      * @return the listenable future
95      */
96     public ListenableFuture<Void> handleMcastForElanL2GwDeviceAdd(String elanName, L2GatewayDevice device) {
97         return updateMcastMacsForAllElanDevices(elanName, device, true/* updateThisDevice */);
98     }
99
100     /**
101      * Updates the remote mcast mac table for all the devices in this elan
102      * includes all the dpn tep ips and other devices tep ips in broadcast
103      * locator set.
104      *
105      * @param elanName
106      *            the elan to be updated
107      * @return the listenable future
108      */
109     public ListenableFuture<Void> updateRemoteMcastMacOnElanL2GwDevices(String elanName) {
110         SettableFuture<Void> future = SettableFuture.create();
111         future.set(null);
112         try {
113             WriteTransaction transaction = broker.newWriteOnlyTransaction();
114             for (L2GatewayDevice device : ElanL2GwCacheUtils.getInvolvedL2GwDevices(elanName).values()) {
115                 prepareRemoteMcastMacUpdateOnDevice(transaction, elanName, device);
116             }
117             return transaction.submit();
118         } catch (Throwable e) {
119             LOG.error("Failed to configure mcast mac on elan " + elanName, e);
120         }
121         return future;
122     }
123
124     public void scheduleMcastMacUpdateJob(String elanName, L2GatewayDevice device) {
125         HwvtepDeviceMcastMacUpdateJob job = new HwvtepDeviceMcastMacUpdateJob(this, elanName,device);
126         DataStoreJobCoordinator.getInstance().enqueueJob(job.getJobKey(), job);
127     }
128
129     /**
130      * Update remote mcast mac on elan l2 gw device.
131      *
132      * @param elanName
133      *            the elan name
134      * @param device
135      *            the device
136      * @return the listenable future
137      */
138     public ListenableFuture<Void> updateRemoteMcastMacOnElanL2GwDevice(String elanName, L2GatewayDevice device) {
139         WriteTransaction transaction = broker.newWriteOnlyTransaction();
140         prepareRemoteMcastMacUpdateOnDevice(transaction, elanName, device);
141         return transaction.submit();
142     }
143
144     public void prepareRemoteMcastMacUpdateOnDevice(WriteTransaction transaction,String elanName,
145                                                            L2GatewayDevice device) {
146         ConcurrentMap<String, L2GatewayDevice> elanL2gwDevices = ElanL2GwCacheUtils
147                 .getInvolvedL2GwDevices(elanName);
148         List<DpnInterfaces> dpns = elanUtils.getInvolvedDpnsInElan(elanName);
149         List<IpAddress> dpnsTepIps = getAllTepIpsOfDpns(device, dpns);
150         List<IpAddress> l2GwDevicesTepIps = getAllTepIpsOfL2GwDevices(elanL2gwDevices);
151         preapareRemoteMcastMacEntry(transaction, elanName, device, dpnsTepIps, l2GwDevicesTepIps);
152     }
153
154     /**
155      * Update mcast macs for this elan.
156      * for all dpns in this elan  recompute and update broadcast group
157      * for all l2gw devices in this elan recompute and update remote mcast mac entry
158      *
159      * @param elanName
160      *            the elan name
161      * @param device
162      *            the device
163      * @param updateThisDevice
164      *            the update this device
165      * @return the listenable future
166      */
167     public ListenableFuture<Void> updateMcastMacsForAllElanDevices(String elanName, L2GatewayDevice device,
168                                                                           boolean updateThisDevice) {
169
170         SettableFuture<Void> ft = SettableFuture.create();
171         ft.set(null);
172
173         ElanInstance elanInstance = elanInstanceManager.getElanInstanceByName(elanName);
174         elanInterfaceManager.updateRemoteBroadcastGroupForAllElanDpns(elanInstance);
175
176         List<DpnInterfaces> dpns = elanUtils.getInvolvedDpnsInElan(elanName);
177
178         ConcurrentMap<String, L2GatewayDevice> devices = ElanL2GwCacheUtils
179                 .getInvolvedL2GwDevices(elanName);
180
181         List<IpAddress> dpnsTepIps = getAllTepIpsOfDpns(device, dpns);
182         List<IpAddress> l2GwDevicesTepIps = getAllTepIpsOfL2GwDevices(devices);
183         // if (allTepIps.size() < 2) {
184         // LOG.debug("no other devices are found in the elan {}", elanName);
185         // return ft;
186         // }
187
188         WriteTransaction transaction = broker.newWriteOnlyTransaction();
189         if (updateThisDevice) {
190             preapareRemoteMcastMacEntry(transaction, elanName, device, dpnsTepIps, l2GwDevicesTepIps);
191         }
192
193         // TODO: Need to revisit below logic as logical switches might not be
194         // present to configure RemoteMcastMac entry
195         for (L2GatewayDevice otherDevice : devices.values()) {
196             if (!otherDevice.getDeviceName().equals(device.getDeviceName())) {
197                 preapareRemoteMcastMacEntry(transaction, elanName, otherDevice, dpnsTepIps, l2GwDevicesTepIps);
198             }
199         }
200         return transaction.submit();
201
202     }
203
204     /**
205      * Update remote mcast mac.
206      *
207      * @param transaction
208      *            the transaction
209      * @param elanName
210      *            the elan name
211      * @param device
212      *            the device
213      * @param dpnsTepIps
214      *            the dpns tep ips
215      * @param l2GwDevicesTepIps
216      *            the l2 gw devices tep ips
217      * @return the write transaction
218      */
219     private void preapareRemoteMcastMacEntry(WriteTransaction transaction, String elanName,
220                                                     L2GatewayDevice device, List<IpAddress> dpnsTepIps,
221                                                     List<IpAddress> l2GwDevicesTepIps) {
222         NodeId nodeId = new NodeId(device.getHwvtepNodeId());
223         String logicalSwitchName = ElanL2GatewayUtils.getLogicalSwitchFromElan(elanName);
224
225         ArrayList<IpAddress> remoteTepIps = new ArrayList<>(l2GwDevicesTepIps);
226         remoteTepIps.remove(device.getTunnelIp());
227         remoteTepIps.addAll(dpnsTepIps);
228         if (dpnsTepIps.isEmpty()) {
229             // If no dpns in elan, configure dhcp designated switch Tep Ip as a
230             // physical locator in l2 gw device
231             IpAddress dhcpDesignatedSwitchTepIp = getTepIpOfDesignatedSwitchForExternalTunnel(device, elanName);
232             if (dhcpDesignatedSwitchTepIp != null) {
233                 remoteTepIps.add(dhcpDesignatedSwitchTepIp);
234
235                 HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
236                         .createHwvtepPhysicalLocatorAugmentation(String.valueOf(dhcpDesignatedSwitchTepIp.getValue()));
237                 HwvtepUtils.putPhysicalLocator(transaction, nodeId, phyLocatorAug);
238
239                 LOG.info(
240                         "Adding PhysicalLocator for node: {} with Dhcp designated switch Tep Ip {} as physical locator, elan {}",
241                         device.getHwvtepNodeId(), String.valueOf(dhcpDesignatedSwitchTepIp.getValue()), elanName);
242             } else {
243                 LOG.warn("Dhcp designated switch Tep Ip not found for l2 gw node {} and elan {}",
244                         device.getHwvtepNodeId(), elanName);
245             }
246         }
247
248         putRemoteMcastMac(transaction, nodeId, logicalSwitchName, remoteTepIps);
249         LOG.info("Adding RemoteMcastMac for node: {} with physical locators: {}", device.getHwvtepNodeId(),
250                 remoteTepIps);
251     }
252
253     /**
254      * Put remote mcast mac in config DS.
255      *
256      * @param transaction
257      *            the transaction
258      * @param nodeId
259      *            the node id
260      * @param logicalSwitchName
261      *            the logical switch name
262      * @param tepIps
263      *            the tep ips
264      */
265     private static void putRemoteMcastMac(WriteTransaction transaction, NodeId nodeId, String logicalSwitchName,
266             ArrayList<IpAddress> tepIps) {
267         List<LocatorSet> locators = new ArrayList<>();
268         for (IpAddress tepIp : tepIps) {
269             HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
270                     .createHwvtepPhysicalLocatorAugmentation(String.valueOf(tepIp.getValue()));
271             HwvtepPhysicalLocatorRef phyLocRef = new HwvtepPhysicalLocatorRef(
272                     HwvtepSouthboundUtils.createPhysicalLocatorInstanceIdentifier(nodeId, phyLocatorAug));
273             locators.add(new LocatorSetBuilder().setLocatorRef(phyLocRef).build());
274         }
275
276         HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils
277                 .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName)));
278         RemoteMcastMacs remoteUcastMac = new RemoteMcastMacsBuilder()
279                 .setMacEntryKey(new MacAddress(ElanConstants.UNKNOWN_DMAC)).setLogicalSwitchRef(lsRef)
280                 .setLocatorSet(locators).build();
281         HwvtepUtils.putRemoteMcastMac(transaction, nodeId, remoteUcastMac);
282     }
283
284     /**
285      * Gets all the tep ips of dpns.
286      *
287      * @param l2GwDevice
288      *            the device
289      * @param dpns
290      *            the dpns
291      * @return the all tep ips of dpns and devices
292      */
293     private List<IpAddress> getAllTepIpsOfDpns(L2GatewayDevice l2GwDevice, List<DpnInterfaces> dpns) {
294         List<IpAddress> tepIps = new ArrayList<>();
295         for (DpnInterfaces dpn : dpns) {
296             IpAddress internalTunnelIp = elanL2GatewayUtils.getSourceDpnTepIp(dpn.getDpId(),
297                     new NodeId(l2GwDevice.getHwvtepNodeId()));
298             if (internalTunnelIp != null) {
299                 tepIps.add(internalTunnelIp);
300             }
301         }
302         return tepIps;
303     }
304
305     /**
306      * Gets the all tep ips of l2 gw devices.
307      *
308      * @param devices
309      *            the devices
310      * @return the all tep ips of l2 gw devices
311      */
312     private static List<IpAddress> getAllTepIpsOfL2GwDevices(ConcurrentMap<String, L2GatewayDevice> devices) {
313         List<IpAddress> tepIps = new ArrayList<>();
314         for (L2GatewayDevice otherDevice : devices.values()) {
315             tepIps.add(otherDevice.getTunnelIp());
316         }
317         return tepIps;
318     }
319
320     /**
321      * Handle mcast for elan l2 gw device delete.
322      *
323      * @param elanInstance
324      *            the elan instance
325      * @param l2GatewayDevice
326      *            the l2 gateway device
327      * @return the listenable future
328      */
329     public List<ListenableFuture<Void>> handleMcastForElanL2GwDeviceDelete(ElanInstance elanInstance,
330             L2GatewayDevice l2GatewayDevice) {
331         ListenableFuture<Void> updateMcastMacsFuture = updateMcastMacsForAllElanDevices(elanInstance.getElanInstanceName(),
332                 l2GatewayDevice, false/* updateThisDevice */);
333         ListenableFuture<Void> deleteRemoteMcastMacFuture = deleteRemoteMcastMac(
334                 new NodeId(l2GatewayDevice.getHwvtepNodeId()), elanInstance.getElanInstanceName());
335         return Lists.newArrayList(updateMcastMacsFuture, deleteRemoteMcastMacFuture);
336     }
337
338     /**
339      * Delete remote mcast mac from Hwvtep node.
340      *
341      * @param nodeId
342      *            the node id
343      * @param logicalSwitchName
344      *            the logical switch name
345      * @return the listenable future
346      */
347     private ListenableFuture<Void> deleteRemoteMcastMac(NodeId nodeId, String logicalSwitchName) {
348         InstanceIdentifier<LogicalSwitches> logicalSwitch = HwvtepSouthboundUtils
349                 .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName));
350         RemoteMcastMacsKey remoteMcastMacsKey = new RemoteMcastMacsKey(new HwvtepLogicalSwitchRef(logicalSwitch),
351                 new MacAddress(ElanConstants.UNKNOWN_DMAC));
352
353         LOG.info("Deleting RemoteMcastMacs entry on node: {} for logical switch: {}", nodeId.getValue(),
354                 logicalSwitchName);
355         return HwvtepUtils.deleteRemoteMcastMac(broker, nodeId, remoteMcastMacsKey);
356     }
357
358     /**
359      * Gets the tep ip of designated switch for external tunnel.
360      *
361      * @param l2GwDevice
362      *            the l2 gw device
363      * @param elanInstanceName
364      *            the elan instance name
365      * @return the tep ip of designated switch for external tunnel
366      */
367     public IpAddress getTepIpOfDesignatedSwitchForExternalTunnel(L2GatewayDevice l2GwDevice,
368             String elanInstanceName) {
369         IpAddress tepIp = null;
370         if (l2GwDevice.getTunnelIp() == null) {
371             LOG.warn("Tunnel IP not found for {}", l2GwDevice.getDeviceName());
372             return tepIp;
373         }
374         DesignatedSwitchForTunnel desgSwitch = getDesignatedSwitchForExternalTunnel(l2GwDevice.getTunnelIp(),
375                 elanInstanceName);
376         if (desgSwitch != null) {
377             tepIp = elanL2GatewayUtils.getSourceDpnTepIp(BigInteger.valueOf(desgSwitch.getDpId()),
378                     new NodeId(l2GwDevice.getHwvtepNodeId()));
379         }
380         return tepIp;
381     }
382
383     /**
384      * Gets the designated switch for external tunnel.
385      *
386      * @param tunnelIp
387      *            the tunnel ip
388      * @param elanInstanceName
389      *            the elan instance name
390      * @return the designated switch for external tunnel
391      */
392     public DesignatedSwitchForTunnel getDesignatedSwitchForExternalTunnel(IpAddress tunnelIp,
393             String elanInstanceName) {
394         InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier = InstanceIdentifier
395                 .builder(DesignatedSwitchesForExternalTunnels.class)
396                 .child(DesignatedSwitchForTunnel.class, new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp))
397                 .build();
398         Optional<DesignatedSwitchForTunnel> designatedSwitchForTunnelOptional = MDSALUtil.read(broker,
399                 LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
400         if (designatedSwitchForTunnelOptional.isPresent()) {
401             return designatedSwitchForTunnelOptional.get();
402         }
403         return null;
404     }
405
406 }