You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ap...@apache.org on 2017/07/19 00:44:46 UTC

[04/21] hbase git commit: HBASE-15631 Backport Regionserver Groups (HBASE-6721) to branch-1

http://git-wip-us.apache.org/repos/asf/hbase/blob/a0a7f6f4/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 51aeff8..c409ee9 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
@@ -2925,4 +2925,79 @@ public class TestAccessController extends SecureTestUtil {
     verifyDenied(replicateLogEntriesAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER,
       USER_GROUP_READ, USER_GROUP_ADMIN, USER_GROUP_CREATE);
   }
+
+  @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/a0a7f6f4/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/a0a7f6f4/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 88a6f04..2c0aecb 100644
--- a/hbase-shell/src/main/ruby/hbase.rb
+++ b/hbase-shell/src/main/ruby/hbase.rb
@@ -112,6 +112,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/a0a7f6f4/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..42a0b62 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(formatter)
+      ::Hbase::RSGroupAdmin.new(@connection, formatter)
+    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/a0a7f6f4/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..51a4efb
--- /dev/null
+++ b/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb
@@ -0,0 +1,150 @@
+#
+# 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, formatter)
+      @admin = org.apache.hadoop.hbase.rsgroup.RSGroupAdmin.newClient(connection)
+      @formatter = formatter
+    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(com.google.common.net.HostAndPort.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(
+          com.google.common.net.HostAndPort.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
+
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/a0a7f6f4/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 99adf73..49d0929 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(formatter)
+    end
+
     def export_commands(where)
       ::Shell.commands.keys.each do |cmd|
         # here where is the IRB namespace
@@ -442,3 +446,20 @@ 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_rsgroup_servers
+    move_rsgroup_tables
+    get_server_rsgroup
+    get_table_rsgroup
+  ]
+)

http://git-wip-us.apache.org/repos/asf/hbase/blob/a0a7f6f4/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/a0a7f6f4/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/a0a7f6f4/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/a0a7f6f4/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..ce4be71
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/get_rsgroup.rb
@@ -0,0 +1,44 @@
+#
+# 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)
+        now = Time.now
+        formatter.header(['GROUP INFORMATION'])
+        rsgroup_admin.get_rsgroup(group_name) do |s|
+          formatter.row([s])
+        end
+        formatter.footer(now)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/a0a7f6f4/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..322f6bb
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/get_server_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 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)
+        now = Time.now
+        group_name = rsgroup_admin.getGroupOfServer(server).getName
+        formatter.row([group_name])
+        formatter.footer(now, 1)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/a0a7f6f4/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..d15cffa
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/get_table_rsgroup.rb
@@ -0,0 +1,41 @@
+#
+# 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)
+        now = Time.now
+        group_name =
+            rsgroup_admin.get_rsgroup_of_table(table).getName
+        formatter.row([group_name])
+        formatter.footer(now, 1)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/a0a7f6f4/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..6ea1d45
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/list_rsgroups.rb
@@ -0,0 +1,50 @@
+#
+# 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 = '.*')
+        now = Time.now
+        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(now, list.size)
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/a0a7f6f4/hbase-shell/src/main/ruby/shell/commands/move_rsgroup_servers.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/move_rsgroup_servers.rb b/hbase-shell/src/main/ruby/shell/commands/move_rsgroup_servers.rb
new file mode 100644
index 0000000..6f48400
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/move_rsgroup_servers.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 MoveRsgroupServers < Command
+      def help
+        return <<-EOF
+Reassign a region server from one group to another.
+
+  hbase> move_rsgroup_servers '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/a0a7f6f4/hbase-shell/src/main/ruby/shell/commands/move_rsgroup_tables.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands/move_rsgroup_tables.rb b/hbase-shell/src/main/ruby/shell/commands/move_rsgroup_tables.rb
new file mode 100644
index 0000000..3c1555a
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/move_rsgroup_tables.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 MoveRsgroupTables < Command
+      def help
+        return <<-EOF
+Reassign tables from one group to another.
+
+  hbase> move_rsgroup_tables '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/a0a7f6f4/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/a0a7f6f4/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/a0a7f6f4/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/a0a7f6f4/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..d892775
--- /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'
+require 'shell/formatter'
+
+module Hbase
+  class RSGroupShellTest < Test::Unit::TestCase
+    def setup
+      @formatter = ::Shell::Formatter::Console.new
+      @hbase = ::Hbase::Hbase.new($TEST_CLUSTER.getConfiguration)
+      @shell = Shell::Shell.new(@hbase, @formatter)
+      connection = $TEST_CLUSTER.getConnection
+      @rsgroup_admin =
+          org.apache.hadoop.hbase.rsgroup.RSGroupAdmin.newClient(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)
+
+      hostport =
+          @rsgroup_admin.getRSGroupInfo('default').getServers.iterator.next.toString
+      @shell.command('move_rsgroup_servers',
+                     group_name,
+                     [hostport])
+      assert_equal(1, @rsgroup_admin.getRSGroupInfo(group_name).getServers.count)
+
+      @shell.command('move_rsgroup_tables',
+                     group_name,
+                     [table_name])
+      assert_equal(1, @rsgroup_admin.getRSGroupInfo(group_name).getTables.count)
+
+      count = 0
+      @hbase.rsgroup_admin(@formatter).get_rsgroup(group_name) do |line|
+        case count
+        when 1
+          assert_equal(hostport, line)
+        when 3
+          assert_equal(table_name, line)
+        end
+        count += 1
+      end
+      assert_equal(4, count)
+
+      assert_equal(2,
+                   @hbase.rsgroup_admin(@formatter).list_rs_groups.count)
+
+      # just run it to verify jruby->java api binding
+      @hbase.rsgroup_admin(@formatter).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(@formatter).get_rsgroup('foobar')
+      end
+      assert_raise(ArgumentError) do
+        @hbase.rsgroup_admin(@formatter).get_rsgroup_of_server('foobar:123')
+      end
+      assert_raise(ArgumentError) do
+        @hbase.rsgroup_admin(@formatter).get_rsgroup_of_table('foobar')
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/a0a7f6f4/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..fb70ce8 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(_formatter)
+      @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/a0a7f6f4/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index c11b191..19d4075 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1408,6 +1408,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>
@@ -1923,6 +1935,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>