diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionServerHostname.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionServerHostname.java new file mode 100644 index 0000000..ee76080 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionServerHostname.java @@ -0,0 +1,154 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.regionserver; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.CoordinatedStateManager; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.MiniHBaseCluster.MiniHBaseClusterRegionServer; +import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerStartupResponse; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.hadoop.hbase.testclassification.RegionServerTests; +import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; +import org.apache.hadoop.hbase.util.Threads; +import org.apache.hadoop.hbase.zookeeper.ZKUtil; +import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * Tests for the hostname specification by region server + */ +@Category({RegionServerTests.class, MediumTests.class}) +public class TestRegionServerHostname { + private static final Log LOG = LogFactory.getLog(TestRegionServerHostname.class); + private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + + @Test (timeout=30000) + public void testInvalidRegionServerHostnameAbortsServer() throws Exception { + final int NUM_MASTERS = 1; + final int NUM_RS = 1; + String invalidHostname = "hostAddr"; + TEST_UTIL.getConfiguration().set(HRegionServer.HOSTNAME_KEY, invalidHostname); + try { + TEST_UTIL.startMiniCluster(NUM_MASTERS, NUM_RS); + } catch (IOException ioe) { + Throwable t1 = ioe.getCause(); + Throwable t2 = t1.getCause(); + assertTrue(t2.getMessage().contains("Failed resolve of " + invalidHostname)); + return; + } finally { + TEST_UTIL.shutdownMiniCluster(); + } + assertTrue("Failed to validate against invalid hostname", false); + } + + @Test(timeout=120000) + public void testRegionServerHostname() throws Exception { + final int NUM_MASTERS = 1; + final int NUM_RS = 1; + Enumeration netInterfaceList = NetworkInterface.getNetworkInterfaces(); + + while (netInterfaceList.hasMoreElements()) { + NetworkInterface ni = netInterfaceList.nextElement(); + Enumeration addrList = ni.getInetAddresses(); + // iterate through host addresses and use each as hostname + while (addrList.hasMoreElements()) { + InetAddress addr = addrList.nextElement(); + if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) continue; + if (addr.isMulticastAddress()) continue; + String hostAddr = addr.getHostAddress(); + LOG.info("Found " + hostAddr + " on " + ni); + + TEST_UTIL.getConfiguration().set(HRegionServer.HOSTNAME_KEY, hostAddr); + TEST_UTIL.startMiniCluster(NUM_MASTERS, NUM_RS); + try { + ZooKeeperWatcher zkw = TEST_UTIL.getZooKeeperWatcher(); + List servers = ZKUtil.listChildrenNoWatch(zkw, zkw.rsZNode); + // there would be NUM_RS+1 children - one for the master + assertTrue(servers.size() == NUM_RS+1); + for (String server : servers) { + assertTrue(server.startsWith(hostAddr+",")); + } + zkw.close(); + } finally { + TEST_UTIL.shutdownMiniCluster(); + } + } + } + } + + private static final String maliciousHostname = "malicious.xom"; + private static boolean rsAborted = false; + private static class RegionServerWithFakeHostname extends MiniHBaseClusterRegionServer { + public RegionServerWithFakeHostname(Configuration conf, CoordinatedStateManager cp) + throws IOException, InterruptedException { + super(conf, cp); + } + + protected RSRpcServices createRpcServices() throws IOException { + this.conf.set(HRegionServer.HOSTNAME_KEY, maliciousHostname); + + return new RSRpcServicesWithFakeHostname(this); + } + public void abort(String reason, Throwable cause) { + super.abort(reason, cause); + rsAborted = true; + } + } + + private static class RSRpcServicesWithFakeHostname extends RSRpcServices { + public RSRpcServicesWithFakeHostname(HRegionServer rs) + throws IOException { + super(rs); + } + @Override + public String getHostnameWithoutDNSResolutionIfPossible(Configuration conf) + throws UnknownHostException { + return RSRpcServices.getHostname(conf); + } + } + @Test(timeout=120000) + public void testRegionServerWithMaliciousHostnameGetsRejected() throws Exception { + final int NUM_MASTERS = 1; + final int NUM_RS = 1; + TEST_UTIL.getConfiguration().setStrings(HConstants.REGION_SERVER_IMPL, + RegionServerWithFakeHostname.class.getName()); + TEST_UTIL.startMiniCluster(NUM_MASTERS, NUM_RS); + try { + // wait for region server to abort + while (!rsAborted) { + Threads.sleep(10); + } + } finally { + TEST_UTIL.shutdownMiniCluster(); + } + } +} diff --git a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RegionServerStatusProtos.java b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RegionServerStatusProtos.java index ec169d5..764d47c 100644 --- a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RegionServerStatusProtos.java +++ b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RegionServerStatusProtos.java @@ -64,6 +64,33 @@ public final class RegionServerStatusProtos { * */ long getServerCurrentTime(); + + // optional string useThisHostnameInstead = 4; + /** + * optional string useThisHostnameInstead = 4; + * + *
+     ** hostname for region server, optional 
+     * 
+ */ + boolean hasUseThisHostnameInstead(); + /** + * optional string useThisHostnameInstead = 4; + * + *
+     ** hostname for region server, optional 
+     * 
+ */ + java.lang.String getUseThisHostnameInstead(); + /** + * optional string useThisHostnameInstead = 4; + * + *
+     ** hostname for region server, optional 
+     * 
+ */ + com.google.protobuf.ByteString + getUseThisHostnameInsteadBytes(); } /** * Protobuf type {@code RegionServerStartupRequest} @@ -131,6 +158,11 @@ public final class RegionServerStatusProtos { serverCurrentTime_ = input.readUInt64(); break; } + case 34: { + bitField0_ |= 0x00000008; + useThisHostnameInstead_ = input.readBytes(); + break; + } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -243,10 +275,66 @@ public final class RegionServerStatusProtos { return serverCurrentTime_; } + // optional string useThisHostnameInstead = 4; + public static final int USETHISHOSTNAMEINSTEAD_FIELD_NUMBER = 4; + private java.lang.Object useThisHostnameInstead_; + /** + * optional string useThisHostnameInstead = 4; + * + *
+     ** hostname for region server, optional 
+     * 
+ */ + public boolean hasUseThisHostnameInstead() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string useThisHostnameInstead = 4; + * + *
+     ** hostname for region server, optional 
+     * 
+ */ + public java.lang.String getUseThisHostnameInstead() { + java.lang.Object ref = useThisHostnameInstead_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + useThisHostnameInstead_ = s; + } + return s; + } + } + /** + * optional string useThisHostnameInstead = 4; + * + *
+     ** hostname for region server, optional 
+     * 
+ */ + public com.google.protobuf.ByteString + getUseThisHostnameInsteadBytes() { + java.lang.Object ref = useThisHostnameInstead_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + useThisHostnameInstead_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private void initFields() { port_ = 0; serverStartCode_ = 0L; serverCurrentTime_ = 0L; + useThisHostnameInstead_ = ""; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -281,6 +369,9 @@ public final class RegionServerStatusProtos { if (((bitField0_ & 0x00000004) == 0x00000004)) { output.writeUInt64(3, serverCurrentTime_); } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, getUseThisHostnameInsteadBytes()); + } getUnknownFields().writeTo(output); } @@ -302,6 +393,10 @@ public final class RegionServerStatusProtos { size += com.google.protobuf.CodedOutputStream .computeUInt64Size(3, serverCurrentTime_); } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, getUseThisHostnameInsteadBytes()); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -340,6 +435,11 @@ public final class RegionServerStatusProtos { result = result && (getServerCurrentTime() == other.getServerCurrentTime()); } + result = result && (hasUseThisHostnameInstead() == other.hasUseThisHostnameInstead()); + if (hasUseThisHostnameInstead()) { + result = result && getUseThisHostnameInstead() + .equals(other.getUseThisHostnameInstead()); + } result = result && getUnknownFields().equals(other.getUnknownFields()); return result; @@ -365,6 +465,10 @@ public final class RegionServerStatusProtos { hash = (37 * hash) + SERVER_CURRENT_TIME_FIELD_NUMBER; hash = (53 * hash) + hashLong(getServerCurrentTime()); } + if (hasUseThisHostnameInstead()) { + hash = (37 * hash) + USETHISHOSTNAMEINSTEAD_FIELD_NUMBER; + hash = (53 * hash) + getUseThisHostnameInstead().hashCode(); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -480,6 +584,8 @@ public final class RegionServerStatusProtos { bitField0_ = (bitField0_ & ~0x00000002); serverCurrentTime_ = 0L; bitField0_ = (bitField0_ & ~0x00000004); + useThisHostnameInstead_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); return this; } @@ -520,6 +626,10 @@ public final class RegionServerStatusProtos { to_bitField0_ |= 0x00000004; } result.serverCurrentTime_ = serverCurrentTime_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.useThisHostnameInstead_ = useThisHostnameInstead_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -545,6 +655,11 @@ public final class RegionServerStatusProtos { if (other.hasServerCurrentTime()) { setServerCurrentTime(other.getServerCurrentTime()); } + if (other.hasUseThisHostnameInstead()) { + bitField0_ |= 0x00000008; + useThisHostnameInstead_ = other.useThisHostnameInstead_; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -731,6 +846,104 @@ public final class RegionServerStatusProtos { return this; } + // optional string useThisHostnameInstead = 4; + private java.lang.Object useThisHostnameInstead_ = ""; + /** + * optional string useThisHostnameInstead = 4; + * + *
+       ** hostname for region server, optional 
+       * 
+ */ + public boolean hasUseThisHostnameInstead() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string useThisHostnameInstead = 4; + * + *
+       ** hostname for region server, optional 
+       * 
+ */ + public java.lang.String getUseThisHostnameInstead() { + java.lang.Object ref = useThisHostnameInstead_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + useThisHostnameInstead_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string useThisHostnameInstead = 4; + * + *
+       ** hostname for region server, optional 
+       * 
+ */ + public com.google.protobuf.ByteString + getUseThisHostnameInsteadBytes() { + java.lang.Object ref = useThisHostnameInstead_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + useThisHostnameInstead_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string useThisHostnameInstead = 4; + * + *
+       ** hostname for region server, optional 
+       * 
+ */ + public Builder setUseThisHostnameInstead( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + useThisHostnameInstead_ = value; + onChanged(); + return this; + } + /** + * optional string useThisHostnameInstead = 4; + * + *
+       ** hostname for region server, optional 
+       * 
+ */ + public Builder clearUseThisHostnameInstead() { + bitField0_ = (bitField0_ & ~0x00000008); + useThisHostnameInstead_ = getDefaultInstance().getUseThisHostnameInstead(); + onChanged(); + return this; + } + /** + * optional string useThisHostnameInstead = 4; + * + *
+       ** hostname for region server, optional 
+       * 
+ */ + public Builder setUseThisHostnameInsteadBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + useThisHostnameInstead_ = value; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:RegionServerStartupRequest) } @@ -8399,47 +8612,48 @@ public final class RegionServerStatusProtos { static { java.lang.String[] descriptorData = { "\n\030RegionServerStatus.proto\032\013HBase.proto\032" + - "\023ClusterStatus.proto\"b\n\032RegionServerStar" + - "tupRequest\022\014\n\004port\030\001 \002(\r\022\031\n\021server_start" + - "_code\030\002 \002(\004\022\033\n\023server_current_time\030\003 \002(\004" + - "\"C\n\033RegionServerStartupResponse\022$\n\013map_e" + - "ntries\030\001 \003(\0132\017.NameStringPair\"S\n\031RegionS" + - "erverReportRequest\022\033\n\006server\030\001 \002(\0132\013.Ser" + - "verName\022\031\n\004load\030\002 \001(\0132\013.ServerLoad\"\034\n\032Re" + - "gionServerReportResponse\"O\n\031ReportRSFata" + - "lErrorRequest\022\033\n\006server\030\001 \002(\0132\013.ServerNa", - "me\022\025\n\rerror_message\030\002 \002(\t\"\034\n\032ReportRSFat" + - "alErrorResponse\"6\n\037GetLastFlushedSequenc" + - "eIdRequest\022\023\n\013region_name\030\001 \002(\014\"D\n GetLa" + - "stFlushedSequenceIdResponse\022 \n\030last_flus" + - "hed_sequence_id\030\001 \002(\004\"\322\002\n\025RegionStateTra" + - "nsition\022>\n\017transition_code\030\001 \002(\0162%.Regio" + - "nStateTransition.TransitionCode\022 \n\013regio" + - "n_info\030\002 \003(\0132\013.RegionInfo\022\024\n\014open_seq_nu" + - "m\030\003 \001(\004\"\300\001\n\016TransitionCode\022\n\n\006OPENED\020\000\022\017" + - "\n\013FAILED_OPEN\020\001\022\n\n\006CLOSED\020\002\022\022\n\016READY_TO_", - "SPLIT\020\003\022\022\n\016READY_TO_MERGE\020\004\022\016\n\nSPLIT_PON" + - "R\020\005\022\016\n\nMERGE_PONR\020\006\022\t\n\005SPLIT\020\007\022\n\n\006MERGED" + - "\020\010\022\022\n\016SPLIT_REVERTED\020\t\022\022\n\016MERGE_REVERTED" + - "\020\n\"m\n\"ReportRegionStateTransitionRequest" + - "\022\033\n\006server\030\001 \002(\0132\013.ServerName\022*\n\ntransit" + - "ion\030\002 \003(\0132\026.RegionStateTransition\"<\n#Rep" + - "ortRegionStateTransitionResponse\022\025\n\rerro" + - "r_message\030\001 \001(\t2\326\003\n\031RegionServerStatusSe" + - "rvice\022P\n\023RegionServerStartup\022\033.RegionSer" + - "verStartupRequest\032\034.RegionServerStartupR", - "esponse\022M\n\022RegionServerReport\022\032.RegionSe" + - "rverReportRequest\032\033.RegionServerReportRe" + - "sponse\022M\n\022ReportRSFatalError\022\032.ReportRSF" + - "atalErrorRequest\032\033.ReportRSFatalErrorRes" + - "ponse\022_\n\030GetLastFlushedSequenceId\022 .GetL" + - "astFlushedSequenceIdRequest\032!.GetLastFlu" + - "shedSequenceIdResponse\022h\n\033ReportRegionSt" + - "ateTransition\022#.ReportRegionStateTransit" + - "ionRequest\032$.ReportRegionStateTransition" + - "ResponseBN\n*org.apache.hadoop.hbase.prot", - "obuf.generatedB\030RegionServerStatusProtos" + - "H\001\210\001\001\240\001\001" + "\023ClusterStatus.proto\"\202\001\n\032RegionServerSta" + + "rtupRequest\022\014\n\004port\030\001 \002(\r\022\031\n\021server_star" + + "t_code\030\002 \002(\004\022\033\n\023server_current_time\030\003 \002(" + + "\004\022\036\n\026useThisHostnameInstead\030\004 \001(\t\"C\n\033Reg" + + "ionServerStartupResponse\022$\n\013map_entries\030" + + "\001 \003(\0132\017.NameStringPair\"S\n\031RegionServerRe" + + "portRequest\022\033\n\006server\030\001 \002(\0132\013.ServerName" + + "\022\031\n\004load\030\002 \001(\0132\013.ServerLoad\"\034\n\032RegionSer" + + "verReportResponse\"O\n\031ReportRSFatalErrorR", + "equest\022\033\n\006server\030\001 \002(\0132\013.ServerName\022\025\n\re" + + "rror_message\030\002 \002(\t\"\034\n\032ReportRSFatalError" + + "Response\"6\n\037GetLastFlushedSequenceIdRequ" + + "est\022\023\n\013region_name\030\001 \002(\014\"D\n GetLastFlush" + + "edSequenceIdResponse\022 \n\030last_flushed_seq" + + "uence_id\030\001 \002(\004\"\322\002\n\025RegionStateTransition" + + "\022>\n\017transition_code\030\001 \002(\0162%.RegionStateT" + + "ransition.TransitionCode\022 \n\013region_info\030" + + "\002 \003(\0132\013.RegionInfo\022\024\n\014open_seq_num\030\003 \001(\004" + + "\"\300\001\n\016TransitionCode\022\n\n\006OPENED\020\000\022\017\n\013FAILE", + "D_OPEN\020\001\022\n\n\006CLOSED\020\002\022\022\n\016READY_TO_SPLIT\020\003" + + "\022\022\n\016READY_TO_MERGE\020\004\022\016\n\nSPLIT_PONR\020\005\022\016\n\n" + + "MERGE_PONR\020\006\022\t\n\005SPLIT\020\007\022\n\n\006MERGED\020\010\022\022\n\016S" + + "PLIT_REVERTED\020\t\022\022\n\016MERGE_REVERTED\020\n\"m\n\"R" + + "eportRegionStateTransitionRequest\022\033\n\006ser" + + "ver\030\001 \002(\0132\013.ServerName\022*\n\ntransition\030\002 \003" + + "(\0132\026.RegionStateTransition\"<\n#ReportRegi" + + "onStateTransitionResponse\022\025\n\rerror_messa" + + "ge\030\001 \001(\t2\326\003\n\031RegionServerStatusService\022P" + + "\n\023RegionServerStartup\022\033.RegionServerStar", + "tupRequest\032\034.RegionServerStartupResponse" + + "\022M\n\022RegionServerReport\022\032.RegionServerRep" + + "ortRequest\032\033.RegionServerReportResponse\022" + + "M\n\022ReportRSFatalError\022\032.ReportRSFatalErr" + + "orRequest\032\033.ReportRSFatalErrorResponse\022_" + + "\n\030GetLastFlushedSequenceId\022 .GetLastFlus" + + "hedSequenceIdRequest\032!.GetLastFlushedSeq" + + "uenceIdResponse\022h\n\033ReportRegionStateTran" + + "sition\022#.ReportRegionStateTransitionRequ" + + "est\032$.ReportRegionStateTransitionRespons", + "eBN\n*org.apache.hadoop.hbase.protobuf.ge" + + "neratedB\030RegionServerStatusProtosH\001\210\001\001\240\001" + + "\001" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -8451,7 +8665,7 @@ public final class RegionServerStatusProtos { internal_static_RegionServerStartupRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_RegionServerStartupRequest_descriptor, - new java.lang.String[] { "Port", "ServerStartCode", "ServerCurrentTime", }); + new java.lang.String[] { "Port", "ServerStartCode", "ServerCurrentTime", "UseThisHostnameInstead", }); internal_static_RegionServerStartupResponse_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_RegionServerStartupResponse_fieldAccessorTable = new diff --git a/hbase-protocol/src/main/protobuf/RegionServerStatus.proto b/hbase-protocol/src/main/protobuf/RegionServerStatus.proto index 75e5ae4..f292812 100644 --- a/hbase-protocol/src/main/protobuf/RegionServerStatus.proto +++ b/hbase-protocol/src/main/protobuf/RegionServerStatus.proto @@ -36,6 +36,9 @@ message RegionServerStartupRequest { /** Current time of the region server in ms */ required uint64 server_current_time = 3; + + /** hostname for region server, optional */ + optional string useThisHostnameInstead = 4; } message RegionServerStartupResponse { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java index 0e81461..a85dd34 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java @@ -302,8 +302,11 @@ public class MasterRpcServices extends RSRpcServices master.checkServiceStarted(); InetAddress ia = master.getRemoteInetAddress( request.getPort(), request.getServerStartCode()); - ServerName rs = master.serverManager.regionServerStartup(ia, request.getPort(), - request.getServerStartCode(), request.getServerCurrentTime()); + // if regionserver passed hostname to use, + // then use it instead of doing a reverse DNS lookup + ServerName rs = master.serverManager.regionServerStartup( + request.hasUseThisHostnameInstead() ? request.getUseThisHostnameInstead() :ia.getHostName(), + request.getPort(), request.getServerStartCode(), request.getServerCurrentTime()); // Send back some config info RegionServerStartupResponse.Builder resp = createConfigurationSubset(); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java index 69c29fd..bb9b9d4 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java @@ -20,6 +20,7 @@ package org.apache.hadoop.hbase.master; import java.io.IOException; import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -229,14 +230,14 @@ public class ServerManager { /** * Let the server manager know a new regionserver has come online - * @param ia The remote address + * @param hostname the hostname * @param port The remote port * @param serverStartcode * @param serverCurrentTime The current time of the region server in ms * @return The ServerName we know this server as. * @throws IOException */ - ServerName regionServerStartup(final InetAddress ia, final int port, + ServerName regionServerStartup(final String hostname, final int port, final long serverStartcode, long serverCurrentTime) throws IOException { // Test for case where we get a region startup message from a regionserver @@ -246,7 +247,12 @@ public class ServerManager { // is, reject the server and trigger its expiration. The next time it comes // in, it should have been removed from serverAddressToServerInfo and queued // for processing by ProcessServerShutdown. - ServerName sn = ServerName.valueOf(ia.getHostName(), port, serverStartcode); + + // try to do hostname to ip resolution + // UnknownHostException would be thrown if the hostname cannot be resolved + InetAddress.getByName(hostname); + ServerName sn = ServerName.valueOf(hostname, port, + serverStartcode); checkClockSkew(sn, serverCurrentTime); checkIsDead(sn, "STARTUP"); if (!checkAndRecordNewServer(sn, ServerLoad.EMPTY_SERVERLOAD)) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index fcb8c6f..243741c 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -27,6 +27,7 @@ import java.lang.reflect.Constructor; import java.net.BindException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -389,6 +390,16 @@ public class HRegionServer extends HasThread implements */ protected ServerName serverName; + /* + * hostname obtained from InetSocketAddress or, specified by hostname config + */ + protected String useThisHostnameInstead; + + // key to the config parameter of server hostname + // the specification of server hostname is optional. The hostname should be resolvable from + // both master and region server + final static String HOSTNAME_KEY = "hbase.regionserver.hostname"; + /** * This servers startcode. */ @@ -502,14 +513,15 @@ public class HRegionServer extends HasThread implements rpcServices = createRpcServices(); this.startcode = System.currentTimeMillis(); - String hostName = rpcServices.isa.getHostName(); - serverName = ServerName.valueOf(hostName, rpcServices.isa.getPort(), startcode); + useThisHostnameInstead = conf.get(HOSTNAME_KEY); + serverName = ServerName.valueOf(shouldUseThisHostnameInstead() ? useThisHostnameInstead : + rpcServices.isa.getHostName(), rpcServices.isa.getPort(), startcode); // login the zookeeper client principal (if using security) ZKUtil.loginClient(this.conf, "hbase.zookeeper.client.keytab.file", - "hbase.zookeeper.client.kerberos.principal", hostName); + "hbase.zookeeper.client.kerberos.principal", useThisHostnameInstead); // login the server principal (if using secure Hadoop) - login(userProvider, hostName); + login(userProvider, useThisHostnameInstead); regionServerAccounting = new RegionServerAccounting(); uncaughtExceptionHandler = new UncaughtExceptionHandler() { @@ -561,6 +573,13 @@ public class HRegionServer extends HasThread implements this.choreService = new ChoreService(getServerName().toString()); } + /* + * Returns true if configured hostname should be used + */ + protected boolean shouldUseThisHostnameInstead() { + return useThisHostnameInstead != null && !useThisHostnameInstead.isEmpty(); + } + protected void login(UserProvider user, String host) throws IOException { user.login("hbase.regionserver.keytab.file", "hbase.regionserver.kerberos.principal", host); @@ -1254,9 +1273,13 @@ public class HRegionServer extends HasThread implements String hostnameFromMasterPOV = e.getValue(); this.serverName = ServerName.valueOf(hostnameFromMasterPOV, rpcServices.isa.getPort(), this.startcode); - if (!hostnameFromMasterPOV.equals(rpcServices.isa.getHostName())) { - LOG.info("Master passed us a different hostname to use; was=" + - rpcServices.isa.getHostName() + ", but now=" + hostnameFromMasterPOV); + if (!hostnameFromMasterPOV.equals(rpcServices.isa.getHostName()) && + (shouldUseThisHostnameInstead() && + !hostnameFromMasterPOV.equals(useThisHostnameInstead))) { + String msg = "Master passed us a different hostname to use; was=" + + this.useThisHostnameInstead + ", but now=" + hostnameFromMasterPOV; + LOG.error(msg); + throw new IOException(msg); } continue; } @@ -2137,6 +2160,9 @@ public class HRegionServer extends HasThread implements long now = EnvironmentEdgeManager.currentTime(); int port = rpcServices.isa.getPort(); RegionServerStartupRequest.Builder request = RegionServerStartupRequest.newBuilder(); + if (shouldUseThisHostnameInstead()) { + request.setUseThisHostnameInstead(useThisHostnameInstead); + } request.setPort(port); request.setServerStartCode(this.startcode); request.setServerCurrentTime(now); @@ -2149,6 +2175,9 @@ public class HRegionServer extends HasThread implements throw ioe; } else if (ioe instanceof ServerNotRunningYetException) { LOG.debug("Master is not running yet"); + } else if (ioe instanceof UnknownHostException) { + LOG.fatal("Master couldn't resolve the hostname we sent: " + this.useThisHostnameInstead); + throw ioe; } else { LOG.warn("error telling master we are up", se); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java index 3bdb528..71c2ed8 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java @@ -764,7 +764,7 @@ public class RSRpcServices implements HBaseRPCErrorHandler, throw new IllegalArgumentException(e); } // Server to handle client requests. - String hostname = getHostname(rs.conf); + String hostname = getHostnameWithoutDNSResolutionIfPossible(rs.conf); boolean mode = rs.conf.getBoolean(HConstants.CLUSTER_DISTRIBUTED, HConstants.DEFAULT_CLUSTER_DISTRIBUTED); @@ -805,11 +805,21 @@ public class RSRpcServices implements HBaseRPCErrorHandler, rs.setName(name); } + public String getHostnameWithoutDNSResolutionIfPossible(Configuration conf) + throws UnknownHostException { + String hostname = conf.get(HRegionServer.HOSTNAME_KEY); + if (hostname == null || hostname.length() == 0) { + return getHostname(conf); + } + LOG.info("hostname is " + hostname); + return hostname; + } + public static String getHostname(Configuration conf) throws UnknownHostException { - return conf.get("hbase.regionserver.ipc.address", + return conf.get("hbase.regionserver.ipc.address", Strings.domainNamePointerToHostName(DNS.getDefaultHost( - conf.get("hbase.regionserver.dns.interface", "default"), - conf.get("hbase.regionserver.dns.nameserver", "default")))); + conf.get("hbase.regionserver.dns.interface", "default"), + conf.get("hbase.regionserver.dns.nameserver", "default")))); } RegionScanner getScanner(long scannerId) { diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestClockSkewDetection.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestClockSkewDetection.java index dd733ad..9993fe9 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestClockSkewDetection.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestClockSkewDetection.java @@ -103,7 +103,7 @@ public class TestClockSkewDetection { LOG.debug("regionServerStartup 1"); InetAddress ia1 = InetAddress.getLocalHost(); - sm.regionServerStartup(ia1, 1234, -1, System.currentTimeMillis()); + sm.regionServerStartup(ia1.getHostName(), 1234, -1, System.currentTimeMillis()); final Configuration c = HBaseConfiguration.create(); long maxSkew = c.getLong("hbase.master.maxclockskew", 30000); @@ -114,7 +114,7 @@ public class TestClockSkewDetection { LOG.debug("Test: Master Time > Region Server Time"); LOG.debug("regionServerStartup 2"); InetAddress ia2 = InetAddress.getLocalHost(); - sm.regionServerStartup(ia2, 1235, -1, System.currentTimeMillis() - maxSkew * 2); + sm.regionServerStartup(ia2.getHostName(), 1235, -1, System.currentTimeMillis() - maxSkew * 2); fail("HMaster should have thrown a ClockOutOfSyncException but didn't."); } catch(ClockOutOfSyncException e) { //we want an exception @@ -126,7 +126,7 @@ public class TestClockSkewDetection { LOG.debug("Test: Master Time < Region Server Time"); LOG.debug("regionServerStartup 3"); InetAddress ia3 = InetAddress.getLocalHost(); - sm.regionServerStartup(ia3, 1236, -1, System.currentTimeMillis() + maxSkew * 2); + sm.regionServerStartup(ia3.getHostName(), 1236, -1, System.currentTimeMillis() + maxSkew * 2); fail("HMaster should have thrown a ClockOutOfSyncException but didn't."); } catch (ClockOutOfSyncException e) { // we want an exception @@ -136,12 +136,12 @@ public class TestClockSkewDetection { // make sure values above warning threshold but below max threshold don't kill LOG.debug("regionServerStartup 4"); InetAddress ia4 = InetAddress.getLocalHost(); - sm.regionServerStartup(ia4, 1237, -1, System.currentTimeMillis() - warningSkew * 2); + sm.regionServerStartup(ia4.getHostName(), 1237, -1, System.currentTimeMillis() - warningSkew * 2); // make sure values above warning threshold but below max threshold don't kill LOG.debug("regionServerStartup 5"); InetAddress ia5 = InetAddress.getLocalHost(); - sm.regionServerStartup(ia5, 1238, -1, System.currentTimeMillis() + warningSkew * 2); + sm.regionServerStartup(ia5.getHostName(), 1238, -1, System.currentTimeMillis() + warningSkew * 2); }