http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManagerOnCluster.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManagerOnCluster.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManagerOnCluster.java
index 4843155..78b23c0 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManagerOnCluster.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManagerOnCluster.java
@@ -27,12 +27,16 @@ import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
@@ -610,7 +614,7 @@ public class TestAssignmentManagerOnCluster {
         desc.getTableName(), Bytes.toBytes("A"), Bytes.toBytes("Z"));
       MetaTableAccessor.addRegionToMeta(meta, hri);
 
-      MyLoadBalancer.controledRegion = hri.getEncodedName();
+      MyLoadBalancer.controledRegion = hri;
 
       HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
       AssignmentManager am = master.getAssignmentManager();
@@ -634,6 +638,105 @@ public class TestAssignmentManagerOnCluster {
   }
 
   /**
+   * This tests round-robin assignment failed due to no bulkplan
+   */
+  @Test (timeout=60000)
+  public void testRoundRobinAssignmentFailed() throws Exception {
+    TableName tableName = TableName.valueOf("testRoundRobinAssignmentFailed");
+    try {
+      HTableDescriptor desc = new HTableDescriptor(tableName);
+      desc.addFamily(new HColumnDescriptor(FAMILY));
+      admin.createTable(desc);
+
+      Table meta = admin.getConnection().getTable(TableName.META_TABLE_NAME);
+      HRegionInfo hri = new HRegionInfo(
+        desc.getTableName(), Bytes.toBytes("A"), Bytes.toBytes("Z"));
+      MetaTableAccessor.addRegionToMeta(meta, hri);
+
+      MyLoadBalancer.controledRegion = hri;
+
+      HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
+      AssignmentManager am = master.getAssignmentManager();
+      // round-robin assignment but balancer cannot find a plan
+      // assignment should fail
+      am.assign(Arrays.asList(hri));
+
+      // if bulk assignment cannot update region state to online
+      // or failed_open this waits until timeout
+      assertFalse(am.waitForAssignment(hri));
+      RegionState state = am.getRegionStates().getRegionState(hri);
+      assertEquals(RegionState.State.FAILED_OPEN, state.getState());
+      // Failed to open since no plan, so it's on no server
+      assertNull(state.getServerName());
+
+      // try again with valid plan
+      MyLoadBalancer.controledRegion = null;
+      am.assign(Arrays.asList(hri));
+      assertTrue(am.waitForAssignment(hri));
+
+      ServerName serverName = master.getAssignmentManager().
+        getRegionStates().getRegionServerOfRegion(hri);
+      TEST_UTIL.assertRegionOnServer(hri, serverName, 200);
+    } finally {
+      MyLoadBalancer.controledRegion = null;
+      TEST_UTIL.deleteTable(tableName);
+    }
+  }
+
+  /**
+   * This tests retain assignment failed due to no bulkplan
+   */
+  @Test (timeout=60000)
+  public void testRetainAssignmentFailed() throws Exception {
+    TableName tableName = TableName.valueOf("testRetainAssignmentFailed");
+    try {
+      HTableDescriptor desc = new HTableDescriptor(tableName);
+      desc.addFamily(new HColumnDescriptor(FAMILY));
+      admin.createTable(desc);
+
+      Table meta = 
TEST_UTIL.getConnection().getTable(TableName.META_TABLE_NAME);
+      HRegionInfo hri = new HRegionInfo(
+        desc.getTableName(), Bytes.toBytes("A"), Bytes.toBytes("Z"));
+      MetaTableAccessor.addRegionToMeta(meta, hri);
+
+      MyLoadBalancer.controledRegion = hri;
+
+      HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
+      AssignmentManager am = master.getAssignmentManager();
+
+      Map<HRegionInfo, ServerName> regions = new HashMap<HRegionInfo, 
ServerName>();
+      ServerName dest = 
TEST_UTIL.getHBaseCluster().getRegionServer(0).getServerName();
+      regions.put(hri, dest);
+      // retainAssignment but balancer cannot find a plan
+      // assignment should fail
+      am.assign(regions);
+
+      // if retain assignment cannot update region state to online
+      // or failed_open this waits until timeout
+      assertFalse(am.waitForAssignment(hri));
+      RegionState state = am.getRegionStates().getRegionState(hri);
+      assertEquals(RegionState.State.FAILED_OPEN, state.getState());
+      // Failed to open since no plan, so it's on no server
+      assertNull(state.getServerName());
+
+      // try retainAssigment again with valid plan
+      MyLoadBalancer.controledRegion = null;
+      am.assign(regions);
+      assertTrue(am.waitForAssignment(hri));
+
+      ServerName serverName = master.getAssignmentManager().
+        getRegionStates().getRegionServerOfRegion(hri);
+      TEST_UTIL.assertRegionOnServer(hri, serverName, 200);
+
+      // it retains on same server as specified
+      assertEquals(serverName, dest);
+    } finally {
+      MyLoadBalancer.controledRegion = null;
+      TEST_UTIL.deleteTable(tableName);
+    }
+  }
+
+  /**
    * This tests region open failure which is not recoverable
    */
   @Test (timeout=60000)
@@ -1264,7 +1367,7 @@ public class TestAssignmentManagerOnCluster {
 
   static class MyLoadBalancer extends StochasticLoadBalancer {
     // For this region, if specified, always assign to nowhere
-    static volatile String controledRegion = null;
+    static volatile HRegionInfo controledRegion = null;
 
     static volatile Integer countRegionServers = null;
     static AtomicInteger counter = new AtomicInteger(0);
@@ -1272,7 +1375,7 @@ public class TestAssignmentManagerOnCluster {
     @Override
     public ServerName randomAssignment(HRegionInfo regionInfo,
         List<ServerName> servers) {
-      if (regionInfo.getEncodedName().equals(controledRegion)) {
+      if (regionInfo.equals(controledRegion)) {
         return null;
       }
       return super.randomAssignment(regionInfo, servers);
@@ -1290,8 +1393,26 @@ public class TestAssignmentManagerOnCluster {
           return null;
         }
       }
+      if (regions.get(0).equals(controledRegion)) {
+        Map<ServerName, List<HRegionInfo>> m = Maps.newHashMap();
+        m.put(LoadBalancer.BOGUS_SERVER_NAME, regions);
+        return m;
+      }
       return super.roundRobinAssignment(regions, servers);
     }
+
+    @Override
+    public Map<ServerName, List<HRegionInfo>> retainAssignment(
+        Map<HRegionInfo, ServerName> regions, List<ServerName> servers) {
+      for (HRegionInfo hri : regions.keySet()) {
+        if (hri.equals(controledRegion)) {
+          Map<ServerName, List<HRegionInfo>> m = Maps.newHashMap();
+          m.put(LoadBalancer.BOGUS_SERVER_NAME, 
Lists.newArrayList(regions.keySet()));
+          return m;
+        }
+      }
+      return super.retainAssignment(regions, servers);
+    }
   }
 
   public static class MyMaster extends HMaster {

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java
index 84e2081..5bcc8d4 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java
@@ -491,6 +491,9 @@ public class TestCatalogJanitor {
         final long nonce) throws IOException {
       return -1;
     }
+    public LoadBalancer getLoadBalancer() {
+      return null;
+    }
 
     @Override
     public long disableTable(

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterStatusServlet.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterStatusServlet.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterStatusServlet.java
index 5e9b41c..94b6531 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterStatusServlet.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterStatusServlet.java
@@ -129,7 +129,7 @@ public class TestMasterStatusServlet {
     setupMockTables();
 
     new MasterStatusTmpl()
-      .setMetaLocation(ServerName.valueOf("metaserver:123,12345"))
+      .setMetaLocation(ServerName.valueOf("metaserver,123,12345"))
       .render(new StringWriter(), master);
   }
 
@@ -138,16 +138,16 @@ public class TestMasterStatusServlet {
     setupMockTables();
 
     List<ServerName> servers = Lists.newArrayList(
-        ServerName.valueOf("rootserver:123,12345"),
-        ServerName.valueOf("metaserver:123,12345"));
+        ServerName.valueOf("rootserver,123,12345"),
+        ServerName.valueOf("metaserver,123,12345"));
     Set<ServerName> deadServers = new HashSet<ServerName>(
         Lists.newArrayList(
-            ServerName.valueOf("badserver:123,12345"),
-            ServerName.valueOf("uglyserver:123,12345"))
+            ServerName.valueOf("badserver,123,12345"),
+            ServerName.valueOf("uglyserver,123,12345"))
     );
 
     new MasterStatusTmpl()
-      .setMetaLocation(ServerName.valueOf("metaserver:123,12345"))
+      .setMetaLocation(ServerName.valueOf("metaserver,123,12345"))
       .setServers(servers)
       .setDeadServers(deadServers)
       .render(new StringWriter(), master);

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizer.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizer.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizer.java
index fad2d33..81ecdcb 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizer.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizer.java
@@ -269,7 +269,7 @@ public class TestSimpleRegionNormalizer {
     masterRpcServices = Mockito.mock(MasterRpcServices.class, 
RETURNS_DEEP_STUBS);
 
     // for simplicity all regions are assumed to be on one server; doesn't 
matter to us
-    ServerName sn = ServerName.valueOf("localhost", -1, 1L);
+    ServerName sn = ServerName.valueOf("localhost", 0, 1L);
     when(masterServices.getAssignmentManager().getRegionStates().
       getRegionsOfTable(any(TableName.class))).thenReturn(hris);
     when(masterServices.getAssignmentManager().getRegionStates().

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
index eff0e68..5aedbf8 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
@@ -2965,4 +2965,79 @@ public class TestAccessController extends SecureTestUtil 
{
       TEST_UTIL.deleteTable(tname);
     }
   }
+
+  @Test
+  public void testMoveServers() throws Exception {
+    AccessTestAction action1 = new AccessTestAction() {
+      @Override
+      public Object run() throws Exception {
+        
ACCESS_CONTROLLER.preMoveServers(ObserverContext.createAndPrepare(CP_ENV, null),
+            null, null);
+        return null;
+      }
+    };
+
+    verifyAllowed(action1, SUPERUSER, USER_ADMIN);
+    verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, 
USER_OWNER);
+  }
+
+  @Test
+  public void testMoveTables() throws Exception {
+    AccessTestAction action1 = new AccessTestAction() {
+      @Override
+      public Object run() throws Exception {
+        
ACCESS_CONTROLLER.preMoveTables(ObserverContext.createAndPrepare(CP_ENV, null),
+            null, null);
+        return null;
+      }
+    };
+
+    verifyAllowed(action1, SUPERUSER, USER_ADMIN);
+    verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, 
USER_OWNER);
+  }
+
+  @Test
+  public void testAddGroup() throws Exception {
+    AccessTestAction action1 = new AccessTestAction() {
+      @Override
+      public Object run() throws Exception {
+        
ACCESS_CONTROLLER.preAddRSGroup(ObserverContext.createAndPrepare(CP_ENV, null),
+            null);
+        return null;
+      }
+    };
+
+    verifyAllowed(action1, SUPERUSER, USER_ADMIN);
+    verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, 
USER_OWNER);
+  }
+
+  @Test
+  public void testRemoveGroup() throws Exception {
+    AccessTestAction action1 = new AccessTestAction() {
+      @Override
+      public Object run() throws Exception {
+        
ACCESS_CONTROLLER.preRemoveRSGroup(ObserverContext.createAndPrepare(CP_ENV, 
null),
+            null);
+        return null;
+      }
+    };
+
+    verifyAllowed(action1, SUPERUSER, USER_ADMIN);
+    verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, 
USER_OWNER);
+  }
+
+  @Test
+  public void testBalanceGroup() throws Exception {
+    AccessTestAction action1 = new AccessTestAction() {
+      @Override
+      public Object run() throws Exception {
+        
ACCESS_CONTROLLER.preBalanceRSGroup(ObserverContext.createAndPrepare(CP_ENV, 
null),
+            null);
+        return null;
+      }
+    };
+
+    verifyAllowed(action1, SUPERUSER, USER_ADMIN);
+    verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, 
USER_OWNER);
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/pom.xml
----------------------------------------------------------------------
diff --git a/hbase-shell/pom.xml b/hbase-shell/pom.xml
index a2a1d0c..44b6095 100644
--- a/hbase-shell/pom.xml
+++ b/hbase-shell/pom.xml
@@ -254,6 +254,41 @@
     </dependency>
   </dependencies>
   <profiles>
+    <profile>
+      <id>rsgroup</id>
+      <activation>
+        <property>
+            <name>!skip-rsgroup</name>
+        </property>
+      </activation>
+      <dependencies>
+        <dependency>
+          <groupId>org.apache.hbase</groupId>
+          <artifactId>hbase-rsgroup</artifactId>
+        </dependency>
+      </dependencies>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>build-helper-maven-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>add-test-source</id>
+                <goals>
+                  <goal>add-test-source</goal>
+                </goals>
+                <configuration>
+                  <sources>
+                    <source>src/test/rsgroup</source>
+                  </sources>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
     <!-- Skip the tests in this module -->
     <profile>
       <id>skipShellTests</id>

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/hbase.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/hbase.rb 
b/hbase-shell/src/main/ruby/hbase.rb
index 0fa1649..1379437 100644
--- a/hbase-shell/src/main/ruby/hbase.rb
+++ b/hbase-shell/src/main/ruby/hbase.rb
@@ -107,6 +107,7 @@ require 'hbase/quotas'
 require 'hbase/replication_admin'
 require 'hbase/security'
 require 'hbase/visibility_labels'
+require 'hbase/rsgroup_admin'
 
 
 include HBaseQuotasConstants

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/hbase/hbase.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/hbase/hbase.rb 
b/hbase-shell/src/main/ruby/hbase/hbase.rb
index e0243ce..5234975 100644
--- a/hbase-shell/src/main/ruby/hbase/hbase.rb
+++ b/hbase-shell/src/main/ruby/hbase/hbase.rb
@@ -54,6 +54,10 @@ module Hbase
       ::Hbase::TaskMonitor.new(configuration)
     end
 
+    def rsgroup_admin()
+      ::Hbase::RSGroupAdmin.new(@connection)
+    end
+
     # Create new one each time
     def table(table, shell)
       ::Hbase::Table.new(@connection.getTable(table), shell)

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/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
new file mode 100644
index 0000000..0b72fd8
--- /dev/null
+++ b/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb
@@ -0,0 +1,164 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+include Java
+java_import org.apache.hadoop.hbase.util.Pair
+
+# Wrapper for org.apache.hadoop.hbase.group.GroupAdminClient
+# Which is an API to manage region server groups
+
+module Hbase
+  class RSGroupAdmin
+    include HBaseConstants
+
+    def initialize(connection)
+      @connection = connection
+      @admin = 
org.apache.hadoop.hbase.rsgroup.RSGroupAdminClient.new(connection)
+    end
+
+    def close
+      @admin.close
+    end
+
+    #--------------------------------------------------------------------------
+    # Returns a list of groups in hbase
+    def list_rs_groups
+      @admin.listRSGroups.map { |g| g.getName }
+    end
+
+    #--------------------------------------------------------------------------
+    # get a group's information
+    def get_rsgroup(group_name)
+      group = @admin.getRSGroupInfo(group_name)
+      if group.nil?
+        raise(ArgumentError, 'Group does not exist: ' + group_name)
+      end
+
+      res = {}
+      if block_given?
+        yield('Servers:')
+      end
+
+      servers = []
+      group.getServers.each do |v|
+        if block_given?
+          yield(v.toString)
+        else
+          servers << v.toString
+        end
+      end
+      res[:servers] = servers
+
+      tables = []
+      if block_given?
+        yield('Tables:')
+      end
+      group.getTables.each do |v|
+        if block_given?
+          yield(v.toString)
+        else
+          tables << v.toString
+        end
+      end
+      res[:tables] = tables
+
+      if !block_given?
+        res
+      else
+        nil
+      end
+    end
+
+    #--------------------------------------------------------------------------
+    # add a group
+    def add_rs_group(group_name)
+      @admin.addRSGroup(group_name)
+    end
+
+    #--------------------------------------------------------------------------
+    # remove a group
+    def remove_rs_group(group_name)
+      @admin.removeRSGroup(group_name)
+    end
+
+    #--------------------------------------------------------------------------
+    # balance a group
+    def balance_rs_group(group_name)
+      @admin.balanceRSGroup(group_name)
+    end
+
+    #--------------------------------------------------------------------------
+    # move server to a group
+    def move_servers(dest, *args)
+      servers = java.util.HashSet.new
+      args[0].each do |s|
+        servers.add(org.apache.hadoop.hbase.net.Address.fromString(s))
+      end
+      @admin.moveServers(servers, dest)
+    end
+
+    #--------------------------------------------------------------------------
+    # move server to a group
+    def move_tables(dest, *args)
+      tables = java.util.HashSet.new;
+      args[0].each do |s|
+        tables.add(org.apache.hadoop.hbase.TableName.valueOf(s))
+      end
+      @admin.moveTables(tables, dest)
+    end
+
+    #--------------------------------------------------------------------------
+    # get group of server
+    def get_rsgroup_of_server(server)
+      res = @admin.getRSGroupOfServer(
+          org.apache.hadoop.hbase.net.Address.fromString(server))
+      if res.nil?
+        raise(ArgumentError,'Server has no group: ' + server)
+      end
+      res
+    end
+
+    #--------------------------------------------------------------------------
+    # get group of table
+    def get_rsgroup_of_table(table)
+      res = @admin.getRSGroupInfoOfTable(
+          org.apache.hadoop.hbase.TableName.valueOf(table))
+      if res.nil?
+        raise(ArgumentError,'Table has no group: ' + table)
+      end
+      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/64328cae/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 433ed10..2eb872c 100644
--- a/hbase-shell/src/main/ruby/shell.rb
+++ b/hbase-shell/src/main/ruby/shell.rb
@@ -109,6 +109,10 @@ module Shell
       @hbase_quotas_admin ||= hbase.quotas_admin()
     end
 
+    def hbase_rsgroup_admin
+      @rsgroup_admin ||= hbase.rsgroup_admin()
+    end
+
     def export_commands(where)
       ::Shell.commands.keys.each do |cmd|
         # here where is the IRB namespace
@@ -444,3 +448,21 @@ Shell.load_command_group(
     set_visibility
   ]
 )
+
+Shell.load_command_group(
+  'rsgroup',
+  :full_name => 'RSGroups',
+  :comment => "NOTE: Above commands are only applicable if running with the 
Groups setup",
+  :commands => %w[
+    list_rsgroups
+    get_rsgroup
+    add_rsgroup
+    remove_rsgroup
+    balance_rsgroup
+    move_servers_rsgroup
+    move_tables_rsgroup
+    move_servers_tables_rsgroup
+    get_server_rsgroup
+    get_table_rsgroup
+  ]
+)

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands.rb 
b/hbase-shell/src/main/ruby/shell/commands.rb
index d580f5e..102a6e1 100644
--- a/hbase-shell/src/main/ruby/shell/commands.rb
+++ b/hbase-shell/src/main/ruby/shell/commands.rb
@@ -81,6 +81,10 @@ module Shell
         @shell.hbase_quotas_admin
       end
 
+      def rsgroup_admin
+        @shell.hbase_rsgroup_admin
+      end
+
       #----------------------------------------------------------------------
       # Creates formatter instance first time and then reuses it.
       def formatter

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/add_rsgroup.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/add_rsgroup.rb 
b/hbase-shell/src/main/ruby/shell/commands/add_rsgroup.rb
new file mode 100644
index 0000000..5a42e27
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/add_rsgroup.rb
@@ -0,0 +1,39 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class AddRsgroup < Command
+      def help
+        return <<-EOF
+Create a new region server group.
+
+Example:
+
+  hbase> add_rsgroup 'my_group'
+EOF
+      end
+
+      def command(group_name)
+        rsgroup_admin.add_rs_group(group_name)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/balance_rsgroup.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/balance_rsgroup.rb 
b/hbase-shell/src/main/ruby/shell/commands/balance_rsgroup.rb
new file mode 100644
index 0000000..bee139f
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/balance_rsgroup.rb
@@ -0,0 +1,37 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class BalanceRsgroup < Command
+      def help
+        return <<-EOF
+Balance a region server group
+
+  hbase> balance_rsgroup 'my_group'
+EOF
+      end
+
+      def command(group_name)
+        rsgroup_admin.balance_rs_group(group_name)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/get_rsgroup.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/get_rsgroup.rb 
b/hbase-shell/src/main/ruby/shell/commands/get_rsgroup.rb
new file mode 100644
index 0000000..ad8a0e3
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/get_rsgroup.rb
@@ -0,0 +1,43 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class GetRsgroup < Command
+      def help
+        return <<-EOF
+Get a region server group's information.
+
+Example:
+
+  hbase> get_rsgroup 'default'
+EOF
+      end
+
+      def command(group_name)
+        formatter.header(['GROUP INFORMATION'])
+        rsgroup_admin.get_rsgroup(group_name) do |s|
+          formatter.row([s])
+        end
+        formatter.footer()
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/get_server_rsgroup.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/get_server_rsgroup.rb 
b/hbase-shell/src/main/ruby/shell/commands/get_server_rsgroup.rb
new file mode 100644
index 0000000..9884cd1
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/get_server_rsgroup.rb
@@ -0,0 +1,39 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class GetServerRsgroup < Command
+      def help
+        return <<-EOF
+Get the group name the given region server is a member of.
+
+  hbase> get_server_rsgroup 'server1:port1'
+EOF
+      end
+
+      def command(server)
+        group_name = rsgroup_admin.get_rsgroup_of_server(server).getName
+        formatter.row([group_name])
+        formatter.footer(1)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/get_table_rsgroup.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/get_table_rsgroup.rb 
b/hbase-shell/src/main/ruby/shell/commands/get_table_rsgroup.rb
new file mode 100644
index 0000000..650cda5
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/get_table_rsgroup.rb
@@ -0,0 +1,40 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class GetTableRsgroup < Command
+      def help
+        return <<-EOF
+Get the group name the given table is a member of.
+
+  hbase> get_table_rsgroup 'myTable'
+EOF
+      end
+
+      def command(table)
+        group_name =
+            rsgroup_admin.get_rsgroup_of_table(table).getName
+        formatter.row([group_name])
+        formatter.footer(1)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/list_rsgroups.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/list_rsgroups.rb 
b/hbase-shell/src/main/ruby/shell/commands/list_rsgroups.rb
new file mode 100644
index 0000000..cabe84b
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/list_rsgroups.rb
@@ -0,0 +1,49 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class ListRsgroups < Command
+      def help
+        return <<-EOF
+List all region server groups. Optional regular expression parameter could
+be used to filter the output.
+
+Example:
+
+  hbase> list_rsgroups
+  hbase> list_rsgroups 'abc.*'
+EOF
+      end
+
+      def command(regex = '.*')
+        formatter.header(['GROUPS'])
+
+        regex = /#{regex}/ unless regex.is_a?(Regexp)
+        list = rsgroup_admin.list_rs_groups.grep(regex)
+        list.each do |group|
+          formatter.row([group])
+        end
+
+        formatter.footer(list.size)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/move_servers_rsgroup.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/move_servers_rsgroup.rb 
b/hbase-shell/src/main/ruby/shell/commands/move_servers_rsgroup.rb
new file mode 100644
index 0000000..1d36f8f
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/move_servers_rsgroup.rb
@@ -0,0 +1,37 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class MoveServersRsgroup < Command
+      def help
+        return <<-EOF
+Reassign a region server from one group to another.
+
+  hbase> move_servers_rsgroup 'dest',['server1:port','server2:port']
+EOF
+      end
+
+      def command(dest, servers)
+        rsgroup_admin.move_servers(dest, servers)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/move_servers_tables_rsgroup.rb
----------------------------------------------------------------------
diff --git 
a/hbase-shell/src/main/ruby/shell/commands/move_servers_tables_rsgroup.rb 
b/hbase-shell/src/main/ruby/shell/commands/move_servers_tables_rsgroup.rb
new file mode 100644
index 0000000..5337141
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/move_servers_tables_rsgroup.rb
@@ -0,0 +1,37 @@
+# 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.
+#
+
+module Shell
+  module Commands
+    class MoveServersTablesRsgroup < Command
+      def help
+        return <<-EOF
+Reassign RegionServers and Tables from one group to another.
+
+Example:
+
+  hbase> move_servers_tables_rsgroup 
'dest',['server1:port','server2:port'],['table1','table2']
+
+EOF
+      end
+
+      def command(dest, servers, tables)
+        rsgroup_admin.move_servers_tables(dest, servers, tables)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/move_tables_rsgroup.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/move_tables_rsgroup.rb 
b/hbase-shell/src/main/ruby/shell/commands/move_tables_rsgroup.rb
new file mode 100644
index 0000000..5d3a75c
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/move_tables_rsgroup.rb
@@ -0,0 +1,37 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class MoveTablesRsgroup < Command
+      def help
+        return <<-EOF
+Reassign tables from one group to another.
+
+  hbase> move_tables_rsgroup 'dest',['table1','table2']
+EOF
+      end
+
+      def command(dest, tables)
+        rsgroup_admin.move_tables(dest, tables)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/main/ruby/shell/commands/remove_rsgroup.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/remove_rsgroup.rb 
b/hbase-shell/src/main/ruby/shell/commands/remove_rsgroup.rb
new file mode 100644
index 0000000..9407732
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/remove_rsgroup.rb
@@ -0,0 +1,37 @@
+#
+# Copyright The Apache Software Foundation
+#
+# 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.
+#
+
+module Shell
+  module Commands
+    class RemoveRsgroup < Command
+      def help
+        return <<-EOF
+Remove a group.
+
+  hbase> remove_rsgroup 'my_group'
+EOF
+      end
+
+      def command(group_name)
+        rsgroup_admin.remove_rs_group(group_name)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/test/java/org/apache/hadoop/hbase/client/TestShell.java
----------------------------------------------------------------------
diff --git 
a/hbase-shell/src/test/java/org/apache/hadoop/hbase/client/TestShell.java 
b/hbase-shell/src/test/java/org/apache/hadoop/hbase/client/TestShell.java
index 976ba45..882b811 100644
--- a/hbase-shell/src/test/java/org/apache/hadoop/hbase/client/TestShell.java
+++ b/hbase-shell/src/test/java/org/apache/hadoop/hbase/client/TestShell.java
@@ -31,7 +31,7 @@ public class TestShell extends AbstractTestShell {
 
   @Test
   public void testRunShellTests() throws IOException {
-    System.setProperty("shell.test.exclude", "replication_admin_test.rb");
+    System.setProperty("shell.test.exclude", 
"replication_admin_test.rb,rsgroup_shell_test.rb");
     // Start all ruby tests
     jruby.runScriptlet(PathType.ABSOLUTE, "src/test/ruby/tests_runner.rb");
   }

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/test/rsgroup/org/apache/hadoop/hbase/client/rsgroup/TestShellRSGroups.java
----------------------------------------------------------------------
diff --git 
a/hbase-shell/src/test/rsgroup/org/apache/hadoop/hbase/client/rsgroup/TestShellRSGroups.java
 
b/hbase-shell/src/test/rsgroup/org/apache/hadoop/hbase/client/rsgroup/TestShellRSGroups.java
new file mode 100644
index 0000000..be23a59
--- /dev/null
+++ 
b/hbase-shell/src/test/rsgroup/org/apache/hadoop/hbase/client/rsgroup/TestShellRSGroups.java
@@ -0,0 +1,111 @@
+/**
+ *
+ * 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.client.rsgroup;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.rsgroup.RSGroupAdminEndpoint;
+import org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer;
+import org.apache.hadoop.hbase.security.access.SecureTestUtil;
+import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil;
+import org.apache.hadoop.hbase.testclassification.ClientTests;
+import org.apache.hadoop.hbase.testclassification.LargeTests;
+import org.jruby.embed.PathType;
+import org.jruby.embed.ScriptingContainer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+//Separate Shell test class for Groups
+//Since we need to use a different balancer and run more than 1 RS
+@Category({ClientTests.class, LargeTests.class})
+public class TestShellRSGroups {
+  final Log LOG = LogFactory.getLog(getClass());
+  private final static HBaseTestingUtility TEST_UTIL = new 
HBaseTestingUtility();
+  private final static ScriptingContainer jruby = new ScriptingContainer();
+  private static String basePath;
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+    basePath = System.getProperty("basedir");
+
+    // Start mini cluster
+    
TEST_UTIL.getConfiguration().setBoolean("hbase.online.schema.update.enable", 
true);
+    TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
+    TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250);
+    
TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 6);
+    
TEST_UTIL.getConfiguration().setBoolean(CoprocessorHost.ABORT_ON_ERROR_KEY, 
false);
+    TEST_UTIL.getConfiguration().setInt("hfile.format.version", 3);
+    TEST_UTIL.getConfiguration().setInt(HConstants.MASTER_INFO_PORT, -1);
+    TEST_UTIL.getConfiguration().setInt(HConstants.REGIONSERVER_INFO_PORT, -1);
+    // Security setup configuration
+    SecureTestUtil.enableSecurity(TEST_UTIL.getConfiguration());
+    VisibilityTestUtil.enableVisiblityLabels(TEST_UTIL.getConfiguration());
+
+    //Setup RegionServer Groups
+    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(
+        HConstants.ZOOKEEPER_USEMULTI,
+        true);
+
+    TEST_UTIL.startMiniCluster(1,4);
+
+    // Configure jruby runtime
+    List<String> loadPaths = new ArrayList();
+    loadPaths.add(basePath+"/src/main/ruby");
+    loadPaths.add(basePath+"/src/test/ruby");
+    jruby.getProvider().setLoadPaths(loadPaths);
+    jruby.put("$TEST_CLUSTER", TEST_UTIL);
+    System.setProperty("jruby.jit.logging.verbose", "true");
+    System.setProperty("jruby.jit.logging", "true");
+    System.setProperty("jruby.native.verbose", "true");
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+  @Test
+  public void testRunShellTests() throws IOException {
+    try {
+      // Start only GroupShellTest
+      System.setProperty("shell.test", "Hbase::RSGroupShellTest");
+      jruby.runScriptlet(PathType.ABSOLUTE,
+          basePath + "/src/test/ruby/tests_runner.rb");
+    } finally {
+      System.clearProperty("shell.test");
+    }
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/test/ruby/shell/rsgroup_shell_test.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/test/ruby/shell/rsgroup_shell_test.rb 
b/hbase-shell/src/test/ruby/shell/rsgroup_shell_test.rb
new file mode 100644
index 0000000..7542e6e
--- /dev/null
+++ b/hbase-shell/src/test/ruby/shell/rsgroup_shell_test.rb
@@ -0,0 +1,96 @@
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'hbase'
+require 'shell'
+
+module Hbase
+  class RSGroupShellTest < Test::Unit::TestCase
+    def setup
+      @hbase = ::Hbase::Hbase.new($TEST_CLUSTER.getConfiguration)
+      @shell = Shell::Shell.new(@hbase)
+      connection = $TEST_CLUSTER.getConnection
+      @rsgroup_admin =
+          org.apache.hadoop.hbase.rsgroup.RSGroupAdminClient.new(connection)
+    end
+
+    define_test 'Test Basic RSGroup Commands' do
+      group_name = 'test_group'
+      table_name = 'test_table'
+
+      @shell.command('create', table_name, 'f')
+
+      @shell.command('add_rsgroup', group_name)
+      assert_not_nil(@rsgroup_admin.getRSGroupInfo(group_name))
+
+      @shell.command('remove_rsgroup', group_name)
+      assert_nil(@rsgroup_admin.getRSGroupInfo(group_name))
+
+      @shell.command('add_rsgroup', group_name)
+      group = @rsgroup_admin.getRSGroupInfo(group_name)
+      assert_not_nil(group)
+      assert_equal(0, group.getServers.count)
+
+      address = 
@rsgroup_admin.getRSGroupInfo('default').getServers.iterator.next
+      @shell.command('get_rsgroup', 'default')
+      addressStr = address.toString
+      @shell.command('get_server_rsgroup', [addressStr])
+      @shell.command('move_servers_rsgroup',
+                     group_name,
+                     [addressStr])
+      assert_equal(1, 
@rsgroup_admin.getRSGroupInfo(group_name).getServers.count)
+      assert_equal(group_name, 
@rsgroup_admin.getRSGroupOfServer(address).getName)
+
+      @shell.command('move_tables_rsgroup',
+                     group_name,
+                     [table_name])
+      assert_equal(1, 
@rsgroup_admin.getRSGroupInfo(group_name).getTables.count)
+
+      count = 0
+      @hbase.rsgroup_admin.get_rsgroup(group_name) do |line|
+        case count
+        when 1
+          assert_equal(addressStr, line)
+        when 3
+          assert_equal(table_name, line)
+        end
+        count += 1
+      end
+      assert_equal(4, count)
+
+      assert_equal(2, @hbase.rsgroup_admin.list_rs_groups.count)
+
+      # just run it to verify jruby->java api binding
+      @hbase.rsgroup_admin.balance_rs_group(group_name)
+    end
+
+    # we test exceptions that could be thrown by the ruby wrappers
+    define_test 'Test bogus arguments' do
+      assert_raise(ArgumentError) do
+        @hbase.rsgroup_admin.get_rsgroup('foobar')
+      end
+      assert_raise(ArgumentError) do
+        @hbase.rsgroup_admin.get_rsgroup_of_server('foobar:123')
+      end
+      assert_raise(ArgumentError) do
+        @hbase.rsgroup_admin.get_rsgroup_of_table('foobar')
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/hbase-shell/src/test/ruby/test_helper.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/test/ruby/test_helper.rb 
b/hbase-shell/src/test/ruby/test_helper.rb
index 179ee5b..61015b7 100644
--- a/hbase-shell/src/test/ruby/test_helper.rb
+++ b/hbase-shell/src/test/ruby/test_helper.rb
@@ -78,6 +78,10 @@ module Hbase
       @shell.hbase_replication_admin
     end
 
+    def group_admin
+      @shell.hbase_group_admin
+    end
+
     def create_test_table(name)
       # Create the table if needed
       unless admin.exists?(name)

http://git-wip-us.apache.org/repos/asf/hbase/blob/64328cae/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 97acf43..0e1910d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1421,6 +1421,18 @@
         <scope>test</scope>
       </dependency>
       <dependency>
+        <artifactId>hbase-rsgroup</artifactId>
+        <groupId>org.apache.hbase</groupId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <artifactId>hbase-rsgroup</artifactId>
+        <groupId>org.apache.hbase</groupId>
+        <version>${project.version}</version>
+        <type>test-jar</type>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
         <artifactId>hbase-server</artifactId>
         <groupId>org.apache.hbase</groupId>
         <version>${project.version}</version>
@@ -1936,6 +1948,17 @@
   -->
   <profiles>
     <profile>
+      <id>rsgroup</id>
+      <activation>
+        <property>
+            <name>!skip-rsgroup</name>
+        </property>
+      </activation>
+      <modules>
+        <module>hbase-rsgroup</module>
+      </modules>
+    </profile>
+    <profile>
       <id>build-with-jdk8</id>
       <activation>
         <jdk>1.8</jdk>

Reply via email to