Skip to content

Commit 05b2e65

Browse files
committed
Enable hostname specification as a script argument for topology script
1 parent bae9e49 commit 05b2e65

File tree

7 files changed

+139
-18
lines changed

7 files changed

+139
-18
lines changed

bookkeeper-server/src/main/java/org/apache/bookkeeper/net/CachedDNSToSwitchMapping.java

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
*
3232
*/
3333
public class CachedDNSToSwitchMapping extends AbstractDNSToSwitchMapping {
34-
private Map<String, String> cache = new ConcurrentHashMap<String, String>();
34+
private Map<String, String> cache = new ConcurrentHashMap<>();
3535

3636
/**
3737
* The uncached mapping.
@@ -46,7 +46,8 @@ public CachedDNSToSwitchMapping(DNSToSwitchMapping rawMapping) {
4646
this.rawMapping = rawMapping;
4747
}
4848

49-
// we'll use IP Address for these mappings.
49+
// we'll use IP Address or Host Name for these mappings.
50+
// The default value is the IP address.
5051
@Override
5152
public boolean useHostName() {
5253
return false;
@@ -58,7 +59,7 @@ public boolean useHostName() {
5859
*/
5960
private List<String> getUncachedHosts(List<String> names) {
6061
// find out all names without cached resolved location
61-
List<String> unCachedHosts = new ArrayList<String>(names.size());
62+
List<String> unCachedHosts = new ArrayList<>(names.size());
6263
for (String name : names) {
6364
if (cache.get(name) == null) {
6465
unCachedHosts.add(name);
@@ -91,7 +92,7 @@ private void cacheResolvedHosts(List<String> uncachedHosts,
9192
* or null if any of the names are not currently in the cache
9293
*/
9394
private List<String> getCachedHosts(List<String> names) {
94-
List<String> result = new ArrayList<String>(names.size());
95+
List<String> result = new ArrayList<>(names.size());
9596
// Construct the result
9697
for (String name : names) {
9798
String networkLocation = cache.get(name);
@@ -106,23 +107,27 @@ private List<String> getCachedHosts(List<String> names) {
106107

107108
@Override
108109
public List<String> resolve(List<String> names) {
109-
// normalize all input names to be in the form of IP addresses
110-
names = NetUtils.normalizeHostNames(names);
111-
112-
List <String> result = new ArrayList<String>(names.size());
110+
List <String> result = new ArrayList<>(names.size());
113111
if (names.isEmpty()) {
114112
return result;
115113
}
116114

115+
if (useHostName()) {
116+
// normalize all input names to be in the form of host name
117+
names = NetUtils.normalizeToHostNames(names);
118+
} else {
119+
// normalize all input names to be in the form of IP addresses
120+
names = NetUtils.normalizeToIPAddresses(names);
121+
}
122+
117123
List<String> uncachedHosts = getUncachedHosts(names);
118124

119125
// Resolve the uncached hosts
120126
List<String> resolvedHosts = rawMapping.resolve(uncachedHosts);
121-
//cache them
127+
// cache them
122128
cacheResolvedHosts(uncachedHosts, resolvedHosts);
123-
//now look up the entire list in the cache
129+
// now look up the entire list in the cache
124130
return getCachedHosts(names);
125-
126131
}
127132

128133
/**
@@ -131,7 +136,7 @@ public List<String> resolve(List<String> names) {
131136
*/
132137
@Override
133138
public Map<String, String> getSwitchMap() {
134-
Map<String, String> switchMap = new HashMap<String, String>(cache);
139+
Map<String, String> switchMap = new HashMap<>(cache);
135140
return switchMap;
136141
}
137142

bookkeeper-server/src/main/java/org/apache/bookkeeper/net/CommonConfigurationKeys.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public interface CommonConfigurationKeys {
2929
String NET_TOPOLOGY_SCRIPT_FILE_NAME_KEY = "networkTopologyScriptFileName";
3030
// number of arguments that network topology resolve script used
3131
String NET_TOPOLOGY_SCRIPT_NUMBER_ARGS_KEY = "networkTopologyScriptNumberArgs";
32+
// use hostname as an argument for network topology resolve script
33+
String NET_TOPOLOGY_SCRIPT_USE_HOSTNAME_ARGS_KEY = "networkTopologyScriptUseHostNameArgs";
3234
// default value of NET_TOPOLOGY_SCRIPT_NUMBER_ARGS_KEY
3335
int NET_TOPOLOGY_SCRIPT_NUMBER_ARGS_DEFAULT = 100;
3436
}

bookkeeper-server/src/main/java/org/apache/bookkeeper/net/NetUtils.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class NetUtils {
4040
* either a textual representation its IP address or its host name
4141
* @return its IP address in the string format
4242
*/
43-
public static String normalizeHostName(String name) {
43+
public static String normalizeToIPAddress(String name) {
4444
try {
4545
return InetAddress.getByName(name).getHostAddress();
4646
} catch (UnknownHostException e) {
@@ -54,19 +54,51 @@ public static String normalizeHostName(String name) {
5454
*
5555
* @param names a collection of string representations of hosts
5656
* @return a list of corresponding IP addresses in the string format
57-
* @see #normalizeHostName(String)
57+
* @see #normalizeToIPAddress(String)
5858
*/
59-
public static List<String> normalizeHostNames(Collection<String> names) {
60-
List<String> hostNames = new ArrayList<String>(names.size());
59+
public static List<String> normalizeToIPAddresses(Collection<String> names) {
60+
List<String> ipAddresses = new ArrayList<>(names.size());
6161
for (String name : names) {
62-
hostNames.add(normalizeHostName(name));
62+
ipAddresses.add(normalizeToIPAddress(name));
63+
}
64+
return ipAddresses;
65+
}
66+
67+
/**
68+
* Given a string representation of an IP address, return its host name
69+
* in textual presentation.
70+
*
71+
* @param name a string representation of an IP Address:
72+
* either a textual representation its IP address or its host name
73+
* @return its host name in the string format
74+
*/
75+
public static String normalizeToHostName(String name) {
76+
try {
77+
return InetAddress.getByName(name).getHostName();
78+
} catch (UnknownHostException e) {
79+
return name;
80+
}
81+
}
82+
83+
/**
84+
* Given a collection of string representation of IP addresses, return a list of
85+
* corresponding hosts in the textual representation.
86+
*
87+
* @param names a collection of string representations of IP addresses
88+
* @return a list of corresponding hosts in the string format
89+
* @see #normalizeToHostName(String)
90+
*/
91+
public static List<String> normalizeToHostNames(Collection<String> names) {
92+
List<String> hostNames = new ArrayList<>(names.size());
93+
for (String name : names) {
94+
hostNames.add(normalizeToHostName(name));
6395
}
6496
return hostNames;
6597
}
6698

6799
public static String resolveNetworkLocation(DNSToSwitchMapping dnsResolver,
68100
BookieSocketAddress addr) {
69-
List<String> names = new ArrayList<String>(1);
101+
List<String> names = new ArrayList<>(1);
70102

71103
InetSocketAddress inetSocketAddress = addr.getSocketAddress();
72104
if (dnsResolver.useHostName()) {

bookkeeper-server/src/main/java/org/apache/bookkeeper/net/ScriptBasedMapping.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public final class ScriptBasedMapping extends CachedDNSToSwitchMapping {
6363
* {@value}.
6464
*/
6565
static final String SCRIPT_ARG_COUNT_KEY = CommonConfigurationKeys.NET_TOPOLOGY_SCRIPT_NUMBER_ARGS_KEY;
66+
/**
67+
* Key to specify whether hostname should be used as an argument
68+
* {@value}.
69+
*/
70+
static final String SCRIPT_USE_HOSTNAME_KEY = CommonConfigurationKeys.NET_TOPOLOGY_SCRIPT_USE_HOSTNAME_ARGS_KEY;
6671
/**
6772
* Text used in the {@link #toString()} method if there is no string
6873
* {@value}.
@@ -122,13 +127,19 @@ public void setConf(Configuration conf) {
122127
getRawMapping().setConf(conf);
123128
}
124129

130+
@Override
131+
public boolean useHostName() {
132+
return getRawMapping().useHostName();
133+
}
134+
125135
/**
126136
* This is the uncached script mapping that is fed into the cache managed
127137
* by the superclass {@link CachedDNSToSwitchMapping}.
128138
*/
129139
private static final class RawScriptBasedMapping extends AbstractDNSToSwitchMapping {
130140
private String scriptName;
131141
private int maxArgs; //max hostnames per call of the script
142+
private boolean useHostName;
132143
private static final Logger LOG = LoggerFactory.getLogger(RawScriptBasedMapping.class);
133144

134145
/*
@@ -149,6 +160,7 @@ protected void validateConf() {
149160
if (StringUtils.isNotBlank(scriptNameConfValue)) {
150161
scriptName = scriptNameConfValue;
151162
maxArgs = conf.getInt(SCRIPT_ARG_COUNT_KEY, DEFAULT_ARG_COUNT);
163+
useHostName = conf.getBoolean(SCRIPT_USE_HOSTNAME_KEY, false);
152164
} else {
153165
scriptName = null;
154166
maxArgs = 0;
@@ -290,5 +302,10 @@ public void reloadCachedMappings() {
290302
// Nothing to do here, since RawScriptBasedMapping has no cache, and
291303
// does not inherit from CachedDNSToSwitchMapping
292304
}
305+
306+
@Override
307+
public boolean useHostName() {
308+
return useHostName;
309+
}
293310
}
294311
}

bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestRackawareEnsemblePlacementPolicyUsingScript.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,14 @@
2626
import static org.junit.Assert.assertFalse;
2727
import static org.junit.Assert.assertTrue;
2828
import static org.junit.Assert.fail;
29+
import static org.mockito.ArgumentMatchers.any;
30+
import static org.mockito.ArgumentMatchers.anyString;
31+
import static org.mockito.ArgumentMatchers.eq;
32+
import static org.mockito.Mockito.times;
2933

3034
import com.google.common.util.concurrent.ThreadFactoryBuilder;
3135
import io.netty.util.HashedWheelTimer;
36+
import java.net.InetAddress;
3237
import java.util.ArrayList;
3338
import java.util.HashSet;
3439
import java.util.List;
@@ -41,13 +46,16 @@
4146
import org.apache.bookkeeper.net.BookieSocketAddress;
4247
import org.apache.bookkeeper.net.CommonConfigurationKeys;
4348
import org.apache.bookkeeper.net.DNSToSwitchMapping;
49+
import org.apache.bookkeeper.net.NetUtils;
4450
import org.apache.bookkeeper.net.ScriptBasedMapping;
4551
import org.apache.bookkeeper.stats.NullStatsLogger;
4652
import org.apache.bookkeeper.util.Shell;
4753
import org.junit.After;
4854
import org.junit.Assume;
4955
import org.junit.Before;
5056
import org.junit.Test;
57+
import org.mockito.MockedStatic;
58+
import org.mockito.Mockito;
5159
import org.slf4j.Logger;
5260
import org.slf4j.LoggerFactory;
5361

@@ -462,6 +470,59 @@ public void testIfValidateConfFails() throws Exception {
462470
}
463471
}
464472

473+
@Test
474+
public void testUseHostnameArgsOption() throws Exception {
475+
ignoreTestIfItIsWindowsOS();
476+
repp.uninitalize();
477+
478+
// Mock NetUtils.
479+
try (MockedStatic<NetUtils> netUtils = Mockito.mockStatic(NetUtils.class)) {
480+
netUtils.when(() -> NetUtils.resolveNetworkLocation(any(DNSToSwitchMapping.class), any(BookieSocketAddress.class))).thenCallRealMethod();
481+
netUtils.when(() -> NetUtils.normalizeToHostNames(any())).thenCallRealMethod();
482+
netUtils.when(() -> NetUtils.normalizeToHostName(anyString())).thenCallRealMethod();
483+
484+
netUtils.when(() -> NetUtils.normalizeToHostName(eq(InetAddress.getLocalHost().getHostAddress()))).thenReturn("bookie1");
485+
netUtils.when(() -> NetUtils.normalizeToHostName(eq("127.0.0.4"))).thenReturn("bookie22");
486+
487+
// Initialize RackawareEnsemblePlacementPolicy.
488+
ClientConfiguration newConf = new ClientConfiguration();
489+
newConf.setProperty(REPP_DNS_RESOLVER_CLASS, ScriptBasedMapping.class.getName());
490+
newConf.setProperty(CommonConfigurationKeys.NET_TOPOLOGY_SCRIPT_FILE_NAME_KEY,
491+
"src/test/resources/networkmappingscript.sh");
492+
newConf.setProperty(CommonConfigurationKeys.NET_TOPOLOGY_SCRIPT_USE_HOSTNAME_ARGS_KEY, true);
493+
timer = new HashedWheelTimer(
494+
new ThreadFactoryBuilder().setNameFormat("TestTimer-%d").build(),
495+
conf.getTimeoutTimerTickDurationMs(), TimeUnit.MILLISECONDS,
496+
conf.getTimeoutTimerNumTicks());
497+
repp = new RackawareEnsemblePlacementPolicy();
498+
repp.initialize(newConf, Optional.<DNSToSwitchMapping>empty(), timer,
499+
DISABLE_ALL, NullStatsLogger.INSTANCE, BookieSocketAddress.LEGACY_BOOKIEID_RESOLVER);
500+
501+
// Join Bookie2, Bookie22, Bookie3, and Bookie33.
502+
BookieSocketAddress addr1 = new BookieSocketAddress("bookie2", 3181); // /2 rack
503+
BookieSocketAddress addr2 = new BookieSocketAddress("127.0.0.4", 3181); // /2 rack
504+
BookieSocketAddress addr3 = new BookieSocketAddress("bookie3", 3181); // /3 rack
505+
BookieSocketAddress addr4 = new BookieSocketAddress("bookie33", 3181); // /3 rack
506+
Set<BookieId> addrs = new HashSet<>();
507+
addrs.add(addr1.toBookieId());
508+
addrs.add(addr2.toBookieId());
509+
addrs.add(addr3.toBookieId());
510+
addrs.add(addr4.toBookieId());
511+
repp.onClusterChanged(addrs, new HashSet<>());
512+
513+
// Remove Bookie2.
514+
addrs.remove(addr1.toBookieId());
515+
repp.onClusterChanged(addrs, new HashSet<>());
516+
517+
BookieId replacedBookie = repp.replaceBookie(1, 1, 1, null, new ArrayList<>(),
518+
addr1.toBookieId(), new HashSet<>()).getResult();
519+
assertEquals(addr2.toBookieId(), replacedBookie);
520+
521+
netUtils.verify(() -> NetUtils.normalizeToHostName(eq(InetAddress.getLocalHost().getHostAddress())), times(1));
522+
netUtils.verify(() -> NetUtils.normalizeToHostName(eq("127.0.0.4")), times(1));
523+
}
524+
}
525+
465526
private int getNumCoveredWriteQuorums(List<BookieId> ensemble, int writeQuorumSize)
466527
throws Exception {
467528
int ensembleSize = ensemble.size();

conf/bk_server.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,9 @@ statsProviderClass=org.apache.bookkeeper.stats.prometheus.PrometheusMetricsProvi
10231023
# The max number of args used in the script provided at `networkTopologyScriptFileName`
10241024
# networkTopologyScriptNumberArgs=100
10251025

1026+
# Whether to use hostname instead of IP address as an argument for the script provided at `networkTopologyScriptFileName`
1027+
# networkTopologyScriptUseHostNameArgs=false
1028+
10261029
# minimum number of racks per write quorum. RackawareEnsemblePlacementPolicy will try to
10271030
# get bookies from atleast 'minNumRacksPerWriteQuorum' racks for a writeQuorum.
10281031
# minNumRacksPerWriteQuorum=2

site3/website/docs/reference/config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ The table below lists parameters that you can set to configure bookies. All conf
322322
| reppDnsResolverClass | The DNS resolver class used for resolving network locations for bookies. The setting is used<br />when using either RackawareEnsemblePlacementPolicy and RegionAwareEnsemblePlacementPolicy.<br /> | org.apache.bookkeeper.net.ScriptBasedMapping |
323323
| networkTopologyScriptFileName | The bash script used by `ScriptBasedMapping` DNS resolver for resolving bookies' network locations.<br /> | |
324324
| networkTopologyScriptNumberArgs | The max number of args used in the script provided at `networkTopologyScriptFileName`.<br /> | |
325+
| networkTopologyScriptUseHostNameArgs | Whether to use hostname instead of IP address as an argument for the script provided at `networkTopologyScriptFileName`.<br /> | false |
325326
| minNumRacksPerWriteQuorum | minimum number of racks per write quorum. RackawareEnsemblePlacementPolicy will try to get bookies from atleast 'minNumRacksPerWriteQuorum' racks for a writeQuorum.<br /> | |
326327
| enforceMinNumRacksPerWriteQuorum | 'enforceMinNumRacksPerWriteQuorum' enforces RackawareEnsemblePlacementPolicy to pick bookies from 'minNumRacksPerWriteQuorum' racks for a writeQuorum. If it cann't find bookie then it would throw BKNotEnoughBookiesException instead of picking random one.<br /> | |
327328
| ignoreLocalNodeInPlacementPolicy | 'ignoreLocalNodeInPlacementPolicy' specifies whether to ignore localnode in the internal logic of placement policy. If it is not possible or useful to use Bookkeeper client node's (or AutoReplicator) rack/region info. for placement policy then it is better to ignore localnode instead of false alarming with log lines and metrics.<br /> | |

0 commit comments

Comments
 (0)