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.rsgroup;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024import static org.junit.Assert.fail;
025
026import java.io.IOException;
027import java.security.SecureRandom;
028import java.util.ArrayList;
029import java.util.EnumSet;
030import java.util.HashSet;
031import java.util.Iterator;
032import java.util.LinkedList;
033import java.util.List;
034import java.util.Map;
035import java.util.Set;
036import java.util.TreeMap;
037import org.apache.hadoop.hbase.ClusterMetrics;
038import org.apache.hadoop.hbase.ClusterMetrics.Option;
039import org.apache.hadoop.hbase.HBaseCluster;
040import org.apache.hadoop.hbase.HBaseTestingUtility;
041import org.apache.hadoop.hbase.HColumnDescriptor;
042import org.apache.hadoop.hbase.HTableDescriptor;
043import org.apache.hadoop.hbase.NamespaceDescriptor;
044import org.apache.hadoop.hbase.RegionMetrics;
045import org.apache.hadoop.hbase.ServerMetrics;
046import org.apache.hadoop.hbase.ServerName;
047import org.apache.hadoop.hbase.TableName;
048import org.apache.hadoop.hbase.TableNotFoundException;
049import org.apache.hadoop.hbase.Waiter;
050import org.apache.hadoop.hbase.client.Admin;
051import org.apache.hadoop.hbase.client.ClusterConnection;
052import org.apache.hadoop.hbase.client.RegionInfo;
053import org.apache.hadoop.hbase.constraint.ConstraintException;
054import org.apache.hadoop.hbase.master.HMaster;
055import org.apache.hadoop.hbase.net.Address;
056import org.apache.hadoop.hbase.util.Bytes;
057import org.junit.Assert;
058import org.junit.Before;
059import org.junit.Rule;
060import org.junit.Test;
061import org.junit.rules.TestName;
062import org.slf4j.Logger;
063import org.slf4j.LoggerFactory;
064
065import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
066import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
067import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
068import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
069import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos;
070import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetServerInfoRequest;
071
072public abstract class TestRSGroupsBase {
073  protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsBase.class);
074  @Rule
075  public TestName name = new TestName();
076
077  //shared
078  protected final static String groupPrefix = "Group";
079  protected final static String tablePrefix = "Group";
080  protected final static SecureRandom rand = new SecureRandom();
081
082  //shared, cluster type specific
083  protected static HBaseTestingUtility TEST_UTIL;
084  protected static Admin admin;
085  protected static HBaseCluster cluster;
086  protected static RSGroupAdmin rsGroupAdmin;
087  protected static HMaster master;
088
089  public final static long WAIT_TIMEOUT = 60000*5;
090  public final static int NUM_SLAVES_BASE = 4; //number of slaves for the smallest cluster
091  public static int NUM_DEAD_SERVERS = 0;
092
093  // Per test variables
094  TableName tableName;
095  @Before
096  public void setup() {
097    LOG.info(name.getMethodName());
098    tableName = TableName.valueOf(tablePrefix + "_" + name.getMethodName());
099  }
100
101  protected RSGroupInfo addGroup(String groupName, int serverCount)
102      throws IOException, InterruptedException {
103    RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
104    assertTrue(defaultInfo != null);
105    assertTrue(defaultInfo.getServers().size() >= serverCount);
106    rsGroupAdmin.addRSGroup(groupName);
107
108    Set<Address> set = new HashSet<>();
109    for(Address server: defaultInfo.getServers()) {
110      if(set.size() == serverCount) {
111        break;
112      }
113      set.add(server);
114    }
115    rsGroupAdmin.moveServers(set, groupName);
116    RSGroupInfo result = rsGroupAdmin.getRSGroupInfo(groupName);
117    assertTrue(result.getServers().size() >= serverCount);
118    return result;
119  }
120
121  void removeGroup(String groupName) throws IOException {
122    RSGroupInfo RSGroupInfo = rsGroupAdmin.getRSGroupInfo(groupName);
123    rsGroupAdmin.moveTables(RSGroupInfo.getTables(), RSGroupInfo.DEFAULT_GROUP);
124    rsGroupAdmin.moveServers(RSGroupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
125    rsGroupAdmin.removeRSGroup(groupName);
126  }
127
128  protected void deleteTableIfNecessary() throws IOException {
129    for (HTableDescriptor desc : TEST_UTIL.getAdmin().listTables(tablePrefix+".*")) {
130      TEST_UTIL.deleteTable(desc.getTableName());
131    }
132  }
133
134  protected void deleteNamespaceIfNecessary() throws IOException {
135    for (NamespaceDescriptor desc : TEST_UTIL.getAdmin().listNamespaceDescriptors()) {
136      if(desc.getName().startsWith(tablePrefix)) {
137        admin.deleteNamespace(desc.getName());
138      }
139    }
140  }
141
142  protected void deleteGroups() throws IOException {
143    RSGroupAdmin groupAdmin =
144        new RSGroupAdminClient(TEST_UTIL.getConnection());
145    for(RSGroupInfo group: groupAdmin.listRSGroups()) {
146      if(!group.getName().equals(RSGroupInfo.DEFAULT_GROUP)) {
147        groupAdmin.moveTables(group.getTables(), RSGroupInfo.DEFAULT_GROUP);
148        groupAdmin.moveServers(group.getServers(), RSGroupInfo.DEFAULT_GROUP);
149        groupAdmin.removeRSGroup(group.getName());
150      }
151    }
152  }
153
154  public Map<TableName, List<String>> getTableRegionMap() throws IOException {
155    Map<TableName, List<String>> map = Maps.newTreeMap();
156    Map<TableName, Map<ServerName, List<String>>> tableServerRegionMap
157        = getTableServerRegionMap();
158    for(TableName tableName : tableServerRegionMap.keySet()) {
159      if(!map.containsKey(tableName)) {
160        map.put(tableName, new LinkedList<>());
161      }
162      for(List<String> subset: tableServerRegionMap.get(tableName).values()) {
163        map.get(tableName).addAll(subset);
164      }
165    }
166    return map;
167  }
168
169  public Map<TableName, Map<ServerName, List<String>>> getTableServerRegionMap()
170      throws IOException {
171    Map<TableName, Map<ServerName, List<String>>> map = Maps.newTreeMap();
172    ClusterMetrics status = TEST_UTIL.getHBaseClusterInterface().getClusterMetrics();
173    for (Map.Entry<ServerName, ServerMetrics> entry : status.getLiveServerMetrics().entrySet()) {
174      ServerName serverName = entry.getKey();
175      for(RegionMetrics rl : entry.getValue().getRegionMetrics().values()) {
176        TableName tableName = null;
177        try {
178          tableName = RegionInfo.getTable(rl.getRegionName());
179        } catch (IllegalArgumentException e) {
180          LOG.warn("Failed parse a table name from regionname=" +
181            Bytes.toStringBinary(rl.getRegionName()));
182          continue;
183        }
184        if(!map.containsKey(tableName)) {
185          map.put(tableName, new TreeMap<>());
186        }
187        if(!map.get(tableName).containsKey(serverName)) {
188          map.get(tableName).put(serverName, new LinkedList<>());
189        }
190        map.get(tableName).get(serverName).add(rl.getNameAsString());
191      }
192    }
193    return map;
194  }
195
196  @Test
197  public void testBogusArgs() throws Exception {
198    assertNull(rsGroupAdmin.getRSGroupInfoOfTable(TableName.valueOf("nonexistent")));
199    assertNull(rsGroupAdmin.getRSGroupOfServer(Address.fromParts("bogus",123)));
200    assertNull(rsGroupAdmin.getRSGroupInfo("bogus"));
201
202    try {
203      rsGroupAdmin.removeRSGroup("bogus");
204      fail("Expected removing bogus group to fail");
205    } catch(ConstraintException ex) {
206      //expected
207    }
208
209    try {
210      rsGroupAdmin.moveTables(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus");
211      fail("Expected move with bogus group to fail");
212    } catch(ConstraintException|TableNotFoundException ex) {
213      //expected
214    }
215
216    try {
217      rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromParts("bogus",123)), "bogus");
218      fail("Expected move with bogus group to fail");
219    } catch(ConstraintException ex) {
220      //expected
221    }
222
223    try {
224      admin.setBalancerRunning(true,true);
225      rsGroupAdmin.balanceRSGroup("bogus");
226      admin.setBalancerRunning(false,true);
227      fail("Expected move with bogus group to fail");
228    } catch(ConstraintException ex) {
229      //expected
230    }
231  }
232
233  @Test
234  public void testCreateMultiRegion() throws IOException {
235    byte[] end = {1,3,5,7,9};
236    byte[] start = {0,2,4,6,8};
237    byte[][] f = {Bytes.toBytes("f")};
238    TEST_UTIL.createTable(tableName, f,1,start,end,10);
239  }
240
241  @Test
242  public void testCreateAndDrop() throws Exception {
243    TEST_UTIL.createTable(tableName, Bytes.toBytes("cf"));
244    //wait for created table to be assigned
245    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
246      @Override
247      public boolean evaluate() throws Exception {
248        return getTableRegionMap().get(tableName) != null;
249      }
250    });
251    TEST_UTIL.deleteTable(tableName);
252  }
253
254
255  @Test
256  public void testSimpleRegionServerMove() throws IOException,
257      InterruptedException {
258    int initNumGroups = rsGroupAdmin.listRSGroups().size();
259    RSGroupInfo appInfo = addGroup(getGroupName(name.getMethodName()), 1);
260    RSGroupInfo adminInfo = addGroup(getGroupName(name.getMethodName()), 1);
261    RSGroupInfo dInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
262    Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size());
263    assertEquals(1, adminInfo.getServers().size());
264    assertEquals(1, appInfo.getServers().size());
265    assertEquals(getNumServers() - 2, dInfo.getServers().size());
266    rsGroupAdmin.moveServers(appInfo.getServers(),
267        RSGroupInfo.DEFAULT_GROUP);
268    rsGroupAdmin.removeRSGroup(appInfo.getName());
269    rsGroupAdmin.moveServers(adminInfo.getServers(),
270        RSGroupInfo.DEFAULT_GROUP);
271    rsGroupAdmin.removeRSGroup(adminInfo.getName());
272    Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups);
273  }
274
275  // return the real number of region servers, excluding the master embedded region server in 2.0+
276  public int getNumServers() throws IOException {
277    ClusterMetrics status =
278        admin.getClusterMetrics(EnumSet.of(Option.MASTER, Option.LIVE_SERVERS));
279    ServerName masterName = status.getMasterName();
280    int count = 0;
281    for (ServerName sn : status.getLiveServerMetrics().keySet()) {
282      if (!sn.equals(masterName)) {
283        count++;
284      }
285    }
286    return count;
287  }
288
289  @Test
290  public void testMoveServers() throws Exception {
291    //create groups and assign servers
292    addGroup("bar", 3);
293    rsGroupAdmin.addRSGroup("foo");
294
295    RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
296    RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
297    assertEquals(3, barGroup.getServers().size());
298    assertEquals(0, fooGroup.getServers().size());
299
300    //test fail bogus server move
301    try {
302      rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromString("foo:9999")),"foo");
303      fail("Bogus servers shouldn't have been successfully moved.");
304    } catch(IOException ex) {
305      String exp = "Source RSGroup for server foo:9999 does not exist.";
306      String msg = "Expected '"+exp+"' in exception message: ";
307      assertTrue(msg+" "+ex.getMessage(), ex.getMessage().contains(exp));
308    }
309
310    //test success case
311    LOG.info("moving servers "+barGroup.getServers()+" to group foo");
312    rsGroupAdmin.moveServers(barGroup.getServers(), fooGroup.getName());
313
314    barGroup = rsGroupAdmin.getRSGroupInfo("bar");
315    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
316    assertEquals(0,barGroup.getServers().size());
317    assertEquals(3,fooGroup.getServers().size());
318
319    LOG.info("moving servers "+fooGroup.getServers()+" to group default");
320    rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
321
322    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
323      @Override
324      public boolean evaluate() throws Exception {
325        return getNumServers() ==
326        rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
327      }
328    });
329
330    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
331    assertEquals(0,fooGroup.getServers().size());
332
333    //test group removal
334    LOG.info("Remove group "+barGroup.getName());
335    rsGroupAdmin.removeRSGroup(barGroup.getName());
336    Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(barGroup.getName()));
337    LOG.info("Remove group "+fooGroup.getName());
338    rsGroupAdmin.removeRSGroup(fooGroup.getName());
339    Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName()));
340  }
341
342  @Test
343  public void testMoveServersFromDefaultGroup() throws Exception {
344    //create groups and assign servers
345    rsGroupAdmin.addRSGroup("foo");
346
347    RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
348    assertEquals(0, fooGroup.getServers().size());
349    RSGroupInfo defaultGroup = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
350
351    //test remove all servers from default
352    try {
353      rsGroupAdmin.moveServers(defaultGroup.getServers(), fooGroup.getName());
354      fail(RSGroupAdminServer.KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE);
355    } catch (ConstraintException ex) {
356      assertTrue(ex.getMessage().contains(RSGroupAdminServer
357              .KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE));
358    }
359
360    //test success case, remove one server from default ,keep at least one server
361    if (defaultGroup.getServers().size() > 1) {
362      Address serverInDefaultGroup = defaultGroup.getServers().iterator().next();
363      LOG.info("moving server " + serverInDefaultGroup + " from group default to group " +
364              fooGroup.getName());
365      rsGroupAdmin.moveServers(Sets.newHashSet(serverInDefaultGroup), fooGroup.getName());
366    }
367
368    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
369    LOG.info("moving servers " + fooGroup.getServers() + " to group default");
370    rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
371
372    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
373      @Override
374      public boolean evaluate() throws Exception {
375        return getNumServers() ==
376                rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
377      }
378    });
379
380    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
381    assertEquals(0, fooGroup.getServers().size());
382
383    //test group removal
384    LOG.info("Remove group " + fooGroup.getName());
385    rsGroupAdmin.removeRSGroup(fooGroup.getName());
386    Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName()));
387  }
388
389  @Test
390  public void testTableMoveTruncateAndDrop() throws Exception {
391    final byte[] familyNameBytes = Bytes.toBytes("f");
392    String newGroupName = getGroupName(name.getMethodName());
393    final RSGroupInfo newGroup = addGroup(newGroupName, 2);
394
395    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
396    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
397      @Override
398      public boolean evaluate() throws Exception {
399        List<String> regions = getTableRegionMap().get(tableName);
400        if (regions == null) {
401          return false;
402        }
403
404        return getTableRegionMap().get(tableName).size() >= 5;
405      }
406    });
407
408    RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
409    assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
410
411    //change table's group
412    LOG.info("Moving table "+tableName+" to "+newGroup.getName());
413    rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
414
415    //verify group change
416    Assert.assertEquals(newGroup.getName(),
417        rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
418
419    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
420      @Override
421      public boolean evaluate() throws Exception {
422        Map<ServerName, List<String>> serverMap = getTableServerRegionMap().get(tableName);
423        int count = 0;
424        if (serverMap != null) {
425          for (ServerName rs : serverMap.keySet()) {
426            if (newGroup.containsServer(rs.getAddress())) {
427              count += serverMap.get(rs).size();
428            }
429          }
430        }
431        return count == 5;
432      }
433    });
434
435    //test truncate
436    admin.disableTable(tableName);
437    admin.truncateTable(tableName, true);
438    Assert.assertEquals(1, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
439    Assert.assertEquals(tableName, rsGroupAdmin.getRSGroupInfo(
440        newGroup.getName()).getTables().first());
441
442    //verify removed table is removed from group
443    TEST_UTIL.deleteTable(tableName);
444    Assert.assertEquals(0, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
445  }
446
447  @Test
448  public void testGroupBalance() throws Exception {
449    LOG.info(name.getMethodName());
450    String newGroupName = getGroupName(name.getMethodName());
451    final RSGroupInfo newGroup = addGroup(newGroupName, 3);
452
453    final TableName tableName = TableName.valueOf(tablePrefix+"_ns", name.getMethodName());
454    admin.createNamespace(
455        NamespaceDescriptor.create(tableName.getNamespaceAsString())
456            .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, newGroupName).build());
457    final byte[] familyNameBytes = Bytes.toBytes("f");
458    final HTableDescriptor desc = new HTableDescriptor(tableName);
459    desc.addFamily(new HColumnDescriptor("f"));
460    byte [] startKey = Bytes.toBytes("aaaaa");
461    byte [] endKey = Bytes.toBytes("zzzzz");
462    admin.createTable(desc, startKey, endKey, 6);
463    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
464      @Override
465      public boolean evaluate() throws Exception {
466        List<String> regions = getTableRegionMap().get(tableName);
467        if (regions == null) {
468          return false;
469        }
470        return regions.size() >= 6;
471      }
472    });
473
474    //make assignment uneven, move all regions to one server
475    Map<ServerName,List<String>> assignMap =
476        getTableServerRegionMap().get(tableName);
477    final ServerName first = assignMap.entrySet().iterator().next().getKey();
478    for(RegionInfo region: admin.getTableRegions(tableName)) {
479      if(!assignMap.get(first).contains(region.getRegionNameAsString())) {
480        admin.move(region.getEncodedNameAsBytes(), Bytes.toBytes(first.getServerName()));
481      }
482    }
483    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
484      @Override
485      public boolean evaluate() throws Exception {
486        Map<ServerName, List<String>> map = getTableServerRegionMap().get(tableName);
487        if (map == null) {
488          return true;
489        }
490        List<String> regions = map.get(first);
491        if (regions == null) {
492          return true;
493        }
494        return regions.size() >= 6;
495      }
496    });
497
498    //balance the other group and make sure it doesn't affect the new group
499    admin.setBalancerRunning(true,true);
500    rsGroupAdmin.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP);
501    assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size());
502
503    //disable balance, balancer will not be run and return false
504    admin.setBalancerRunning(false,true);
505    assertFalse(rsGroupAdmin.balanceRSGroup(newGroupName));
506    assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size());
507
508    //enable balance
509    admin.setBalancerRunning(true,true);
510    rsGroupAdmin.balanceRSGroup(newGroupName);
511    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
512      @Override
513      public boolean evaluate() throws Exception {
514        for (List<String> regions : getTableServerRegionMap().get(tableName).values()) {
515          if (2 != regions.size()) {
516            return false;
517          }
518        }
519        return true;
520      }
521    });
522    admin.setBalancerRunning(false,true);
523  }
524
525  @Test
526  public void testRegionMove() throws Exception {
527    final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1);
528    final byte[] familyNameBytes = Bytes.toBytes("f");
529    // All the regions created below will be assigned to the default group.
530    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 6);
531    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
532      @Override
533      public boolean evaluate() throws Exception {
534        List<String> regions = getTableRegionMap().get(tableName);
535        if (regions == null) {
536          return false;
537        }
538        return getTableRegionMap().get(tableName).size() >= 6;
539      }
540    });
541
542    //get target region to move
543    Map<ServerName,List<String>> assignMap =
544        getTableServerRegionMap().get(tableName);
545    String targetRegion = null;
546    for(ServerName server : assignMap.keySet()) {
547      targetRegion = assignMap.get(server).size() > 0 ? assignMap.get(server).get(0) : null;
548      if(targetRegion != null) {
549        break;
550      }
551    }
552    //get server which is not a member of new group
553    ServerName targetServer = null;
554    for (ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
555                                  .getLiveServerMetrics().keySet()) {
556      if (!newGroup.containsServer(server.getAddress())) {
557        targetServer = server;
558        break;
559      }
560    }
561
562    final AdminProtos.AdminService.BlockingInterface targetRS =
563      ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
564
565    //move target server to group
566    rsGroupAdmin.moveServers(Sets.newHashSet(targetServer.getAddress()),
567        newGroup.getName());
568    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
569      @Override
570      public boolean evaluate() throws Exception {
571        return ProtobufUtil.getOnlineRegions(targetRS).size() <= 0;
572      }
573    });
574
575    // Lets move this region to the new group.
576    TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName(
577        Bytes.toBytes(targetRegion))), Bytes.toBytes(targetServer.getServerName()));
578    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
579      @Override
580      public boolean evaluate() throws Exception {
581        return
582            getTableRegionMap().get(tableName) != null &&
583                getTableRegionMap().get(tableName).size() == 6 &&
584                admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION))
585                     .getRegionStatesInTransition().size() < 1;
586      }
587    });
588
589    //verify that targetServer didn't open it
590    for (RegionInfo region: ProtobufUtil.getOnlineRegions(targetRS)) {
591      if (targetRegion.equals(region.getRegionNameAsString())) {
592        fail("Target server opened region");
593      }
594    }
595  }
596
597  @Test
598  public void testFailRemoveGroup() throws IOException, InterruptedException {
599    int initNumGroups = rsGroupAdmin.listRSGroups().size();
600    addGroup("bar", 3);
601    TEST_UTIL.createTable(tableName, Bytes.toBytes("f"));
602    rsGroupAdmin.moveTables(Sets.newHashSet(tableName), "bar");
603    RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
604    //group is not empty therefore it should fail
605    try {
606      rsGroupAdmin.removeRSGroup(barGroup.getName());
607      fail("Expected remove group to fail");
608    } catch(IOException e) {
609    }
610    //group cannot lose all it's servers therefore it should fail
611    try {
612      rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
613      fail("Expected move servers to fail");
614    } catch(IOException e) {
615    }
616
617    rsGroupAdmin.moveTables(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP);
618    try {
619      rsGroupAdmin.removeRSGroup(barGroup.getName());
620      fail("Expected move servers to fail");
621    } catch(IOException e) {
622    }
623
624    rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
625    rsGroupAdmin.removeRSGroup(barGroup.getName());
626
627    Assert.assertEquals(initNumGroups, rsGroupAdmin.listRSGroups().size());
628  }
629
630  @Test
631  public void testKillRS() throws Exception {
632    RSGroupInfo appInfo = addGroup("appInfo", 1);
633
634    final TableName tableName = TableName.valueOf(tablePrefix+"_ns", name.getMethodName());
635    admin.createNamespace(
636        NamespaceDescriptor.create(tableName.getNamespaceAsString())
637            .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build());
638    final HTableDescriptor desc = new HTableDescriptor(tableName);
639    desc.addFamily(new HColumnDescriptor("f"));
640    admin.createTable(desc);
641    //wait for created table to be assigned
642    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
643      @Override
644      public boolean evaluate() throws Exception {
645        return getTableRegionMap().get(desc.getTableName()) != null;
646      }
647    });
648
649    ServerName targetServer = ServerName.parseServerName(
650        appInfo.getServers().iterator().next().toString());
651    AdminProtos.AdminService.BlockingInterface targetRS =
652      ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
653    RegionInfo targetRegion = ProtobufUtil.getOnlineRegions(targetRS).get(0);
654    Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size());
655
656    try {
657      //stopping may cause an exception
658      //due to the connection loss
659      targetRS.stopServer(null,
660          AdminProtos.StopServerRequest.newBuilder().setReason("Die").build());
661    } catch(Exception e) {
662    }
663    assertFalse(cluster.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer));
664
665    //wait for created table to be assigned
666    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
667      @Override
668      public boolean evaluate() throws Exception {
669        return cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty();
670      }
671    });
672    Set<Address> newServers = Sets.newHashSet();
673    newServers.add(
674        rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().iterator().next());
675    rsGroupAdmin.moveServers(newServers, appInfo.getName());
676
677    //Make sure all the table's regions get reassigned
678    //disabling the table guarantees no conflicting assign/unassign (ie SSH) happens
679    admin.disableTable(tableName);
680    admin.enableTable(tableName);
681
682    //wait for region to be assigned
683    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
684      @Override
685      public boolean evaluate() throws Exception {
686        return cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty();
687      }
688    });
689
690    targetServer = ServerName.parseServerName(
691        newServers.iterator().next().toString());
692    targetRS =
693      ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
694    Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size());
695    Assert.assertEquals(tableName,
696        ProtobufUtil.getOnlineRegions(targetRS).get(0).getTable());
697  }
698
699  @Test
700  public void testValidGroupNames() throws IOException {
701    String[] badNames = {"foo*","foo@","-"};
702    String[] goodNames = {"foo_123"};
703
704    for(String entry: badNames) {
705      try {
706        rsGroupAdmin.addRSGroup(entry);
707        fail("Expected a constraint exception for: "+entry);
708      } catch(ConstraintException ex) {
709        //expected
710      }
711    }
712
713    for(String entry: goodNames) {
714      rsGroupAdmin.addRSGroup(entry);
715    }
716  }
717
718  private String getGroupName(String baseName) {
719    return groupPrefix+"_"+baseName+"_"+rand.nextInt(Integer.MAX_VALUE);
720  }
721
722  @Test
723  public void testMultiTableMove() throws Exception {
724    final TableName tableNameA = TableName.valueOf(tablePrefix + name.getMethodName() + "A");
725    final TableName tableNameB = TableName.valueOf(tablePrefix + name.getMethodName() + "B");
726    final byte[] familyNameBytes = Bytes.toBytes("f");
727    String newGroupName = getGroupName(name.getMethodName());
728    final RSGroupInfo newGroup = addGroup(newGroupName, 1);
729
730    TEST_UTIL.createTable(tableNameA, familyNameBytes);
731    TEST_UTIL.createTable(tableNameB, familyNameBytes);
732    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
733      @Override
734      public boolean evaluate() throws Exception {
735        List<String> regionsA = getTableRegionMap().get(tableNameA);
736        if (regionsA == null) {
737          return false;
738        }
739
740        List<String> regionsB = getTableRegionMap().get(tableNameB);
741        if (regionsB == null) {
742          return false;
743        }
744
745        return getTableRegionMap().get(tableNameA).size() >= 1
746                && getTableRegionMap().get(tableNameB).size() >= 1;
747      }
748    });
749
750    RSGroupInfo tableGrpA = rsGroupAdmin.getRSGroupInfoOfTable(tableNameA);
751    assertTrue(tableGrpA.getName().equals(RSGroupInfo.DEFAULT_GROUP));
752
753    RSGroupInfo tableGrpB = rsGroupAdmin.getRSGroupInfoOfTable(tableNameB);
754    assertTrue(tableGrpB.getName().equals(RSGroupInfo.DEFAULT_GROUP));
755    //change table's group
756    LOG.info("Moving table [" + tableNameA + "," + tableNameB + "] to " + newGroup.getName());
757    rsGroupAdmin.moveTables(Sets.newHashSet(tableNameA, tableNameB), newGroup.getName());
758
759    //verify group change
760    Assert.assertEquals(newGroup.getName(),
761            rsGroupAdmin.getRSGroupInfoOfTable(tableNameA).getName());
762
763    Assert.assertEquals(newGroup.getName(),
764            rsGroupAdmin.getRSGroupInfoOfTable(tableNameB).getName());
765
766    //verify tables' not exist in old group
767    Set<TableName> DefaultTables = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP)
768        .getTables();
769    assertFalse(DefaultTables.contains(tableNameA));
770    assertFalse(DefaultTables.contains(tableNameB));
771
772    //verify tables' exist in new group
773    Set<TableName> newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroupName).getTables();
774    assertTrue(newGroupTables.contains(tableNameA));
775    assertTrue(newGroupTables.contains(tableNameB));
776  }
777
778  @Test
779  public void testDisabledTableMove() throws Exception {
780    final byte[] familyNameBytes = Bytes.toBytes("f");
781    String newGroupName = getGroupName(name.getMethodName());
782    final RSGroupInfo newGroup = addGroup(newGroupName, 2);
783
784    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
785    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
786      @Override
787      public boolean evaluate() throws Exception {
788        List<String> regions = getTableRegionMap().get(tableName);
789        if (regions == null) {
790          return false;
791        }
792        return getTableRegionMap().get(tableName).size() >= 5;
793      }
794    });
795
796    RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
797    assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
798
799    //test disable table
800    admin.disableTable(tableName);
801
802    //change table's group
803    LOG.info("Moving table "+ tableName + " to " + newGroup.getName());
804    rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
805
806    //verify group change
807    Assert.assertEquals(newGroup.getName(),
808        rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
809  }
810
811  @Test
812  public void testNonExistentTableMove() throws Exception {
813    TableName tableName = TableName.valueOf(tablePrefix + name.getMethodName());
814
815    RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
816    assertNull(tableGrp);
817
818    //test if table exists already.
819    boolean exist = admin.tableExists(tableName);
820    assertFalse(exist);
821
822    LOG.info("Moving table "+ tableName + " to " + RSGroupInfo.DEFAULT_GROUP);
823    try {
824      rsGroupAdmin.moveTables(Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
825      fail("Table " + tableName + " shouldn't have been successfully moved.");
826    } catch(IOException ex) {
827      assertTrue(ex instanceof TableNotFoundException);
828    }
829
830    try {
831      rsGroupAdmin.moveServersAndTables(
832          Sets.newHashSet(Address.fromParts("bogus",123)),
833          Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
834      fail("Table " + tableName + " shouldn't have been successfully moved.");
835    } catch(IOException ex) {
836      assertTrue(ex instanceof TableNotFoundException);
837    }
838    //verify group change
839    assertNull(rsGroupAdmin.getRSGroupInfoOfTable(tableName));
840  }
841
842  @Test
843  public void testMoveServersAndTables() throws Exception {
844    LOG.info("testMoveServersAndTables");
845    final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1);
846    //create table
847    final byte[] familyNameBytes = Bytes.toBytes("f");
848    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
849    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
850      @Override
851      public boolean evaluate() throws Exception {
852        List<String> regions = getTableRegionMap().get(tableName);
853        if (regions == null) {
854          return false;
855        }
856        return getTableRegionMap().get(tableName).size() >= 5;
857      }
858    });
859
860    //get server which is not a member of new group
861    ServerName targetServer = null;
862    for(ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
863      .getLiveServerMetrics().keySet()) {
864      if(!newGroup.containsServer(server.getAddress()) &&
865           !rsGroupAdmin.getRSGroupInfo("master").containsServer(server.getAddress())) {
866        targetServer = server;
867        break;
868      }
869    }
870
871    LOG.debug("Print group info : " + rsGroupAdmin.listRSGroups());
872    int oldDefaultGroupServerSize =
873            rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
874    int oldDefaultGroupTableSize =
875            rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size();
876
877    //test fail bogus server move
878    try {
879      rsGroupAdmin.moveServersAndTables(Sets.newHashSet(Address.fromString("foo:9999")),
880              Sets.newHashSet(tableName), newGroup.getName());
881      fail("Bogus servers shouldn't have been successfully moved.");
882    } catch(IOException ex) {
883      String exp = "Source RSGroup for server foo:9999 does not exist.";
884      String msg = "Expected '" + exp + "' in exception message: ";
885      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
886    }
887
888    //test fail server move
889    try {
890      rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()),
891              Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
892      fail("servers shouldn't have been successfully moved.");
893    } catch(IOException ex) {
894      String exp = "Target RSGroup " + RSGroupInfo.DEFAULT_GROUP +
895              " is same as source " + RSGroupInfo.DEFAULT_GROUP + " RSGroup.";
896      String msg = "Expected '" + exp + "' in exception message: ";
897      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
898    }
899
900    //verify default group info
901    Assert.assertEquals(oldDefaultGroupServerSize,
902            rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size());
903    Assert.assertEquals(oldDefaultGroupTableSize,
904            rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size());
905
906    //verify new group info
907    Assert.assertEquals(1,
908            rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers().size());
909    Assert.assertEquals(0,
910            rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
911
912    //get all region to move targetServer
913    List<String> regionList = getTableRegionMap().get(tableName);
914    for(String region : regionList) {
915      // Lets move this region to the targetServer
916      TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName(Bytes.toBytes(region))),
917              Bytes.toBytes(targetServer.getServerName()));
918    }
919
920    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
921      @Override
922      public boolean evaluate() throws Exception {
923        return getTableRegionMap().get(tableName) != null &&
924                getTableRegionMap().get(tableName).size() == 5 &&
925                getTableServerRegionMap().get(tableName).size() == 1 &&
926                admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION))
927                     .getRegionStatesInTransition().size() < 1;
928      }
929    });
930
931    //verify that all region move to targetServer
932    Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size());
933
934    //move targetServer and table to newGroup
935    LOG.info("moving server and table to newGroup");
936    rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()),
937            Sets.newHashSet(tableName), newGroup.getName());
938
939    //verify group change
940    Assert.assertEquals(newGroup.getName(),
941            rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
942
943    //verify servers' not exist in old group
944    Set<Address> defaultServers = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP)
945        .getServers();
946    assertFalse(defaultServers.contains(targetServer.getAddress()));
947
948    //verify servers' exist in new group
949    Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers();
950    assertTrue(newGroupServers.contains(targetServer.getAddress()));
951
952    //verify tables' not exist in old group
953    Set<TableName> defaultTables = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP)
954        .getTables();
955    assertFalse(defaultTables.contains(tableName));
956
957    //verify tables' exist in new group
958    Set<TableName> newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables();
959    assertTrue(newGroupTables.contains(tableName));
960
961    //verify that all region still assgin on targetServer
962    Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size());
963  }
964
965  @Test
966  public void testClearDeadServers() throws Exception {
967    LOG.info("testClearDeadServers");
968    final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3);
969    NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size();
970
971    ServerName targetServer = ServerName.parseServerName(
972        newGroup.getServers().iterator().next().toString());
973    AdminProtos.AdminService.BlockingInterface targetRS =
974        ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
975    try {
976      targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null,
977          GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName());
978      //stopping may cause an exception
979      //due to the connection loss
980      targetRS.stopServer(null,
981          AdminProtos.StopServerRequest.newBuilder().setReason("Die").build());
982      NUM_DEAD_SERVERS ++;
983    } catch(Exception e) {
984    }
985    //wait for stopped regionserver to dead server list
986    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
987      @Override
988      public boolean evaluate() throws Exception {
989        return !master.getServerManager().areDeadServersInProgress()
990            && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS;
991      }
992    });
993    assertFalse(cluster.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer));
994    assertTrue(cluster.getClusterMetrics().getDeadServerNames().contains(targetServer));
995    assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
996
997    //clear dead servers list
998    List<ServerName> notClearedServers = admin.clearDeadServers(Lists.newArrayList(targetServer));
999    assertEquals(0, notClearedServers.size());
1000
1001    Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers();
1002    assertFalse(newGroupServers.contains(targetServer.getAddress()));
1003    assertEquals(2, newGroupServers.size());
1004  }
1005
1006  @Test
1007  public void testRemoveServers() throws Exception {
1008    LOG.info("testRemoveServers");
1009    final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3);
1010    Iterator<Address> iterator = newGroup.getServers().iterator();
1011    ServerName targetServer = ServerName.parseServerName(iterator.next().toString());
1012
1013    // remove online servers
1014    try {
1015      rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress()));
1016      fail("Online servers shouldn't have been successfully removed.");
1017    } catch(IOException ex) {
1018      String exp = "Server " + targetServer.getAddress()
1019          + " is an online server, not allowed to remove.";
1020      String msg = "Expected '" + exp + "' in exception message: ";
1021      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
1022    }
1023    assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
1024
1025    // remove dead servers
1026    NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size();
1027    AdminProtos.AdminService.BlockingInterface targetRS =
1028        ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
1029    try {
1030      targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null,
1031          GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName());
1032      //stopping may cause an exception
1033      //due to the connection loss
1034      LOG.info("stopping server " + targetServer.getHostAndPort());
1035      targetRS.stopServer(null,
1036          AdminProtos.StopServerRequest.newBuilder().setReason("Die").build());
1037      NUM_DEAD_SERVERS ++;
1038    } catch(Exception e) {
1039    }
1040
1041    //wait for stopped regionserver to dead server list
1042    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
1043      @Override
1044      public boolean evaluate() throws Exception {
1045        return !master.getServerManager().areDeadServersInProgress()
1046            && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS;
1047      }
1048    });
1049
1050    try {
1051      rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress()));
1052      fail("Dead servers shouldn't have been successfully removed.");
1053    } catch(IOException ex) {
1054      String exp = "Server " + targetServer.getAddress() + " is on the dead servers list,"
1055          + " Maybe it will come back again, not allowed to remove.";
1056      String msg = "Expected '" + exp + "' in exception message: ";
1057      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
1058    }
1059    assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
1060
1061    // remove decommissioned servers
1062    List<ServerName> serversToDecommission = new ArrayList<>();
1063    targetServer = ServerName.parseServerName(iterator.next().toString());
1064    targetRS = ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
1065    targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null,
1066          GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName());
1067    assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer));
1068    serversToDecommission.add(targetServer);
1069
1070    admin.decommissionRegionServers(serversToDecommission, true);
1071    assertEquals(1, admin.listDecommissionedRegionServers().size());
1072
1073    assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
1074    rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress()));
1075    Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers();
1076    assertFalse(newGroupServers.contains(targetServer.getAddress()));
1077    assertEquals(2, newGroupServers.size());
1078  }
1079
1080  @Test
1081  public void testCreateWhenRsgroupNoOnlineServers() throws Exception {
1082    LOG.info("testCreateWhenRsgroupNoOnlineServers");
1083
1084    // set rsgroup has no online servers and test create table
1085    final RSGroupInfo appInfo = addGroup("appInfo", 1);
1086    Iterator<Address> iterator = appInfo.getServers().iterator();
1087    List<ServerName> serversToDecommission = new ArrayList<>();
1088    ServerName targetServer = ServerName.parseServerName(iterator.next().toString());
1089    AdminProtos.AdminService.BlockingInterface targetRS =
1090        ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
1091    targetServer = ProtobufUtil.toServerName(
1092        targetRS.getServerInfo(null, GetServerInfoRequest.newBuilder().build()).getServerInfo()
1093            .getServerName());
1094    assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer));
1095    serversToDecommission.add(targetServer);
1096    admin.decommissionRegionServers(serversToDecommission, true);
1097    assertEquals(1, admin.listDecommissionedRegionServers().size());
1098
1099    final TableName tableName = TableName.valueOf(tablePrefix + "_ns", name.getMethodName());
1100    admin.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString())
1101        .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build());
1102    final HTableDescriptor desc = new HTableDescriptor(tableName);
1103    desc.addFamily(new HColumnDescriptor("f"));
1104    try {
1105      admin.createTable(desc);
1106      fail("Shouldn't create table successfully!");
1107    } catch (Exception e) {
1108      LOG.debug("create table error", e);
1109    }
1110
1111    // recommission and test create table
1112    admin.recommissionRegionServer(targetServer, null);
1113    assertEquals(0, admin.listDecommissionedRegionServers().size());
1114    admin.createTable(desc);
1115    // wait for created table to be assigned
1116    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
1117      @Override public boolean evaluate() throws Exception {
1118        return getTableRegionMap().get(desc.getTableName()) != null;
1119      }
1120    });
1121  }
1122}