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