http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java
----------------------------------------------------------------------
diff --git 
a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java
 
b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java
index 811cf71..3c0cccf 100644
--- 
a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java
+++ 
b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java
@@ -138,6 +138,139 @@ public class RSGroupAdminServer implements RSGroupAdmin {
     else regions.addFirst(hri);
   }
 
+  /**
+   * Check servers and tables.
+   * Fail if nulls or if servers and tables not belong to the same group
+   * @param servers servers to move
+   * @param tables tables to move
+   * @param targetGroupName target group name
+   * @throws IOException
+   */
+  private void checkServersAndTables(Set<Address> servers, Set<TableName> 
tables,
+                                     String targetGroupName) throws 
IOException {
+    // Presume first server's source group. Later ensure all servers are from 
this group.
+    Address firstServer = servers.iterator().next();
+    RSGroupInfo tmpSrcGrp = rsGroupInfoManager.getRSGroupOfServer(firstServer);
+    if (tmpSrcGrp == null) {
+      // Be careful. This exception message is tested for in 
TestRSGroupsBase...
+      throw new ConstraintException("Source RSGroup for server " + firstServer
+              + " does not exist.");
+    }
+    RSGroupInfo srcGrp = new RSGroupInfo(tmpSrcGrp);
+    if (srcGrp.getName().equals(targetGroupName)) {
+      throw new ConstraintException( "Target RSGroup " + targetGroupName +
+              " is same as source " + srcGrp.getName() + " RSGroup.");
+    }
+    // Only move online servers
+    checkOnlineServersOnly(servers);
+
+    // Ensure all servers are of same rsgroup.
+    for (Address server: servers) {
+      String tmpGroup = 
rsGroupInfoManager.getRSGroupOfServer(server).getName();
+      if (!tmpGroup.equals(srcGrp.getName())) {
+        throw new ConstraintException("Move server request should only come 
from one source " +
+                "RSGroup. Expecting only " + srcGrp.getName() + " but contains 
" + tmpGroup);
+      }
+    }
+
+    // Ensure all tables and servers are of same rsgroup.
+    for (TableName table : tables) {
+      String tmpGroup = rsGroupInfoManager.getRSGroupOfTable(table);
+      if (!tmpGroup.equals(srcGrp.getName())) {
+        throw new ConstraintException("Move table request should only come 
from one source " +
+                "RSGroup. Expecting only " + srcGrp.getName() + " but contains 
" + tmpGroup);
+      }
+    }
+
+    if (srcGrp.getServers().size() <= servers.size()
+            && srcGrp.getTables().size() > tables.size() ) {
+      throw new ConstraintException("Cannot leave a RSGroup " + 
srcGrp.getName() +
+              " that contains tables without servers to host them.");
+    }
+  }
+
+  /**
+   * @param servers the servers that will move to new group
+   * @param targetGroupName the target group name
+   * @param tables The regions of tables assigned to these servers will not 
unassign
+   * @throws IOException
+   */
+  private void unassignRegionFromServers(Set<Address> servers, String 
targetGroupName,
+                                     Set<TableName> tables) throws IOException 
{
+    boolean foundRegionsToUnassign;
+    RSGroupInfo targetGrp = getRSGroupInfo(targetGroupName);
+    Set<Address> allSevers = new HashSet<>(servers);
+    do {
+      foundRegionsToUnassign = false;
+      for (Iterator<Address> iter = allSevers.iterator(); iter.hasNext();) {
+        Address rs = iter.next();
+        // Get regions that are associated with this server and filter regions 
by tables.
+        List<HRegionInfo> regions = new ArrayList<>();
+        for (HRegionInfo region : getRegions(rs)) {
+          if (!tables.contains(region.getTable())) {
+            regions.add(region);
+          }
+        }
+
+        LOG.info("Unassigning " + regions.size() +
+                " region(s) from " + rs + " for server move to " + 
targetGroupName);
+        if (!regions.isEmpty()) {
+          for (HRegionInfo region: regions) {
+            // Regions might get assigned from tables of target group so we 
need to filter
+            if (!targetGrp.containsTable(region.getTable())) {
+              this.master.getAssignmentManager().unassign(region);
+              if (master.getAssignmentManager().getRegionStates().
+                      getRegionState(region).isFailedOpen()) {
+                continue;
+              }
+              foundRegionsToUnassign = true;
+            }
+          }
+        }
+        if (!foundRegionsToUnassign) {
+          iter.remove();
+        }
+      }
+      try {
+        rsGroupInfoManager.wait(1000);
+      } catch (InterruptedException e) {
+        LOG.warn("Sleep interrupted", e);
+        Thread.currentThread().interrupt();
+      }
+    } while (foundRegionsToUnassign);
+  }
+
+  /**
+   * @param tables the tables that will move to new group
+   * @param targetGroupName the target group name
+   * @param servers the regions of tables assigned to these servers will not 
unassign
+   * @throws IOException
+   */
+  private void unassignRegionFromTables(Set<TableName> tables, String 
targetGroupName,
+                                        Set<Address> servers) throws 
IOException {
+    for (TableName table: tables) {
+      LOG.info("Unassigning region(s) from " + table + " for table move to " + 
targetGroupName);
+      LockManager.MasterLock lock = 
master.getLockManager().createMasterLock(table,
+              LockProcedure.LockType.EXCLUSIVE, this.getClass().getName() + ": 
RSGroup: table move");
+      try {
+        try {
+          lock.acquire();
+        } catch (InterruptedException e) {
+          throw new IOException("Interrupted when waiting for table lock", e);
+        }
+        for (HRegionInfo region :
+                
master.getAssignmentManager().getRegionStates().getRegionsOfTable(table)) {
+          ServerName sn = 
master.getAssignmentManager().getRegionStates().getRegionServerOfRegion(region);
+          if (!servers.contains(sn.getAddress())) {
+            master.getAssignmentManager().unassign(region);
+          }
+        }
+      } finally {
+        lock.release();
+      }
+    }
+  }
+
   @edu.umd.cs.findbugs.annotations.SuppressWarnings(
       value="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE",
       justification="Ignoring complaint because don't know what it is 
complaining about")
@@ -420,6 +553,45 @@ public class RSGroupAdminServer implements RSGroupAdmin {
     return rsGroupInfoManager.getRSGroupOfServer(hostPort);
   }
 
+  @Override
+  public void moveServersAndTables(Set<Address> servers, Set<TableName> 
tables, String targetGroup)
+      throws IOException {
+    if (servers == null || servers.isEmpty() ) {
+      throw new ConstraintException("The list of servers to move cannot be 
null or empty.");
+    }
+    if (tables == null || tables.isEmpty()) {
+      throw new ConstraintException("The list of tables to move cannot be null 
or empty.");
+    }
+
+    //check target group
+    getAndCheckRSGroupInfo(targetGroup);
+
+    // Hold a lock on the manager instance while moving servers and tables to 
prevent
+    // another writer changing our state while we are working.
+    synchronized (rsGroupInfoManager) {
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().preMoveServersAndTables(servers, 
tables, targetGroup);
+      }
+      //check servers and tables status
+      checkServersAndTables(servers, tables, targetGroup);
+
+      //Move servers and tables to a new group.
+      String srcGroup = 
getRSGroupOfServer(servers.iterator().next()).getName();
+      rsGroupInfoManager.moveServersAndTables(servers, tables, srcGroup, 
targetGroup);
+
+      //unassign regions which not belong to these tables
+      unassignRegionFromServers(servers, targetGroup, tables);
+      //unassign regions which not assigned to these servers
+      unassignRegionFromTables(tables, targetGroup, servers);
+
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().postMoveServersAndTables(servers, 
tables, targetGroup);
+      }
+    }
+    LOG.info("Move servers and tables done. Severs :"
+            + servers + " , Tables : " + tables + " => " +  targetGroup);
+  }
+
   private Map<String, RegionState> rsGroupGetRegionsInTransition(String 
groupName)
       throws IOException {
     Map<String, RegionState> rit = Maps.newTreeMap();

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
----------------------------------------------------------------------
diff --git 
a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
 
b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
index 88ea04b..ab5c09f 100644
--- 
a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
+++ 
b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
@@ -104,4 +104,14 @@ public interface RSGroupInfoManager {
    * @return whether the manager is in online mode
    */
   boolean isOnline();
+
+  /**
+   * Move servers and tables to a new group.
+   * @param servers list of servers, must be part of the same group
+   * @param tables set of tables to move
+   * @param srcGroup groupName being moved from
+   * @param dstGroup groupName being moved to
+   */
+  void moveServersAndTables(Set<Address> servers, Set<TableName> tables,
+      String srcGroup, String dstGroup) throws IOException;
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
----------------------------------------------------------------------
diff --git 
a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
 
b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
index 6d157cc..9f77c77 100644
--- 
a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
+++ 
b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
@@ -276,6 +276,30 @@ class RSGroupInfoManagerImpl implements RSGroupInfoManager 
{
     return rsGroupStartupWorker.isOnline();
   }
 
+  @Override
+  public void moveServersAndTables(Set<Address> servers, Set<TableName> tables,
+                                   String srcGroup, String dstGroup) throws 
IOException {
+    //get server's group
+    RSGroupInfo srcGroupInfo = getRSGroupInfo(srcGroup);
+    RSGroupInfo dstGroupInfo = getRSGroupInfo(dstGroup);
+
+    //move servers
+    for (Address el: servers) {
+      srcGroupInfo.removeServer(el);
+      dstGroupInfo.addServer(el);
+    }
+    //move tables
+    for(TableName tableName: tables) {
+      srcGroupInfo.removeTable(tableName);
+      dstGroupInfo.addTable(tableName);
+    }
+
+    //flush changed groupinfo
+    Map<String,RSGroupInfo> newGroupMap = Maps.newHashMap(rsGroupMap);
+    newGroupMap.put(srcGroupInfo.getName(), srcGroupInfo);
+    newGroupMap.put(dstGroupInfo.getName(), dstGroupInfo);
+    flushConfig(newGroupMap);
+  }
 
   List<RSGroupInfo> retrieveGroupListFromGroupTable() throws IOException {
     List<RSGroupInfo> rsGroupInfoList = Lists.newArrayList();

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-rsgroup/src/main/protobuf/RSGroupAdmin.proto
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/protobuf/RSGroupAdmin.proto 
b/hbase-rsgroup/src/main/protobuf/RSGroupAdmin.proto
index fda9b09..0213402 100644
--- a/hbase-rsgroup/src/main/protobuf/RSGroupAdmin.proto
+++ b/hbase-rsgroup/src/main/protobuf/RSGroupAdmin.proto
@@ -106,6 +106,15 @@ message GetRSGroupInfoOfServerResponse {
   optional RSGroupInfo r_s_group_info = 1;
 }
 
+message MoveServersAndTablesRequest {
+  required string target_group = 1;
+  repeated ServerName servers = 2;
+  repeated TableName table_name = 3;
+}
+
+message MoveServersAndTablesResponse {
+}
+
 service RSGroupAdminService {
   rpc GetRSGroupInfo(GetRSGroupInfoRequest)
     returns (GetRSGroupInfoResponse);
@@ -133,4 +142,7 @@ service RSGroupAdminService {
 
   rpc ListRSGroupInfos(ListRSGroupInfosRequest)
     returns (ListRSGroupInfosResponse);
+
+  rpc MoveServersAndTables(MoveServersAndTablesRequest)
+    returns (MoveServersAndTablesResponse);
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
----------------------------------------------------------------------
diff --git 
a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
 
b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
index 59853a5..e8cdb78 100644
--- 
a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
+++ 
b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
@@ -691,4 +691,115 @@ public abstract class TestRSGroupsBase {
     assertTrue(newGroupTables.contains(tableNameA));
     assertTrue(newGroupTables.contains(tableNameB));
   }
+
+  @Test
+  public void testMoveServersAndTables() throws Exception {
+    final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 
1);
+    //create table
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regions = getTableRegionMap().get(tableName);
+        if (regions == null)
+          return false;
+        return getTableRegionMap().get(tableName).size() >= 5;
+      }
+    });
+
+    //get server which is not a member of new group
+    ServerName targetServer = null;
+    for(ServerName server : admin.getClusterStatus().getServers()) {
+      if(!newGroup.containsServer(server.getAddress()) && 
+           
!rsGroupAdmin.getRSGroupInfo("master").containsServer(server.getAddress())) {
+        targetServer = server;
+        break;
+      }
+    }
+
+    //test fail bogus server move
+    try {
+      
rsGroupAdmin.moveServersAndTables(Sets.newHashSet(Address.fromString("foo:9999")),
+              Sets.newHashSet(tableName), newGroup.getName());
+      fail("Bogus servers shouldn't have been successfully moved.");
+    } catch(IOException ex) {
+      String exp = "Source RSGroup for server foo:9999 does not exist.";
+      String msg = "Expected '" + exp + "' in exception message: ";
+      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
+    }
+
+    //test fail server move
+    try {
+      
rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()),
+              Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP);
+      fail("servers shouldn't have been successfully moved.");
+    } catch(IOException ex) {
+      String exp = "Target RSGroup " + RSGroupInfo.DEFAULT_GROUP +
+              " is same as source " + RSGroupInfo.DEFAULT_GROUP + " RSGroup.";
+      String msg = "Expected '" + exp + "' in exception message: ";
+      assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp));
+    }
+
+    //verify default group info
+    Assert.assertEquals(3,
+            
rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size());
+    Assert.assertEquals(4,
+            
rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size());
+
+    //verify new group info
+    Assert.assertEquals(1,
+            
rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers().size());
+    Assert.assertEquals(0,
+            
rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
+
+    //get all region to move targetServer
+    List<String> regionList = getTableRegionMap().get(tableName);
+    for(String region : regionList) {
+      // Lets move this region to the targetServer
+      
TEST_UTIL.getAdmin().move(Bytes.toBytes(HRegionInfo.encodeRegionName(Bytes.toBytes(region))),
+              Bytes.toBytes(targetServer.getServerName()));
+    }
+
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return getTableRegionMap().get(tableName) != null &&
+                getTableRegionMap().get(tableName).size() == 5 &&
+                getTableServerRegionMap().get(tableName).size() == 1 &&
+                admin.getClusterStatus().getRegionsInTransition().size() < 1;
+      }
+    });
+
+    //verify that all region move to targetServer
+    Assert.assertEquals(5, 
getTableServerRegionMap().get(tableName).get(targetServer).size());
+
+    //move targetServer and table to newGroup
+    LOG.info("moving server and table to newGroup");
+    
rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()),
+            Sets.newHashSet(tableName), newGroup.getName());
+
+    //verify group change
+    Assert.assertEquals(newGroup.getName(),
+            rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
+
+    //verify servers' not exist in old group
+    Set<Address> defaultServers = 
rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers();
+    assertFalse(defaultServers.contains(targetServer.getAddress()));
+
+    //verify servers' exist in new group
+    Set<Address> newGroupServers = 
rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers();
+    assertTrue(newGroupServers.contains(targetServer.getAddress()));
+
+    //verify tables' not exist in old group
+    Set<TableName> defaultTables = 
rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables();
+    assertFalse(defaultTables.contains(tableName));
+
+    //verify tables' exist in new group
+    Set<TableName> newGroupTables = 
rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables();
+    assertTrue(newGroupTables.contains(tableName));
+
+    //verify that all region still assgin on targetServer
+    Assert.assertEquals(5, 
getTableServerRegionMap().get(tableName).get(targetServer).size());
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
----------------------------------------------------------------------
diff --git 
a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
 
b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
index 77b7cea..2e89110 100644
--- 
a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
+++ 
b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
@@ -103,6 +103,12 @@ public class VerifyingRSGroupAdminClient implements 
RSGroupAdmin {
     return wrapped.getRSGroupOfServer(hostPort);
   }
 
+  @Override
+  public void moveServersAndTables(Set<Address> servers, Set<TableName> 
tables, String targetGroup) throws IOException {
+    wrapped.moveServersAndTables(servers, tables, targetGroup);
+    verify();
+  }
+
   public void verify() throws IOException {
     Map<String, RSGroupInfo> groupMap = Maps.newHashMap();
     Set<RSGroupInfo> zList = Sets.newHashSet();

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
index 9d7a395..aab852c 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
@@ -1652,6 +1652,24 @@ public interface MasterObserver extends Coprocessor {
    * @param servers set of servers to move
    * @param targetGroup destination group
    */
+  default void preMoveServersAndTables(final 
ObserverContext<MasterCoprocessorEnvironment> ctx,
+                      Set<Address> servers, Set<TableName> tables, String 
targetGroup) throws IOException {}
+
+  /**
+   * Called after servers are moved to target region server group
+   * @param ctx the environment to interact with the framework and master
+   * @param servers set of servers to move
+   * @param targetGroup name of group
+   */
+  default void postMoveServersAndTables(final 
ObserverContext<MasterCoprocessorEnvironment> ctx,
+      Set<Address> servers, Set<TableName> tables, String targetGroup) throws 
IOException {}
+
+  /**
+   * Called before servers are moved to target region server group
+   * @param ctx the environment to interact with the framework and master
+   * @param servers set of servers to move
+   * @param targetGroup destination group
+   */
   default void preMoveServers(final 
ObserverContext<MasterCoprocessorEnvironment> ctx,
                       Set<Address> servers, String targetGroup) throws 
IOException {}
 

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
index 7f296f4..8a7a387 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
@@ -1511,6 +1511,32 @@ public class MasterCoprocessorHost
     return bypass;
   }
 
+  public void preMoveServersAndTables(final Set<Address> servers, final 
Set<TableName> tables, final String targetGroup)
+          throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+                       ObserverContext<MasterCoprocessorEnvironment> ctx) 
throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.preMoveServersAndTables(ctx, servers, tables, targetGroup);
+        }
+      }
+    });
+  }
+
+  public void postMoveServersAndTables(final Set<Address> servers, final 
Set<TableName> tables, final String targetGroup)
+          throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+                       ObserverContext<MasterCoprocessorEnvironment> ctx) 
throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.postMoveServersAndTables(ctx, servers, tables, targetGroup);
+        }
+      }
+    });
+  }
+
   public void preMoveServers(final Set<Address> servers, final String 
targetGroup)
       throws IOException {
     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
index 64ac900..a38d705 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
@@ -2668,6 +2668,12 @@ public class AccessController implements MasterObserver, 
RegionObserver, RegionS
   }
 
   @Override
+  public void 
preMoveServersAndTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+      Set<Address> servers, Set<TableName> tables, String targetGroup) throws 
IOException {
+    requirePermission(getActiveUser(ctx), "moveServersAndTables", 
Action.ADMIN);
+  }
+
+  @Override
   public void preMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
                              Set<Address> servers, String targetGroup) throws 
IOException {
     requirePermission(getActiveUser(ctx), "moveServers", Action.ADMIN);

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
index 029cdae..6b52e0c 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
@@ -1461,6 +1461,16 @@ public class TestMasterObserver {
     }
 
     @Override
+    public void 
preMoveServersAndTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+        Set<Address> servers, Set<TableName> tables, String targetGroup) 
throws IOException {
+    }
+
+    @Override
+    public void 
postMoveServersAndTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+        Set<Address> servers, Set<TableName> tables,String targetGroup) throws 
IOException {
+    }
+
+    @Override
     public void preMoveServers(ObserverContext<MasterCoprocessorEnvironment> 
ctx,
                                Set<Address> servers, String targetGroup) 
throws IOException {
     }

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb 
b/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb
index 6847f8b..3b71062 100644
--- a/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb
+++ b/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb
@@ -142,5 +142,19 @@ module Hbase
       res
     end
 
+    #--------------------------------------------------------------------------
+    # move server and table to a group
+    def move_servers_tables(dest, *args)
+      servers = java.util.HashSet.new
+      tables = java.util.HashSet.new;
+      args[0].each do |s|
+        servers.add(org.apache.hadoop.hbase.net.Address.fromString(s))
+      end
+      args[1].each do |t|
+        tables.add(org.apache.hadoop.hbase.TableName.valueOf(t))
+      end
+      @admin.moveServersAndTables(servers, tables, dest)
+    end
+
   end
 end

http://git-wip-us.apache.org/repos/asf/hbase/blob/7f0e6f1c/hbase-shell/src/main/ruby/shell.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell.rb 
b/hbase-shell/src/main/ruby/shell.rb
index b112b21..66480f9 100644
--- a/hbase-shell/src/main/ruby/shell.rb
+++ b/hbase-shell/src/main/ruby/shell.rb
@@ -470,6 +470,7 @@ Shell.load_command_group(
     balance_rsgroup
     move_servers_rsgroup
     move_tables_rsgroup
+    move_servers_tables_rsgroup
     get_server_rsgroup
     get_table_rsgroup
   ]

Reply via email to