001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertNull;
022import static org.junit.Assert.assertTrue;
023
024import java.net.InetAddress;
025import java.net.NetworkInterface;
026import java.util.Enumeration;
027import java.util.List;
028import java.util.Locale;
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.hbase.HBaseClassTestRule;
031import org.apache.hadoop.hbase.HBaseConfiguration;
032import org.apache.hadoop.hbase.HBaseTestingUtility;
033import org.apache.hadoop.hbase.StartMiniClusterOption;
034import org.apache.hadoop.hbase.master.LoadBalancer;
035import org.apache.hadoop.hbase.testclassification.MediumTests;
036import org.apache.hadoop.hbase.testclassification.RegionServerTests;
037import org.apache.hadoop.hbase.util.DNS;
038import org.apache.hadoop.hbase.zookeeper.ZKUtil;
039import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
040import org.junit.After;
041import org.junit.Before;
042import org.junit.ClassRule;
043import org.junit.Test;
044import org.junit.experimental.categories.Category;
045import org.slf4j.Logger;
046import org.slf4j.LoggerFactory;
047
048/**
049 * Tests for the hostname specification by region server
050 */
051@Category({RegionServerTests.class, MediumTests.class})
052public class TestRegionServerHostname {
053
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE =
056      HBaseClassTestRule.forClass(TestRegionServerHostname.class);
057
058  private static final Logger LOG = LoggerFactory.getLogger(TestRegionServerHostname.class);
059
060  private HBaseTestingUtility TEST_UTIL;
061
062  private static final int NUM_MASTERS = 1;
063  private static final int NUM_RS = 1;
064
065  @Before
066  public void setup() {
067    Configuration conf = HBaseConfiguration.create();
068    TEST_UTIL = new HBaseTestingUtility(conf);
069  }
070
071  @After
072  public void teardown() throws Exception {
073    TEST_UTIL.shutdownMiniCluster();
074  }
075
076  @Test
077  public void testInvalidRegionServerHostnameAbortsServer() throws Exception {
078    String invalidHostname = "hostAddr.invalid";
079    TEST_UTIL.getConfiguration().set(DNS.RS_HOSTNAME_KEY, invalidHostname);
080    HRegionServer hrs = null;
081    try {
082      hrs = new HRegionServer(TEST_UTIL.getConfiguration());
083    } catch (IllegalArgumentException iae) {
084      assertTrue(iae.getMessage(),
085        iae.getMessage().contains("Failed resolve of " + invalidHostname) ||
086        iae.getMessage().contains("Problem binding to " + invalidHostname));
087    }
088    assertNull("Failed to validate against invalid hostname", hrs);
089  }
090
091  @Test
092  public void testRegionServerHostname() throws Exception {
093    Enumeration<NetworkInterface> netInterfaceList = NetworkInterface.getNetworkInterfaces();
094    while (netInterfaceList.hasMoreElements()) {
095      NetworkInterface ni = netInterfaceList.nextElement();
096      Enumeration<InetAddress> addrList = ni.getInetAddresses();
097      // iterate through host addresses and use each as hostname
098      while (addrList.hasMoreElements()) {
099        InetAddress addr = addrList.nextElement();
100        if (addr.isLoopbackAddress() || addr.isLinkLocalAddress() || addr.isMulticastAddress() ||
101            !addr.isSiteLocalAddress()) {
102          continue;
103        }
104        String hostName = addr.getHostName();
105        LOG.info("Found " + hostName + " on " + ni + ", addr=" + addr);
106
107        TEST_UTIL.getConfiguration().set(DNS.MASTER_HOSTNAME_KEY, hostName);
108        TEST_UTIL.getConfiguration().set(DNS.RS_HOSTNAME_KEY, hostName);
109        StartMiniClusterOption option = StartMiniClusterOption.builder()
110            .numMasters(NUM_MASTERS).numRegionServers(NUM_RS).numDataNodes(NUM_RS).build();
111        TEST_UTIL.startMiniCluster(option);
112        try {
113          ZKWatcher zkw = TEST_UTIL.getZooKeeperWatcher();
114          List<String> servers = ZKUtil.listChildrenNoWatch(zkw, zkw.getZNodePaths().rsZNode);
115          // there would be NUM_RS+1 children - one for the master
116          assertTrue(servers.size() ==
117            NUM_RS + (LoadBalancer.isTablesOnMaster(TEST_UTIL.getConfiguration())? 1: 0));
118          for (String server : servers) {
119            assertTrue("From zookeeper: " + server + " hostname: " + hostName,
120              server.startsWith(hostName.toLowerCase(Locale.ROOT)+","));
121          }
122          zkw.close();
123        } finally {
124          TEST_UTIL.shutdownMiniCluster();
125        }
126      }
127    }
128  }
129
130  @Test
131  public void testConflictRegionServerHostnameConfigurationsAbortServer() throws Exception {
132    Enumeration<NetworkInterface> netInterfaceList = NetworkInterface.getNetworkInterfaces();
133    while (netInterfaceList.hasMoreElements()) {
134      NetworkInterface ni = netInterfaceList.nextElement();
135      Enumeration<InetAddress> addrList = ni.getInetAddresses();
136      // iterate through host addresses and use each as hostname
137      while (addrList.hasMoreElements()) {
138        InetAddress addr = addrList.nextElement();
139        if (addr.isLoopbackAddress() || addr.isLinkLocalAddress() || addr.isMulticastAddress()) {
140          continue;
141        }
142        String hostName = addr.getHostName();
143        LOG.info("Found " + hostName + " on " + ni);
144
145        TEST_UTIL.getConfiguration().set(DNS.MASTER_HOSTNAME_KEY, hostName);
146        // "hbase.regionserver.hostname" and "hbase.regionserver.hostname.disable.master.reversedns"
147        // are mutually exclusive. Exception should be thrown if both are used.
148        TEST_UTIL.getConfiguration().set(DNS.RS_HOSTNAME_KEY, hostName);
149        TEST_UTIL.getConfiguration().setBoolean(HRegionServer.RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, true);
150        try {
151          StartMiniClusterOption option = StartMiniClusterOption.builder()
152              .numMasters(NUM_MASTERS).numRegionServers(NUM_RS).numDataNodes(NUM_RS).build();
153          TEST_UTIL.startMiniCluster(option);
154        } catch (Exception e) {
155          Throwable t1 = e.getCause();
156          Throwable t2 = t1.getCause();
157          assertTrue(t1.getMessage()+" - "+t2.getMessage(), t2.getMessage().contains(
158            HRegionServer.RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY + " and " +
159                DNS.RS_HOSTNAME_KEY + " are mutually exclusive"));
160          return;
161        } finally {
162          TEST_UTIL.shutdownMiniCluster();
163        }
164        assertTrue("Failed to validate against conflict hostname configurations", false);
165      }
166    }
167  }
168
169  @Test
170  public void testRegionServerHostnameReportedToMaster() throws Exception {
171    TEST_UTIL.getConfiguration().setBoolean(HRegionServer.RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY,
172    true);
173    StartMiniClusterOption option = StartMiniClusterOption.builder()
174        .numMasters(NUM_MASTERS).numRegionServers(NUM_RS).numDataNodes(NUM_RS).build();
175    TEST_UTIL.startMiniCluster(option);
176    boolean tablesOnMaster = LoadBalancer.isTablesOnMaster(TEST_UTIL.getConfiguration());
177    int expectedRS = NUM_RS + (tablesOnMaster? 1: 0);
178    try (ZKWatcher zkw = TEST_UTIL.getZooKeeperWatcher()) {
179      List<String> servers = ZKUtil.listChildrenNoWatch(zkw, zkw.getZNodePaths().rsZNode);
180      assertEquals(expectedRS, servers.size());
181    }
182  }
183}