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