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