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