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.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory.TRACKER_IMPL;
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.assertFalse;
023import static org.junit.Assert.assertNotEquals;
024import static org.junit.Assert.assertNotNull;
025import static org.junit.Assert.assertNull;
026import static org.junit.Assert.assertTrue;
027import static org.junit.Assert.fail;
028
029import java.io.IOException;
030import java.util.ArrayList;
031import java.util.HashMap;
032import java.util.Iterator;
033import java.util.List;
034import java.util.Map;
035import org.apache.hadoop.hbase.HBaseClassTestRule;
036import org.apache.hadoop.hbase.HConstants;
037import org.apache.hadoop.hbase.HRegionLocation;
038import org.apache.hadoop.hbase.ServerName;
039import org.apache.hadoop.hbase.TableExistsException;
040import org.apache.hadoop.hbase.TableName;
041import org.apache.hadoop.hbase.TableNotFoundException;
042import org.apache.hadoop.hbase.master.HMaster;
043import org.apache.hadoop.hbase.master.LoadBalancer;
044import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
045import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
046import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
047import org.apache.hadoop.hbase.testclassification.ClientTests;
048import org.apache.hadoop.hbase.testclassification.LargeTests;
049import org.apache.hadoop.hbase.util.Bytes;
050import org.junit.ClassRule;
051import org.junit.Test;
052import org.junit.experimental.categories.Category;
053import org.slf4j.Logger;
054import org.slf4j.LoggerFactory;
055
056@Category({ LargeTests.class, ClientTests.class })
057public class TestAdmin extends TestAdminBase {
058
059  @ClassRule
060  public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestAdmin.class);
061
062  private static final Logger LOG = LoggerFactory.getLogger(TestAdmin.class);
063
064  @Test
065  public void testCreateTable() throws IOException {
066    List<TableDescriptor> tables = ADMIN.listTableDescriptors();
067    int numTables = tables.size();
068    final TableName tableName = TableName.valueOf(name.getMethodName());
069    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
070    tables = ADMIN.listTableDescriptors();
071    assertEquals(numTables + 1, tables.size());
072    assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster().getMaster()
073      .getTableStateManager().isTableState(tableName, TableState.State.ENABLED));
074    assertEquals(TableState.State.ENABLED, getStateFromMeta(tableName));
075  }
076
077  @Test
078  public void testTruncateTable() throws IOException {
079    testTruncateTable(TableName.valueOf(name.getMethodName()), false);
080  }
081
082  @Test
083  public void testTruncateTablePreservingSplits() throws IOException {
084    testTruncateTable(TableName.valueOf(name.getMethodName()), true);
085  }
086
087  private void testTruncateTable(final TableName tableName, boolean preserveSplits)
088    throws IOException {
089    byte[][] splitKeys = new byte[2][];
090    splitKeys[0] = Bytes.toBytes(4);
091    splitKeys[1] = Bytes.toBytes(8);
092
093    // Create & Fill the table
094    Table table = TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY, splitKeys);
095    try {
096      TEST_UTIL.loadNumericRows(table, HConstants.CATALOG_FAMILY, 0, 10);
097      assertEquals(10, TEST_UTIL.countRows(table));
098    } finally {
099      table.close();
100    }
101    assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
102
103    // Truncate & Verify
104    ADMIN.disableTable(tableName);
105    ADMIN.truncateTable(tableName, preserveSplits);
106    table = TEST_UTIL.getConnection().getTable(tableName);
107    try {
108      assertEquals(0, TEST_UTIL.countRows(table));
109    } finally {
110      table.close();
111    }
112    if (preserveSplits) {
113      assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
114    } else {
115      assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
116    }
117  }
118
119  @Test
120  public void testCreateTableNumberOfRegions() throws IOException, InterruptedException {
121    TableName table = TableName.valueOf(name.getMethodName());
122    ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY);
123    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table).setColumnFamily(cfd).build());
124    List<HRegionLocation> regions;
125    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table)) {
126      regions = l.getAllRegionLocations();
127      assertEquals("Table should have only 1 region", 1, regions.size());
128    }
129
130    TableName table2 = TableName.valueOf(table.getNameAsString() + "_2");
131    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table2).setColumnFamily(cfd).build(),
132      new byte[][] { new byte[] { 42 } });
133    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table2)) {
134      regions = l.getAllRegionLocations();
135      assertEquals("Table should have only 2 region", 2, regions.size());
136    }
137
138    TableName table3 = TableName.valueOf(table.getNameAsString() + "_3");
139    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table3).setColumnFamily(cfd).build(),
140      Bytes.toBytes("a"), Bytes.toBytes("z"), 3);
141    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table3)) {
142      regions = l.getAllRegionLocations();
143      assertEquals("Table should have only 3 region", 3, regions.size());
144    }
145
146    TableName table4 = TableName.valueOf(table.getNameAsString() + "_4");
147    try {
148      ADMIN.createTable(TableDescriptorBuilder.newBuilder(table4).setColumnFamily(cfd).build(),
149        Bytes.toBytes("a"), Bytes.toBytes("z"), 2);
150      fail("Should not be able to create a table with only 2 regions using this API.");
151    } catch (IllegalArgumentException eae) {
152      // Expected
153    }
154
155    TableName table5 = TableName.valueOf(table.getNameAsString() + "_5");
156    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table5).setColumnFamily(cfd).build(),
157      new byte[] { 1 }, new byte[] { 127 }, 16);
158    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table5)) {
159      regions = l.getAllRegionLocations();
160      assertEquals("Table should have 16 region", 16, regions.size());
161    }
162  }
163
164  @Test
165  public void testCreateTableWithRegions() throws IOException, InterruptedException {
166    TableName table = TableName.valueOf(name.getMethodName());
167    ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY);
168    byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, new byte[] { 3, 3, 3 },
169      new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 }, new byte[] { 6, 6, 6 },
170      new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 }, new byte[] { 9, 9, 9 }, };
171    int expectedRegions = splitKeys.length + 1;
172
173    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table).setColumnFamily(cfd).build(),
174      splitKeys);
175
176    boolean tableAvailable = ADMIN.isTableAvailable(table);
177    assertTrue("Table should be created with splitKyes + 1 rows in META", tableAvailable);
178
179    List<HRegionLocation> regions;
180    Iterator<HRegionLocation> hris;
181    RegionInfo hri;
182    ClusterConnection conn = (ClusterConnection) TEST_UTIL.getConnection();
183    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table)) {
184      regions = l.getAllRegionLocations();
185
186      assertEquals(
187        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
188        expectedRegions, regions.size());
189      System.err.println("Found " + regions.size() + " regions");
190
191      hris = regions.iterator();
192      hri = hris.next().getRegion();
193      assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
194      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0]));
195      hri = hris.next().getRegion();
196      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0]));
197      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1]));
198      hri = hris.next().getRegion();
199      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1]));
200      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2]));
201      hri = hris.next().getRegion();
202      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2]));
203      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3]));
204      hri = hris.next().getRegion();
205      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3]));
206      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4]));
207      hri = hris.next().getRegion();
208      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4]));
209      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5]));
210      hri = hris.next().getRegion();
211      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5]));
212      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6]));
213      hri = hris.next().getRegion();
214      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6]));
215      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7]));
216      hri = hris.next().getRegion();
217      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7]));
218      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8]));
219      hri = hris.next().getRegion();
220      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8]));
221      assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
222
223      verifyRoundRobinDistribution(l, expectedRegions);
224    }
225
226    // Now test using start/end with a number of regions
227
228    // Use 80 bit numbers to make sure we aren't limited
229    byte[] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
230    byte[] endKey = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
231
232    // Splitting into 10 regions, we expect (null,1) ... (9, null)
233    // with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle
234
235    expectedRegions = 10;
236
237    TableName table2 = TableName.valueOf(table.getNameAsString() + "_2");
238    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table2).setColumnFamily(cfd).build(),
239      startKey, endKey, expectedRegions);
240
241    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table2)) {
242      regions = l.getAllRegionLocations();
243      assertEquals(
244        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
245        expectedRegions, regions.size());
246      System.err.println("Found " + regions.size() + " regions");
247
248      hris = regions.iterator();
249      hri = hris.next().getRegion();
250      assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
251      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
252      hri = hris.next().getRegion();
253      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
254      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
255      hri = hris.next().getRegion();
256      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
257      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
258      hri = hris.next().getRegion();
259      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
260      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
261      hri = hris.next().getRegion();
262      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
263      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
264      hri = hris.next().getRegion();
265      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
266      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
267      hri = hris.next().getRegion();
268      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
269      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
270      hri = hris.next().getRegion();
271      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
272      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
273      hri = hris.next().getRegion();
274      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
275      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
276      hri = hris.next().getRegion();
277      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
278      assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
279
280      verifyRoundRobinDistribution(l, expectedRegions);
281    }
282
283    // Try once more with something that divides into something infinite
284
285    startKey = new byte[] { 0, 0, 0, 0, 0, 0 };
286    endKey = new byte[] { 1, 0, 0, 0, 0, 0 };
287
288    expectedRegions = 5;
289
290    TableName table3 = TableName.valueOf(table.getNameAsString() + "_3");
291    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table3).setColumnFamily(cfd).build(),
292      startKey, endKey, expectedRegions);
293
294    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(table3)) {
295      regions = l.getAllRegionLocations();
296      assertEquals(
297        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
298        expectedRegions, regions.size());
299      System.err.println("Found " + regions.size() + " regions");
300
301      verifyRoundRobinDistribution(l, expectedRegions);
302    }
303
304    // Try an invalid case where there are duplicate split keys
305    splitKeys = new byte[][] { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 },
306      new byte[] { 3, 3, 3 }, new byte[] { 2, 2, 2 } };
307
308    TableName table4 = TableName.valueOf(table.getNameAsString() + "_4");
309    try {
310      ADMIN.createTable(TableDescriptorBuilder.newBuilder(table4).setColumnFamily(cfd).build(),
311        splitKeys);
312      assertTrue("Should not be able to create this table because of " + "duplicate split keys",
313        false);
314    } catch (IllegalArgumentException iae) {
315      // Expected
316    }
317  }
318
319  @Test
320  public void testCreateTableWithOnlyEmptyStartRow() throws IOException {
321    final byte[] tableName = Bytes.toBytes(name.getMethodName());
322    byte[][] splitKeys = new byte[1][];
323    splitKeys[0] = HConstants.EMPTY_BYTE_ARRAY;
324    TableDescriptor desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName))
325      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("col")).build();
326    try {
327      ADMIN.createTable(desc, splitKeys);
328      fail("Test case should fail as empty split key is passed.");
329    } catch (IllegalArgumentException e) {
330    }
331  }
332
333  @Test
334  public void testCreateTableWithEmptyRowInTheSplitKeys() throws IOException {
335    final byte[] tableName = Bytes.toBytes(name.getMethodName());
336    byte[][] splitKeys = new byte[3][];
337    splitKeys[0] = Bytes.toBytes("region1");
338    splitKeys[1] = HConstants.EMPTY_BYTE_ARRAY;
339    splitKeys[2] = Bytes.toBytes("region2");
340    TableDescriptor desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName))
341      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("col")).build();
342    try {
343      ADMIN.createTable(desc, splitKeys);
344      fail("Test case should fail as empty split key is passed.");
345    } catch (IllegalArgumentException e) {
346      LOG.info("Expected ", e);
347    }
348  }
349
350  private void verifyRoundRobinDistribution(RegionLocator regionLocator, int expectedRegions)
351    throws IOException {
352    int numRS = TEST_UTIL.getMiniHBaseCluster().getNumLiveRegionServers();
353    List<HRegionLocation> regions = regionLocator.getAllRegionLocations();
354    Map<ServerName, List<RegionInfo>> server2Regions = new HashMap<>();
355    for (HRegionLocation loc : regions) {
356      ServerName server = loc.getServerName();
357      List<RegionInfo> regs = server2Regions.get(server);
358      if (regs == null) {
359        regs = new ArrayList<>();
360        server2Regions.put(server, regs);
361      }
362      regs.add(loc.getRegion());
363    }
364    boolean tablesOnMaster = LoadBalancer.isTablesOnMaster(TEST_UTIL.getConfiguration());
365    if (tablesOnMaster) {
366      // Ignore the master region server,
367      // which contains less regions by intention.
368      numRS--;
369    }
370    float average = (float) expectedRegions / numRS;
371    int min = (int) Math.floor(average);
372    int max = (int) Math.ceil(average);
373    for (List<RegionInfo> regionList : server2Regions.values()) {
374      assertTrue(
375        "numRS=" + numRS + ", min=" + min + ", max=" + max + ", size=" + regionList.size()
376          + ", tablesOnMaster=" + tablesOnMaster,
377        regionList.size() == min || regionList.size() == max);
378    }
379  }
380
381  @Test
382  public void testCloneTableSchema() throws Exception {
383    final TableName tableName = TableName.valueOf(name.getMethodName());
384    final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new");
385    testCloneTableSchema(tableName, newTableName, false);
386  }
387
388  @Test
389  public void testCloneTableSchemaPreservingSplits() throws Exception {
390    final TableName tableName = TableName.valueOf(name.getMethodName());
391    final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new");
392    testCloneTableSchema(tableName, newTableName, true);
393  }
394
395  private void testCloneTableSchema(final TableName tableName, final TableName newTableName,
396    boolean preserveSplits) throws Exception {
397    byte[] FAMILY_0 = Bytes.toBytes("cf0");
398    byte[] FAMILY_1 = Bytes.toBytes("cf1");
399    byte[][] splitKeys = new byte[2][];
400    splitKeys[0] = Bytes.toBytes(4);
401    splitKeys[1] = Bytes.toBytes(8);
402    int NUM_FAMILYS = 2;
403    int NUM_REGIONS = 3;
404    int BLOCK_SIZE = 1024;
405    int TTL = 86400;
406    boolean BLOCK_CACHE = false;
407
408    // Create the table
409    TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(tableName)
410      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_0))
411      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(FAMILY_1).setBlocksize(BLOCK_SIZE)
412        .setBlockCacheEnabled(BLOCK_CACHE).setTimeToLive(TTL).build())
413      .build();
414    ADMIN.createTable(tableDesc, splitKeys);
415
416    assertEquals(NUM_REGIONS, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
417    assertTrue("Table should be created with splitKyes + 1 rows in META",
418      ADMIN.isTableAvailable(tableName));
419
420    // clone & Verify
421    ADMIN.cloneTableSchema(tableName, newTableName, preserveSplits);
422    TableDescriptor newTableDesc = ADMIN.getDescriptor(newTableName);
423
424    assertEquals(NUM_FAMILYS, newTableDesc.getColumnFamilyCount());
425    assertEquals(BLOCK_SIZE, newTableDesc.getColumnFamily(FAMILY_1).getBlocksize());
426    assertEquals(BLOCK_CACHE, newTableDesc.getColumnFamily(FAMILY_1).isBlockCacheEnabled());
427    assertEquals(TTL, newTableDesc.getColumnFamily(FAMILY_1).getTimeToLive());
428    // HBASE-26246 introduced persist of store file tracker into table descriptor
429    tableDesc = TableDescriptorBuilder.newBuilder(tableDesc).setValue(TRACKER_IMPL,
430      StoreFileTrackerFactory.getStoreFileTrackerName(TEST_UTIL.getConfiguration())).build();
431    TEST_UTIL.verifyTableDescriptorIgnoreTableName(tableDesc, newTableDesc);
432
433    if (preserveSplits) {
434      assertEquals(NUM_REGIONS, TEST_UTIL.getHBaseCluster().getRegions(newTableName).size());
435      assertTrue("New table should be created with splitKyes + 1 rows in META",
436        ADMIN.isTableAvailable(newTableName));
437    } else {
438      assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(newTableName).size());
439    }
440  }
441
442  @Test
443  public void testCloneTableSchemaWithNonExistentSourceTable() throws Exception {
444    final TableName tableName = TableName.valueOf(name.getMethodName());
445    final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new");
446    // test for non-existent source table
447    try {
448      ADMIN.cloneTableSchema(tableName, newTableName, false);
449      fail("Should have failed to create a new table by cloning non-existent source table.");
450    } catch (TableNotFoundException ex) {
451      // expected
452    }
453  }
454
455  @Test
456  public void testCloneTableSchemaWithExistentDestinationTable() throws Exception {
457    final TableName tableName = TableName.valueOf(name.getMethodName());
458    final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new");
459    byte[] FAMILY_0 = Bytes.toBytes("cf0");
460    TEST_UTIL.createTable(tableName, FAMILY_0);
461    TEST_UTIL.createTable(newTableName, FAMILY_0);
462    // test for existent destination table
463    try {
464      ADMIN.cloneTableSchema(tableName, newTableName, false);
465      fail("Should have failed to create a existent table.");
466    } catch (TableExistsException ex) {
467      // expected
468    }
469  }
470
471  @Test
472  public void testModifyTableOnTableWithRegionReplicas() throws Exception {
473    TableName tableName = TableName.valueOf(name.getMethodName());
474    TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
475      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(Bytes.toBytes("cf")))
476      .setRegionReplication(5).build();
477
478    ADMIN.createTable(desc);
479
480    int maxFileSize = 10000000;
481    TableDescriptor newDesc =
482      TableDescriptorBuilder.newBuilder(desc).setMaxFileSize(maxFileSize).build();
483
484    ADMIN.modifyTable(newDesc);
485    TableDescriptor newTableDesc = ADMIN.getDescriptor(tableName);
486    assertEquals(maxFileSize, newTableDesc.getMaxFileSize());
487  }
488
489  /**
490   * Verify schema modification takes.
491   */
492  @Test
493  public void testOnlineChangeTableSchema() throws IOException, InterruptedException {
494    final TableName tableName = TableName.valueOf(name.getMethodName());
495    List<TableDescriptor> tables = ADMIN.listTableDescriptors();
496    int numTables = tables.size();
497    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
498    tables = ADMIN.listTableDescriptors();
499    assertEquals(numTables + 1, tables.size());
500    // FIRST, do htabledescriptor changes.
501    TableDescriptor htd = ADMIN.getDescriptor(tableName);
502    // Make a copy and assert copy is good.
503    TableDescriptor copy = TableDescriptorBuilder.newBuilder(htd).build();
504    assertEquals(htd, copy);
505    String key = "anyoldkey";
506    assertNull(htd.getValue(key));
507    // Now amend the copy. Introduce differences.
508    long newFlushSize = htd.getMemStoreFlushSize() / 2;
509    if (newFlushSize <= 0) {
510      newFlushSize = TableDescriptorBuilder.DEFAULT_MEMSTORE_FLUSH_SIZE / 2;
511    }
512    copy = TableDescriptorBuilder.newBuilder(copy).setMemStoreFlushSize(newFlushSize)
513      .setValue(key, key).build();
514    ADMIN.modifyTable(copy);
515    TableDescriptor modifiedHtd = ADMIN.getDescriptor(tableName);
516    assertNotEquals(htd, modifiedHtd);
517    assertEquals(copy, modifiedHtd);
518    assertEquals(newFlushSize, modifiedHtd.getMemStoreFlushSize());
519    assertEquals(key, modifiedHtd.getValue(key));
520
521    // Now work on column family changes.
522    int countOfFamilies = modifiedHtd.getColumnFamilyCount();
523    assertTrue(countOfFamilies > 0);
524    ColumnFamilyDescriptor hcd = modifiedHtd.getColumnFamilies()[0];
525    int maxversions = hcd.getMaxVersions();
526    int newMaxVersions = maxversions + 1;
527    hcd = ColumnFamilyDescriptorBuilder.newBuilder(hcd).setMaxVersions(newMaxVersions).build();
528    byte[] hcdName = hcd.getName();
529    ADMIN.modifyColumnFamily(tableName, hcd);
530    modifiedHtd = ADMIN.getDescriptor(tableName);
531    ColumnFamilyDescriptor modifiedHcd = modifiedHtd.getColumnFamily(hcdName);
532    assertEquals(newMaxVersions, modifiedHcd.getMaxVersions());
533
534    // Try adding a column
535    assertFalse(ADMIN.isTableDisabled(tableName));
536    String xtracolName = "xtracol";
537    ColumnFamilyDescriptor xtracol = ColumnFamilyDescriptorBuilder
538      .newBuilder(Bytes.toBytes(xtracolName)).setValue(xtracolName, xtracolName).build();
539    ADMIN.addColumnFamily(tableName, xtracol);
540    modifiedHtd = ADMIN.getDescriptor(tableName);
541    hcd = modifiedHtd.getColumnFamily(xtracol.getName());
542    assertNotNull(hcd);
543    assertEquals(xtracolName, Bytes.toString(hcd.getValue(Bytes.toBytes(xtracolName))));
544
545    // Delete the just-added column.
546    ADMIN.deleteColumnFamily(tableName, xtracol.getName());
547    modifiedHtd = ADMIN.getDescriptor(tableName);
548    hcd = modifiedHtd.getColumnFamily(xtracol.getName());
549    assertNull(hcd);
550
551    // Delete the table
552    ADMIN.disableTable(tableName);
553    ADMIN.deleteTable(tableName);
554    ADMIN.listTableDescriptors();
555    assertFalse(ADMIN.tableExists(tableName));
556  }
557
558  @Test
559  public void testUnknownServers() throws Exception {
560    TableName table = TableName.valueOf(name.getMethodName());
561    ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY);
562    ADMIN.createTable(TableDescriptorBuilder.newBuilder(table).setColumnFamily(cfd).build());
563    final List<RegionInfo> regions = ADMIN.getRegions(table);
564    HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
565    final AssignmentManager am = master.getAssignmentManager();
566    RegionStateNode rsNode = am.getRegionStates().getRegionStateNode(regions.get(0));
567    ServerName regionLocation = rsNode.getRegionLocation();
568    rsNode.setRegionLocation(ServerName.valueOf("dummyserver", 1234, System.currentTimeMillis()));
569    try {
570      assertTrue(ADMIN.listUnknownServers().get(0).getHostname().equals("dummyserver"));
571    } finally {
572      rsNode.setRegionLocation(regionLocation);
573    }
574    assertTrue(ADMIN.listUnknownServers().isEmpty());
575  }
576}