You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by zh...@apache.org on 2019/11/15 10:56:14 UTC

[hbase] branch HBASE-22514 updated: HBASE-23253 Rewrite rsgroup related UTs with the new methods introduced in HBASE-22932 (#813)

This is an automated email from the ASF dual-hosted git repository.

zhangduo pushed a commit to branch HBASE-22514
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/HBASE-22514 by this push:
     new e32a3cd  HBASE-23253 Rewrite rsgroup related UTs with the new methods introduced in HBASE-22932 (#813)
e32a3cd is described below

commit e32a3cdc04b85af589a80b4a44bd452437765099
Author: Duo Zhang <zh...@apache.org>
AuthorDate: Fri Nov 15 18:56:01 2019 +0800

    HBASE-23253 Rewrite rsgroup related UTs with the new methods introduced in HBASE-22932 (#813)
    
    Signed-off-by: Guanghao Zhang <zg...@apache.org>
---
 .../hbase/testclassification/RSGroupTests.java     |   24 +
 .../java/org/apache/hadoop/hbase/client/Admin.java |    4 +-
 .../hadoop/hbase/client/AdminOverAsyncAdmin.java   |    8 +-
 .../org/apache/hadoop/hbase/client/AsyncAdmin.java |    4 +-
 .../hadoop/hbase/client/AsyncHBaseAdmin.java       |    8 +-
 .../hadoop/hbase/client/RawAsyncHBaseAdmin.java    |   93 +-
 .../hbase/rsgroup/IntegrationTestRSGroup.java      |   27 +-
 .../src/main/protobuf/Master.proto                 |    9 +
 .../hadoop/hbase/master/MasterRpcServices.java     |   95 +-
 .../apache/hadoop/hbase/rsgroup/RSGroupAdmin.java  |   82 --
 .../hadoop/hbase/rsgroup/RSGroupAdminClient.java   | 1067 +++-----------------
 ...leRSGroups.java => EnableRSGroupsTestBase.java} |   28 +-
 .../hadoop/hbase/rsgroup/TestEnableRSGroups.java   |   71 +-
 .../rsgroup/TestEnableRSGroupsCompatibility.java   |   45 +
 .../hbase/rsgroup/TestMigrateRSGroupInfo.java      |   15 +-
 .../rsgroup/TestRSGroupMajorCompactionTTL.java     |   16 +-
 .../hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java   |  241 ++---
 .../hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java   |  275 +++--
 .../hadoop/hbase/rsgroup/TestRSGroupsBalance.java  |   54 +-
 .../hadoop/hbase/rsgroup/TestRSGroupsBase.java     |  271 ++---
 .../hadoop/hbase/rsgroup/TestRSGroupsBasics.java   |   67 +-
 .../hbase/rsgroup/TestRSGroupsCPHookCalled.java    |   91 ++
 .../hadoop/hbase/rsgroup/TestRSGroupsKillRS.java   |  107 +-
 .../hbase/rsgroup/TestRSGroupsOfflineMode.java     |   39 +-
 .../hadoop/hbase/rsgroup/TestRSGroupsWithACL.java  |   19 +-
 .../hbase/rsgroup/VerifyingRSGroupAdmin.java}      |  835 ++++++---------
 .../hbase/rsgroup/VerifyingRSGroupAdminClient.java |  193 ----
 .../hadoop/hbase/thrift2/client/ThriftAdmin.java   |    4 +-
 pom.xml                                            |   15 +
 29 files changed, 1259 insertions(+), 2548 deletions(-)

diff --git a/hbase-annotations/src/test/java/org/apache/hadoop/hbase/testclassification/RSGroupTests.java b/hbase-annotations/src/test/java/org/apache/hadoop/hbase/testclassification/RSGroupTests.java
new file mode 100644
index 0000000..80b04eb
--- /dev/null
+++ b/hbase-annotations/src/test/java/org/apache/hadoop/hbase/testclassification/RSGroupTests.java
@@ -0,0 +1,24 @@
+/**
+ * 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.testclassification;
+
+/**
+ * Tag the tests related to rs group feature.
+ */
+public interface RSGroupTests {
+}
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
index 2b4eaf8..184d588 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
@@ -2315,7 +2315,7 @@ public interface Admin extends Abortable, Closeable {
    * @param servers set of servers to remove
    * @throws IOException if a remote or network exception occurs
    */
-  void removeRSGroup(Set<Address> servers) throws IOException;
+  void removeServersFromRSGroup(Set<Address> servers) throws IOException;
 
   /**
    * Move given set of servers to the specified target RegionServer group
@@ -2323,7 +2323,7 @@ public interface Admin extends Abortable, Closeable {
    * @param targetGroup the group to move servers to
    * @throws IOException if a remote or network exception occurs
    */
-  void moveToRSGroup(Set<Address> servers, String targetGroup) throws IOException;
+  void moveServersToRSGroup(Set<Address> servers, String targetGroup) throws IOException;
 
   /**
    * Set the RegionServer group for tables
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
index 060170c..9ebc593 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
@@ -968,8 +968,8 @@ class AdminOverAsyncAdmin implements Admin {
   }
 
   @Override
-  public void moveToRSGroup(Set<Address> servers, String groupName) throws IOException {
-    get(admin.moveToRSGroup(servers, groupName));
+  public void moveServersToRSGroup(Set<Address> servers, String groupName) throws IOException {
+    get(admin.moveServersToRSGroup(servers, groupName));
   }
 
   @Override
@@ -998,8 +998,8 @@ class AdminOverAsyncAdmin implements Admin {
   }
 
   @Override
-  public void removeRSGroup(Set<Address> servers) throws IOException {
-    get(admin.removeRSGroup(servers));
+  public void removeServersFromRSGroup(Set<Address> servers) throws IOException {
+    get(admin.removeServersFromRSGroup(servers));
   }
 
   @Override
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
index 12926cc..07cce90 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
@@ -1560,7 +1560,7 @@ public interface AsyncAdmin {
    * @param servers set of servers to remove
    * @throws IOException if a remote or network exception occurs
    */
-  CompletableFuture<Void> removeRSGroup(Set<Address> servers);
+  CompletableFuture<Void> removeServersFromRSGroup(Set<Address> servers);
 
   /**
    * Move given set of servers to the specified target RegionServer group
@@ -1568,7 +1568,7 @@ public interface AsyncAdmin {
    * @param groupName the group to move servers to
    * @throws IOException if a remote or network exception occurs
    */
-  CompletableFuture<Void> moveToRSGroup(Set<Address> servers, String groupName);
+  CompletableFuture<Void> moveServersToRSGroup(Set<Address> servers, String groupName);
 
   /**
    * Set the RegionServer group for tables
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
index 459c225..6bf2504 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
@@ -845,8 +845,8 @@ class AsyncHBaseAdmin implements AsyncAdmin {
   }
 
   @Override
-  public CompletableFuture<Void> moveToRSGroup(Set<Address> servers, String groupName) {
-    return wrap(rawAdmin.moveToRSGroup(servers, groupName));
+  public CompletableFuture<Void> moveServersToRSGroup(Set<Address> servers, String groupName) {
+    return wrap(rawAdmin.moveServersToRSGroup(servers, groupName));
   }
 
   @Override
@@ -875,8 +875,8 @@ class AsyncHBaseAdmin implements AsyncAdmin {
   }
 
   @Override
-  public CompletableFuture<Void> removeRSGroup(Set<Address> servers) {
-    return wrap(rawAdmin.removeRSGroup(servers));
+  public CompletableFuture<Void> removeServersFromRSGroup(Set<Address> servers) {
+    return wrap(rawAdmin.removeServersFromRSGroup(servers));
   }
 
   @Override
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
index 399ae28..e336e23 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
@@ -289,6 +289,12 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.AddR
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.AddRSGroupResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.BalanceRSGroupRequest;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.BalanceRSGroupResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosRequest;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.MoveServersRequest;
@@ -3894,7 +3900,7 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
   }
 
   @Override
-  public CompletableFuture<Void> moveToRSGroup(Set<Address> servers, String groupName) {
+  public CompletableFuture<Void> moveServersToRSGroup(Set<Address> servers, String groupName) {
     return this.<Void> newMasterCaller()
         .action((controller, stub) -> this.
             <MoveServersRequest, MoveServersResponse, Void> call(controller, stub,
@@ -3948,27 +3954,23 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
 
   @Override
   public CompletableFuture<RSGroupInfo> getRSGroup(Address hostPort) {
-    CompletableFuture<RSGroupInfo> future = new CompletableFuture<>();
-    addListener(listRSGroups(), (groups, err) -> {
-      if (err != null) {
-        future.completeExceptionally(err);
-        return;
-      }
-      for (RSGroupInfo rsGroupInfo : groups) {
-        if (rsGroupInfo.getServers().contains(hostPort)){
-          future.complete(rsGroupInfo);
-          return;
-        }
-      }
-      future.complete(null);
-    });
-    return future;
+    return this.<RSGroupInfo> newMasterCaller()
+      .action(((controller, stub) -> this
+        .<GetRSGroupInfoOfServerRequest, GetRSGroupInfoOfServerResponse, RSGroupInfo> call(
+          controller, stub,
+          GetRSGroupInfoOfServerRequest.newBuilder()
+            .setServer(HBaseProtos.ServerName.newBuilder().setHostName(hostPort.getHostname())
+              .setPort(hostPort.getPort()).build())
+            .build(),
+          (s, c, req, done) -> s.getRSGroupInfoOfServer(c, req, done),
+          resp -> resp.hasRSGroupInfo() ? ProtobufUtil.toGroupInfo(resp.getRSGroupInfo()) : null)))
+      .call();
   }
 
   @Override
-  public CompletableFuture<Void> removeRSGroup(Set<Address> servers) {
+  public CompletableFuture<Void> removeServersFromRSGroup(Set<Address> servers) {
     return this.<Void> newMasterCaller()
-        .action((controller, stub) -> this.
+      .action((controller, stub) -> this.
             <RemoveServersRequest, RemoveServersResponse, Void> call(controller, stub,
                 RequestConverter.buildRemoveServersRequest(servers),
               (s, c, req, done) -> s.removeServers(c, req, done), resp -> null))
@@ -4019,49 +4021,24 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
 
   @Override
   public CompletableFuture<RSGroupInfo> getRSGroup(TableName table) {
-    CompletableFuture<RSGroupInfo> future = new CompletableFuture<>();
-    addListener(getDescriptor(table), (td, err) -> {
-      if (err != null) {
-        // return null instead of err to keep compatible with old semantics
-        // todo: need to change both this and UTs
-        future.complete(null);
-        return;
-      }
-      addListener(listRSGroups(), (groups, err2) -> {
-        if (err2 != null) {
-          future.completeExceptionally(err2);
-          return;
-        }
-        for (RSGroupInfo rsGroupInfo : groups) {
-          if (rsGroupInfo.getTables().contains(table)) {
-            future.complete(rsGroupInfo);
-            return;
-          }
-        }
-        future.complete(null);
-      });
-    });
-    return future;
+    return this.<RSGroupInfo> newMasterCaller().action(((controller, stub) -> this
+      .<GetRSGroupInfoOfTableRequest, GetRSGroupInfoOfTableResponse, RSGroupInfo> call(controller,
+        stub,
+        GetRSGroupInfoOfTableRequest.newBuilder().setTableName(ProtobufUtil.toProtoTableName(table))
+          .build(),
+        (s, c, req, done) -> s.getRSGroupInfoOfTable(c, req, done),
+        resp -> resp.hasRSGroupInfo() ? ProtobufUtil.toGroupInfo(resp.getRSGroupInfo()) : null)))
+      .call();
   }
 
   @Override
   public CompletableFuture<RSGroupInfo> getRSGroup(String groupName) {
-    CompletableFuture<RSGroupInfo> future = new CompletableFuture<>();
-    addListener(listRSGroups(), (groups, err) -> {
-      if (err != null) {
-        future.completeExceptionally(err);
-        return;
-      }
-      for (RSGroupInfo rsGroupInfo : groups) {
-        if (rsGroupInfo.getName().equals(groupName)){
-          future.complete(rsGroupInfo);
-          return;
-        }
-      }
-      future.complete(null);
-    });
-    return future;
+    return this.<RSGroupInfo> newMasterCaller()
+      .action(((controller, stub) -> this
+        .<GetRSGroupInfoRequest, GetRSGroupInfoResponse, RSGroupInfo> call(controller, stub,
+          GetRSGroupInfoRequest.newBuilder().setRSGroupName(groupName).build(),
+          (s, c, req, done) -> s.getRSGroupInfo(c, req, done),
+          resp -> resp.hasRSGroupInfo() ? ProtobufUtil.toGroupInfo(resp.getRSGroupInfo()) : null)))
+      .call();
   }
-
-
 }
diff --git a/hbase-it/src/test/java/org/apache/hadoop/hbase/rsgroup/IntegrationTestRSGroup.java b/hbase-it/src/test/java/org/apache/hadoop/hbase/rsgroup/IntegrationTestRSGroup.java
index ef3a93a..551d280 100644
--- a/hbase-it/src/test/java/org/apache/hadoop/hbase/rsgroup/IntegrationTestRSGroup.java
+++ b/hbase-it/src/test/java/org/apache/hadoop/hbase/rsgroup/IntegrationTestRSGroup.java
@@ -41,7 +41,7 @@ public class IntegrationTestRSGroup extends TestRSGroupsBase {
 
   @Before
   public void beforeMethod() throws Exception {
-    if(!initialized) {
+    if (!initialized) {
       LOG.info("Setting up IntegrationTestRSGroup");
       LOG.info("Initializing cluster with " + NUM_SLAVES_BASE + " servers");
       TEST_UTIL = new IntegrationTestingUtility();
@@ -49,16 +49,15 @@ public class IntegrationTestRSGroup extends TestRSGroupsBase {
         RSGroupBasedLoadBalancer.class.getName());
       TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
         RSGroupAdminEndpoint.class.getName());
-      ((IntegrationTestingUtility)TEST_UTIL).initializeCluster(NUM_SLAVES_BASE);
-      //set shared configs
-      admin = TEST_UTIL.getAdmin();
-      cluster = TEST_UTIL.getHBaseClusterInterface();
-      rsGroupAdmin = new VerifyingRSGroupAdminClient(new RSGroupAdminClient(TEST_UTIL.getConnection()),
-          TEST_UTIL.getConfiguration());
+      ((IntegrationTestingUtility) TEST_UTIL).initializeCluster(NUM_SLAVES_BASE);
+      // set shared configs
+      ADMIN = TEST_UTIL.getAdmin();
+      CLUSTER = TEST_UTIL.getHBaseClusterInterface();
+      RS_GROUP_ADMIN_CLIENT = new RSGroupAdminClient(TEST_UTIL.getConnection());
       LOG.info("Done initializing cluster");
       initialized = true;
-      //cluster may not be clean
-      //cleanup when initializing
+      // cluster may not be clean
+      // cleanup when initializing
       afterMethod();
     }
   }
@@ -70,7 +69,7 @@ public class IntegrationTestRSGroup extends TestRSGroupsBase {
     deleteTableIfNecessary();
     deleteNamespaceIfNecessary();
     deleteGroups();
-    admin.balancerSwitch(true, true);
+    ADMIN.balancerSwitch(true, true);
 
     LOG.info("Restoring the cluster");
     ((IntegrationTestingUtility)TEST_UTIL).restoreCluster();
@@ -79,10 +78,10 @@ public class IntegrationTestRSGroup extends TestRSGroupsBase {
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        LOG.info("Waiting for cleanup to finish "+ rsGroupAdmin.listRSGroups());
+        LOG.info("Waiting for cleanup to finish "+ ADMIN.listRSGroups());
         //Might be greater since moving servers back to default
         //is after starting a server
-        return rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size()
+        return ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size()
             >= NUM_SLAVES_BASE;
       }
     });
@@ -90,10 +89,10 @@ public class IntegrationTestRSGroup extends TestRSGroupsBase {
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        LOG.info("Waiting for regionservers to be registered "+ rsGroupAdmin.listRSGroups());
+        LOG.info("Waiting for regionservers to be registered "+ ADMIN.listRSGroups());
         //Might be greater since moving servers back to default
         //is after starting a server
-        return rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size()
+        return ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size()
             == getNumServers();
       }
     });
diff --git a/hbase-protocol-shaded/src/main/protobuf/Master.proto b/hbase-protocol-shaded/src/main/protobuf/Master.proto
index 37878f4..a90fb47 100644
--- a/hbase-protocol-shaded/src/main/protobuf/Master.proto
+++ b/hbase-protocol-shaded/src/main/protobuf/Master.proto
@@ -1083,6 +1083,15 @@ service MasterService {
   rpc ListNamespaces(ListNamespacesRequest)
     returns(ListNamespacesResponse);
 
+  rpc GetRSGroupInfo(GetRSGroupInfoRequest)
+    returns (GetRSGroupInfoResponse);
+
+  rpc GetRSGroupInfoOfTable(GetRSGroupInfoOfTableRequest)
+    returns (GetRSGroupInfoOfTableResponse);
+
+  rpc GetRSGroupInfoOfServer(GetRSGroupInfoOfServerRequest)
+    returns (GetRSGroupInfoOfServerResponse);
+
   rpc MoveServers(MoveServersRequest)
     returns (MoveServersResponse);
 
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
index e79663e..6204448 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
@@ -309,6 +309,12 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.AddR
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.AddRSGroupResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.BalanceRSGroupRequest;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.BalanceRSGroupResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosRequest;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.MoveServersRequest;
@@ -2861,6 +2867,94 @@ public class MasterRpcServices extends RSRpcServices implements MasterService.Bl
   }
 
   @Override
+  public GetRSGroupInfoResponse getRSGroupInfo(RpcController controller,
+    GetRSGroupInfoRequest request) throws ServiceException {
+    String groupName = request.getRSGroupName();
+    LOG.info(
+      master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, group=" + groupName);
+    try {
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().preGetRSGroupInfo(groupName);
+      }
+      RSGroupInfo rsGroupInfo = master.getRSGroupInfoManager().getRSGroup(groupName);
+      GetRSGroupInfoResponse resp;
+      if (rsGroupInfo != null) {
+        resp = GetRSGroupInfoResponse.newBuilder()
+          .setRSGroupInfo(ProtobufUtil.toProtoGroupInfo(rsGroupInfo)).build();
+      } else {
+        resp = GetRSGroupInfoResponse.getDefaultInstance();
+      }
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().postGetRSGroupInfo(groupName);
+      }
+      return resp;
+    } catch (IOException e) {
+      throw new ServiceException(e);
+    }
+  }
+
+  @Override
+  public GetRSGroupInfoOfTableResponse getRSGroupInfoOfTable(RpcController controller,
+    GetRSGroupInfoOfTableRequest request) throws ServiceException {
+    TableName tableName = ProtobufUtil.toTableName(request.getTableName());
+    LOG.info(
+      master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, table=" + tableName);
+    try {
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().preGetRSGroupInfoOfTable(tableName);
+      }
+      GetRSGroupInfoOfTableResponse resp;
+      TableDescriptor td = master.getTableDescriptors().get(tableName);
+      if (td == null) {
+        resp = GetRSGroupInfoOfTableResponse.getDefaultInstance();
+      } else {
+        RSGroupInfo rsGroupInfo = null;
+        if (td.getRegionServerGroup().isPresent()) {
+          rsGroupInfo = master.getRSGroupInfoManager().getRSGroup(td.getRegionServerGroup().get());
+        }
+        if (rsGroupInfo == null) {
+          rsGroupInfo = master.getRSGroupInfoManager().getRSGroup(RSGroupInfo.DEFAULT_GROUP);
+        }
+        resp = GetRSGroupInfoOfTableResponse.newBuilder()
+          .setRSGroupInfo(ProtobufUtil.toProtoGroupInfo(rsGroupInfo)).build();
+      }
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().postGetRSGroupInfoOfTable(tableName);
+      }
+      return resp;
+    } catch (IOException e) {
+      throw new ServiceException(e);
+    }
+  }
+
+  @Override
+  public GetRSGroupInfoOfServerResponse getRSGroupInfoOfServer(RpcController controller,
+    GetRSGroupInfoOfServerRequest request) throws ServiceException {
+    Address hp =
+      Address.fromParts(request.getServer().getHostName(), request.getServer().getPort());
+    LOG.info(master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, server=" + hp);
+    try {
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().preGetRSGroupInfoOfServer(hp);
+      }
+      RSGroupInfo rsGroupInfo = master.getRSGroupInfoManager().getRSGroupOfServer(hp);
+      GetRSGroupInfoOfServerResponse resp;
+      if (rsGroupInfo != null) {
+        resp = GetRSGroupInfoOfServerResponse.newBuilder()
+          .setRSGroupInfo(ProtobufUtil.toProtoGroupInfo(rsGroupInfo)).build();
+      } else {
+        resp = GetRSGroupInfoOfServerResponse.getDefaultInstance();
+      }
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().postGetRSGroupInfoOfServer(hp);
+      }
+      return resp;
+    } catch (IOException e) {
+      throw new ServiceException(e);
+    }
+  }
+
+  @Override
   public MoveServersResponse moveServers(RpcController controller, MoveServersRequest request)
       throws ServiceException {
     Set<Address> hostPorts = Sets.newHashSet();
@@ -3009,5 +3103,4 @@ public class MasterRpcServices extends RSRpcServices implements MasterService.Bl
     }
     return builder.build();
   }
-
 }
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdmin.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdmin.java
deleted file mode 100644
index 3de6965..0000000
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdmin.java
+++ /dev/null
@@ -1,82 +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 java.io.IOException;
-import java.util.List;
-import java.util.Set;
-import org.apache.hadoop.hbase.client.Admin;
-import org.apache.hadoop.hbase.net.Address;
-import org.apache.yetus.audience.InterfaceAudience;
-
-/**
- * Group user API interface used between client and server.
- *
- * @deprecated Keep it here only for tests, using {@link Admin} instead.
- */
-@Deprecated
-@InterfaceAudience.Private
-public interface RSGroupAdmin {
-  /**
-   * Gets {@code RSGroupInfo} for given group name.
-   */
-  RSGroupInfo getRSGroupInfo(String groupName) throws IOException;
-
-  /**
-   * Move given set of servers to the specified target RegionServer group.
-   */
-  void moveServers(Set<Address> servers, String targetGroup) throws IOException;
-
-  /**
-   * Creates a new RegionServer group with the given name.
-   */
-  void addRSGroup(String groupName) throws IOException;
-
-  /**
-   * Removes RegionServer group associated with the given name.
-   */
-  void removeRSGroup(String groupName) throws IOException;
-
-  /**
-   * Balance regions in the given RegionServer group.
-   *
-   * @return boolean Whether balance ran or not
-   */
-  boolean balanceRSGroup(String groupName) throws IOException;
-
-  /**
-   * Lists current set of RegionServer groups.
-   */
-  List<RSGroupInfo> listRSGroups() throws IOException;
-
-  /**
-   * Retrieve the RSGroupInfo a server is affiliated to
-   * @param hostPort HostPort to get RSGroupInfo for
-   */
-  RSGroupInfo getRSGroupOfServer(Address hostPort) throws IOException;
-
-  /**
-   * Remove decommissioned servers from rsgroup.
-   * 1. Sometimes we may find the server aborted due to some hardware failure and we must offline
-   * the server for repairing. Or we need to move some servers to join other clusters.
-   * So we need to remove these servers from the rsgroup.
-   * 2. Dead/recovering/live servers will be disallowed.
-   * @param servers set of servers to remove
-   */
-  void removeServers(Set<Address> servers) throws IOException;
-}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java
index 7cf4ed1..3e17234 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java
@@ -20,33 +20,12 @@ package org.apache.hadoop.hbase.rsgroup;
 import com.google.protobuf.ServiceException;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.EnumSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.Future;
-import java.util.regex.Pattern;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.CacheEvictionStats;
-import org.apache.hadoop.hbase.ClusterMetrics;
-import org.apache.hadoop.hbase.NamespaceDescriptor;
-import org.apache.hadoop.hbase.NamespaceNotFoundException;
-import org.apache.hadoop.hbase.RegionMetrics;
-import org.apache.hadoop.hbase.ServerName;
-import org.apache.hadoop.hbase.TableExistsException;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.TableNotFoundException;
 import org.apache.hadoop.hbase.client.Admin;
-import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
-import org.apache.hadoop.hbase.client.CompactType;
-import org.apache.hadoop.hbase.client.CompactionState;
 import org.apache.hadoop.hbase.client.Connection;
-import org.apache.hadoop.hbase.client.RegionInfo;
-import org.apache.hadoop.hbase.client.SnapshotDescription;
-import org.apache.hadoop.hbase.client.TableDescriptor;
-import org.apache.hadoop.hbase.client.replication.TableCFs;
-import org.apache.hadoop.hbase.client.security.SecurityCapability;
-import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
 import org.apache.hadoop.hbase.net.Address;
 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
@@ -59,39 +38,24 @@ import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupI
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveServersAndTablesRequest;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveServersRequest;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveTablesRequest;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RSGroupAdminService;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveRSGroupRequest;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveServersRequest;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
-import org.apache.hadoop.hbase.quotas.QuotaFilter;
-import org.apache.hadoop.hbase.quotas.QuotaSettings;
-import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshotView;
-import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
-import org.apache.hadoop.hbase.replication.ReplicationPeerConfig;
-import org.apache.hadoop.hbase.replication.ReplicationPeerDescription;
-import org.apache.hadoop.hbase.replication.SyncReplicationState;
-import org.apache.hadoop.hbase.security.access.GetUserPermissionsRequest;
-import org.apache.hadoop.hbase.security.access.Permission;
-import org.apache.hadoop.hbase.security.access.UserPermission;
-import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
-import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
-import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
-import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
 import org.apache.yetus.audience.InterfaceAudience;
 
-import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
 import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
 
 /**
  * Client used for managing region server group information.
- *
  * @deprecated Keep it here only for tests, using {@link Admin} instead.
  */
 @Deprecated
 @InterfaceAudience.Private
-public class RSGroupAdminClient implements RSGroupAdmin, Admin {
+public class RSGroupAdminClient {
   private RSGroupAdminService.BlockingInterface stub;
   private Admin admin;
 
@@ -100,868 +64,13 @@ public class RSGroupAdminClient implements RSGroupAdmin, Admin {
     stub = RSGroupAdminService.newBlockingStub(admin.coprocessorService());
   }
 
-  // for writing UTs
-  @VisibleForTesting
-  protected RSGroupAdminClient() {
-  }
-
-  @Override
-  public int getOperationTimeout() {
-    return 0;
-  }
-
-  @Override
-  public int getSyncWaitTimeout() {
-    return 0;
-  }
-
-  @Override
-  public void abort(String why, Throwable e) {
-
-  }
-
-  @Override
-  public boolean isAborted() {
-    return false;
-  }
-
-  @Override
-  public Connection getConnection() {
-    return null;
-  }
-
-  @Override
-  public boolean tableExists(TableName tableName) throws IOException {
-    return false;
-  }
-
-  @Override
-  public List<TableDescriptor> listTableDescriptors() throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<TableDescriptor> listTableDescriptors(boolean includeSysTables) throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<TableDescriptor> listTableDescriptors(Pattern pattern) throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<TableDescriptor> listTableDescriptors(Pattern pattern, boolean includeSysTables)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public TableName[] listTableNames() throws IOException {
-    return new TableName[0];
-  }
-
-  @Override
-  public TableName[] listTableNames(Pattern pattern, boolean includeSysTables) throws IOException {
-    return new TableName[0];
-  }
-
-  @Override
-  public TableDescriptor getDescriptor(TableName tableName)
-      throws TableNotFoundException, IOException {
-    return null;
-  }
-
-  @Override
-  public void createTable(TableDescriptor desc, byte[] startKey, byte[] endKey, int numRegions)
-      throws IOException {
-
-  }
-
-  @Override
-  public Future<Void> createTableAsync(TableDescriptor desc) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> createTableAsync(TableDescriptor desc, byte[][] splitKeys)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> deleteTableAsync(TableName tableName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> truncateTableAsync(TableName tableName, boolean preserveSplits)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> enableTableAsync(TableName tableName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> disableTableAsync(TableName tableName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public boolean isTableEnabled(TableName tableName) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean isTableDisabled(TableName tableName) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean isTableAvailable(TableName tableName) throws IOException {
-    return false;
-  }
-
-  @Override
-  public Future<Void> addColumnFamilyAsync(TableName tableName, ColumnFamilyDescriptor columnFamily)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> deleteColumnFamilyAsync(TableName tableName, byte[] columnFamily)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> modifyColumnFamilyAsync(TableName tableName,
-      ColumnFamilyDescriptor columnFamily) throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<RegionInfo> getRegions(ServerName serverName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public void flush(TableName tableName) throws IOException {
-
-  }
-
-  @Override
-  public void flushRegion(byte[] regionName) throws IOException {
-
-  }
-
-  @Override
-  public void flushRegionServer(ServerName serverName) throws IOException {
-
-  }
-
-  @Override
-  public void compact(TableName tableName) throws IOException {
-
-  }
-
-  @Override
-  public void compactRegion(byte[] regionName) throws IOException {
-
-  }
-
-  @Override
-  public void compact(TableName tableName, byte[] columnFamily) throws IOException {
-
-  }
-
-  @Override
-  public void compactRegion(byte[] regionName, byte[] columnFamily) throws IOException {
-
-  }
-
-  @Override
-  public void compact(TableName tableName, CompactType compactType)
-      throws IOException, InterruptedException {
-
-  }
-
-  @Override
-  public void compact(TableName tableName, byte[] columnFamily, CompactType compactType)
-      throws IOException, InterruptedException {
-
-  }
-
-  @Override
-  public void majorCompact(TableName tableName) throws IOException {
-
-  }
-
-  @Override
-  public void majorCompactRegion(byte[] regionName) throws IOException {
-
-  }
-
-  @Override
-  public void majorCompact(TableName tableName, byte[] columnFamily) throws IOException {
-
-  }
-
-  @Override
-  public void majorCompactRegion(byte[] regionName, byte[] columnFamily) throws IOException {
-
-  }
-
-  @Override
-  public void majorCompact(TableName tableName, CompactType compactType)
-      throws IOException, InterruptedException {
-
-  }
-
-  @Override
-  public void majorCompact(TableName tableName, byte[] columnFamily, CompactType compactType)
-      throws IOException, InterruptedException {
-
-  }
-
-  @Override
-  public Map<ServerName, Boolean> compactionSwitch(boolean switchState,
-      List<String> serverNamesList) throws IOException {
-    return null;
-  }
-
-  @Override
-  public void compactRegionServer(ServerName serverName) throws IOException {
-
-  }
-
-  @Override
-  public void majorCompactRegionServer(ServerName serverName) throws IOException {
-
-  }
-
-  @Override
-  public void move(byte[] encodedRegionName) throws IOException {
-
-  }
-
-  @Override
-  public void move(byte[] encodedRegionName, ServerName destServerName) throws IOException {
-
-  }
-
-  @Override
-  public void assign(byte[] regionName) throws IOException {
-
-  }
-
-  @Override
-  public void unassign(byte[] regionName, boolean force) throws IOException {
-
-  }
-
-  @Override
-  public void offline(byte[] regionName) throws IOException {
-
-  }
-
-  @Override
-  public boolean balancerSwitch(boolean onOrOff, boolean synchronous) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean balance() throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean balance(boolean force) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean isBalancerEnabled() throws IOException {
-    return false;
-  }
-
-  @Override
-  public CacheEvictionStats clearBlockCache(TableName tableName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public boolean normalize() throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean isNormalizerEnabled() throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean normalizerSwitch(boolean on) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean catalogJanitorSwitch(boolean onOrOff) throws IOException {
-    return false;
-  }
-
-  @Override
-  public int runCatalogJanitor() throws IOException {
-    return 0;
-  }
-
-  @Override
-  public boolean isCatalogJanitorEnabled() throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean cleanerChoreSwitch(boolean onOrOff) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean runCleanerChore() throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean isCleanerChoreEnabled() throws IOException {
-    return false;
-  }
-
-  @Override
-  public Future<Void> mergeRegionsAsync(byte[][] nameofRegionsToMerge, boolean forcible)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public void split(TableName tableName) throws IOException {
-
-  }
-
-  @Override
-  public void split(TableName tableName, byte[] splitPoint) throws IOException {
-
-  }
-
-  @Override
-  public Future<Void> splitRegionAsync(byte[] regionName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> splitRegionAsync(byte[] regionName, byte[] splitPoint) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> modifyTableAsync(TableDescriptor td) throws IOException {
-    return null;
-  }
-
-  @Override
-  public void shutdown() throws IOException {
-
-  }
-
-  @Override
-  public void stopMaster() throws IOException {
-
-  }
-
-  @Override
-  public boolean isMasterInMaintenanceMode() throws IOException {
-    return false;
-  }
-
-  @Override
-  public void stopRegionServer(String hostnamePort) throws IOException {
-
-  }
-
-  @Override
-  public ClusterMetrics getClusterMetrics(EnumSet<ClusterMetrics.Option> options)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<RegionMetrics> getRegionMetrics(ServerName serverName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<RegionMetrics> getRegionMetrics(ServerName serverName, TableName tableName)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public Configuration getConfiguration() {
-    return null;
-  }
-
-  @Override
-  public Future<Void> createNamespaceAsync(NamespaceDescriptor descriptor) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> modifyNamespaceAsync(NamespaceDescriptor descriptor) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> deleteNamespaceAsync(String name) throws IOException {
-    return null;
-  }
-
-  @Override
-  public NamespaceDescriptor getNamespaceDescriptor(String name)
-      throws NamespaceNotFoundException, IOException {
-    return null;
-  }
-
-  @Override
-  public String[] listNamespaces() throws IOException {
-    return new String[0];
-  }
-
-  @Override
-  public NamespaceDescriptor[] listNamespaceDescriptors() throws IOException {
-    return new NamespaceDescriptor[0];
-  }
-
-  @Override
-  public List<TableDescriptor> listTableDescriptorsByNamespace(byte[] name) throws IOException {
-    return null;
-  }
-
-  @Override
-  public TableName[] listTableNamesByNamespace(String name) throws IOException {
-    return new TableName[0];
-  }
-
-  @Override
-  public List<RegionInfo> getRegions(TableName tableName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public void close() {
-
-  }
-
-  @Override
-  public List<TableDescriptor> listTableDescriptors(List<TableName> tableNames) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Boolean> abortProcedureAsync(long procId, boolean mayInterruptIfRunning)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public String getProcedures() throws IOException {
-    return null;
-  }
-
-  @Override
-  public String getLocks() throws IOException {
-    return null;
-  }
-
-  @Override
-  public void rollWALWriter(ServerName serverName) throws IOException, FailedLogCloseException {
-
-  }
-
-  @Override
-  public CompactionState getCompactionState(TableName tableName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public CompactionState getCompactionState(TableName tableName, CompactType compactType)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public CompactionState getCompactionStateForRegion(byte[] regionName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public long getLastMajorCompactionTimestamp(TableName tableName) throws IOException {
-    return 0;
-  }
-
-  @Override
-  public long getLastMajorCompactionTimestampForRegion(byte[] regionName) throws IOException {
-    return 0;
-  }
-
-  @Override
-  public void snapshot(SnapshotDescription snapshot)
-      throws IOException, SnapshotCreationException, IllegalArgumentException {
-
-  }
-
-  @Override
-  public Future<Void> snapshotAsync(SnapshotDescription snapshot)
-      throws IOException, SnapshotCreationException {
-    return null;
-  }
-
-  @Override
-  public boolean isSnapshotFinished(SnapshotDescription snapshot)
-      throws IOException, HBaseSnapshotException, UnknownSnapshotException {
-    return false;
-  }
-
-  @Override
-  public void restoreSnapshot(String snapshotName) throws IOException, RestoreSnapshotException {
-
-  }
-
-  @Override
-  public void restoreSnapshot(String snapshotName, boolean takeFailSafeSnapshot, boolean restoreAcl)
-      throws IOException, RestoreSnapshotException {
-
-  }
-
-  @Override
-  public Future<Void> cloneSnapshotAsync(String snapshotName, TableName tableName,
-      boolean restoreAcl) throws IOException, TableExistsException, RestoreSnapshotException {
-    return null;
-  }
-
-  @Override
-  public void execProcedure(String signature, String instance, Map<String, String> props)
-      throws IOException {
-
-  }
-
-  @Override
-  public byte[] execProcedureWithReturn(String signature, String instance,
-      Map<String, String> props) throws IOException {
-    return new byte[0];
-  }
-
-  @Override
-  public boolean isProcedureFinished(String signature, String instance, Map<String, String> props)
-      throws IOException {
-    return false;
-  }
-
-  @Override
-  public List<SnapshotDescription> listSnapshots() throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<SnapshotDescription> listSnapshots(Pattern pattern) throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<SnapshotDescription> listTableSnapshots(Pattern tableNamePattern,
-      Pattern snapshotNamePattern) throws IOException {
-    return null;
-  }
-
-  @Override
-  public void deleteSnapshot(String snapshotName) throws IOException {
-
-  }
-
-  @Override
-  public void deleteSnapshots(Pattern pattern) throws IOException {
-
-  }
-
-  @Override
-  public void deleteTableSnapshots(Pattern tableNamePattern, Pattern snapshotNamePattern)
-      throws IOException {
-
-  }
-
-  @Override
-  public void setQuota(QuotaSettings quota) throws IOException {
-
-  }
-
-  @Override
-  public List<QuotaSettings> getQuota(QuotaFilter filter) throws IOException {
-    return null;
-  }
-
-  @Override
-  public CoprocessorRpcChannel coprocessorService() {
-    return null;
-  }
-
-  @Override
-  public CoprocessorRpcChannel coprocessorService(ServerName serverName) {
-    return null;
-  }
-
-  @Override
-  public void updateConfiguration(ServerName server) throws IOException {
-
-  }
-
-  @Override
-  public void updateConfiguration() throws IOException {
-
-  }
-
-  @Override
-  public List<SecurityCapability> getSecurityCapabilities() throws IOException {
-    return null;
-  }
-
-  @Override
-  public boolean splitSwitch(boolean enabled, boolean synchronous) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean mergeSwitch(boolean enabled, boolean synchronous) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean isSplitEnabled() throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean isMergeEnabled() throws IOException {
-    return false;
-  }
-
-  @Override
-  public Future<Void> addReplicationPeerAsync(String peerId, ReplicationPeerConfig peerConfig,
-      boolean enabled) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> removeReplicationPeerAsync(String peerId) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> enableReplicationPeerAsync(String peerId) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> disableReplicationPeerAsync(String peerId) throws IOException {
-    return null;
-  }
-
-  @Override
-  public ReplicationPeerConfig getReplicationPeerConfig(String peerId) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> updateReplicationPeerConfigAsync(String peerId,
-      ReplicationPeerConfig peerConfig) throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<ReplicationPeerDescription> listReplicationPeers() throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<ReplicationPeerDescription> listReplicationPeers(Pattern pattern) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Future<Void> transitReplicationPeerSyncReplicationStateAsync(String peerId,
-      SyncReplicationState state) throws IOException {
-    return null;
-  }
-
-  @Override
-  public void decommissionRegionServers(List<ServerName> servers, boolean offload)
-      throws IOException {
-
-  }
-
-  @Override
-  public List<ServerName> listDecommissionedRegionServers() throws IOException {
-    return null;
-  }
-
-  @Override
-  public void recommissionRegionServer(ServerName server, List<byte[]> encodedRegionNames)
-      throws IOException {
-
-  }
-
-  @Override
-  public List<TableCFs> listReplicatedTableCFs() throws IOException {
-    return null;
-  }
-
-  @Override
-  public void enableTableReplication(TableName tableName) throws IOException {
-
-  }
-
-  @Override
-  public void disableTableReplication(TableName tableName) throws IOException {
-
-  }
-
-  @Override
-  public void clearCompactionQueues(ServerName serverName, Set<String> queues)
-      throws IOException, InterruptedException {
-
-  }
-
-  @Override
-  public List<ServerName> clearDeadServers(List<ServerName> servers) throws IOException {
-    return null;
-  }
-
-  @Override
-  public void cloneTableSchema(TableName tableName, TableName newTableName, boolean preserveSplits)
-      throws IOException {
-
-  }
-
-  @Override
-  public boolean switchRpcThrottle(boolean enable) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean isRpcThrottleEnabled() throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean exceedThrottleQuotaSwitch(boolean enable) throws IOException {
-    return false;
-  }
-
-  @Override
-  public Map<TableName, Long> getSpaceQuotaTableSizes() throws IOException {
-    return null;
-  }
-
-  @Override
-  public Map<TableName, ? extends SpaceQuotaSnapshotView> getRegionServerSpaceQuotaSnapshots(
-      ServerName serverName) throws IOException {
-    return null;
-  }
-
-  @Override
-  public SpaceQuotaSnapshotView getCurrentSpaceQuotaSnapshot(String namespace) throws IOException {
-    return null;
-  }
-
-  @Override
-  public SpaceQuotaSnapshotView getCurrentSpaceQuotaSnapshot(TableName tableName)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public void grant(UserPermission userPermission, boolean mergeExistingPermissions)
-      throws IOException {
-
-  }
-
-  @Override
-  public void revoke(UserPermission userPermission) throws IOException {
-
-  }
-
-  @Override
-  public List<UserPermission> getUserPermissions(
-      GetUserPermissionsRequest getUserPermissionsRequest) throws IOException {
-    return null;
-  }
-
-  @Override
-  public List<Boolean> hasUserPermissions(String userName, List<Permission> permissions)
-      throws IOException {
-    return null;
-  }
-
-  @Override
-  public boolean snapshotCleanupSwitch(boolean on, boolean synchronous) throws IOException {
-    return false;
-  }
-
-  @Override
-  public boolean isSnapshotCleanupEnabled() throws IOException {
-    return false;
-  }
-
-  @Override
+  /**
+   * Gets {@code RSGroupInfo} for given group name.
+   */
   public RSGroupInfo getRSGroupInfo(String groupName) throws IOException {
-    return getRSGroup(groupName);
-  }
-
-  @Override
-  public void moveServers(Set<Address> servers, String targetGroup) throws IOException {
-    moveToRSGroup(servers, targetGroup);
-  }
-
-  @Override
-  public void addRSGroup(String groupName) throws IOException {
-    AddRSGroupRequest request = AddRSGroupRequest.newBuilder().setRSGroupName(groupName).build();
-    try {
-      stub.addRSGroup(null, request);
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
-  }
-
-  @Override
-  public RSGroupInfo getRSGroup(String groupName) throws IOException {
     try {
       GetRSGroupInfoResponse resp = stub.getRSGroupInfo(null,
-          GetRSGroupInfoRequest.newBuilder().setRSGroupName(groupName).build());
-      if (resp.hasRSGroupInfo()) {
-        return ProtobufUtil.toGroupInfo(resp.getRSGroupInfo());
-      }
-      return null;
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
-  }
-
-  @Override
-  public RSGroupInfo getRSGroup(Address hostPort) throws IOException {
-    GetRSGroupInfoOfServerRequest request = GetRSGroupInfoOfServerRequest.newBuilder().setServer(
-        HBaseProtos.ServerName.newBuilder().setHostName(hostPort.getHostname())
-            .setPort(hostPort.getPort()).build()).build();
-    try {
-      GetRSGroupInfoOfServerResponse resp = stub.getRSGroupInfoOfServer(null, request);
+        GetRSGroupInfoRequest.newBuilder().setRSGroupName(groupName).build());
       if (resp.hasRSGroupInfo()) {
         return ProtobufUtil.toGroupInfo(resp.getRSGroupInfo());
       }
@@ -971,10 +80,12 @@ public class RSGroupAdminClient implements RSGroupAdmin, Admin {
     }
   }
 
-  @Override
-  public RSGroupInfo getRSGroup(TableName tableName) throws IOException {
+  /**
+   * Gets {@code RSGroupInfo} for the given table's group.
+   */
+  public RSGroupInfo getRSGroupInfoOfTable(TableName tableName) throws IOException {
     GetRSGroupInfoOfTableRequest request = GetRSGroupInfoOfTableRequest.newBuilder()
-        .setTableName(ProtobufUtil.toProtoTableName(tableName)).build();
+      .setTableName(ProtobufUtil.toProtoTableName(tableName)).build();
     try {
       GetRSGroupInfoOfTableResponse resp = stub.getRSGroupInfoOfTable(null, request);
       if (resp.hasRSGroupInfo()) {
@@ -986,71 +97,74 @@ public class RSGroupAdminClient implements RSGroupAdmin, Admin {
     }
   }
 
-  @Override
-  public void removeRSGroup(String name) throws IOException {
-    RemoveRSGroupRequest request = RemoveRSGroupRequest.newBuilder().setRSGroupName(name).build();
+  /**
+   * Move given set of servers to the specified target RegionServer group.
+   */
+  public void moveServers(Set<Address> servers, String targetGroup) throws IOException {
+    Set<HBaseProtos.ServerName> hostPorts = Sets.newHashSet();
+    for (Address el : servers) {
+      hostPorts.add(HBaseProtos.ServerName.newBuilder().setHostName(el.getHostname())
+        .setPort(el.getPort()).build());
+    }
+    MoveServersRequest request =
+      MoveServersRequest.newBuilder().setTargetGroup(targetGroup).addAllServers(hostPorts).build();
     try {
-      stub.removeRSGroup(null, request);
+      stub.moveServers(null, request);
     } catch (ServiceException e) {
       throw ProtobufUtil.handleRemoteException(e);
     }
   }
 
-  @Override
-  public void removeRSGroup(Set<Address> servers) throws IOException {
-    Set<HBaseProtos.ServerName> hostPorts = Sets.newHashSet();
-    for (Address el : servers) {
-      hostPorts.add(
-          HBaseProtos.ServerName.newBuilder().setHostName(el.getHostname()).setPort(el.getPort())
-              .build());
+  /**
+   * Move given set of tables to the specified target RegionServer group. This will unassign all of
+   * a table's region so it can be reassigned to the correct group.
+   */
+  public void moveTables(Set<TableName> tables, String targetGroup) throws IOException {
+    MoveTablesRequest.Builder builder = MoveTablesRequest.newBuilder().setTargetGroup(targetGroup);
+    for (TableName tableName : tables) {
+      builder.addTableName(ProtobufUtil.toProtoTableName(tableName));
+      if (!admin.tableExists(tableName)) {
+        throw new TableNotFoundException(tableName);
+      }
     }
-    RemoveServersRequest request =
-        RemoveServersRequest.newBuilder().addAllServers(hostPorts).build();
     try {
-      stub.removeServers(null, request);
+      stub.moveTables(null, builder.build());
     } catch (ServiceException e) {
       throw ProtobufUtil.handleRemoteException(e);
     }
   }
 
-  @Override
-  public void moveToRSGroup(Set<Address> servers, String targetGroup) throws IOException {
-    Set<HBaseProtos.ServerName> hostPorts = Sets.newHashSet();
-    for (Address el : servers) {
-      hostPorts.add(
-          HBaseProtos.ServerName.newBuilder().setHostName(el.getHostname()).setPort(el.getPort())
-              .build());
-    }
-    MoveServersRequest request =
-        MoveServersRequest.newBuilder().setTargetGroup(targetGroup).addAllServers(hostPorts)
-            .build();
+  /**
+   * Creates a new RegionServer group with the given name.
+   */
+  public void addRSGroup(String groupName) throws IOException {
+    AddRSGroupRequest request = AddRSGroupRequest.newBuilder().setRSGroupName(groupName).build();
     try {
-      stub.moveServers(null, request);
+      stub.addRSGroup(null, request);
     } catch (ServiceException e) {
       throw ProtobufUtil.handleRemoteException(e);
     }
   }
 
-  @Override
-  public void setRSGroup(Set<TableName> tables, String groupName) throws IOException {
-    MoveTablesRequest.Builder builder = MoveTablesRequest.newBuilder().setTargetGroup(groupName);
-    for (TableName tableName : tables) {
-      builder.addTableName(ProtobufUtil.toProtoTableName(tableName));
-      if (!admin.tableExists(tableName)) {
-        throw new TableNotFoundException(tableName);
-      }
-    }
+  /**
+   * Removes RegionServer group associated with the given name.
+   */
+  public void removeRSGroup(String name) throws IOException {
+    RemoveRSGroupRequest request = RemoveRSGroupRequest.newBuilder().setRSGroupName(name).build();
     try {
-      stub.moveTables(null, builder.build());
+      stub.removeRSGroup(null, request);
     } catch (ServiceException e) {
       throw ProtobufUtil.handleRemoteException(e);
     }
   }
 
-  @Override
+  /**
+   * Balance regions in the given RegionServer group.
+   * @return boolean Whether balance ran or not
+   */
   public boolean balanceRSGroup(String groupName) throws IOException {
     BalanceRSGroupRequest request =
-        BalanceRSGroupRequest.newBuilder().setRSGroupName(groupName).build();
+      BalanceRSGroupRequest.newBuilder().setRSGroupName(groupName).build();
     try {
       return stub.balanceRSGroup(null, request).getBalanceRan();
     } catch (ServiceException e) {
@@ -1058,12 +172,13 @@ public class RSGroupAdminClient implements RSGroupAdmin, Admin {
     }
   }
 
-  @Override
+  /**
+   * Lists current set of RegionServer groups.
+   */
   public List<RSGroupInfo> listRSGroups() throws IOException {
     try {
-      List<RSGroupProtos.RSGroupInfo> resp =
-          stub.listRSGroupInfos(null, ListRSGroupInfosRequest.getDefaultInstance())
-              .getRSGroupInfoList();
+      List<RSGroupProtos.RSGroupInfo> resp = stub
+        .listRSGroupInfos(null, ListRSGroupInfosRequest.getDefaultInstance()).getRSGroupInfoList();
       List<RSGroupInfo> result = new ArrayList<>(resp.size());
       for (RSGroupProtos.RSGroupInfo entry : resp) {
         result.add(ProtobufUtil.toGroupInfo(entry));
@@ -1074,14 +189,72 @@ public class RSGroupAdminClient implements RSGroupAdmin, Admin {
     }
   }
 
-  @Override
+  /**
+   * Retrieve the RSGroupInfo a server is affiliated to
+   * @param hostPort HostPort to get RSGroupInfo for
+   */
   public RSGroupInfo getRSGroupOfServer(Address hostPort) throws IOException {
-    return getRSGroup(hostPort);
+    GetRSGroupInfoOfServerRequest request =
+      GetRSGroupInfoOfServerRequest.newBuilder().setServer(HBaseProtos.ServerName.newBuilder()
+        .setHostName(hostPort.getHostname()).setPort(hostPort.getPort()).build()).build();
+    try {
+      GetRSGroupInfoOfServerResponse resp = stub.getRSGroupInfoOfServer(null, request);
+      if (resp.hasRSGroupInfo()) {
+        return ProtobufUtil.toGroupInfo(resp.getRSGroupInfo());
+      }
+      return null;
+    } catch (ServiceException e) {
+      throw ProtobufUtil.handleRemoteException(e);
+    }
   }
 
-  @Override
-  public void removeServers(Set<Address> servers) throws IOException {
-    removeRSGroup(servers);
+  /**
+   * Move given set of servers and tables to the specified target RegionServer group.
+   * @param servers set of servers to move
+   * @param tables set of tables to move
+   * @param targetGroup the target group name
+   * @throws IOException if moving the server and tables fail
+   */
+  public void moveServersAndTables(Set<Address> servers, Set<TableName> tables, String targetGroup)
+    throws IOException {
+    MoveServersAndTablesRequest.Builder builder =
+      MoveServersAndTablesRequest.newBuilder().setTargetGroup(targetGroup);
+    for (Address el : servers) {
+      builder.addServers(HBaseProtos.ServerName.newBuilder().setHostName(el.getHostname())
+        .setPort(el.getPort()).build());
+    }
+    for (TableName tableName : tables) {
+      builder.addTableName(ProtobufUtil.toProtoTableName(tableName));
+      if (!admin.tableExists(tableName)) {
+        throw new TableNotFoundException(tableName);
+      }
+    }
+    try {
+      stub.moveServersAndTables(null, builder.build());
+    } catch (ServiceException e) {
+      throw ProtobufUtil.handleRemoteException(e);
+    }
   }
 
+  /**
+   * Remove decommissioned servers from rsgroup. 1. Sometimes we may find the server aborted due to
+   * some hardware failure and we must offline the server for repairing. Or we need to move some
+   * servers to join other clusters. So we need to remove these servers from the rsgroup. 2.
+   * Dead/recovering/live servers will be disallowed.
+   * @param servers set of servers to remove
+   */
+  public void removeServers(Set<Address> servers) throws IOException {
+    Set<HBaseProtos.ServerName> hostPorts = Sets.newHashSet();
+    for (Address el : servers) {
+      hostPorts.add(HBaseProtos.ServerName.newBuilder().setHostName(el.getHostname())
+        .setPort(el.getPort()).build());
+    }
+    RemoveServersRequest request =
+      RemoveServersRequest.newBuilder().addAllServers(hostPorts).build();
+    try {
+      stub.removeServers(null, request);
+    } catch (ServiceException e) {
+      throw ProtobufUtil.handleRemoteException(e);
+    }
+  }
 }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/EnableRSGroupsTestBase.java
similarity index 73%
copy from hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java
copy to hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/EnableRSGroupsTestBase.java
index d15c7a8..dc3144f 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/EnableRSGroupsTestBase.java
@@ -21,32 +21,18 @@ 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);
+public abstract class EnableRSGroupsTestBase {
 
-  protected static final Logger LOG = LoggerFactory.getLogger(TestEnableRSGroups.class);
+  private static final Logger LOG = LoggerFactory.getLogger(TestEnableRSGroupsCompatibility.class);
 
   private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
 
@@ -63,16 +49,15 @@ public class TestEnableRSGroups {
     TEST_UTIL.shutdownMiniCluster();
   }
 
+  protected abstract void enableRSGroup(Configuration conf);
+
   @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());
+    enableRSGroup(TEST_UTIL.getMiniHBaseCluster().getConfiguration());
 
     TEST_UTIL.getMiniHBaseCluster().startMaster();
     TEST_UTIL.getMiniHBaseCluster().waitForActiveAndReadyMaster(60000);
@@ -83,7 +68,7 @@ public class TestEnableRSGroups {
 
     // wait RSGroupBasedLoadBalancer online
     RSGroupBasedLoadBalancer loadBalancer =
-        (RSGroupBasedLoadBalancer) TEST_UTIL.getMiniHBaseCluster().getMaster().getLoadBalancer();
+      (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...");
@@ -92,5 +77,4 @@ public class TestEnableRSGroups {
 
     assertTrue(loadBalancer.isOnline());
   }
-
 }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java
index d15c7a8..72dacb5 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java
@@ -17,80 +17,23 @@
  */
 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.apache.hadoop.hbase.testclassification.RSGroupTests;
 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 {
+@Category({ RSGroupTests.class, MediumTests.class })
+public class TestEnableRSGroups extends EnableRSGroupsTestBase {
 
   @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);
-    }
+    HBaseClassTestRule.forClass(TestEnableRSGroups.class);
 
-    assertTrue(loadBalancer.isOnline());
+  @Override
+  protected void enableRSGroup(Configuration conf) {
+    conf.setBoolean(RSGroupInfoManager.RS_GROUP_ENABLED, true);
   }
 
 }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroupsCompatibility.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroupsCompatibility.java
new file mode 100644
index 0000000..92faa5b
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroupsCompatibility.java
@@ -0,0 +1,45 @@
+/**
+ * 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 org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
+import org.junit.ClassRule;
+import org.junit.experimental.categories.Category;
+
+/**
+ * Test enable RSGroup using the old coprocessor way, to make sure that we keep compatible with old
+ * config way.
+ */
+@Category({ RSGroupTests.class, MediumTests.class })
+public class TestEnableRSGroupsCompatibility extends EnableRSGroupsTestBase {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+    HBaseClassTestRule.forClass(TestEnableRSGroupsCompatibility.class);
+
+  @Override
+  protected void enableRSGroup(Configuration conf) {
+    conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, RSGroupAdminEndpoint.class.getName());
+    conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, RSGroupBasedLoadBalancer.class.getName());
+  }
+}
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestMigrateRSGroupInfo.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestMigrateRSGroupInfo.java
index 122abc4..c3e4fe7 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestMigrateRSGroupInfo.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestMigrateRSGroupInfo.java
@@ -39,6 +39,7 @@ import org.apache.hadoop.hbase.master.HMaster;
 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
 import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.zookeeper.KeeperException;
 import org.junit.After;
@@ -46,14 +47,11 @@ import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 /**
  * Testcase for HBASE-22819
  */
-@RunWith(Parameterized.class)
-@Category({ MediumTests.class })
+@Category({ RSGroupTests.class, MediumTests.class })
 public class TestMigrateRSGroupInfo extends TestRSGroupsBase {
 
   @ClassRule
@@ -109,10 +107,9 @@ public class TestMigrateRSGroupInfo extends TestRSGroupsBase {
 
   @Test
   public void testMigrate() throws IOException, InterruptedException {
-    setAdmin();
     String groupName = getNameWithoutIndex(name.getMethodName());
     addGroup(groupName, TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().size() - 1);
-    RSGroupInfo rsGroupInfo = rsGroupAdmin.getRSGroup(groupName);
+    RSGroupInfo rsGroupInfo = ADMIN.getRSGroup(groupName);
     assertTrue(rsGroupInfo.getTables().isEmpty());
     for (int i = 0; i < NUM_TABLES; i++) {
       rsGroupInfo.addTable(TableName.valueOf(TABLE_NAME_PREFIX + i));
@@ -130,7 +127,7 @@ public class TestMigrateRSGroupInfo extends TestRSGroupsBase {
     // wait until we can get the rs group info for a table
     TEST_UTIL.waitFor(30000, () -> {
       try {
-        rsGroupAdmin.getRSGroup(TableName.valueOf(TABLE_NAME_PREFIX + 0));
+        RS_GROUP_ADMIN_CLIENT.getRSGroupInfoOfTable(TableName.valueOf(TABLE_NAME_PREFIX + 0));
         return true;
       } catch (IOException e) {
         return false;
@@ -139,7 +136,7 @@ public class TestMigrateRSGroupInfo extends TestRSGroupsBase {
     // confirm that before migrating, we could still get the correct rs group for a table.
     for (int i = 0; i < NUM_TABLES; i++) {
       RSGroupInfo info =
-        rsGroupAdmin.getRSGroup(TableName.valueOf(TABLE_NAME_PREFIX + i));
+        RS_GROUP_ADMIN_CLIENT.getRSGroupInfoOfTable(TableName.valueOf(TABLE_NAME_PREFIX + i));
       assertEquals(rsGroupInfo.getName(), info.getName());
       assertEquals(NUM_TABLES, info.getTables().size());
     }
@@ -175,7 +172,7 @@ public class TestMigrateRSGroupInfo extends TestRSGroupsBase {
     // make sure we could still get the correct rs group info after migration
     for (int i = 0; i < NUM_TABLES; i++) {
       RSGroupInfo info =
-        rsGroupAdmin.getRSGroup(TableName.valueOf(TABLE_NAME_PREFIX + i));
+        RS_GROUP_ADMIN_CLIENT.getRSGroupInfoOfTable(TableName.valueOf(TABLE_NAME_PREFIX + i));
       assertEquals(rsGroupInfo.getName(), info.getName());
       assertEquals(NUM_TABLES, info.getTables().size());
     }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupMajorCompactionTTL.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupMajorCompactionTTL.java
index 9b3dcbb..6725cf0 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupMajorCompactionTTL.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupMajorCompactionTTL.java
@@ -6,38 +6,41 @@
  * 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
+ *
+ *     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.assertTrue;
 
 import java.util.List;
-
 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.MiniHBaseCluster;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.Waiter;
-import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
 import org.apache.hadoop.hbase.master.HMaster;
 import org.apache.hadoop.hbase.master.ServerManager;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
 import org.apache.hadoop.hbase.util.compaction.TestMajorCompactorTTL;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
 import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
 
+@Category({ RSGroupTests.class, MediumTests.class })
 public class TestRSGroupMajorCompactionTTL extends TestMajorCompactorTTL {
   @ClassRule
   public static final HBaseClassTestRule CLASS_RULE =
@@ -50,8 +53,7 @@ public class TestRSGroupMajorCompactionTTL extends TestMajorCompactorTTL {
   public void setUp() throws Exception {
     utility = new HBaseTestingUtility();
     Configuration conf = utility.getConfiguration();
-    conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, RSGroupBasedLoadBalancer.class.getName());
-    conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, RSGroupAdminEndpoint.class.getName());
+    conf.setBoolean(RSGroupInfoManager.RS_GROUP_ENABLED, true);
     conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, NUM_SLAVES_BASE);
     conf.setInt("hbase.hfile.compaction.discharger.interval", 10);
     utility.startMiniCluster(NUM_SLAVES_BASE);
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java
index 614b6c4..255f80d 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java
@@ -24,13 +24,11 @@ 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 java.util.SortedSet;
 import org.apache.hadoop.hbase.HBaseClassTestRule;
-import org.apache.hadoop.hbase.MiniHBaseCluster;
 import org.apache.hadoop.hbase.NamespaceDescriptor;
 import org.apache.hadoop.hbase.ServerName;
 import org.apache.hadoop.hbase.TableExistsException;
@@ -45,31 +43,28 @@ import org.apache.hadoop.hbase.constraint.ConstraintException;
 import org.apache.hadoop.hbase.master.TableNamespaceManager;
 import org.apache.hadoop.hbase.net.Address;
 import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
 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.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
 
-@RunWith(Parameterized.class)
-@Category({ MediumTests.class })
+@Category({ RSGroupTests.class, 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);
+  private static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsAdmin1.class);
 
   @BeforeClass
   public static void setUp() throws Exception {
@@ -98,7 +93,7 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
 
     for (String entry : badNames) {
       try {
-        rsGroupAdmin.addRSGroup(entry);
+        ADMIN.addRSGroup(entry);
         fail("Expected a constraint exception for: " + entry);
       } catch (ConstraintException ex) {
         // expected
@@ -106,42 +101,41 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
     }
 
     for (String entry : goodNames) {
-      rsGroupAdmin.addRSGroup(entry);
+      ADMIN.addRSGroup(entry);
     }
   }
 
   @Test
   public void testBogusArgs() throws Exception {
-    assertNull(rsGroupAdmin.getRSGroup(TableName.valueOf("nonexistent")));
-    assertNull(rsGroupAdmin.getRSGroup(Address.fromParts("bogus", 123)));
-    assertNull(rsGroupAdmin.getRSGroup("bogus"));
+    assertNull(ADMIN.getRSGroup(TableName.valueOf("nonexistent")));
+    assertNull(ADMIN.getRSGroup(Address.fromParts("bogus", 123)));
+    assertNull(ADMIN.getRSGroup("bogus"));
 
     try {
-      rsGroupAdmin.removeRSGroup("bogus");
+      ADMIN.removeRSGroup("bogus");
       fail("Expected removing bogus group to fail");
     } catch (ConstraintException ex) {
       // expected
     }
 
     try {
-      rsGroupAdmin.setRSGroup(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus");
+      ADMIN.setRSGroup(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus");
       fail("Expected set table to bogus group fail");
     } catch (ConstraintException | TableNotFoundException ex) {
       // expected
     }
 
     try {
-      rsGroupAdmin.moveToRSGroup(Sets.newHashSet(Address.fromParts("bogus", 123)),
-          "bogus");
+      ADMIN.moveServersToRSGroup(Sets.newHashSet(Address.fromParts("bogus", 123)), "bogus");
       fail("Expected move with bogus group to fail");
     } catch (ConstraintException ex) {
       // expected
     }
 
     try {
-      admin.balancerSwitch(true, true);
-      rsGroupAdmin.balanceRSGroup("bogus");
-      admin.balancerSwitch(false, true);
+      ADMIN.balancerSwitch(true, true);
+      ADMIN.balanceRSGroup("bogus");
+      ADMIN.balancerSwitch(false, true);
       fail("Expected move with bogus group to fail");
     } catch (ConstraintException ex) {
       // expected
@@ -150,36 +144,36 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
 
   @Test
   public void testNamespaceConstraint() throws Exception {
-    String nsName = tablePrefix + "_foo";
-    String groupName = tablePrefix + "_foo";
+    String nsName = TABLE_PREFIX + "_foo";
+    String groupName = TABLE_PREFIX + "_foo";
     LOG.info("testNamespaceConstraint");
     addGroup(groupName, 1);
-    assertTrue(observer.preAddRSGroupCalled);
-    assertTrue(observer.postAddRSGroupCalled);
+    assertTrue(OBSERVER.preAddRSGroupCalled);
+    assertTrue(OBSERVER.postAddRSGroupCalled);
 
-    admin.createNamespace(NamespaceDescriptor.create(nsName)
+    ADMIN.createNamespace(NamespaceDescriptor.create(nsName)
       .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName).build());
-    RSGroupInfo rsGroupInfo = rsGroupAdmin.getRSGroup(groupName);
-    rsGroupAdmin.moveToRSGroup(rsGroupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    RSGroupInfo rsGroupInfo = ADMIN.getRSGroup(groupName);
+    ADMIN.moveServersToRSGroup(rsGroupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
     // test removing a referenced group
     try {
-      rsGroupAdmin.removeRSGroup(groupName);
+      ADMIN.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)
+    ADMIN.modifyNamespace(NamespaceDescriptor.create(nsName)
       .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName).build());
-    String anotherGroup = tablePrefix + "_anotherGroup";
-    rsGroupAdmin.addRSGroup(anotherGroup);
+    String anotherGroup = TABLE_PREFIX + "_anotherGroup";
+    ADMIN.addRSGroup(anotherGroup);
     // test add non-existent group
-    admin.deleteNamespace(nsName);
-    rsGroupAdmin.removeRSGroup(groupName);
-    assertTrue(observer.preRemoveRSGroupCalled);
-    assertTrue(observer.postRemoveRSGroupCalled);
+    ADMIN.deleteNamespace(nsName);
+    ADMIN.removeRSGroup(groupName);
+    assertTrue(OBSERVER.preRemoveRSGroupCalled);
+    assertTrue(OBSERVER.postRemoveRSGroupCalled);
     try {
-      admin.createNamespace(NamespaceDescriptor.create(nsName)
+      ADMIN.createNamespace(NamespaceDescriptor.create(nsName)
         .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "foo").build());
       fail("Expected a constraint exception");
     } catch (IOException ex) {
@@ -187,90 +181,44 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
   }
 
   @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 testGetRSGroupInfoCPHookCalled() throws Exception {
-    rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
-    if (rsGroupAdmin instanceof RSGroupAdminClient) {
-      assertTrue(observer.preGetRSGroupInfoCalled);
-      assertTrue(observer.postGetRSGroupInfoCalled);
-    }
-  }
-
-  @Test
-  public void testGetRSGroupInfoOfTableCPHookCalled() throws Exception {
-    rsGroupAdmin.getRSGroup(TableName.META_TABLE_NAME);
-    if (rsGroupAdmin instanceof RSGroupAdminClient) {
-      assertTrue(observer.preGetRSGroupInfoOfTableCalled);
-      assertTrue(observer.postGetRSGroupInfoOfTableCalled);
-    }
-  }
-
-  @Test
-  public void testListRSGroupsCPHookCalled() throws Exception {
-    rsGroupAdmin.listRSGroups();
-    assertTrue(observer.preListRSGroupsCalled);
-    assertTrue(observer.postListRSGroupsCalled);
-  }
-
-  @Test
-  public void testGetRSGroupInfoOfServerCPHookCalled() throws Exception {
-    ServerName masterServerName = ((MiniHBaseCluster) cluster).getMaster().getServerName();
-    rsGroupAdmin.getRSGroup(masterServerName.getAddress());
-    if (rsGroupAdmin instanceof RSGroupAdminClient) {
-      assertTrue(observer.preGetRSGroupInfoOfServerCalled);
-      assertTrue(observer.postGetRSGroupInfoOfServerCalled);
-    }
-  }
-
-  @Test
   public void testFailRemoveGroup() throws IOException, InterruptedException {
-    int initNumGroups = rsGroupAdmin.listRSGroups().size();
+    int initNumGroups = ADMIN.listRSGroups().size();
     addGroup("bar", 3);
     TEST_UTIL.createTable(tableName, Bytes.toBytes("f"));
-    rsGroupAdmin.setRSGroup(Sets.newHashSet(tableName), "bar");
-    RSGroupInfo barGroup = rsGroupAdmin.getRSGroup("bar");
+    ADMIN.setRSGroup(Sets.newHashSet(tableName), "bar");
+    RSGroupInfo barGroup = RS_GROUP_ADMIN_CLIENT.getRSGroupInfo("bar");
     // group is not empty therefore it should fail
     try {
-      rsGroupAdmin.removeRSGroup(barGroup.getName());
+      ADMIN.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.moveToRSGroup(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+      ADMIN.moveServersToRSGroup(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
       fail("Expected move servers to fail");
     } catch (IOException e) {
     }
 
-    rsGroupAdmin.setRSGroup(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.setRSGroup(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP);
     try {
-      rsGroupAdmin.removeRSGroup(barGroup.getName());
+      ADMIN.removeRSGroup(barGroup.getName());
       fail("Expected move servers to fail");
     } catch (IOException e) {
     }
 
-    rsGroupAdmin.moveToRSGroup(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
-    rsGroupAdmin.removeRSGroup(barGroup.getName());
+    ADMIN.moveServersToRSGroup(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.removeRSGroup(barGroup.getName());
 
-    Assert.assertEquals(initNumGroups, rsGroupAdmin.listRSGroups().size());
+    assertEquals(initNumGroups, ADMIN.listRSGroups().size());
   }
 
   @Test
   public void testMultiTableMove() throws Exception {
-    final TableName tableNameA = TableName.valueOf(tablePrefix +
-        getNameWithoutIndex(name.getMethodName()) + "A");
-    final TableName tableNameB = TableName.valueOf(tablePrefix +
-        getNameWithoutIndex(name.getMethodName()) + "B");
+    final TableName tableNameA =
+      TableName.valueOf(TABLE_PREFIX + getNameWithoutIndex(name.getMethodName()) + "A");
+    final TableName tableNameB =
+      TableName.valueOf(TABLE_PREFIX + getNameWithoutIndex(name.getMethodName()) + "B");
     final byte[] familyNameBytes = Bytes.toBytes("f");
     String newGroupName = getGroupName(getNameWithoutIndex(name.getMethodName()));
     final RSGroupInfo newGroup = addGroup(newGroupName, 1);
@@ -293,30 +241,28 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
       }
     });
 
-    RSGroupInfo tableGrpA = rsGroupAdmin.getRSGroup(tableNameA);
+    RSGroupInfo tableGrpA = ADMIN.getRSGroup(tableNameA);
     assertTrue(tableGrpA.getName().equals(RSGroupInfo.DEFAULT_GROUP));
 
-    RSGroupInfo tableGrpB = rsGroupAdmin.getRSGroup(tableNameB);
+    RSGroupInfo tableGrpB = ADMIN.getRSGroup(tableNameB);
     assertTrue(tableGrpB.getName().equals(RSGroupInfo.DEFAULT_GROUP));
     // change table's group
     LOG.info("Moving table [" + tableNameA + "," + tableNameB + "] to " + newGroup.getName());
-    rsGroupAdmin.setRSGroup(Sets.newHashSet(tableNameA, tableNameB), newGroup.getName());
+    ADMIN.setRSGroup(Sets.newHashSet(tableNameA, tableNameB), newGroup.getName());
 
     // verify group change
-    Assert.assertEquals(newGroup.getName(),
-      rsGroupAdmin.getRSGroup(tableNameA).getName());
+    assertEquals(newGroup.getName(), ADMIN.getRSGroup(tableNameA).getName());
 
-    Assert.assertEquals(newGroup.getName(),
-      rsGroupAdmin.getRSGroup(tableNameB).getName());
+    assertEquals(newGroup.getName(), ADMIN.getRSGroup(tableNameB).getName());
 
     // verify tables' not exist in old group
-    Set<TableName> DefaultTables =
-      rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getTables();
-    assertFalse(DefaultTables.contains(tableNameA));
-    assertFalse(DefaultTables.contains(tableNameB));
+    Set<TableName> defaultTables =
+      RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables();
+    assertFalse(defaultTables.contains(tableNameA));
+    assertFalse(defaultTables.contains(tableNameB));
 
     // verify tables' exist in new group
-    Set<TableName> newGroupTables = rsGroupAdmin.getRSGroup(newGroupName).getTables();
+    Set<TableName> newGroupTables = RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(newGroupName).getTables();
     assertTrue(newGroupTables.contains(tableNameA));
     assertTrue(newGroupTables.contains(tableNameB));
   }
@@ -340,17 +286,16 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
       }
     });
 
-    RSGroupInfo tableGrp = rsGroupAdmin.getRSGroup(tableName);
+    RSGroupInfo tableGrp = ADMIN.getRSGroup(tableName);
     LOG.info("got table group info is {}", tableGrp);
     assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
 
     // change table's group
     LOG.info("Moving table " + tableName + " to " + newGroup.getName());
-    rsGroupAdmin.setRSGroup(Sets.newHashSet(tableName), newGroup.getName());
+    ADMIN.setRSGroup(Sets.newHashSet(tableName), newGroup.getName());
 
     // verify group change
-    Assert.assertEquals(newGroup.getName(),
-      rsGroupAdmin.getRSGroup(tableName).getName());
+    assertEquals(newGroup.getName(), ADMIN.getRSGroup(tableName).getName());
 
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
@@ -369,16 +314,15 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
     });
 
     // test truncate
-    admin.disableTable(tableName);
-    admin.truncateTable(tableName, true);
-    Assert.assertEquals(1, rsGroupAdmin.getRSGroup(newGroup.getName()).getTables().size());
-    Assert.assertEquals(tableName,
-      rsGroupAdmin.getRSGroup(newGroup.getName()).getTables().first());
+    ADMIN.disableTable(tableName);
+    ADMIN.truncateTable(tableName, true);
+    assertEquals(1, RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(newGroup.getName()).getTables().size());
+    assertEquals(tableName,
+      RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(newGroup.getName()).getTables().first());
 
     // verify removed table is removed from group
     TEST_UTIL.deleteTable(tableName);
-    Assert.assertEquals(0, rsGroupAdmin.getRSGroup(newGroup.getName()).getTables().size());
-
+    assertEquals(0, RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(newGroup.getName()).getTables().size());
   }
 
   @Test
@@ -399,50 +343,49 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
       }
     });
 
-    RSGroupInfo tableGrp = rsGroupAdmin.getRSGroup(tableName);
+    RSGroupInfo tableGrp = ADMIN.getRSGroup(tableName);
     assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
 
     // test disable table
-    admin.disableTable(tableName);
+    ADMIN.disableTable(tableName);
 
     // change table's group
     LOG.info("Moving table " + tableName + " to " + newGroup.getName());
-    rsGroupAdmin.setRSGroup(Sets.newHashSet(tableName), newGroup.getName());
+    ADMIN.setRSGroup(Sets.newHashSet(tableName), newGroup.getName());
 
     // verify group change
-    Assert.assertEquals(newGroup.getName(),
-      rsGroupAdmin.getRSGroup(tableName).getName());
+    assertEquals(newGroup.getName(), ADMIN.getRSGroup(tableName).getName());
   }
 
   @Test
   public void testNonExistentTableMove() throws Exception {
-    TableName tableName = TableName.valueOf(tablePrefix +
-        getNameWithoutIndex(name.getMethodName()));
-    RSGroupInfo tableGrp = rsGroupAdmin.getRSGroup(tableName);
+    TableName tableName =
+      TableName.valueOf(TABLE_PREFIX + getNameWithoutIndex(name.getMethodName()));
+    RSGroupInfo tableGrp = ADMIN.getRSGroup(tableName);
     assertNull(tableGrp);
 
     // test if table exists already.
-    boolean exist = admin.tableExists(tableName);
+    boolean exist = ADMIN.tableExists(tableName);
     assertFalse(exist);
 
     LOG.info("Moving table " + tableName + " to " + RSGroupInfo.DEFAULT_GROUP);
     try {
-      rsGroupAdmin.setRSGroup(Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
+      ADMIN.setRSGroup(Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
       fail("Table " + tableName + " shouldn't have been successfully moved.");
     } catch (IOException ex) {
       assertTrue(ex instanceof TableNotFoundException);
     }
 
     try {
-      rsGroupAdmin.setRSGroup(Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
-      rsGroupAdmin.moveToRSGroup(Sets.newHashSet(Address.fromParts("bogus", 123)),
-          RSGroupInfo.DEFAULT_GROUP);
+      ADMIN.setRSGroup(Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
+      ADMIN.moveServersToRSGroup(Sets.newHashSet(Address.fromParts("bogus", 123)),
+        RSGroupInfo.DEFAULT_GROUP);
       fail("Table " + tableName + " shouldn't have been successfully moved.");
     } catch (IOException ex) {
       assertTrue(ex instanceof TableNotFoundException);
     }
     // verify group change
-    assertNull(rsGroupAdmin.getRSGroup(tableName));
+    assertNull(ADMIN.getRSGroup(tableName));
   }
 
   @Test
@@ -452,13 +395,13 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
     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);
+    ADMIN.createNamespace(nspDesc);
+    assertEquals(3, ADMIN.listNamespaceDescriptors().length);
     ColumnFamilyDescriptor fam1 = ColumnFamilyDescriptorBuilder.of("fam1");
     TableDescriptor tableDescOne = TableDescriptorBuilder
       .newBuilder(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1"))
       .setColumnFamily(fam1).build();
-    admin.createTable(tableDescOne);
+    ADMIN.createTable(tableDescOne);
 
     TableDescriptor tableDescTwo = TableDescriptorBuilder
       .newBuilder(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2"))
@@ -466,8 +409,8 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
     boolean constraintViolated = false;
 
     try {
-      admin.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 6);
-      Assert.fail("Creation table should fail because of quota violation.");
+      ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 6);
+      fail("Creation table should fail because of quota violation.");
     } catch (Exception exp) {
       assertTrue(exp instanceof IOException);
       constraintViolated = true;
@@ -475,7 +418,7 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
       assertTrue("Constraint not violated for table " + tableDescTwo.getTableName(),
         constraintViolated);
     }
-    List<RSGroupInfo> rsGroupInfoList = rsGroupAdmin.listRSGroups();
+    List<RSGroupInfo> rsGroupInfoList = RS_GROUP_ADMIN_CLIENT.listRSGroups();
     boolean foundTable2 = false;
     boolean foundTable1 = false;
     for (int i = 0; i < rsGroupInfoList.size(); i++) {
@@ -490,14 +433,13 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
     assertTrue("Did not find table1 in rsgroup list", foundTable1);
 
     TEST_UTIL.deleteTable(tableDescOne.getTableName());
-    admin.deleteNamespace(nspDesc.getName());
+    ADMIN.deleteNamespace(nspDesc.getName());
     toggleQuotaCheckAndRestartMiniCluster(false);
 
   }
 
   @Test
-  public void testNotMoveTableToNullRSGroupWhenCreatingExistingTable()
-      throws Exception {
+  public void testNotMoveTableToNullRSGroupWhenCreatingExistingTable() throws Exception {
     // Trigger
     TableName tn1 = TableName.valueOf("t1");
     TEST_UTIL.createTable(tn1, "cf1");
@@ -509,19 +451,18 @@ public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
     }
 
     // Wait then verify
-    //   Could not verify until the rollback of CreateTableProcedure is done
-    //   (that is, the coprocessor finishes its work),
-    //   or the table is still in the "default" rsgroup even though HBASE-21866
-    //   is not fixed.
+    // Could not verify until the rollback of CreateTableProcedure is done
+    // (that is, the coprocessor finishes its work),
+    // or the table is still in the "default" rsgroup even though HBASE-21866
+    // is not fixed.
     TEST_UTIL.waitFor(5000, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return
-            (master.getMasterProcedureExecutor().getActiveExecutorCount() == 0);
+        return (MASTER.getMasterProcedureExecutor().getActiveExecutorCount() == 0);
       }
     });
-    SortedSet<TableName> tables
-        = rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getTables();
+    SortedSet<TableName> tables =
+      RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables();
     assertTrue("Table 't1' must be in 'default' rsgroup", tables.contains(tn1));
 
     // Cleanup
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java
index eb808e3..2faa786 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java
@@ -44,33 +44,30 @@ import org.apache.hadoop.hbase.master.RegionState;
 import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
 import org.apache.hadoop.hbase.net.Address;
 import org.apache.hadoop.hbase.testclassification.LargeTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.Pair;
 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.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
 import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
 
-@RunWith(Parameterized.class)
-@Category({ LargeTests.class })
+@Category({ RSGroupTests.class, LargeTests.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);
+  private static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsAdmin2.class);
 
   @BeforeClass
   public static void setUp() throws Exception {
@@ -121,7 +118,7 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
     }
     // get server which is not a member of new group
     ServerName tmpTargetServer = null;
-    for (ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
+    for (ServerName server : ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
       .getLiveServerMetrics().keySet()) {
       if (!newGroup.containsServer(server.getAddress())) {
         tmpTargetServer = server;
@@ -130,11 +127,11 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
     }
     final ServerName targetServer = tmpTargetServer;
     // move target server to group
-    rsGroupAdmin.moveToRSGroup(Sets.newHashSet(targetServer.getAddress()), newGroup.getName());
+    ADMIN.moveServersToRSGroup(Sets.newHashSet(targetServer.getAddress()), newGroup.getName());
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return admin.getRegions(targetServer).size() <= 0;
+        return ADMIN.getRegions(targetServer).size() <= 0;
       }
     });
 
@@ -146,13 +143,13 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
       public boolean evaluate() throws Exception {
         return getTableRegionMap().get(tableName) != null &&
           getTableRegionMap().get(tableName).size() == 6 &&
-          admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION))
+          ADMIN.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION))
             .getRegionStatesInTransition().size() < 1;
       }
     });
 
     // verify that targetServer didn't open it
-    for (RegionInfo region : admin.getRegions(targetServer)) {
+    for (RegionInfo region : ADMIN.getRegions(targetServer)) {
       if (targetRegion.equals(region.getRegionNameAsString())) {
         fail("Target server opened region");
       }
@@ -161,35 +158,35 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
 
   @Test
   public void testRegionServerMove() throws IOException, InterruptedException {
-    int initNumGroups = rsGroupAdmin.listRSGroups().size();
+    int initNumGroups = ADMIN.listRSGroups().size();
     RSGroupInfo appInfo = addGroup(getGroupName(name.getMethodName()), 1);
     RSGroupInfo adminInfo = addGroup(getGroupName(name.getMethodName()), 1);
-    RSGroupInfo dInfo = rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
-    Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size());
+    RSGroupInfo dInfo = ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
+    assertEquals(initNumGroups + 2, ADMIN.listRSGroups().size());
     assertEquals(1, adminInfo.getServers().size());
     assertEquals(1, appInfo.getServers().size());
     assertEquals(getNumServers() - 2, dInfo.getServers().size());
-    rsGroupAdmin.moveToRSGroup(appInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
-    rsGroupAdmin.removeRSGroup(appInfo.getName());
-    rsGroupAdmin.moveToRSGroup(adminInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
-    rsGroupAdmin.removeRSGroup(adminInfo.getName());
-    Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups);
+    ADMIN.moveServersToRSGroup(appInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.removeRSGroup(appInfo.getName());
+    ADMIN.moveServersToRSGroup(adminInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.removeRSGroup(adminInfo.getName());
+    assertEquals(ADMIN.listRSGroups().size(), initNumGroups);
   }
 
   @Test
   public void testMoveServers() throws Exception {
     // create groups and assign servers
     addGroup("bar", 3);
-    rsGroupAdmin.addRSGroup("foo");
+    ADMIN.addRSGroup("foo");
 
-    RSGroupInfo barGroup = rsGroupAdmin.getRSGroup("bar");
-    RSGroupInfo fooGroup = rsGroupAdmin.getRSGroup("foo");
+    RSGroupInfo barGroup = ADMIN.getRSGroup("bar");
+    RSGroupInfo fooGroup = ADMIN.getRSGroup("foo");
     assertEquals(3, barGroup.getServers().size());
     assertEquals(0, fooGroup.getServers().size());
 
     // test fail bogus server move
     try {
-      rsGroupAdmin.moveToRSGroup(Sets.newHashSet(Address.fromString("foo:9999")), "foo");
+      ADMIN.moveServersToRSGroup(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.";
@@ -199,34 +196,33 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
 
     // test success case
     LOG.info("moving servers " + barGroup.getServers() + " to group foo");
-    rsGroupAdmin.moveToRSGroup(barGroup.getServers(), fooGroup.getName());
+    ADMIN.moveServersToRSGroup(barGroup.getServers(), fooGroup.getName());
 
-    barGroup = rsGroupAdmin.getRSGroup("bar");
-    fooGroup = rsGroupAdmin.getRSGroup("foo");
+    barGroup = ADMIN.getRSGroup("bar");
+    fooGroup = ADMIN.getRSGroup("foo");
     assertEquals(0, barGroup.getServers().size());
     assertEquals(3, fooGroup.getServers().size());
 
     LOG.info("moving servers " + fooGroup.getServers() + " to group default");
-    rsGroupAdmin.moveToRSGroup(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.moveServersToRSGroup(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
 
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return getNumServers() == rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP)
-          .getServers().size();
+        return getNumServers() == ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size();
       }
     });
 
-    fooGroup = rsGroupAdmin.getRSGroup("foo");
+    fooGroup = ADMIN.getRSGroup("foo");
     assertEquals(0, fooGroup.getServers().size());
 
     // test group removal
     LOG.info("Remove group " + barGroup.getName());
-    rsGroupAdmin.removeRSGroup(barGroup.getName());
-    Assert.assertEquals(null, rsGroupAdmin.getRSGroup(barGroup.getName()));
+    ADMIN.removeRSGroup(barGroup.getName());
+    assertEquals(null, ADMIN.getRSGroup(barGroup.getName()));
     LOG.info("Remove group " + fooGroup.getName());
-    rsGroupAdmin.removeRSGroup(fooGroup.getName());
-    Assert.assertEquals(null, rsGroupAdmin.getRSGroup(fooGroup.getName()));
+    ADMIN.removeRSGroup(fooGroup.getName());
+    assertEquals(null, ADMIN.getRSGroup(fooGroup.getName()));
   }
 
   @Test
@@ -238,7 +234,7 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
 
     // remove online servers
     try {
-      rsGroupAdmin.removeRSGroup(Sets.newHashSet(targetServer.getAddress()));
+      ADMIN.removeServersFromRSGroup(Sets.newHashSet(targetServer.getAddress()));
       fail("Online servers shouldn't have been successfully removed.");
     } catch (IOException ex) {
       String exp =
@@ -249,12 +245,12 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
     assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
 
     // remove dead servers
-    NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size();
+    NUM_DEAD_SERVERS = CLUSTER.getClusterMetrics().getDeadServerNames().size();
     try {
       // stopping may cause an exception
       // due to the connection loss
       LOG.info("stopping server " + targetServer.getServerName());
-      admin.stopRegionServer(targetServer.getAddress().toString());
+      ADMIN.stopRegionServer(targetServer.getAddress().toString());
       NUM_DEAD_SERVERS++;
     } catch (Exception e) {
     }
@@ -263,13 +259,13 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
     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;
+        return !MASTER.getServerManager().areDeadServersInProgress() &&
+          CLUSTER.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS;
       }
     });
 
     try {
-      rsGroupAdmin.removeRSGroup(Sets.newHashSet(targetServer.getAddress()));
+      ADMIN.removeServersFromRSGroup(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," +
@@ -282,20 +278,20 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
     // remove decommissioned servers
     List<ServerName> serversToDecommission = new ArrayList<>();
     targetServer = getServerName(iterator.next());
-    assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer));
+    assertTrue(MASTER.getServerManager().getOnlineServers().containsKey(targetServer));
     serversToDecommission.add(targetServer);
 
-    admin.decommissionRegionServers(serversToDecommission, true);
-    assertEquals(1, admin.listDecommissionedRegionServers().size());
+    ADMIN.decommissionRegionServers(serversToDecommission, true);
+    assertEquals(1, ADMIN.listDecommissionedRegionServers().size());
 
     assertTrue(newGroup.getServers().contains(targetServer.getAddress()));
-    rsGroupAdmin.removeRSGroup(Sets.newHashSet(targetServer.getAddress()));
-    Set<Address> newGroupServers = rsGroupAdmin.getRSGroup(newGroup.getName()).getServers();
+    ADMIN.removeServersFromRSGroup(Sets.newHashSet(targetServer.getAddress()));
+    Set<Address> newGroupServers = ADMIN.getRSGroup(newGroup.getName()).getServers();
     assertFalse(newGroupServers.contains(targetServer.getAddress()));
     assertEquals(2, newGroupServers.size());
 
-    assertTrue(observer.preRemoveServersCalled);
-    assertTrue(observer.postRemoveServersCalled);
+    assertTrue(OBSERVER.preRemoveServersCalled);
+    assertTrue(OBSERVER.postRemoveServersCalled);
   }
 
   @Test
@@ -319,26 +315,25 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
 
     // get server which is not a member of new group
     ServerName targetServer = null;
-    for (ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
+    for (ServerName server : ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
       .getLiveServerMetrics().keySet()) {
       if (!newGroup.containsServer(server.getAddress()) &&
-        !rsGroupAdmin.getRSGroup("master").containsServer(server.getAddress())) {
+        !ADMIN.getRSGroup("master").containsServer(server.getAddress())) {
         targetServer = server;
         break;
       }
     }
 
-    LOG.debug("Print group info : " + rsGroupAdmin.listRSGroups());
-    int oldDefaultGroupServerSize =
-      rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size();
+    LOG.debug("Print group info : " + ADMIN.listRSGroups());
+    int oldDefaultGroupServerSize = ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size();
     int oldDefaultGroupTableSize =
-      rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getTables().size();
+      RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size();
 
     // test fail bogus server move
     try {
-      rsGroupAdmin.moveToRSGroup(Sets.newHashSet(Address.fromString("foo:9999")),
-          newGroup.getName());
-      rsGroupAdmin.setRSGroup(Sets.newHashSet(tableName), newGroup.getName());
+      ADMIN.moveServersToRSGroup(Sets.newHashSet(Address.fromString("foo:9999")),
+        newGroup.getName());
+      ADMIN.setRSGroup(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.";
@@ -347,19 +342,19 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
     }
 
     // test move when src = dst
-    rsGroupAdmin.moveToRSGroup(Sets.newHashSet(targetServer.getAddress()),
-        RSGroupInfo.DEFAULT_GROUP);
-    rsGroupAdmin.setRSGroup(Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.moveServersToRSGroup(Sets.newHashSet(targetServer.getAddress()),
+      RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.setRSGroup(Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
 
     // verify default group info
-    Assert.assertEquals(oldDefaultGroupServerSize,
-      rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size());
-    Assert.assertEquals(oldDefaultGroupTableSize,
-      rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getTables().size());
+    assertEquals(oldDefaultGroupServerSize,
+      ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size());
+    assertEquals(oldDefaultGroupTableSize,
+      RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size());
 
     // verify new group info
-    Assert.assertEquals(1, rsGroupAdmin.getRSGroup(newGroup.getName()).getServers().size());
-    Assert.assertEquals(0, rsGroupAdmin.getRSGroup(newGroup.getName()).getTables().size());
+    assertEquals(1, ADMIN.getRSGroup(newGroup.getName()).getServers().size());
+    assertEquals(0, RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(newGroup.getName()).getTables().size());
 
     // get all region to move targetServer
     List<String> regionList = getTableRegionMap().get(tableName);
@@ -375,63 +370,61 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
         return getTableRegionMap().get(tableName) != null &&
           getTableRegionMap().get(tableName).size() == 5 &&
           getTableServerRegionMap().get(tableName).size() == 1 &&
-          admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION))
+          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());
+    assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size());
 
     // move targetServer and table to newGroup
     LOG.info("moving server and table to newGroup");
-    rsGroupAdmin.moveToRSGroup(Sets.newHashSet(targetServer.getAddress()),
-        newGroup.getName());
-    rsGroupAdmin.setRSGroup(Sets.newHashSet(tableName), newGroup.getName());
+    ADMIN.moveServersToRSGroup(Sets.newHashSet(targetServer.getAddress()), newGroup.getName());
+    ADMIN.setRSGroup(Sets.newHashSet(tableName), newGroup.getName());
 
     // verify group change
-    Assert.assertEquals(newGroup.getName(),
-      rsGroupAdmin.getRSGroup(tableName).getName());
+    assertEquals(newGroup.getName(), ADMIN.getRSGroup(tableName).getName());
 
     // verify servers' not exist in old group
-    Set<Address> defaultServers =
-      rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers();
+    Set<Address> defaultServers = ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers();
     assertFalse(defaultServers.contains(targetServer.getAddress()));
 
     // verify servers' exist in new group
-    Set<Address> newGroupServers = rsGroupAdmin.getRSGroup(newGroup.getName()).getServers();
+    Set<Address> newGroupServers = ADMIN.getRSGroup(newGroup.getName()).getServers();
     assertTrue(newGroupServers.contains(targetServer.getAddress()));
 
     // verify tables' not exist in old group
     Set<TableName> defaultTables =
-      rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getTables();
+      RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables();
     assertFalse(defaultTables.contains(tableName));
 
     // verify tables' exist in new group
-    Set<TableName> newGroupTables = rsGroupAdmin.getRSGroup(newGroup.getName()).getTables();
+    Set<TableName> newGroupTables =
+      RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(newGroup.getName()).getTables();
     assertTrue(newGroupTables.contains(tableName));
 
-    // verify that all region still assgin on targetServer
+    // verify that all region still assign on targetServer
     // TODO: uncomment after we reimplement moveServersAndTables, now the implementation is
     // moveToRSGroup first and then moveTables, so the region will be moved to other region servers.
-    // Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size());
+    // assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size());
 
-    assertTrue(observer.preMoveServersCalled);
-    assertTrue(observer.postMoveServersCalled);
+    assertTrue(OBSERVER.preMoveServersCalled);
+    assertTrue(OBSERVER.postMoveServersCalled);
   }
 
   @Test
   public void testMoveServersFromDefaultGroup() throws Exception {
     // create groups and assign servers
-    rsGroupAdmin.addRSGroup("foo");
+    ADMIN.addRSGroup("foo");
 
-    RSGroupInfo fooGroup = rsGroupAdmin.getRSGroup("foo");
+    RSGroupInfo fooGroup = ADMIN.getRSGroup("foo");
     assertEquals(0, fooGroup.getServers().size());
-    RSGroupInfo defaultGroup = rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
+    RSGroupInfo defaultGroup = ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
 
     // test remove all servers from default
     try {
-      rsGroupAdmin.moveToRSGroup(defaultGroup.getServers(), fooGroup.getName());
+      ADMIN.moveServersToRSGroup(defaultGroup.getServers(), fooGroup.getName());
       fail(RSGroupInfoManagerImpl.KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE);
     } catch (ConstraintException ex) {
       assertTrue(
@@ -443,51 +436,49 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
       Address serverInDefaultGroup = defaultGroup.getServers().iterator().next();
       LOG.info("moving server " + serverInDefaultGroup + " from group default to group " +
         fooGroup.getName());
-      rsGroupAdmin.moveToRSGroup(Sets.newHashSet(serverInDefaultGroup), fooGroup.getName());
+      ADMIN.moveServersToRSGroup(Sets.newHashSet(serverInDefaultGroup), fooGroup.getName());
     }
 
-    fooGroup = rsGroupAdmin.getRSGroup("foo");
+    fooGroup = ADMIN.getRSGroup("foo");
     LOG.info("moving servers " + fooGroup.getServers() + " to group default");
-    rsGroupAdmin.moveToRSGroup(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.moveServersToRSGroup(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
 
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return getNumServers() == rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP)
-          .getServers().size();
+        return getNumServers() == ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size();
       }
     });
 
-    fooGroup = rsGroupAdmin.getRSGroup("foo");
+    fooGroup = ADMIN.getRSGroup("foo");
     assertEquals(0, fooGroup.getServers().size());
 
     // test group removal
     LOG.info("Remove group " + fooGroup.getName());
-    rsGroupAdmin.removeRSGroup(fooGroup.getName());
-    Assert.assertEquals(null, rsGroupAdmin.getRSGroup(fooGroup.getName()));
+    ADMIN.removeRSGroup(fooGroup.getName());
+    assertEquals(null, ADMIN.getRSGroup(fooGroup.getName()));
   }
 
   @Test
   public void testFailedMoveBeforeRetryExhaustedWhenMoveServer() throws Exception {
     String groupName = getGroupName(name.getMethodName());
-    rsGroupAdmin.addRSGroup(groupName);
-    final RSGroupInfo newGroup = rsGroupAdmin.getRSGroup(groupName);
-    Pair<ServerName, RegionStateNode> gotPair = createTableWithRegionSplitting(newGroup,
-        10);
+    ADMIN.addRSGroup(groupName);
+    final RSGroupInfo newGroup = ADMIN.getRSGroup(groupName);
+    Pair<ServerName, RegionStateNode> gotPair = createTableWithRegionSplitting(newGroup, 10);
 
     // start thread to recover region state
     final ServerName movedServer = gotPair.getFirst();
     final RegionStateNode rsn = gotPair.getSecond();
     AtomicBoolean changed = new AtomicBoolean(false);
     Thread t1 = recoverRegionStateThread(movedServer,
-      server -> master.getAssignmentManager().getRegionsOnServer(movedServer), rsn, changed);
+      server -> MASTER.getAssignmentManager().getRegionsOnServer(movedServer), rsn, changed);
     t1.start();
 
     // move target server to group
     Thread t2 = new Thread(() -> {
       LOG.info("thread2 start running, to move regions");
       try {
-        rsGroupAdmin.moveToRSGroup(Sets.newHashSet(movedServer.getAddress()), newGroup.getName());
+        ADMIN.moveServersToRSGroup(Sets.newHashSet(movedServer.getAddress()), newGroup.getName());
       } catch (IOException e) {
         LOG.error("move server error", e);
       }
@@ -501,8 +492,8 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
       @Override
       public boolean evaluate() {
         if (changed.get()) {
-          return master.getAssignmentManager().getRegionsOnServer(movedServer).size() == 0 && !rsn
-              .getRegionLocation().equals(movedServer);
+          return MASTER.getAssignmentManager().getRegionsOnServer(movedServer).size() == 0 &&
+            !rsn.getRegionLocation().equals(movedServer);
         }
         return false;
       }
@@ -510,15 +501,15 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
   }
 
   private <T> Thread recoverRegionStateThread(T owner, Function<T, List<RegionInfo>> getRegions,
-      RegionStateNode rsn, AtomicBoolean changed){
+    RegionStateNode rsn, AtomicBoolean changed) {
     return new Thread(() -> {
       LOG.info("thread1 start running, will recover region state");
       long current = System.currentTimeMillis();
       // wait until there is only left the region we changed state and recover its state.
       // wait time is set according to the number of max retries, all except failed regions will be
       // moved in one retry, and will sleep 1s until next retry.
-      while (System.currentTimeMillis() - current <=
-          RSGroupInfoManagerImpl.DEFAULT_MAX_RETRY_VALUE * 1000) {
+      while (System.currentTimeMillis() -
+        current <= RSGroupInfoManagerImpl.DEFAULT_MAX_RETRY_VALUE * 1000) {
         List<RegionInfo> regions = getRegions.apply(owner);
         LOG.debug("server table region size is:{}", regions.size());
         assert regions.size() >= 1;
@@ -526,7 +517,7 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
         // exception caused by the strange region state.
         if (regions.size() == 1) {
           assertEquals(regions.get(0).getRegionNameAsString(),
-              rsn.getRegionInfo().getRegionNameAsString());
+            rsn.getRegionInfo().getRegionNameAsString());
           rsn.setState(RegionState.State.OPEN);
           LOG.info("set region {} state OPEN", rsn.getRegionInfo().getRegionNameAsString());
           changed.set(true);
@@ -538,7 +529,7 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
   }
 
   private Pair<ServerName, RegionStateNode> createTableWithRegionSplitting(RSGroupInfo rsGroupInfo,
-      int tableRegionCount) throws Exception{
+    int tableRegionCount) throws Exception {
     final byte[] familyNameBytes = Bytes.toBytes("f");
     // All the regions created below will be assigned to the default group.
     TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, tableRegionCount);
@@ -562,11 +553,11 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
    * @return source server of region, and region state
    * @throws IOException if methods called throw
    */
-  private Pair<ServerName, RegionStateNode> randomlySetOneRegionStateToSplitting(
-      RSGroupInfo newGroup) throws IOException{
+  private Pair<ServerName, RegionStateNode>
+    randomlySetOneRegionStateToSplitting(RSGroupInfo newGroup) throws IOException {
     // get target server to move, which should has more than one regions
     // randomly set a region state to SPLITTING to make move fail
-    return randomlySetRegionState(newGroup, RegionState.State.SPLITTING,tableName);
+    return randomlySetRegionState(newGroup, RegionState.State.SPLITTING, tableName);
   }
 
   private Pair<ServerName, RegionStateNode> randomlySetRegionState(RSGroupInfo groupInfo,
@@ -605,7 +596,7 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
   }
 
   @Test
-  public void testFailedMoveServersAndRepair() throws Exception{
+  public void testFailedMoveServersAndRepair() throws Exception {
     // This UT calls moveToRSGroup() twice to test the idempotency of it.
     // The first time, movement fails because a region is made in SPLITTING state
     // which will not be moved.
@@ -615,35 +606,36 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
 
     // create table
     // randomly set a region state to SPLITTING to make move abort
-    Pair<ServerName, RegionStateNode> gotPair = createTableWithRegionSplitting(newGroup,
-        new Random().nextInt(8) + 4);
+    Pair<ServerName, RegionStateNode> gotPair =
+      createTableWithRegionSplitting(newGroup, new Random().nextInt(8) + 4);
     RegionStateNode rsn = gotPair.getSecond();
     ServerName srcServer = rsn.getRegionLocation();
 
     // move server to newGroup and check regions
     try {
-      rsGroupAdmin.moveToRSGroup(Sets.newHashSet(srcServer.getAddress()), newGroup.getName());
-      fail("should get IOException when retry exhausted but there still exists failed moved "
-          + "regions");
-    }catch (Exception e){
-      assertTrue(e.getMessage().contains(
-          gotPair.getSecond().getRegionInfo().getRegionNameAsString()));
+      ADMIN.moveServersToRSGroup(Sets.newHashSet(srcServer.getAddress()), newGroup.getName());
+      fail("should get IOException when retry exhausted but there still exists failed moved " +
+        "regions");
+    } catch (Exception e) {
+      assertTrue(
+        e.getMessage().contains(gotPair.getSecond().getRegionInfo().getRegionNameAsString()));
     }
-    for(RegionInfo regionInfo : master.getAssignmentManager().getAssignedRegions()){
+    for (RegionInfo regionInfo : MASTER.getAssignmentManager().getAssignedRegions()) {
       if (regionInfo.getTable().equals(tableName) && regionInfo.equals(rsn.getRegionInfo())) {
-        assertEquals(master.getAssignmentManager().getRegionStates()
-            .getRegionServerOfRegion(regionInfo), srcServer);
+        assertEquals(
+          MASTER.getAssignmentManager().getRegionStates().getRegionServerOfRegion(regionInfo),
+          srcServer);
       }
     }
 
     // retry move server to newGroup and check if all regions on srcServer was moved
     rsn.setState(RegionState.State.OPEN);
-    rsGroupAdmin.moveToRSGroup(Sets.newHashSet(srcServer.getAddress()), newGroup.getName());
-    assertEquals(master.getAssignmentManager().getRegionsOnServer(srcServer).size(), 0);
+    ADMIN.moveServersToRSGroup(Sets.newHashSet(srcServer.getAddress()), newGroup.getName());
+    assertEquals(MASTER.getAssignmentManager().getRegionsOnServer(srcServer).size(), 0);
   }
 
   @Test
-  public void testFailedMoveServersTablesAndRepair() throws Exception{
+  public void testFailedMoveServersTablesAndRepair() throws Exception {
     // This UT calls moveTablesAndServers() twice to test the idempotency of it.
     // The first time, movement fails because a region is made in SPLITTING state
     // which will not be moved.
@@ -654,40 +646,39 @@ public class TestRSGroupsAdmin2 extends TestRSGroupsBase {
     final byte[] familyNameBytes = Bytes.toBytes("f");
     TableName table1 = TableName.valueOf(tableName.getNameAsString() + "_1");
     TableName table2 = TableName.valueOf(tableName.getNameAsString() + "_2");
-    TEST_UTIL.createMultiRegionTable(table1, familyNameBytes,
-        new Random().nextInt(12) + 4);
-    TEST_UTIL.createMultiRegionTable(table2, familyNameBytes,
-        new Random().nextInt(12) + 4);
+    TEST_UTIL.createMultiRegionTable(table1, familyNameBytes, new Random().nextInt(12) + 4);
+    TEST_UTIL.createMultiRegionTable(table2, familyNameBytes, new Random().nextInt(12) + 4);
 
     // randomly set a region state to SPLITTING to make move abort
     Pair<ServerName, RegionStateNode> gotPair =
-        randomlySetRegionState(newGroup, RegionState.State.SPLITTING, table1, table2);
+      randomlySetRegionState(newGroup, RegionState.State.SPLITTING, table1, table2);
     RegionStateNode rsn = gotPair.getSecond();
     ServerName srcServer = rsn.getRegionLocation();
 
     // move server and table to newGroup and check regions
     try {
-      rsGroupAdmin.moveToRSGroup(Sets.newHashSet(srcServer.getAddress()), newGroup.getName());
-      rsGroupAdmin.setRSGroup(Sets.newHashSet(table2), newGroup.getName());
-      fail("should get IOException when retry exhausted but there still exists failed moved "
-          + "regions");
-    }catch (Exception e){
-      assertTrue(e.getMessage().contains(
-          gotPair.getSecond().getRegionInfo().getRegionNameAsString()));
+      ADMIN.moveServersToRSGroup(Sets.newHashSet(srcServer.getAddress()), newGroup.getName());
+      ADMIN.setRSGroup(Sets.newHashSet(table2), newGroup.getName());
+      fail("should get IOException when retry exhausted but there still exists failed moved " +
+        "regions");
+    } catch (Exception e) {
+      assertTrue(
+        e.getMessage().contains(gotPair.getSecond().getRegionInfo().getRegionNameAsString()));
     }
-    for(RegionInfo regionInfo : master.getAssignmentManager().getAssignedRegions()){
+    for (RegionInfo regionInfo : MASTER.getAssignmentManager().getAssignedRegions()) {
       if (regionInfo.getTable().equals(table1) && regionInfo.equals(rsn.getRegionInfo())) {
-        assertEquals(master.getAssignmentManager().getRegionStates()
-            .getRegionServerOfRegion(regionInfo), srcServer);
+        assertEquals(
+          MASTER.getAssignmentManager().getRegionStates().getRegionServerOfRegion(regionInfo),
+          srcServer);
       }
     }
 
     // retry moveServersAndTables to newGroup and check if all regions on srcServer belongs to
     // table2
     rsn.setState(RegionState.State.OPEN);
-    rsGroupAdmin.moveToRSGroup(Sets.newHashSet(srcServer.getAddress()), newGroup.getName());
-    rsGroupAdmin.setRSGroup(Sets.newHashSet(table2), newGroup.getName());
-    for(RegionInfo regionsInfo : master.getAssignmentManager().getRegionsOnServer(srcServer)){
+    ADMIN.moveServersToRSGroup(Sets.newHashSet(srcServer.getAddress()), newGroup.getName());
+    ADMIN.setRSGroup(Sets.newHashSet(table2), newGroup.getName());
+    for (RegionInfo regionsInfo : MASTER.getAssignmentManager().getRegionsOnServer(srcServer)) {
       assertEquals(regionsInfo.getTable(), table2);
     }
   }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java
index be93186..0230671 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java
@@ -34,6 +34,7 @@ import org.apache.hadoop.hbase.client.RegionInfo;
 import org.apache.hadoop.hbase.client.TableDescriptor;
 import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
 import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -42,13 +43,10 @@ import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@RunWith(Parameterized.class)
-@Category({ MediumTests.class })
+@Category({ RSGroupTests.class, MediumTests.class })
 public class TestRSGroupsBalance extends TestRSGroupsBase {
 
   @ClassRule
@@ -83,15 +81,15 @@ public class TestRSGroupsBalance extends TestRSGroupsBase {
     String newGroupName = getGroupName(name.getMethodName());
     addGroup(newGroupName, 3);
 
-    final TableName tableName = TableName.valueOf(tablePrefix + "_ns",
-        getNameWithoutIndex(name.getMethodName()));
-    admin.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString())
+    final TableName tableName =
+      TableName.valueOf(TABLE_PREFIX + "_ns", getNameWithoutIndex(name.getMethodName()));
+    ADMIN.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString())
       .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, newGroupName).build());
     final TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
       .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build();
     byte[] startKey = Bytes.toBytes("aaaaa");
     byte[] endKey = Bytes.toBytes("zzzzz");
-    admin.createTable(desc, startKey, endKey, 6);
+    ADMIN.createTable(desc, startKey, endKey, 6);
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
@@ -106,9 +104,9 @@ public class TestRSGroupsBalance extends TestRSGroupsBase {
     // 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.getRegions(tableName)) {
+    for (RegionInfo region : ADMIN.getRegions(tableName)) {
       if (!assignMap.get(first).contains(region.getRegionNameAsString())) {
-        admin.move(region.getEncodedNameAsBytes(), first);
+        ADMIN.move(region.getEncodedNameAsBytes(), first);
       }
     }
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
@@ -127,18 +125,18 @@ public class TestRSGroupsBalance extends TestRSGroupsBase {
     });
 
     // balance the other group and make sure it doesn't affect the new group
-    admin.balancerSwitch(true, true);
-    rsGroupAdmin.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.balancerSwitch(true, true);
+    ADMIN.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP);
     assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size());
 
     // disable balance, balancer will not be run and return false
-    admin.balancerSwitch(false, true);
-    assertFalse(rsGroupAdmin.balanceRSGroup(newGroupName));
+    ADMIN.balancerSwitch(false, true);
+    assertFalse(ADMIN.balanceRSGroup(newGroupName));
     assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size());
 
     // enable balance
-    admin.balancerSwitch(true, true);
-    rsGroupAdmin.balanceRSGroup(newGroupName);
+    ADMIN.balancerSwitch(true, true);
+    ADMIN.balanceRSGroup(newGroupName);
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
@@ -150,35 +148,35 @@ public class TestRSGroupsBalance extends TestRSGroupsBase {
         return true;
       }
     });
-    admin.balancerSwitch(false, true);
+    ADMIN.balancerSwitch(false, true);
   }
 
   @Test
   public void testMisplacedRegions() throws Exception {
-    String namespace = tablePrefix + "_" + getNameWithoutIndex(name.getMethodName());
+    String namespace = TABLE_PREFIX + "_" + getNameWithoutIndex(name.getMethodName());
     TEST_UTIL.getAdmin().createNamespace(NamespaceDescriptor.create(namespace).build());
-    final TableName tableName = TableName.valueOf(namespace, tablePrefix + "_" +
-        getNameWithoutIndex(name.getMethodName()));
+    final TableName tableName =
+      TableName.valueOf(namespace, TABLE_PREFIX + "_" + getNameWithoutIndex(name.getMethodName()));
 
     final RSGroupInfo rsGroupInfo = addGroup(getGroupName(name.getMethodName()), 1);
 
     TEST_UTIL.createMultiRegionTable(tableName, new byte[] { 'f' }, 15);
     TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
     TEST_UTIL.getAdmin().modifyNamespace(NamespaceDescriptor.create(namespace)
-        .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, rsGroupInfo.getName()).build());
+      .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, rsGroupInfo.getName()).build());
 
-    admin.balancerSwitch(true, true);
-    assertTrue(rsGroupAdmin.balanceRSGroup(rsGroupInfo.getName()));
-    admin.balancerSwitch(false, true);
-    assertTrue(observer.preBalanceRSGroupCalled);
-    assertTrue(observer.postBalanceRSGroupCalled);
+    ADMIN.balancerSwitch(true, true);
+    assertTrue(ADMIN.balanceRSGroup(rsGroupInfo.getName()));
+    ADMIN.balancerSwitch(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().getRegions(serverName).size() == 15;
+          ServerName.valueOf(rsGroupInfo.getServers().iterator().next().toString(), 1);
+        return ADMIN.getConnection().getAdmin().getRegions(serverName).size() == 15;
       }
     });
   }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
index 6626fba..7963126 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
@@ -21,23 +21,20 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Random;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.function.Supplier;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.regex.Pattern;
 import org.apache.hadoop.hbase.ClusterMetrics;
 import org.apache.hadoop.hbase.ClusterMetrics.Option;
 import org.apache.hadoop.hbase.HBaseCluster;
 import org.apache.hadoop.hbase.HBaseTestingUtility;
-import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.MiniHBaseCluster;
 import org.apache.hadoop.hbase.NamespaceDescriptor;
 import org.apache.hadoop.hbase.ServerName;
@@ -59,7 +56,6 @@ import org.apache.hadoop.hbase.net.Address;
 import org.apache.hadoop.hbase.quotas.QuotaUtil;
 import org.junit.Rule;
 import org.junit.rules.TestName;
-import org.junit.runners.Parameterized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,22 +65,21 @@ import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
 public abstract class TestRSGroupsBase {
   protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsBase.class);
 
-  //shared
-  protected final static String groupPrefix = "Group";
-  protected final static String tablePrefix = "Group";
-  protected final static Random rand = new Random();
+  // shared
+  protected static final String GROUP_PREFIX = "Group";
+  protected static final String TABLE_PREFIX = "Group";
 
-  //shared, cluster type specific
+  // shared, cluster type specific
   protected static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
-  protected static Admin admin;
-  protected static HBaseCluster cluster;
-  protected static HMaster master;
+  protected static Admin ADMIN;
+  protected static HBaseCluster CLUSTER;
+  protected static HMaster MASTER;
   protected boolean INIT = false;
-  protected static RSGroupAdminEndpoint rsGroupAdminEndpoint;
-  protected static CPMasterObserver observer;
+  protected static CPMasterObserver OBSERVER;
+  protected static RSGroupAdminClient RS_GROUP_ADMIN_CLIENT;
 
   public final static long WAIT_TIMEOUT = 60000;
-  public final static int NUM_SLAVES_BASE = 4; //number of slaves for the smallest cluster
+  public final static int NUM_SLAVES_BASE = 4; // number of slaves for the smallest cluster
   public static int NUM_DEAD_SERVERS = 0;
 
   // Per test variables
@@ -92,59 +87,17 @@ public abstract class TestRSGroupsBase {
   public TestName name = new TestName();
   protected TableName tableName;
 
-  protected Admin rsGroupAdmin;
-
-  @Parameterized.Parameter
-  public Supplier<Object> getAdmin;
-
-  private static RSGroupAdminClient getRSGroupAdmin(){
-    try {
-      return new VerifyingRSGroupAdminClient(
-          new RSGroupAdminClient(TEST_UTIL.getConnection()), TEST_UTIL.getConfiguration());
-    } catch (IOException e) {
-      LOG.error("Get group admin failed", e);
-      return null;
-    }
-  }
-
-  private static Admin getAdmin(){
-    try {
-      return TEST_UTIL.getAdmin();
-    } catch (IOException e) {
-      LOG.error("Get hbase admin failed", e);
-      return null;
-    }
-  }
-
-  public static Object resetAdminConnection(Object admin) {
-    if(admin instanceof RSGroupAdminClient) {
-      return getRSGroupAdmin();
-    }else {
-      return getAdmin();
-    }
-  }
-
   public static String getNameWithoutIndex(String name) {
     return name.split("\\[")[0];
   }
 
-  @Parameterized.Parameters
-  public static List<Object[]> params() {
-    return Arrays.asList(new Supplier<?>[] { TestRSGroupsBase::getRSGroupAdmin },
-        new Supplier<?>[] { TestRSGroupsBase::getAdmin });
-  }
-
   public static void setUpTestBeforeClass() throws Exception {
-    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().setFloat("hbase.master.balancer.stochastic.tableSkewCost", 6000);
+    TEST_UTIL.getConfiguration().setBoolean(RSGroupInfoManager.RS_GROUP_ENABLED, true);
     TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
-        RSGroupAdminEndpoint.class.getName() + "," + CPMasterObserver.class.getName());
-    TEST_UTIL.getConfiguration().setInt(
-        ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
-        NUM_SLAVES_BASE - 1);
+      RSGroupAdminEndpoint.class.getName() + "," + CPMasterObserver.class.getName());
+    TEST_UTIL.getConfiguration().setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
+      NUM_SLAVES_BASE - 1);
     TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
     TEST_UTIL.getConfiguration().setInt("hbase.rpc.timeout", 100000);
 
@@ -152,28 +105,23 @@ public abstract class TestRSGroupsBase {
     initialize();
   }
 
-  public void setAdmin(){
-    rsGroupAdmin = (Admin) getAdmin.get();
-  }
-
   protected static void initialize() throws Exception {
-    admin = TEST_UTIL.getAdmin();
-    cluster = TEST_UTIL.getHBaseCluster();
-    master = TEST_UTIL.getMiniHBaseCluster().getMaster();
+    ADMIN = new VerifyingRSGroupAdmin(TEST_UTIL.getConfiguration());
+    CLUSTER = TEST_UTIL.getHBaseCluster();
+    MASTER = TEST_UTIL.getMiniHBaseCluster().getMaster();
 
-    //wait for balancer to come online
+    // 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();
+        return MASTER.isInitialized() &&
+          ((RSGroupBasedLoadBalancer) MASTER.getLoadBalancer()).isOnline();
       }
     });
-    admin.balancerSwitch(false, true);
-    MasterCoprocessorHost host = master.getMasterCoprocessorHost();
-    observer = (CPMasterObserver) host.findCoprocessor(CPMasterObserver.class.getName());
-    rsGroupAdminEndpoint = (RSGroupAdminEndpoint)
-        host.findCoprocessor(RSGroupAdminEndpoint.class.getName());
+    ADMIN.balancerSwitch(false, true);
+    MasterCoprocessorHost host = MASTER.getMasterCoprocessorHost();
+    OBSERVER = (CPMasterObserver) host.findCoprocessor(CPMasterObserver.class.getName());
+    RS_GROUP_ADMIN_CLIENT = new RSGroupAdminClient(TEST_UTIL.getConnection());
   }
 
   public static void tearDownAfterClass() throws Exception {
@@ -181,14 +129,13 @@ public abstract class TestRSGroupsBase {
   }
 
   public void setUpBeforeMethod() throws Exception {
-    setAdmin();
     LOG.info(name.getMethodName());
-    tableName = TableName.valueOf(tablePrefix + "_" + name.getMethodName().split("\\[")[0]);
+    tableName = TableName.valueOf(TABLE_PREFIX + "_" + name.getMethodName().split("\\[")[0]);
     if (!INIT) {
       INIT = true;
       tearDownAfterMethod();
     }
-    observer.resetFlags();
+    OBSERVER.resetFlags();
   }
 
   public void tearDownAfterMethod() throws Exception {
@@ -196,45 +143,40 @@ public abstract class TestRSGroupsBase {
     deleteNamespaceIfNecessary();
     deleteGroups();
 
-    for(ServerName sn : admin.listDecommissionedRegionServers()){
-      admin.recommissionRegionServer(sn, null);
+    for (ServerName sn : ADMIN.listDecommissionedRegionServers()) {
+      ADMIN.recommissionRegionServer(sn, null);
     }
-    assertTrue(admin.listDecommissionedRegionServers().isEmpty());
+    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();
+    LOG.info("Restoring servers: " + missing);
+    for (int i = 0; i < missing; i++) {
+      ((MiniHBaseCluster) CLUSTER).startRegionServer();
     }
-
-    rsGroupAdmin.addRSGroup("master");
-    ServerName masterServerName =
-        ((MiniHBaseCluster)cluster).getMaster().getServerName();
-
+    ADMIN.addRSGroup("master");
+    ServerName masterServerName = ((MiniHBaseCluster) CLUSTER).getMaster().getServerName();
     try {
-      rsGroupAdmin.moveToRSGroup(Sets.newHashSet(masterServerName.getAddress()),
-          "master");
+      ADMIN.moveServersToRSGroup(Sets.newHashSet(masterServerName.getAddress()), "master");
     } catch (Exception ex) {
       LOG.warn("Got this on setup, FYI", ex);
     }
-    assertTrue(observer.preMoveServersCalled);
+    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
+        LOG.info("Waiting for cleanup to finish " + ADMIN.listRSGroups());
+        // Might be greater since moving servers back to default
+        // is after starting a server
 
-        return rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size()
-            == NUM_SLAVES_BASE;
+        return ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size() == NUM_SLAVES_BASE;
       }
     });
   }
 
-  protected RSGroupInfo addGroup(String groupName, int serverCount)
-      throws IOException, InterruptedException {
-    RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
-    rsGroupAdmin.addRSGroup(groupName);
+  protected final RSGroupInfo addGroup(String groupName, int serverCount)
+    throws IOException, InterruptedException {
+    RSGroupInfo defaultInfo = ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.addRSGroup(groupName);
     Set<Address> set = new HashSet<>();
     for (Address server : defaultInfo.getServers()) {
       if (set.size() == serverCount) {
@@ -242,53 +184,56 @@ public abstract class TestRSGroupsBase {
       }
       set.add(server);
     }
-    rsGroupAdmin.moveToRSGroup(set, groupName);
-    RSGroupInfo result = rsGroupAdmin.getRSGroup(groupName);
+    ADMIN.moveServersToRSGroup(set, groupName);
+    RSGroupInfo result = ADMIN.getRSGroup(groupName);
     return result;
   }
 
-  public void removeGroup(String groupName) throws IOException {
-    RSGroupInfo groupInfo = rsGroupAdmin.getRSGroup(groupName);
-    rsGroupAdmin.setRSGroup(groupInfo.getTables(), RSGroupInfo.DEFAULT_GROUP);
-    rsGroupAdmin.moveToRSGroup(groupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
-    rsGroupAdmin.removeRSGroup(groupName);
+  protected final void removeGroup(String groupName) throws IOException {
+    Set<TableName> tables = new HashSet<>();
+    for (TableDescriptor td : ADMIN.listTableDescriptors(true)) {
+      RSGroupInfo groupInfo = ADMIN.getRSGroup(td.getTableName());
+      if (groupInfo != null && groupInfo.getName().equals(groupName)) {
+        tables.add(td.getTableName());
+      }
+    }
+    ADMIN.setRSGroup(tables, RSGroupInfo.DEFAULT_GROUP);
+    RSGroupInfo groupInfo = ADMIN.getRSGroup(groupName);
+    ADMIN.moveServersToRSGroup(groupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    ADMIN.removeRSGroup(groupName);
   }
 
-  protected void deleteTableIfNecessary() throws IOException {
+  protected final void deleteTableIfNecessary() throws IOException {
     for (TableDescriptor desc : TEST_UTIL.getAdmin()
-      .listTableDescriptors(Pattern.compile(tablePrefix + ".*"))) {
+      .listTableDescriptors(Pattern.compile(TABLE_PREFIX + ".*"))) {
       TEST_UTIL.deleteTable(desc.getTableName());
     }
   }
 
-  protected void deleteNamespaceIfNecessary() throws IOException {
+  protected final void deleteNamespaceIfNecessary() throws IOException {
     for (NamespaceDescriptor desc : TEST_UTIL.getAdmin().listNamespaceDescriptors()) {
-      if(desc.getName().startsWith(tablePrefix)) {
-        admin.deleteNamespace(desc.getName());
+      if (desc.getName().startsWith(TABLE_PREFIX)) {
+        ADMIN.deleteNamespace(desc.getName());
       }
     }
   }
 
-  protected void deleteGroups() throws IOException {
-    RSGroupAdminClient groupAdmin = new RSGroupAdminClient(TEST_UTIL.getConnection());
-    for(RSGroupInfo group: groupAdmin.listRSGroups()) {
-      if(!group.getName().equals(RSGroupInfo.DEFAULT_GROUP)) {
-        groupAdmin.setRSGroup(group.getTables(), RSGroupInfo.DEFAULT_GROUP);
-        groupAdmin.moveServers(group.getServers(), RSGroupInfo.DEFAULT_GROUP);
-        groupAdmin.removeRSGroup(group.getName());
+  protected final void deleteGroups() throws IOException {
+    for (RSGroupInfo groupInfo : ADMIN.listRSGroups()) {
+      if (!groupInfo.getName().equals(RSGroupInfo.DEFAULT_GROUP)) {
+        removeGroup(groupInfo.getName());
       }
     }
   }
 
   protected Map<TableName, List<String>> getTableRegionMap() throws IOException {
     Map<TableName, List<String>> map = Maps.newTreeMap();
-    Map<TableName, Map<ServerName, List<String>>> tableServerRegionMap
-        = getTableServerRegionMap();
-    for(TableName tableName : tableServerRegionMap.keySet()) {
-      if(!map.containsKey(tableName)) {
+    Map<TableName, Map<ServerName, List<String>>> tableServerRegionMap = getTableServerRegionMap();
+    for (TableName tableName : tableServerRegionMap.keySet()) {
+      if (!map.containsKey(tableName)) {
         map.put(tableName, new LinkedList<>());
       }
-      for(List<String> subset: tableServerRegionMap.get(tableName).values()) {
+      for (List<String> subset : tableServerRegionMap.get(tableName).values()) {
         map.get(tableName).addAll(subset);
       }
     }
@@ -313,8 +258,7 @@ public abstract class TestRSGroupsBase {
 
   // return the real number of region servers, excluding the master embedded region server in 2.0+
   protected int getNumServers() throws IOException {
-    ClusterMetrics status =
-        admin.getClusterMetrics(EnumSet.of(Option.MASTER, Option.LIVE_SERVERS));
+    ClusterMetrics status = ADMIN.getClusterMetrics(EnumSet.of(Option.MASTER, Option.LIVE_SERVERS));
     ServerName masterName = status.getMasterName();
     int count = 0;
     for (ServerName sn : status.getLiveServerMetrics().keySet()) {
@@ -325,30 +269,29 @@ public abstract class TestRSGroupsBase {
     return count;
   }
 
-  public String getGroupName(String baseName) {
-    return groupPrefix + "_" + getNameWithoutIndex(baseName) + "_" +
-        rand.nextInt(Integer.MAX_VALUE);
+  protected final String getGroupName(String baseName) {
+    return GROUP_PREFIX + "_" + getNameWithoutIndex(baseName) + "_" +
+      ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
   }
 
   /**
    * The server name in group does not contain the start code, this method will find out the start
    * code and construct the ServerName object.
    */
-  protected ServerName getServerName(Address addr) {
+  protected final ServerName getServerName(Address addr) {
     return TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().stream()
       .map(t -> t.getRegionServer().getServerName()).filter(sn -> sn.getAddress().equals(addr))
       .findFirst().get();
   }
 
-  protected void toggleQuotaCheckAndRestartMiniCluster(boolean enable) throws Exception {
+  protected final 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);
+      NUM_SLAVES_BASE - 1);
     TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
     initialize();
-    rsGroupAdmin = (Admin) resetAdminConnection(rsGroupAdmin);
   }
 
   public static class CPMasterObserver implements MasterCoprocessor, MasterObserver {
@@ -411,135 +354,133 @@ public abstract class TestRSGroupsBase {
 
     @Override
     public void preMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
+      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 {
+      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 {
+    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 {
+    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 {
+      String name) throws IOException {
       preRemoveRSGroupCalled = true;
     }
 
     @Override
     public void postRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String name) throws IOException {
+      String name) throws IOException {
       postRemoveRSGroupCalled = true;
     }
 
     @Override
-    public void preAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String name) throws IOException {
+    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 {
+    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 {
+      Set<TableName> tables, String targetGroup) throws IOException {
       preMoveTablesCalled = true;
     }
 
     @Override
     public void postMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<TableName> tables, String targetGroup) throws IOException {
+      Set<TableName> tables, String targetGroup) throws IOException {
       postMoveTablesCalled = true;
     }
 
     @Override
     public void preMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<Address> servers, String targetGroup) throws IOException {
+      Set<Address> servers, String targetGroup) throws IOException {
       preMoveServersCalled = true;
     }
 
     @Override
     public void postMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        Set<Address> servers, String targetGroup) throws IOException {
+      Set<Address> servers, String targetGroup) throws IOException {
       postMoveServersCalled = true;
     }
 
     @Override
     public void preBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String groupName) throws IOException {
+      String groupName) throws IOException {
       preBalanceRSGroupCalled = true;
     }
 
     @Override
     public void postBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        String groupName, boolean balancerRan) throws IOException {
+      String groupName, boolean balancerRan) throws IOException {
       postBalanceRSGroupCalled = true;
     }
 
     @Override
     public void preGetRSGroupInfo(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        final String groupName) throws IOException {
+      final String groupName) throws IOException {
       preGetRSGroupInfoCalled = true;
     }
 
     @Override
     public void postGetRSGroupInfo(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        final String groupName) throws IOException {
+      final String groupName) throws IOException {
       postGetRSGroupInfoCalled = true;
     }
 
     @Override
     public void preGetRSGroupInfoOfTable(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        final TableName tableName) throws IOException {
+      final TableName tableName) throws IOException {
       preGetRSGroupInfoOfTableCalled = true;
     }
 
     @Override
     public void postGetRSGroupInfoOfTable(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        final TableName tableName) throws IOException {
+      final TableName tableName) throws IOException {
       postGetRSGroupInfoOfTableCalled = true;
     }
 
     @Override
     public void preListRSGroups(final ObserverContext<MasterCoprocessorEnvironment> ctx)
-        throws IOException {
+      throws IOException {
       preListRSGroupsCalled = true;
     }
 
     @Override
     public void postListRSGroups(final ObserverContext<MasterCoprocessorEnvironment> ctx)
-        throws IOException {
+      throws IOException {
       postListRSGroupsCalled = true;
     }
 
     @Override
     public void preGetRSGroupInfoOfServer(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        final Address server) throws IOException {
+      final Address server) throws IOException {
       preGetRSGroupInfoOfServerCalled = true;
     }
 
     @Override
     public void postGetRSGroupInfoOfServer(final ObserverContext<MasterCoprocessorEnvironment> ctx,
-        final Address server) throws IOException {
+      final Address server) throws IOException {
       postGetRSGroupInfoOfServerCalled = true;
     }
   }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBasics.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBasics.java
index 45d2a2e..88503ff 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBasics.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBasics.java
@@ -35,6 +35,7 @@ import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
 import org.apache.hadoop.hbase.net.Address;
 import org.apache.hadoop.hbase.quotas.QuotaTableUtil;
 import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -44,15 +45,12 @@ import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
 
-@RunWith(Parameterized.class)
-@Category({ MediumTests.class })
+@Category({ RSGroupTests.class, MediumTests.class })
 public class TestRSGroupsBasics extends TestRSGroupsBase {
 
   @ClassRule
@@ -83,12 +81,12 @@ public class TestRSGroupsBasics extends TestRSGroupsBase {
 
   @Test
   public void testBasicStartUp() throws IOException {
-    RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
+    RSGroupInfo defaultInfo = ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
     assertEquals(NUM_SLAVES_BASE, defaultInfo.getServers().size());
     // Assignment of meta and rsgroup regions.
-    int count = master.getAssignmentManager().getRegionStates().getRegionAssignments().size();
+    int count = MASTER.getAssignmentManager().getRegionStates().getRegionAssignments().size();
     LOG.info("regions assignments are" +
-        master.getAssignmentManager().getRegionStates().getRegionAssignments().toString());
+      MASTER.getAssignmentManager().getRegionStates().getRegionAssignments().toString());
     // 2 (meta and rsgroup)
     assertEquals(2, count);
   }
@@ -117,14 +115,14 @@ public class TestRSGroupsBasics extends TestRSGroupsBase {
   @Test
   public void testNamespaceCreateAndAssign() throws Exception {
     LOG.info("testNamespaceCreateAndAssign");
-    String nsName = tablePrefix + "_foo";
-    final TableName tableName = TableName.valueOf(nsName, tablePrefix + "_testCreateAndAssign");
+    String nsName = TABLE_PREFIX + "_foo";
+    final TableName tableName = TableName.valueOf(nsName, TABLE_PREFIX + "_testCreateAndAssign");
     RSGroupInfo appInfo = addGroup("appInfo", 1);
-    admin.createNamespace(NamespaceDescriptor.create(nsName)
+    ADMIN.createNamespace(NamespaceDescriptor.create(nsName)
       .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "appInfo").build());
     final TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
       .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build();
-    admin.createTable(desc);
+    ADMIN.createTable(desc);
     // wait for created table to be assigned
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
@@ -134,18 +132,18 @@ public class TestRSGroupsBasics extends TestRSGroupsBase {
     });
     ServerName targetServer = getServerName(appInfo.getServers().iterator().next());
     // verify it was assigned to the right group
-    Assert.assertEquals(1, admin.getRegions(targetServer).size());
+    Assert.assertEquals(1, ADMIN.getRegions(targetServer).size());
   }
 
   @Test
   public void testDefaultNamespaceCreateAndAssign() throws Exception {
     LOG.info("testDefaultNamespaceCreateAndAssign");
-    String tableName = tablePrefix + "_testCreateAndAssign";
-    admin.modifyNamespace(NamespaceDescriptor.create("default")
+    String tableName = TABLE_PREFIX + "_testCreateAndAssign";
+    ADMIN.modifyNamespace(NamespaceDescriptor.create("default")
       .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "default").build());
     final TableDescriptor desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName))
       .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build();
-    admin.createTable(desc);
+    ADMIN.createTable(desc);
     // wait for created table to be assigned
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
@@ -165,11 +163,11 @@ public class TestRSGroupsBasics extends TestRSGroupsBase {
     TEST_UTIL.createTable(tableName, FAMILY);
 
     // create snapshot
-    admin.snapshot(snapshotName, tableName);
+    ADMIN.snapshot(snapshotName, tableName);
 
     // clone
-    admin.cloneSnapshot(snapshotName, clonedTableName);
-    admin.deleteSnapshot(snapshotName);
+    ADMIN.cloneSnapshot(snapshotName, clonedTableName);
+    ADMIN.deleteSnapshot(snapshotName);
   }
 
   @Test
@@ -179,17 +177,17 @@ public class TestRSGroupsBasics extends TestRSGroupsBase {
     // move region servers from default group to new group
     final int serverCountToMoveToNewGroup = 3;
     final RSGroupInfo newGroup =
-        addGroup(getGroupName(name.getMethodName()), serverCountToMoveToNewGroup);
+      addGroup(getGroupName(name.getMethodName()), serverCountToMoveToNewGroup);
 
     // get the existing dead servers
-    NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size();
+    NUM_DEAD_SERVERS = CLUSTER.getClusterMetrics().getDeadServerNames().size();
 
     // stop 1 region server in new group
     ServerName serverToStop = getServerName(newGroup.getServers().iterator().next());
     try {
       // stopping may cause an exception
       // due to the connection loss
-      admin.stopRegionServer(serverToStop.getAddress().toString());
+      ADMIN.stopRegionServer(serverToStop.getAddress().toString());
       NUM_DEAD_SERVERS++;
     } catch (Exception e) {
     }
@@ -198,20 +196,20 @@ public class TestRSGroupsBasics extends TestRSGroupsBase {
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS &&
-          !master.getServerManager().areDeadServersInProgress();
+        return CLUSTER.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS &&
+          !MASTER.getServerManager().areDeadServersInProgress();
       }
     });
-    assertFalse(cluster.getClusterMetrics().getLiveServerMetrics().containsKey(serverToStop));
-    assertTrue(cluster.getClusterMetrics().getDeadServerNames().contains(serverToStop));
+    assertFalse(CLUSTER.getClusterMetrics().getLiveServerMetrics().containsKey(serverToStop));
+    assertTrue(CLUSTER.getClusterMetrics().getDeadServerNames().contains(serverToStop));
     assertTrue(newGroup.getServers().contains(serverToStop.getAddress()));
 
     // clear dead servers list
-    List<ServerName> notClearedServers = admin.clearDeadServers(Lists.newArrayList(serverToStop));
+    List<ServerName> notClearedServers = ADMIN.clearDeadServers(Lists.newArrayList(serverToStop));
     assertEquals(0, notClearedServers.size());
 
     // the stopped region server gets cleared and removed from the group
-    Set<Address> newGroupServers = rsGroupAdmin.getRSGroup(newGroup.getName()).getServers();
+    Set<Address> newGroupServers = ADMIN.getRSGroup(newGroup.getName()).getServers();
     assertFalse(newGroupServers.contains(serverToStop.getAddress()));
     assertEquals(serverCountToMoveToNewGroup - 1 /* 1 stopped */, newGroupServers.size());
   }
@@ -221,35 +219,34 @@ public class TestRSGroupsBasics extends TestRSGroupsBase {
     LOG.info("testClearNotProcessedDeadServer");
 
     // get the existing dead servers
-    NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size();
+    NUM_DEAD_SERVERS = CLUSTER.getClusterMetrics().getDeadServerNames().size();
 
     // move region servers from default group to "dead server" group
     final int serverCountToMoveToDeadServerGroup = 1;
-    RSGroupInfo deadServerGroup =
-        addGroup("deadServerGroup", serverCountToMoveToDeadServerGroup);
+    RSGroupInfo deadServerGroup = addGroup("deadServerGroup", serverCountToMoveToDeadServerGroup);
 
     // stop 1 region servers in "dead server" group
     ServerName serverToStop = getServerName(deadServerGroup.getServers().iterator().next());
     try {
       // stopping may cause an exception
       // due to the connection loss
-      admin.stopRegionServer(serverToStop.getAddress().toString());
+      ADMIN.stopRegionServer(serverToStop.getAddress().toString());
       NUM_DEAD_SERVERS++;
     } catch (Exception e) {
     }
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS;
+        return CLUSTER.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS;
       }
     });
 
     // the one and only region server in the group does not get cleared, even though it is stopped
-    List<ServerName> notClearedServers = admin.clearDeadServers(Lists.newArrayList(serverToStop));
+    List<ServerName> notClearedServers = ADMIN.clearDeadServers(Lists.newArrayList(serverToStop));
     assertEquals(serverCountToMoveToDeadServerGroup, notClearedServers.size());
 
     Set<Address> ServersInDeadServerGroup =
-        rsGroupAdmin.getRSGroup(deadServerGroup.getName()).getServers();
+      ADMIN.getRSGroup(deadServerGroup.getName()).getServers();
     assertEquals(serverCountToMoveToDeadServerGroup, ServersInDeadServerGroup.size());
     assertTrue(ServersInDeadServerGroup.contains(serverToStop.getAddress()));
   }
@@ -260,7 +257,7 @@ public class TestRSGroupsBasics extends TestRSGroupsBase {
     TEST_UTIL.waitFor(90000, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return admin.isTableAvailable(QuotaTableUtil.QUOTA_TABLE_NAME);
+        return ADMIN.isTableAvailable(QuotaTableUtil.QUOTA_TABLE_NAME);
       }
     });
     toggleQuotaCheckAndRestartMiniCluster(false);
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsCPHookCalled.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsCPHookCalled.java
new file mode 100644
index 0000000..4322784
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsCPHookCalled.java
@@ -0,0 +1,91 @@
+/**
+ * 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.assertTrue;
+
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.MiniHBaseCluster;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
+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;
+
+@Category({ RSGroupTests.class, MediumTests.class })
+public class TestRSGroupsCPHookCalled extends TestRSGroupsBase {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+    HBaseClassTestRule.forClass(TestRSGroupsCPHookCalled.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 testGetRSGroupInfoCPHookCalled() throws Exception {
+    ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
+    assertTrue(OBSERVER.preGetRSGroupInfoCalled);
+    assertTrue(OBSERVER.postGetRSGroupInfoCalled);
+  }
+
+  @Test
+  public void testGetRSGroupInfoOfTableCPHookCalled() throws Exception {
+    ADMIN.getRSGroup(TableName.META_TABLE_NAME);
+    assertTrue(OBSERVER.preGetRSGroupInfoOfTableCalled);
+    assertTrue(OBSERVER.postGetRSGroupInfoOfTableCalled);
+  }
+
+  @Test
+  public void testListRSGroupsCPHookCalled() throws Exception {
+    ADMIN.listRSGroups();
+    assertTrue(OBSERVER.preListRSGroupsCalled);
+    assertTrue(OBSERVER.postListRSGroupsCalled);
+  }
+
+  @Test
+  public void testGetRSGroupInfoOfServerCPHookCalled() throws Exception {
+    ServerName masterServerName = ((MiniHBaseCluster) CLUSTER).getMaster().getServerName();
+    ADMIN.getRSGroup(masterServerName.getAddress());
+    assertTrue(OBSERVER.preGetRSGroupInfoOfServerCalled);
+    assertTrue(OBSERVER.postGetRSGroupInfoOfServerCalled);
+  }
+}
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsKillRS.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsKillRS.java
index 2593216..1794a86 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsKillRS.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsKillRS.java
@@ -41,6 +41,7 @@ import org.apache.hadoop.hbase.client.TableDescriptor;
 import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
 import org.apache.hadoop.hbase.net.Address;
 import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.JVMClusterUtil;
 import org.apache.hadoop.hbase.util.VersionInfo;
@@ -49,24 +50,22 @@ import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
 
-@RunWith(Parameterized.class)
-@Category({ MediumTests.class })
+@Category({ RSGroupTests.class, MediumTests.class })
 public class TestRSGroupsKillRS extends TestRSGroupsBase {
 
   @ClassRule
   public static final HBaseClassTestRule CLASS_RULE =
     HBaseClassTestRule.forClass(TestRSGroupsKillRS.class);
 
-  protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsKillRS.class);
+  private static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsKillRS.class);
 
   @BeforeClass
   public static void setUp() throws Exception {
@@ -91,13 +90,13 @@ public class TestRSGroupsKillRS extends TestRSGroupsBase {
   @Test
   public void testKillRS() throws Exception {
     RSGroupInfo appInfo = addGroup("appInfo", 1);
-    final TableName tableName = TableName.valueOf(tablePrefix + "_ns",
-        getNameWithoutIndex(name.getMethodName()));
-    admin.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString())
+    final TableName tableName =
+      TableName.valueOf(TABLE_PREFIX + "_ns", getNameWithoutIndex(name.getMethodName()));
+    ADMIN.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString())
       .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build());
     final TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
       .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build();
-    admin.createTable(desc);
+    ADMIN.createTable(desc);
     // wait for created table to be assigned
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
@@ -107,19 +106,19 @@ public class TestRSGroupsKillRS extends TestRSGroupsBase {
     });
 
     ServerName targetServer = getServerName(appInfo.getServers().iterator().next());
-    assertEquals(1, admin.getRegions(targetServer).size());
+    assertEquals(1, ADMIN.getRegions(targetServer).size());
 
     try {
       // stopping may cause an exception
       // due to the connection loss
-      admin.stopRegionServer(targetServer.getAddress().toString());
+      ADMIN.stopRegionServer(targetServer.getAddress().toString());
     } catch (Exception e) {
     }
     // wait until the server is actually down
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return !cluster.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer);
+        return !CLUSTER.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer);
       }
     });
     // there is only one rs in the group and we killed it, so the region can not be online, until
@@ -127,30 +126,29 @@ public class TestRSGroupsKillRS extends TestRSGroupsBase {
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return !cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty();
+        return !CLUSTER.getClusterMetrics().getRegionStatesInTransition().isEmpty();
       }
     });
     Set<Address> newServers = Sets.newHashSet();
-    newServers
-      .add(rsGroupAdmin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().iterator().next());
-    rsGroupAdmin.moveToRSGroup(newServers, appInfo.getName());
+    newServers.add(ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().iterator().next());
+    ADMIN.moveServersToRSGroup(newServers, appInfo.getName());
 
     // Make sure all the table's regions get reassigned
     // disabling the table guarantees no conflicting assign/unassign (ie SSH) happens
-    admin.disableTable(tableName);
-    admin.enableTable(tableName);
+    ADMIN.disableTable(tableName);
+    ADMIN.enableTable(tableName);
 
     // wait for region to be assigned
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
-        return cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty();
+        return CLUSTER.getClusterMetrics().getRegionStatesInTransition().isEmpty();
       }
     });
 
     ServerName targetServer1 = getServerName(newServers.iterator().next());
-    assertEquals(1, admin.getRegions(targetServer1).size());
-    assertEquals(tableName, admin.getRegions(targetServer1).get(0).getTable());
+    assertEquals(1, ADMIN.getRegions(targetServer1).size());
+    assertEquals(tableName, ADMIN.getRegions(targetServer1).get(0).getTable());
   }
 
   @Test
@@ -165,34 +163,33 @@ public class TestRSGroupsKillRS extends TestRSGroupsBase {
     TEST_UTIL.loadTable(t, Bytes.toBytes("f"));
     Set<TableName> toAddTables = new HashSet<>();
     toAddTables.add(tableName);
-    rsGroupAdmin.setRSGroup(toAddTables, groupName);
-    assertTrue(rsGroupAdmin.getRSGroup(groupName).getTables().contains(tableName));
+    ADMIN.setRSGroup(toAddTables, groupName);
+    assertTrue(RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(groupName).getTables().contains(tableName));
     TEST_UTIL.waitTableAvailable(tableName, 30000);
 
     // check my_group servers and table regions
-    Set<Address> servers = rsGroupAdmin.getRSGroup(groupName).getServers();
+    Set<Address> servers = ADMIN.getRSGroup(groupName).getServers();
     assertEquals(2, servers.size());
     LOG.debug("group servers {}", servers);
-    for (RegionInfo tr :
-        master.getAssignmentManager().getRegionStates().getRegionsOfTable(tableName)) {
-      assertTrue(servers.contains(
-          master.getAssignmentManager().getRegionStates().getRegionAssignments()
-              .get(tr).getAddress()));
+    for (RegionInfo tr : MASTER.getAssignmentManager().getRegionStates()
+      .getRegionsOfTable(tableName)) {
+      assertTrue(servers.contains(MASTER.getAssignmentManager().getRegionStates()
+        .getRegionAssignments().get(tr).getAddress()));
     }
 
     // Move a region, to ensure there exists a region whose 'lastHost' is in my_group
     // ('lastHost' of other regions are in 'default' group)
     // and check if all table regions are online
     List<ServerName> gsn = new ArrayList<>();
-    for(Address addr : servers){
+    for (Address addr : servers) {
       gsn.add(getServerName(addr));
     }
     assertEquals(2, gsn.size());
-    for(Map.Entry<RegionInfo, ServerName> entry :
-        master.getAssignmentManager().getRegionStates().getRegionAssignments().entrySet()){
-      if(entry.getKey().getTable().equals(tableName)){
+    for (Map.Entry<RegionInfo, ServerName> entry : MASTER.getAssignmentManager().getRegionStates()
+      .getRegionAssignments().entrySet()) {
+      if (entry.getKey().getTable().equals(tableName)) {
         LOG.debug("move region {} from {} to {}", entry.getKey().getRegionNameAsString(),
-            entry.getValue(), gsn.get(1 - gsn.indexOf(entry.getValue())));
+          entry.getValue(), gsn.get(1 - gsn.indexOf(entry.getValue())));
         TEST_UTIL.moveRegionAndWait(entry.getKey(), gsn.get(1 - gsn.indexOf(entry.getValue())));
         break;
       }
@@ -201,40 +198,43 @@ public class TestRSGroupsKillRS extends TestRSGroupsBase {
 
     // case 1: stop all the regionservers in my_group, and restart a regionserver in my_group,
     // and then check if all table regions are online
-    for(Address addr : rsGroupAdmin.getRSGroup(groupName).getServers()) {
+    for (Address addr : ADMIN.getRSGroup(groupName).getServers()) {
       TEST_UTIL.getMiniHBaseCluster().stopRegionServer(getServerName(addr));
     }
     // better wait for a while for region reassign
     sleep(10000);
     assertEquals(NUM_SLAVES_BASE - gsn.size(),
-        TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
+      TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
     TEST_UTIL.getMiniHBaseCluster().startRegionServer(gsn.get(0).getHostname(),
-        gsn.get(0).getPort());
+      gsn.get(0).getPort());
     assertEquals(NUM_SLAVES_BASE - gsn.size() + 1,
-        TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
+      TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
     TEST_UTIL.waitTableAvailable(tableName, 30000);
 
     // case 2: stop all the regionservers in my_group, and move another
     // regionserver(from the 'default' group) to my_group,
     // and then check if all table regions are online
-    for(JVMClusterUtil.RegionServerThread rst :
-        TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads()){
-      if(rst.getRegionServer().getServerName().getAddress().equals(gsn.get(0).getAddress())){
+    for (JVMClusterUtil.RegionServerThread rst : TEST_UTIL.getMiniHBaseCluster()
+      .getLiveRegionServerThreads()) {
+      if (rst.getRegionServer().getServerName().getAddress().equals(gsn.get(0).getAddress())) {
         TEST_UTIL.getMiniHBaseCluster().stopRegionServer(rst.getRegionServer().getServerName());
         break;
       }
     }
     sleep(10000);
     assertEquals(NUM_SLAVES_BASE - gsn.size(),
-        TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
-    ServerName newServer = master.getServerManager().getOnlineServersList().get(0);
-    rsGroupAdmin.moveToRSGroup(Sets.newHashSet(newServer.getAddress()), groupName);
+      TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
+    ServerName newServer = MASTER.getServerManager().getOnlineServersList().get(0);
+    ADMIN.moveServersToRSGroup(Sets.newHashSet(newServer.getAddress()), groupName);
     // wait and check if table regions are online
     TEST_UTIL.waitTableAvailable(tableName, 30000);
   }
 
+  // TODO: can not change meta group for now as we can not change the table descriptor of meta
+  // table, this has to be done before we merge back to master.
+  @Ignore
   @Test
-  public void testLowerMetaGroupVersion() throws Exception{
+  public void testLowerMetaGroupVersion() throws Exception {
     // create a rsgroup and move one regionserver to it
     String groupName = "meta_group";
     int groupRSCount = 1;
@@ -244,33 +244,32 @@ public class TestRSGroupsKillRS extends TestRSGroupsBase {
     tableName = TableName.META_TABLE_NAME;
     Set<TableName> toAddTables = new HashSet<>();
     toAddTables.add(tableName);
-    rsGroupAdmin.setRSGroup(toAddTables, groupName);
-    assertTrue(rsGroupAdmin.getRSGroup(groupName).getTables().contains(tableName));
+    ADMIN.setRSGroup(toAddTables, groupName);
+    assertTrue(RS_GROUP_ADMIN_CLIENT.getRSGroupInfo(groupName).getTables().contains(tableName));
     TEST_UTIL.waitTableAvailable(tableName, 30000);
 
     // restart the regionserver in meta_group, and lower its version
     String originVersion = "";
     Set<Address> servers = new HashSet<>();
-    for(Address addr : rsGroupAdmin.getRSGroup(groupName).getServers()) {
+    for (Address addr : ADMIN.getRSGroup(groupName).getServers()) {
       servers.add(addr);
       TEST_UTIL.getMiniHBaseCluster().stopRegionServer(getServerName(addr));
-      originVersion = master.getRegionServerVersion(getServerName(addr));
+      originVersion = MASTER.getRegionServerVersion(getServerName(addr));
     }
     // better wait for a while for region reassign
     sleep(10000);
     assertEquals(NUM_SLAVES_BASE - groupRSCount,
-        TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
+      TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
     Address address = servers.iterator().next();
     int majorVersion = VersionInfo.getMajorVersion(originVersion);
     assertTrue(majorVersion >= 1);
     String lowerVersion = String.valueOf(majorVersion - 1) + originVersion.split("\\.")[1];
     setFinalStatic(Version.class.getField("version"), lowerVersion);
-    TEST_UTIL.getMiniHBaseCluster().startRegionServer(address.getHostname(),
-        address.getPort());
+    TEST_UTIL.getMiniHBaseCluster().startRegionServer(address.getHostname(), address.getPort());
     assertEquals(NUM_SLAVES_BASE,
-        TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
+      TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
     assertTrue(VersionInfo.compareVersion(originVersion,
-        master.getRegionServerVersion(getServerName(servers.iterator().next()))) > 0);
+      MASTER.getRegionServerVersion(getServerName(servers.iterator().next()))) > 0);
     LOG.debug("wait for META assigned...");
     TEST_UTIL.waitTableAvailable(tableName, 30000);
   }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsOfflineMode.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsOfflineMode.java
index cfc721f..c4a2031 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsOfflineMode.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsOfflineMode.java
@@ -17,26 +17,25 @@
  */
 package org.apache.hadoop.hbase.rsgroup;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
 import org.apache.hadoop.hbase.HBaseClassTestRule;
 import org.apache.hadoop.hbase.HBaseCluster;
 import org.apache.hadoop.hbase.HBaseTestingUtility;
-import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.MiniHBaseCluster;
 import org.apache.hadoop.hbase.StartMiniClusterOption;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.Waiter;
 import org.apache.hadoop.hbase.client.Admin;
 import org.apache.hadoop.hbase.client.RegionInfo;
-import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
 import org.apache.hadoop.hbase.master.HMaster;
 import org.apache.hadoop.hbase.master.ServerManager;
 import org.apache.hadoop.hbase.regionserver.HRegionServer;
 import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.junit.AfterClass;
-import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Rule;
@@ -48,14 +47,17 @@ import org.slf4j.LoggerFactory;
 
 import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
 
-// This tests that GroupBasedBalancer will use data in zk to do balancing during master startup.
-// This does not test retain assignment.
-// The tests brings up 3 RS, creates a new RS group 'my_group', moves 1 RS to 'my_group', assigns
-// 'hbase:rsgroup' to 'my_group', and kill the only server in that group so that 'hbase:rsgroup'
-// table isn't available. It then kills the active master and waits for backup master to come
-// online. In new master, RSGroupInfoManagerImpl gets the data from zk and waits for the expected
-// assignment with a timeout.
-@Category(MediumTests.class)
+/**
+ * This tests that GroupBasedBalancer will use data in zk to do balancing during master startup.
+ * This does not test retain assignment.
+ * <p/>
+ * The tests brings up 3 RS, creates a new RS group 'my_group', moves 1 RS to 'my_group', assigns
+ * 'hbase:rsgroup' to 'my_group', and kill the only server in that group so that 'hbase:rsgroup'
+ * table isn't available. It then kills the active master and waits for backup master to come
+ * online. In new master, RSGroupInfoManagerImpl gets the data from zk and waits for the expected
+ * assignment with a timeout.
+ */
+@Category({ RSGroupTests.class, MediumTests.class })
 public class TestRSGroupsOfflineMode extends TestRSGroupsBase {
 
   @ClassRule
@@ -75,10 +77,7 @@ public class TestRSGroupsOfflineMode extends TestRSGroupsBase {
   @BeforeClass
   public static void setUp() throws Exception {
     TEST_UTIL = new HBaseTestingUtility();
-    TEST_UTIL.getConfiguration().set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
-      RSGroupBasedLoadBalancer.class.getName());
-    TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
-      RSGroupAdminEndpoint.class.getName());
+    TEST_UTIL.getConfiguration().setBoolean(RSGroupInfoManager.RS_GROUP_ENABLED, true);
     TEST_UTIL.getConfiguration().set(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, "1");
     StartMiniClusterOption option =
       StartMiniClusterOption.builder().numMasters(2).numRegionServers(3).numDataNodes(3).build();
@@ -112,8 +111,8 @@ public class TestRSGroupsOfflineMode extends TestRSGroupsBase {
     final HRegionServer groupRS = ((MiniHBaseCluster) cluster).getRegionServer(1);
     final HRegionServer failoverRS = ((MiniHBaseCluster) cluster).getRegionServer(2);
     String newGroup = "my_group";
-    RSGroupAdminClient groupAdmin = new RSGroupAdminClient(TEST_UTIL.getConnection());
-    groupAdmin.addRSGroup(newGroup);
+    Admin admin = TEST_UTIL.getAdmin();
+    admin.addRSGroup(newGroup);
     if (master.getAssignmentManager().getRegionStates().getRegionAssignments()
       .containsValue(failoverRS.getServerName())) {
       for (RegionInfo regionInfo : hbaseAdmin.getRegions(failoverRS.getServerName())) {
@@ -130,7 +129,7 @@ public class TestRSGroupsOfflineMode extends TestRSGroupsBase {
     }
 
     // Move server to group and make sure all tables are assigned.
-    groupAdmin.moveServers(Sets.newHashSet(groupRS.getServerName().getAddress()), newGroup);
+    admin.moveServersToRSGroup(Sets.newHashSet(groupRS.getServerName().getAddress()), newGroup);
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
       public boolean evaluate() throws Exception {
@@ -139,7 +138,7 @@ public class TestRSGroupsOfflineMode extends TestRSGroupsBase {
       }
     });
     // Move table to group and wait.
-    groupAdmin.setRSGroup(Sets.newHashSet(RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME), newGroup);
+    admin.setRSGroup(Sets.newHashSet(RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME), newGroup);
     LOG.info("Waiting for move table...");
     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
       @Override
@@ -177,7 +176,7 @@ public class TestRSGroupsOfflineMode extends TestRSGroupsBase {
         return failoverRS.getRegions(failoverTable).size() >= 1;
       }
     });
-    Assert.assertEquals(0, failoverRS.getRegions(RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME).size());
+    assertEquals(0, failoverRS.getRegions(RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME).size());
 
     // Need this for minicluster to shutdown cleanly.
     master.stopMaster();
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsWithACL.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsWithACL.java
index 06c4e4e..12ad7f5 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsWithACL.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsWithACL.java
@@ -26,13 +26,11 @@ import java.util.Optional;
 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.TableName;
 import org.apache.hadoop.hbase.TableNotFoundException;
 import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
 import org.apache.hadoop.hbase.client.Connection;
 import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
-import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
 import org.apache.hadoop.hbase.ipc.RpcServer;
 import org.apache.hadoop.hbase.master.HMaster;
 import org.apache.hadoop.hbase.security.User;
@@ -96,8 +94,6 @@ public class TestRSGroupsWithACL extends SecureTestUtil {
   private static User USER_GROUP_WRITE;
 
   private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
-
-  private static RSGroupAdminEndpoint rsGroupAdminEndpoint;
   private static HMaster master;
   private static AccessChecker accessChecker;
   private static UserProvider userProvider;
@@ -106,17 +102,14 @@ public class TestRSGroupsWithACL extends SecureTestUtil {
   public static void setupBeforeClass() throws Exception {
     // setup configuration
     conf = TEST_UTIL.getConfiguration();
-    conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, RSGroupBasedLoadBalancer.class.getName());
     // Enable security
     enableSecurity(conf);
     // Verify enableSecurity sets up what we require
     verifyConfiguration(conf);
     // Enable rsgroup
-    configureRSGroupAdminEndpoint(conf);
+    conf.setBoolean(RSGroupInfoManager.RS_GROUP_ENABLED, true);
 
     TEST_UTIL.startMiniCluster();
-    rsGroupAdminEndpoint = (RSGroupAdminEndpoint) TEST_UTIL.getMiniHBaseCluster().getMaster()
-        .getMasterCoprocessorHost().findCoprocessor(RSGroupAdminEndpoint.class.getName());
     // Wait for the ACL table to become available
     TEST_UTIL.waitUntilAllRegionsAssigned(PermissionStorage.ACL_TABLE_NAME);
 
@@ -216,16 +209,6 @@ public class TestRSGroupsWithACL extends SecureTestUtil {
     TEST_UTIL.shutdownMiniCluster();
   }
 
-  private static void configureRSGroupAdminEndpoint(Configuration conf) {
-    String currentCoprocessors = conf.get(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY);
-    String coprocessors = RSGroupAdminEndpoint.class.getName();
-    if (currentCoprocessors != null) {
-      coprocessors += "," + currentCoprocessors;
-    }
-    conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, coprocessors);
-    conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, RSGroupBasedLoadBalancer.class.getName());
-  }
-
   @Test
   public void testGetRSGroupInfo() throws Exception {
     AccessTestAction action = () -> {
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
similarity index 53%
copy from hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java
copy to hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
index 7cf4ed1..6fe2c0a 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
@@ -17,18 +17,24 @@
  */
 package org.apache.hadoop.hbase.rsgroup;
 
-import com.google.protobuf.ServiceException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
 import java.util.concurrent.Future;
 import java.util.regex.Pattern;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.CacheEvictionStats;
 import org.apache.hadoop.hbase.ClusterMetrics;
+import org.apache.hadoop.hbase.ClusterMetrics.Option;
 import org.apache.hadoop.hbase.NamespaceDescriptor;
 import org.apache.hadoop.hbase.NamespaceNotFoundException;
 import org.apache.hadoop.hbase.RegionMetrics;
@@ -41,29 +47,20 @@ import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
 import org.apache.hadoop.hbase.client.CompactType;
 import org.apache.hadoop.hbase.client.CompactionState;
 import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.client.ConnectionFactory;
 import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.client.SnapshotDescription;
+import org.apache.hadoop.hbase.client.Table;
 import org.apache.hadoop.hbase.client.TableDescriptor;
 import org.apache.hadoop.hbase.client.replication.TableCFs;
 import org.apache.hadoop.hbase.client.security.SecurityCapability;
+import org.apache.hadoop.hbase.exceptions.DeserializationException;
 import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
 import org.apache.hadoop.hbase.net.Address;
 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
-import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.AddRSGroupRequest;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.BalanceRSGroupRequest;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerRequest;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerResponse;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableRequest;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableResponse;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosRequest;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveServersRequest;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveTablesRequest;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RSGroupAdminService;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveRSGroupRequest;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveServersRequest;
 import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
 import org.apache.hadoop.hbase.quotas.QuotaFilter;
 import org.apache.hadoop.hbase.quotas.QuotaSettings;
@@ -79,1009 +76,797 @@ import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
 import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
 import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
 import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
+import org.apache.hadoop.hbase.zookeeper.ZKUtil;
+import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
+import org.apache.hadoop.hbase.zookeeper.ZNodePaths;
 import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.zookeeper.KeeperException;
 
-import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
+import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
 import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
 
-/**
- * Client used for managing region server group information.
- *
- * @deprecated Keep it here only for tests, using {@link Admin} instead.
- */
-@Deprecated
 @InterfaceAudience.Private
-public class RSGroupAdminClient implements RSGroupAdmin, Admin {
-  private RSGroupAdminService.BlockingInterface stub;
-  private Admin admin;
+public class VerifyingRSGroupAdmin implements Admin, Closeable {
 
-  public RSGroupAdminClient(Connection conn) throws IOException {
-    admin = conn.getAdmin();
-    stub = RSGroupAdminService.newBlockingStub(admin.coprocessorService());
-  }
+  private final Connection conn;
+
+  private final Admin admin;
 
-  // for writing UTs
-  @VisibleForTesting
-  protected RSGroupAdminClient() {
+  private final ZKWatcher zkw;
+
+  public VerifyingRSGroupAdmin(Configuration conf) throws IOException {
+    conn = ConnectionFactory.createConnection(conf);
+    admin = conn.getAdmin();
+    zkw = new ZKWatcher(conf, this.getClass().getSimpleName(), null);
   }
 
-  @Override
   public int getOperationTimeout() {
-    return 0;
+    return admin.getOperationTimeout();
   }
 
-  @Override
   public int getSyncWaitTimeout() {
-    return 0;
+    return admin.getSyncWaitTimeout();
   }
 
-  @Override
   public void abort(String why, Throwable e) {
-
+    admin.abort(why, e);
   }
 
-  @Override
   public boolean isAborted() {
-    return false;
+    return admin.isAborted();
   }
 
-  @Override
   public Connection getConnection() {
-    return null;
+    return admin.getConnection();
   }
 
-  @Override
   public boolean tableExists(TableName tableName) throws IOException {
-    return false;
+    return admin.tableExists(tableName);
   }
 
-  @Override
   public List<TableDescriptor> listTableDescriptors() throws IOException {
-    return null;
+    return admin.listTableDescriptors();
   }
 
-  @Override
   public List<TableDescriptor> listTableDescriptors(boolean includeSysTables) throws IOException {
-    return null;
+    return admin.listTableDescriptors(includeSysTables);
   }
 
-  @Override
-  public List<TableDescriptor> listTableDescriptors(Pattern pattern) throws IOException {
-    return null;
-  }
-
-  @Override
   public List<TableDescriptor> listTableDescriptors(Pattern pattern, boolean includeSysTables)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.listTableDescriptors(pattern, includeSysTables);
   }
 
-  @Override
   public TableName[] listTableNames() throws IOException {
-    return new TableName[0];
+    return admin.listTableNames();
   }
 
-  @Override
   public TableName[] listTableNames(Pattern pattern, boolean includeSysTables) throws IOException {
-    return new TableName[0];
+    return admin.listTableNames(pattern, includeSysTables);
   }
 
-  @Override
   public TableDescriptor getDescriptor(TableName tableName)
-      throws TableNotFoundException, IOException {
-    return null;
+    throws TableNotFoundException, IOException {
+    return admin.getDescriptor(tableName);
   }
 
-  @Override
   public void createTable(TableDescriptor desc, byte[] startKey, byte[] endKey, int numRegions)
-      throws IOException {
-
+    throws IOException {
+    admin.createTable(desc, startKey, endKey, numRegions);
   }
 
-  @Override
   public Future<Void> createTableAsync(TableDescriptor desc) throws IOException {
-    return null;
+    return admin.createTableAsync(desc);
   }
 
-  @Override
   public Future<Void> createTableAsync(TableDescriptor desc, byte[][] splitKeys)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.createTableAsync(desc, splitKeys);
   }
 
-  @Override
   public Future<Void> deleteTableAsync(TableName tableName) throws IOException {
-    return null;
+    return admin.deleteTableAsync(tableName);
   }
 
-  @Override
   public Future<Void> truncateTableAsync(TableName tableName, boolean preserveSplits)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.truncateTableAsync(tableName, preserveSplits);
   }
 
-  @Override
   public Future<Void> enableTableAsync(TableName tableName) throws IOException {
-    return null;
+    return admin.enableTableAsync(tableName);
   }
 
-  @Override
   public Future<Void> disableTableAsync(TableName tableName) throws IOException {
-    return null;
+    return admin.disableTableAsync(tableName);
   }
 
-  @Override
   public boolean isTableEnabled(TableName tableName) throws IOException {
-    return false;
+    return admin.isTableEnabled(tableName);
   }
 
-  @Override
   public boolean isTableDisabled(TableName tableName) throws IOException {
-    return false;
+    return admin.isTableDisabled(tableName);
   }
 
-  @Override
   public boolean isTableAvailable(TableName tableName) throws IOException {
-    return false;
+    return admin.isTableAvailable(tableName);
   }
 
-  @Override
   public Future<Void> addColumnFamilyAsync(TableName tableName, ColumnFamilyDescriptor columnFamily)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.addColumnFamilyAsync(tableName, columnFamily);
   }
 
-  @Override
   public Future<Void> deleteColumnFamilyAsync(TableName tableName, byte[] columnFamily)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.deleteColumnFamilyAsync(tableName, columnFamily);
   }
 
-  @Override
   public Future<Void> modifyColumnFamilyAsync(TableName tableName,
-      ColumnFamilyDescriptor columnFamily) throws IOException {
-    return null;
+    ColumnFamilyDescriptor columnFamily) throws IOException {
+    return admin.modifyColumnFamilyAsync(tableName, columnFamily);
   }
 
-  @Override
   public List<RegionInfo> getRegions(ServerName serverName) throws IOException {
-    return null;
+    return admin.getRegions(serverName);
   }
 
-  @Override
   public void flush(TableName tableName) throws IOException {
-
+    admin.flush(tableName);
   }
 
-  @Override
   public void flushRegion(byte[] regionName) throws IOException {
-
+    admin.flushRegion(regionName);
   }
 
-  @Override
   public void flushRegionServer(ServerName serverName) throws IOException {
-
+    admin.flushRegionServer(serverName);
   }
 
-  @Override
   public void compact(TableName tableName) throws IOException {
-
+    admin.compact(tableName);
   }
 
-  @Override
   public void compactRegion(byte[] regionName) throws IOException {
-
+    admin.compactRegion(regionName);
   }
 
-  @Override
   public void compact(TableName tableName, byte[] columnFamily) throws IOException {
-
+    admin.compact(tableName, columnFamily);
   }
 
-  @Override
   public void compactRegion(byte[] regionName, byte[] columnFamily) throws IOException {
-
+    admin.compactRegion(regionName, columnFamily);
   }
 
-  @Override
   public void compact(TableName tableName, CompactType compactType)
-      throws IOException, InterruptedException {
-
+    throws IOException, InterruptedException {
+    admin.compact(tableName, compactType);
   }
 
-  @Override
   public void compact(TableName tableName, byte[] columnFamily, CompactType compactType)
-      throws IOException, InterruptedException {
-
+    throws IOException, InterruptedException {
+    admin.compact(tableName, columnFamily, compactType);
   }
 
-  @Override
   public void majorCompact(TableName tableName) throws IOException {
-
+    admin.majorCompact(tableName);
   }
 
-  @Override
   public void majorCompactRegion(byte[] regionName) throws IOException {
-
+    admin.majorCompactRegion(regionName);
   }
 
-  @Override
   public void majorCompact(TableName tableName, byte[] columnFamily) throws IOException {
-
+    admin.majorCompact(tableName, columnFamily);
   }
 
-  @Override
   public void majorCompactRegion(byte[] regionName, byte[] columnFamily) throws IOException {
-
+    admin.majorCompactRegion(regionName, columnFamily);
   }
 
-  @Override
   public void majorCompact(TableName tableName, CompactType compactType)
-      throws IOException, InterruptedException {
-
+    throws IOException, InterruptedException {
+    admin.majorCompact(tableName, compactType);
   }
 
-  @Override
   public void majorCompact(TableName tableName, byte[] columnFamily, CompactType compactType)
-      throws IOException, InterruptedException {
-
+    throws IOException, InterruptedException {
+    admin.majorCompact(tableName, columnFamily, compactType);
   }
 
-  @Override
   public Map<ServerName, Boolean> compactionSwitch(boolean switchState,
-      List<String> serverNamesList) throws IOException {
-    return null;
+    List<String> serverNamesList) throws IOException {
+    return admin.compactionSwitch(switchState, serverNamesList);
   }
 
-  @Override
   public void compactRegionServer(ServerName serverName) throws IOException {
-
+    admin.compactRegionServer(serverName);
   }
 
-  @Override
   public void majorCompactRegionServer(ServerName serverName) throws IOException {
-
+    admin.majorCompactRegionServer(serverName);
   }
 
-  @Override
   public void move(byte[] encodedRegionName) throws IOException {
-
+    admin.move(encodedRegionName);
   }
 
-  @Override
   public void move(byte[] encodedRegionName, ServerName destServerName) throws IOException {
-
+    admin.move(encodedRegionName, destServerName);
   }
 
-  @Override
   public void assign(byte[] regionName) throws IOException {
-
+    admin.assign(regionName);
   }
 
-  @Override
   public void unassign(byte[] regionName, boolean force) throws IOException {
-
+    admin.unassign(regionName, force);
   }
 
-  @Override
   public void offline(byte[] regionName) throws IOException {
-
+    admin.offline(regionName);
   }
 
-  @Override
   public boolean balancerSwitch(boolean onOrOff, boolean synchronous) throws IOException {
-    return false;
+    return admin.balancerSwitch(onOrOff, synchronous);
   }
 
-  @Override
   public boolean balance() throws IOException {
-    return false;
+    return admin.balance();
   }
 
-  @Override
   public boolean balance(boolean force) throws IOException {
-    return false;
+    return admin.balance(force);
   }
 
-  @Override
   public boolean isBalancerEnabled() throws IOException {
-    return false;
+    return admin.isBalancerEnabled();
   }
 
-  @Override
   public CacheEvictionStats clearBlockCache(TableName tableName) throws IOException {
-    return null;
+    return admin.clearBlockCache(tableName);
   }
 
-  @Override
   public boolean normalize() throws IOException {
-    return false;
+    return admin.normalize();
   }
 
-  @Override
   public boolean isNormalizerEnabled() throws IOException {
-    return false;
+    return admin.isNormalizerEnabled();
   }
 
-  @Override
   public boolean normalizerSwitch(boolean on) throws IOException {
-    return false;
+    return admin.normalizerSwitch(on);
   }
 
-  @Override
   public boolean catalogJanitorSwitch(boolean onOrOff) throws IOException {
-    return false;
+    return admin.catalogJanitorSwitch(onOrOff);
   }
 
-  @Override
   public int runCatalogJanitor() throws IOException {
-    return 0;
+    return admin.runCatalogJanitor();
   }
 
-  @Override
   public boolean isCatalogJanitorEnabled() throws IOException {
-    return false;
+    return admin.isCatalogJanitorEnabled();
   }
 
-  @Override
   public boolean cleanerChoreSwitch(boolean onOrOff) throws IOException {
-    return false;
+    return admin.cleanerChoreSwitch(onOrOff);
   }
 
-  @Override
   public boolean runCleanerChore() throws IOException {
-    return false;
+    return admin.runCleanerChore();
   }
 
-  @Override
   public boolean isCleanerChoreEnabled() throws IOException {
-    return false;
+    return admin.isCleanerChoreEnabled();
   }
 
-  @Override
   public Future<Void> mergeRegionsAsync(byte[][] nameofRegionsToMerge, boolean forcible)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.mergeRegionsAsync(nameofRegionsToMerge, forcible);
   }
 
-  @Override
   public void split(TableName tableName) throws IOException {
-
+    admin.split(tableName);
   }
 
-  @Override
   public void split(TableName tableName, byte[] splitPoint) throws IOException {
-
+    admin.split(tableName, splitPoint);
   }
 
-  @Override
   public Future<Void> splitRegionAsync(byte[] regionName) throws IOException {
-    return null;
+    return admin.splitRegionAsync(regionName);
   }
 
-  @Override
   public Future<Void> splitRegionAsync(byte[] regionName, byte[] splitPoint) throws IOException {
-    return null;
+    return admin.splitRegionAsync(regionName, splitPoint);
   }
 
-  @Override
   public Future<Void> modifyTableAsync(TableDescriptor td) throws IOException {
-    return null;
+    return admin.modifyTableAsync(td);
   }
 
-  @Override
   public void shutdown() throws IOException {
-
+    admin.shutdown();
   }
 
-  @Override
   public void stopMaster() throws IOException {
-
+    admin.stopMaster();
   }
 
-  @Override
   public boolean isMasterInMaintenanceMode() throws IOException {
-    return false;
+    return admin.isMasterInMaintenanceMode();
   }
 
-  @Override
   public void stopRegionServer(String hostnamePort) throws IOException {
-
+    admin.stopRegionServer(hostnamePort);
   }
 
-  @Override
-  public ClusterMetrics getClusterMetrics(EnumSet<ClusterMetrics.Option> options)
-      throws IOException {
-    return null;
+  public ClusterMetrics getClusterMetrics(EnumSet<Option> options) throws IOException {
+    return admin.getClusterMetrics(options);
   }
 
-  @Override
   public List<RegionMetrics> getRegionMetrics(ServerName serverName) throws IOException {
-    return null;
+    return admin.getRegionMetrics(serverName);
   }
 
-  @Override
   public List<RegionMetrics> getRegionMetrics(ServerName serverName, TableName tableName)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.getRegionMetrics(serverName, tableName);
   }
 
-  @Override
   public Configuration getConfiguration() {
-    return null;
+    return admin.getConfiguration();
   }
 
-  @Override
   public Future<Void> createNamespaceAsync(NamespaceDescriptor descriptor) throws IOException {
-    return null;
+    return admin.createNamespaceAsync(descriptor);
   }
 
-  @Override
   public Future<Void> modifyNamespaceAsync(NamespaceDescriptor descriptor) throws IOException {
-    return null;
+    return admin.modifyNamespaceAsync(descriptor);
   }
 
-  @Override
   public Future<Void> deleteNamespaceAsync(String name) throws IOException {
-    return null;
+    return admin.deleteNamespaceAsync(name);
   }
 
-  @Override
   public NamespaceDescriptor getNamespaceDescriptor(String name)
-      throws NamespaceNotFoundException, IOException {
-    return null;
+    throws NamespaceNotFoundException, IOException {
+    return admin.getNamespaceDescriptor(name);
   }
 
-  @Override
   public String[] listNamespaces() throws IOException {
-    return new String[0];
+    return admin.listNamespaces();
   }
 
-  @Override
   public NamespaceDescriptor[] listNamespaceDescriptors() throws IOException {
-    return new NamespaceDescriptor[0];
+    return admin.listNamespaceDescriptors();
   }
 
-  @Override
   public List<TableDescriptor> listTableDescriptorsByNamespace(byte[] name) throws IOException {
-    return null;
+    return admin.listTableDescriptorsByNamespace(name);
   }
 
-  @Override
   public TableName[] listTableNamesByNamespace(String name) throws IOException {
-    return new TableName[0];
+    return admin.listTableNamesByNamespace(name);
   }
 
-  @Override
   public List<RegionInfo> getRegions(TableName tableName) throws IOException {
-    return null;
+    return admin.getRegions(tableName);
   }
 
-  @Override
   public void close() {
-
+    admin.close();
   }
 
-  @Override
   public List<TableDescriptor> listTableDescriptors(List<TableName> tableNames) throws IOException {
-    return null;
+    return admin.listTableDescriptors(tableNames);
   }
 
-  @Override
   public Future<Boolean> abortProcedureAsync(long procId, boolean mayInterruptIfRunning)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.abortProcedureAsync(procId, mayInterruptIfRunning);
   }
 
-  @Override
   public String getProcedures() throws IOException {
-    return null;
+    return admin.getProcedures();
   }
 
-  @Override
   public String getLocks() throws IOException {
-    return null;
+    return admin.getLocks();
   }
 
-  @Override
   public void rollWALWriter(ServerName serverName) throws IOException, FailedLogCloseException {
-
+    admin.rollWALWriter(serverName);
   }
 
-  @Override
   public CompactionState getCompactionState(TableName tableName) throws IOException {
-    return null;
+    return admin.getCompactionState(tableName);
   }
 
-  @Override
   public CompactionState getCompactionState(TableName tableName, CompactType compactType)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.getCompactionState(tableName, compactType);
   }
 
-  @Override
   public CompactionState getCompactionStateForRegion(byte[] regionName) throws IOException {
-    return null;
+    return admin.getCompactionStateForRegion(regionName);
   }
 
-  @Override
   public long getLastMajorCompactionTimestamp(TableName tableName) throws IOException {
-    return 0;
+    return admin.getLastMajorCompactionTimestamp(tableName);
   }
 
-  @Override
   public long getLastMajorCompactionTimestampForRegion(byte[] regionName) throws IOException {
-    return 0;
+    return admin.getLastMajorCompactionTimestampForRegion(regionName);
   }
 
-  @Override
   public void snapshot(SnapshotDescription snapshot)
-      throws IOException, SnapshotCreationException, IllegalArgumentException {
-
+    throws IOException, SnapshotCreationException, IllegalArgumentException {
+    admin.snapshot(snapshot);
   }
 
-  @Override
   public Future<Void> snapshotAsync(SnapshotDescription snapshot)
-      throws IOException, SnapshotCreationException {
-    return null;
+    throws IOException, SnapshotCreationException {
+    return admin.snapshotAsync(snapshot);
   }
 
-  @Override
   public boolean isSnapshotFinished(SnapshotDescription snapshot)
-      throws IOException, HBaseSnapshotException, UnknownSnapshotException {
-    return false;
+    throws IOException, HBaseSnapshotException, UnknownSnapshotException {
+    return admin.isSnapshotFinished(snapshot);
   }
 
-  @Override
   public void restoreSnapshot(String snapshotName) throws IOException, RestoreSnapshotException {
-
+    admin.restoreSnapshot(snapshotName);
   }
 
-  @Override
   public void restoreSnapshot(String snapshotName, boolean takeFailSafeSnapshot, boolean restoreAcl)
-      throws IOException, RestoreSnapshotException {
-
+    throws IOException, RestoreSnapshotException {
+    admin.restoreSnapshot(snapshotName, takeFailSafeSnapshot, restoreAcl);
   }
 
-  @Override
   public Future<Void> cloneSnapshotAsync(String snapshotName, TableName tableName,
-      boolean restoreAcl) throws IOException, TableExistsException, RestoreSnapshotException {
-    return null;
+    boolean restoreAcl) throws IOException, TableExistsException, RestoreSnapshotException {
+    return admin.cloneSnapshotAsync(snapshotName, tableName, restoreAcl);
   }
 
-  @Override
   public void execProcedure(String signature, String instance, Map<String, String> props)
-      throws IOException {
-
+    throws IOException {
+    admin.execProcedure(signature, instance, props);
   }
 
-  @Override
   public byte[] execProcedureWithReturn(String signature, String instance,
-      Map<String, String> props) throws IOException {
-    return new byte[0];
+    Map<String, String> props) throws IOException {
+    return admin.execProcedureWithReturn(signature, instance, props);
   }
 
-  @Override
   public boolean isProcedureFinished(String signature, String instance, Map<String, String> props)
-      throws IOException {
-    return false;
+    throws IOException {
+    return admin.isProcedureFinished(signature, instance, props);
   }
 
-  @Override
   public List<SnapshotDescription> listSnapshots() throws IOException {
-    return null;
+    return admin.listSnapshots();
   }
 
-  @Override
   public List<SnapshotDescription> listSnapshots(Pattern pattern) throws IOException {
-    return null;
+    return admin.listSnapshots(pattern);
   }
 
-  @Override
   public List<SnapshotDescription> listTableSnapshots(Pattern tableNamePattern,
-      Pattern snapshotNamePattern) throws IOException {
-    return null;
+    Pattern snapshotNamePattern) throws IOException {
+    return admin.listTableSnapshots(tableNamePattern, snapshotNamePattern);
   }
 
-  @Override
   public void deleteSnapshot(String snapshotName) throws IOException {
-
+    admin.deleteSnapshot(snapshotName);
   }
 
-  @Override
   public void deleteSnapshots(Pattern pattern) throws IOException {
-
+    admin.deleteSnapshots(pattern);
   }
 
-  @Override
   public void deleteTableSnapshots(Pattern tableNamePattern, Pattern snapshotNamePattern)
-      throws IOException {
-
+    throws IOException {
+    admin.deleteTableSnapshots(tableNamePattern, snapshotNamePattern);
   }
 
-  @Override
   public void setQuota(QuotaSettings quota) throws IOException {
-
+    admin.setQuota(quota);
   }
 
-  @Override
   public List<QuotaSettings> getQuota(QuotaFilter filter) throws IOException {
-    return null;
+    return admin.getQuota(filter);
   }
 
-  @Override
   public CoprocessorRpcChannel coprocessorService() {
-    return null;
+    return admin.coprocessorService();
   }
 
-  @Override
   public CoprocessorRpcChannel coprocessorService(ServerName serverName) {
-    return null;
+    return admin.coprocessorService(serverName);
   }
 
-  @Override
   public void updateConfiguration(ServerName server) throws IOException {
-
+    admin.updateConfiguration(server);
   }
 
-  @Override
   public void updateConfiguration() throws IOException {
-
+    admin.updateConfiguration();
   }
 
-  @Override
   public List<SecurityCapability> getSecurityCapabilities() throws IOException {
-    return null;
+    return admin.getSecurityCapabilities();
   }
 
-  @Override
   public boolean splitSwitch(boolean enabled, boolean synchronous) throws IOException {
-    return false;
+    return admin.splitSwitch(enabled, synchronous);
   }
 
-  @Override
   public boolean mergeSwitch(boolean enabled, boolean synchronous) throws IOException {
-    return false;
+    return admin.mergeSwitch(enabled, synchronous);
   }
 
-  @Override
   public boolean isSplitEnabled() throws IOException {
-    return false;
+    return admin.isSplitEnabled();
   }
 
-  @Override
   public boolean isMergeEnabled() throws IOException {
-    return false;
+    return admin.isMergeEnabled();
   }
 
-  @Override
   public Future<Void> addReplicationPeerAsync(String peerId, ReplicationPeerConfig peerConfig,
-      boolean enabled) throws IOException {
-    return null;
+    boolean enabled) throws IOException {
+    return admin.addReplicationPeerAsync(peerId, peerConfig, enabled);
   }
 
-  @Override
   public Future<Void> removeReplicationPeerAsync(String peerId) throws IOException {
-    return null;
+    return admin.removeReplicationPeerAsync(peerId);
   }
 
-  @Override
   public Future<Void> enableReplicationPeerAsync(String peerId) throws IOException {
-    return null;
+    return admin.enableReplicationPeerAsync(peerId);
   }
 
-  @Override
   public Future<Void> disableReplicationPeerAsync(String peerId) throws IOException {
-    return null;
+    return admin.disableReplicationPeerAsync(peerId);
   }
 
-  @Override
   public ReplicationPeerConfig getReplicationPeerConfig(String peerId) throws IOException {
-    return null;
+    return admin.getReplicationPeerConfig(peerId);
   }
 
-  @Override
   public Future<Void> updateReplicationPeerConfigAsync(String peerId,
-      ReplicationPeerConfig peerConfig) throws IOException {
-    return null;
+    ReplicationPeerConfig peerConfig) throws IOException {
+    return admin.updateReplicationPeerConfigAsync(peerId, peerConfig);
   }
 
-  @Override
   public List<ReplicationPeerDescription> listReplicationPeers() throws IOException {
-    return null;
+    return admin.listReplicationPeers();
   }
 
-  @Override
   public List<ReplicationPeerDescription> listReplicationPeers(Pattern pattern) throws IOException {
-    return null;
+    return admin.listReplicationPeers(pattern);
   }
 
-  @Override
   public Future<Void> transitReplicationPeerSyncReplicationStateAsync(String peerId,
-      SyncReplicationState state) throws IOException {
-    return null;
+    SyncReplicationState state) throws IOException {
+    return admin.transitReplicationPeerSyncReplicationStateAsync(peerId, state);
   }
 
-  @Override
   public void decommissionRegionServers(List<ServerName> servers, boolean offload)
-      throws IOException {
-
+    throws IOException {
+    admin.decommissionRegionServers(servers, offload);
   }
 
-  @Override
   public List<ServerName> listDecommissionedRegionServers() throws IOException {
-    return null;
+    return admin.listDecommissionedRegionServers();
   }
 
-  @Override
   public void recommissionRegionServer(ServerName server, List<byte[]> encodedRegionNames)
-      throws IOException {
-
+    throws IOException {
+    admin.recommissionRegionServer(server, encodedRegionNames);
   }
 
-  @Override
   public List<TableCFs> listReplicatedTableCFs() throws IOException {
-    return null;
+    return admin.listReplicatedTableCFs();
   }
 
-  @Override
   public void enableTableReplication(TableName tableName) throws IOException {
-
+    admin.enableTableReplication(tableName);
   }
 
-  @Override
   public void disableTableReplication(TableName tableName) throws IOException {
-
+    admin.disableTableReplication(tableName);
   }
 
-  @Override
   public void clearCompactionQueues(ServerName serverName, Set<String> queues)
-      throws IOException, InterruptedException {
-
+    throws IOException, InterruptedException {
+    admin.clearCompactionQueues(serverName, queues);
   }
 
-  @Override
   public List<ServerName> clearDeadServers(List<ServerName> servers) throws IOException {
-    return null;
+    return admin.clearDeadServers(servers);
   }
 
-  @Override
   public void cloneTableSchema(TableName tableName, TableName newTableName, boolean preserveSplits)
-      throws IOException {
-
+    throws IOException {
+    admin.cloneTableSchema(tableName, newTableName, preserveSplits);
   }
 
-  @Override
   public boolean switchRpcThrottle(boolean enable) throws IOException {
-    return false;
+    return admin.switchRpcThrottle(enable);
   }
 
-  @Override
   public boolean isRpcThrottleEnabled() throws IOException {
-    return false;
+    return admin.isRpcThrottleEnabled();
   }
 
-  @Override
   public boolean exceedThrottleQuotaSwitch(boolean enable) throws IOException {
-    return false;
+    return admin.exceedThrottleQuotaSwitch(enable);
   }
 
-  @Override
   public Map<TableName, Long> getSpaceQuotaTableSizes() throws IOException {
-    return null;
+    return admin.getSpaceQuotaTableSizes();
   }
 
-  @Override
-  public Map<TableName, ? extends SpaceQuotaSnapshotView> getRegionServerSpaceQuotaSnapshots(
-      ServerName serverName) throws IOException {
-    return null;
+  public Map<TableName, ? extends SpaceQuotaSnapshotView>
+    getRegionServerSpaceQuotaSnapshots(ServerName serverName) throws IOException {
+    return admin.getRegionServerSpaceQuotaSnapshots(serverName);
   }
 
-  @Override
   public SpaceQuotaSnapshotView getCurrentSpaceQuotaSnapshot(String namespace) throws IOException {
-    return null;
+    return admin.getCurrentSpaceQuotaSnapshot(namespace);
   }
 
-  @Override
   public SpaceQuotaSnapshotView getCurrentSpaceQuotaSnapshot(TableName tableName)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.getCurrentSpaceQuotaSnapshot(tableName);
   }
 
-  @Override
   public void grant(UserPermission userPermission, boolean mergeExistingPermissions)
-      throws IOException {
-
+    throws IOException {
+    admin.grant(userPermission, mergeExistingPermissions);
   }
 
-  @Override
   public void revoke(UserPermission userPermission) throws IOException {
-
+    admin.revoke(userPermission);
   }
 
-  @Override
-  public List<UserPermission> getUserPermissions(
-      GetUserPermissionsRequest getUserPermissionsRequest) throws IOException {
-    return null;
+  public List<UserPermission>
+    getUserPermissions(GetUserPermissionsRequest getUserPermissionsRequest) throws IOException {
+    return admin.getUserPermissions(getUserPermissionsRequest);
   }
 
-  @Override
   public List<Boolean> hasUserPermissions(String userName, List<Permission> permissions)
-      throws IOException {
-    return null;
+    throws IOException {
+    return admin.hasUserPermissions(userName, permissions);
   }
 
-  @Override
   public boolean snapshotCleanupSwitch(boolean on, boolean synchronous) throws IOException {
-    return false;
+    return admin.snapshotCleanupSwitch(on, synchronous);
   }
 
-  @Override
   public boolean isSnapshotCleanupEnabled() throws IOException {
-    return false;
-  }
-
-  @Override
-  public RSGroupInfo getRSGroupInfo(String groupName) throws IOException {
-    return getRSGroup(groupName);
-  }
-
-  @Override
-  public void moveServers(Set<Address> servers, String targetGroup) throws IOException {
-    moveToRSGroup(servers, targetGroup);
+    return admin.isSnapshotCleanupEnabled();
   }
 
-  @Override
   public void addRSGroup(String groupName) throws IOException {
-    AddRSGroupRequest request = AddRSGroupRequest.newBuilder().setRSGroupName(groupName).build();
-    try {
-      stub.addRSGroup(null, request);
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
+    admin.addRSGroup(groupName);
+    verify();
   }
 
-  @Override
   public RSGroupInfo getRSGroup(String groupName) throws IOException {
-    try {
-      GetRSGroupInfoResponse resp = stub.getRSGroupInfo(null,
-          GetRSGroupInfoRequest.newBuilder().setRSGroupName(groupName).build());
-      if (resp.hasRSGroupInfo()) {
-        return ProtobufUtil.toGroupInfo(resp.getRSGroupInfo());
-      }
-      return null;
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
+    return admin.getRSGroup(groupName);
   }
 
-  @Override
   public RSGroupInfo getRSGroup(Address hostPort) throws IOException {
-    GetRSGroupInfoOfServerRequest request = GetRSGroupInfoOfServerRequest.newBuilder().setServer(
-        HBaseProtos.ServerName.newBuilder().setHostName(hostPort.getHostname())
-            .setPort(hostPort.getPort()).build()).build();
-    try {
-      GetRSGroupInfoOfServerResponse resp = stub.getRSGroupInfoOfServer(null, request);
-      if (resp.hasRSGroupInfo()) {
-        return ProtobufUtil.toGroupInfo(resp.getRSGroupInfo());
-      }
-      return null;
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
+    return admin.getRSGroup(hostPort);
   }
 
-  @Override
   public RSGroupInfo getRSGroup(TableName tableName) throws IOException {
-    GetRSGroupInfoOfTableRequest request = GetRSGroupInfoOfTableRequest.newBuilder()
-        .setTableName(ProtobufUtil.toProtoTableName(tableName)).build();
-    try {
-      GetRSGroupInfoOfTableResponse resp = stub.getRSGroupInfoOfTable(null, request);
-      if (resp.hasRSGroupInfo()) {
-        return ProtobufUtil.toGroupInfo(resp.getRSGroupInfo());
-      }
-      return null;
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
+    return admin.getRSGroup(tableName);
   }
 
-  @Override
-  public void removeRSGroup(String name) throws IOException {
-    RemoveRSGroupRequest request = RemoveRSGroupRequest.newBuilder().setRSGroupName(name).build();
-    try {
-      stub.removeRSGroup(null, request);
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
+  public List<RSGroupInfo> listRSGroups() throws IOException {
+    return admin.listRSGroups();
   }
 
-  @Override
-  public void removeRSGroup(Set<Address> servers) throws IOException {
-    Set<HBaseProtos.ServerName> hostPorts = Sets.newHashSet();
-    for (Address el : servers) {
-      hostPorts.add(
-          HBaseProtos.ServerName.newBuilder().setHostName(el.getHostname()).setPort(el.getPort())
-              .build());
-    }
-    RemoveServersRequest request =
-        RemoveServersRequest.newBuilder().addAllServers(hostPorts).build();
-    try {
-      stub.removeServers(null, request);
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
+  public void removeRSGroup(String groupName) throws IOException {
+    admin.removeRSGroup(groupName);
+    verify();
   }
 
-  @Override
-  public void moveToRSGroup(Set<Address> servers, String targetGroup) throws IOException {
-    Set<HBaseProtos.ServerName> hostPorts = Sets.newHashSet();
-    for (Address el : servers) {
-      hostPorts.add(
-          HBaseProtos.ServerName.newBuilder().setHostName(el.getHostname()).setPort(el.getPort())
-              .build());
-    }
-    MoveServersRequest request =
-        MoveServersRequest.newBuilder().setTargetGroup(targetGroup).addAllServers(hostPorts)
-            .build();
-    try {
-      stub.moveServers(null, request);
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
+  public void removeServersFromRSGroup(Set<Address> servers) throws IOException {
+    admin.removeServersFromRSGroup(servers);
+    verify();
+  }
+
+  public void moveServersToRSGroup(Set<Address> servers, String targetGroup) throws IOException {
+    admin.moveServersToRSGroup(servers, targetGroup);
+    verify();
   }
 
-  @Override
   public void setRSGroup(Set<TableName> tables, String groupName) throws IOException {
-    MoveTablesRequest.Builder builder = MoveTablesRequest.newBuilder().setTargetGroup(groupName);
-    for (TableName tableName : tables) {
-      builder.addTableName(ProtobufUtil.toProtoTableName(tableName));
-      if (!admin.tableExists(tableName)) {
-        throw new TableNotFoundException(tableName);
-      }
-    }
-    try {
-      stub.moveTables(null, builder.build());
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
+    admin.setRSGroup(tables, groupName);
+    verify();
   }
 
-  @Override
   public boolean balanceRSGroup(String groupName) throws IOException {
-    BalanceRSGroupRequest request =
-        BalanceRSGroupRequest.newBuilder().setRSGroupName(groupName).build();
-    try {
-      return stub.balanceRSGroup(null, request).getBalanceRan();
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
-    }
+    return admin.balanceRSGroup(groupName);
   }
 
-  @Override
-  public List<RSGroupInfo> listRSGroups() throws IOException {
-    try {
-      List<RSGroupProtos.RSGroupInfo> resp =
-          stub.listRSGroupInfos(null, ListRSGroupInfosRequest.getDefaultInstance())
-              .getRSGroupInfoList();
-      List<RSGroupInfo> result = new ArrayList<>(resp.size());
-      for (RSGroupProtos.RSGroupInfo entry : resp) {
-        result.add(ProtobufUtil.toGroupInfo(entry));
+  private void verify() throws IOException {
+    Map<String, RSGroupInfo> groupMap = Maps.newHashMap();
+    Set<RSGroupInfo> zList = Sets.newHashSet();
+    List<TableDescriptor> tds = new ArrayList<>();
+    try (Admin admin = conn.getAdmin()) {
+      tds.addAll(admin.listTableDescriptors());
+      tds.addAll(admin.listTableDescriptorsByNamespace(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME));
+    }
+    SortedSet<Address> lives = Sets.newTreeSet();
+    for (ServerName sn : conn.getAdmin().getClusterMetrics().getLiveServerMetrics().keySet()) {
+      lives.add(sn.getAddress());
+    }
+    for (ServerName sn : conn.getAdmin().listDecommissionedRegionServers()) {
+      lives.remove(sn.getAddress());
+    }
+    try (Table table = conn.getTable(RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME);
+      ResultScanner scanner = table.getScanner(new Scan())) {
+      for (;;) {
+        Result result = scanner.next();
+        if (result == null) {
+          break;
+        }
+        RSGroupProtos.RSGroupInfo proto = RSGroupProtos.RSGroupInfo.parseFrom(result.getValue(
+          RSGroupInfoManagerImpl.META_FAMILY_BYTES, RSGroupInfoManagerImpl.META_QUALIFIER_BYTES));
+        RSGroupInfo rsGroupInfo = ProtobufUtil.toGroupInfo(proto);
+        groupMap.put(proto.getName(), RSGroupUtil.fillTables(rsGroupInfo, tds));
+        for (Address address : rsGroupInfo.getServers()) {
+          lives.remove(address);
+        }
+      }
+    }
+    SortedSet<TableName> tables = Sets.newTreeSet();
+    for (TableDescriptor td : conn.getAdmin().listTableDescriptors(Pattern.compile(".*"), true)) {
+      String groupName = td.getRegionServerGroup().orElse(RSGroupInfo.DEFAULT_GROUP);
+      if (groupName.equals(RSGroupInfo.DEFAULT_GROUP)) {
+        tables.add(td.getTableName());
       }
-      return result;
-    } catch (ServiceException e) {
-      throw ProtobufUtil.handleRemoteException(e);
     }
-  }
-
-  @Override
-  public RSGroupInfo getRSGroupOfServer(Address hostPort) throws IOException {
-    return getRSGroup(hostPort);
-  }
 
-  @Override
-  public void removeServers(Set<Address> servers) throws IOException {
-    removeRSGroup(servers);
+    groupMap.put(RSGroupInfo.DEFAULT_GROUP,
+      new RSGroupInfo(RSGroupInfo.DEFAULT_GROUP, lives, tables));
+    assertEquals(Sets.newHashSet(groupMap.values()), Sets.newHashSet(admin.listRSGroups()));
+    try {
+      String groupBasePath = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "rsgroup");
+      for (String znode : ZKUtil.listChildrenNoWatch(zkw, groupBasePath)) {
+        byte[] data = ZKUtil.getData(zkw, ZNodePaths.joinZNode(groupBasePath, znode));
+        if (data.length > 0) {
+          ProtobufUtil.expectPBMagicPrefix(data);
+          ByteArrayInputStream bis =
+            new ByteArrayInputStream(data, ProtobufUtil.lengthOfPBMagic(), data.length);
+          RSGroupInfo rsGroupInfo =
+            ProtobufUtil.toGroupInfo(RSGroupProtos.RSGroupInfo.parseFrom(bis));
+          zList.add(RSGroupUtil.fillTables(rsGroupInfo, tds));
+        }
+      }
+      groupMap.remove(RSGroupInfo.DEFAULT_GROUP);
+      assertEquals(zList.size(), groupMap.size());
+      for (RSGroupInfo rsGroupInfo : zList) {
+        assertTrue(groupMap.get(rsGroupInfo.getName()).equals(rsGroupInfo));
+      }
+    } catch (KeeperException e) {
+      throw new IOException("ZK verification failed", e);
+    } catch (DeserializationException e) {
+      throw new IOException("ZK verification failed", e);
+    } catch (InterruptedException e) {
+      throw new IOException("ZK verification failed", e);
+    }
   }
-
 }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
deleted file mode 100644
index b189697..0000000
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
+++ /dev/null
@@ -1,193 +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.assertTrue;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.regex.Pattern;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.NamespaceDescriptor;
-import org.apache.hadoop.hbase.ServerName;
-import org.apache.hadoop.hbase.TableName;
-import org.apache.hadoop.hbase.client.Admin;
-import org.apache.hadoop.hbase.client.Connection;
-import org.apache.hadoop.hbase.client.ConnectionFactory;
-import org.apache.hadoop.hbase.client.Result;
-import org.apache.hadoop.hbase.client.ResultScanner;
-import org.apache.hadoop.hbase.client.Scan;
-import org.apache.hadoop.hbase.client.Table;
-import org.apache.hadoop.hbase.client.TableDescriptor;
-import org.apache.hadoop.hbase.exceptions.DeserializationException;
-import org.apache.hadoop.hbase.net.Address;
-import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
-import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
-import org.apache.hadoop.hbase.zookeeper.ZKUtil;
-import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
-import org.apache.hadoop.hbase.zookeeper.ZNodePaths;
-import org.apache.yetus.audience.InterfaceAudience;
-import org.apache.zookeeper.KeeperException;
-
-import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
-import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
-
-@InterfaceAudience.Private
-public class VerifyingRSGroupAdminClient extends RSGroupAdminClient {
-  private Connection conn;
-  private ZKWatcher zkw;
-  private RSGroupAdminClient wrapped;
-
-  public VerifyingRSGroupAdminClient(RSGroupAdminClient RSGroupAdmin, Configuration conf)
-      throws IOException {
-    wrapped = RSGroupAdmin;
-    conn = ConnectionFactory.createConnection(conf);
-    zkw = new ZKWatcher(conf, this.getClass().getSimpleName(), null);
-  }
-
-  @Override
-  public void addRSGroup(String groupName) throws IOException {
-    wrapped.addRSGroup(groupName);
-    verify();
-  }
-
-  @Override
-  public RSGroupInfo getRSGroup(String groupName) throws IOException {
-    return wrapped.getRSGroupInfo(groupName);
-  }
-
-  @Override
-  public RSGroupInfo getRSGroup(TableName tableName) throws IOException {
-    return wrapped.getRSGroup(tableName);
-  }
-
-  @Override
-  public void moveToRSGroup(Set<Address> servers, String targetGroup) throws IOException {
-    wrapped.moveToRSGroup(servers, targetGroup);
-    verify();
-  }
-
-  @Override
-  public void removeRSGroup(String name) throws IOException {
-    wrapped.removeRSGroup(name);
-    verify();
-  }
-
-  @Override
-  public boolean balanceRSGroup(String groupName) throws IOException {
-    return wrapped.balanceRSGroup(groupName);
-  }
-
-  @Override
-  public List<RSGroupInfo> listRSGroups() throws IOException {
-    return wrapped.listRSGroups();
-  }
-
-  @Override
-  public RSGroupInfo getRSGroup(Address hostPort) throws IOException {
-    return wrapped.getRSGroup(hostPort);
-  }
-
-  @Override
-  public void removeRSGroup(Set<Address> servers) throws IOException {
-    wrapped.removeRSGroup(servers);
-    verify();
-  }
-
-  @Override
-  public void setRSGroup(Set<TableName> tables, String groupName) throws IOException{
-    wrapped.setRSGroup(tables, groupName);
-    verify();
-  }
-
-  public void verify() throws IOException {
-    Map<String, RSGroupInfo> groupMap = Maps.newHashMap();
-    Set<RSGroupInfo> zList = Sets.newHashSet();
-    List<TableDescriptor> tds = new ArrayList<>();
-    try (Admin admin = conn.getAdmin()) {
-      tds.addAll(admin.listTableDescriptors());
-      tds.addAll(admin.listTableDescriptorsByNamespace(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME));
-    }
-    SortedSet<Address> lives = Sets.newTreeSet();
-    for (ServerName sn : conn.getAdmin().getClusterMetrics().getLiveServerMetrics().keySet()) {
-      lives.add(sn.getAddress());
-    }
-    for (ServerName sn : conn.getAdmin().listDecommissionedRegionServers()) {
-      lives.remove(sn.getAddress());
-    }
-    try (Table table = conn.getTable(RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME);
-        ResultScanner scanner = table.getScanner(new Scan())) {
-      for (;;) {
-        Result result = scanner.next();
-        if (result == null) {
-          break;
-        }
-        RSGroupProtos.RSGroupInfo proto = RSGroupProtos.RSGroupInfo.parseFrom(result.getValue(
-          RSGroupInfoManagerImpl.META_FAMILY_BYTES, RSGroupInfoManagerImpl.META_QUALIFIER_BYTES));
-        RSGroupInfo rsGroupInfo = ProtobufUtil.toGroupInfo(proto);
-        groupMap.put(proto.getName(), RSGroupUtil.fillTables(rsGroupInfo, tds));
-        for(Address address : rsGroupInfo.getServers()){
-          lives.remove(address);
-        }
-      }
-    }
-    SortedSet<TableName> tables = Sets.newTreeSet();
-    for (TableDescriptor td : conn.getAdmin().listTableDescriptors(Pattern.compile(".*"),
-        true)){
-      String groupName = td.getRegionServerGroup().orElse(RSGroupInfo.DEFAULT_GROUP);
-      if (groupName.equals(RSGroupInfo.DEFAULT_GROUP)) {
-        tables.add(td.getTableName());
-      }
-    }
-
-    groupMap.put(RSGroupInfo.DEFAULT_GROUP,
-        new RSGroupInfo(RSGroupInfo.DEFAULT_GROUP, lives, tables));
-    assertEquals(Sets.newHashSet(groupMap.values()), Sets.newHashSet(wrapped.listRSGroups()));
-    try {
-      String groupBasePath = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "rsgroup");
-      for (String znode : ZKUtil.listChildrenNoWatch(zkw, groupBasePath)) {
-        byte[] data = ZKUtil.getData(zkw, ZNodePaths.joinZNode(groupBasePath, znode));
-        if (data.length > 0) {
-          ProtobufUtil.expectPBMagicPrefix(data);
-          ByteArrayInputStream bis =
-              new ByteArrayInputStream(data, ProtobufUtil.lengthOfPBMagic(), data.length);
-          RSGroupInfo rsGroupInfo =
-              ProtobufUtil.toGroupInfo(RSGroupProtos.RSGroupInfo.parseFrom(bis));
-          zList.add(RSGroupUtil.fillTables(rsGroupInfo, tds));
-        }
-      }
-      groupMap.remove(RSGroupInfo.DEFAULT_GROUP);
-      assertEquals(zList.size(), groupMap.size());
-      for (RSGroupInfo rsGroupInfo : zList) {
-        assertTrue(groupMap.get(rsGroupInfo.getName()).equals(rsGroupInfo));
-      }
-    } catch (KeeperException e) {
-      throw new IOException("ZK verification failed", e);
-    } catch (DeserializationException e) {
-      throw new IOException("ZK verification failed", e);
-    } catch (InterruptedException e) {
-      throw new IOException("ZK verification failed", e);
-    }
-  }
-}
diff --git a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
index 52d032e..a222c74 100644
--- a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
+++ b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
@@ -1165,7 +1165,7 @@ public class ThriftAdmin implements Admin {
   }
 
   @Override
-  public void moveToRSGroup(Set<Address> servers, String targetGroup) {
+  public void moveServersToRSGroup(Set<Address> servers, String targetGroup) {
     throw new NotImplementedException("moveToRSGroup not supported in ThriftAdmin");
   }
 
@@ -1195,7 +1195,7 @@ public class ThriftAdmin implements Admin {
   }
 
   @Override
-  public void removeRSGroup(Set<Address> servers) {
+  public void removeServersFromRSGroup(Set<Address> servers) {
     throw new NotImplementedException("removeRSGroup not supported in ThriftAdmin");
   }
 
diff --git a/pom.xml b/pom.xml
index 923dfc9..23fe2e4 100755
--- a/pom.xml
+++ b/pom.xml
@@ -3556,6 +3556,21 @@
         <surefire.secondPartGroups></surefire.secondPartGroups>
       </properties>
     </profile>
+    <profile>
+      <id>runRSGroupTests</id>
+      <activation>
+        <activeByDefault>false</activeByDefault>
+      </activation>
+      <properties>
+        <surefire.firstPartForkCount>1</surefire.firstPartForkCount>
+        <surefire.secondPartForkCount>1</surefire.secondPartForkCount>
+        <surefire.skipFirstPart>false</surefire.skipFirstPart>
+        <surefire.skipSecondPart>true</surefire.skipSecondPart>
+        <surefire.firstPartGroups>org.apache.hadoop.hbase.testclassification.RSGroupTests
+        </surefire.firstPartGroups>
+        <surefire.secondPartGroups></surefire.secondPartGroups>
+      </properties>
+    </profile>
 
     <profile>
       <!-- Use it to launch tests locally-->