2 * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.elan.l2gw.utils;
11 import static java.util.Collections.emptyMap;
12 import static org.opendaylight.netvirt.elan.utils.ElanUtils.isVxlanNetworkOrVxlanSegment;
14 import com.google.common.collect.Lists;
15 import com.google.common.util.concurrent.FluentFuture;
16 import com.google.common.util.concurrent.FutureCallback;
17 import com.google.common.util.concurrent.Futures;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import com.google.common.util.concurrent.SettableFuture;
20 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Objects;
24 import java.util.Optional;
26 import java.util.concurrent.CopyOnWriteArrayList;
27 import java.util.concurrent.ExecutionException;
28 import javax.annotation.PreDestroy;
29 import javax.inject.Inject;
30 import javax.inject.Singleton;
31 import org.eclipse.jdt.annotation.NonNull;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
34 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
35 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
36 import org.opendaylight.mdsal.binding.api.DataBroker;
37 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
38 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
39 import org.opendaylight.netvirt.elan.l2gw.jobs.AssociateHwvtepToElanJob;
40 import org.opendaylight.netvirt.elan.l2gw.jobs.DisAssociateHwvtepFromElanJob;
41 import org.opendaylight.netvirt.elan.l2gw.listeners.LocalUcastMacListener;
42 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
43 import org.opendaylight.netvirt.elan.utils.Scheduler;
44 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
45 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayCache;
46 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.L2gatewayConnections;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.L2gateways;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.l2gateways.L2gateway;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.l2gateways.L2gatewayKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LocalUcastMacs;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
66 public class L2GatewayConnectionUtils implements AutoCloseable {
67 private static final Logger LOG = LoggerFactory.getLogger(L2GatewayConnectionUtils.class);
69 private final DataBroker broker;
70 private final ElanL2GatewayUtils elanL2GatewayUtils;
71 private final ElanClusterUtils elanClusterUtils;
72 private final ElanL2GatewayMulticastUtils elanL2GatewayMulticastUtils;
73 private final ElanL2GatewayBcGroupUtils elanL2GatewayBcGroupUtils;
74 private final Scheduler scheduler;
75 private final JobCoordinator jobCoordinator;
76 private final L2GatewayCache l2GatewayCache;
77 private final ElanInstanceCache elanInstanceCache;
78 private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
79 private final LocalUcastMacListener localUcastMacListener;
82 public L2GatewayConnectionUtils(DataBroker dataBroker,
83 ElanClusterUtils elanClusterUtils, ElanL2GatewayUtils elanL2GatewayUtils,
84 JobCoordinator jobCoordinator,
85 ElanL2GatewayMulticastUtils elanL2GatewayMulticastUtils,
86 ElanL2GatewayBcGroupUtils elanL2GatewayBcGroupUtils, Scheduler scheduler,
87 L2GatewayCache l2GatewayCache,
88 ElanInstanceCache elanInstanceCache,
89 LocalUcastMacListener localUcastMacListener) {
90 this.broker = dataBroker;
91 this.elanL2GatewayUtils = elanL2GatewayUtils;
92 this.elanClusterUtils = elanClusterUtils;
93 this.elanL2GatewayMulticastUtils = elanL2GatewayMulticastUtils;
94 this.elanL2GatewayBcGroupUtils = elanL2GatewayBcGroupUtils;
95 this.scheduler = scheduler;
96 this.jobCoordinator = jobCoordinator;
97 this.l2GatewayCache = l2GatewayCache;
98 this.elanInstanceCache = elanInstanceCache;
99 this.localUcastMacListener = localUcastMacListener;
104 @SuppressWarnings("checkstyle:IllegalCatch")
105 public void close() {
106 closeables.forEach(c -> {
109 } catch (Exception e) {
110 LOG.warn("Error closing {}", c, e);
115 public static boolean isGatewayAssociatedToL2Device(L2GatewayDevice l2GwDevice) {
116 return !l2GwDevice.getHwvtepNodeId().isEmpty();
120 public static L2gateway getNeutronL2gateway(DataBroker broker, Uuid l2GatewayId) {
121 if (l2GatewayId != null) {
122 LOG.debug("getNeutronL2gateway for {}", l2GatewayId.getValue());
123 InstanceIdentifier<L2gateway> inst = InstanceIdentifier.create(Neutron.class).child(L2gateways.class)
124 .child(L2gateway.class, new L2gatewayKey(l2GatewayId));
126 return SingleTransactionDataBroker
127 .syncReadOptional(broker, LogicalDatastoreType.CONFIGURATION, inst)
129 } catch (ExecutionException | InterruptedException e) {
130 LOG.error("getNeutronL2gateway: Exception while reading L2gateway DS for the ID {}", l2GatewayId, e);
139 public static List<L2gateway> getL2gatewayList(DataBroker broker) {
140 InstanceIdentifier<L2gateways> inst = InstanceIdentifier.create(Neutron.class).child(L2gateways.class);
142 return new ArrayList<>((SingleTransactionDataBroker.syncReadOptional(broker,
143 LogicalDatastoreType.CONFIGURATION, inst).map(L2gateways::nonnullL2gateway)
144 .orElse(emptyMap())).values());
145 } catch (ExecutionException | InterruptedException e) {
146 LOG.error("getNeutronL2gateway: Exception while reading L2gateway DS", e);
152 public static List<L2gatewayConnection> getAllL2gatewayConnections(DataBroker broker) {
153 InstanceIdentifier<L2gatewayConnections> inst = InstanceIdentifier.create(Neutron.class)
154 .child(L2gatewayConnections.class);
156 return new ArrayList<>((SingleTransactionDataBroker.syncReadOptional(broker,
157 LogicalDatastoreType.CONFIGURATION, inst).map(L2gatewayConnections::nonnullL2gatewayConnection)
158 .orElse(emptyMap())).values());
159 } catch (ExecutionException | InterruptedException e) {
160 LOG.error("getNeutronL2gateway: Exception while reading L2gateway DS", e);
166 * Gets the associated l2 gw connections.
170 * @param l2GatewayIds
172 * @return the associated l2 gw connections
175 public static List<L2gatewayConnection> getAssociatedL2GwConnections(DataBroker broker, Set<Uuid> l2GatewayIds) {
176 List<L2gatewayConnection> allL2GwConns = getAllL2gatewayConnections(broker);
177 List<L2gatewayConnection> l2GwConnections = new ArrayList<>();
178 for (Uuid l2GatewayId : l2GatewayIds) {
179 for (L2gatewayConnection l2GwConn : allL2GwConns) {
180 if (l2GwConn.getL2gatewayId() != null) {
181 if (Objects.equals(l2GwConn.getL2gatewayId(), l2GatewayId)) {
182 l2GwConnections.add(l2GwConn);
186 LOG.warn("No l2gatewayId for l2gatewayconnection {} ", l2GwConn.key().getUuid());
191 return l2GwConnections;
195 * Gets the associated l2 gw connections.
201 * @return the associated l2 gw connection with elan
204 public static List<L2gatewayConnection> getL2GwConnectionsByElanName(DataBroker broker, String elanName) {
205 List<L2gatewayConnection> allL2GwConns = getAllL2gatewayConnections(broker);
206 List<L2gatewayConnection> elanL2GateWayConnections = new ArrayList<>();
207 for (L2gatewayConnection l2GwConn : allL2GwConns) {
208 if (l2GwConn.getNetworkId().getValue().equalsIgnoreCase(elanName)) {
209 elanL2GateWayConnections.add(l2GwConn);
212 return elanL2GateWayConnections;
215 public void addL2GatewayConnection(L2gatewayConnection input) {
216 addL2GatewayConnection(input, null/*deviceName*/, null);
219 public void addL2GatewayConnection(final L2gatewayConnection input,
220 final String l2GwDeviceName) {
221 addL2GatewayConnection(input, l2GwDeviceName, null);
224 public void addL2GatewayConnection(final L2gatewayConnection input,
225 @Nullable final String l2GwDeviceName ,
226 @Nullable L2gateway l2Gateway) {
227 LOG.info("Adding L2gateway Connection:{} vlan: {} device name {}",
228 input.key().getUuid(), input.getSegmentId(), l2GwDeviceName);
230 Uuid networkUuid = input.getNetworkId();
232 // Taking cluster reboot scenario , if Elan instance is not available when l2GatewayConnection add events
233 // comes we need to wait for elaninstance to resolve. Hence updating the map with the runnable .
234 // When elanInstance add comes , it look in to the map and run the associated runnable associated with it.
235 ElanInstance elanInstance = elanInstanceCache.get(networkUuid.getValue(),
236 () -> addL2GatewayConnection(input, l2GwDeviceName)).orElse(null);
237 if (elanInstance == null) {
241 if (!isVxlanNetworkOrVxlanSegment(elanInstance)) {
242 LOG.error("Neutron network with id {} is not VxlanNetwork", networkUuid.getValue());
244 Uuid l2GatewayId = input.getL2gatewayId();
245 if (l2Gateway == null) {
246 l2Gateway = getNeutronL2gateway(broker, l2GatewayId);
248 if (l2Gateway == null) {
249 LOG.error("L2Gateway with id {} is not present", l2GatewayId);
251 associateHwvtepsToElan(elanInstance, l2Gateway, input, l2GwDeviceName);
256 public void deleteL2GatewayConnection(L2gatewayConnection input) {
257 LOG.info("Deleting L2gateway Connection with ID: {} vlan : {}",
258 input.key().getUuid(), input.getSegmentId());
260 Uuid networkUuid = input.getNetworkId();
261 String elanName = networkUuid.getValue();
262 disAssociateHwvtepsFromElan(elanName, input);
265 private void disAssociateHwvtepsFromElan(String elanName, L2gatewayConnection input) {
266 Integer defaultVlan = input.getSegmentId();
267 List<L2GatewayDevice> l2Devices = ElanL2GwCacheUtils.getAllElanDevicesFromCache();
268 List<Devices> l2gwDevicesToBeDeleted = new ArrayList<>();
269 for (L2GatewayDevice elanL2gwDevice : l2Devices) {
270 if (elanL2gwDevice.getL2GatewayIds().contains(input.key().getUuid())) {
271 l2gwDevicesToBeDeleted.addAll(elanL2gwDevice.getDevicesForL2gwConnectionId(input.key().getUuid()));
274 if (l2gwDevicesToBeDeleted.isEmpty()) {
275 //delete logical switch
276 Uuid l2GatewayId = input.getL2gatewayId();
277 L2gateway l2Gateway = L2GatewayConnectionUtils.getNeutronL2gateway(broker, l2GatewayId);
278 if (l2Gateway == null) {
279 LOG.error("Failed to find the l2gateway for the connection {}", input.getUuid());
281 } else if (l2Gateway.nonnullDevices() != null) {
282 l2gwDevicesToBeDeleted.addAll(l2Gateway.nonnullDevices().values());
285 for (Devices l2Device : l2gwDevicesToBeDeleted) {
286 String l2DeviceName = l2Device.getDeviceName();
287 L2GatewayDevice l2GatewayDevice = l2GatewayCache.get(l2DeviceName);
288 String hwvtepNodeId = l2GatewayDevice.getHwvtepNodeId();
289 if (hwvtepNodeId == null) {
290 LOG.error("Could not disassociate failed to get node id {}", l2DeviceName);
293 boolean isLastL2GwConnDeleted = false;
294 L2GatewayDevice elanL2GwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName, hwvtepNodeId);
295 if (elanL2GwDevice != null && isLastL2GwConnBeingDeleted(elanL2GwDevice)) {
296 // Delete L2 Gateway device from 'ElanL2GwDevice' cache
297 LOG.debug("Elan L2Gw Conn cache removed for id {}", hwvtepNodeId);
298 ElanL2GwCacheUtils.removeL2GatewayDeviceFromCache(elanName, hwvtepNodeId);
299 isLastL2GwConnDeleted = true;
301 Uuid l2GwConnId = input.key().getUuid();
302 LOG.debug("Elan L2Gw Conn cache with id {} is being referred by other L2Gw Conns; so only "
303 + "L2 Gw Conn {} reference is removed", hwvtepNodeId, l2GwConnId);
304 if (elanL2GwDevice != null) {
305 elanL2GwDevice.removeL2GatewayId(l2GwConnId);
307 isLastL2GwConnDeleted = true;
311 DisAssociateHwvtepFromElanJob disAssociateHwvtepToElanJob =
312 new DisAssociateHwvtepFromElanJob(elanL2GatewayUtils, elanL2GatewayMulticastUtils,
313 elanL2GatewayBcGroupUtils, elanClusterUtils, scheduler, jobCoordinator,
314 elanL2GwDevice, elanName, l2Device, defaultVlan, hwvtepNodeId, isLastL2GwConnDeleted);
315 elanClusterUtils.runOnlyInOwnerNode(disAssociateHwvtepToElanJob.getJobKey(), "remove l2gw connection job",
316 disAssociateHwvtepToElanJob);
320 private void associateHwvtepsToElan(ElanInstance elanInstance,
321 L2gateway l2Gateway, L2gatewayConnection input, String l2GwDeviceName) {
322 String elanName = elanInstance.getElanInstanceName();
323 Integer defaultVlan = input.getSegmentId();
324 Uuid l2GwConnId = input.key().getUuid();
325 List<Devices> l2Devices = new ArrayList<>(l2Gateway.nonnullDevices().values());
327 LOG.trace("Associating ELAN {} with L2Gw Conn Id {} having below L2Gw devices {}", elanName, l2GwConnId,
330 for (Devices l2Device : l2Devices) {
331 String l2DeviceName = l2Device.getDeviceName();
332 // L2gateway can have more than one L2 Gw devices. Configure Logical Switch, VLAN mappings,...
333 // only on the switch which has come up just now and exclude all other devices from
334 // preprovisioning/re-provisioning
335 if (l2GwDeviceName != null && !l2GwDeviceName.equals(l2DeviceName)) {
336 LOG.debug("Associating Hwvtep to ELAN is not been processed for {}; as only {} got connected now!",
337 l2DeviceName, l2GwDeviceName);
340 L2GatewayDevice l2GatewayDevice = l2GatewayCache.get(l2DeviceName);
341 if (isL2GwDeviceConnected(l2GatewayDevice)) {
342 NodeId hwvtepNodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId());
344 // Delete pending delete logical switch task if scheduled
345 elanL2GatewayUtils.cancelDeleteLogicalSwitch(hwvtepNodeId,
346 ElanL2GatewayUtils.getLogicalSwitchFromElan(elanName));
348 // Add L2 Gateway device to 'ElanL2GwDevice' cache
349 addL2DeviceToElanL2GwCache(elanName, l2GatewayDevice, l2GwConnId, l2Device);
350 AssociateHwvtepToElanJob associateHwvtepToElanJob = new AssociateHwvtepToElanJob(broker,
351 elanL2GatewayUtils, elanL2GatewayMulticastUtils, elanL2GatewayBcGroupUtils,
352 l2GatewayDevice, elanInstance, l2Device, defaultVlan);
354 elanClusterUtils.runOnlyInOwnerNode(associateHwvtepToElanJob.getJobKey(),
355 "create logical switch in hwvtep topo", associateHwvtepToElanJob);
358 LOG.error("L2GwConn create is not handled for device with id {} as it's not connected {}",
359 l2DeviceName, input);
364 public L2GatewayDevice addL2DeviceToElanL2GwCache(String elanName, L2GatewayDevice l2GatewayDevice, Uuid l2GwConnId,
366 String l2gwDeviceNodeId = l2GatewayDevice.getHwvtepNodeId();
367 L2GatewayDevice elanL2GwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName, l2gwDeviceNodeId);
368 if (elanL2GwDevice == null) {
369 elanL2GwDevice = new L2GatewayDevice(l2GatewayDevice.getDeviceName());
370 elanL2GwDevice.setHwvtepNodeId(l2gwDeviceNodeId);
371 elanL2GwDevice.setTunnelIps(l2GatewayDevice.getTunnelIps());
372 ElanL2GwCacheUtils.addL2GatewayDeviceToCache(elanName, elanL2GwDevice);
373 LOG.debug("Elan L2GwConn cache created for hwvtep id {}", l2gwDeviceNodeId);
375 LOG.debug("Elan L2GwConn cache already exists for hwvtep id {}; updating L2GwConn id {} to it",
376 l2gwDeviceNodeId, l2GwConnId);
378 elanL2GwDevice.addL2GatewayId(l2GwConnId);
379 elanL2GwDevice.addL2gwConnectionIdToDevice(l2GwConnId, l2Device);
381 //incase of cluster reboot scenario southbound device would have added more macs
382 //while odl is down, pull them now
383 readAndCopyLocalUcastMacsToCache(elanName, l2GatewayDevice);
385 LOG.info("Elan L2GwConn cache updated with below details: {}", elanL2GwDevice);
386 return elanL2GwDevice;
389 private static boolean isL2GwDeviceConnected(L2GatewayDevice l2GwDevice) {
390 return l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null;
393 protected static boolean isLastL2GwConnBeingDeleted(@NonNull L2GatewayDevice l2GwDevice) {
394 return l2GwDevice.getL2GatewayIds().size() == 1;
397 private void readAndCopyLocalUcastMacsToCache(final String elanName, final L2GatewayDevice l2GatewayDevice) {
398 final InstanceIdentifier<Node> nodeIid = HwvtepSouthboundUtils.createInstanceIdentifier(
399 new NodeId(l2GatewayDevice.getHwvtepNodeId()));
400 jobCoordinator.enqueueJob(elanName + ":" + l2GatewayDevice.getDeviceName(), () -> {
401 final SettableFuture settableFuture = SettableFuture.create();
402 FluentFuture<Optional<Node>> fluentFuture = broker.newReadOnlyTransaction().read(
403 LogicalDatastoreType.OPERATIONAL, nodeIid);
404 Futures.addCallback(fluentFuture, new FutureCallback<Optional<Node>>() {
406 public void onSuccess(@NonNull Optional<Node> resultNode) {
407 Optional<Node> nodeOptional = resultNode;
408 if (nodeOptional.isPresent()) {
409 Node node = nodeOptional.get();
410 if (node.augmentation(HwvtepGlobalAugmentation.class) != null) {
411 List<LocalUcastMacs> localUcastMacs = new ArrayList<>(
412 node.augmentation(HwvtepGlobalAugmentation.class)
413 .nonnullLocalUcastMacs().values());
414 if (localUcastMacs == null) {
417 localUcastMacs.stream()
418 .filter(mac -> macBelongsToLogicalSwitch(mac, elanName))
420 InstanceIdentifier<LocalUcastMacs> macIid = getMacIid(nodeIid, mac);
421 localUcastMacListener.added(macIid, mac);
428 public void onFailure(Throwable throwable) {
430 }, MoreExecutors.directExecutor());
431 return Lists.newArrayList(fluentFuture);
436 * Gets the associated l2 gw connections.
438 * @param l2GatewayId the l2 gateway id
440 * @return the associated l2 gw connections
442 public List<L2gatewayConnection> getL2GwConnectionsByL2GatewayId(Uuid l2GatewayId) {
443 List<L2gatewayConnection> l2GwConnections = new ArrayList<>();
444 List<L2gatewayConnection> allL2GwConns = getAllL2gatewayConnections(broker);
445 for (L2gatewayConnection l2GwConn : allL2GwConns) {
446 if (l2GwConn.getL2gatewayId() != null && Objects.equals(l2GwConn.getL2gatewayId(), l2GatewayId)) {
447 l2GwConnections.add(l2GwConn);
450 return l2GwConnections;
453 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
454 justification = "https://github.com/spotbugs/spotbugs/issues/811")
455 private static boolean macBelongsToLogicalSwitch(LocalUcastMacs mac, String logicalSwitchName) {
456 InstanceIdentifier<LogicalSwitches> iid = (InstanceIdentifier<LogicalSwitches>)
457 mac.getLogicalSwitchRef().getValue();
458 return iid.firstKeyOf(LogicalSwitches.class).getHwvtepNodeName().getValue().equals(logicalSwitchName);
461 static InstanceIdentifier<LocalUcastMacs> getMacIid(InstanceIdentifier<Node> nodeIid, LocalUcastMacs mac) {
462 return nodeIid.augmentation(HwvtepGlobalAugmentation.class).child(LocalUcastMacs.class, mac.key());