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