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