View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.rsgroup;
21  
22  import com.google.common.collect.Maps;
23  import com.google.common.collect.Sets;
24  import com.google.common.net.HostAndPort;
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.hbase.ClusterStatus;
28  import org.apache.hadoop.hbase.HBaseCluster;
29  import org.apache.hadoop.hbase.HBaseTestingUtility;
30  import org.apache.hadoop.hbase.HColumnDescriptor;
31  import org.apache.hadoop.hbase.HRegionInfo;
32  import org.apache.hadoop.hbase.HTableDescriptor;
33  import org.apache.hadoop.hbase.NamespaceDescriptor;
34  import org.apache.hadoop.hbase.RegionLoad;
35  import org.apache.hadoop.hbase.ServerName;
36  import org.apache.hadoop.hbase.TableName;
37  import org.apache.hadoop.hbase.Waiter;
38  import org.apache.hadoop.hbase.client.HBaseAdmin;
39  import org.apache.hadoop.hbase.constraint.ConstraintException;
40  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
41  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
42  import org.apache.hadoop.hbase.util.Bytes;
43  import org.junit.Assert;
44  import org.junit.Test;
45  
46  import java.io.IOException;
47  import java.security.SecureRandom;
48  import java.util.HashSet;
49  import java.util.LinkedList;
50  import java.util.List;
51  import java.util.Map;
52  import java.util.Set;
53  import java.util.TreeMap;
54  import static org.junit.Assert.assertEquals;
55  import static org.junit.Assert.assertFalse;
56  import static org.junit.Assert.assertNull;
57  import static org.junit.Assert.assertTrue;
58  import static org.junit.Assert.fail;
59  
60  public abstract class TestRSGroupsBase {
61    protected static final Log LOG = LogFactory.getLog(TestRSGroupsBase.class);
62  
63    //shared
64    protected final static String groupPrefix = "Group";
65    protected final static String tablePrefix = "Group";
66    protected final static SecureRandom rand = new SecureRandom();
67  
68    //shared, cluster type specific
69    protected static HBaseTestingUtility TEST_UTIL;
70    protected static HBaseAdmin admin;
71    protected static HBaseCluster cluster;
72    protected static RSGroupAdmin rsGroupAdmin;
73  
74    public final static long WAIT_TIMEOUT = 60000*5;
75    public final static int NUM_SLAVES_BASE = 4; //number of slaves for the smallest cluster
76  
77  
78  
79    protected RSGroupInfo addGroup(RSGroupAdmin gAdmin, String groupName,
80                                   int serverCount) throws IOException, InterruptedException {
81      RSGroupInfo defaultInfo = gAdmin
82          .getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
83      assertTrue(defaultInfo != null);
84      assertTrue(defaultInfo.getServers().size() >= serverCount);
85      gAdmin.addRSGroup(groupName);
86  
87      Set<HostAndPort> set = new HashSet<HostAndPort>();
88      for(HostAndPort server: defaultInfo.getServers()) {
89        if(set.size() == serverCount) {
90          break;
91        }
92        set.add(server);
93      }
94      gAdmin.moveServers(set, groupName);
95      RSGroupInfo result = gAdmin.getRSGroupInfo(groupName);
96      assertTrue(result.getServers().size() >= serverCount);
97      return result;
98    }
99  
100   static void removeGroup(RSGroupAdminClient groupAdmin, String groupName) throws IOException {
101     RSGroupInfo RSGroupInfo = groupAdmin.getRSGroupInfo(groupName);
102     groupAdmin.moveTables(RSGroupInfo.getTables(), RSGroupInfo.DEFAULT_GROUP);
103     groupAdmin.moveServers(RSGroupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
104     groupAdmin.removeRSGroup(groupName);
105   }
106 
107   protected void deleteTableIfNecessary() throws IOException {
108     for (HTableDescriptor desc : TEST_UTIL.getHBaseAdmin().listTables(tablePrefix+".*")) {
109       TEST_UTIL.deleteTable(desc.getTableName());
110     }
111   }
112 
113   protected void deleteNamespaceIfNecessary() throws IOException {
114     for (NamespaceDescriptor desc : TEST_UTIL.getHBaseAdmin().listNamespaceDescriptors()) {
115       if(desc.getName().startsWith(tablePrefix)) {
116         admin.deleteNamespace(desc.getName());
117       }
118     }
119   }
120 
121   protected void deleteGroups() throws IOException {
122     RSGroupAdmin groupAdmin = rsGroupAdmin.newClient(TEST_UTIL.getConnection());
123     for(RSGroupInfo group: groupAdmin.listRSGroups()) {
124       if(!group.getName().equals(RSGroupInfo.DEFAULT_GROUP)) {
125         groupAdmin.moveTables(group.getTables(), RSGroupInfo.DEFAULT_GROUP);
126         groupAdmin.moveServers(group.getServers(), RSGroupInfo.DEFAULT_GROUP);
127         groupAdmin.removeRSGroup(group.getName());
128       }
129     }
130   }
131 
132   public Map<TableName, List<String>> getTableRegionMap() throws IOException {
133     Map<TableName, List<String>> map = Maps.newTreeMap();
134     Map<TableName, Map<ServerName, List<String>>> tableServerRegionMap
135         = getTableServerRegionMap();
136     for(TableName tableName : tableServerRegionMap.keySet()) {
137       if(!map.containsKey(tableName)) {
138         map.put(tableName, new LinkedList<String>());
139       }
140       for(List<String> subset: tableServerRegionMap.get(tableName).values()) {
141         map.get(tableName).addAll(subset);
142       }
143     }
144     return map;
145   }
146 
147   public Map<TableName, Map<ServerName, List<String>>> getTableServerRegionMap()
148       throws IOException {
149     Map<TableName, Map<ServerName, List<String>>> map = Maps.newTreeMap();
150     ClusterStatus status = TEST_UTIL.getHBaseClusterInterface().getClusterStatus();
151     for(ServerName serverName : status.getServers()) {
152       for(RegionLoad rl : status.getLoad(serverName).getRegionsLoad().values()) {
153         TableName tableName = HRegionInfo.getTable(rl.getName());
154         if(!map.containsKey(tableName)) {
155           map.put(tableName, new TreeMap<ServerName, List<String>>());
156         }
157         if(!map.get(tableName).containsKey(serverName)) {
158           map.get(tableName).put(serverName, new LinkedList<String>());
159         }
160         map.get(tableName).get(serverName).add(rl.getNameAsString());
161       }
162     }
163     return map;
164   }
165 
166   @Test
167   public void testBogusArgs() throws Exception {
168     assertNull(rsGroupAdmin.getRSGroupInfoOfTable(TableName.valueOf("nonexistent")));
169     assertNull(rsGroupAdmin.getRSGroupOfServer(HostAndPort.fromParts("bogus",123)));
170     assertNull(rsGroupAdmin.getRSGroupInfo("bogus"));
171 
172     try {
173       rsGroupAdmin.removeRSGroup("bogus");
174       fail("Expected removing bogus group to fail");
175     } catch(ConstraintException ex) {
176       //expected
177     }
178 
179     try {
180       rsGroupAdmin.moveTables(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus");
181       fail("Expected move with bogus group to fail");
182     } catch(ConstraintException ex) {
183       //expected
184     }
185 
186     try {
187       rsGroupAdmin.moveServers(Sets.newHashSet(HostAndPort.fromParts("bogus",123)), "bogus");
188       fail("Expected move with bogus group to fail");
189     } catch(ConstraintException ex) {
190       //expected
191     }
192 
193     try {
194       rsGroupAdmin.balanceRSGroup("bogus");
195       fail("Expected move with bogus group to fail");
196     } catch(ConstraintException ex) {
197       //expected
198     }
199   }
200 
201   @Test
202   public void testCreateMultiRegion() throws IOException {
203     LOG.info("testCreateMultiRegion");
204     TableName tableName = TableName.valueOf(tablePrefix + "_testCreateMultiRegion");
205     byte[] end = {1,3,5,7,9};
206     byte[] start = {0,2,4,6,8};
207     byte[][] f = {Bytes.toBytes("f")};
208     TEST_UTIL.createTable(tableName, f,1,start,end,10);
209   }
210 
211   @Test
212   public void testCreateAndDrop() throws Exception {
213     LOG.info("testCreateAndDrop");
214 
215     final TableName tableName = TableName.valueOf(tablePrefix + "_testCreateAndDrop");
216     TEST_UTIL.createTable(tableName, Bytes.toBytes("cf"));
217     //wait for created table to be assigned
218     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
219       @Override
220       public boolean evaluate() throws Exception {
221         return getTableRegionMap().get(tableName) != null;
222       }
223     });
224     TEST_UTIL.deleteTable(tableName);
225   }
226 
227 
228   @Test
229   public void testSimpleRegionServerMove() throws IOException,
230       InterruptedException {
231     LOG.info("testSimpleRegionServerMove");
232 
233     int initNumGroups = rsGroupAdmin.listRSGroups().size();
234     RSGroupInfo appInfo = addGroup(rsGroupAdmin, getGroupName("testSimpleRegionServerMove"), 1);
235     RSGroupInfo adminInfo = addGroup(rsGroupAdmin, getGroupName("testSimpleRegionServerMove"), 1);
236     RSGroupInfo dInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
237     Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size());
238     assertEquals(1, adminInfo.getServers().size());
239     assertEquals(1, appInfo.getServers().size());
240     assertEquals(getNumServers() - 2, dInfo.getServers().size());
241     rsGroupAdmin.moveServers(appInfo.getServers(),
242         RSGroupInfo.DEFAULT_GROUP);
243     rsGroupAdmin.removeRSGroup(appInfo.getName());
244     rsGroupAdmin.moveServers(adminInfo.getServers(),
245         RSGroupInfo.DEFAULT_GROUP);
246     rsGroupAdmin.removeRSGroup(adminInfo.getName());
247     Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups);
248   }
249 
250   // return the real number of region servers, excluding the master embedded region server in 2.0+
251   public int getNumServers() throws IOException {
252     ClusterStatus status = admin.getClusterStatus();
253     ServerName master = status.getMaster();
254     int count = 0;
255     for (ServerName sn : status.getServers()) {
256       if (!sn.equals(master)) {
257         count++;
258       }
259     }
260     return count;
261   }
262 
263   @Test
264   public void testMoveServers() throws Exception {
265     LOG.info("testMoveServers");
266 
267     //create groups and assign servers
268     addGroup(rsGroupAdmin, "bar", 3);
269     rsGroupAdmin.addRSGroup("foo");
270 
271     RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
272     RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
273     assertEquals(3, barGroup.getServers().size());
274     assertEquals(0, fooGroup.getServers().size());
275 
276     //test fail bogus server move
277     try {
278       rsGroupAdmin.moveServers(Sets.newHashSet(HostAndPort.fromString("foo:9999")),"foo");
279       fail("Bogus servers shouldn't have been successfully moved.");
280     } catch(IOException ex) {
281       String exp = "Server foo:9999 does not have a group.";
282       String msg = "Expected '"+exp+"' in exception message: ";
283       assertTrue(msg+" "+ex.getMessage(), ex.getMessage().contains(exp));
284     }
285 
286     //test success case
287     LOG.info("moving servers "+barGroup.getServers()+" to group foo");
288     rsGroupAdmin.moveServers(barGroup.getServers(), fooGroup.getName());
289 
290     barGroup = rsGroupAdmin.getRSGroupInfo("bar");
291     fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
292     assertEquals(0,barGroup.getServers().size());
293     assertEquals(3,fooGroup.getServers().size());
294 
295     LOG.info("moving servers "+fooGroup.getServers()+" to group default");
296     rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
297 
298     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
299       @Override
300       public boolean evaluate() throws Exception {
301         return getNumServers() ==
302         rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
303       }
304     });
305 
306     fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
307     assertEquals(0,fooGroup.getServers().size());
308 
309     //test group removal
310     LOG.info("Remove group "+barGroup.getName());
311     rsGroupAdmin.removeRSGroup(barGroup.getName());
312     Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(barGroup.getName()));
313     LOG.info("Remove group "+fooGroup.getName());
314     rsGroupAdmin.removeRSGroup(fooGroup.getName());
315     Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName()));
316   }
317 
318   @Test
319   public void testTableMoveTruncateAndDrop() throws Exception {
320     LOG.info("testTableMove");
321 
322     final TableName tableName = TableName.valueOf(tablePrefix + "_testTableMoveAndDrop");
323     final byte[] familyNameBytes = Bytes.toBytes("f");
324     String newGroupName = getGroupName("testTableMove");
325     final RSGroupInfo newGroup = addGroup(rsGroupAdmin, newGroupName, 2);
326 
327     TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
328     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
329       @Override
330       public boolean evaluate() throws Exception {
331         List<String> regions = getTableRegionMap().get(tableName);
332         if (regions == null)
333           return false;
334         return getTableRegionMap().get(tableName).size() >= 5;
335       }
336     });
337 
338     RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
339     assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
340 
341     //change table's group
342     LOG.info("Moving table "+tableName+" to "+newGroup.getName());
343     rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
344 
345     //verify group change
346     Assert.assertEquals(newGroup.getName(),
347         rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
348 
349     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
350       @Override
351       public boolean evaluate() throws Exception {
352         Map<ServerName, List<String>> serverMap = getTableServerRegionMap().get(tableName);
353         int count = 0;
354         if (serverMap != null) {
355           for (ServerName rs : serverMap.keySet()) {
356             if (newGroup.containsServer(rs.getHostPort())) {
357               count += serverMap.get(rs).size();
358             }
359           }
360         }
361         return count == 5;
362       }
363     });
364 
365     //test truncate
366     admin.disableTable(tableName);
367     admin.truncateTable(tableName, true);
368     Assert.assertEquals(1, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
369     Assert.assertEquals(tableName, rsGroupAdmin.getRSGroupInfo(
370         newGroup.getName()).getTables().first());
371 
372     //verify removed table is removed from group
373     TEST_UTIL.deleteTable(tableName);
374     Assert.assertEquals(0, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
375   }
376 
377   @Test
378   public void testGroupBalance() throws Exception {
379     LOG.info("testGroupBalance");
380     String newGroupName = getGroupName("testGroupBalance");
381     final RSGroupInfo newGroup = addGroup(rsGroupAdmin, newGroupName, 3);
382 
383     final TableName tableName = TableName.valueOf(tablePrefix+"_ns", "testGroupBalance");
384     admin.createNamespace(
385         NamespaceDescriptor.create(tableName.getNamespaceAsString())
386             .addConfiguration(RSGroupInfo.NAMESPACEDESC_PROP_GROUP, newGroupName).build());
387     final byte[] familyNameBytes = Bytes.toBytes("f");
388     final HTableDescriptor desc = new HTableDescriptor(tableName);
389     desc.addFamily(new HColumnDescriptor("f"));
390     byte [] startKey = Bytes.toBytes("aaaaa");
391     byte [] endKey = Bytes.toBytes("zzzzz");
392     admin.createTable(desc, startKey, endKey, 6);
393     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
394       @Override
395       public boolean evaluate() throws Exception {
396         List<String> regions = getTableRegionMap().get(tableName);
397         if (regions == null) {
398           return false;
399         }
400         return regions.size() >= 6;
401       }
402     });
403 
404     //make assignment uneven, move all regions to one server
405     Map<ServerName,List<String>> assignMap =
406         getTableServerRegionMap().get(tableName);
407     final ServerName first = assignMap.entrySet().iterator().next().getKey();
408     for(HRegionInfo region: admin.getTableRegions(tableName)) {
409       if(!assignMap.get(first).contains(region)) {
410         admin.move(region.getEncodedNameAsBytes(), Bytes.toBytes(first.getServerName()));
411       }
412     }
413     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
414       @Override
415       public boolean evaluate() throws Exception {
416         Map<ServerName, List<String>> map = getTableServerRegionMap().get(tableName);
417         if (map == null) {
418           return true;
419         }
420         List<String> regions = map.get(first);
421         if (regions == null) {
422           return true;
423         }
424         return regions.size() >= 6;
425       }
426     });
427 
428     //balance the other group and make sure it doesn't affect the new group
429     rsGroupAdmin.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP);
430     assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size());
431 
432     rsGroupAdmin.balanceRSGroup(newGroupName);
433     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
434       @Override
435       public boolean evaluate() throws Exception {
436         for (List<String> regions : getTableServerRegionMap().get(tableName).values()) {
437           if (2 != regions.size()) {
438             return false;
439           }
440         }
441         return true;
442       }
443     });
444   }
445 
446   @Test
447   public void testRegionMove() throws Exception {
448     LOG.info("testRegionMove");
449 
450     final RSGroupInfo newGroup = addGroup(rsGroupAdmin, getGroupName("testRegionMove"), 1);
451     final TableName tableName = TableName.valueOf(tablePrefix + rand.nextInt());
452     final byte[] familyNameBytes = Bytes.toBytes("f");
453     // All the regions created below will be assigned to the default group.
454     TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 6);
455     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
456       @Override
457       public boolean evaluate() throws Exception {
458         List<String> regions = getTableRegionMap().get(tableName);
459         if (regions == null)
460           return false;
461         return getTableRegionMap().get(tableName).size() >= 6;
462       }
463     });
464 
465     //get target region to move
466     Map<ServerName,List<String>> assignMap =
467         getTableServerRegionMap().get(tableName);
468     String targetRegion = null;
469     for(ServerName server : assignMap.keySet()) {
470       targetRegion = assignMap.get(server).size() > 0 ? assignMap.get(server).get(0) : null;
471       if(targetRegion != null) {
472         break;
473       }
474     }
475     //get server which is not a member of new group
476     ServerName targetServer = null;
477     for(ServerName server : admin.getClusterStatus().getServers()) {
478       if(!newGroup.containsServer(server.getHostPort())) {
479         targetServer = server;
480         break;
481       }
482     }
483 
484     final AdminProtos.AdminService.BlockingInterface targetRS =
485         admin.getConnection().getAdmin(targetServer);
486 
487     //move target server to group
488     rsGroupAdmin.moveServers(Sets.newHashSet(targetServer.getHostPort()),
489         newGroup.getName());
490     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
491       @Override
492       public boolean evaluate() throws Exception {
493         return ProtobufUtil.getOnlineRegions(targetRS).size() <= 0;
494       }
495     });
496 
497     // Lets move this region to the new group.
498     TEST_UTIL.getHBaseAdmin().move(Bytes.toBytes(HRegionInfo.encodeRegionName(Bytes.toBytes(targetRegion))),
499         Bytes.toBytes(targetServer.getServerName()));
500     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
501       @Override
502       public boolean evaluate() throws Exception {
503         return
504             getTableRegionMap().get(tableName) != null &&
505                 getTableRegionMap().get(tableName).size() == 6 &&
506                 admin.getClusterStatus().getRegionsInTransition().size() < 1;
507       }
508     });
509 
510     //verify that targetServer didn't open it
511     assertFalse(ProtobufUtil.getOnlineRegions(targetRS).contains(targetRegion));
512   }
513 
514   @Test
515   public void testFailRemoveGroup() throws IOException, InterruptedException {
516     LOG.info("testFailRemoveGroup");
517 
518     int initNumGroups = rsGroupAdmin.listRSGroups().size();
519     addGroup(rsGroupAdmin, "bar", 3);
520     TableName tableName = TableName.valueOf(tablePrefix+"_my_table");
521     TEST_UTIL.createTable(tableName, Bytes.toBytes("f"));
522     rsGroupAdmin.moveTables(Sets.newHashSet(tableName), "bar");
523     RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
524     //group is not empty therefore it should fail
525     try {
526       rsGroupAdmin.removeRSGroup(barGroup.getName());
527       fail("Expected remove group to fail");
528     } catch(IOException e) {
529     }
530     //group cannot lose all it's servers therefore it should fail
531     try {
532       rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
533       fail("Expected move servers to fail");
534     } catch(IOException e) {
535     }
536 
537     rsGroupAdmin.moveTables(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP);
538     try {
539       rsGroupAdmin.removeRSGroup(barGroup.getName());
540       fail("Expected move servers to fail");
541     } catch(IOException e) {
542     }
543 
544     rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
545     rsGroupAdmin.removeRSGroup(barGroup.getName());
546 
547     Assert.assertEquals(initNumGroups, rsGroupAdmin.listRSGroups().size());
548   }
549 
550   @Test
551   public void testKillRS() throws Exception {
552     LOG.info("testKillRS");
553     RSGroupInfo appInfo = addGroup(rsGroupAdmin, "appInfo", 1);
554 
555 
556     final TableName tableName = TableName.valueOf(tablePrefix+"_ns", "_testKillRS");
557     admin.createNamespace(
558         NamespaceDescriptor.create(tableName.getNamespaceAsString())
559             .addConfiguration(RSGroupInfo.NAMESPACEDESC_PROP_GROUP, appInfo.getName()).build());
560     final HTableDescriptor desc = new HTableDescriptor(tableName);
561     desc.addFamily(new HColumnDescriptor("f"));
562     admin.createTable(desc);
563     //wait for created table to be assigned
564     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
565       @Override
566       public boolean evaluate() throws Exception {
567         return getTableRegionMap().get(desc.getTableName()) != null;
568       }
569     });
570 
571     ServerName targetServer = ServerName.parseServerName(
572         appInfo.getServers().iterator().next().toString());
573     AdminProtos.AdminService.BlockingInterface targetRS =
574         admin.getConnection().getAdmin(targetServer);
575     HRegionInfo targetRegion = ProtobufUtil.getOnlineRegions(targetRS).get(0);
576     Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size());
577 
578     try {
579       //stopping may cause an exception
580       //due to the connection loss
581       targetRS.stopServer(null,
582           AdminProtos.StopServerRequest.newBuilder().setReason("Die").build());
583     } catch(Exception e) {
584     }
585     assertFalse(cluster.getClusterStatus().getServers().contains(targetServer));
586 
587     //wait for created table to be assigned
588     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
589       @Override
590       public boolean evaluate() throws Exception {
591         return cluster.getClusterStatus().getRegionsInTransition().size() == 0;
592       }
593     });
594     Set<HostAndPort> newServers = Sets.newHashSet();
595     newServers.add(
596         rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().iterator().next());
597     rsGroupAdmin.moveServers(newServers, appInfo.getName());
598 
599     //Make sure all the table's regions get reassigned
600     //disabling the table guarantees no conflicting assign/unassign (ie SSH) happens
601     admin.disableTable(tableName);
602     admin.enableTable(tableName);
603 
604     //wait for region to be assigned
605     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
606       @Override
607       public boolean evaluate() throws Exception {
608         return cluster.getClusterStatus().getRegionsInTransition().size() == 0;
609       }
610     });
611 
612     targetServer = ServerName.parseServerName(
613         newServers.iterator().next().toString());
614     targetRS =
615         admin.getConnection().getAdmin(targetServer);
616     Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size());
617     Assert.assertEquals(tableName,
618         ProtobufUtil.getOnlineRegions(targetRS).get(0).getTable());
619   }
620 
621   @Test
622   public void testValidGroupNames() throws IOException {
623     String[] badNames = {"foo*","foo@","-"};
624     String[] goodNames = {"foo_123"};
625 
626     for(String entry: badNames) {
627       try {
628         rsGroupAdmin.addRSGroup(entry);
629         fail("Expected a constraint exception for: "+entry);
630       } catch(ConstraintException ex) {
631         //expected
632       }
633     }
634 
635     for(String entry: goodNames) {
636       rsGroupAdmin.addRSGroup(entry);
637     }
638   }
639 
640   private String getGroupName(String baseName) {
641     return groupPrefix+"_"+baseName+"_"+rand.nextInt(Integer.MAX_VALUE);
642   }
643 }