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