You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ap...@apache.org on 2018/10/05 00:20:12 UTC

[6/9] hbase git commit: HBASE-21265 Split up TestRSGroups

HBASE-21265 Split up TestRSGroups

Signed-off-by: Ted Yu <ty...@apache.org>

Conflicts:
	hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java
        hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java
	hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/24d19e87
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/24d19e87
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/24d19e87

Branch: refs/heads/branch-2
Commit: 24d19e87e0af9621f8e2f0d3940ba254fb2432a1
Parents: 9e3f3fd
Author: Andrew Purtell <ap...@apache.org>
Authored: Wed Oct 3 16:13:09 2018 -0700
Committer: Andrew Purtell <ap...@apache.org>
Committed: Thu Oct 4 16:24:28 2018 -0700

----------------------------------------------------------------------
 .../hadoop/hbase/rsgroup/TestEnableRSGroup.java |   95 --
 .../hbase/rsgroup/TestEnableRSGroups.java       |   96 ++
 .../hadoop/hbase/rsgroup/TestRSGroups.java      |  547 ---------
 .../hbase/rsgroup/TestRSGroupsAdmin1.java       |  441 +++++++
 .../hbase/rsgroup/TestRSGroupsAdmin2.java       |  487 ++++++++
 .../hbase/rsgroup/TestRSGroupsBalance.java      |  188 +++
 .../hadoop/hbase/rsgroup/TestRSGroupsBase.java  | 1126 ++++--------------
 .../hbase/rsgroup/TestRSGroupsBasics.java       |  320 +++++
 .../hbase/rsgroup/TestRSGroupsKillRS.java       |  149 +++
 .../hbase/rsgroup/TestRSGroupsOfflineMode.java  |    1 +
 .../hbase/rsgroup/TestRSGroupsWithACL.java      |    4 +-
 11 files changed, 1899 insertions(+), 1555 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/24d19e87/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroup.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroup.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroup.java
deleted file mode 100644
index 77faec7..0000000
--- a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroup.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.hadoop.hbase.rsgroup;
-
-import static java.lang.Thread.sleep;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.HBaseClassTestRule;
-import org.apache.hadoop.hbase.HBaseTestingUtility;
-import org.apache.hadoop.hbase.HConstants;
-import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
-import org.apache.hadoop.hbase.testclassification.MediumTests;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Test enable RSGroup
- */
-@Category({ MediumTests.class })
-public class TestEnableRSGroup {
-
-  @ClassRule
-  public static final HBaseClassTestRule CLASS_RULE =
-      HBaseClassTestRule.forClass(TestEnableRSGroup.class);
-
-  protected static final Logger LOG = LoggerFactory.getLogger(TestEnableRSGroup.class);
-
-  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
-
-  @BeforeClass
-  public static void setUp() throws Exception {
-    final Configuration conf = TEST_UTIL.getConfiguration();
-    conf.setBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, true);
-    TEST_UTIL.startMiniCluster(5);
-  }
-
-  @AfterClass
-  public static void tearDown() throws Exception {
-    LOG.info("to stop miniCluster");
-    TEST_UTIL.shutdownMiniCluster();
-  }
-
-  @Test
-  public void testEnableRSGroup() throws IOException, InterruptedException {
-    TEST_UTIL.getMiniHBaseCluster().stopMaster(0);
-    TEST_UTIL.getMiniHBaseCluster().waitOnMaster(0);
-
-    LOG.info("stopped master...");
-    final Configuration conf = TEST_UTIL.getMiniHBaseCluster().getConfiguration();
-    conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, RSGroupAdminEndpoint.class.getName());
-    conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, RSGroupBasedLoadBalancer.class.getName());
-
-    TEST_UTIL.getMiniHBaseCluster().startMaster();
-    TEST_UTIL.getMiniHBaseCluster().waitForActiveAndReadyMaster(60000);
-    LOG.info("started master...");
-
-    // check if master started successfully
-    assertTrue(TEST_UTIL.getMiniHBaseCluster().getMaster() != null);
-
-    // wait RSGroupBasedLoadBalancer online
-    RSGroupBasedLoadBalancer loadBalancer =
-        (RSGroupBasedLoadBalancer) TEST_UTIL.getMiniHBaseCluster().getMaster().getLoadBalancer();
-    long start = System.currentTimeMillis();
-    while (System.currentTimeMillis() - start <= 60000 && !loadBalancer.isOnline()) {
-      LOG.info("waiting for rsgroup load balancer onLine...");
-      sleep(200);
-    }
-
-    assertTrue(loadBalancer.isOnline());
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/hbase/blob/24d19e87/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java
new file mode 100644
index 0000000..d15c7a8
--- /dev/null
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import static java.lang.Thread.sleep;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test enable RSGroup
+ */
+@Category({ MediumTests.class })
+public class TestEnableRSGroups {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+      HBaseClassTestRule.forClass(TestEnableRSGroups.class);
+
+  protected static final Logger LOG = LoggerFactory.getLogger(TestEnableRSGroups.class);
+
+  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    final Configuration conf = TEST_UTIL.getConfiguration();
+    conf.setBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, true);
+    TEST_UTIL.startMiniCluster(5);
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    LOG.info("to stop miniCluster");
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+  @Test
+  public void testEnableRSGroup() throws IOException, InterruptedException {
+    TEST_UTIL.getMiniHBaseCluster().stopMaster(0);
+    TEST_UTIL.getMiniHBaseCluster().waitOnMaster(0);
+
+    LOG.info("stopped master...");
+    final Configuration conf = TEST_UTIL.getMiniHBaseCluster().getConfiguration();
+    conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, RSGroupAdminEndpoint.class.getName());
+    conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
+      RSGroupBasedLoadBalancer.class.getName());
+
+    TEST_UTIL.getMiniHBaseCluster().startMaster();
+    TEST_UTIL.getMiniHBaseCluster().waitForActiveAndReadyMaster(60000);
+    LOG.info("started master...");
+
+    // check if master started successfully
+    assertTrue(TEST_UTIL.getMiniHBaseCluster().getMaster() != null);
+
+    // wait RSGroupBasedLoadBalancer online
+    RSGroupBasedLoadBalancer loadBalancer =
+        (RSGroupBasedLoadBalancer) TEST_UTIL.getMiniHBaseCluster().getMaster().getLoadBalancer();
+    long start = System.currentTimeMillis();
+    while (System.currentTimeMillis() - start <= 60000 && !loadBalancer.isOnline()) {
+      LOG.info("waiting for rsgroup load balancer onLine...");
+      sleep(200);
+    }
+
+    assertTrue(loadBalancer.isOnline());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/24d19e87/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java
deleted file mode 100644
index 02acca9..0000000
--- a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java
+++ /dev/null
@@ -1,547 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.hadoop.hbase.rsgroup;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-
-import org.apache.hadoop.hbase.HBaseClassTestRule;
-import org.apache.hadoop.hbase.HBaseTestingUtility;
-import org.apache.hadoop.hbase.HColumnDescriptor;
-import org.apache.hadoop.hbase.HConstants;
-import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.MiniHBaseCluster;
-import org.apache.hadoop.hbase.NamespaceDescriptor;
-import org.apache.hadoop.hbase.ServerName;
-import org.apache.hadoop.hbase.TableName;
-import org.apache.hadoop.hbase.Waiter;
-import org.apache.hadoop.hbase.Waiter.Predicate;
-import org.apache.hadoop.hbase.client.ClusterConnection;
-import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
-import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
-import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
-import org.apache.hadoop.hbase.coprocessor.MasterObserver;
-import org.apache.hadoop.hbase.coprocessor.ObserverContext;
-import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
-import org.apache.hadoop.hbase.master.ServerManager;
-import org.apache.hadoop.hbase.master.TableNamespaceManager;
-import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
-import org.apache.hadoop.hbase.net.Address;
-import org.apache.hadoop.hbase.quotas.QuotaTableUtil;
-import org.apache.hadoop.hbase.quotas.QuotaUtil;
-import org.apache.hadoop.hbase.testclassification.MediumTests;
-import org.apache.hadoop.hbase.util.Bytes;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
-
-import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
-import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos;
-
-@Category({MediumTests.class})
-public class TestRSGroups extends TestRSGroupsBase {
-
-  @ClassRule
-  public static final HBaseClassTestRule CLASS_RULE =
-      HBaseClassTestRule.forClass(TestRSGroups.class);
-
-  protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroups.class);
-  private static boolean INIT = false;
-  private static RSGroupAdminEndpoint rsGroupAdminEndpoint;
-  private static CPMasterObserver observer;
-
-  @BeforeClass
-  public static void setUp() throws Exception {
-    TEST_UTIL = new HBaseTestingUtility();
-    TEST_UTIL.getConfiguration().setFloat(
-            "hbase.master.balancer.stochastic.tableSkewCost", 6000);
-    TEST_UTIL.getConfiguration().set(
-        HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
-        RSGroupBasedLoadBalancer.class.getName());
-    TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
-        RSGroupAdminEndpoint.class.getName() + "," + CPMasterObserver.class.getName());
-    TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1);
-    TEST_UTIL.getConfiguration().setInt(
-        ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
-        NUM_SLAVES_BASE - 1);
-    TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
-    initialize();
-  }
-
-  private static void initialize() throws Exception {
-    admin = TEST_UTIL.getAdmin();
-    cluster = TEST_UTIL.getHBaseCluster();
-    master = ((MiniHBaseCluster)cluster).getMaster();
-
-    //wait for balancer to come online
-    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
-      @Override
-      public boolean evaluate() throws Exception {
-        return master.isInitialized() &&
-            ((RSGroupBasedLoadBalancer) master.getLoadBalancer()).isOnline();
-      }
-    });
-    admin.setBalancerRunning(false,true);
-    rsGroupAdmin = new VerifyingRSGroupAdminClient(
-        new RSGroupAdminClient(TEST_UTIL.getConnection()), TEST_UTIL.getConfiguration());
-    MasterCoprocessorHost host = master.getMasterCoprocessorHost();
-    observer = (CPMasterObserver) host.findCoprocessor(CPMasterObserver.class.getName());
-    rsGroupAdminEndpoint = (RSGroupAdminEndpoint)
-        host.findCoprocessor(RSGroupAdminEndpoint.class.getName());
-  }
-
-  @AfterClass
-  public static void tearDown() throws Exception {
-    TEST_UTIL.shutdownMiniCluster();
-  }
-
-  @Before
-  public void beforeMethod() throws Exception {
-    if (!INIT) {
-      INIT = true;
-      afterMethod();
-    }
-    observer.resetFlags();
-  }
-
-  @After
-  public void afterMethod() throws Exception {
-    deleteTableIfNecessary();
-    deleteNamespaceIfNecessary();
-    deleteGroups();
-
-    for(ServerName sn : admin.listDecommissionedRegionServers()){
-      admin.recommissionRegionServer(sn, null);
-    }
-    assertTrue(admin.listDecommissionedRegionServers().isEmpty());
-
-    int missing = NUM_SLAVES_BASE - getNumServers();
-    LOG.info("Restoring servers: "+missing);
-    for(int i=0; i<missing; i++) {
-      ((MiniHBaseCluster)cluster).startRegionServer();
-    }
-
-    rsGroupAdmin.addRSGroup("master");
-    ServerName masterServerName =
-        ((MiniHBaseCluster)cluster).getMaster().getServerName();
-
-    try {
-      rsGroupAdmin.moveServers(Sets.newHashSet(masterServerName.getAddress()), "master");
-    } catch (Exception ex) {
-      LOG.warn("Got this on setup, FYI", ex);
-    }
-    assertTrue(observer.preMoveServersCalled);
-    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
-      @Override
-      public boolean evaluate() throws Exception {
-        LOG.info("Waiting for cleanup to finish " + rsGroupAdmin.listRSGroups());
-        //Might be greater since moving servers back to default
-        //is after starting a server
-
-        return rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()
-            == NUM_SLAVES_BASE;
-      }
-    });
-  }
-
-  @Test
-  public void testBasicStartUp() throws IOException {
-    RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
-    assertEquals(4, defaultInfo.getServers().size());
-    // Assignment of root and meta regions.
-    int count = master.getAssignmentManager().getRegionStates().getRegionAssignments().size();
-    //3 meta,namespace, group
-    assertEquals(3, count);
-  }
-
-  @Test
-  public void testNamespaceCreateAndAssign() throws Exception {
-    LOG.info("testNamespaceCreateAndAssign");
-    String nsName = tablePrefix+"_foo";
-    final TableName tableName = TableName.valueOf(nsName, tablePrefix + "_testCreateAndAssign");
-    RSGroupInfo appInfo = addGroup("appInfo", 1);
-    admin.createNamespace(NamespaceDescriptor.create(nsName)
-        .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "appInfo").build());
-    final HTableDescriptor desc = new HTableDescriptor(tableName);
-    desc.addFamily(new HColumnDescriptor("f"));
-    admin.createTable(desc);
-    //wait for created table to be assigned
-    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
-      @Override
-      public boolean evaluate() throws Exception {
-        return getTableRegionMap().get(desc.getTableName()) != null;
-      }
-    });
-    ServerName targetServer =
-        ServerName.parseServerName(appInfo.getServers().iterator().next().toString());
-    AdminProtos.AdminService.BlockingInterface rs =
-      ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
-    //verify it was assigned to the right group
-    Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(rs).size());
-  }
-
-  @Test
-  public void testDefaultNamespaceCreateAndAssign() throws Exception {
-    LOG.info("testDefaultNamespaceCreateAndAssign");
-    String tableName = tablePrefix + "_testCreateAndAssign";
-    admin.modifyNamespace(NamespaceDescriptor.create("default")
-        .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "default").build());
-    final HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
-    desc.addFamily(new HColumnDescriptor("f"));
-    admin.createTable(desc);
-    //wait for created table to be assigned
-    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
-      @Override
-      public boolean evaluate() throws Exception {
-        return getTableRegionMap().get(desc.getTableName()) != null;
-      }
-    });
-  }
-
-  @Test
-  public void testNamespaceConstraint() throws Exception {
-    String nsName = tablePrefix+"_foo";
-    String groupName = tablePrefix+"_foo";
-    LOG.info("testNamespaceConstraint");
-    rsGroupAdmin.addRSGroup(groupName);
-    assertTrue(observer.preAddRSGroupCalled);
-    assertTrue(observer.postAddRSGroupCalled);
-
-    admin.createNamespace(NamespaceDescriptor.create(nsName)
-        .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
-        .build());
-    //test removing a referenced group
-    try {
-      rsGroupAdmin.removeRSGroup(groupName);
-      fail("Expected a constraint exception");
-    } catch (IOException ex) {
-    }
-    //test modify group
-    //changing with the same name is fine
-    admin.modifyNamespace(
-        NamespaceDescriptor.create(nsName)
-          .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
-          .build());
-    String anotherGroup = tablePrefix+"_anotherGroup";
-    rsGroupAdmin.addRSGroup(anotherGroup);
-    //test add non-existent group
-    admin.deleteNamespace(nsName);
-    rsGroupAdmin.removeRSGroup(groupName);
-    assertTrue(observer.preRemoveRSGroupCalled);
-    assertTrue(observer.postRemoveRSGroupCalled);
-    try {
-      admin.createNamespace(NamespaceDescriptor.create(nsName)
-          .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "foo")
-          .build());
-      fail("Expected a constraint exception");
-    } catch (IOException ex) {
-    }
-  }
-
-  @Test
-  public void testGroupInfoMultiAccessing() throws Exception {
-    RSGroupInfoManager manager = rsGroupAdminEndpoint.getGroupInfoManager();
-    RSGroupInfo defaultGroup = manager.getRSGroup("default");
-    // getRSGroup updates default group's server list
-    // this process must not affect other threads iterating the list
-    Iterator<Address> it = defaultGroup.getServers().iterator();
-    manager.getRSGroup("default");
-    it.next();
-  }
-
-  public static class CPMasterObserver implements MasterCoprocessor, MasterObserver {
-    boolean preBalanceRSGroupCalled = false;
-    boolean postBalanceRSGroupCalled = false;
-    boolean preMoveServersCalled = false;
-    boolean postMoveServersCalled = false;
-    boolean preMoveTablesCalled = false;
-    boolean postMoveTablesCalled = false;
-    boolean preAddRSGroupCalled = false;
-    boolean postAddRSGroupCalled = false;
-    boolean preRemoveRSGroupCalled = false;
-    boolean postRemoveRSGroupCalled = false;
-    boolean preRemoveServersCalled = false;
-    boolean postRemoveServersCalled = false;
-    boolean preMoveServersAndTables = false;
-    boolean postMoveServersAndTables = false;
-
-    void resetFlags() {
-      preBalanceRSGroupCalled = false;
-      postBalanceRSGroupCalled = false;
-      preMoveServersCalled = false;
-      postMoveServersCalled = false;
-      preMoveTablesCalled = false;
-      postMoveTablesCalled = false;
-      preAddRSGroupCalled = false;
-      postAddRSGroupCalled = false;
-      preRemoveRSGroupCalled = false;
-      postRemoveRSGroupCalled = false;
-      preRemoveServersCalled = false;
-      postRemoveServersCalled = false;
-      preMoveServersAndTables = false;
-      postMoveServersAndTables = false;
-    }
-
-    @Override
-    public Optional<MasterObserver> getMasterObserver() {
-      return Optional.of(this);
-    }
-    @Override
-    public void preMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
-      preMoveServersAndTables = true;
-    }
-    @Override
-    public void postMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
-      postMoveServersAndTables = true;
-    }
-    @Override
-    public void preRemoveServers(
-        final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<Address> servers) throws IOException {
-      preRemoveServersCalled = true;
-    }
-    @Override
-    public void postRemoveServers(
-        final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<Address> servers) throws IOException {
-      postRemoveServersCalled = true;
-    }
-    @Override
-    public void preRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String name) throws IOException {
-      preRemoveRSGroupCalled = true;
-    }
-    @Override
-    public void postRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String name) throws IOException {
-      postRemoveRSGroupCalled = true;
-    }
-    @Override
-    public void preAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String name) throws IOException {
-      preAddRSGroupCalled = true;
-    }
-    @Override
-    public void postAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String name) throws IOException {
-      postAddRSGroupCalled = true;
-    }
-    @Override
-    public void preMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<TableName> tables, String targetGroup) throws IOException {
-      preMoveTablesCalled = true;
-    }
-    @Override
-    public void postMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<TableName> tables, String targetGroup) throws IOException {
-      postMoveTablesCalled = true;
-    }
-    @Override
-    public void preMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<Address> servers, String targetGroup) throws IOException {
-      preMoveServersCalled = true;
-    }
-
-    @Override
-    public void postMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<Address> servers, String targetGroup) throws IOException {
-      postMoveServersCalled = true;
-    }
-    @Override
-    public void preBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String groupName) throws IOException {
-      preBalanceRSGroupCalled = true;
-    }
-    @Override
-    public void postBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String groupName, boolean balancerRan) throws IOException {
-      postBalanceRSGroupCalled = true;
-    }
-  }
-  @Test
-  public void testMoveServersAndTables() throws Exception {
-    super.testMoveServersAndTables();
-    assertTrue(observer.preMoveServersAndTables);
-    assertTrue(observer.postMoveServersAndTables);
-  }
-  @Test
-  public void testTableMoveTruncateAndDrop() throws Exception {
-    super.testTableMoveTruncateAndDrop();
-    assertTrue(observer.preMoveTablesCalled);
-    assertTrue(observer.postMoveTablesCalled);
-  }
-
-  @Test
-  public void testRemoveServers() throws Exception {
-    super.testRemoveServers();
-    assertTrue(observer.preRemoveServersCalled);
-    assertTrue(observer.postRemoveServersCalled);
-  }
-
-  @Test
-  public void testMisplacedRegions() throws Exception {
-    final TableName tableName = TableName.valueOf(tablePrefix+"_testMisplacedRegions");
-    LOG.info("testMisplacedRegions");
-
-    final RSGroupInfo RSGroupInfo = addGroup("testMisplacedRegions", 1);
-
-    TEST_UTIL.createMultiRegionTable(tableName, new byte[]{'f'}, 15);
-    TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
-
-    rsGroupAdminEndpoint.getGroupInfoManager()
-        .moveTables(Sets.newHashSet(tableName), RSGroupInfo.getName());
-
-    admin.setBalancerRunning(true,true);
-    assertTrue(rsGroupAdmin.balanceRSGroup(RSGroupInfo.getName()));
-    admin.setBalancerRunning(false,true);
-    assertTrue(observer.preBalanceRSGroupCalled);
-    assertTrue(observer.postBalanceRSGroupCalled);
-
-    TEST_UTIL.waitFor(60000, new Predicate<Exception>() {
-      @Override
-      public boolean evaluate() throws Exception {
-        ServerName serverName =
-            ServerName.valueOf(RSGroupInfo.getServers().iterator().next().toString(), 1);
-        return admin.getConnection().getAdmin()
-            .getOnlineRegions(serverName).size() == 15;
-      }
-    });
-  }
-
-  @Test
-  public void testCloneSnapshot() throws Exception {
-    byte[] FAMILY = Bytes.toBytes("test");
-    String snapshotName = tableName.getNameAsString() + "_snap";
-    TableName clonedTableName = TableName.valueOf(tableName.getNameAsString() + "_clone");
-
-    // create base table
-    TEST_UTIL.createTable(tableName, FAMILY);
-
-    // create snapshot
-    admin.snapshot(snapshotName, tableName);
-
-    // clone
-    admin.cloneSnapshot(snapshotName, clonedTableName);
-  }
-
-  @Test
-  public void testRSGroupsWithHBaseQuota() throws Exception {
-    TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, true);
-    restartHBaseCluster();
-    try {
-      TEST_UTIL.waitFor(90000, new Waiter.Predicate<Exception>() {
-        @Override
-        public boolean evaluate() throws Exception {
-          return admin.isTableAvailable(QuotaTableUtil.QUOTA_TABLE_NAME);
-        }
-      });
-    } finally {
-      TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, false);
-      restartHBaseCluster();
-    }
-  }
-
-  @Test
-  public void testRSGroupListDoesNotContainFailedTableCreation() throws Exception {
-    toggleQuotaCheckAndRestartMiniCluster(true);
-    String nsp = "np1";
-    NamespaceDescriptor nspDesc =
-        NamespaceDescriptor.create(nsp).addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "5")
-            .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build();
-    admin.createNamespace(nspDesc);
-    assertEquals(3, admin.listNamespaceDescriptors().length);
-    HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
-    HTableDescriptor tableDescOne =
-        new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1"));
-    tableDescOne.addFamily(fam1);
-    admin.createTable(tableDescOne);
-
-    HTableDescriptor tableDescTwo =
-        new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2"));
-    tableDescTwo.addFamily(fam1);
-    boolean constraintViolated = false;
-
-    try {
-      admin.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"),
-          6);
-      Assert.fail("Creation table should fail because of quota violation.");
-    } catch (Exception exp) {
-      assertTrue(exp instanceof IOException);
-      constraintViolated = true;
-    } finally {
-      assertTrue("Constraint not violated for table " + tableDescTwo.getTableName(),
-          constraintViolated);
-    }
-    List<RSGroupInfo> rsGroupInfoList = rsGroupAdmin.listRSGroups();
-    boolean foundTable2 = false;
-    boolean foundTable1 = false;
-    for(int i = 0; i < rsGroupInfoList.size(); i++){
-      if(rsGroupInfoList.get(i).getTables().contains(tableDescTwo.getTableName())){
-        foundTable2 = true;
-      }
-      if(rsGroupInfoList.get(i).getTables().contains(tableDescOne.getTableName())){
-        foundTable1 = true;
-      }
-    }
-    assertFalse("Found table2 in rsgroup list.", foundTable2);
-    assertTrue("Did not find table1 in rsgroup list", foundTable1);
-
-    TEST_UTIL.deleteTable(tableDescOne.getTableName());
-    admin.deleteNamespace(nspDesc.getName());
-    toggleQuotaCheckAndRestartMiniCluster(false);
-
-  }
-  private void toggleQuotaCheckAndRestartMiniCluster(boolean enable) throws Exception{
-    TEST_UTIL.shutdownMiniCluster();
-    TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, enable);
-    TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1);
-    TEST_UTIL.getConfiguration().setInt(
-        ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
-        NUM_SLAVES_BASE - 1);
-    TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
-    initialize();
-  }
-
-  private void restartHBaseCluster() throws Exception {
-    LOG.info("\n\nShutting down cluster");
-    TEST_UTIL.shutdownMiniHBaseCluster();
-    LOG.info("\n\nSleeping a bit");
-    Thread.sleep(2000);
-    TEST_UTIL.restartHBaseCluster(NUM_SLAVES_BASE - 1);
-    initialize();
-  }
-}

http://git-wip-us.apache.org/repos/asf/hbase/blob/24d19e87/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java
new file mode 100644
index 0000000..5888afa
--- /dev/null
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java
@@ -0,0 +1,441 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.TableNotFoundException;
+import org.apache.hadoop.hbase.Waiter;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.master.ServerManager;
+import org.apache.hadoop.hbase.master.TableNamespaceManager;
+import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
+import org.apache.hadoop.hbase.net.Address;
+import org.apache.hadoop.hbase.quotas.QuotaUtil;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
+
+@Category({MediumTests.class})
+public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+      HBaseClassTestRule.forClass(TestRSGroupsAdmin1.class);
+
+  protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsAdmin1.class);
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    setUpTestBeforeClass();
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    tearDownAfterClass();
+  }
+
+  @Before
+  public void beforeMethod() throws Exception {
+    setUpBeforeMethod();
+  }
+
+  @After
+  public void afterMethod() throws Exception {
+    tearDownAfterMethod();
+  }
+
+  @Test
+  public void testValidGroupNames() throws IOException {
+    String[] badNames = {"foo*","foo@","-"};
+    String[] goodNames = {"foo_123"};
+
+    for(String entry: badNames) {
+      try {
+        rsGroupAdmin.addRSGroup(entry);
+        fail("Expected a constraint exception for: "+entry);
+      } catch(ConstraintException ex) {
+        //expected
+      }
+    }
+
+    for(String entry: goodNames) {
+      rsGroupAdmin.addRSGroup(entry);
+    }
+  }
+
+  @Test
+  public void testBogusArgs() throws Exception {
+    assertNull(rsGroupAdmin.getRSGroupInfoOfTable(TableName.valueOf("nonexistent")));
+    assertNull(rsGroupAdmin.getRSGroupOfServer(Address.fromParts("bogus",123)));
+    assertNull(rsGroupAdmin.getRSGroupInfo("bogus"));
+
+    try {
+      rsGroupAdmin.removeRSGroup("bogus");
+      fail("Expected removing bogus group to fail");
+    } catch(ConstraintException ex) {
+      //expected
+    }
+
+    try {
+      rsGroupAdmin.moveTables(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus");
+      fail("Expected move with bogus group to fail");
+    } catch(ConstraintException|TableNotFoundException ex) {
+      //expected
+    }
+
+    try {
+      rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromParts("bogus",123)), "bogus");
+      fail("Expected move with bogus group to fail");
+    } catch(ConstraintException ex) {
+      //expected
+    }
+
+    try {
+      admin.setBalancerRunning(true,true);
+      rsGroupAdmin.balanceRSGroup("bogus");
+      admin.setBalancerRunning(false,true);
+      fail("Expected move with bogus group to fail");
+    } catch(ConstraintException ex) {
+      //expected
+    }
+  }
+
+  @Test
+  public void testNamespaceConstraint() throws Exception {
+    String nsName = tablePrefix+"_foo";
+    String groupName = tablePrefix+"_foo";
+    LOG.info("testNamespaceConstraint");
+    rsGroupAdmin.addRSGroup(groupName);
+    assertTrue(observer.preAddRSGroupCalled);
+    assertTrue(observer.postAddRSGroupCalled);
+
+    admin.createNamespace(NamespaceDescriptor.create(nsName)
+        .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
+        .build());
+    //test removing a referenced group
+    try {
+      rsGroupAdmin.removeRSGroup(groupName);
+      fail("Expected a constraint exception");
+    } catch (IOException ex) {
+    }
+    //test modify group
+    //changing with the same name is fine
+    admin.modifyNamespace(
+        NamespaceDescriptor.create(nsName)
+          .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
+          .build());
+    String anotherGroup = tablePrefix+"_anotherGroup";
+    rsGroupAdmin.addRSGroup(anotherGroup);
+    //test add non-existent group
+    admin.deleteNamespace(nsName);
+    rsGroupAdmin.removeRSGroup(groupName);
+    assertTrue(observer.preRemoveRSGroupCalled);
+    assertTrue(observer.postRemoveRSGroupCalled);
+    try {
+      admin.createNamespace(NamespaceDescriptor.create(nsName)
+          .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "foo")
+          .build());
+      fail("Expected a constraint exception");
+    } catch (IOException ex) {
+    }
+  }
+
+  @Test
+  public void testGroupInfoMultiAccessing() throws Exception {
+    RSGroupInfoManager manager = rsGroupAdminEndpoint.getGroupInfoManager();
+    RSGroupInfo defaultGroup = manager.getRSGroup("default");
+    // getRSGroup updates default group's server list
+    // this process must not affect other threads iterating the list
+    Iterator<Address> it = defaultGroup.getServers().iterator();
+    manager.getRSGroup("default");
+    it.next();
+  }
+
+  @Test
+  public void testFailRemoveGroup() throws IOException, InterruptedException {
+    int initNumGroups = rsGroupAdmin.listRSGroups().size();
+    addGroup("bar", 3);
+    TEST_UTIL.createTable(tableName, Bytes.toBytes("f"));
+    rsGroupAdmin.moveTables(Sets.newHashSet(tableName), "bar");
+    RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
+    //group is not empty therefore it should fail
+    try {
+      rsGroupAdmin.removeRSGroup(barGroup.getName());
+      fail("Expected remove group to fail");
+    } catch(IOException e) {
+    }
+    //group cannot lose all it's servers therefore it should fail
+    try {
+      rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+      fail("Expected move servers to fail");
+    } catch(IOException e) {
+    }
+
+    rsGroupAdmin.moveTables(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP);
+    try {
+      rsGroupAdmin.removeRSGroup(barGroup.getName());
+      fail("Expected move servers to fail");
+    } catch(IOException e) {
+    }
+
+    rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    rsGroupAdmin.removeRSGroup(barGroup.getName());
+
+    Assert.assertEquals(initNumGroups, rsGroupAdmin.listRSGroups().size());
+  }
+
+  @Test
+  public void testMultiTableMove() throws Exception {
+    final TableName tableNameA = TableName.valueOf(tablePrefix + name.getMethodName() + "A");
+    final TableName tableNameB = TableName.valueOf(tablePrefix + name.getMethodName() + "B");
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    String newGroupName = getGroupName(name.getMethodName());
+    final RSGroupInfo newGroup = addGroup(newGroupName, 1);
+
+    TEST_UTIL.createTable(tableNameA, familyNameBytes);
+    TEST_UTIL.createTable(tableNameB, familyNameBytes);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regionsA = getTableRegionMap().get(tableNameA);
+        if (regionsA == null) {
+          return false;
+        }
+        List<String> regionsB = getTableRegionMap().get(tableNameB);
+        if (regionsB == null) {
+          return false;
+        }
+        return getTableRegionMap().get(tableNameA).size() >= 1
+                && getTableRegionMap().get(tableNameB).size() >= 1;
+      }
+    });
+
+    RSGroupInfo tableGrpA = rsGroupAdmin.getRSGroupInfoOfTable(tableNameA);
+    assertTrue(tableGrpA.getName().equals(RSGroupInfo.DEFAULT_GROUP));
+
+    RSGroupInfo tableGrpB = rsGroupAdmin.getRSGroupInfoOfTable(tableNameB);
+    assertTrue(tableGrpB.getName().equals(RSGroupInfo.DEFAULT_GROUP));
+    //change table's group
+    LOG.info("Moving table [" + tableNameA + "," + tableNameB + "] to " + newGroup.getName());
+    rsGroupAdmin.moveTables(Sets.newHashSet(tableNameA, tableNameB), newGroup.getName());
+
+    //verify group change
+    Assert.assertEquals(newGroup.getName(),
+            rsGroupAdmin.getRSGroupInfoOfTable(tableNameA).getName());
+
+    Assert.assertEquals(newGroup.getName(),
+            rsGroupAdmin.getRSGroupInfoOfTable(tableNameB).getName());
+
+    //verify tables' not exist in old group
+    Set<TableName> DefaultTables =
+        rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables();
+    assertFalse(DefaultTables.contains(tableNameA));
+    assertFalse(DefaultTables.contains(tableNameB));
+
+    //verify tables' exist in new group
+    Set<TableName> newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroupName).getTables();
+    assertTrue(newGroupTables.contains(tableNameA));
+    assertTrue(newGroupTables.contains(tableNameB));
+  }
+
+  @Test
+  public void testTableMoveTruncateAndDrop() throws Exception {
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    String newGroupName = getGroupName(name.getMethodName());
+    final RSGroupInfo newGroup = addGroup(newGroupName, 2);
+
+    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regions = getTableRegionMap().get(tableName);
+        if (regions == null) {
+          return false;
+        }
+
+        return getTableRegionMap().get(tableName).size() >= 5;
+      }
+    });
+
+    RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
+    assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
+
+    //change table's group
+    LOG.info("Moving table "+tableName+" to "+newGroup.getName());
+    rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
+
+    //verify group change
+    Assert.assertEquals(newGroup.getName(),
+        rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
+
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        Map<ServerName, List<String>> serverMap = getTableServerRegionMap().get(tableName);
+        int count = 0;
+        if (serverMap != null) {
+          for (ServerName rs : serverMap.keySet()) {
+            if (newGroup.containsServer(rs.getAddress())) {
+              count += serverMap.get(rs).size();
+            }
+          }
+        }
+        return count == 5;
+      }
+    });
+
+    //test truncate
+    admin.disableTable(tableName);
+    admin.truncateTable(tableName, true);
+    Assert.assertEquals(1, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
+    Assert.assertEquals(tableName, rsGroupAdmin.getRSGroupInfo(
+        newGroup.getName()).getTables().first());
+
+    //verify removed table is removed from group
+    TEST_UTIL.deleteTable(tableName);
+    Assert.assertEquals(0, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
+
+    assertTrue(observer.preMoveTablesCalled);
+    assertTrue(observer.postMoveTablesCalled);
+  }
+
+  @Test
+  public void testDisabledTableMove() throws Exception {
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    String newGroupName = getGroupName(name.getMethodName());
+    final RSGroupInfo newGroup = addGroup(newGroupName, 2);
+
+    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regions = getTableRegionMap().get(tableName);
+        if (regions == null) {
+          return false;
+        }
+        return getTableRegionMap().get(tableName).size() >= 5;
+      }
+    });
+
+    RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
+    assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
+
+    //test disable table
+    admin.disableTable(tableName);
+
+    //change table's group
+    LOG.info("Moving table "+ tableName + " to " + newGroup.getName());
+    rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
+
+    //verify group change
+    Assert.assertEquals(newGroup.getName(),
+        rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
+  }
+
+  @Test
+  public void testRSGroupListDoesNotContainFailedTableCreation() throws Exception {
+    toggleQuotaCheckAndRestartMiniCluster(true);
+    String nsp = "np1";
+    NamespaceDescriptor nspDesc =
+        NamespaceDescriptor.create(nsp)
+            .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "5")
+            .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build();
+    admin.createNamespace(nspDesc);
+    assertEquals(3, admin.listNamespaceDescriptors().length);
+    HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
+    HTableDescriptor tableDescOne =
+        new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1"));
+    tableDescOne.addFamily(fam1);
+    admin.createTable(tableDescOne);
+
+    HTableDescriptor tableDescTwo =
+        new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2"));
+    tableDescTwo.addFamily(fam1);
+    boolean constraintViolated = false;
+
+    try {
+      admin.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"),
+          6);
+      Assert.fail("Creation table should fail because of quota violation.");
+    } catch (Exception exp) {
+      assertTrue(exp instanceof IOException);
+      constraintViolated = true;
+    } finally {
+      assertTrue("Constraint not violated for table " + tableDescTwo.getTableName(),
+          constraintViolated);
+    }
+    List<RSGroupInfo> rsGroupInfoList = rsGroupAdmin.listRSGroups();
+    boolean foundTable2 = false;
+    boolean foundTable1 = false;
+    for(int i = 0; i < rsGroupInfoList.size(); i++){
+      if(rsGroupInfoList.get(i).getTables().contains(tableDescTwo.getTableName())){
+        foundTable2 = true;
+      }
+      if(rsGroupInfoList.get(i).getTables().contains(tableDescOne.getTableName())){
+        foundTable1 = true;
+      }
+    }
+    assertFalse("Found table2 in rsgroup list.", foundTable2);
+    assertTrue("Did not find table1 in rsgroup list", foundTable1);
+
+    TEST_UTIL.deleteTable(tableDescOne.getTableName());
+    admin.deleteNamespace(nspDesc.getName());
+    toggleQuotaCheckAndRestartMiniCluster(false);
+
+  }
+
+  private void toggleQuotaCheckAndRestartMiniCluster(boolean enable) throws Exception{
+    TEST_UTIL.shutdownMiniCluster();
+    TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, enable);
+    TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1);
+    TEST_UTIL.getConfiguration().setInt(
+        ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
+        NUM_SLAVES_BASE - 1);
+    TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
+    initialize();
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/24d19e87/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java
new file mode 100644
index 0000000..f64e507
--- /dev/null
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java
@@ -0,0 +1,487 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.hadoop.hbase.ClusterMetrics.Option;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.Waiter;
+import org.apache.hadoop.hbase.client.ClusterConnection;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.net.Address;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetServerInfoRequest;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
+
+@Category({MediumTests.class})
+public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+      HBaseClassTestRule.forClass(TestRSGroupsAdmin2.class);
+
+  protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsAdmin2.class);
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    setUpTestBeforeClass();
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    tearDownAfterClass();
+  }
+
+  @Before
+  public void beforeMethod() throws Exception {
+    setUpBeforeMethod();
+  }
+
+  @After
+  public void afterMethod() throws Exception {
+    tearDownAfterMethod();
+  }
+
+  @Test
+  public void testRegionMove() throws Exception {
+    final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1);
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    // All the regions created below will be assigned to the default group.
+    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 6);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regions = getTableRegionMap().get(tableName);
+        if (regions == null) {
+          return false;
+        }
+
+        return getTableRegionMap().get(tableName).size() >= 6;
+      }
+    });
+
+    //get target region to move
+    Map<ServerName,List<String>> assignMap =
+        getTableServerRegionMap().get(tableName);
+    String targetRegion = null;
+    for(ServerName server : assignMap.keySet()) {
+      targetRegion = assignMap.get(server).size() > 0 ? assignMap.get(server).get(0) : null;
+      if(targetRegion != null) {
+        break;
+      }
+    }
+    //get server which is not a member of new group
+    ServerName targetServer = null;
+    for (ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
+                                  .getLiveServerMetrics().keySet()) {
+      if (!newGroup.containsServer(server.getAddress())) {
+        targetServer = server;
+        break;
+      }
+    }
+
+    final AdminProtos.AdminService.BlockingInterface targetRS =
+      ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
+
+    //move target server to group
+    rsGroupAdmin.moveServers(Sets.newHashSet(targetServer.getAddress()),
+        newGroup.getName());
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return ProtobufUtil.getOnlineRegions(targetRS).size() <= 0;
+      }
+    });
+
+    // Lets move this region to the new group.
+    TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName(
+            Bytes.toBytes(targetRegion))), Bytes.toBytes(targetServer.getServerName()));
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return
+            getTableRegionMap().get(tableName) != null &&
+                getTableRegionMap().get(tableName).size() == 6 &&
+                admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION))
+                     .getRegionStatesInTransition().size() < 1;
+      }
+    });
+
+    //verify that targetServer didn't open it
+    for (RegionInfo region: ProtobufUtil.getOnlineRegions(targetRS)) {
+      if (targetRegion.equals(region.getRegionNameAsString())) {
+        fail("Target server opened region");
+      }
+    }
+  }
+
+  @Test
+  public void testRegionServerMove() throws IOException,
+      InterruptedException {
+    int initNumGroups = rsGroupAdmin.listRSGroups().size();
+    RSGroupInfo appInfo = addGroup(getGroupName(name.getMethodName()), 1);
+    RSGroupInfo adminInfo = addGroup(getGroupName(name.getMethodName()), 1);
+    RSGroupInfo dInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
+    Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size());
+    assertEquals(1, adminInfo.getServers().size());
+    assertEquals(1, appInfo.getServers().size());
+    assertEquals(getNumServers() - 2, dInfo.getServers().size());
+    rsGroupAdmin.moveServers(appInfo.getServers(),
+        RSGroupInfo.DEFAULT_GROUP);
+    rsGroupAdmin.removeRSGroup(appInfo.getName());
+    rsGroupAdmin.moveServers(adminInfo.getServers(),
+        RSGroupInfo.DEFAULT_GROUP);
+    rsGroupAdmin.removeRSGroup(adminInfo.getName());
+    Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups);
+  }
+
+  @Test
+  public void testMoveServers() throws Exception {
+    //create groups and assign servers
+    addGroup("bar", 3);
+    rsGroupAdmin.addRSGroup("foo");
+
+    RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
+    RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
+    assertEquals(3, barGroup.getServers().size());
+    assertEquals(0, fooGroup.getServers().size());
+
+    //test fail bogus server move
+    try {
+      rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromString("foo:9999")),"foo");
+      fail("Bogus servers shouldn't have been successfully moved.");
+    } catch(IOException ex) {
+      String exp = "Source RSGroup for server foo:9999 does not exist.";
+      String msg = "Expected '"+exp+"' in exception message: ";
+      assertTrue(msg+" "+ex.getMessage(), ex.getMessage().contains(exp));
+    }
+
+    //test success case
+    LOG.info("moving servers "+barGroup.getServers()+" to group foo");
+    rsGroupAdmin.moveServers(barGroup.getServers(), fooGroup.getName());
+
+    barGroup = rsGroupAdmin.getRSGroupInfo("bar");
+    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
+    assertEquals(0,barGroup.getServers().size());
+    assertEquals(3,fooGroup.getServers().size());
+
+    LOG.info("moving servers "+fooGroup.getServers()+" to group default");
+    rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return getNumServers() ==
+        rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
+      }
+    });
+
+    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
+    assertEquals(0,fooGroup.getServers().size());
+
+    //test group removal
+    LOG.info("Remove group "+barGroup.getName());
+    rsGroupAdmin.removeRSGroup(barGroup.getName());
+    Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(barGroup.getName()));
+    LOG.info("Remove group "+fooGroup.getName());
+    rsGroupAdmin.removeRSGroup(fooGroup.getName());
+    Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName()));
+  }
+
+  @Test
+  public void testRemoveServers() throws Exception {
+    LOG.info("testRemoveServers");
+    final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3);
+    Iterator<Address> iterator = newGroup.getServers().iterator();
+    ServerName targetServer = ServerName.parseServerName(iterator.next().toString());
+
+    // remove online servers
+    try {
+      rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress()));
+      fail("Online servers shouldn't have been successfully removed.");
+    } catch(IOException ex) {
+      String exp = "Server " + targetServer.getAddress()
+          + " is an online server, not allowed to remove.";
+      String msg = "Expected '" + exp + "' in exception message: ";
+      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
+    }
+    assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
+
+    // remove dead servers
+    NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size();
+    AdminProtos.AdminService.BlockingInterface targetRS =
+        ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
+    try {
+      targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null,
+          GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName());
+      //stopping may cause an exception
+      //due to the connection loss
+      LOG.info("stopping server " + targetServer.getServerName());
+      targetRS.stopServer(null,
+          AdminProtos.StopServerRequest.newBuilder().setReason("Die").build());
+      NUM_DEAD_SERVERS ++;
+    } catch(Exception e) {
+    }
+
+    //wait for stopped regionserver to dead server list
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return !master.getServerManager().areDeadServersInProgress()
+            && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS;
+      }
+    });
+
+    try {
+      rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress()));
+      fail("Dead servers shouldn't have been successfully removed.");
+    } catch(IOException ex) {
+      String exp = "Server " + targetServer.getAddress() + " is on the dead servers list,"
+          + " Maybe it will come back again, not allowed to remove.";
+      String msg = "Expected '" + exp + "' in exception message: ";
+      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
+    }
+    assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
+
+    // remove decommissioned servers
+    List<ServerName> serversToDecommission = new ArrayList<>();
+    targetServer = ServerName.parseServerName(iterator.next().toString());
+    targetRS = ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
+    targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null,
+          GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName());
+    assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer));
+    serversToDecommission.add(targetServer);
+
+    admin.decommissionRegionServers(serversToDecommission, true);
+    assertEquals(1, admin.listDecommissionedRegionServers().size());
+
+    assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
+    rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress()));
+    Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers();
+    assertFalse(newGroupServers.contains(targetServer.getAddress()));
+    assertEquals(2, newGroupServers.size());
+
+    assertTrue(observer.preRemoveServersCalled);
+    assertTrue(observer.postRemoveServersCalled);
+  }
+
+  @Test
+  public void testMoveServersAndTables() throws Exception {
+    LOG.info("testMoveServersAndTables");
+    final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1);
+    //create table
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regions = getTableRegionMap().get(tableName);
+        if (regions == null) {
+          return false;
+        }
+
+        return getTableRegionMap().get(tableName).size() >= 5;
+      }
+    });
+
+    //get server which is not a member of new group
+    ServerName targetServer = null;
+    for(ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
+      .getLiveServerMetrics().keySet()) {
+      if(!newGroup.containsServer(server.getAddress()) &&
+           !rsGroupAdmin.getRSGroupInfo("master").containsServer(server.getAddress())) {
+        targetServer = server;
+        break;
+      }
+    }
+
+    LOG.debug("Print group info : " + rsGroupAdmin.listRSGroups());
+    int oldDefaultGroupServerSize =
+            rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
+    int oldDefaultGroupTableSize =
+            rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size();
+
+    //test fail bogus server move
+    try {
+      rsGroupAdmin.moveServersAndTables(Sets.newHashSet(Address.fromString("foo:9999")),
+              Sets.newHashSet(tableName), newGroup.getName());
+      fail("Bogus servers shouldn't have been successfully moved.");
+    } catch(IOException ex) {
+      String exp = "Source RSGroup for server foo:9999 does not exist.";
+      String msg = "Expected '" + exp + "' in exception message: ";
+      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
+    }
+
+    //test fail server move
+    try {
+      rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()),
+              Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
+      fail("servers shouldn't have been successfully moved.");
+    } catch(IOException ex) {
+      String exp = "Target RSGroup " + RSGroupInfo.DEFAULT_GROUP +
+              " is same as source " + RSGroupInfo.DEFAULT_GROUP + " RSGroup.";
+      String msg = "Expected '" + exp + "' in exception message: ";
+      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
+    }
+
+    //verify default group info
+    Assert.assertEquals(oldDefaultGroupServerSize,
+            rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size());
+    Assert.assertEquals(oldDefaultGroupTableSize,
+            rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size());
+
+    //verify new group info
+    Assert.assertEquals(1,
+            rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers().size());
+    Assert.assertEquals(0,
+            rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
+
+    //get all region to move targetServer
+    List<String> regionList = getTableRegionMap().get(tableName);
+    for(String region : regionList) {
+      // Lets move this region to the targetServer
+      TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName(Bytes.toBytes(region))),
+              Bytes.toBytes(targetServer.getServerName()));
+    }
+
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return getTableRegionMap().get(tableName) != null &&
+                getTableRegionMap().get(tableName).size() == 5 &&
+                getTableServerRegionMap().get(tableName).size() == 1 &&
+                admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION))
+                     .getRegionStatesInTransition().size() < 1;
+      }
+    });
+
+    //verify that all region move to targetServer
+    Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size());
+
+    //move targetServer and table to newGroup
+    LOG.info("moving server and table to newGroup");
+    rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()),
+            Sets.newHashSet(tableName), newGroup.getName());
+
+    //verify group change
+    Assert.assertEquals(newGroup.getName(),
+            rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
+
+    //verify servers' not exist in old group
+    Set<Address> defaultServers = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP)
+            .getServers();
+    assertFalse(defaultServers.contains(targetServer.getAddress()));
+
+    //verify servers' exist in new group
+    Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers();
+    assertTrue(newGroupServers.contains(targetServer.getAddress()));
+
+    //verify tables' not exist in old group
+    Set<TableName> defaultTables = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP)
+            .getTables();
+    assertFalse(defaultTables.contains(tableName));
+
+    //verify tables' exist in new group
+    Set<TableName> newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables();
+    assertTrue(newGroupTables.contains(tableName));
+
+    //verify that all region still assgin on targetServer
+    Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size());
+
+    assertTrue(observer.preMoveServersAndTables);
+    assertTrue(observer.postMoveServersAndTables);
+  }
+
+  @Test
+  public void testMoveServersFromDefaultGroup() throws Exception {
+    //create groups and assign servers
+    rsGroupAdmin.addRSGroup("foo");
+
+    RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
+    assertEquals(0, fooGroup.getServers().size());
+    RSGroupInfo defaultGroup = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
+
+    //test remove all servers from default
+    try {
+      rsGroupAdmin.moveServers(defaultGroup.getServers(), fooGroup.getName());
+      fail(RSGroupAdminServer.KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE);
+    } catch (ConstraintException ex) {
+      assertTrue(ex.getMessage().contains(RSGroupAdminServer
+              .KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE));
+    }
+
+    //test success case, remove one server from default ,keep at least one server
+    if (defaultGroup.getServers().size() > 1) {
+      Address serverInDefaultGroup = defaultGroup.getServers().iterator().next();
+      LOG.info("moving server " + serverInDefaultGroup + " from group default to group " +
+              fooGroup.getName());
+      rsGroupAdmin.moveServers(Sets.newHashSet(serverInDefaultGroup), fooGroup.getName());
+    }
+
+    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
+    LOG.info("moving servers " + fooGroup.getServers() + " to group default");
+    rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return getNumServers() ==
+                rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
+      }
+    });
+
+    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
+    assertEquals(0, fooGroup.getServers().size());
+
+    //test group removal
+    LOG.info("Remove group " + fooGroup.getName());
+    rsGroupAdmin.removeRSGroup(fooGroup.getName());
+    Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName()));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/24d19e87/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java
new file mode 100644
index 0000000..6cb6c81
--- /dev/null
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java
@@ -0,0 +1,188 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.Waiter;
+import org.apache.hadoop.hbase.Waiter.Predicate;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
+
+@Category({MediumTests.class})
+public class TestRSGroupsBalance extends TestRSGroupsBase {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+      HBaseClassTestRule.forClass(TestRSGroupsBalance.class);
+
+  protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsBalance.class);
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    setUpTestBeforeClass();
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    tearDownAfterClass();
+  }
+
+  @Before
+  public void beforeMethod() throws Exception {
+    setUpBeforeMethod();
+  }
+
+  @After
+  public void afterMethod() throws Exception {
+    tearDownAfterMethod();
+  }
+
+  @Test
+  public void testGroupBalance() throws Exception {
+    LOG.info(name.getMethodName());
+    String newGroupName = getGroupName(name.getMethodName());
+    final RSGroupInfo newGroup = addGroup(newGroupName, 3);
+
+    final TableName tableName = TableName.valueOf(tablePrefix+"_ns", name.getMethodName());
+    admin.createNamespace(
+        NamespaceDescriptor.create(tableName.getNamespaceAsString())
+            .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, newGroupName).build());
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    final HTableDescriptor desc = new HTableDescriptor(tableName);
+    desc.addFamily(new HColumnDescriptor("f"));
+    byte [] startKey = Bytes.toBytes("aaaaa");
+    byte [] endKey = Bytes.toBytes("zzzzz");
+    admin.createTable(desc, startKey, endKey, 6);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regions = getTableRegionMap().get(tableName);
+        if (regions == null) {
+          return false;
+        }
+        return regions.size() >= 6;
+      }
+    });
+
+    //make assignment uneven, move all regions to one server
+    Map<ServerName,List<String>> assignMap =
+        getTableServerRegionMap().get(tableName);
+    final ServerName first = assignMap.entrySet().iterator().next().getKey();
+    for(RegionInfo region: admin.getTableRegions(tableName)) {
+      if(!assignMap.get(first).contains(region.getRegionNameAsString())) {
+        admin.move(region.getEncodedNameAsBytes(), Bytes.toBytes(first.getServerName()));
+      }
+    }
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        Map<ServerName, List<String>> map = getTableServerRegionMap().get(tableName);
+        if (map == null) {
+          return true;
+        }
+        List<String> regions = map.get(first);
+        if (regions == null) {
+          return true;
+        }
+        return regions.size() >= 6;
+      }
+    });
+
+    //balance the other group and make sure it doesn't affect the new group
+    admin.setBalancerRunning(true,true);
+    rsGroupAdmin.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP);
+    assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size());
+
+    //disable balance, balancer will not be run and return false
+    admin.setBalancerRunning(false,true);
+    assertFalse(rsGroupAdmin.balanceRSGroup(newGroupName));
+    assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size());
+
+    //enable balance
+    admin.setBalancerRunning(true,true);
+    rsGroupAdmin.balanceRSGroup(newGroupName);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        for (List<String> regions : getTableServerRegionMap().get(tableName).values()) {
+          if (2 != regions.size()) {
+            return false;
+          }
+        }
+        return true;
+      }
+    });
+    admin.setBalancerRunning(false,true);
+  }
+
+  @Test
+  public void testMisplacedRegions() throws Exception {
+    final TableName tableName = TableName.valueOf(tablePrefix+"_testMisplacedRegions");
+    LOG.info("testMisplacedRegions");
+
+    final RSGroupInfo RSGroupInfo = addGroup("testMisplacedRegions", 1);
+
+    TEST_UTIL.createMultiRegionTable(tableName, new byte[]{'f'}, 15);
+    TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
+
+    rsGroupAdminEndpoint.getGroupInfoManager()
+        .moveTables(Sets.newHashSet(tableName), RSGroupInfo.getName());
+
+    admin.setBalancerRunning(true,true);
+    assertTrue(rsGroupAdmin.balanceRSGroup(RSGroupInfo.getName()));
+    admin.setBalancerRunning(false,true);
+    assertTrue(observer.preBalanceRSGroupCalled);
+    assertTrue(observer.postBalanceRSGroupCalled);
+
+    TEST_UTIL.waitFor(60000, new Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        ServerName serverName =
+            ServerName.valueOf(RSGroupInfo.getServers().iterator().next().toString(), 1);
+        return admin.getConnection().getAdmin()
+            .getOnlineRegions(serverName).size() == 15;
+      }
+    });
+  }
+
+}