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.client;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023import static org.junit.Assert.fail;
024
025import java.io.IOException;
026import java.util.ArrayList;
027import java.util.EnumSet;
028import java.util.HashMap;
029import java.util.List;
030import java.util.Random;
031import java.util.concurrent.atomic.AtomicInteger;
032import java.util.stream.Collectors;
033import org.apache.hadoop.conf.Configuration;
034import org.apache.hadoop.hbase.ClusterMetrics.Option;
035import org.apache.hadoop.hbase.HBaseClassTestRule;
036import org.apache.hadoop.hbase.HBaseTestingUtility;
037import org.apache.hadoop.hbase.HColumnDescriptor;
038import org.apache.hadoop.hbase.HConstants;
039import org.apache.hadoop.hbase.HRegionLocation;
040import org.apache.hadoop.hbase.HTableDescriptor;
041import org.apache.hadoop.hbase.MiniHBaseCluster;
042import org.apache.hadoop.hbase.ServerName;
043import org.apache.hadoop.hbase.TableExistsException;
044import org.apache.hadoop.hbase.TableName;
045import org.apache.hadoop.hbase.TableNotDisabledException;
046import org.apache.hadoop.hbase.TableNotEnabledException;
047import org.apache.hadoop.hbase.TableNotFoundException;
048import org.apache.hadoop.hbase.UnknownRegionException;
049import org.apache.hadoop.hbase.ZooKeeperConnectionException;
050import org.apache.hadoop.hbase.constraint.ConstraintException;
051import org.apache.hadoop.hbase.ipc.HBaseRpcController;
052import org.apache.hadoop.hbase.master.HMaster;
053import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
054import org.apache.hadoop.hbase.regionserver.HRegion;
055import org.apache.hadoop.hbase.regionserver.HRegionServer;
056import org.apache.hadoop.hbase.regionserver.HStore;
057import org.apache.hadoop.hbase.testclassification.ClientTests;
058import org.apache.hadoop.hbase.testclassification.LargeTests;
059import org.apache.hadoop.hbase.util.Bytes;
060import org.apache.hadoop.hbase.util.Pair;
061import org.apache.hadoop.hbase.wal.AbstractFSWALProvider;
062import org.junit.After;
063import org.junit.AfterClass;
064import org.junit.Assert;
065import org.junit.Before;
066import org.junit.BeforeClass;
067import org.junit.ClassRule;
068import org.junit.Rule;
069import org.junit.Test;
070import org.junit.experimental.categories.Category;
071import org.junit.rules.TestName;
072import org.slf4j.Logger;
073import org.slf4j.LoggerFactory;
074
075import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
076
077/**
078 * Class to test HBaseAdmin.
079 * Spins up the minicluster once at test start and then takes it down afterward.
080 * Add any testing of HBaseAdmin functionality here.
081 */
082@Category({LargeTests.class, ClientTests.class})
083public class TestAdmin2 {
084
085  @ClassRule
086  public static final HBaseClassTestRule CLASS_RULE =
087      HBaseClassTestRule.forClass(TestAdmin2.class);
088
089  private static final Logger LOG = LoggerFactory.getLogger(TestAdmin2.class);
090  private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
091  private Admin admin;
092
093  @Rule
094  public TestName name = new TestName();
095
096  @BeforeClass
097  public static void setUpBeforeClass() throws Exception {
098    TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
099    TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250);
100    TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 6);
101    TEST_UTIL.getConfiguration().setInt(HConstants.REGION_SERVER_HIGH_PRIORITY_HANDLER_COUNT, 30);
102    TEST_UTIL.getConfiguration().setInt(HConstants.REGION_SERVER_HANDLER_COUNT, 30);
103    TEST_UTIL.getConfiguration().setBoolean("hbase.master.enabletable.roundrobin", true);
104    TEST_UTIL.startMiniCluster(3);
105  }
106
107  @AfterClass
108  public static void tearDownAfterClass() throws Exception {
109    TEST_UTIL.shutdownMiniCluster();
110  }
111
112  @Before
113  public void setUp() throws Exception {
114    this.admin = TEST_UTIL.getHBaseAdmin();
115  }
116
117  @After
118  public void tearDown() throws Exception {
119    for (HTableDescriptor htd : this.admin.listTables()) {
120      TEST_UTIL.deleteTable(htd.getTableName());
121    }
122  }
123
124  @Test
125  public void testCreateBadTables() throws IOException {
126    String msg = null;
127    try {
128      this.admin.createTable(new HTableDescriptor(TableName.META_TABLE_NAME));
129    } catch(TableExistsException e) {
130      msg = e.toString();
131    }
132    assertTrue("Unexcepted exception message " + msg, msg != null &&
133      msg.startsWith(TableExistsException.class.getName()) &&
134      msg.contains(TableName.META_TABLE_NAME.getNameAsString()));
135
136    // Now try and do concurrent creation with a bunch of threads.
137    final HTableDescriptor threadDesc = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
138    threadDesc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
139    int count = 10;
140    Thread [] threads = new Thread [count];
141    final AtomicInteger successes = new AtomicInteger(0);
142    final AtomicInteger failures = new AtomicInteger(0);
143    final Admin localAdmin = this.admin;
144    for (int i = 0; i < count; i++) {
145      threads[i] = new Thread(Integer.toString(i)) {
146        @Override
147        public void run() {
148          try {
149            localAdmin.createTable(threadDesc);
150            successes.incrementAndGet();
151          } catch (TableExistsException e) {
152            failures.incrementAndGet();
153          } catch (IOException e) {
154            throw new RuntimeException("Failed threaded create" + getName(), e);
155          }
156        }
157      };
158    }
159    for (int i = 0; i < count; i++) {
160      threads[i].start();
161    }
162    for (int i = 0; i < count; i++) {
163      while(threads[i].isAlive()) {
164        try {
165          Thread.sleep(100);
166        } catch (InterruptedException e) {
167          // continue
168        }
169      }
170    }
171    // All threads are now dead.  Count up how many tables were created and
172    // how many failed w/ appropriate exception.
173    assertEquals(1, successes.get());
174    assertEquals(count - 1, failures.get());
175  }
176
177  /**
178   * Test for hadoop-1581 'HBASE: Unopenable tablename bug'.
179   * @throws Exception
180   */
181  @Test
182  public void testTableNameClash() throws Exception {
183    final String name = this.name.getMethodName();
184    HTableDescriptor htd1 = new HTableDescriptor(TableName.valueOf(name + "SOMEUPPERCASE"));
185    HTableDescriptor htd2 = new HTableDescriptor(TableName.valueOf(name));
186    htd1.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
187    htd2.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
188    admin.createTable(htd1);
189    admin.createTable(htd2);
190    // Before fix, below would fail throwing a NoServerForRegionException.
191    TEST_UTIL.getConnection().getTable(htd2.getTableName()).close();
192  }
193
194  /***
195   * HMaster.createTable used to be kind of synchronous call
196   * Thus creating of table with lots of regions can cause RPC timeout
197   * After the fix to make createTable truly async, RPC timeout shouldn't be an
198   * issue anymore
199   * @throws Exception
200   */
201  @Test
202  public void testCreateTableRPCTimeOut() throws Exception {
203    final String name = this.name.getMethodName();
204    int oldTimeout = TEST_UTIL.getConfiguration().
205      getInt(HConstants.HBASE_RPC_TIMEOUT_KEY, HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
206    TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1500);
207    try {
208      int expectedRegions = 100;
209      // Use 80 bit numbers to make sure we aren't limited
210      byte [] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
211      byte [] endKey =   { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
212      Admin hbaseadmin = TEST_UTIL.getHBaseAdmin();
213      HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name));
214      htd.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
215      hbaseadmin.createTable(htd, startKey, endKey, expectedRegions);
216    } finally {
217      TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, oldTimeout);
218    }
219  }
220
221  /**
222   * Test read only tables
223   * @throws Exception
224   */
225  @Test
226  public void testReadOnlyTable() throws Exception {
227    final TableName name = TableName.valueOf(this.name.getMethodName());
228    Table table = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
229    byte[] value = Bytes.toBytes("somedata");
230    // This used to use an empty row... That must have been a bug
231    Put put = new Put(value);
232    put.addColumn(HConstants.CATALOG_FAMILY, HConstants.CATALOG_FAMILY, value);
233    table.put(put);
234    table.close();
235  }
236
237  /**
238   * Test that user table names can contain '-' and '.' so long as they do not
239   * start with same. HBASE-771
240   * @throws IOException
241   */
242  @Test
243  public void testTableNames() throws IOException {
244    byte[][] illegalNames = new byte[][] {
245        Bytes.toBytes("-bad"),
246        Bytes.toBytes(".bad")
247    };
248    for (byte[] illegalName : illegalNames) {
249      try {
250        new HTableDescriptor(TableName.valueOf(illegalName));
251        throw new IOException("Did not detect '" +
252            Bytes.toString(illegalName) + "' as an illegal user table name");
253      } catch (IllegalArgumentException e) {
254        // expected
255      }
256    }
257    byte[] legalName = Bytes.toBytes("g-oo.d");
258    try {
259      new HTableDescriptor(TableName.valueOf(legalName));
260    } catch (IllegalArgumentException e) {
261      throw new IOException("Legal user table name: '" +
262        Bytes.toString(legalName) + "' caused IllegalArgumentException: " +
263        e.getMessage());
264    }
265  }
266
267  /**
268   * For HADOOP-2579
269   * @throws IOException
270   */
271  @Test (expected=TableExistsException.class)
272  public void testTableExistsExceptionWithATable() throws IOException {
273    final TableName name = TableName.valueOf(this.name.getMethodName());
274    TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY).close();
275    TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
276  }
277
278  /**
279   * Can't disable a table if the table isn't in enabled state
280   * @throws IOException
281   */
282  @Test (expected=TableNotEnabledException.class)
283  public void testTableNotEnabledExceptionWithATable() throws IOException {
284    final TableName name = TableName.valueOf(this.name.getMethodName());
285    TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY).close();
286    this.admin.disableTable(name);
287    this.admin.disableTable(name);
288  }
289
290  /**
291   * Can't enable a table if the table isn't in disabled state
292   * @throws IOException
293   */
294  @Test (expected=TableNotDisabledException.class)
295  public void testTableNotDisabledExceptionWithATable() throws IOException {
296    final TableName name = TableName.valueOf(this.name.getMethodName());
297    Table t = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
298    try {
299    this.admin.enableTable(name);
300    }finally {
301       t.close();
302    }
303  }
304
305  /**
306   * For HADOOP-2579
307   * @throws IOException
308   */
309  @Test (expected=TableNotFoundException.class)
310  public void testTableNotFoundExceptionWithoutAnyTables() throws IOException {
311    TableName tableName = TableName
312        .valueOf("testTableNotFoundExceptionWithoutAnyTables");
313    Table ht = TEST_UTIL.getConnection().getTable(tableName);
314    ht.get(new Get(Bytes.toBytes("e")));
315  }
316
317  @Test
318  public void testShouldUnassignTheRegion() throws Exception {
319    final TableName tableName = TableName.valueOf(name.getMethodName());
320    createTableWithDefaultConf(tableName);
321
322    RegionInfo info = null;
323    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(tableName);
324    List<RegionInfo> onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices());
325    for (RegionInfo regionInfo : onlineRegions) {
326      if (!regionInfo.getTable().isSystemTable()) {
327        info = regionInfo;
328        admin.unassign(regionInfo.getRegionName(), true);
329      }
330    }
331    boolean isInList = ProtobufUtil.getOnlineRegions(
332      rs.getRSRpcServices()).contains(info);
333    long timeout = System.currentTimeMillis() + 10000;
334    while ((System.currentTimeMillis() < timeout) && (isInList)) {
335      Thread.sleep(100);
336      isInList = ProtobufUtil.getOnlineRegions(
337        rs.getRSRpcServices()).contains(info);
338    }
339
340    assertFalse("The region should not be present in online regions list.",
341      isInList);
342  }
343
344  @Test
345  public void testCloseRegionIfInvalidRegionNameIsPassed() throws Exception {
346    final String name = this.name.getMethodName();
347    byte[] tableName = Bytes.toBytes(name);
348    createTableWithDefaultConf(tableName);
349
350    RegionInfo info = null;
351    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TableName.valueOf(tableName));
352    List<RegionInfo> onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices());
353    for (RegionInfo regionInfo : onlineRegions) {
354      if (!regionInfo.isMetaRegion()) {
355        if (regionInfo.getRegionNameAsString().contains(name)) {
356          info = regionInfo;
357          try {
358            admin.unassign(Bytes.toBytes("sample"), true);
359          } catch (UnknownRegionException nsre) {
360            // expected, ignore it
361          }
362        }
363      }
364    }
365    onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices());
366    assertTrue("The region should be present in online regions list.",
367        onlineRegions.contains(info));
368  }
369
370  @Test
371  public void testCloseRegionThatFetchesTheHRIFromMeta() throws Exception {
372    final TableName tableName = TableName.valueOf(name.getMethodName());
373    createTableWithDefaultConf(tableName);
374
375    RegionInfo info = null;
376    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(tableName);
377    List<RegionInfo> onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices());
378    for (RegionInfo regionInfo : onlineRegions) {
379      if (!regionInfo.isMetaRegion()) {
380        if (regionInfo.getRegionNameAsString().contains("TestHBACloseRegion2")) {
381          info = regionInfo;
382          admin.unassign(regionInfo.getRegionName(), true);
383        }
384      }
385    }
386
387    boolean isInList = ProtobufUtil.getOnlineRegions(
388      rs.getRSRpcServices()).contains(info);
389    long timeout = System.currentTimeMillis() + 10000;
390    while ((System.currentTimeMillis() < timeout) && (isInList)) {
391      Thread.sleep(100);
392      isInList = ProtobufUtil.getOnlineRegions(
393        rs.getRSRpcServices()).contains(info);
394    }
395
396    assertFalse("The region should not be present in online regions list.",
397      isInList);
398  }
399
400  private HBaseAdmin createTable(TableName tableName) throws IOException {
401    HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
402
403    HTableDescriptor htd = new HTableDescriptor(tableName);
404    HColumnDescriptor hcd = new HColumnDescriptor("value");
405
406    htd.addFamily(hcd);
407    admin.createTable(htd, null);
408    return admin;
409  }
410
411  private void createTableWithDefaultConf(byte[] TABLENAME) throws IOException {
412    createTableWithDefaultConf(TableName.valueOf(TABLENAME));
413  }
414
415  private void createTableWithDefaultConf(TableName TABLENAME) throws IOException {
416    HTableDescriptor htd = new HTableDescriptor(TABLENAME);
417    HColumnDescriptor hcd = new HColumnDescriptor("value");
418    htd.addFamily(hcd);
419
420    admin.createTable(htd, null);
421  }
422
423  /**
424   * For HBASE-2556
425   * @throws IOException
426   */
427  @Test
428  public void testGetTableRegions() throws IOException {
429    final TableName tableName = TableName.valueOf(name.getMethodName());
430
431    int expectedRegions = 10;
432
433    // Use 80 bit numbers to make sure we aren't limited
434    byte [] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
435    byte [] endKey =   { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
436
437
438    HTableDescriptor desc = new HTableDescriptor(tableName);
439    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
440    admin.createTable(desc, startKey, endKey, expectedRegions);
441
442    List<RegionInfo> RegionInfos = admin.getRegions(tableName);
443
444    assertEquals("Tried to create " + expectedRegions + " regions " +
445        "but only found " + RegionInfos.size(),
446        expectedRegions, RegionInfos.size());
447 }
448
449  @Test
450  public void testMoveToPreviouslyAssignedRS() throws IOException, InterruptedException {
451    MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
452    HMaster master = cluster.getMaster();
453    final TableName tableName = TableName.valueOf(name.getMethodName());
454    Admin localAdmin = createTable(tableName);
455    List<RegionInfo> tableRegions = localAdmin.getRegions(tableName);
456    RegionInfo hri = tableRegions.get(0);
457    AssignmentManager am = master.getAssignmentManager();
458    ServerName server = am.getRegionStates().getRegionServerOfRegion(hri);
459    localAdmin.move(hri.getEncodedNameAsBytes(), Bytes.toBytes(server.getServerName()));
460    assertEquals("Current region server and region server before move should be same.", server,
461      am.getRegionStates().getRegionServerOfRegion(hri));
462  }
463
464  @Test
465  public void testWALRollWriting() throws Exception {
466    setUpforLogRolling();
467    String className = this.getClass().getName();
468    StringBuilder v = new StringBuilder(className);
469    while (v.length() < 1000) {
470      v.append(className);
471    }
472    byte[] value = Bytes.toBytes(v.toString());
473    HRegionServer regionServer = startAndWriteData(TableName.valueOf(name.getMethodName()), value);
474    LOG.info("after writing there are "
475        + AbstractFSWALProvider.getNumRolledLogFiles(regionServer.getWAL(null)) + " log files");
476
477    // flush all regions
478    for (HRegion r : regionServer.getOnlineRegionsLocalContext()) {
479      r.flush(true);
480    }
481    admin.rollWALWriter(regionServer.getServerName());
482    int count = AbstractFSWALProvider.getNumRolledLogFiles(regionServer.getWAL(null));
483    LOG.info("after flushing all regions and rolling logs there are " +
484        count + " log files");
485    assertTrue(("actual count: " + count), count <= 2);
486  }
487
488  private void setUpforLogRolling() {
489    // Force a region split after every 768KB
490    TEST_UTIL.getConfiguration().setLong(HConstants.HREGION_MAX_FILESIZE,
491        768L * 1024L);
492
493    // We roll the log after every 32 writes
494    TEST_UTIL.getConfiguration().setInt("hbase.regionserver.maxlogentries", 32);
495
496    TEST_UTIL.getConfiguration().setInt(
497        "hbase.regionserver.logroll.errors.tolerated", 2);
498    TEST_UTIL.getConfiguration().setInt("hbase.rpc.timeout", 10 * 1000);
499
500    // For less frequently updated regions flush after every 2 flushes
501    TEST_UTIL.getConfiguration().setInt(
502        "hbase.hregion.memstore.optionalflushcount", 2);
503
504    // We flush the cache after every 8192 bytes
505    TEST_UTIL.getConfiguration().setInt(HConstants.HREGION_MEMSTORE_FLUSH_SIZE,
506        8192);
507
508    // Increase the amount of time between client retries
509    TEST_UTIL.getConfiguration().setLong("hbase.client.pause", 10 * 1000);
510
511    // Reduce thread wake frequency so that other threads can get
512    // a chance to run.
513    TEST_UTIL.getConfiguration().setInt(HConstants.THREAD_WAKE_FREQUENCY,
514        2 * 1000);
515
516    /**** configuration for testLogRollOnDatanodeDeath ****/
517    // lower the namenode & datanode heartbeat so the namenode
518    // quickly detects datanode failures
519    TEST_UTIL.getConfiguration().setInt("dfs.namenode.heartbeat.recheck-interval", 5000);
520    TEST_UTIL.getConfiguration().setInt("dfs.heartbeat.interval", 1);
521    // the namenode might still try to choose the recently-dead datanode
522    // for a pipeline, so try to a new pipeline multiple times
523    TEST_UTIL.getConfiguration().setInt("dfs.client.block.write.retries", 30);
524    TEST_UTIL.getConfiguration().setInt(
525        "hbase.regionserver.hlog.tolerable.lowreplication", 2);
526    TEST_UTIL.getConfiguration().setInt(
527        "hbase.regionserver.hlog.lowreplication.rolllimit", 3);
528  }
529
530  private HRegionServer startAndWriteData(TableName tableName, byte[] value)
531  throws IOException, InterruptedException {
532    // When the hbase:meta table can be opened, the region servers are running
533    TEST_UTIL.getConnection().getTable(TableName.META_TABLE_NAME).close();
534
535    // Create the test table and open it
536    HTableDescriptor desc = new HTableDescriptor(tableName);
537    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
538    admin.createTable(desc);
539    Table table = TEST_UTIL.getConnection().getTable(tableName);
540
541    HRegionServer regionServer = TEST_UTIL.getRSForFirstRegionInTable(tableName);
542    for (int i = 1; i <= 256; i++) { // 256 writes should cause 8 log rolls
543      Put put = new Put(Bytes.toBytes("row" + String.format("%1$04d", i)));
544      put.addColumn(HConstants.CATALOG_FAMILY, null, value);
545      table.put(put);
546      if (i % 32 == 0) {
547        // After every 32 writes sleep to let the log roller run
548        try {
549          Thread.sleep(2000);
550        } catch (InterruptedException e) {
551          // continue
552        }
553      }
554    }
555
556    table.close();
557    return regionServer;
558  }
559
560  /**
561   * Check that we have an exception if the cluster is not there.
562   */
563  @Test
564  public void testCheckHBaseAvailableWithoutCluster() {
565    Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
566
567    // Change the ZK address to go to something not used.
568    conf.setInt(HConstants.ZOOKEEPER_CLIENT_PORT,
569      conf.getInt(HConstants.ZOOKEEPER_CLIENT_PORT, 9999)+10);
570
571    long start = System.currentTimeMillis();
572    try {
573      HBaseAdmin.available(conf);
574      assertTrue(false);
575    } catch (ZooKeeperConnectionException ignored) {
576    } catch (IOException ignored) {
577    }
578    long end = System.currentTimeMillis();
579
580    LOG.info("It took "+(end-start)+" ms to find out that" +
581      " HBase was not available");
582  }
583
584  @Test
585  public void testDisableCatalogTable() throws Exception {
586    try {
587      this.admin.disableTable(TableName.META_TABLE_NAME);
588      fail("Expected to throw ConstraintException");
589    } catch (ConstraintException e) {
590    }
591    // Before the fix for HBASE-6146, the below table creation was failing as the hbase:meta table
592    // actually getting disabled by the disableTable() call.
593    HTableDescriptor htd =
594        new HTableDescriptor(TableName.valueOf(Bytes.toBytes(name.getMethodName())));
595    HColumnDescriptor hcd = new HColumnDescriptor(Bytes.toBytes("cf1"));
596    htd.addFamily(hcd);
597    TEST_UTIL.getHBaseAdmin().createTable(htd);
598  }
599
600  @Test
601  public void testIsEnabledOrDisabledOnUnknownTable() throws Exception {
602    try {
603      admin.isTableEnabled(TableName.valueOf(name.getMethodName()));
604      fail("Test should fail if isTableEnabled called on unknown table.");
605    } catch (IOException e) {
606    }
607
608    try {
609      admin.isTableDisabled(TableName.valueOf(name.getMethodName()));
610      fail("Test should fail if isTableDisabled called on unknown table.");
611    } catch (IOException e) {
612    }
613  }
614
615  @Test
616  public void testGetRegion() throws Exception {
617    // We use actual HBaseAdmin instance instead of going via Admin interface in
618    // here because makes use of an internal HBA method (TODO: Fix.).
619    HBaseAdmin rawAdmin = TEST_UTIL.getHBaseAdmin();
620
621    final TableName tableName = TableName.valueOf(name.getMethodName());
622    LOG.info("Started " + tableName);
623    Table t = TEST_UTIL.createMultiRegionTable(tableName, HConstants.CATALOG_FAMILY);
624
625    try (RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
626      HRegionLocation regionLocation = locator.getRegionLocation(Bytes.toBytes("mmm"));
627      RegionInfo region = regionLocation.getRegionInfo();
628      byte[] regionName = region.getRegionName();
629      Pair<RegionInfo, ServerName> pair = rawAdmin.getRegion(regionName);
630      assertTrue(Bytes.equals(regionName, pair.getFirst().getRegionName()));
631      pair = rawAdmin.getRegion(region.getEncodedNameAsBytes());
632      assertTrue(Bytes.equals(regionName, pair.getFirst().getRegionName()));
633    }
634  }
635
636  @Test
637  public void testBalancer() throws Exception {
638    boolean initialState = admin.isBalancerEnabled();
639
640    // Start the balancer, wait for it.
641    boolean prevState = admin.setBalancerRunning(!initialState, true);
642
643    // The previous state should be the original state we observed
644    assertEquals(initialState, prevState);
645
646    // Current state should be opposite of the original
647    assertEquals(!initialState, admin.isBalancerEnabled());
648
649    // Reset it back to what it was
650    prevState = admin.setBalancerRunning(initialState, true);
651
652    // The previous state should be the opposite of the initial state
653    assertEquals(!initialState, prevState);
654    // Current state should be the original state again
655    assertEquals(initialState, admin.isBalancerEnabled());
656  }
657
658  @Test
659  public void testRegionNormalizer() throws Exception {
660    boolean initialState = admin.isNormalizerEnabled();
661
662    // flip state
663    boolean prevState = admin.setNormalizerRunning(!initialState);
664
665    // The previous state should be the original state we observed
666    assertEquals(initialState, prevState);
667
668    // Current state should be opposite of the original
669    assertEquals(!initialState, admin.isNormalizerEnabled());
670
671    // Reset it back to what it was
672    prevState = admin.setNormalizerRunning(initialState);
673
674    // The previous state should be the opposite of the initial state
675    assertEquals(!initialState, prevState);
676    // Current state should be the original state again
677    assertEquals(initialState, admin.isNormalizerEnabled());
678  }
679
680  @Test
681  public void testAbortProcedureFail() throws Exception {
682    Random randomGenerator = new Random();
683    long procId = randomGenerator.nextLong();
684
685    boolean abortResult = admin.abortProcedure(procId, true);
686    assertFalse(abortResult);
687  }
688
689  @Test
690  public void testGetProcedures() throws Exception {
691    String procList = admin.getProcedures();
692    assertTrue(procList.startsWith("["));
693  }
694
695  @Test
696  public void testGetLocks() throws Exception {
697    String lockList = admin.getLocks();
698    assertTrue(lockList.startsWith("["));
699  }
700
701  @Test
702  public void testDecommissionRegionServers() throws Exception {
703    List<ServerName> decommissionedRegionServers = admin.listDecommissionedRegionServers();
704    assertTrue(decommissionedRegionServers.isEmpty());
705
706    final TableName tableName = TableName.valueOf(name.getMethodName());
707    TEST_UTIL.createMultiRegionTable(tableName, Bytes.toBytes("f"), 6);
708
709    ArrayList<ServerName> clusterRegionServers =
710        new ArrayList<>(admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
711          .getLiveServerMetrics().keySet());
712
713    assertEquals(3, clusterRegionServers.size());
714
715    HashMap<ServerName, List<RegionInfo>> serversToDecommssion = new HashMap<>();
716    // Get a server that has regions. We will decommission two of the servers,
717    // leaving one online.
718    int i;
719    for (i = 0; i < clusterRegionServers.size(); i++) {
720      List<RegionInfo> regionsOnServer = admin.getRegions(clusterRegionServers.get(i));
721      if (regionsOnServer.size() > 0) {
722        serversToDecommssion.put(clusterRegionServers.get(i), regionsOnServer);
723        break;
724      }
725    }
726
727    clusterRegionServers.remove(i);
728    // Get another server to decommission.
729    serversToDecommssion.put(clusterRegionServers.get(0),
730      admin.getRegions(clusterRegionServers.get(0)));
731
732    ServerName remainingServer = clusterRegionServers.get(1);
733
734    // Decommission
735    admin.decommissionRegionServers(new ArrayList<ServerName>(serversToDecommssion.keySet()), true);
736    assertEquals(2, admin.listDecommissionedRegionServers().size());
737
738    // Verify the regions have been off the decommissioned servers, all on the one
739    // remaining server.
740    for (ServerName server : serversToDecommssion.keySet()) {
741      for (RegionInfo region : serversToDecommssion.get(server)) {
742        TEST_UTIL.assertRegionOnServer(region, remainingServer, 10000);
743      }
744    }
745
746    // Recommission and load the regions.
747    for (ServerName server : serversToDecommssion.keySet()) {
748      List<byte[]> encodedRegionNames = serversToDecommssion.get(server).stream()
749          .map(region -> region.getEncodedNameAsBytes()).collect(Collectors.toList());
750      admin.recommissionRegionServer(server, encodedRegionNames);
751    }
752    assertTrue(admin.listDecommissionedRegionServers().isEmpty());
753    // Verify the regions have been moved to the recommissioned servers
754    for (ServerName server : serversToDecommssion.keySet()) {
755      for (RegionInfo region : serversToDecommssion.get(server)) {
756        TEST_UTIL.assertRegionOnServer(region, server, 10000);
757      }
758    }
759  }
760
761  /**
762   * TestCase for HBASE-21355
763   */
764  @Test
765  public void testGetRegionInfo() throws Exception {
766    final TableName tableName = TableName.valueOf(name.getMethodName());
767    Table table = TEST_UTIL.createTable(tableName, Bytes.toBytes("f"));
768    for (int i = 0; i < 100; i++) {
769      table.put(new Put(Bytes.toBytes(i)).addColumn(Bytes.toBytes("f"), Bytes.toBytes("q"),
770        Bytes.toBytes(i)));
771    }
772    admin.flush(tableName);
773
774    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(table.getName());
775    List<HRegion> regions = rs.getRegions(tableName);
776    Assert.assertEquals(1, regions.size());
777
778    HRegion region = regions.get(0);
779    byte[] regionName = region.getRegionInfo().getRegionName();
780    HStore store = region.getStore(Bytes.toBytes("f"));
781    long expectedStoreFilesSize = store.getStorefilesSize();
782    Assert.assertNotNull(store);
783    Assert.assertEquals(expectedStoreFilesSize, store.getSize());
784
785    ClusterConnection conn = ((ClusterConnection) admin.getConnection());
786    HBaseRpcController controller = conn.getRpcControllerFactory().newController();
787    for (int i = 0; i < 10; i++) {
788      RegionInfo ri =
789          ProtobufUtil.getRegionInfo(controller, conn.getAdmin(rs.getServerName()), regionName);
790      Assert.assertEquals(region.getRegionInfo(), ri);
791
792      // Make sure that the store size is still the actual file system's store size.
793      Assert.assertEquals(expectedStoreFilesSize, store.getSize());
794    }
795  }
796}