NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / utils / TransportZoneNotificationUtil.java
1 /*
2  * Copyright (c) 2017 HPE 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.utils;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11
12 import com.google.common.collect.MapDifference;
13 import com.google.common.collect.MapDifference.ValueDifference;
14 import com.google.common.collect.Maps;
15 import java.util.Collections;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Objects;
21 import java.util.Optional;
22 import java.util.Set;
23 import java.util.concurrent.ExecutionException;
24 import java.util.stream.Collectors;
25 import javax.inject.Inject;
26 import javax.inject.Singleton;
27 import org.eclipse.jdt.annotation.NonNull;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
30 import org.opendaylight.genius.infra.Datastore.Configuration;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
32 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
33 import org.opendaylight.genius.infra.TypedReadTransaction;
34 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
35 import org.opendaylight.genius.infra.TypedWriteTransaction;
36 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
37 import org.opendaylight.mdsal.binding.api.DataBroker;
38 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
39 import org.opendaylight.mdsal.common.api.ReadFailedException;
40 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
41 import org.opendaylight.netvirt.elan.internal.ElanBridgeManager;
42 import org.opendaylight.netvirt.elanmanager.api.IElanService;
43 import org.opendaylight.ovsdb.utils.mdsal.utils.ControllerMdsalUtils;
44 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.BridgeRefInfo;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntry;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntryKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.DpnEndpoints;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfoKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.tunnel.end.points.TzMembership;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZone;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.Vteps;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.VtepsBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.VtepsKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.config.rev150710.ElanConfig;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces;
66 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68 import org.opendaylight.yangtools.yang.common.Uint64;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 @Singleton
73 public class TransportZoneNotificationUtil {
74     private static final Logger LOG = LoggerFactory.getLogger(TransportZoneNotificationUtil.class);
75     private static final String TUNNEL_PORT = "tunnel_port";
76     private static final String LOCAL_IP = "local_ip";
77     private static final String LOCAL_IPS = "local_ips";
78     private static final char IP_NETWORK_ZONE_NAME_DELIMITER = '-';
79     private static final String ALL_SUBNETS_GW = "0.0.0.0";
80     private static final String ALL_SUBNETS = "0.0.0.0/0";
81     private final ManagedNewTransactionRunner txRunner;
82     private final SingleTransactionDataBroker singleTxBroker;
83     private final SouthboundUtils southBoundUtils;
84     private final IElanService elanService;
85     private final ElanConfig elanConfig;
86     private final ElanBridgeManager elanBridgeManager;
87     private final ElanInstanceCache elanInstanceCache;
88
89     @Inject
90     public TransportZoneNotificationUtil(final DataBroker dbx,
91             final IElanService elanService, final ElanConfig elanConfig, final ElanBridgeManager elanBridgeManager,
92             final ElanInstanceCache elanInstanceCache) {
93         this.txRunner = new ManagedNewTransactionRunnerImpl(dbx);
94         this.singleTxBroker = new SingleTransactionDataBroker(dbx);
95         this.elanService = elanService;
96         this.elanConfig = elanConfig;
97         this.elanBridgeManager = elanBridgeManager;
98         this.elanInstanceCache = elanInstanceCache;
99         southBoundUtils = new SouthboundUtils(new ControllerMdsalUtils(dbx));
100     }
101
102     public boolean shouldCreateVtep(List<VpnInterfaces> vpnInterfaces) {
103         if (vpnInterfaces == null || vpnInterfaces.isEmpty()) {
104             return false;
105         }
106
107         for (VpnInterfaces vpnInterface : vpnInterfaces) {
108             String interfaceName = vpnInterface.getInterfaceName();
109
110             ElanInterface elanInt = elanService.getElanInterfaceByElanInterfaceName(interfaceName);
111             if (elanInt == null) {
112                 continue;
113             }
114
115             if (ElanUtils.isVxlanNetworkOrVxlanSegment(
116                     elanInstanceCache.get(elanInt.getElanInstanceName()).orElse(null))) {
117                 return true;
118             } else {
119                 LOG.debug("Non-VXLAN elanInstance: {}", elanInt.getElanInstanceName());
120             }
121         }
122
123         return false;
124     }
125
126     private static TransportZone createZone(String subnetIp, String zoneName) {
127         TransportZoneBuilder tzb = new TransportZoneBuilder().withKey(new TransportZoneKey(zoneName))
128                 .setTunnelType(TunnelTypeVxlan.class).setZoneName(zoneName);
129         return tzb.build();
130     }
131
132     private static void updateTransportZone(TransportZone zone, Uint64 dpnId,
133             @NonNull TypedWriteTransaction<Configuration> tx) {
134         InstanceIdentifier<TransportZone> path = InstanceIdentifier.builder(TransportZones.class)
135                 .child(TransportZone.class, new TransportZoneKey(zone.getZoneName())).build();
136
137         tx.merge(path, zone);
138         LOG.info("Transport zone {} updated due to dpn {} handling.", zone.getZoneName(), dpnId);
139     }
140
141     public void updateTransportZone(String zoneNamePrefix, Uint64 dpnId) {
142         LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
143             Map<String, String> localIps = getDpnLocalIps(dpnId);
144             if (!localIps.isEmpty()) {
145                 LOG.debug("Will use local_ips for transport zone update for dpn {} and zone name prefix {}", dpnId,
146                         zoneNamePrefix);
147                 for (Entry<String, String> entry : localIps.entrySet()) {
148                     String localIp = entry.getKey();
149                     String underlayNetworkName = entry.getValue();
150                     String zoneName = getTzNameForUnderlayNetwork(zoneNamePrefix, underlayNetworkName);
151                     updateTransportZone(zoneName, dpnId, localIp, tx);
152                 }
153             } else {
154                 updateTransportZone(zoneNamePrefix, dpnId, getDpnLocalIp(dpnId), tx);
155             }
156         }), LOG, "Error updating transport zone");
157     }
158
159     @SuppressWarnings("checkstyle:IllegalCatch")
160     private void updateTransportZone(String zoneName, Uint64 dpnId, @Nullable String localIp,
161             @NonNull TypedReadWriteTransaction<Configuration> tx)
162             throws ExecutionException, InterruptedException {
163         InstanceIdentifier<TransportZone> inst = InstanceIdentifier.create(TransportZones.class)
164                 .child(TransportZone.class, new TransportZoneKey(zoneName));
165
166         // FIXME: Read this through a cache
167         TransportZone zone = tx.read(inst).get().orElse(null);
168
169         if (zone == null) {
170             zone = createZone(ALL_SUBNETS, zoneName);
171         }
172
173         try {
174             if (addVtep(zone, ALL_SUBNETS, dpnId, localIp)) {
175                 updateTransportZone(zone, dpnId, tx);
176             }
177         } catch (Exception e) {
178             LOG.error("Failed to add tunnels for dpn {} in zone {}", dpnId, zoneName, e);
179         }
180     }
181
182     private static void deleteTransportZone(TransportZone zone, Uint64 dpnId,
183             @NonNull TypedWriteTransaction<Configuration> tx) {
184         InstanceIdentifier<TransportZone> path = InstanceIdentifier.builder(TransportZones.class)
185                 .child(TransportZone.class, new TransportZoneKey(zone.getZoneName())).build();
186         tx.delete(path);
187         LOG.info("Transport zone {} deleted due to dpn {} handling.", zone.getZoneName(), dpnId);
188     }
189
190     public void deleteTransportZone(String zoneNamePrefix, Uint64 dpnId) {
191         LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
192             Map<String, String> localIps = getDpnLocalIps(dpnId);
193             if (!localIps.isEmpty()) {
194                 LOG.debug("Will use local_ips for transport zone delete for dpn {} and zone name prefix {}", dpnId,
195                         zoneNamePrefix);
196                 for (String underlayNetworkName : localIps.values()) {
197                     String zoneName = getTzNameForUnderlayNetwork(zoneNamePrefix, underlayNetworkName);
198                     deleteTransportZone(zoneName, dpnId, tx);
199                 }
200             } else {
201                 deleteTransportZone(zoneNamePrefix, dpnId, tx);
202             }
203         }), LOG, "Error deleting transport zone");
204     }
205
206     @SuppressWarnings("checkstyle:IllegalCatch")
207     private static void deleteTransportZone(String zoneName, Uint64 dpnId,
208             @NonNull TypedReadWriteTransaction<Configuration> tx) throws ExecutionException, InterruptedException {
209         InstanceIdentifier<TransportZone> inst = InstanceIdentifier.create(TransportZones.class)
210                 .child(TransportZone.class, new TransportZoneKey(zoneName));
211
212         // FIXME: Read this through a cache
213         TransportZone zone = tx.read(inst).get().orElse(null);
214         if (zone != null) {
215             try {
216                 deleteTransportZone(zone, dpnId, tx);
217             } catch (Exception e) {
218                 LOG.error("Failed to remove tunnels for dpn {} in zone {}", dpnId, zoneName, e);
219             }
220         }
221     }
222
223
224     /**
225      * Update transport zones based on local_ips TEP ips mapping to underlay
226      * networks.<br>
227      * Deleted local_ips will be removed from the VTEP list of the corresponding
228      * transport zones.<br>
229      * Added local_ips will be added to all transport zones currently associated
230      * with the TEP<br>
231      * local_ips for whom the underlay network mapping has been changed will be
232      * updated in the VTEP lists of the corresponding transport zones.
233      *
234      * @param origNode
235      *            original OVS node
236      * @param updatedNode
237      *            updated OVS node
238      * @param managerNodeId
239      *            uuid of the OVS manager node
240      */
241     public void handleOvsdbNodeUpdate(Node origNode, Node updatedNode, String managerNodeId) {
242         Map<String,
243                 String> origLocalIpMap = java.util.Optional
244                         .ofNullable(elanBridgeManager.getOpenvswitchOtherConfigMap(origNode, LOCAL_IPS))
245                         .orElse(Collections.emptyMap());
246         Map<String,
247                 String> updatedLocalIpMap = java.util.Optional
248                         .ofNullable(elanBridgeManager.getOpenvswitchOtherConfigMap(updatedNode, LOCAL_IPS))
249                         .orElse(Collections.emptyMap());
250         MapDifference<String, String> mapDiff = Maps.difference(origLocalIpMap, updatedLocalIpMap);
251         if (mapDiff.areEqual()) {
252             return;
253         }
254
255         java.util.Optional<Uint64> dpIdOpt = elanBridgeManager.getDpIdFromManagerNodeId(managerNodeId);
256         if (!dpIdOpt.isPresent()) {
257             LOG.debug("No DPN id found for node {}", managerNodeId);
258             return;
259         }
260
261         LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
262             Uint64 dpId = dpIdOpt.get();
263             Optional<DPNTEPsInfo> dpnTepsInfoOpt = getDpnTepsInfo(dpId, tx);
264             if (!dpnTepsInfoOpt.isPresent()) {
265                 LOG.debug("No DPNTEPsInfo found for DPN id {}", dpId);
266                 return;
267             }
268
269             List<TunnelEndPoints> tunnelEndPoints = dpnTepsInfoOpt.get().getTunnelEndPoints();
270             if (tunnelEndPoints == null || tunnelEndPoints.isEmpty()) {
271                 LOG.debug("No tunnel endpoints defined for DPN id {}", dpId);
272                 return;
273             }
274
275             Set<String> zonePrefixes = new HashSet<>();
276             Map<String, List<String>> tepTzMap = tunnelEndPoints.stream().collect(Collectors
277                     .toMap(tep -> tep.getIpAddress().stringValue(), this::getTepTransportZoneNames));
278             LOG.trace("Transport zone prefixes {}", tepTzMap);
279
280             handleRemovedLocalIps(mapDiff.entriesOnlyOnLeft(), dpId, zonePrefixes, tepTzMap, tx);
281             handleChangedLocalIps(mapDiff.entriesDiffering(), dpId, zonePrefixes, tepTzMap, tx);
282             handleAddedLocalIps(mapDiff.entriesOnlyOnRight(), dpId, zonePrefixes, tx);
283         }), LOG, "Error handling OVSDB node update");
284     }
285
286     private void handleAddedLocalIps(Map<String, String> addedEntries, Uint64 dpId, Set<String> zonePrefixes,
287             TypedReadWriteTransaction<Configuration> tx) throws ExecutionException, InterruptedException {
288         if (addedEntries == null || addedEntries.isEmpty()) {
289             LOG.trace("No added local_ips found for DPN {}", dpId);
290             return;
291         }
292
293         LOG.debug("Added local_ips {} on DPN {}", addedEntries.keySet(), dpId);
294         for (Map.Entry<String, String> addedEntry : addedEntries.entrySet()) {
295             String ipAddress = addedEntry.getKey();
296             String underlayNetworkName = addedEntry.getValue();
297             for (String zonePrefix : zonePrefixes) {
298                 String zoneName = getTzNameForUnderlayNetwork(zonePrefix, underlayNetworkName);
299                 updateTransportZone(zoneName, dpId, ipAddress, tx);
300             }
301         }
302     }
303
304     private void handleChangedLocalIps(Map<String, ValueDifference<String>> changedEntries, Uint64 dpId,
305             Set<String> zonePrefixes, Map<String, List<String>> tepTzMap,
306             @NonNull TypedReadWriteTransaction<Configuration> tx) throws ExecutionException, InterruptedException {
307         if (changedEntries == null || changedEntries.isEmpty()) {
308             LOG.trace("No changed local_ips found for DPN {}", dpId);
309             return;
310         }
311
312         LOG.debug("Changing underlay network mapping for local_ips {} on DPN {}", changedEntries.keySet(), dpId);
313         for (Map.Entry<String, ValueDifference<String>> changedEntry : changedEntries.entrySet()) {
314             String ipAddress = changedEntry.getKey();
315             ValueDifference<String> underlayNetworkDiff = changedEntry.getValue();
316             List<String> zoneNames = tepTzMap.get(ipAddress);
317             if (zoneNames != null) {
318                 for (String zoneName : zoneNames) {
319                     String removedUnderlayNetwork = underlayNetworkDiff.leftValue();
320                     String addedUnderlayNetwork = underlayNetworkDiff.rightValue();
321                     Optional<String> zonePrefixOpt = getZonePrefixForUnderlayNetwork(zoneName, removedUnderlayNetwork);
322                     if (zonePrefixOpt.isPresent()) {
323                         String zonePrefix = zonePrefixOpt.get();
324                         removeVtep(zoneName, dpId, tx);
325                         zonePrefixes.add(zonePrefix);
326                         String newZoneName = getTzNameForUnderlayNetwork(zonePrefix, addedUnderlayNetwork);
327                         updateTransportZone(newZoneName, dpId, ipAddress, tx);
328                     }
329                 }
330             }
331         }
332     }
333
334     private static void handleRemovedLocalIps(Map<String, String> removedEntries, Uint64 dpId,
335             Set<String> zonePrefixes, Map<String, List<String>> tepTzMap,
336             @NonNull TypedWriteTransaction<Configuration> tx) {
337         if (removedEntries == null || removedEntries.isEmpty()) {
338             LOG.trace("No removed local_ips found on DPN {}", dpId);
339             return;
340         }
341
342         LOG.debug("Removed local_ips {} for DPN {}", removedEntries.keySet(), dpId);
343         removedEntries.forEach((ipAddress, underlayNetworkName) -> {
344             List<String> zoneNames = tepTzMap.get(ipAddress);
345             if (zoneNames != null) {
346                 for (String zoneName : zoneNames) {
347                     Optional<String> zonePrefix = getZonePrefixForUnderlayNetwork(zoneName, underlayNetworkName);
348                     if (zonePrefix.isPresent()) {
349                         removeVtep(zoneName, dpId, tx);
350                         zonePrefixes.add(zonePrefix.get());
351                     }
352                 }
353             }
354         });
355     }
356
357     private List<String> getTepTransportZoneNames(TunnelEndPoints tep) {
358         List<TzMembership> tzMembershipList = tep.getTzMembership();
359         if (tzMembershipList == null) {
360             LOG.debug("No TZ membership exist for TEP ip {}", tep.getIpAddress().stringValue());
361             return Collections.emptyList();
362         }
363
364         return tzMembershipList.stream().map(TzMembership::getZoneName).distinct()
365                 .collect(Collectors.toList());
366     }
367
368     private static Optional<DPNTEPsInfo> getDpnTepsInfo(Uint64 dpId, TypedReadTransaction<Configuration> tx) {
369         InstanceIdentifier<DPNTEPsInfo> identifier = InstanceIdentifier.builder(DpnEndpoints.class)
370                 .child(DPNTEPsInfo.class, new DPNTEPsInfoKey(dpId)).build();
371         try {
372             return tx.read(identifier).get();
373         } catch (InterruptedException | ExecutionException e) {
374             LOG.warn("Failed to read DPNTEPsInfo for DPN id {}", dpId);
375             return Optional.empty();
376         }
377     }
378
379     /**
380      * Tries to add a vtep for a transport zone.
381      *
382      * @return Whether a vtep was added or not.
383      */
384     private boolean addVtep(TransportZone zone, String subnetIp, Uint64 dpnId, @Nullable String localIp) {
385         for (Vteps existingVtep : zone.nonnullVteps()) {
386             if (Objects.equals(existingVtep.getDpnId(), dpnId)) {
387                 return false;
388             }
389         }
390
391         if (localIp != null) {
392             IpAddress nodeIp = IpAddressBuilder.getDefaultInstance(localIp);
393             VtepsBuilder vtepsBuilder = new VtepsBuilder().setDpnId(dpnId).setIpAddress(nodeIp)
394                     .setOptionOfTunnel(elanConfig.isUseOfTunnels());
395             zone.getVteps().add(vtepsBuilder.build());
396             return true;
397         }
398
399         return false;
400     }
401
402     private static void removeVtep(String zoneName, Uint64 dpId, @NonNull TypedWriteTransaction<Configuration> tx) {
403         InstanceIdentifier<Vteps> path = InstanceIdentifier.builder(TransportZones.class)
404                 .child(TransportZone.class, new TransportZoneKey(zoneName))
405                 .child(Vteps.class, new VtepsKey(dpId)).build();
406         tx.delete(path);
407     }
408
409     @Nullable
410     private String getDpnLocalIp(Uint64 dpId) throws ReadFailedException {
411         Optional<Node> node = getPortsNode(dpId);
412
413         if (node.isPresent()) {
414             String localIp = southBoundUtils.getOpenvswitchOtherConfig(node.get(), LOCAL_IP);
415             if (localIp == null) {
416                 LOG.error("missing local_ip key in ovsdb:openvswitch-other-configs in operational"
417                         + " network-topology for node: {}", node.get().getNodeId().getValue());
418             } else {
419                 return localIp;
420             }
421         }
422
423         return null;
424     }
425
426     @NonNull
427     private Map<String, String> getDpnLocalIps(Uint64 dpId) throws ReadFailedException {
428         // Example of local IPs from other_config:
429         // local_ips="10.0.43.159:MPLS,11.11.11.11:DSL,ip:underlay-network"
430         return getPortsNode(dpId).map(
431             node -> elanBridgeManager.getOpenvswitchOtherConfigMap(node, LOCAL_IPS)).orElse(Collections.emptyMap());
432     }
433
434     @SuppressWarnings("unchecked")
435     private Optional<Node> getPortsNode(Uint64 dpnId) throws ReadFailedException {
436         InstanceIdentifier<BridgeRefEntry> bridgeRefInfoPath = InstanceIdentifier.create(BridgeRefInfo.class)
437                 .child(BridgeRefEntry.class, new BridgeRefEntryKey(dpnId));
438
439         try {
440             // FIXME: Read this through a cache
441             Optional<BridgeRefEntry> optionalBridgeRefEntry =
442                 singleTxBroker
443                     .syncReadOptional(LogicalDatastoreType.OPERATIONAL, bridgeRefInfoPath);
444             if (!optionalBridgeRefEntry.isPresent()) {
445                 LOG.error("no bridge ref entry found for dpnId {}", dpnId);
446                 return Optional.empty();
447             }
448
449             InstanceIdentifier<Node> nodeId =
450                 optionalBridgeRefEntry.get().getBridgeReference().getValue()
451                     .firstIdentifierOf(Node.class);
452
453             // FIXME: Read this through a cache
454             Optional<Node> optionalNode = singleTxBroker
455                 .syncReadOptional(LogicalDatastoreType.OPERATIONAL, nodeId);
456             if (!optionalNode.isPresent()) {
457                 LOG.error("missing node for dpnId {}", dpnId);
458             }
459             return optionalNode;
460         } catch (ExecutionException | InterruptedException e) {
461             LOG.error("Exception while getting ports for Node {}", dpnId, e);
462         }
463         return Optional.empty();
464     }
465
466     private static String getTzNameForUnderlayNetwork(String zoneNamePrefix, String underlayNetworkName) {
467         return zoneNamePrefix + IP_NETWORK_ZONE_NAME_DELIMITER + underlayNetworkName;
468     }
469
470     private static Optional<String> getZonePrefixForUnderlayNetwork(String zoneName, String underlayNetworkName) {
471         String[] zoneParts = zoneName.split(IP_NETWORK_ZONE_NAME_DELIMITER + underlayNetworkName);
472         return zoneParts.length == 2 ? Optional.of(zoneParts[0]) : Optional.empty();
473     }
474 }