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.assertFalse;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024
025import java.net.InetAddress;
026import java.net.NetworkInterface;
027import java.util.ArrayList;
028import java.util.Enumeration;
029import java.util.List;
030import java.util.Locale;
031import org.apache.hadoop.conf.Configuration;
032import org.apache.hadoop.hbase.HBaseClassTestRule;
033import org.apache.hadoop.hbase.HBaseConfiguration;
034import org.apache.hadoop.hbase.HBaseTestingUtil;
035import org.apache.hadoop.hbase.StartTestingClusterOption;
036import org.apache.hadoop.hbase.testclassification.MediumTests;
037import org.apache.hadoop.hbase.testclassification.RegionServerTests;
038import org.apache.hadoop.hbase.util.DNS;
039import org.apache.hadoop.hbase.zookeeper.ZKUtil;
040import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
041import org.junit.After;
042import org.junit.Before;
043import org.junit.ClassRule;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048
049/**
050 * Tests for the hostname specification by region server
051 */
052@Category({ RegionServerTests.class, MediumTests.class })
053public class TestRegionServerHostname {
054
055  @ClassRule
056  public static final HBaseClassTestRule CLASS_RULE =
057    HBaseClassTestRule.forClass(TestRegionServerHostname.class);
058
059  private static final Logger LOG = LoggerFactory.getLogger(TestRegionServerHostname.class);
060
061  private HBaseTestingUtil TEST_UTIL;
062
063  private static final int NUM_MASTERS = 1;
064  private static final int NUM_RS = 1;
065
066  @Before
067  public void setup() {
068    Configuration conf = HBaseConfiguration.create();
069    TEST_UTIL = new HBaseTestingUtil(conf);
070  }
071
072  @After
073  public void teardown() throws Exception {
074    TEST_UTIL.shutdownMiniCluster();
075  }
076
077  @Test
078  public void testInvalidRegionServerHostnameAbortsServer() throws Exception {
079    String invalidHostname = "hostAddr.invalid";
080    TEST_UTIL.getConfiguration().set(DNS.UNSAFE_RS_HOSTNAME_KEY, invalidHostname);
081    HRegionServer hrs = null;
082    try {
083      hrs = new HRegionServer(TEST_UTIL.getConfiguration());
084    } catch (IllegalArgumentException iae) {
085      assertTrue(iae.getMessage(), 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    for (NetworkInterface ni : getValidNetworkInterfaces()) {
094      Enumeration<InetAddress> addrList = ni.getInetAddresses();
095      // iterate through host addresses and use each as hostname
096      while (addrList.hasMoreElements()) {
097        InetAddress addr = addrList.nextElement();
098        if (
099          addr.isLoopbackAddress() || addr.isLinkLocalAddress() || addr.isMulticastAddress()
100            || !addr.isSiteLocalAddress()
101        ) {
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.UNSAFE_RS_HOSTNAME_KEY, hostName);
109        StartTestingClusterOption option = StartTestingClusterOption.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          assertEquals(NUM_RS, servers.size());
116          for (String server : servers) {
117            assertTrue("From zookeeper: " + server + " hostname: " + hostName,
118              server.startsWith(hostName.toLowerCase(Locale.ROOT) + ","));
119          }
120          zkw.close();
121        } finally {
122          TEST_UTIL.shutdownMiniCluster();
123        }
124      }
125    }
126  }
127
128  @Test
129  public void testDeprecatedConfigs() throws Exception {
130    Configuration conf = TEST_UTIL.getConfiguration();
131    new HRegionServer(conf);
132    conf.setBoolean(HRegionServer.RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, false);
133    assertFalse(
134      conf.getBoolean(HRegionServer.UNSAFE_RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, true));
135    conf.setBoolean(HRegionServer.RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, true);
136    assertTrue(
137      conf.getBoolean(HRegionServer.UNSAFE_RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, false));
138    conf.setBoolean(HRegionServer.UNSAFE_RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, true);
139    assertTrue(conf.getBoolean(HRegionServer.RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, false));
140    conf.setBoolean(HRegionServer.UNSAFE_RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, false);
141    assertFalse(conf.getBoolean(HRegionServer.RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, true));
142
143    conf.setBoolean(DNS.RS_HOSTNAME_KEY, false);
144    assertFalse(conf.getBoolean(DNS.UNSAFE_RS_HOSTNAME_KEY, true));
145    conf.setBoolean(DNS.RS_HOSTNAME_KEY, true);
146    assertTrue(conf.getBoolean(DNS.UNSAFE_RS_HOSTNAME_KEY, false));
147    conf.setBoolean(DNS.UNSAFE_RS_HOSTNAME_KEY, true);
148    assertTrue(conf.getBoolean(DNS.RS_HOSTNAME_KEY, false));
149    conf.setBoolean(DNS.UNSAFE_RS_HOSTNAME_KEY, false);
150    assertFalse(conf.getBoolean(DNS.RS_HOSTNAME_KEY, true));
151  }
152
153  @Test
154  public void testConflictRegionServerHostnameConfigurationsAbortServer() throws Exception {
155    Enumeration<NetworkInterface> netInterfaceList = NetworkInterface.getNetworkInterfaces();
156    while (netInterfaceList.hasMoreElements()) {
157      NetworkInterface ni = netInterfaceList.nextElement();
158      Enumeration<InetAddress> addrList = ni.getInetAddresses();
159      // iterate through host addresses and use each as hostname
160      while (addrList.hasMoreElements()) {
161        InetAddress addr = addrList.nextElement();
162        if (addr.isLoopbackAddress() || addr.isLinkLocalAddress() || addr.isMulticastAddress()) {
163          continue;
164        }
165        String hostName = addr.getHostName();
166        LOG.info("Found " + hostName + " on " + ni);
167
168        TEST_UTIL.getConfiguration().set(DNS.MASTER_HOSTNAME_KEY, hostName);
169        // "hbase.unsafe.regionserver.hostname" and
170        // "hbase.unsafe.regionserver.hostname.disable.master.reversedns"
171        // are mutually exclusive. Exception should be thrown if both are used.
172        TEST_UTIL.getConfiguration().set(DNS.UNSAFE_RS_HOSTNAME_KEY, hostName);
173        TEST_UTIL.getConfiguration()
174          .setBoolean(HRegionServer.UNSAFE_RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, true);
175        try {
176          StartTestingClusterOption option = StartTestingClusterOption.builder()
177            .numMasters(NUM_MASTERS).numRegionServers(NUM_RS).numDataNodes(NUM_RS).build();
178          TEST_UTIL.startMiniCluster(option);
179        } catch (Exception e) {
180          Throwable t1 = e.getCause();
181          Throwable t2 = t1.getCause();
182          assertTrue(t1.getMessage() + " - " + t2.getMessage(),
183            t2.getMessage().contains(HRegionServer.UNSAFE_RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY
184              + " and " + DNS.UNSAFE_RS_HOSTNAME_KEY + " are mutually exclusive"));
185          return;
186        } finally {
187          TEST_UTIL.shutdownMiniCluster();
188        }
189        assertTrue("Failed to validate against conflict hostname configurations", false);
190      }
191    }
192  }
193
194  @Test
195  public void testRegionServerHostnameReportedToMaster() throws Exception {
196    TEST_UTIL.getConfiguration()
197      .setBoolean(HRegionServer.UNSAFE_RS_HOSTNAME_DISABLE_MASTER_REVERSEDNS_KEY, true);
198    StartTestingClusterOption option = StartTestingClusterOption.builder().numMasters(NUM_MASTERS)
199      .numRegionServers(NUM_RS).numDataNodes(NUM_RS).build();
200    TEST_UTIL.startMiniCluster(option);
201    int expectedRS = NUM_RS;
202    try (ZKWatcher zkw = TEST_UTIL.getZooKeeperWatcher()) {
203      List<String> servers = ZKUtil.listChildrenNoWatch(zkw, zkw.getZNodePaths().rsZNode);
204      assertEquals(expectedRS, servers.size());
205    }
206  }
207
208  private boolean ignoreNetworkInterface(NetworkInterface networkInterface) throws Exception {
209    return networkInterface == null || networkInterface.isLoopback() || networkInterface.isVirtual()
210      || !networkInterface.isUp();
211  }
212
213  private List<NetworkInterface> getValidNetworkInterfaces() throws Exception {
214    List<NetworkInterface> validNetworkInterfaces = new ArrayList<>();
215    Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
216    while (interfaces.hasMoreElements()) {
217      NetworkInterface networkInterface = interfaces.nextElement();
218      if (ignoreNetworkInterface(networkInterface)) {
219        continue;
220      }
221      validNetworkInterfaces.add(networkInterface);
222    }
223    return validNetworkInterfaces;
224  }
225}