You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by en...@apache.org on 2016/03/15 02:29:13 UTC

[1/7] hbase git commit: HBASE-6721 RegionServer Group based Assignment (Francis Liu)

Repository: hbase
Updated Branches:
  refs/heads/master 122e6f579 -> ca816f078


http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/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 c096a9f..66ea050 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
@@ -2738,4 +2738,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/ca816f07/hbase-shell/pom.xml
----------------------------------------------------------------------
diff --git a/hbase-shell/pom.xml b/hbase-shell/pom.xml
index 7c3754e..cf63e94 100644
--- a/hbase-shell/pom.xml
+++ b/hbase-shell/pom.xml
@@ -253,6 +253,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/ca816f07/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 aca1006..21f88f9 100644
--- a/hbase-shell/src/main/ruby/hbase.rb
+++ b/hbase-shell/src/main/ruby/hbase.rb
@@ -102,5 +102,6 @@ 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/ca816f07/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 135e1d5..785f8bc 100644
--- a/hbase-shell/src/main/ruby/hbase/hbase.rb
+++ b/hbase-shell/src/main/ruby/hbase/hbase.rb
@@ -47,6 +47,10 @@ module Hbase
       ::Hbase::Admin.new(@connection.getAdmin, formatter)
     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/ca816f07/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/ca816f07/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 4144b91..ed7dd76 100644
--- a/hbase-shell/src/main/ruby/shell.rb
+++ b/hbase-shell/src/main/ruby/shell.rb
@@ -107,6 +107,10 @@ module Shell
       @hbase_quotas_admin ||= hbase.quotas_admin(formatter)
     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
@@ -429,3 +433,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/ca816f07/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 4ad04cd..0c37c4f 100644
--- a/hbase-shell/src/main/ruby/shell/commands.rb
+++ b/hbase-shell/src/main/ruby/shell/commands.rb
@@ -74,6 +74,10 @@ module Shell
         @shell.hbase_quotas_admin
       end
 
+      def rsgroup_admin
+        @shell.hbase_rsgroup_admin
+      end
+
       #----------------------------------------------------------------------
 
       def formatter

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/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/ca816f07/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/ca816f07/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..6772aa1
--- /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'])
+        group_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/ca816f07/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/ca816f07/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/ca816f07/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/ca816f07/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/ca816f07/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/ca816f07/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/ca816f07/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/ca816f07/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/ca816f07/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/ca816f07/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 67bbb14..e75cd36 100644
--- a/hbase-shell/src/test/ruby/test_helper.rb
+++ b/hbase-shell/src/test/ruby/test_helper.rb
@@ -72,6 +72,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/ca816f07/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index b3fa787..cc3df91 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1353,6 +1353,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>
@@ -1800,6 +1812,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>


[6/7] hbase git commit: HBASE-6721 RegionServer Group based Assignment (Francis Liu)

Posted by en...@apache.org.
http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RSGroupAdminProtos.java
----------------------------------------------------------------------
diff --git a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RSGroupAdminProtos.java b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RSGroupAdminProtos.java
new file mode 100644
index 0000000..3d1f4bd
--- /dev/null
+++ b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RSGroupAdminProtos.java
@@ -0,0 +1,11855 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: RSGroupAdmin.proto
+
+package org.apache.hadoop.hbase.protobuf.generated;
+
+public final class RSGroupAdminProtos {
+  private RSGroupAdminProtos() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+  }
+  public interface ListTablesOfRSGroupRequestOrBuilder
+      extends com.google.protobuf.MessageOrBuilder {
+
+    // required string r_s_group_name = 1;
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    boolean hasRSGroupName();
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    java.lang.String getRSGroupName();
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    com.google.protobuf.ByteString
+        getRSGroupNameBytes();
+  }
+  /**
+   * Protobuf type {@code hbase.pb.ListTablesOfRSGroupRequest}
+   */
+  public static final class ListTablesOfRSGroupRequest extends
+      com.google.protobuf.GeneratedMessage
+      implements ListTablesOfRSGroupRequestOrBuilder {
+    // Use ListTablesOfRSGroupRequest.newBuilder() to construct.
+    private ListTablesOfRSGroupRequest(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
+      super(builder);
+      this.unknownFields = builder.getUnknownFields();
+    }
+    private ListTablesOfRSGroupRequest(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+    private static final ListTablesOfRSGroupRequest defaultInstance;
+    public static ListTablesOfRSGroupRequest getDefaultInstance() {
+      return defaultInstance;
+    }
+
+    public ListTablesOfRSGroupRequest getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+
+    private final com.google.protobuf.UnknownFieldSet unknownFields;
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+        getUnknownFields() {
+      return this.unknownFields;
+    }
+    private ListTablesOfRSGroupRequest(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      initFields();
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!parseUnknownField(input, unknownFields,
+                                     extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+            case 10: {
+              bitField0_ |= 0x00000001;
+              rSGroupName_ = input.readBytes();
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e.getMessage()).setUnfinishedMessage(this);
+      } finally {
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupRequest_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest.Builder.class);
+    }
+
+    public static com.google.protobuf.Parser<ListTablesOfRSGroupRequest> PARSER =
+        new com.google.protobuf.AbstractParser<ListTablesOfRSGroupRequest>() {
+      public ListTablesOfRSGroupRequest parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new ListTablesOfRSGroupRequest(input, extensionRegistry);
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<ListTablesOfRSGroupRequest> getParserForType() {
+      return PARSER;
+    }
+
+    private int bitField0_;
+    // required string r_s_group_name = 1;
+    public static final int R_S_GROUP_NAME_FIELD_NUMBER = 1;
+    private java.lang.Object rSGroupName_;
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    public boolean hasRSGroupName() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    public java.lang.String getRSGroupName() {
+      java.lang.Object ref = rSGroupName_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        if (bs.isValidUtf8()) {
+          rSGroupName_ = s;
+        }
+        return s;
+      }
+    }
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    public com.google.protobuf.ByteString
+        getRSGroupNameBytes() {
+      java.lang.Object ref = rSGroupName_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        rSGroupName_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+
+    private void initFields() {
+      rSGroupName_ = "";
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+
+      if (!hasRSGroupName()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeBytes(1, getRSGroupNameBytes());
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(1, getRSGroupNameBytes());
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSerializedSize = size;
+      return size;
+    }
+
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest)) {
+        return super.equals(obj);
+      }
+      org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest other = (org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest) obj;
+
+      boolean result = true;
+      result = result && (hasRSGroupName() == other.hasRSGroupName());
+      if (hasRSGroupName()) {
+        result = result && getRSGroupName()
+            .equals(other.getRSGroupName());
+      }
+      result = result &&
+          getUnknownFields().equals(other.getUnknownFields());
+      return result;
+    }
+
+    private int memoizedHashCode = 0;
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (hasRSGroupName()) {
+        hash = (37 * hash) + R_S_GROUP_NAME_FIELD_NUMBER;
+        hash = (53 * hash) + getRSGroupName().hashCode();
+      }
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code hbase.pb.ListTablesOfRSGroupRequest}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder>
+       implements org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequestOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupRequest_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupRequest_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest.Builder.class);
+      }
+
+      // Construct using org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+        }
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+
+      public Builder clear() {
+        super.clear();
+        rSGroupName_ = "";
+        bitField0_ = (bitField0_ & ~0x00000001);
+        return this;
+      }
+
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupRequest_descriptor;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest getDefaultInstanceForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest.getDefaultInstance();
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest build() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest buildPartial() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest result = new org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.rSGroupName_ = rSGroupName_;
+        result.bitField0_ = to_bitField0_;
+        onBuilt();
+        return result;
+      }
+
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest) {
+          return mergeFrom((org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest other) {
+        if (other == org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest.getDefaultInstance()) return this;
+        if (other.hasRSGroupName()) {
+          bitField0_ |= 0x00000001;
+          rSGroupName_ = other.rSGroupName_;
+          onChanged();
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        return this;
+      }
+
+      public final boolean isInitialized() {
+        if (!hasRSGroupName()) {
+          
+          return false;
+        }
+        return true;
+      }
+
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupRequest) e.getUnfinishedMessage();
+          throw e;
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      // required string r_s_group_name = 1;
+      private java.lang.Object rSGroupName_ = "";
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public boolean hasRSGroupName() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public java.lang.String getRSGroupName() {
+        java.lang.Object ref = rSGroupName_;
+        if (!(ref instanceof java.lang.String)) {
+          java.lang.String s = ((com.google.protobuf.ByteString) ref)
+              .toStringUtf8();
+          rSGroupName_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
+        }
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public com.google.protobuf.ByteString
+          getRSGroupNameBytes() {
+        java.lang.Object ref = rSGroupName_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          rSGroupName_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public Builder setRSGroupName(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+        rSGroupName_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public Builder clearRSGroupName() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        rSGroupName_ = getDefaultInstance().getRSGroupName();
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public Builder setRSGroupNameBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+        rSGroupName_ = value;
+        onChanged();
+        return this;
+      }
+
+      // @@protoc_insertion_point(builder_scope:hbase.pb.ListTablesOfRSGroupRequest)
+    }
+
+    static {
+      defaultInstance = new ListTablesOfRSGroupRequest(true);
+      defaultInstance.initFields();
+    }
+
+    // @@protoc_insertion_point(class_scope:hbase.pb.ListTablesOfRSGroupRequest)
+  }
+
+  public interface ListTablesOfRSGroupResponseOrBuilder
+      extends com.google.protobuf.MessageOrBuilder {
+
+    // repeated .hbase.pb.TableName table_name = 1;
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> 
+        getTableNameList();
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName getTableName(int index);
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    int getTableNameCount();
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    java.util.List<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> 
+        getTableNameOrBuilderList();
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder getTableNameOrBuilder(
+        int index);
+  }
+  /**
+   * Protobuf type {@code hbase.pb.ListTablesOfRSGroupResponse}
+   */
+  public static final class ListTablesOfRSGroupResponse extends
+      com.google.protobuf.GeneratedMessage
+      implements ListTablesOfRSGroupResponseOrBuilder {
+    // Use ListTablesOfRSGroupResponse.newBuilder() to construct.
+    private ListTablesOfRSGroupResponse(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
+      super(builder);
+      this.unknownFields = builder.getUnknownFields();
+    }
+    private ListTablesOfRSGroupResponse(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+    private static final ListTablesOfRSGroupResponse defaultInstance;
+    public static ListTablesOfRSGroupResponse getDefaultInstance() {
+      return defaultInstance;
+    }
+
+    public ListTablesOfRSGroupResponse getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+
+    private final com.google.protobuf.UnknownFieldSet unknownFields;
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+        getUnknownFields() {
+      return this.unknownFields;
+    }
+    private ListTablesOfRSGroupResponse(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      initFields();
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!parseUnknownField(input, unknownFields,
+                                     extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+            case 10: {
+              if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
+                tableName_ = new java.util.ArrayList<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName>();
+                mutable_bitField0_ |= 0x00000001;
+              }
+              tableName_.add(input.readMessage(org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.PARSER, extensionRegistry));
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e.getMessage()).setUnfinishedMessage(this);
+      } finally {
+        if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
+          tableName_ = java.util.Collections.unmodifiableList(tableName_);
+        }
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupResponse_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse.Builder.class);
+    }
+
+    public static com.google.protobuf.Parser<ListTablesOfRSGroupResponse> PARSER =
+        new com.google.protobuf.AbstractParser<ListTablesOfRSGroupResponse>() {
+      public ListTablesOfRSGroupResponse parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new ListTablesOfRSGroupResponse(input, extensionRegistry);
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<ListTablesOfRSGroupResponse> getParserForType() {
+      return PARSER;
+    }
+
+    // repeated .hbase.pb.TableName table_name = 1;
+    public static final int TABLE_NAME_FIELD_NUMBER = 1;
+    private java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> tableName_;
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    public java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> getTableNameList() {
+      return tableName_;
+    }
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    public java.util.List<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> 
+        getTableNameOrBuilderList() {
+      return tableName_;
+    }
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    public int getTableNameCount() {
+      return tableName_.size();
+    }
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName getTableName(int index) {
+      return tableName_.get(index);
+    }
+    /**
+     * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+     */
+    public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder getTableNameOrBuilder(
+        int index) {
+      return tableName_.get(index);
+    }
+
+    private void initFields() {
+      tableName_ = java.util.Collections.emptyList();
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+
+      for (int i = 0; i < getTableNameCount(); i++) {
+        if (!getTableName(i).isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      for (int i = 0; i < tableName_.size(); i++) {
+        output.writeMessage(1, tableName_.get(i));
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      for (int i = 0; i < tableName_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, tableName_.get(i));
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSerializedSize = size;
+      return size;
+    }
+
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse)) {
+        return super.equals(obj);
+      }
+      org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse other = (org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse) obj;
+
+      boolean result = true;
+      result = result && getTableNameList()
+          .equals(other.getTableNameList());
+      result = result &&
+          getUnknownFields().equals(other.getUnknownFields());
+      return result;
+    }
+
+    private int memoizedHashCode = 0;
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (getTableNameCount() > 0) {
+        hash = (37 * hash) + TABLE_NAME_FIELD_NUMBER;
+        hash = (53 * hash) + getTableNameList().hashCode();
+      }
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code hbase.pb.ListTablesOfRSGroupResponse}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder>
+       implements org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponseOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupResponse_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupResponse_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse.Builder.class);
+      }
+
+      // Construct using org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+          getTableNameFieldBuilder();
+        }
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+
+      public Builder clear() {
+        super.clear();
+        if (tableNameBuilder_ == null) {
+          tableName_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+        } else {
+          tableNameBuilder_.clear();
+        }
+        return this;
+      }
+
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_ListTablesOfRSGroupResponse_descriptor;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse getDefaultInstanceForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse.getDefaultInstance();
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse build() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse buildPartial() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse result = new org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse(this);
+        int from_bitField0_ = bitField0_;
+        if (tableNameBuilder_ == null) {
+          if (((bitField0_ & 0x00000001) == 0x00000001)) {
+            tableName_ = java.util.Collections.unmodifiableList(tableName_);
+            bitField0_ = (bitField0_ & ~0x00000001);
+          }
+          result.tableName_ = tableName_;
+        } else {
+          result.tableName_ = tableNameBuilder_.build();
+        }
+        onBuilt();
+        return result;
+      }
+
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse) {
+          return mergeFrom((org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse other) {
+        if (other == org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse.getDefaultInstance()) return this;
+        if (tableNameBuilder_ == null) {
+          if (!other.tableName_.isEmpty()) {
+            if (tableName_.isEmpty()) {
+              tableName_ = other.tableName_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+            } else {
+              ensureTableNameIsMutable();
+              tableName_.addAll(other.tableName_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.tableName_.isEmpty()) {
+            if (tableNameBuilder_.isEmpty()) {
+              tableNameBuilder_.dispose();
+              tableNameBuilder_ = null;
+              tableName_ = other.tableName_;
+              bitField0_ = (bitField0_ & ~0x00000001);
+              tableNameBuilder_ = 
+                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
+                   getTableNameFieldBuilder() : null;
+            } else {
+              tableNameBuilder_.addAllMessages(other.tableName_);
+            }
+          }
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        return this;
+      }
+
+      public final boolean isInitialized() {
+        for (int i = 0; i < getTableNameCount(); i++) {
+          if (!getTableName(i).isInitialized()) {
+            
+            return false;
+          }
+        }
+        return true;
+      }
+
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListTablesOfRSGroupResponse) e.getUnfinishedMessage();
+          throw e;
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      // repeated .hbase.pb.TableName table_name = 1;
+      private java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> tableName_ =
+        java.util.Collections.emptyList();
+      private void ensureTableNameIsMutable() {
+        if (!((bitField0_ & 0x00000001) == 0x00000001)) {
+          tableName_ = new java.util.ArrayList<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName>(tableName_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> tableNameBuilder_;
+
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> getTableNameList() {
+        if (tableNameBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(tableName_);
+        } else {
+          return tableNameBuilder_.getMessageList();
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public int getTableNameCount() {
+        if (tableNameBuilder_ == null) {
+          return tableName_.size();
+        } else {
+          return tableNameBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName getTableName(int index) {
+        if (tableNameBuilder_ == null) {
+          return tableName_.get(index);
+        } else {
+          return tableNameBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public Builder setTableName(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName value) {
+        if (tableNameBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureTableNameIsMutable();
+          tableName_.set(index, value);
+          onChanged();
+        } else {
+          tableNameBuilder_.setMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public Builder setTableName(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder builderForValue) {
+        if (tableNameBuilder_ == null) {
+          ensureTableNameIsMutable();
+          tableName_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          tableNameBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public Builder addTableName(org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName value) {
+        if (tableNameBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureTableNameIsMutable();
+          tableName_.add(value);
+          onChanged();
+        } else {
+          tableNameBuilder_.addMessage(value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public Builder addTableName(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName value) {
+        if (tableNameBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureTableNameIsMutable();
+          tableName_.add(index, value);
+          onChanged();
+        } else {
+          tableNameBuilder_.addMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public Builder addTableName(
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder builderForValue) {
+        if (tableNameBuilder_ == null) {
+          ensureTableNameIsMutable();
+          tableName_.add(builderForValue.build());
+          onChanged();
+        } else {
+          tableNameBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public Builder addTableName(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder builderForValue) {
+        if (tableNameBuilder_ == null) {
+          ensureTableNameIsMutable();
+          tableName_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          tableNameBuilder_.addMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public Builder addAllTableName(
+          java.lang.Iterable<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> values) {
+        if (tableNameBuilder_ == null) {
+          ensureTableNameIsMutable();
+          super.addAll(values, tableName_);
+          onChanged();
+        } else {
+          tableNameBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public Builder clearTableName() {
+        if (tableNameBuilder_ == null) {
+          tableName_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000001);
+          onChanged();
+        } else {
+          tableNameBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public Builder removeTableName(int index) {
+        if (tableNameBuilder_ == null) {
+          ensureTableNameIsMutable();
+          tableName_.remove(index);
+          onChanged();
+        } else {
+          tableNameBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder getTableNameBuilder(
+          int index) {
+        return getTableNameFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder getTableNameOrBuilder(
+          int index) {
+        if (tableNameBuilder_ == null) {
+          return tableName_.get(index);  } else {
+          return tableNameBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public java.util.List<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> 
+           getTableNameOrBuilderList() {
+        if (tableNameBuilder_ != null) {
+          return tableNameBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(tableName_);
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder addTableNameBuilder() {
+        return getTableNameFieldBuilder().addBuilder(
+            org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder addTableNameBuilder(
+          int index) {
+        return getTableNameFieldBuilder().addBuilder(
+            index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName table_name = 1;</code>
+       */
+      public java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder> 
+           getTableNameBuilderList() {
+        return getTableNameFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> 
+          getTableNameFieldBuilder() {
+        if (tableNameBuilder_ == null) {
+          tableNameBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
+              org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder>(
+                  tableName_,
+                  ((bitField0_ & 0x00000001) == 0x00000001),
+                  getParentForChildren(),
+                  isClean());
+          tableName_ = null;
+        }
+        return tableNameBuilder_;
+      }
+
+      // @@protoc_insertion_point(builder_scope:hbase.pb.ListTablesOfRSGroupResponse)
+    }
+
+    static {
+      defaultInstance = new ListTablesOfRSGroupResponse(true);
+      defaultInstance.initFields();
+    }
+
+    // @@protoc_insertion_point(class_scope:hbase.pb.ListTablesOfRSGroupResponse)
+  }
+
+  public interface GetRSGroupInfoRequestOrBuilder
+      extends com.google.protobuf.MessageOrBuilder {
+
+    // required string r_s_group_name = 1;
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    boolean hasRSGroupName();
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    java.lang.String getRSGroupName();
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    com.google.protobuf.ByteString
+        getRSGroupNameBytes();
+  }
+  /**
+   * Protobuf type {@code hbase.pb.GetRSGroupInfoRequest}
+   */
+  public static final class GetRSGroupInfoRequest extends
+      com.google.protobuf.GeneratedMessage
+      implements GetRSGroupInfoRequestOrBuilder {
+    // Use GetRSGroupInfoRequest.newBuilder() to construct.
+    private GetRSGroupInfoRequest(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
+      super(builder);
+      this.unknownFields = builder.getUnknownFields();
+    }
+    private GetRSGroupInfoRequest(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+    private static final GetRSGroupInfoRequest defaultInstance;
+    public static GetRSGroupInfoRequest getDefaultInstance() {
+      return defaultInstance;
+    }
+
+    public GetRSGroupInfoRequest getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+
+    private final com.google.protobuf.UnknownFieldSet unknownFields;
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+        getUnknownFields() {
+      return this.unknownFields;
+    }
+    private GetRSGroupInfoRequest(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      initFields();
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!parseUnknownField(input, unknownFields,
+                                     extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+            case 10: {
+              bitField0_ |= 0x00000001;
+              rSGroupName_ = input.readBytes();
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e.getMessage()).setUnfinishedMessage(this);
+      } finally {
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoRequest_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest.Builder.class);
+    }
+
+    public static com.google.protobuf.Parser<GetRSGroupInfoRequest> PARSER =
+        new com.google.protobuf.AbstractParser<GetRSGroupInfoRequest>() {
+      public GetRSGroupInfoRequest parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new GetRSGroupInfoRequest(input, extensionRegistry);
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<GetRSGroupInfoRequest> getParserForType() {
+      return PARSER;
+    }
+
+    private int bitField0_;
+    // required string r_s_group_name = 1;
+    public static final int R_S_GROUP_NAME_FIELD_NUMBER = 1;
+    private java.lang.Object rSGroupName_;
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    public boolean hasRSGroupName() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    public java.lang.String getRSGroupName() {
+      java.lang.Object ref = rSGroupName_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        if (bs.isValidUtf8()) {
+          rSGroupName_ = s;
+        }
+        return s;
+      }
+    }
+    /**
+     * <code>required string r_s_group_name = 1;</code>
+     */
+    public com.google.protobuf.ByteString
+        getRSGroupNameBytes() {
+      java.lang.Object ref = rSGroupName_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        rSGroupName_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+
+    private void initFields() {
+      rSGroupName_ = "";
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+
+      if (!hasRSGroupName()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeBytes(1, getRSGroupNameBytes());
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(1, getRSGroupNameBytes());
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSerializedSize = size;
+      return size;
+    }
+
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest)) {
+        return super.equals(obj);
+      }
+      org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest other = (org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest) obj;
+
+      boolean result = true;
+      result = result && (hasRSGroupName() == other.hasRSGroupName());
+      if (hasRSGroupName()) {
+        result = result && getRSGroupName()
+            .equals(other.getRSGroupName());
+      }
+      result = result &&
+          getUnknownFields().equals(other.getUnknownFields());
+      return result;
+    }
+
+    private int memoizedHashCode = 0;
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (hasRSGroupName()) {
+        hash = (37 * hash) + R_S_GROUP_NAME_FIELD_NUMBER;
+        hash = (53 * hash) + getRSGroupName().hashCode();
+      }
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code hbase.pb.GetRSGroupInfoRequest}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder>
+       implements org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequestOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoRequest_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoRequest_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest.Builder.class);
+      }
+
+      // Construct using org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+        }
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+
+      public Builder clear() {
+        super.clear();
+        rSGroupName_ = "";
+        bitField0_ = (bitField0_ & ~0x00000001);
+        return this;
+      }
+
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoRequest_descriptor;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest getDefaultInstanceForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest.getDefaultInstance();
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest build() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest buildPartial() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest result = new org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.rSGroupName_ = rSGroupName_;
+        result.bitField0_ = to_bitField0_;
+        onBuilt();
+        return result;
+      }
+
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest) {
+          return mergeFrom((org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest other) {
+        if (other == org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest.getDefaultInstance()) return this;
+        if (other.hasRSGroupName()) {
+          bitField0_ |= 0x00000001;
+          rSGroupName_ = other.rSGroupName_;
+          onChanged();
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        return this;
+      }
+
+      public final boolean isInitialized() {
+        if (!hasRSGroupName()) {
+          
+          return false;
+        }
+        return true;
+      }
+
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest) e.getUnfinishedMessage();
+          throw e;
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      // required string r_s_group_name = 1;
+      private java.lang.Object rSGroupName_ = "";
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public boolean hasRSGroupName() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public java.lang.String getRSGroupName() {
+        java.lang.Object ref = rSGroupName_;
+        if (!(ref instanceof java.lang.String)) {
+          java.lang.String s = ((com.google.protobuf.ByteString) ref)
+              .toStringUtf8();
+          rSGroupName_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
+        }
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public com.google.protobuf.ByteString
+          getRSGroupNameBytes() {
+        java.lang.Object ref = rSGroupName_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          rSGroupName_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public Builder setRSGroupName(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+        rSGroupName_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public Builder clearRSGroupName() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        rSGroupName_ = getDefaultInstance().getRSGroupName();
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string r_s_group_name = 1;</code>
+       */
+      public Builder setRSGroupNameBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+        rSGroupName_ = value;
+        onChanged();
+        return this;
+      }
+
+      // @@protoc_insertion_point(builder_scope:hbase.pb.GetRSGroupInfoRequest)
+    }
+
+    static {
+      defaultInstance = new GetRSGroupInfoRequest(true);
+      defaultInstance.initFields();
+    }
+
+    // @@protoc_insertion_point(class_scope:hbase.pb.GetRSGroupInfoRequest)
+  }
+
+  public interface GetRSGroupInfoResponseOrBuilder
+      extends com.google.protobuf.MessageOrBuilder {
+
+    // optional .hbase.pb.RSGroupInfo r_s_group_info = 1;
+    /**
+     * <code>optional .hbase.pb.RSGroupInfo r_s_group_info = 1;</code>
+     */
+    boolean hasRSGroupInfo();
+    /**
+     * <code>optional .hbase.pb.RSGroupInfo r_s_group_info = 1;</code>
+     */
+    org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo getRSGroupInfo();
+    /**
+     * <code>optional .hbase.pb.RSGroupInfo r_s_group_info = 1;</code>
+     */
+    org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfoOrBuilder getRSGroupInfoOrBuilder();
+  }
+  /**
+   * Protobuf type {@code hbase.pb.GetRSGroupInfoResponse}
+   */
+  public static final class GetRSGroupInfoResponse extends
+      com.google.protobuf.GeneratedMessage
+      implements GetRSGroupInfoResponseOrBuilder {
+    // Use GetRSGroupInfoResponse.newBuilder() to construct.
+    private GetRSGroupInfoResponse(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
+      super(builder);
+      this.unknownFields = builder.getUnknownFields();
+    }
+    private GetRSGroupInfoResponse(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+    private static final GetRSGroupInfoResponse defaultInstance;
+    public static GetRSGroupInfoResponse getDefaultInstance() {
+      return defaultInstance;
+    }
+
+    public GetRSGroupInfoResponse getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+
+    private final com.google.protobuf.UnknownFieldSet unknownFields;
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+        getUnknownFields() {
+      return this.unknownFields;
+    }
+    private GetRSGroupInfoResponse(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      initFields();
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!parseUnknownField(input, unknownFields,
+                                     extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+            case 10: {
+              org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.Builder subBuilder = null;
+              if (((bitField0_ & 0x00000001) == 0x00000001)) {
+                subBuilder = rSGroupInfo_.toBuilder();
+              }
+              rSGroupInfo_ = input.readMessage(org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.PARSER, extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(rSGroupInfo_);
+                rSGroupInfo_ = subBuilder.buildPartial();
+              }
+              bitField0_ |= 0x00000001;
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e.getMessage()).setUnfinishedMessage(this);
+      } finally {
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoResponse_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse.Builder.class);
+    }
+
+    public static com.google.protobuf.Parser<GetRSGroupInfoResponse> PARSER =
+        new com.google.protobuf.AbstractParser<GetRSGroupInfoResponse>() {
+      public GetRSGroupInfoResponse parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new GetRSGroupInfoResponse(input, extensionRegistry);
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<GetRSGroupInfoResponse> getParserForType() {
+      return PARSER;
+    }
+
+    private int bitField0_;
+    // optional .hbase.pb.RSGroupInfo r_s_group_info = 1;
+    public static final int R_S_GROUP_INFO_FIELD_NUMBER = 1;
+    private org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo rSGroupInfo_;
+    /**
+     * <code>optional .hbase.pb.RSGroupInfo r_s_group_info = 1;</code>
+     */
+    public boolean hasRSGroupInfo() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>optional .hbase.pb.RSGroupInfo r_s_group_info = 1;</code>
+     */
+    public org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo getRSGroupInfo() {
+      return rSGroupInfo_;
+    }
+    /**
+     * <code>optional .hbase.pb.RSGroupInfo r_s_group_info = 1;</code>
+     */
+    public org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfoOrBuilder getRSGroupInfoOrBuilder() {
+      return rSGroupInfo_;
+    }
+
+    private void initFields() {
+      rSGroupInfo_ = org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.getDefaultInstance();
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+
+      if (hasRSGroupInfo()) {
+        if (!getRSGroupInfo().isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeMessage(1, rSGroupInfo_);
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, rSGroupInfo_);
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSerializedSize = size;
+      return size;
+    }
+
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse)) {
+        return super.equals(obj);
+      }
+      org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse other = (org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse) obj;
+
+      boolean result = true;
+      result = result && (hasRSGroupInfo() == other.hasRSGroupInfo());
+      if (hasRSGroupInfo()) {
+        result = result && getRSGroupInfo()
+            .equals(other.getRSGroupInfo());
+      }
+      result = result &&
+          getUnknownFields().equals(other.getUnknownFields());
+      return result;
+    }
+
+    private int memoizedHashCode = 0;
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (hasRSGroupInfo()) {
+        hash = (37 * hash) + R_S_GROUP_INFO_FIELD_NUMBER;
+        hash = (53 * hash) + getRSGroupInfo().hashCode();
+      }
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code hbase.pb.GetRSGroupInfoResponse}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder>
+       implements org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponseOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoResponse_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoResponse_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse.Builder.class);
+      }
+
+      // Construct using org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+          getRSGroupInfoFieldBuilder();
+        }
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+
+      public Builder clear() {
+        super.clear();
+        if (rSGroupInfoBuilder_ == null) {
+          rSGroupInfo_ = org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.getDefaultInstance();
+        } else {
+          rSGroupInfoBuilder_.clear();
+        }
+        bitField0_ = (bitField0_ & ~0x00000001);
+        return this;
+      }
+
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.internal_static_hbase_pb_GetRSGroupInfoResponse_descriptor;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse getDefaultInstanceForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse.getDefaultInstance();
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse build() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse buildPartial() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse result = new org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        if (rSGroupInfoBuilder_ == null) {
+          result.rSGroupInfo_ = rSGroupInfo_;
+        } else {
+          result.rSGroupInfo_ = rSGroupInfoBuilder_.build();
+        }
+        result.bitField0_ = to_bitField0_;
+        onBuilt();
+        return result;
+      }
+
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse) {
+          return mergeFrom((org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse other) {
+        if (other == org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse.getDefaultInstance()) return this;
+        if (other.hasRSGroupInfo()) {
+          mergeRSGroupInfo(other.getRSGroupInfo());
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        return this;
+      }
+
+      public final boolean isInitialized() {
+        if (hasRSGroupInfo()) {
+          if (!getRSGroupInfo().isInitialized()) {
+            
+            return false;
+          }
+        }
+        return true;
+      }
+
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupI

<TRUNCATED>

[7/7] hbase git commit: HBASE-6721 RegionServer Group based Assignment (Francis Liu)

Posted by en...@apache.org.
HBASE-6721 RegionServer Group based Assignment (Francis Liu)


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

Branch: refs/heads/master
Commit: ca816f0780f6e5a117b85810cf35f3b29c964ddc
Parents: 122e6f5
Author: Enis Soztutar <en...@apache.org>
Authored: Mon Mar 14 18:28:50 2016 -0700
Committer: Enis Soztutar <en...@apache.org>
Committed: Mon Mar 14 18:28:50 2016 -0700

----------------------------------------------------------------------
 .../hadoop/hbase/protobuf/ProtobufUtil.java     |    69 +-
 .../org/apache/hadoop/hbase/ServerName.java     |    24 +-
 .../hadoop/hbase/rsgroup/RSGroupInfo.java       |   187 +
 hbase-it/pom.xml                                |    10 +
 .../hbase/rsgroup/IntegrationTestRSGroup.java   |    99 +
 hbase-protocol/pom.xml                          |     2 +
 .../protobuf/generated/RSGroupAdminProtos.java  | 11855 +++++++++++++++++
 .../hbase/protobuf/generated/RSGroupProtos.java |  1331 ++
 hbase-protocol/src/main/protobuf/RSGroup.proto  |    34 +
 .../src/main/protobuf/RSGroupAdmin.proto        |   136 +
 hbase-rsgroup/pom.xml                           |   346 +
 .../hadoop/hbase/rsgroup/RSGroupAdmin.java      |   121 +
 .../hbase/rsgroup/RSGroupAdminClient.java       |   204 +
 .../hbase/rsgroup/RSGroupAdminEndpoint.java     |   965 ++
 .../hbase/rsgroup/RSGroupAdminServer.java       |   501 +
 .../hbase/rsgroup/RSGroupBasedLoadBalancer.java |   422 +
 .../hbase/rsgroup/RSGroupInfoManager.java       |   132 +
 .../hbase/rsgroup/RSGroupInfoManagerImpl.java   |   755 ++
 .../hadoop/hbase/rsgroup/RSGroupSerDe.java      |    88 +
 .../hbase/rsgroup/RSGroupableBalancer.java      |    29 +
 .../balancer/TestRSGroupBasedLoadBalancer.java  |   574 +
 .../hadoop/hbase/rsgroup/TestRSGroups.java      |   287 +
 .../hadoop/hbase/rsgroup/TestRSGroupsBase.java  |   643 +
 .../hbase/rsgroup/TestRSGroupsOfflineMode.java  |   187 +
 .../rsgroup/VerifyingRSGroupAdminClient.java    |   149 +
 .../hbase/tmpl/master/MasterStatusTmpl.jamon    |    34 +-
 .../BaseMasterAndRegionObserver.java            |    53 +
 .../hbase/coprocessor/BaseMasterObserver.java   |    54 +
 .../hbase/coprocessor/MasterObserver.java       |    98 +
 .../hadoop/hbase/master/AssignmentManager.java  |    25 +-
 .../org/apache/hadoop/hbase/master/HMaster.java |    32 +-
 .../hadoop/hbase/master/LoadBalancer.java       |     3 +
 .../hbase/master/MasterCoprocessorHost.java     |   137 +
 .../hadoop/hbase/master/MasterRpcServices.java  |     8 +
 .../hadoop/hbase/master/MasterServices.java     |     5 +
 .../hbase/security/access/AccessController.java |    32 +
 .../hbase/coprocessor/TestMasterObserver.java   |    53 +
 .../master/TestAssignmentManagerOnCluster.java  |   127 +-
 .../hadoop/hbase/master/TestCatalogJanitor.java |     3 +
 .../hbase/master/TestMasterStatusServlet.java   |    12 +-
 .../normalizer/TestSimpleRegionNormalizer.java  |     2 +-
 .../security/access/TestAccessController.java   |    75 +
 hbase-shell/pom.xml                             |    35 +
 hbase-shell/src/main/ruby/hbase.rb              |     1 +
 hbase-shell/src/main/ruby/hbase/hbase.rb        |     4 +
 .../src/main/ruby/hbase/rsgroup_admin.rb        |   150 +
 hbase-shell/src/main/ruby/shell.rb              |    21 +
 hbase-shell/src/main/ruby/shell/commands.rb     |     4 +
 .../src/main/ruby/shell/commands/add_rsgroup.rb |    39 +
 .../main/ruby/shell/commands/balance_rsgroup.rb |    37 +
 .../src/main/ruby/shell/commands/get_rsgroup.rb |    44 +
 .../ruby/shell/commands/get_server_rsgroup.rb   |    40 +
 .../ruby/shell/commands/get_table_rsgroup.rb    |    41 +
 .../main/ruby/shell/commands/list_rsgroups.rb   |    50 +
 .../ruby/shell/commands/move_rsgroup_servers.rb |    37 +
 .../ruby/shell/commands/move_rsgroup_tables.rb  |    37 +
 .../main/ruby/shell/commands/remove_rsgroup.rb  |    37 +
 .../apache/hadoop/hbase/client/TestShell.java   |     2 +-
 .../hbase/client/rsgroup/TestShellRSGroups.java |   111 +
 .../src/test/ruby/shell/rsgroup_shell_test.rb   |    96 +
 hbase-shell/src/test/ruby/test_helper.rb        |     4 +
 pom.xml                                         |    23 +
 62 files changed, 20652 insertions(+), 64 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java
index b052e63..9c71d97 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java
@@ -18,7 +18,19 @@
 package org.apache.hadoop.hbase.protobuf;
 
 
-import static org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType.REGION_NAME;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.net.HostAndPort;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Message;
+import com.google.protobuf.Parser;
+import com.google.protobuf.RpcChannel;
+import com.google.protobuf.Service;
+import com.google.protobuf.ServiceException;
+import com.google.protobuf.TextFormat;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -124,6 +136,7 @@ import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateTableReques
 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsResponse;
 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MasterService;
 import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
 import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerReportRequest;
 import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerStartupRequest;
 import org.apache.hadoop.hbase.protobuf.generated.WALProtos;
@@ -139,6 +152,7 @@ import org.apache.hadoop.hbase.quotas.QuotaType;
 import org.apache.hadoop.hbase.quotas.ThrottleType;
 import org.apache.hadoop.hbase.replication.ReplicationLoadSink;
 import org.apache.hadoop.hbase.replication.ReplicationLoadSource;
+import org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
 import org.apache.hadoop.hbase.security.User;
 import org.apache.hadoop.hbase.security.access.Permission;
 import org.apache.hadoop.hbase.security.access.TablePermission;
@@ -157,18 +171,8 @@ import org.apache.hadoop.io.Text;
 import org.apache.hadoop.ipc.RemoteException;
 import org.apache.hadoop.security.token.Token;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Lists;
-import com.google.protobuf.ByteString;
-import com.google.protobuf.CodedInputStream;
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.Message;
-import com.google.protobuf.Parser;
-import com.google.protobuf.RpcChannel;
-import com.google.protobuf.Service;
-import com.google.protobuf.ServiceException;
-import com.google.protobuf.TextFormat;
+import static org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier
+    .RegionSpecifierType.REGION_NAME;
 
 /**
  * Protobufs utility.
@@ -3057,7 +3061,7 @@ public final class ProtobufUtil {
    * @param builder current message builder
    * @param in InputStream containing protobuf data
    * @param size known size of protobuf data
-   * @throws IOException 
+   * @throws IOException
    */
   public static void mergeFrom(Message.Builder builder, InputStream in, int size)
       throws IOException {
@@ -3072,7 +3076,7 @@ public final class ProtobufUtil {
    * buffers where the message size is not known
    * @param builder current message builder
    * @param in InputStream containing protobuf data
-   * @throws IOException 
+   * @throws IOException
    */
   public static void mergeFrom(Message.Builder builder, InputStream in)
       throws IOException {
@@ -3086,8 +3090,8 @@ public final class ProtobufUtil {
    * This version of protobuf's mergeFrom avoids the hard-coded 64MB limit for decoding
    * buffers when working with ByteStrings
    * @param builder current message builder
-   * @param bs ByteString containing the 
-   * @throws IOException 
+   * @param bs ByteString containing the
+   * @throws IOException
    */
   public static void mergeFrom(Message.Builder builder, ByteString bs) throws IOException {
     final CodedInputStream codedInput = bs.newCodedInput();
@@ -3101,7 +3105,7 @@ public final class ProtobufUtil {
    * buffers when working with byte arrays
    * @param builder current message builder
    * @param b byte array
-   * @throws IOException 
+   * @throws IOException
    */
   public static void mergeFrom(Message.Builder builder, byte[] b) throws IOException {
     final CodedInputStream codedInput = CodedInputStream.newInstance(b);
@@ -3221,4 +3225,33 @@ public final class ProtobufUtil {
     return new TimeRange(minStamp, maxStamp);
   }
 
+  public static RSGroupInfo toGroupInfo(RSGroupProtos.RSGroupInfo proto) {
+    RSGroupInfo RSGroupInfo = new RSGroupInfo(proto.getName());
+    for(HBaseProtos.ServerName el: proto.getServersList()) {
+      RSGroupInfo.addServer(HostAndPort.fromParts(el.getHostName(), el.getPort()));
+    }
+    for(HBaseProtos.TableName pTableName: proto.getTablesList()) {
+      RSGroupInfo.addTable(ProtobufUtil.toTableName(pTableName));
+    }
+    return RSGroupInfo;
+  }
+
+  public static RSGroupProtos.RSGroupInfo toProtoGroupInfo(RSGroupInfo pojo) {
+    List<HBaseProtos.TableName> tables =
+        new ArrayList<HBaseProtos.TableName>(pojo.getTables().size());
+    for(TableName arg: pojo.getTables()) {
+      tables.add(ProtobufUtil.toProtoTableName(arg));
+    }
+    List<HBaseProtos.ServerName> hostports =
+        new ArrayList<HBaseProtos.ServerName>(pojo.getServers().size());
+    for(HostAndPort el: pojo.getServers()) {
+      hostports.add(HBaseProtos.ServerName.newBuilder()
+          .setHostName(el.getHostText())
+          .setPort(el.getPort())
+          .build());
+    }
+    return RSGroupProtos.RSGroupInfo.newBuilder().setName(pojo.getName())
+        .addAllServers(hostports)
+        .addAllTables(tables).build();
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-common/src/main/java/org/apache/hadoop/hbase/ServerName.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/ServerName.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/ServerName.java
index 059dcb8..c0c43ed 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/ServerName.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/ServerName.java
@@ -18,6 +18,10 @@
  */
 package org.apache.hadoop.hbase;
 
+import com.google.common.net.HostAndPort;
+import com.google.common.net.InetAddresses;
+import com.google.protobuf.InvalidProtocolBufferException;
+
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
@@ -31,9 +35,6 @@ import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
 import org.apache.hadoop.hbase.util.Addressing;
 import org.apache.hadoop.hbase.util.Bytes;
 
-import com.google.common.net.InetAddresses;
-import com.google.protobuf.InvalidProtocolBufferException;
-
 /**
  * Instance of an HBase ServerName.
  * A server name is used uniquely identifying a server instance in a cluster and is made
@@ -54,7 +55,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
  */
 @InterfaceAudience.Public
 @InterfaceStability.Evolving
-public class ServerName implements Comparable<ServerName>, Serializable {
+  public class ServerName implements Comparable<ServerName>, Serializable {
   private static final long serialVersionUID = 1367463982557264981L;
 
   /**
@@ -91,6 +92,7 @@ public class ServerName implements Comparable<ServerName>, Serializable {
   private final String hostnameOnly;
   private final int port;
   private final long startcode;
+  private transient HostAndPort hostAndPort;
 
   /**
    * Cached versioned bytes of this ServerName instance.
@@ -105,7 +107,7 @@ public class ServerName implements Comparable<ServerName>, Serializable {
     this.hostnameOnly = hostname;
     this.port = port;
     this.startcode = startcode;
-    this.servername = getServerName(this.hostnameOnly, port, startcode);
+    this.servername = getServerName(hostname, port, startcode);
   }
 
   /**
@@ -189,7 +191,8 @@ public class ServerName implements Comparable<ServerName>, Serializable {
    * in compares, etc.
    */
   public String toShortString() {
-    return Addressing.createHostAndPortStr(getHostNameMinusDomain(this.hostnameOnly), this.port);
+    return Addressing.createHostAndPortStr(
+        getHostNameMinusDomain(hostnameOnly), port);
   }
 
   /**
@@ -256,7 +259,14 @@ public class ServerName implements Comparable<ServerName>, Serializable {
    * {@link Addressing#createHostAndPortStr(String, int)}
    */
   public String getHostAndPort() {
-    return Addressing.createHostAndPortStr(this.hostnameOnly, this.port);
+    return Addressing.createHostAndPortStr(hostnameOnly, port);
+  }
+
+  public HostAndPort getHostPort() {
+    if (hostAndPort == null) {
+      hostAndPort = HostAndPort.fromParts(hostnameOnly, port);
+    }
+    return hostAndPort;
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-common/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfo.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfo.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfo.java
new file mode 100644
index 0000000..0fb02d8
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfo.java
@@ -0,0 +1,187 @@
+/**
+ * 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.
+ */
+
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
+
+import java.util.Collection;
+import java.util.NavigableSet;
+import java.util.Set;
+
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.classification.InterfaceStability;
+
+/**
+ * Stores the group information of region server groups.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public class RSGroupInfo {
+
+  public static final String DEFAULT_GROUP = "default";
+  public static final String NAMESPACEDESC_PROP_GROUP = "hbase.rsgroup.name";
+
+  private String name;
+  private Set<HostAndPort> servers;
+  private NavigableSet<TableName> tables;
+
+  public RSGroupInfo(String name) {
+    this(name, Sets.<HostAndPort>newHashSet(), Sets.<TableName>newTreeSet());
+  }
+
+  RSGroupInfo(String name,
+              Set<HostAndPort> servers,
+              NavigableSet<TableName> tables) {
+    this.name = name;
+    this.servers = servers;
+    this.tables = tables;
+  }
+
+  public RSGroupInfo(RSGroupInfo src) {
+    name = src.getName();
+    servers = Sets.newHashSet(src.getServers());
+    tables = Sets.newTreeSet(src.getTables());
+  }
+
+  /**
+   * Get group name.
+   *
+   * @return group name
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Adds the server to the group.
+   *
+   * @param hostPort the server
+   */
+  public void addServer(HostAndPort hostPort){
+    servers.add(hostPort);
+  }
+
+  /**
+   * Adds a group of servers.
+   *
+   * @param hostPort the servers
+   */
+  public void addAllServers(Collection<HostAndPort> hostPort){
+    servers.addAll(hostPort);
+  }
+
+  /**
+   * @param hostPort hostPort of the server
+   * @return true, if a server with hostPort is found
+   */
+  public boolean containsServer(HostAndPort hostPort) {
+    return servers.contains(hostPort);
+  }
+
+  /**
+   * Get list of servers.
+   *
+   * @return set of servers
+   */
+  public Set<HostAndPort> getServers() {
+    return servers;
+  }
+
+  /**
+   * Remove a server from this group.
+   *
+   * @param hostPort HostPort of the server to remove
+   */
+  public boolean removeServer(HostAndPort hostPort) {
+    return servers.remove(hostPort);
+  }
+
+  /**
+   * Set of tables that are members of this group
+   * @return set of tables
+   */
+  public NavigableSet<TableName> getTables() {
+    return tables;
+  }
+
+  public void addTable(TableName table) {
+    tables.add(table);
+  }
+
+  public void addAllTables(Collection<TableName> arg) {
+    tables.addAll(arg);
+  }
+
+  public boolean containsTable(TableName table) {
+    return tables.contains(table);
+  }
+
+  public boolean removeTable(TableName table) {
+    return tables.remove(table);
+  }
+
+  @Override
+  public String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.append("Name:");
+    sb.append(this.name);
+    sb.append(", ");
+    sb.append(" Servers:");
+    sb.append(this.servers);
+    return sb.toString();
+
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    RSGroupInfo RSGroupInfo = (RSGroupInfo) o;
+
+    if (!name.equals(RSGroupInfo.name)) {
+      return false;
+    }
+    if (!servers.equals(RSGroupInfo.servers)) {
+      return false;
+    }
+    if (!tables.equals(RSGroupInfo.tables)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = servers.hashCode();
+    result = 31 * result + tables.hashCode();
+    result = 31 * result + name.hashCode();
+    return result;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-it/pom.xml
----------------------------------------------------------------------
diff --git a/hbase-it/pom.xml b/hbase-it/pom.xml
index 92243f0..70d13ad 100644
--- a/hbase-it/pom.xml
+++ b/hbase-it/pom.xml
@@ -209,6 +209,16 @@
     </dependency>
     <dependency>
       <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-rsgroup</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-rsgroup</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
       <artifactId>hbase-server</artifactId>
     </dependency>
     <dependency>

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-it/src/test/java/org/apache/hadoop/hbase/rsgroup/IntegrationTestRSGroup.java
----------------------------------------------------------------------
diff --git a/hbase-it/src/test/java/org/apache/hadoop/hbase/rsgroup/IntegrationTestRSGroup.java b/hbase-it/src/test/java/org/apache/hadoop/hbase/rsgroup/IntegrationTestRSGroup.java
new file mode 100644
index 0000000..fe9c58c2
--- /dev/null
+++ b/hbase-it/src/test/java/org/apache/hadoop/hbase/rsgroup/IntegrationTestRSGroup.java
@@ -0,0 +1,99 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.IntegrationTestingUtility;
+import org.apache.hadoop.hbase.Waiter;
+import org.apache.hadoop.hbase.testclassification.IntegrationTests;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.experimental.categories.Category;
+
+/**
+ * Runs all of the units tests defined in TestGroupBase
+ * as an integration test.
+ * Requires TestRSGroupBase.NUM_SLAVE_BASE servers to run.
+ */
+@Category(IntegrationTests.class)
+public class IntegrationTestRSGroup extends TestRSGroupsBase {
+  //Integration specific
+  private final static Log LOG = LogFactory.getLog(IntegrationTestRSGroup.class);
+  private static boolean initialized = false;
+
+  @Before
+  public void beforeMethod() throws Exception {
+    if(!initialized) {
+      LOG.info("Setting up IntegrationTestGroup");
+      LOG.info("Initializing cluster with " + NUM_SLAVES_BASE + " servers");
+      TEST_UTIL = new IntegrationTestingUtility();
+      ((IntegrationTestingUtility)TEST_UTIL).initializeCluster(NUM_SLAVES_BASE);
+      //set shared configs
+      admin = TEST_UTIL.getHBaseAdmin();
+      cluster = TEST_UTIL.getHBaseClusterInterface();
+      rsGroupAdmin = new VerifyingRSGroupAdminClient(rsGroupAdmin.newClient(TEST_UTIL.getConnection()),
+          TEST_UTIL.getConfiguration());
+      LOG.info("Done initializing cluster");
+      initialized = true;
+      //cluster may not be clean
+      //cleanup when initializing
+      afterMethod();
+    }
+  }
+
+  @After
+  public void afterMethod() throws Exception {
+    LOG.info("Cleaning up previous test run");
+    //cleanup previous artifacts
+    deleteTableIfNecessary();
+    deleteNamespaceIfNecessary();
+    deleteGroups();
+    admin.setBalancerRunning(true, true);
+
+    LOG.info("Restoring the cluster");
+    ((IntegrationTestingUtility)TEST_UTIL).restoreCluster();
+    LOG.info("Done restoring the cluster");
+
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        LOG.info("Waiting for cleanup to finish "+ rsGroupAdmin.listRSGroups());
+        //Might be greater since moving servers back to default
+        //is after starting a server
+        return rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()
+            >= NUM_SLAVES_BASE;
+      }
+    });
+
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        LOG.info("Waiting for regionservers to be registered "+ rsGroupAdmin.listRSGroups());
+        //Might be greater since moving servers back to default
+        //is after starting a server
+        return rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()
+            == getNumServers();
+      }
+    });
+
+    LOG.info("Done cleaning up previous test run");
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-protocol/pom.xml
----------------------------------------------------------------------
diff --git a/hbase-protocol/pom.xml b/hbase-protocol/pom.xml
index 8034576..d43b7b9 100644
--- a/hbase-protocol/pom.xml
+++ b/hbase-protocol/pom.xml
@@ -193,6 +193,8 @@
                           <include>RegionServerStatus.proto</include>
                           <include>RowProcessor.proto</include>
                           <include>RPC.proto</include>
+                          <include>RSGroup.proto</include>
+                          <include>RSGroupAdmin.proto</include>
                           <include>SecureBulkLoad.proto</include>
                           <include>Tracing.proto</include>
                           <include>VisibilityLabels.proto</include>


[3/7] hbase git commit: HBASE-6721 RegionServer Group based Assignment (Francis Liu)

Posted by en...@apache.org.
http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
new file mode 100644
index 0000000..01efefc
--- /dev/null
+++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
@@ -0,0 +1,755 @@
+/**
+ * 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.
+ */
+
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
+import com.google.protobuf.ServiceException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellUtil;
+import org.apache.hadoop.hbase.Coprocessor;
+import org.apache.hadoop.hbase.DoNotRetryIOException;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.MetaTableAccessor;
+import org.apache.hadoop.hbase.MetaTableAccessor.DefaultVisitorBase;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.ClusterConnection;
+import org.apache.hadoop.hbase.client.Delete;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.Mutation;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.client.TableState;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint;
+import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
+import org.apache.hadoop.hbase.master.MasterServices;
+import org.apache.hadoop.hbase.master.ServerListener;
+import org.apache.hadoop.hbase.master.TableStateManager;
+import org.apache.hadoop.hbase.master.procedure.CreateTableProcedure;
+import org.apache.hadoop.hbase.master.procedure.ProcedurePrepareLatch;
+import org.apache.hadoop.hbase.protobuf.ProtobufMagic;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.RequestConverter;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
+import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
+import org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy;
+import org.apache.hadoop.hbase.security.access.AccessControlLists;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.ModifyRegionUtils;
+import org.apache.hadoop.hbase.zookeeper.ZKUtil;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.zookeeper.KeeperException;
+
+/**
+ * This is an implementation of {@link RSGroupInfoManager}. Which makes
+ * use of an HBase table as the persistence store for the group information.
+ * It also makes use of zookeeper to store group information needed
+ * for bootstrapping during offline mode.
+ */
+public class RSGroupInfoManagerImpl implements RSGroupInfoManager, ServerListener {
+  private static final Log LOG = LogFactory.getLog(RSGroupInfoManagerImpl.class);
+
+  /** Table descriptor for <code>hbase:rsgroup</code> catalog table */
+  private final static HTableDescriptor RSGROUP_TABLE_DESC;
+  static {
+    RSGROUP_TABLE_DESC = new HTableDescriptor(RSGROUP_TABLE_NAME_BYTES);
+    RSGROUP_TABLE_DESC.addFamily(new HColumnDescriptor(META_FAMILY_BYTES));
+    RSGROUP_TABLE_DESC.setRegionSplitPolicyClassName(DisabledRegionSplitPolicy.class.getName());
+    try {
+      RSGROUP_TABLE_DESC.addCoprocessor(
+        MultiRowMutationEndpoint.class.getName(),
+          null, Coprocessor.PRIORITY_SYSTEM, null);
+    } catch (IOException ex) {
+      throw new RuntimeException(ex);
+    }
+  }
+
+  private volatile Map<String, RSGroupInfo> rsGroupMap;
+  private volatile Map<TableName, String> tableMap;
+  private MasterServices master;
+  private Table rsGroupTable;
+  private ClusterConnection conn;
+  private ZooKeeperWatcher watcher;
+  private RSGroupStartupWorker rsGroupStartupWorker;
+  // contains list of groups that were last flushed to persistent store
+  private volatile Set<String> prevRSGroups;
+  private RSGroupSerDe rsGroupSerDe;
+  private DefaultServerUpdater defaultServerUpdater;
+
+
+  public RSGroupInfoManagerImpl(MasterServices master) throws IOException {
+    this.rsGroupMap = Collections.EMPTY_MAP;
+    this.tableMap = Collections.EMPTY_MAP;
+    rsGroupSerDe = new RSGroupSerDe();
+    this.master = master;
+    this.watcher = master.getZooKeeper();
+    this.conn = master.getClusterConnection();
+    rsGroupStartupWorker = new RSGroupStartupWorker(this, master, conn);
+    prevRSGroups = new HashSet<String>();
+    refresh();
+    rsGroupStartupWorker.start();
+    defaultServerUpdater = new DefaultServerUpdater(this);
+    master.getServerManager().registerListener(this);
+    defaultServerUpdater.start();
+  }
+
+  /**
+   * Adds the group.
+   *
+   * @param rsGroupInfo the group name
+   */
+  @Override
+  public synchronized void addRSGroup(RSGroupInfo rsGroupInfo) throws IOException {
+    checkGroupName(rsGroupInfo.getName());
+    if (rsGroupMap.get(rsGroupInfo.getName()) != null ||
+        rsGroupInfo.getName().equals(rsGroupInfo.DEFAULT_GROUP)) {
+      throw new DoNotRetryIOException("Group already exists: "+ rsGroupInfo.getName());
+    }
+    Map<String, RSGroupInfo> newGroupMap = Maps.newHashMap(rsGroupMap);
+    newGroupMap.put(rsGroupInfo.getName(), rsGroupInfo);
+    flushConfig(newGroupMap);
+  }
+
+  @Override
+  public synchronized boolean moveServers(Set<HostAndPort> hostPorts, String srcGroup,
+                                          String dstGroup) throws IOException {
+    if (!rsGroupMap.containsKey(srcGroup)) {
+      throw new DoNotRetryIOException("Group "+srcGroup+" does not exist");
+    }
+    if (!rsGroupMap.containsKey(dstGroup)) {
+      throw new DoNotRetryIOException("Group "+dstGroup+" does not exist");
+    }
+
+    RSGroupInfo src = new RSGroupInfo(getRSGroup(srcGroup));
+    RSGroupInfo dst = new RSGroupInfo(getRSGroup(dstGroup));
+    boolean foundOne = false;
+    for(HostAndPort el: hostPorts) {
+      foundOne = src.removeServer(el) || foundOne;
+      dst.addServer(el);
+    }
+
+    Map<String,RSGroupInfo> newGroupMap = Maps.newHashMap(rsGroupMap);
+    newGroupMap.put(src.getName(), src);
+    newGroupMap.put(dst.getName(), dst);
+
+    flushConfig(newGroupMap);
+    return foundOne;
+  }
+
+  /**
+   * Gets the group info of server.
+   *
+   * @param hostPort the server
+   * @return An instance of GroupInfo.
+   */
+  @Override
+  public RSGroupInfo getRSGroupOfServer(HostAndPort hostPort) throws IOException {
+    for (RSGroupInfo info : rsGroupMap.values()) {
+      if (info.containsServer(hostPort)){
+        return info;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Gets the group information.
+   *
+   * @param groupName
+   *          the group name
+   * @return An instance of GroupInfo
+   */
+  @Override
+  public RSGroupInfo getRSGroup(String groupName) throws IOException {
+    RSGroupInfo RSGroupInfo = rsGroupMap.get(groupName);
+    return RSGroupInfo;
+  }
+
+
+
+  @Override
+  public String getRSGroupOfTable(TableName tableName) throws IOException {
+    return tableMap.get(tableName);
+  }
+
+  @Override
+  public synchronized void moveTables(
+      Set<TableName> tableNames, String groupName) throws IOException {
+    if (groupName != null && !rsGroupMap.containsKey(groupName)) {
+      throw new DoNotRetryIOException("Group "+groupName+" does not exist or is a special group");
+    }
+
+    Map<String,RSGroupInfo> newGroupMap = Maps.newHashMap(rsGroupMap);
+    for(TableName tableName: tableNames) {
+      if (tableMap.containsKey(tableName)) {
+        RSGroupInfo src = new RSGroupInfo(rsGroupMap.get(tableMap.get(tableName)));
+        src.removeTable(tableName);
+        newGroupMap.put(src.getName(), src);
+      }
+      if(groupName != null) {
+        RSGroupInfo dst = new RSGroupInfo(newGroupMap.get(groupName));
+        dst.addTable(tableName);
+        newGroupMap.put(dst.getName(), dst);
+      }
+    }
+
+    flushConfig(newGroupMap);
+  }
+
+
+  /**
+   * Delete a region server group.
+   *
+   * @param groupName the group name
+   * @throws java.io.IOException Signals that an I/O exception has occurred.
+   */
+  @Override
+  public synchronized void removeRSGroup(String groupName) throws IOException {
+    if (!rsGroupMap.containsKey(groupName) || groupName.equals(RSGroupInfo.DEFAULT_GROUP)) {
+      throw new DoNotRetryIOException("Group "+groupName+" does not exist or is a reserved group");
+    }
+    Map<String,RSGroupInfo> newGroupMap = Maps.newHashMap(rsGroupMap);
+    newGroupMap.remove(groupName);
+    flushConfig(newGroupMap);
+  }
+
+  @Override
+  public List<RSGroupInfo> listRSGroups() throws IOException {
+    List<RSGroupInfo> list = Lists.newLinkedList(rsGroupMap.values());
+    return list;
+  }
+
+  @Override
+  public boolean isOnline() {
+    return rsGroupStartupWorker.isOnline();
+  }
+
+  @Override
+  public synchronized void refresh() throws IOException {
+    refresh(false);
+  }
+
+  private synchronized void refresh(boolean forceOnline) throws IOException {
+    List<RSGroupInfo> groupList = new LinkedList<RSGroupInfo>();
+
+    // overwrite anything read from zk, group table is source of truth
+    // if online read from GROUP table
+    if (forceOnline || isOnline()) {
+      LOG.debug("Refreshing in Online mode.");
+      if (rsGroupTable == null) {
+        rsGroupTable = conn.getTable(RSGROUP_TABLE_NAME);
+      }
+      groupList.addAll(rsGroupSerDe.retrieveGroupList(rsGroupTable));
+    } else {
+      LOG.debug("Refershing in Offline mode.");
+      String groupBasePath = ZKUtil.joinZNode(watcher.baseZNode, rsGroupZNode);
+      groupList.addAll(rsGroupSerDe.retrieveGroupList(watcher, groupBasePath));
+    }
+
+    // refresh default group, prune
+    NavigableSet<TableName> orphanTables = new TreeSet<TableName>();
+    for(String entry: master.getTableDescriptors().getAll().keySet()) {
+      orphanTables.add(TableName.valueOf(entry));
+    }
+
+    List<TableName> specialTables;
+    if(!master.isInitialized()) {
+      specialTables = new ArrayList<TableName>();
+      specialTables.add(AccessControlLists.ACL_TABLE_NAME);
+      specialTables.add(TableName.META_TABLE_NAME);
+      specialTables.add(TableName.NAMESPACE_TABLE_NAME);
+      specialTables.add(RSGROUP_TABLE_NAME);
+    } else {
+      specialTables =
+          master.listTableNamesByNamespace(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR);
+    }
+
+    for(TableName table : specialTables) {
+      orphanTables.add(table);
+    }
+    for(RSGroupInfo group: groupList) {
+      if(!group.getName().equals(RSGroupInfo.DEFAULT_GROUP)) {
+        orphanTables.removeAll(group.getTables());
+      }
+    }
+
+    // This is added to the last of the list
+    // so it overwrites the default group loaded
+    // from region group table or zk
+    groupList.add(new RSGroupInfo(RSGroupInfo.DEFAULT_GROUP,
+        Sets.newHashSet(getDefaultServers()),
+        orphanTables));
+
+
+    // populate the data
+    HashMap<String, RSGroupInfo> newGroupMap = Maps.newHashMap();
+    HashMap<TableName, String> newTableMap = Maps.newHashMap();
+    for (RSGroupInfo group : groupList) {
+      newGroupMap.put(group.getName(), group);
+      for(TableName table: group.getTables()) {
+        newTableMap.put(table, group.getName());
+      }
+    }
+    rsGroupMap = Collections.unmodifiableMap(newGroupMap);
+    tableMap = Collections.unmodifiableMap(newTableMap);
+
+    prevRSGroups.clear();
+    prevRSGroups.addAll(rsGroupMap.keySet());
+  }
+
+  private synchronized Map<TableName,String> flushConfigTable(Map<String,RSGroupInfo> newGroupMap)
+      throws IOException {
+    Map<TableName,String> newTableMap = Maps.newHashMap();
+    List<Mutation> mutations = Lists.newArrayList();
+
+    // populate deletes
+    for(String groupName : prevRSGroups) {
+      if(!newGroupMap.containsKey(groupName)) {
+        Delete d = new Delete(Bytes.toBytes(groupName));
+        mutations.add(d);
+      }
+    }
+
+    // populate puts
+    for(RSGroupInfo RSGroupInfo : newGroupMap.values()) {
+      RSGroupProtos.RSGroupInfo proto = ProtobufUtil.toProtoGroupInfo(RSGroupInfo);
+      Put p = new Put(Bytes.toBytes(RSGroupInfo.getName()));
+      p.addColumn(META_FAMILY_BYTES,
+          META_QUALIFIER_BYTES,
+          proto.toByteArray());
+      mutations.add(p);
+      for(TableName entry: RSGroupInfo.getTables()) {
+        newTableMap.put(entry, RSGroupInfo.getName());
+      }
+    }
+
+    if(mutations.size() > 0) {
+      multiMutate(mutations);
+    }
+    return newTableMap;
+  }
+
+  private synchronized void flushConfig(Map<String, RSGroupInfo> newGroupMap) throws IOException {
+    Map<TableName, String> newTableMap;
+
+    // For offline mode persistence is still unavailable
+    // We're refreshing in-memory state but only for default servers
+    if (!isOnline()) {
+      Map<String, RSGroupInfo> m = Maps.newHashMap(rsGroupMap);
+      RSGroupInfo oldDefaultGroup = m.remove(RSGroupInfo.DEFAULT_GROUP);
+      RSGroupInfo newDefaultGroup = newGroupMap.remove(RSGroupInfo.DEFAULT_GROUP);
+      if (!m.equals(newGroupMap) ||
+          !oldDefaultGroup.getTables().equals(newDefaultGroup.getTables())) {
+        throw new IOException("Only default servers can be updated during offline mode");
+      }
+      newGroupMap.put(RSGroupInfo.DEFAULT_GROUP, newDefaultGroup);
+      rsGroupMap = newGroupMap;
+      return;
+    }
+
+    newTableMap = flushConfigTable(newGroupMap);
+
+    // make changes visible since it has been
+    // persisted in the source of truth
+    rsGroupMap = Collections.unmodifiableMap(newGroupMap);
+    tableMap = Collections.unmodifiableMap(newTableMap);
+
+
+    try {
+      String groupBasePath = ZKUtil.joinZNode(watcher.baseZNode, rsGroupZNode);
+      ZKUtil.createAndFailSilent(watcher, groupBasePath, ProtobufMagic.PB_MAGIC);
+
+      List<ZKUtil.ZKUtilOp> zkOps = new ArrayList<ZKUtil.ZKUtilOp>(newGroupMap.size());
+      for(String groupName : prevRSGroups) {
+        if(!newGroupMap.containsKey(groupName)) {
+          String znode = ZKUtil.joinZNode(groupBasePath, groupName);
+          zkOps.add(ZKUtil.ZKUtilOp.deleteNodeFailSilent(znode));
+        }
+      }
+
+
+      for(RSGroupInfo RSGroupInfo : newGroupMap.values()) {
+        String znode = ZKUtil.joinZNode(groupBasePath, RSGroupInfo.getName());
+        RSGroupProtos.RSGroupInfo proto = ProtobufUtil.toProtoGroupInfo(RSGroupInfo);
+        LOG.debug("Updating znode: "+znode);
+        ZKUtil.createAndFailSilent(watcher, znode);
+        zkOps.add(ZKUtil.ZKUtilOp.deleteNodeFailSilent(znode));
+        zkOps.add(ZKUtil.ZKUtilOp.createAndFailSilent(znode,
+            ProtobufUtil.prependPBMagic(proto.toByteArray())));
+      }
+      LOG.debug("Writing ZK GroupInfo count: " + zkOps.size());
+
+      ZKUtil.multiOrSequential(watcher, zkOps, false);
+    } catch (KeeperException e) {
+      LOG.error("Failed to write to rsGroupZNode", e);
+      master.abort("Failed to write to rsGroupZNode", e);
+      throw new IOException("Failed to write to rsGroupZNode",e);
+    }
+
+    prevRSGroups.clear();
+    prevRSGroups.addAll(newGroupMap.keySet());
+  }
+
+  private List<ServerName> getOnlineRS() throws IOException {
+    if (master != null) {
+      return master.getServerManager().getOnlineServersList();
+    }
+    try {
+      LOG.debug("Reading online RS from zookeeper");
+      List<ServerName> servers = new LinkedList<ServerName>();
+      for (String el: ZKUtil.listChildrenNoWatch(watcher, watcher.rsZNode)) {
+        servers.add(ServerName.parseServerName(el));
+      }
+      return servers;
+    } catch (KeeperException e) {
+      throw new IOException("Failed to retrieve server list from zookeeper", e);
+    }
+  }
+
+  private List<HostAndPort> getDefaultServers() throws IOException {
+    List<HostAndPort> defaultServers = new LinkedList<HostAndPort>();
+    for(ServerName server : getOnlineRS()) {
+      HostAndPort hostPort = HostAndPort.fromParts(server.getHostname(), server.getPort());
+      boolean found = false;
+      for(RSGroupInfo RSGroupInfo : rsGroupMap.values()) {
+        if(!RSGroupInfo.DEFAULT_GROUP.equals(RSGroupInfo.getName()) &&
+            RSGroupInfo.containsServer(hostPort)) {
+          found = true;
+          break;
+        }
+      }
+      if(!found) {
+        defaultServers.add(hostPort);
+      }
+    }
+    return defaultServers;
+  }
+
+  private synchronized void updateDefaultServers(
+      Set<HostAndPort> hostPort) throws IOException {
+    RSGroupInfo info = rsGroupMap.get(RSGroupInfo.DEFAULT_GROUP);
+    RSGroupInfo newInfo = new RSGroupInfo(info.getName(), hostPort, info.getTables());
+    HashMap<String, RSGroupInfo> newGroupMap = Maps.newHashMap(rsGroupMap);
+    newGroupMap.put(newInfo.getName(), newInfo);
+    flushConfig(newGroupMap);
+  }
+
+  @Override
+  public void serverAdded(ServerName serverName) {
+    defaultServerUpdater.serverChanged();
+  }
+
+  @Override
+  public void serverRemoved(ServerName serverName) {
+    defaultServerUpdater.serverChanged();
+  }
+
+  private static class DefaultServerUpdater extends Thread {
+    private static final Log LOG = LogFactory.getLog(DefaultServerUpdater.class);
+    private RSGroupInfoManagerImpl mgr;
+    private boolean hasChanged = false;
+
+    public DefaultServerUpdater(RSGroupInfoManagerImpl mgr) {
+      this.mgr = mgr;
+    }
+
+    @Override
+    public void run() {
+      List<HostAndPort> prevDefaultServers = new LinkedList<HostAndPort>();
+      while(!mgr.master.isAborted() || !mgr.master.isStopped()) {
+        try {
+          LOG.info("Updating default servers.");
+          List<HostAndPort> servers = mgr.getDefaultServers();
+          Collections.sort(servers, new Comparator<HostAndPort>() {
+            @Override
+            public int compare(HostAndPort o1, HostAndPort o2) {
+              int diff = o1.getHostText().compareTo(o2.getHostText());
+              if (diff != 0) {
+                return diff;
+              }
+              return o1.getPort() - o2.getPort();
+            }
+          });
+          if(!servers.equals(prevDefaultServers)) {
+            mgr.updateDefaultServers(Sets.<HostAndPort>newHashSet(servers));
+            prevDefaultServers = servers;
+            LOG.info("Updated with servers: "+servers.size());
+          }
+          try {
+            synchronized (this) {
+              if(!hasChanged) {
+                wait();
+              }
+              hasChanged = false;
+            }
+          } catch (InterruptedException e) {
+          }
+        } catch (IOException e) {
+          LOG.warn("Failed to update default servers", e);
+        }
+      }
+    }
+
+    public void serverChanged() {
+      synchronized (this) {
+        hasChanged = true;
+        this.notify();
+      }
+    }
+  }
+
+
+  private static class RSGroupStartupWorker extends Thread {
+    private static final Log LOG = LogFactory.getLog(RSGroupStartupWorker.class);
+
+    private Configuration conf;
+    private volatile boolean isOnline = false;
+    private MasterServices masterServices;
+    private RSGroupInfoManagerImpl groupInfoManager;
+    private ClusterConnection conn;
+
+    public RSGroupStartupWorker(RSGroupInfoManagerImpl groupInfoManager,
+                                MasterServices masterServices,
+                                ClusterConnection conn) {
+      this.conf = masterServices.getConfiguration();
+      this.masterServices = masterServices;
+      this.groupInfoManager = groupInfoManager;
+      this.conn = conn;
+      setName(RSGroupStartupWorker.class.getName()+"-"+masterServices.getServerName());
+      setDaemon(true);
+    }
+
+    @Override
+    public void run() {
+      if(waitForGroupTableOnline()) {
+        LOG.info("GroupBasedLoadBalancer is now online");
+      }
+    }
+
+    public boolean waitForGroupTableOnline() {
+      final List<HRegionInfo> foundRegions = new LinkedList<HRegionInfo>();
+      final List<HRegionInfo> assignedRegions = new LinkedList<HRegionInfo>();
+      final AtomicBoolean found = new AtomicBoolean(false);
+      final TableStateManager tsm = masterServices.getTableStateManager();
+      boolean createSent = false;
+      while (!found.get() && isMasterRunning()) {
+        foundRegions.clear();
+        assignedRegions.clear();
+        found.set(true);
+        try {
+          final Table nsTable = conn.getTable(TableName.NAMESPACE_TABLE_NAME);
+          final Table groupTable = conn.getTable(RSGROUP_TABLE_NAME);
+          boolean rootMetaFound =
+              masterServices.getMetaTableLocator().verifyMetaRegionLocation(
+                  conn,
+                  masterServices.getZooKeeper(),
+                  1);
+          final AtomicBoolean nsFound = new AtomicBoolean(false);
+          if (rootMetaFound) {
+
+            MetaTableAccessor.Visitor visitor = new DefaultVisitorBase() {
+              @Override
+              public boolean visitInternal(Result row) throws IOException {
+
+                HRegionInfo info = MetaTableAccessor.getHRegionInfo(row);
+                if (info != null) {
+                  Cell serverCell =
+                      row.getColumnLatestCell(HConstants.CATALOG_FAMILY,
+                          HConstants.SERVER_QUALIFIER);
+                  if (RSGROUP_TABLE_NAME.equals(info.getTable()) && serverCell != null) {
+                    ServerName sn =
+                        ServerName.parseVersionedServerName(CellUtil.cloneValue(serverCell));
+                    if (sn == null) {
+                      found.set(false);
+                    } else if (tsm.isTableState(RSGROUP_TABLE_NAME, TableState.State.ENABLED)) {
+                      try {
+                        ClientProtos.ClientService.BlockingInterface rs = conn.getClient(sn);
+                        ClientProtos.GetRequest request =
+                            RequestConverter.buildGetRequest(info.getRegionName(),
+                                new Get(ROW_KEY));
+                        rs.get(null, request);
+                        assignedRegions.add(info);
+                      } catch(Exception ex) {
+                        LOG.debug("Caught exception while verifying group region", ex);
+                      }
+                    }
+                    foundRegions.add(info);
+                  }
+                  if (TableName.NAMESPACE_TABLE_NAME.equals(info.getTable())) {
+                    Cell cell = row.getColumnLatestCell(HConstants.CATALOG_FAMILY,
+                        HConstants.SERVER_QUALIFIER);
+                    ServerName sn = null;
+                    if(cell != null) {
+                      sn = ServerName.parseVersionedServerName(CellUtil.cloneValue(cell));
+                    }
+                    if (tsm.isTableState(TableName.NAMESPACE_TABLE_NAME,
+                        TableState.State.ENABLED)) {
+                      try {
+                        ClientProtos.ClientService.BlockingInterface rs = conn.getClient(sn);
+                        ClientProtos.GetRequest request =
+                            RequestConverter.buildGetRequest(info.getRegionName(),
+                                new Get(ROW_KEY));
+                        rs.get(null, request);
+                        nsFound.set(true);
+                      } catch(Exception ex) {
+                        LOG.debug("Caught exception while verifying group region", ex);
+                      }
+                    }
+                  }
+                }
+                return true;
+              }
+            };
+            MetaTableAccessor.fullScanRegions(conn, visitor);
+            // if no regions in meta then we have to create the table
+            if (foundRegions.size() < 1 && rootMetaFound && !createSent && nsFound.get()) {
+              groupInfoManager.createGroupTable(masterServices);
+              createSent = true;
+            }
+            LOG.info("Group table: " + RSGROUP_TABLE_NAME + " isOnline: " + found.get()
+                + ", regionCount: " + foundRegions.size() + ", assignCount: "
+                + assignedRegions.size() + ", rootMetaFound: "+rootMetaFound);
+            found.set(found.get() && assignedRegions.size() == foundRegions.size()
+                && foundRegions.size() > 0);
+          } else {
+            LOG.info("Waiting for catalog tables to come online");
+            found.set(false);
+          }
+          if (found.get()) {
+            LOG.debug("With group table online, refreshing cached information.");
+            groupInfoManager.refresh(true);
+            isOnline = true;
+            //flush any inconsistencies between ZK and HTable
+            groupInfoManager.flushConfig(groupInfoManager.rsGroupMap);
+          }
+        } catch(Exception e) {
+          found.set(false);
+          LOG.warn("Failed to perform check", e);
+        }
+        try {
+          Thread.sleep(100);
+        } catch (InterruptedException e) {
+          LOG.info("Sleep interrupted", e);
+        }
+      }
+      return found.get();
+    }
+
+    public boolean isOnline() {
+      return isOnline;
+    }
+
+    private boolean isMasterRunning() {
+      return !masterServices.isAborted() && !masterServices.isStopped();
+    }
+  }
+
+  private void createGroupTable(MasterServices masterServices) throws IOException {
+    HRegionInfo[] newRegions =
+        ModifyRegionUtils.createHRegionInfos(RSGROUP_TABLE_DESC, null);
+    ProcedurePrepareLatch latch = ProcedurePrepareLatch.createLatch();
+    masterServices.getMasterProcedureExecutor().submitProcedure(
+        new CreateTableProcedure(
+            masterServices.getMasterProcedureExecutor().getEnvironment(),
+            RSGROUP_TABLE_DESC,
+            newRegions,
+            latch),
+        HConstants.NO_NONCE,
+        HConstants.NO_NONCE);
+    latch.await();
+    // wait for region to be online
+    int tries = 600;
+    while(masterServices.getAssignmentManager().getRegionStates()
+        .getRegionServerOfRegion(newRegions[0]) == null && tries > 0) {
+      try {
+        Thread.sleep(100);
+      } catch (InterruptedException e) {
+        throw new IOException("Wait interrupted", e);
+      }
+      tries--;
+    }
+    if(tries <= 0) {
+      throw new IOException("Failed to create group table.");
+    }
+  }
+
+  private void multiMutate(List<Mutation> mutations)
+      throws IOException {
+    CoprocessorRpcChannel channel = rsGroupTable.coprocessorService(ROW_KEY);
+    MultiRowMutationProtos.MutateRowsRequest.Builder mmrBuilder
+      = MultiRowMutationProtos.MutateRowsRequest.newBuilder();
+    for (Mutation mutation : mutations) {
+      if (mutation instanceof Put) {
+        mmrBuilder.addMutationRequest(ProtobufUtil.toMutation(
+          ClientProtos.MutationProto.MutationType.PUT, mutation));
+      } else if (mutation instanceof Delete) {
+        mmrBuilder.addMutationRequest(ProtobufUtil.toMutation(
+          ClientProtos.MutationProto.MutationType.DELETE, mutation));
+      } else {
+        throw new DoNotRetryIOException("multiMutate doesn't support "
+          + mutation.getClass().getName());
+      }
+    }
+
+    MultiRowMutationProtos.MultiRowMutationService.BlockingInterface service =
+      MultiRowMutationProtos.MultiRowMutationService.newBlockingStub(channel);
+    try {
+      service.mutateRows(null, mmrBuilder.build());
+    } catch (ServiceException ex) {
+      ProtobufUtil.toIOException(ex);
+    }
+  }
+
+  private void checkGroupName(String groupName) throws ConstraintException {
+    if(!groupName.matches("[a-zA-Z0-9_]+")) {
+      throw new ConstraintException("Group name should only contain alphanumeric characters");
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupSerDe.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupSerDe.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupSerDe.java
new file mode 100644
index 0000000..530db58
--- /dev/null
+++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupSerDe.java
@@ -0,0 +1,88 @@
+/**
+ * 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.
+ */
+
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Lists;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.exceptions.DeserializationException;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
+import org.apache.hadoop.hbase.zookeeper.ZKUtil;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.zookeeper.KeeperException;
+
+//TODO do better encapsulation of SerDe logic from GroupInfoManager and GroupTracker
+public class RSGroupSerDe {
+  private static final Log LOG = LogFactory.getLog(RSGroupSerDe.class);
+
+  public RSGroupSerDe() {
+
+  }
+
+  public List<RSGroupInfo> retrieveGroupList(Table groupTable) throws IOException {
+    List<RSGroupInfo> RSGroupInfoList = Lists.newArrayList();
+    for (Result result : groupTable.getScanner(new Scan())) {
+      RSGroupProtos.RSGroupInfo proto =
+          RSGroupProtos.RSGroupInfo.parseFrom(
+              result.getValue(
+                  RSGroupInfoManager.META_FAMILY_BYTES,
+                  RSGroupInfoManager.META_QUALIFIER_BYTES));
+      RSGroupInfoList.add(ProtobufUtil.toGroupInfo(proto));
+    }
+    return RSGroupInfoList;
+  }
+
+  public List<RSGroupInfo> retrieveGroupList(ZooKeeperWatcher watcher,
+                                             String groupBasePath) throws IOException {
+    List<RSGroupInfo> RSGroupInfoList = Lists.newArrayList();
+    //Overwrite any info stored by table, this takes precedence
+    try {
+      if(ZKUtil.checkExists(watcher, groupBasePath) != -1) {
+        for(String znode: ZKUtil.listChildrenAndWatchForNewChildren(watcher, groupBasePath)) {
+          byte[] data = ZKUtil.getData(watcher, ZKUtil.joinZNode(groupBasePath, znode));
+          if(data.length > 0) {
+            ProtobufUtil.expectPBMagicPrefix(data);
+            ByteArrayInputStream bis = new ByteArrayInputStream(
+                data, ProtobufUtil.lengthOfPBMagic(), data.length);
+            RSGroupInfoList.add(ProtobufUtil.toGroupInfo(RSGroupProtos.RSGroupInfo.parseFrom(bis)));
+          }
+        }
+        LOG.debug("Read ZK GroupInfo count:" + RSGroupInfoList.size());
+      }
+    } catch (KeeperException e) {
+      throw new IOException("Failed to read rsGroupZNode",e);
+    } catch (DeserializationException e) {
+      throw new IOException("Failed to read rsGroupZNode",e);
+    } catch (InterruptedException e) {
+      throw new IOException("Failed to read rsGroupZNode",e);
+    }
+    return RSGroupInfoList;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupableBalancer.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupableBalancer.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupableBalancer.java
new file mode 100644
index 0000000..ec86dda
--- /dev/null
+++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupableBalancer.java
@@ -0,0 +1,29 @@
+/**
+ * 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.
+ */
+
+package org.apache.hadoop.hbase.rsgroup;
+
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.master.LoadBalancer;
+
+
+@InterfaceAudience.Private
+public interface RSGroupableBalancer extends LoadBalancer {
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/master/balancer/TestRSGroupBasedLoadBalancer.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/master/balancer/TestRSGroupBasedLoadBalancer.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/master/balancer/TestRSGroupBasedLoadBalancer.java
new file mode 100644
index 0000000..1539f73
--- /dev/null
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/master/balancer/TestRSGroupBasedLoadBalancer.java
@@ -0,0 +1,574 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.hbase.master.balancer;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.net.HostAndPort;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableDescriptors;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer;
+import org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
+import org.apache.hadoop.hbase.rsgroup.RSGroupInfoManager;
+import org.apache.hadoop.hbase.master.AssignmentManager;
+import org.apache.hadoop.hbase.master.HMaster;
+import org.apache.hadoop.hbase.master.MasterServices;
+import org.apache.hadoop.hbase.master.RegionPlan;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+//TODO use stochastic based load balancer instead
+@Category(SmallTests.class)
+public class TestRSGroupBasedLoadBalancer {
+
+  private static final Log LOG = LogFactory.getLog(TestRSGroupBasedLoadBalancer.class);
+  private static RSGroupBasedLoadBalancer loadBalancer;
+  private static SecureRandom rand;
+
+  static String[]  groups = new String[] { RSGroupInfo.DEFAULT_GROUP, "dg2", "dg3",
+      "dg4" };
+  static TableName[] tables =
+      new TableName[] { TableName.valueOf("dt1"),
+          TableName.valueOf("dt2"),
+          TableName.valueOf("dt3"),
+          TableName.valueOf("dt4")};
+  static List<ServerName> servers;
+  static Map<String, RSGroupInfo> groupMap;
+  static Map<TableName, String> tableMap;
+  static List<HTableDescriptor> tableDescs;
+  int[] regionAssignment = new int[] { 2, 5, 7, 10, 4, 3, 1 };
+  static int regionId = 0;
+
+  @BeforeClass
+  public static void beforeAllTests() throws Exception {
+    rand = new SecureRandom();
+    servers = generateServers(7);
+    groupMap = constructGroupInfo(servers, groups);
+    tableMap = new HashMap<TableName, String>();
+    tableDescs = constructTableDesc();
+    Configuration conf = HBaseConfiguration.create();
+    conf.set("hbase.regions.slop", "0");
+    conf.set("hbase.group.grouploadbalancer.class", SimpleLoadBalancer.class.getCanonicalName());
+    loadBalancer = new RSGroupBasedLoadBalancer(getMockedGroupInfoManager());
+    loadBalancer.setMasterServices(getMockedMaster());
+    loadBalancer.setConf(conf);
+    loadBalancer.initialize();
+  }
+
+  /**
+   * Test the load balancing algorithm.
+   *
+   * Invariant is that all servers of the group should be hosting either floor(average) or
+   * ceiling(average)
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testBalanceCluster() throws Exception {
+    Map<ServerName, List<HRegionInfo>> servers = mockClusterServers();
+    ArrayListMultimap<String, ServerAndLoad> list = convertToGroupBasedMap(servers);
+    LOG.info("Mock Cluster :  " + printStats(list));
+    List<RegionPlan> plans = loadBalancer.balanceCluster(servers);
+    ArrayListMultimap<String, ServerAndLoad> balancedCluster = reconcile(
+        list, plans);
+    LOG.info("Mock Balance : " + printStats(balancedCluster));
+    assertClusterAsBalanced(balancedCluster);
+  }
+
+  /**
+   * Invariant is that all servers of a group have load between floor(avg) and
+   * ceiling(avg) number of regions.
+   */
+  private void assertClusterAsBalanced(
+      ArrayListMultimap<String, ServerAndLoad> groupLoadMap) {
+    for (String gName : groupLoadMap.keySet()) {
+      List<ServerAndLoad> groupLoad = groupLoadMap.get(gName);
+      int numServers = groupLoad.size();
+      int numRegions = 0;
+      int maxRegions = 0;
+      int minRegions = Integer.MAX_VALUE;
+      for (ServerAndLoad server : groupLoad) {
+        int nr = server.getLoad();
+        if (nr > maxRegions) {
+          maxRegions = nr;
+        }
+        if (nr < minRegions) {
+          minRegions = nr;
+        }
+        numRegions += nr;
+      }
+      if (maxRegions - minRegions < 2) {
+        // less than 2 between max and min, can't balance
+        return;
+      }
+      int min = numRegions / numServers;
+      int max = numRegions % numServers == 0 ? min : min + 1;
+
+      for (ServerAndLoad server : groupLoad) {
+        assertTrue(server.getLoad() <= max);
+        assertTrue(server.getLoad() >= min);
+      }
+    }
+  }
+
+  /**
+   * All regions have an assignment.
+   *
+   * @param regions
+   * @param servers
+   * @param assignments
+   * @throws java.io.IOException
+   * @throws java.io.FileNotFoundException
+   */
+  private void assertImmediateAssignment(List<HRegionInfo> regions,
+                                         List<ServerName> servers,
+                                         Map<HRegionInfo, ServerName> assignments)
+      throws IOException {
+    for (HRegionInfo region : regions) {
+      assertTrue(assignments.containsKey(region));
+      ServerName server = assignments.get(region);
+      TableName tableName = region.getTable();
+
+      String groupName =
+          getMockedGroupInfoManager().getRSGroupOfTable(tableName);
+      assertTrue(StringUtils.isNotEmpty(groupName));
+      RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(groupName);
+      assertTrue("Region is not correctly assigned to group servers.",
+          gInfo.containsServer(server.getHostPort()));
+    }
+  }
+
+  /**
+   * Tests the bulk assignment used during cluster startup.
+   *
+   * Round-robin. Should yield a balanced cluster so same invariant as the
+   * load balancer holds, all servers holding either floor(avg) or
+   * ceiling(avg).
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testBulkAssignment() throws Exception {
+    List<HRegionInfo> regions = randomRegions(25);
+    Map<ServerName, List<HRegionInfo>> assignments = loadBalancer
+        .roundRobinAssignment(regions, servers);
+    //test empty region/servers scenario
+    //this should not throw an NPE
+    loadBalancer.roundRobinAssignment(regions,
+        Collections.EMPTY_LIST);
+    //test regular scenario
+    assertTrue(assignments.keySet().size() == servers.size());
+    for (ServerName sn : assignments.keySet()) {
+      List<HRegionInfo> regionAssigned = assignments.get(sn);
+      for (HRegionInfo region : regionAssigned) {
+        TableName tableName = region.getTable();
+        String groupName =
+            getMockedGroupInfoManager().getRSGroupOfTable(tableName);
+        assertTrue(StringUtils.isNotEmpty(groupName));
+        RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(
+            groupName);
+        assertTrue(
+            "Region is not correctly assigned to group servers.",
+            gInfo.containsServer(sn.getHostPort()));
+      }
+    }
+    ArrayListMultimap<String, ServerAndLoad> loadMap = convertToGroupBasedMap(assignments);
+    assertClusterAsBalanced(loadMap);
+  }
+
+  /**
+   * Test the cluster startup bulk assignment which attempts to retain
+   * assignment info.
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testRetainAssignment() throws Exception {
+    // Test simple case where all same servers are there
+    Map<ServerName, List<HRegionInfo>> currentAssignments = mockClusterServers();
+    Map<HRegionInfo, ServerName> inputForTest = new HashMap<HRegionInfo, ServerName>();
+    for (ServerName sn : currentAssignments.keySet()) {
+      for (HRegionInfo region : currentAssignments.get(sn)) {
+        inputForTest.put(region, sn);
+      }
+    }
+    //verify region->null server assignment is handled
+    inputForTest.put(randomRegions(1).get(0), null);
+    Map<ServerName, List<HRegionInfo>> newAssignment = loadBalancer
+        .retainAssignment(inputForTest, servers);
+    assertRetainedAssignment(inputForTest, servers, newAssignment);
+  }
+
+  /**
+   * Asserts a valid retained assignment plan.
+   * <p>
+   * Must meet the following conditions:
+   * <ul>
+   * <li>Every input region has an assignment, and to an online server
+   * <li>If a region had an existing assignment to a server with the same
+   * address a a currently online server, it will be assigned to it
+   * </ul>
+   *
+   * @param existing
+   * @param assignment
+   * @throws java.io.IOException
+   * @throws java.io.FileNotFoundException
+   */
+  private void assertRetainedAssignment(
+      Map<HRegionInfo, ServerName> existing, List<ServerName> servers,
+      Map<ServerName, List<HRegionInfo>> assignment)
+      throws FileNotFoundException, IOException {
+    // Verify condition 1, every region assigned, and to online server
+    Set<ServerName> onlineServerSet = new TreeSet<ServerName>(servers);
+    Set<HRegionInfo> assignedRegions = new TreeSet<HRegionInfo>();
+    for (Map.Entry<ServerName, List<HRegionInfo>> a : assignment.entrySet()) {
+      assertTrue(
+          "Region assigned to server that was not listed as online",
+          onlineServerSet.contains(a.getKey()));
+      for (HRegionInfo r : a.getValue())
+        assignedRegions.add(r);
+    }
+    assertEquals(existing.size(), assignedRegions.size());
+
+    // Verify condition 2, every region must be assigned to correct server.
+    Set<String> onlineHostNames = new TreeSet<String>();
+    for (ServerName s : servers) {
+      onlineHostNames.add(s.getHostname());
+    }
+
+    for (Map.Entry<ServerName, List<HRegionInfo>> a : assignment.entrySet()) {
+      ServerName currentServer = a.getKey();
+      for (HRegionInfo r : a.getValue()) {
+        ServerName oldAssignedServer = existing.get(r);
+        TableName tableName = r.getTable();
+        String groupName =
+            getMockedGroupInfoManager().getRSGroupOfTable(tableName);
+        assertTrue(StringUtils.isNotEmpty(groupName));
+        RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(
+            groupName);
+        assertTrue(
+            "Region is not correctly assigned to group servers.",
+            gInfo.containsServer(currentServer.getHostPort()));
+        if (oldAssignedServer != null
+            && onlineHostNames.contains(oldAssignedServer
+            .getHostname())) {
+          // this region was previously assigned somewhere, and that
+          // host is still around, then the host must have been is a
+          // different group.
+          if (!oldAssignedServer.getHostPort().equals(currentServer.getHostPort())) {
+            assertFalse(gInfo.containsServer(oldAssignedServer.getHostPort()));
+          }
+        }
+      }
+    }
+  }
+
+  private String printStats(
+      ArrayListMultimap<String, ServerAndLoad> groupBasedLoad) {
+    StringBuffer sb = new StringBuffer();
+    sb.append("\n");
+    for (String groupName : groupBasedLoad.keySet()) {
+      sb.append("Stats for group: " + groupName);
+      sb.append("\n");
+      sb.append(groupMap.get(groupName).getServers());
+      sb.append("\n");
+      List<ServerAndLoad> groupLoad = groupBasedLoad.get(groupName);
+      int numServers = groupLoad.size();
+      int totalRegions = 0;
+      sb.append("Per Server Load: \n");
+      for (ServerAndLoad sLoad : groupLoad) {
+        sb.append("Server :" + sLoad.getServerName() + " Load : "
+            + sLoad.getLoad() + "\n");
+        totalRegions += sLoad.getLoad();
+      }
+      sb.append(" Group Statistics : \n");
+      float average = (float) totalRegions / numServers;
+      int max = (int) Math.ceil(average);
+      int min = (int) Math.floor(average);
+      sb.append("[srvr=" + numServers + " rgns=" + totalRegions + " avg="
+          + average + " max=" + max + " min=" + min + "]");
+      sb.append("\n");
+      sb.append("===============================");
+      sb.append("\n");
+    }
+    return sb.toString();
+  }
+
+  private ArrayListMultimap<String, ServerAndLoad> convertToGroupBasedMap(
+      final Map<ServerName, List<HRegionInfo>> serversMap) throws IOException {
+    ArrayListMultimap<String, ServerAndLoad> loadMap = ArrayListMultimap
+        .create();
+    for (RSGroupInfo gInfo : getMockedGroupInfoManager().listRSGroups()) {
+      Set<HostAndPort> groupServers = gInfo.getServers();
+      for (HostAndPort hostPort : groupServers) {
+        ServerName actual = null;
+        for(ServerName entry: servers) {
+          if(entry.getHostPort().equals(hostPort)) {
+            actual = entry;
+            break;
+          }
+        }
+        List<HRegionInfo> regions = serversMap.get(actual);
+        assertTrue("No load for " + actual, regions != null);
+        loadMap.put(gInfo.getName(),
+            new ServerAndLoad(actual, regions.size()));
+      }
+    }
+    return loadMap;
+  }
+
+  private ArrayListMultimap<String, ServerAndLoad> reconcile(
+      ArrayListMultimap<String, ServerAndLoad> previousLoad,
+      List<RegionPlan> plans) {
+    ArrayListMultimap<String, ServerAndLoad> result = ArrayListMultimap
+        .create();
+    result.putAll(previousLoad);
+    if (plans != null) {
+      for (RegionPlan plan : plans) {
+        ServerName source = plan.getSource();
+        updateLoad(result, source, -1);
+        ServerName destination = plan.getDestination();
+        updateLoad(result, destination, +1);
+      }
+    }
+    return result;
+  }
+
+  private void updateLoad(
+      ArrayListMultimap<String, ServerAndLoad> previousLoad,
+      final ServerName sn, final int diff) {
+    for (String groupName : previousLoad.keySet()) {
+      ServerAndLoad newSAL = null;
+      ServerAndLoad oldSAL = null;
+      for (ServerAndLoad sal : previousLoad.get(groupName)) {
+        if (ServerName.isSameHostnameAndPort(sn, sal.getServerName())) {
+          oldSAL = sal;
+          newSAL = new ServerAndLoad(sn, sal.getLoad() + diff);
+          break;
+        }
+      }
+      if (newSAL != null) {
+        previousLoad.remove(groupName, oldSAL);
+        previousLoad.put(groupName, newSAL);
+        break;
+      }
+    }
+  }
+
+  private Map<ServerName, List<HRegionInfo>> mockClusterServers() throws IOException {
+    assertTrue(servers.size() == regionAssignment.length);
+    Map<ServerName, List<HRegionInfo>> assignment = new TreeMap<ServerName, List<HRegionInfo>>();
+    for (int i = 0; i < servers.size(); i++) {
+      int numRegions = regionAssignment[i];
+      List<HRegionInfo> regions = assignedRegions(numRegions, servers.get(i));
+      assignment.put(servers.get(i), regions);
+    }
+    return assignment;
+  }
+
+  /**
+   * Generate a list of regions evenly distributed between the tables.
+   *
+   * @param numRegions The number of regions to be generated.
+   * @return List of HRegionInfo.
+   */
+  private List<HRegionInfo> randomRegions(int numRegions) {
+    List<HRegionInfo> regions = new ArrayList<HRegionInfo>(numRegions);
+    byte[] start = new byte[16];
+    byte[] end = new byte[16];
+    rand.nextBytes(start);
+    rand.nextBytes(end);
+    int regionIdx = rand.nextInt(tables.length);
+    for (int i = 0; i < numRegions; i++) {
+      Bytes.putInt(start, 0, numRegions << 1);
+      Bytes.putInt(end, 0, (numRegions << 1) + 1);
+      int tableIndex = (i + regionIdx) % tables.length;
+      HRegionInfo hri = new HRegionInfo(
+          tables[tableIndex], start, end, false, regionId++);
+      regions.add(hri);
+    }
+    return regions;
+  }
+
+  /**
+   * Generate assigned regions to a given server using group information.
+   *
+   * @param numRegions the num regions to generate
+   * @param sn the servername
+   * @return the list of regions
+   * @throws java.io.IOException Signals that an I/O exception has occurred.
+   */
+  private List<HRegionInfo> assignedRegions(int numRegions, ServerName sn) throws IOException {
+    List<HRegionInfo> regions = new ArrayList<HRegionInfo>(numRegions);
+    byte[] start = new byte[16];
+    byte[] end = new byte[16];
+    Bytes.putInt(start, 0, numRegions << 1);
+    Bytes.putInt(end, 0, (numRegions << 1) + 1);
+    for (int i = 0; i < numRegions; i++) {
+      TableName tableName = getTableName(sn);
+      HRegionInfo hri = new HRegionInfo(
+          tableName, start, end, false,
+          regionId++);
+      regions.add(hri);
+    }
+    return regions;
+  }
+
+  private static List<ServerName> generateServers(int numServers) {
+    List<ServerName> servers = new ArrayList<ServerName>(numServers);
+    for (int i = 0; i < numServers; i++) {
+      String host = "server" + rand.nextInt(100000);
+      int port = rand.nextInt(60000);
+      servers.add(ServerName.valueOf(host, port, -1));
+    }
+    return servers;
+  }
+
+  /**
+   * Construct group info, with each group having at least one server.
+   *
+   * @param servers the servers
+   * @param groups the groups
+   * @return the map
+   */
+  private static Map<String, RSGroupInfo> constructGroupInfo(
+      List<ServerName> servers, String[] groups) {
+    assertTrue(servers != null);
+    assertTrue(servers.size() >= groups.length);
+    int index = 0;
+    Map<String, RSGroupInfo> groupMap = new HashMap<String, RSGroupInfo>();
+    for (String grpName : groups) {
+      RSGroupInfo RSGroupInfo = new RSGroupInfo(grpName);
+      RSGroupInfo.addServer(servers.get(index).getHostPort());
+      groupMap.put(grpName, RSGroupInfo);
+      index++;
+    }
+    while (index < servers.size()) {
+      int grpIndex = rand.nextInt(groups.length);
+      groupMap.get(groups[grpIndex]).addServer(
+          servers.get(index).getHostPort());
+      index++;
+    }
+    return groupMap;
+  }
+
+  /**
+   * Construct table descriptors evenly distributed between the groups.
+   *
+   * @return the list
+   */
+  private static List<HTableDescriptor> constructTableDesc() {
+    List<HTableDescriptor> tds = Lists.newArrayList();
+    int index = rand.nextInt(groups.length);
+    for (int i = 0; i < tables.length; i++) {
+      HTableDescriptor htd = new HTableDescriptor(tables[i]);
+      int grpIndex = (i + index) % groups.length ;
+      String groupName = groups[grpIndex];
+      tableMap.put(tables[i], groupName);
+      tds.add(htd);
+    }
+    return tds;
+  }
+
+  private static MasterServices getMockedMaster() throws IOException {
+    TableDescriptors tds = Mockito.mock(TableDescriptors.class);
+    Mockito.when(tds.get(tables[0])).thenReturn(tableDescs.get(0));
+    Mockito.when(tds.get(tables[1])).thenReturn(tableDescs.get(1));
+    Mockito.when(tds.get(tables[2])).thenReturn(tableDescs.get(2));
+    Mockito.when(tds.get(tables[3])).thenReturn(tableDescs.get(3));
+    MasterServices services = Mockito.mock(HMaster.class);
+    Mockito.when(services.getTableDescriptors()).thenReturn(tds);
+    AssignmentManager am = Mockito.mock(AssignmentManager.class);
+    Mockito.when(services.getAssignmentManager()).thenReturn(am);
+    return services;
+  }
+
+  private static RSGroupInfoManager getMockedGroupInfoManager() throws IOException {
+    RSGroupInfoManager gm = Mockito.mock(RSGroupInfoManager.class);
+    Mockito.when(gm.getRSGroup(groups[0])).thenReturn(
+        groupMap.get(groups[0]));
+    Mockito.when(gm.getRSGroup(groups[1])).thenReturn(
+        groupMap.get(groups[1]));
+    Mockito.when(gm.getRSGroup(groups[2])).thenReturn(
+        groupMap.get(groups[2]));
+    Mockito.when(gm.getRSGroup(groups[3])).thenReturn(
+        groupMap.get(groups[3]));
+    Mockito.when(gm.listRSGroups()).thenReturn(
+        Lists.newLinkedList(groupMap.values()));
+    Mockito.when(gm.isOnline()).thenReturn(true);
+    Mockito.when(gm.getRSGroupOfTable(Mockito.any(TableName.class)))
+        .thenAnswer(new Answer<String>() {
+          @Override
+          public String answer(InvocationOnMock invocation) throws Throwable {
+            return tableMap.get(invocation.getArguments()[0]);
+          }
+        });
+    return gm;
+  }
+
+  private TableName getTableName(ServerName sn) throws IOException {
+    TableName tableName = null;
+    RSGroupInfoManager gm = getMockedGroupInfoManager();
+    RSGroupInfo groupOfServer = null;
+    for(RSGroupInfo gInfo : gm.listRSGroups()){
+      if(gInfo.containsServer(sn.getHostPort())){
+        groupOfServer = gInfo;
+        break;
+      }
+    }
+
+    for(HTableDescriptor desc : tableDescs){
+      if(gm.getRSGroupOfTable(desc.getTableName()).endsWith(groupOfServer.getName())){
+        tableName = desc.getTableName();
+      }
+    }
+    return tableName;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java
new file mode 100644
index 0000000..34add63
--- /dev/null
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java
@@ -0,0 +1,287 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import com.google.common.net.HostAndPort;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.MiniHBaseCluster;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.Waiter;
+import org.apache.hadoop.hbase.Waiter.Predicate;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.master.HMaster;
+import org.apache.hadoop.hbase.master.MasterServices;
+import org.apache.hadoop.hbase.master.ServerManager;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@Category({MediumTests.class})
+public class TestRSGroups extends TestRSGroupsBase {
+  protected static final Log LOG = LogFactory.getLog(TestRSGroups.class);
+  private static HMaster master;
+  private static boolean init = false;
+  private static RSGroupAdminEndpoint RSGroupAdminEndpoint;
+
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    TEST_UTIL = new HBaseTestingUtility();
+    TEST_UTIL.getConfiguration().set(
+        HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
+        RSGroupBasedLoadBalancer.class.getName());
+    TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
+        RSGroupAdminEndpoint.class.getName());
+    TEST_UTIL.getConfiguration().setBoolean(
+        HConstants.ZOOKEEPER_USEMULTI,
+        true);
+    TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE);
+    TEST_UTIL.getConfiguration().set(
+        ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
+        ""+NUM_SLAVES_BASE);
+
+    admin = TEST_UTIL.getHBaseAdmin();
+    cluster = TEST_UTIL.getHBaseCluster();
+    master = ((MiniHBaseCluster)cluster).getMaster();
+
+    //wait for balancer to come online
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return master.isInitialized() &&
+            ((RSGroupBasedLoadBalancer) master.getLoadBalancer()).isOnline();
+      }
+    });
+    admin.setBalancerRunning(false,true);
+    rsGroupAdmin = new VerifyingRSGroupAdminClient(rsGroupAdmin.newClient(TEST_UTIL.getConnection()),
+        TEST_UTIL.getConfiguration());
+    RSGroupAdminEndpoint =
+        master.getMasterCoprocessorHost().findCoprocessors(RSGroupAdminEndpoint.class).get(0);
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+  @Before
+  public void beforeMethod() throws Exception {
+    if(!init) {
+      init = true;
+      afterMethod();
+    }
+
+  }
+
+  @After
+  public void afterMethod() throws Exception {
+    deleteTableIfNecessary();
+    deleteNamespaceIfNecessary();
+    deleteGroups();
+
+    int missing = NUM_SLAVES_BASE - getNumServers();
+    LOG.info("Restoring servers: "+missing);
+    for(int i=0; i<missing; i++) {
+      ((MiniHBaseCluster)cluster).startRegionServer();
+    }
+
+    rsGroupAdmin.addRSGroup("master");
+    ServerName masterServerName =
+        ((MiniHBaseCluster)cluster).getMaster().getServerName();
+
+    try {
+      rsGroupAdmin.moveServers(
+          Sets.newHashSet(masterServerName.getHostPort()),
+          "master");
+    } catch (Exception ex) {
+      // ignore
+    }
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        LOG.info("Waiting for cleanup to finish " + rsGroupAdmin.listRSGroups());
+        //Might be greater since moving servers back to default
+        //is after starting a server
+
+        return rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()
+            == NUM_SLAVES_BASE;
+      }
+    });
+  }
+
+  @Test
+  public void testBasicStartUp() throws IOException {
+    RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
+    assertEquals(4, defaultInfo.getServers().size());
+    // Assignment of root and meta regions.
+    int count = master.getAssignmentManager().getRegionStates().getRegionAssignments().size();
+    //3 meta,namespace, group
+    assertEquals(3, count);
+  }
+
+  @Test
+  public void testNamespaceCreateAndAssign() throws Exception {
+    LOG.info("testNamespaceCreateAndAssign");
+    String nsName = tablePrefix+"_foo";
+    final TableName tableName = TableName.valueOf(nsName, tablePrefix + "_testCreateAndAssign");
+    RSGroupInfo appInfo = addGroup(rsGroupAdmin, "appInfo", 1);
+    admin.createNamespace(NamespaceDescriptor.create(nsName)
+        .addConfiguration(RSGroupInfo.NAMESPACEDESC_PROP_GROUP, "appInfo").build());
+    final HTableDescriptor desc = new HTableDescriptor(tableName);
+    desc.addFamily(new HColumnDescriptor("f"));
+    admin.createTable(desc);
+    //wait for created table to be assigned
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return getTableRegionMap().get(desc.getTableName()) != null;
+      }
+    });
+    ServerName targetServer =
+        ServerName.parseServerName(appInfo.getServers().iterator().next().toString());
+    AdminProtos.AdminService.BlockingInterface rs = admin.getConnection().getAdmin(targetServer);
+    //verify it was assigned to the right group
+    Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(rs).size());
+  }
+
+  @Test
+  public void testDefaultNamespaceCreateAndAssign() throws Exception {
+    LOG.info("testDefaultNamespaceCreateAndAssign");
+    final byte[] tableName = Bytes.toBytes(tablePrefix + "_testCreateAndAssign");
+    admin.modifyNamespace(NamespaceDescriptor.create("default")
+        .addConfiguration(RSGroupInfo.NAMESPACEDESC_PROP_GROUP, "default").build());
+    final HTableDescriptor desc = new HTableDescriptor(tableName);
+    desc.addFamily(new HColumnDescriptor("f"));
+    admin.createTable(desc);
+    //wait for created table to be assigned
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return getTableRegionMap().get(desc.getTableName()) != null;
+      }
+    });
+  }
+
+  @Test
+  public void testNamespaceConstraint() throws Exception {
+    String nsName = tablePrefix+"_foo";
+    String groupName = tablePrefix+"_foo";
+    LOG.info("testNamespaceConstraint");
+    rsGroupAdmin.addRSGroup(groupName);
+    admin.createNamespace(NamespaceDescriptor.create(nsName)
+        .addConfiguration(RSGroupInfo.NAMESPACEDESC_PROP_GROUP, groupName)
+        .build());
+    //test removing a referenced group
+    try {
+      rsGroupAdmin.removeRSGroup(groupName);
+      fail("Expected a constraint exception");
+    } catch (IOException ex) {
+    }
+    //test modify group
+    //changing with the same name is fine
+    admin.modifyNamespace(
+        NamespaceDescriptor.create(nsName)
+          .addConfiguration(RSGroupInfo.NAMESPACEDESC_PROP_GROUP, groupName)
+          .build());
+    String anotherGroup = tablePrefix+"_anotherGroup";
+    rsGroupAdmin.addRSGroup(anotherGroup);
+    //test add non-existent group
+    admin.deleteNamespace(nsName);
+    rsGroupAdmin.removeRSGroup(groupName);
+    try {
+      admin.createNamespace(NamespaceDescriptor.create(nsName)
+          .addConfiguration(RSGroupInfo.NAMESPACEDESC_PROP_GROUP, "foo")
+          .build());
+      fail("Expected a constraint exception");
+    } catch (IOException ex) {
+    }
+  }
+
+  @Test
+  public void testGroupInfoMultiAccessing() throws Exception {
+    RSGroupInfoManager manager = RSGroupAdminEndpoint.getGroupInfoManager();
+    final RSGroupInfo defaultGroup = manager.getRSGroup("default");
+    // getRSGroup updates default group's server list
+    // this process must not affect other threads iterating the list
+    Iterator<HostAndPort> it = defaultGroup.getServers().iterator();
+    manager.getRSGroup("default");
+    it.next();
+  }
+
+  @Test
+  public void testMisplacedRegions() throws Exception {
+    final TableName tableName = TableName.valueOf(tablePrefix+"_testMisplacedRegions");
+    LOG.info("testMisplacedRegions");
+
+    final RSGroupInfo RSGroupInfo = addGroup(rsGroupAdmin, "testMisplacedRegions", 1);
+
+    TEST_UTIL.createMultiRegionTable(tableName, new byte[]{'f'}, 15);
+    TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
+
+    RSGroupAdminEndpoint.getGroupInfoManager()
+        .moveTables(Sets.newHashSet(tableName), RSGroupInfo.getName());
+
+    assertTrue(rsGroupAdmin.balanceRSGroup(RSGroupInfo.getName()));
+
+    TEST_UTIL.waitFor(60000, new Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        ServerName serverName =
+            ServerName.valueOf(RSGroupInfo.getServers().iterator().next().toString(), 1);
+        return admin.getConnection().getAdmin()
+            .getOnlineRegions(serverName).size() == 15;
+      }
+    });
+  }
+}


[5/7] hbase git commit: HBASE-6721 RegionServer Group based Assignment (Francis Liu)

Posted by en...@apache.org.
http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RSGroupProtos.java
----------------------------------------------------------------------
diff --git a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RSGroupProtos.java b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RSGroupProtos.java
new file mode 100644
index 0000000..979f762
--- /dev/null
+++ b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/RSGroupProtos.java
@@ -0,0 +1,1331 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: RSGroup.proto
+
+package org.apache.hadoop.hbase.protobuf.generated;
+
+public final class RSGroupProtos {
+  private RSGroupProtos() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+  }
+  public interface RSGroupInfoOrBuilder
+      extends com.google.protobuf.MessageOrBuilder {
+
+    // required string name = 1;
+    /**
+     * <code>required string name = 1;</code>
+     */
+    boolean hasName();
+    /**
+     * <code>required string name = 1;</code>
+     */
+    java.lang.String getName();
+    /**
+     * <code>required string name = 1;</code>
+     */
+    com.google.protobuf.ByteString
+        getNameBytes();
+
+    // repeated .hbase.pb.ServerName servers = 4;
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName> 
+        getServersList();
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName getServers(int index);
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    int getServersCount();
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    java.util.List<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder> 
+        getServersOrBuilderList();
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder getServersOrBuilder(
+        int index);
+
+    // repeated .hbase.pb.TableName tables = 3;
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> 
+        getTablesList();
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName getTables(int index);
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    int getTablesCount();
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    java.util.List<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> 
+        getTablesOrBuilderList();
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder getTablesOrBuilder(
+        int index);
+  }
+  /**
+   * Protobuf type {@code hbase.pb.RSGroupInfo}
+   */
+  public static final class RSGroupInfo extends
+      com.google.protobuf.GeneratedMessage
+      implements RSGroupInfoOrBuilder {
+    // Use RSGroupInfo.newBuilder() to construct.
+    private RSGroupInfo(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
+      super(builder);
+      this.unknownFields = builder.getUnknownFields();
+    }
+    private RSGroupInfo(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+    private static final RSGroupInfo defaultInstance;
+    public static RSGroupInfo getDefaultInstance() {
+      return defaultInstance;
+    }
+
+    public RSGroupInfo getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+
+    private final com.google.protobuf.UnknownFieldSet unknownFields;
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+        getUnknownFields() {
+      return this.unknownFields;
+    }
+    private RSGroupInfo(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      initFields();
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!parseUnknownField(input, unknownFields,
+                                     extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+            case 10: {
+              bitField0_ |= 0x00000001;
+              name_ = input.readBytes();
+              break;
+            }
+            case 26: {
+              if (!((mutable_bitField0_ & 0x00000004) == 0x00000004)) {
+                tables_ = new java.util.ArrayList<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName>();
+                mutable_bitField0_ |= 0x00000004;
+              }
+              tables_.add(input.readMessage(org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.PARSER, extensionRegistry));
+              break;
+            }
+            case 34: {
+              if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) {
+                servers_ = new java.util.ArrayList<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName>();
+                mutable_bitField0_ |= 0x00000002;
+              }
+              servers_.add(input.readMessage(org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.PARSER, extensionRegistry));
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e.getMessage()).setUnfinishedMessage(this);
+      } finally {
+        if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) {
+          tables_ = java.util.Collections.unmodifiableList(tables_);
+        }
+        if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) {
+          servers_ = java.util.Collections.unmodifiableList(servers_);
+        }
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.internal_static_hbase_pb_RSGroupInfo_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.internal_static_hbase_pb_RSGroupInfo_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.Builder.class);
+    }
+
+    public static com.google.protobuf.Parser<RSGroupInfo> PARSER =
+        new com.google.protobuf.AbstractParser<RSGroupInfo>() {
+      public RSGroupInfo parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new RSGroupInfo(input, extensionRegistry);
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<RSGroupInfo> getParserForType() {
+      return PARSER;
+    }
+
+    private int bitField0_;
+    // required string name = 1;
+    public static final int NAME_FIELD_NUMBER = 1;
+    private java.lang.Object name_;
+    /**
+     * <code>required string name = 1;</code>
+     */
+    public boolean hasName() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>required string name = 1;</code>
+     */
+    public java.lang.String getName() {
+      java.lang.Object ref = name_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        if (bs.isValidUtf8()) {
+          name_ = s;
+        }
+        return s;
+      }
+    }
+    /**
+     * <code>required string name = 1;</code>
+     */
+    public com.google.protobuf.ByteString
+        getNameBytes() {
+      java.lang.Object ref = name_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        name_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+
+    // repeated .hbase.pb.ServerName servers = 4;
+    public static final int SERVERS_FIELD_NUMBER = 4;
+    private java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName> servers_;
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    public java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName> getServersList() {
+      return servers_;
+    }
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    public java.util.List<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder> 
+        getServersOrBuilderList() {
+      return servers_;
+    }
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    public int getServersCount() {
+      return servers_.size();
+    }
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName getServers(int index) {
+      return servers_.get(index);
+    }
+    /**
+     * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+     */
+    public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder getServersOrBuilder(
+        int index) {
+      return servers_.get(index);
+    }
+
+    // repeated .hbase.pb.TableName tables = 3;
+    public static final int TABLES_FIELD_NUMBER = 3;
+    private java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> tables_;
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    public java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> getTablesList() {
+      return tables_;
+    }
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    public java.util.List<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> 
+        getTablesOrBuilderList() {
+      return tables_;
+    }
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    public int getTablesCount() {
+      return tables_.size();
+    }
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName getTables(int index) {
+      return tables_.get(index);
+    }
+    /**
+     * <code>repeated .hbase.pb.TableName tables = 3;</code>
+     */
+    public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder getTablesOrBuilder(
+        int index) {
+      return tables_.get(index);
+    }
+
+    private void initFields() {
+      name_ = "";
+      servers_ = java.util.Collections.emptyList();
+      tables_ = java.util.Collections.emptyList();
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+
+      if (!hasName()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      for (int i = 0; i < getServersCount(); i++) {
+        if (!getServers(i).isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      for (int i = 0; i < getTablesCount(); i++) {
+        if (!getTables(i).isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeBytes(1, getNameBytes());
+      }
+      for (int i = 0; i < tables_.size(); i++) {
+        output.writeMessage(3, tables_.get(i));
+      }
+      for (int i = 0; i < servers_.size(); i++) {
+        output.writeMessage(4, servers_.get(i));
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(1, getNameBytes());
+      }
+      for (int i = 0; i < tables_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(3, tables_.get(i));
+      }
+      for (int i = 0; i < servers_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(4, servers_.get(i));
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSerializedSize = size;
+      return size;
+    }
+
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo)) {
+        return super.equals(obj);
+      }
+      org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo other = (org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo) obj;
+
+      boolean result = true;
+      result = result && (hasName() == other.hasName());
+      if (hasName()) {
+        result = result && getName()
+            .equals(other.getName());
+      }
+      result = result && getServersList()
+          .equals(other.getServersList());
+      result = result && getTablesList()
+          .equals(other.getTablesList());
+      result = result &&
+          getUnknownFields().equals(other.getUnknownFields());
+      return result;
+    }
+
+    private int memoizedHashCode = 0;
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (hasName()) {
+        hash = (37 * hash) + NAME_FIELD_NUMBER;
+        hash = (53 * hash) + getName().hashCode();
+      }
+      if (getServersCount() > 0) {
+        hash = (37 * hash) + SERVERS_FIELD_NUMBER;
+        hash = (53 * hash) + getServersList().hashCode();
+      }
+      if (getTablesCount() > 0) {
+        hash = (37 * hash) + TABLES_FIELD_NUMBER;
+        hash = (53 * hash) + getTablesList().hashCode();
+      }
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code hbase.pb.RSGroupInfo}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder>
+       implements org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfoOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.internal_static_hbase_pb_RSGroupInfo_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.internal_static_hbase_pb_RSGroupInfo_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.class, org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.Builder.class);
+      }
+
+      // Construct using org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+          getServersFieldBuilder();
+          getTablesFieldBuilder();
+        }
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+
+      public Builder clear() {
+        super.clear();
+        name_ = "";
+        bitField0_ = (bitField0_ & ~0x00000001);
+        if (serversBuilder_ == null) {
+          servers_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000002);
+        } else {
+          serversBuilder_.clear();
+        }
+        if (tablesBuilder_ == null) {
+          tables_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000004);
+        } else {
+          tablesBuilder_.clear();
+        }
+        return this;
+      }
+
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.internal_static_hbase_pb_RSGroupInfo_descriptor;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo getDefaultInstanceForType() {
+        return org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.getDefaultInstance();
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo build() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo buildPartial() {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo result = new org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.name_ = name_;
+        if (serversBuilder_ == null) {
+          if (((bitField0_ & 0x00000002) == 0x00000002)) {
+            servers_ = java.util.Collections.unmodifiableList(servers_);
+            bitField0_ = (bitField0_ & ~0x00000002);
+          }
+          result.servers_ = servers_;
+        } else {
+          result.servers_ = serversBuilder_.build();
+        }
+        if (tablesBuilder_ == null) {
+          if (((bitField0_ & 0x00000004) == 0x00000004)) {
+            tables_ = java.util.Collections.unmodifiableList(tables_);
+            bitField0_ = (bitField0_ & ~0x00000004);
+          }
+          result.tables_ = tables_;
+        } else {
+          result.tables_ = tablesBuilder_.build();
+        }
+        result.bitField0_ = to_bitField0_;
+        onBuilt();
+        return result;
+      }
+
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo) {
+          return mergeFrom((org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo other) {
+        if (other == org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo.getDefaultInstance()) return this;
+        if (other.hasName()) {
+          bitField0_ |= 0x00000001;
+          name_ = other.name_;
+          onChanged();
+        }
+        if (serversBuilder_ == null) {
+          if (!other.servers_.isEmpty()) {
+            if (servers_.isEmpty()) {
+              servers_ = other.servers_;
+              bitField0_ = (bitField0_ & ~0x00000002);
+            } else {
+              ensureServersIsMutable();
+              servers_.addAll(other.servers_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.servers_.isEmpty()) {
+            if (serversBuilder_.isEmpty()) {
+              serversBuilder_.dispose();
+              serversBuilder_ = null;
+              servers_ = other.servers_;
+              bitField0_ = (bitField0_ & ~0x00000002);
+              serversBuilder_ = 
+                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
+                   getServersFieldBuilder() : null;
+            } else {
+              serversBuilder_.addAllMessages(other.servers_);
+            }
+          }
+        }
+        if (tablesBuilder_ == null) {
+          if (!other.tables_.isEmpty()) {
+            if (tables_.isEmpty()) {
+              tables_ = other.tables_;
+              bitField0_ = (bitField0_ & ~0x00000004);
+            } else {
+              ensureTablesIsMutable();
+              tables_.addAll(other.tables_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.tables_.isEmpty()) {
+            if (tablesBuilder_.isEmpty()) {
+              tablesBuilder_.dispose();
+              tablesBuilder_ = null;
+              tables_ = other.tables_;
+              bitField0_ = (bitField0_ & ~0x00000004);
+              tablesBuilder_ = 
+                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
+                   getTablesFieldBuilder() : null;
+            } else {
+              tablesBuilder_.addAllMessages(other.tables_);
+            }
+          }
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        return this;
+      }
+
+      public final boolean isInitialized() {
+        if (!hasName()) {
+          
+          return false;
+        }
+        for (int i = 0; i < getServersCount(); i++) {
+          if (!getServers(i).isInitialized()) {
+            
+            return false;
+          }
+        }
+        for (int i = 0; i < getTablesCount(); i++) {
+          if (!getTables(i).isInitialized()) {
+            
+            return false;
+          }
+        }
+        return true;
+      }
+
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos.RSGroupInfo) e.getUnfinishedMessage();
+          throw e;
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      // required string name = 1;
+      private java.lang.Object name_ = "";
+      /**
+       * <code>required string name = 1;</code>
+       */
+      public boolean hasName() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      /**
+       * <code>required string name = 1;</code>
+       */
+      public java.lang.String getName() {
+        java.lang.Object ref = name_;
+        if (!(ref instanceof java.lang.String)) {
+          java.lang.String s = ((com.google.protobuf.ByteString) ref)
+              .toStringUtf8();
+          name_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
+        }
+      }
+      /**
+       * <code>required string name = 1;</code>
+       */
+      public com.google.protobuf.ByteString
+          getNameBytes() {
+        java.lang.Object ref = name_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          name_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+      /**
+       * <code>required string name = 1;</code>
+       */
+      public Builder setName(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+        name_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string name = 1;</code>
+       */
+      public Builder clearName() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        name_ = getDefaultInstance().getName();
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string name = 1;</code>
+       */
+      public Builder setNameBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+        name_ = value;
+        onChanged();
+        return this;
+      }
+
+      // repeated .hbase.pb.ServerName servers = 4;
+      private java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName> servers_ =
+        java.util.Collections.emptyList();
+      private void ensureServersIsMutable() {
+        if (!((bitField0_ & 0x00000002) == 0x00000002)) {
+          servers_ = new java.util.ArrayList<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName>(servers_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder> serversBuilder_;
+
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName> getServersList() {
+        if (serversBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(servers_);
+        } else {
+          return serversBuilder_.getMessageList();
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public int getServersCount() {
+        if (serversBuilder_ == null) {
+          return servers_.size();
+        } else {
+          return serversBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName getServers(int index) {
+        if (serversBuilder_ == null) {
+          return servers_.get(index);
+        } else {
+          return serversBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public Builder setServers(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName value) {
+        if (serversBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureServersIsMutable();
+          servers_.set(index, value);
+          onChanged();
+        } else {
+          serversBuilder_.setMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public Builder setServers(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder builderForValue) {
+        if (serversBuilder_ == null) {
+          ensureServersIsMutable();
+          servers_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          serversBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public Builder addServers(org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName value) {
+        if (serversBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureServersIsMutable();
+          servers_.add(value);
+          onChanged();
+        } else {
+          serversBuilder_.addMessage(value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public Builder addServers(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName value) {
+        if (serversBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureServersIsMutable();
+          servers_.add(index, value);
+          onChanged();
+        } else {
+          serversBuilder_.addMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public Builder addServers(
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder builderForValue) {
+        if (serversBuilder_ == null) {
+          ensureServersIsMutable();
+          servers_.add(builderForValue.build());
+          onChanged();
+        } else {
+          serversBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public Builder addServers(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder builderForValue) {
+        if (serversBuilder_ == null) {
+          ensureServersIsMutable();
+          servers_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          serversBuilder_.addMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public Builder addAllServers(
+          java.lang.Iterable<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName> values) {
+        if (serversBuilder_ == null) {
+          ensureServersIsMutable();
+          super.addAll(values, servers_);
+          onChanged();
+        } else {
+          serversBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public Builder clearServers() {
+        if (serversBuilder_ == null) {
+          servers_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000002);
+          onChanged();
+        } else {
+          serversBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public Builder removeServers(int index) {
+        if (serversBuilder_ == null) {
+          ensureServersIsMutable();
+          servers_.remove(index);
+          onChanged();
+        } else {
+          serversBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder getServersBuilder(
+          int index) {
+        return getServersFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder getServersOrBuilder(
+          int index) {
+        if (serversBuilder_ == null) {
+          return servers_.get(index);  } else {
+          return serversBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public java.util.List<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder> 
+           getServersOrBuilderList() {
+        if (serversBuilder_ != null) {
+          return serversBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(servers_);
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder addServersBuilder() {
+        return getServersFieldBuilder().addBuilder(
+            org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder addServersBuilder(
+          int index) {
+        return getServersFieldBuilder().addBuilder(
+            index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .hbase.pb.ServerName servers = 4;</code>
+       */
+      public java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder> 
+           getServersBuilderList() {
+        return getServersFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder> 
+          getServersFieldBuilder() {
+        if (serversBuilder_ == null) {
+          serversBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
+              org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder>(
+                  servers_,
+                  ((bitField0_ & 0x00000002) == 0x00000002),
+                  getParentForChildren(),
+                  isClean());
+          servers_ = null;
+        }
+        return serversBuilder_;
+      }
+
+      // repeated .hbase.pb.TableName tables = 3;
+      private java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> tables_ =
+        java.util.Collections.emptyList();
+      private void ensureTablesIsMutable() {
+        if (!((bitField0_ & 0x00000004) == 0x00000004)) {
+          tables_ = new java.util.ArrayList<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName>(tables_);
+          bitField0_ |= 0x00000004;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> tablesBuilder_;
+
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> getTablesList() {
+        if (tablesBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(tables_);
+        } else {
+          return tablesBuilder_.getMessageList();
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public int getTablesCount() {
+        if (tablesBuilder_ == null) {
+          return tables_.size();
+        } else {
+          return tablesBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName getTables(int index) {
+        if (tablesBuilder_ == null) {
+          return tables_.get(index);
+        } else {
+          return tablesBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public Builder setTables(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName value) {
+        if (tablesBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureTablesIsMutable();
+          tables_.set(index, value);
+          onChanged();
+        } else {
+          tablesBuilder_.setMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public Builder setTables(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder builderForValue) {
+        if (tablesBuilder_ == null) {
+          ensureTablesIsMutable();
+          tables_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          tablesBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public Builder addTables(org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName value) {
+        if (tablesBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureTablesIsMutable();
+          tables_.add(value);
+          onChanged();
+        } else {
+          tablesBuilder_.addMessage(value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public Builder addTables(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName value) {
+        if (tablesBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureTablesIsMutable();
+          tables_.add(index, value);
+          onChanged();
+        } else {
+          tablesBuilder_.addMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public Builder addTables(
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder builderForValue) {
+        if (tablesBuilder_ == null) {
+          ensureTablesIsMutable();
+          tables_.add(builderForValue.build());
+          onChanged();
+        } else {
+          tablesBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public Builder addTables(
+          int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder builderForValue) {
+        if (tablesBuilder_ == null) {
+          ensureTablesIsMutable();
+          tables_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          tablesBuilder_.addMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public Builder addAllTables(
+          java.lang.Iterable<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName> values) {
+        if (tablesBuilder_ == null) {
+          ensureTablesIsMutable();
+          super.addAll(values, tables_);
+          onChanged();
+        } else {
+          tablesBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public Builder clearTables() {
+        if (tablesBuilder_ == null) {
+          tables_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000004);
+          onChanged();
+        } else {
+          tablesBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public Builder removeTables(int index) {
+        if (tablesBuilder_ == null) {
+          ensureTablesIsMutable();
+          tables_.remove(index);
+          onChanged();
+        } else {
+          tablesBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder getTablesBuilder(
+          int index) {
+        return getTablesFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder getTablesOrBuilder(
+          int index) {
+        if (tablesBuilder_ == null) {
+          return tables_.get(index);  } else {
+          return tablesBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public java.util.List<? extends org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> 
+           getTablesOrBuilderList() {
+        if (tablesBuilder_ != null) {
+          return tablesBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(tables_);
+        }
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder addTablesBuilder() {
+        return getTablesFieldBuilder().addBuilder(
+            org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder addTablesBuilder(
+          int index) {
+        return getTablesFieldBuilder().addBuilder(
+            index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .hbase.pb.TableName tables = 3;</code>
+       */
+      public java.util.List<org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder> 
+           getTablesBuilderList() {
+        return getTablesFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder> 
+          getTablesFieldBuilder() {
+        if (tablesBuilder_ == null) {
+          tablesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
+              org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder>(
+                  tables_,
+                  ((bitField0_ & 0x00000004) == 0x00000004),
+                  getParentForChildren(),
+                  isClean());
+          tables_ = null;
+        }
+        return tablesBuilder_;
+      }
+
+      // @@protoc_insertion_point(builder_scope:hbase.pb.RSGroupInfo)
+    }
+
+    static {
+      defaultInstance = new RSGroupInfo(true);
+      defaultInstance.initFields();
+    }
+
+    // @@protoc_insertion_point(class_scope:hbase.pb.RSGroupInfo)
+  }
+
+  private static com.google.protobuf.Descriptors.Descriptor
+    internal_static_hbase_pb_RSGroupInfo_descriptor;
+  private static
+    com.google.protobuf.GeneratedMessage.FieldAccessorTable
+      internal_static_hbase_pb_RSGroupInfo_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n\rRSGroup.proto\022\010hbase.pb\032\013HBase.proto\"g" +
+      "\n\013RSGroupInfo\022\014\n\004name\030\001 \002(\t\022%\n\007servers\030\004" +
+      " \003(\0132\024.hbase.pb.ServerName\022#\n\006tables\030\003 \003" +
+      "(\0132\023.hbase.pb.TableNameBC\n*org.apache.ha" +
+      "doop.hbase.protobuf.generatedB\rRSGroupPr" +
+      "otosH\001\210\001\001\240\001\001"
+    };
+    com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
+      new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
+        public com.google.protobuf.ExtensionRegistry assignDescriptors(
+            com.google.protobuf.Descriptors.FileDescriptor root) {
+          descriptor = root;
+          internal_static_hbase_pb_RSGroupInfo_descriptor =
+            getDescriptor().getMessageTypes().get(0);
+          internal_static_hbase_pb_RSGroupInfo_fieldAccessorTable = new
+            com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+              internal_static_hbase_pb_RSGroupInfo_descriptor,
+              new java.lang.String[] { "Name", "Servers", "Tables", });
+          return null;
+        }
+      };
+    com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+          org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.getDescriptor(),
+        }, assigner);
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-protocol/src/main/protobuf/RSGroup.proto
----------------------------------------------------------------------
diff --git a/hbase-protocol/src/main/protobuf/RSGroup.proto b/hbase-protocol/src/main/protobuf/RSGroup.proto
new file mode 100644
index 0000000..7358941
--- /dev/null
+++ b/hbase-protocol/src/main/protobuf/RSGroup.proto
@@ -0,0 +1,34 @@
+/**
+ * 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 hbase.pb;
+
+option java_package = "org.apache.hadoop.hbase.protobuf.generated";
+option java_outer_classname = "RSGroupProtos";
+option java_generic_services = true;
+option java_generate_equals_and_hash = true;
+option optimize_for = SPEED;
+
+import "HBase.proto";
+
+message RSGroupInfo {
+  required string name = 1;
+  repeated ServerName servers = 4;
+  repeated TableName tables = 3;
+}
+

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-protocol/src/main/protobuf/RSGroupAdmin.proto
----------------------------------------------------------------------
diff --git a/hbase-protocol/src/main/protobuf/RSGroupAdmin.proto b/hbase-protocol/src/main/protobuf/RSGroupAdmin.proto
new file mode 100644
index 0000000..fda9b09
--- /dev/null
+++ b/hbase-protocol/src/main/protobuf/RSGroupAdmin.proto
@@ -0,0 +1,136 @@
+/**
+ * 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 hbase.pb;
+
+option java_package = "org.apache.hadoop.hbase.protobuf.generated";
+option java_outer_classname = "RSGroupAdminProtos";
+option java_generic_services = true;
+option java_generate_equals_and_hash = true;
+option optimize_for = SPEED;
+
+import "HBase.proto";
+import "RSGroup.proto";
+
+/** Group level protobufs */
+
+message ListTablesOfRSGroupRequest {
+  required string r_s_group_name = 1;
+}
+
+message ListTablesOfRSGroupResponse {
+  repeated TableName table_name = 1;
+}
+
+message GetRSGroupInfoRequest {
+  required string r_s_group_name = 1;
+}
+
+message GetRSGroupInfoResponse {
+  optional RSGroupInfo r_s_group_info = 1;
+}
+
+message GetRSGroupInfoOfTableRequest {
+  required TableName table_name = 1;
+}
+
+message GetRSGroupInfoOfTableResponse {
+  optional RSGroupInfo r_s_group_info = 1;
+}
+
+message MoveServersRequest {
+  required string target_group = 1;
+  repeated ServerName servers = 3;
+}
+
+message MoveServersResponse {
+}
+
+message MoveTablesRequest {
+  required string target_group = 1;
+  repeated TableName table_name = 2;
+}
+
+message MoveTablesResponse {
+}
+
+message AddRSGroupRequest {
+  required string r_s_group_name = 1;
+}
+
+message AddRSGroupResponse {
+}
+
+message RemoveRSGroupRequest {
+  required string r_s_group_name = 1;
+}
+
+message RemoveRSGroupResponse {
+}
+
+message BalanceRSGroupRequest {
+  required string r_s_group_name = 1;
+}
+
+message BalanceRSGroupResponse {
+  required bool balanceRan = 1;
+}
+
+message ListRSGroupInfosRequest {
+}
+
+message ListRSGroupInfosResponse {
+  repeated RSGroupInfo  r_s_group_info = 1;
+}
+
+message GetRSGroupInfoOfServerRequest {
+  required ServerName server = 2;
+}
+
+message GetRSGroupInfoOfServerResponse {
+  optional RSGroupInfo r_s_group_info = 1;
+}
+
+service RSGroupAdminService {
+  rpc GetRSGroupInfo(GetRSGroupInfoRequest)
+    returns (GetRSGroupInfoResponse);
+
+  rpc GetRSGroupInfoOfTable(GetRSGroupInfoOfTableRequest)
+    returns (GetRSGroupInfoOfTableResponse);
+
+  rpc GetRSGroupInfoOfServer(GetRSGroupInfoOfServerRequest)
+    returns (GetRSGroupInfoOfServerResponse);
+
+  rpc MoveServers(MoveServersRequest)
+    returns (MoveServersResponse);
+
+  rpc MoveTables(MoveTablesRequest)
+    returns (MoveTablesResponse);
+
+  rpc AddRSGroup(AddRSGroupRequest)
+    returns (AddRSGroupResponse);
+
+  rpc RemoveRSGroup(RemoveRSGroupRequest)
+    returns (RemoveRSGroupResponse);
+
+  rpc BalanceRSGroup(BalanceRSGroupRequest)
+    returns (BalanceRSGroupResponse);
+
+  rpc ListRSGroupInfos(ListRSGroupInfosRequest)
+    returns (ListRSGroupInfosResponse);
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/pom.xml
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/pom.xml b/hbase-rsgroup/pom.xml
new file mode 100644
index 0000000..aff49b7
--- /dev/null
+++ b/hbase-rsgroup/pom.xml
@@ -0,0 +1,346 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <!--
+  /**
+   * 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.
+   */
+  -->
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>hbase</artifactId>
+    <groupId>org.apache.hbase</groupId>
+    <version>2.0.0-SNAPSHOT</version>
+    <relativePath>..</relativePath>
+  </parent>
+
+  <artifactId>hbase-rsgroup</artifactId>
+  <name>Apache HBase - RSGroup</name>
+  <description>Regionserver Groups for HBase</description>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-site-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <!--Make it so assembly:single does nothing in here-->
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>${maven.assembly.version}</version>
+        <configuration>
+          <skipAssembly>true</skipAssembly>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <!-- Always skip the second part executions, since we only run
+          simple unit tests in this module -->
+        <executions>
+          <execution>
+            <id>secondPartTestsExecution</id>
+            <phase>test</phase>
+            <goals>
+              <goal>test</goal>
+            </goals>
+            <configuration>
+              <skip>true</skip>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- Make a jar and put the sources in the jar -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+      </plugin>
+    </plugins>
+    <pluginManagement>
+      <plugins>
+        <!--This plugin's configuration is used to store Eclipse m2e settings
+             only. It has no influence on the Maven build itself.-->
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <versionRange>[3.2,)</versionRange>
+                    <goals>
+                      <goal>compile</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore></ignore>
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+
+  <dependencies>
+    <!-- Intra-project dependencies -->
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-annotations</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>jdk.tools</groupId>
+          <artifactId>jdk.tools</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-annotations</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-client</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-server</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-common</artifactId>
+      <type>test-jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-protocol</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-testing-util</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- General dependencies -->
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.google.protobuf</groupId>
+      <artifactId>protobuf-java</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-all</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.zookeeper</groupId>
+      <artifactId>zookeeper</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.htrace</groupId>
+      <artifactId>htrace-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-mapper-asl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jruby.jcodings</groupId>
+      <artifactId>jcodings</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jruby.joni</groupId>
+      <artifactId>joni</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.dropwizard.metrics</groupId>
+      <artifactId>metrics-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <!-- Skip the tests in this module -->
+    <profile>
+      <id>skipRSGroupTests</id>
+      <activation>
+        <property>
+          <name>skipRSGroupTests</name>
+        </property>
+      </activation>
+      <properties>
+        <surefire.skipFirstPart>true</surefire.skipFirstPart>
+      </properties>
+    </profile>
+    <!-- profile against Hadoop 1.0.x: This is the default. It has to have the same
+ activation property as the parent Hadoop 1.0.x profile to make sure it gets run at
+ the same time. -->
+    <profile>
+      <id>hadoop-1.1</id>
+      <activation>
+        <property>
+            <!--Below formatting for dev-support/generate-hadoopX-poms.sh-->
+            <!--h1--><name>hadoop.profile</name><value>1.1</value>
+        </property>
+      </activation>
+      <dependencies>
+        <dependency>
+          <groupId>org.apache.hadoop</groupId>
+          <artifactId>hadoop-core</artifactId>
+        </dependency>
+      </dependencies>
+    </profile>
+
+    <!--
+      profile for building against Hadoop 2.0.0-alpha. Activate using:
+       mvn -Dhadoop.profile=2.0
+    -->
+    <profile>
+      <id>hadoop-2.0</id>
+      <activation>
+        <property>
+            <!--Below formatting for dev-support/generate-hadoopX-poms.sh-->
+            <!--h2--><name>!hadoop.profile</name>
+        </property>
+      </activation>
+      <dependencies>
+        <dependency>
+           <groupId>com.google.code.findbugs</groupId>
+           <artifactId>jsr305</artifactId>
+           <optional>true</optional>
+        </dependency>
+        <dependency>
+          <groupId>org.apache.hadoop</groupId>
+          <artifactId>hadoop-auth</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.apache.hadoop</groupId>
+          <artifactId>hadoop-common</artifactId>
+          <exclusions>
+            <exclusion>
+              <groupId>com.github.stephenc.findbugs</groupId>
+              <artifactId>findbugs-annotations</artifactId>
+            </exclusion>
+            <exclusion>
+              <groupId>net.java.dev.jets3t</groupId>
+              <artifactId>jets3t</artifactId>
+            </exclusion>
+            <exclusion>
+              <groupId>javax.servlet.jsp</groupId>
+              <artifactId>jsp-api</artifactId>
+            </exclusion>
+            <exclusion>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty</artifactId>
+            </exclusion>
+            <exclusion>
+              <groupId>com.sun.jersey</groupId>
+              <artifactId>jersey-server</artifactId>
+            </exclusion>
+            <exclusion>
+              <groupId>com.sun.jersey</groupId>
+              <artifactId>jersey-core</artifactId>
+            </exclusion>
+            <exclusion>
+              <groupId>com.sun.jersey</groupId>
+              <artifactId>jersey-json</artifactId>
+            </exclusion> 
+            <exclusion>
+              <groupId>javax.servlet</groupId>
+              <artifactId>servlet-api</artifactId>
+            </exclusion>
+            <exclusion>
+              <groupId>tomcat</groupId>
+              <artifactId>jasper-compiler</artifactId>
+            </exclusion>
+            <exclusion>
+              <groupId>tomcat</groupId>
+              <artifactId>jasper-runtime</artifactId>
+            </exclusion>
+          </exclusions>
+        </dependency>
+      </dependencies>
+    </profile>
+
+    <!--
+      profile for building against Hadoop 3.0.x. Activate using:
+       mvn -Dhadoop.profile=3.0
+    -->
+    <profile>
+      <id>hadoop-3.0</id>
+      <activation>
+        <property>
+          <name>hadoop.profile</name>
+          <value>3.0</value>
+        </property>
+      </activation>
+      <properties>
+        <hadoop.version>3.0-SNAPSHOT</hadoop.version>
+      </properties>
+      <dependencies>
+        <dependency>
+          <groupId>org.apache.hadoop</groupId>
+          <artifactId>hadoop-auth</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.apache.hadoop</groupId>
+          <artifactId>hadoop-common</artifactId>
+        </dependency>
+      </dependencies>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdmin.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdmin.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdmin.java
new file mode 100644
index 0000000..f94d0f6
--- /dev/null
+++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdmin.java
@@ -0,0 +1,121 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.net.HostAndPort;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.client.Connection;
+
+/**
+ * Group user API interface used between client and server.
+ */
+@InterfaceAudience.Private
+public abstract class RSGroupAdmin implements Closeable {
+
+  /**
+   * Create a new RSGroupAdmin client
+   * @param conn connection RSGroupAdmin instance will use
+   * @return a new RSGroupAdmin client
+   * @throws IOException on failure to create new client
+   */
+  public static RSGroupAdmin newClient(Connection conn) throws IOException {
+    return new RSGroupAdminClient(conn);
+  }
+
+  /**
+   * Gets the regionserver group information.
+   *
+   * @param groupName the group name
+   * @return An instance of RSGroupInfo
+   */
+  public abstract RSGroupInfo getRSGroupInfo(String groupName) throws IOException;
+
+  /**
+   * Gets the regionserver group info of table.
+   *
+   * @param tableName the table name
+   * @return An instance of RSGroupInfo.
+   */
+  public abstract RSGroupInfo getRSGroupInfoOfTable(TableName tableName) throws IOException;
+
+  /**
+   * Move a set of serves to another group
+   *
+   *
+   * @param servers set of servers, must be in the form HOST:PORT
+   * @param targetGroup the target group
+   * @throws java.io.IOException Signals that an I/O exception has occurred.
+   */
+  public abstract void moveServers(Set<HostAndPort> servers, String targetGroup) throws IOException;
+
+  /**
+   * Move tables to a new group.
+   * This will unassign all of a table's region so it can be reassigned to the correct group.
+   * @param tables list of tables to move
+   * @param targetGroup target group
+   * @throws java.io.IOException on failure to move tables
+   */
+  public abstract void moveTables(Set<TableName> tables, String targetGroup) throws IOException;
+
+  /**
+   * Add a new group
+   * @param name name of the group
+   * @throws java.io.IOException on failure to add group
+   */
+  public abstract void addRSGroup(String name) throws IOException;
+
+  /**
+   * Remove a regionserver group
+   * @param name name of the group
+   * @throws java.io.IOException on failure to remove group
+   */
+  public abstract void removeRSGroup(String name) throws IOException;
+
+  /**
+   * Balance the regions in a group
+   *
+   * @param name the name of the group to balance
+   * @return boolean whether balance ran or not
+   * @throws java.io.IOException on unexpected failure to balance group
+   */
+  public abstract boolean balanceRSGroup(String name) throws IOException;
+
+  /**
+   * Lists the existing groups.
+   *
+   * @return Collection of RSGroupInfo.
+   */
+  public abstract List<RSGroupInfo> listRSGroups() throws IOException;
+
+  /**
+   * Retrieve the RSGroupInfo a server is affiliated to
+   * @param hostPort HostPort to get RSGroupInfo for
+   * @return RSGroupInfo associated with the server
+   * @throws java.io.IOException on unexpected failure to retrieve GroupInfo
+   */
+  public abstract RSGroupInfo getRSGroupOfServer(HostAndPort hostPort) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java
new file mode 100644
index 0000000..a7f14f7
--- /dev/null
+++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminClient.java
@@ -0,0 +1,204 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
+import com.google.protobuf.ServiceException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.classification.InterfaceStability;
+import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
+
+
+/**
+ * Client used for managing region server group information.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+class RSGroupAdminClient extends RSGroupAdmin {
+  private RSGroupAdminProtos.RSGroupAdminService.BlockingInterface proxy;
+  private static final Log LOG = LogFactory.getLog(RSGroupAdminClient.class);
+
+  public RSGroupAdminClient(Connection conn) throws IOException {
+    proxy = RSGroupAdminProtos.RSGroupAdminService.newBlockingStub(
+        conn.getAdmin().coprocessorService());
+  }
+
+  @Override
+  public RSGroupInfo getRSGroupInfo(String groupName) throws IOException {
+    try {
+      RSGroupAdminProtos.GetRSGroupInfoResponse resp =
+        proxy.getRSGroupInfo(null,
+            RSGroupAdminProtos.GetRSGroupInfoRequest.newBuilder()
+                .setRSGroupName(groupName).build());
+      if(resp.hasRSGroupInfo()) {
+        return ProtobufUtil.toGroupInfo(resp.getRSGroupInfo());
+      }
+      return null;
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public RSGroupInfo getRSGroupInfoOfTable(TableName tableName) throws IOException {
+    RSGroupAdminProtos.GetRSGroupInfoOfTableRequest request =
+        RSGroupAdminProtos.GetRSGroupInfoOfTableRequest.newBuilder()
+            .setTableName(ProtobufUtil.toProtoTableName(tableName)).build();
+
+    try {
+      GetRSGroupInfoOfTableResponse resp = proxy.getRSGroupInfoOfTable(null, request);
+      if (resp.hasRSGroupInfo()) {
+        return ProtobufUtil.toGroupInfo(resp.getRSGroupInfo());
+      }
+      return null;
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public void moveServers(Set<HostAndPort> servers, String targetGroup) throws IOException {
+    Set<HBaseProtos.ServerName> hostPorts = Sets.newHashSet();
+    for(HostAndPort el: servers) {
+      hostPorts.add(HBaseProtos.ServerName.newBuilder()
+        .setHostName(el.getHostText())
+        .setPort(el.getPort())
+        .build());
+    }
+    RSGroupAdminProtos.MoveServersRequest request =
+        RSGroupAdminProtos.MoveServersRequest.newBuilder()
+            .setTargetGroup(targetGroup)
+            .addAllServers(hostPorts).build();
+
+    try {
+      proxy.moveServers(null, request);
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public void moveTables(Set<TableName> tables, String targetGroup) throws IOException {
+    RSGroupAdminProtos.MoveTablesRequest.Builder builder =
+        RSGroupAdminProtos.MoveTablesRequest.newBuilder()
+            .setTargetGroup(targetGroup);
+    for(TableName tableName: tables) {
+      builder.addTableName(ProtobufUtil.toProtoTableName(tableName));
+    }
+    try {
+      proxy.moveTables(null, builder.build());
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public void addRSGroup(String groupName) throws IOException {
+    RSGroupAdminProtos.AddRSGroupRequest request =
+        RSGroupAdminProtos.AddRSGroupRequest.newBuilder()
+            .setRSGroupName(groupName).build();
+    try {
+      proxy.addRSGroup(null, request);
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public void removeRSGroup(String name) throws IOException {
+    RSGroupAdminProtos.RemoveRSGroupRequest request =
+        RSGroupAdminProtos.RemoveRSGroupRequest.newBuilder()
+            .setRSGroupName(name).build();
+    try {
+      proxy.removeRSGroup(null, request);
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public boolean balanceRSGroup(String name) throws IOException {
+    RSGroupAdminProtos.BalanceRSGroupRequest request =
+        RSGroupAdminProtos.BalanceRSGroupRequest.newBuilder()
+            .setRSGroupName(name).build();
+
+    try {
+      return proxy.balanceRSGroup(null, request).getBalanceRan();
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public List<RSGroupInfo> listRSGroups() throws IOException {
+    try {
+      List<RSGroupProtos.RSGroupInfo> resp =
+          proxy.listRSGroupInfos(null,
+              RSGroupAdminProtos.ListRSGroupInfosRequest.newBuilder().build()).getRSGroupInfoList();
+      List<RSGroupInfo> result = new ArrayList<RSGroupInfo>(resp.size());
+      for(RSGroupProtos.RSGroupInfo entry: resp) {
+        result.add(ProtobufUtil.toGroupInfo(entry));
+      }
+      return result;
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public RSGroupInfo getRSGroupOfServer(HostAndPort hostPort) throws IOException {
+    RSGroupAdminProtos.GetRSGroupInfoOfServerRequest request =
+        RSGroupAdminProtos.GetRSGroupInfoOfServerRequest.newBuilder()
+            .setServer(HBaseProtos.ServerName.newBuilder()
+                .setHostName(hostPort.getHostText())
+                .setPort(hostPort.getPort())
+                .build())
+            .build();
+    try {
+      GetRSGroupInfoOfServerResponse resp = proxy.getRSGroupInfoOfServer(null, request);
+      if (resp.hasRSGroupInfo()) {
+        return ProtobufUtil.toGroupInfo(resp.getRSGroupInfo());
+      }
+      return null;
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+  }
+}


[4/7] hbase git commit: HBASE-6721 RegionServer Group based Assignment (Francis Liu)

Posted by en...@apache.org.
http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java
new file mode 100644
index 0000000..e51dcc2
--- /dev/null
+++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java
@@ -0,0 +1,965 @@
+/**
+ * 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.
+ */
+
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
+import com.google.protobuf.RpcCallback;
+import com.google.protobuf.RpcController;
+import com.google.protobuf.Service;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.Coprocessor;
+import org.apache.hadoop.hbase.CoprocessorEnvironment;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.ProcedureInfo;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
+import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
+import org.apache.hadoop.hbase.coprocessor.MasterObserver;
+import org.apache.hadoop.hbase.coprocessor.ObserverContext;
+import org.apache.hadoop.hbase.master.MasterServices;
+import org.apache.hadoop.hbase.master.RegionPlan;
+import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
+import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.ResponseConverter;
+import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
+import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
+import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.AddRSGroupRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.AddRSGroupResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.BalanceRSGroupRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.BalanceRSGroupResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveServersRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveServersResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveTablesRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveTablesResponse;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RSGroupAdminService;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveRSGroupRequest;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveRSGroupResponse;
+
+
+public class RSGroupAdminEndpoint extends RSGroupAdminService
+    implements CoprocessorService, Coprocessor, MasterObserver {
+
+  private static final Log LOG = LogFactory.getLog(RSGroupAdminEndpoint.class);
+  private MasterServices master = null;
+
+  private static RSGroupInfoManagerImpl groupInfoManager;
+  private RSGroupAdminServer groupAdminServer;
+
+  @Override
+  public void start(CoprocessorEnvironment env) throws IOException {
+    MasterCoprocessorEnvironment menv = (MasterCoprocessorEnvironment)env;
+    master = menv.getMasterServices();
+    groupInfoManager = new RSGroupInfoManagerImpl(master);
+    groupAdminServer = new RSGroupAdminServer(master, groupInfoManager);
+    Class clazz =
+        master.getConfiguration().getClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, null);
+    if (!RSGroupableBalancer.class.isAssignableFrom(clazz)) {
+      throw new IOException("Configured balancer is not a GroupableBalancer");
+    }
+  }
+
+  @Override
+  public void stop(CoprocessorEnvironment env) throws IOException {
+  }
+
+  @Override
+  public Service getService() {
+    return this;
+  }
+
+  public RSGroupInfoManager getGroupInfoManager() {
+    return groupInfoManager;
+  }
+
+  @Override
+  public void getRSGroupInfo(RpcController controller,
+                           GetRSGroupInfoRequest request,
+                           RpcCallback<GetRSGroupInfoResponse> done) {
+    GetRSGroupInfoResponse response = null;
+    try {
+      GetRSGroupInfoResponse.Builder builder =
+          GetRSGroupInfoResponse.newBuilder();
+      RSGroupInfo RSGroupInfo = groupAdminServer.getRSGroupInfo(request.getRSGroupName());
+      if(RSGroupInfo != null) {
+        builder.setRSGroupInfo(ProtobufUtil.toProtoGroupInfo(RSGroupInfo));
+      }
+      response = builder.build();
+    } catch (IOException e) {
+      ResponseConverter.setControllerException(controller, e);
+    }
+    done.run(response);
+  }
+
+  @Override
+  public void getRSGroupInfoOfTable(RpcController controller,
+                                  GetRSGroupInfoOfTableRequest request,
+                                  RpcCallback<GetRSGroupInfoOfTableResponse> done) {
+    GetRSGroupInfoOfTableResponse response = null;
+    try {
+      GetRSGroupInfoOfTableResponse.Builder builder =
+          GetRSGroupInfoOfTableResponse.newBuilder();
+      TableName tableName = ProtobufUtil.toTableName(request.getTableName());
+      RSGroupInfo RSGroupInfo = groupAdminServer.getRSGroupInfoOfTable(tableName);
+      if (RSGroupInfo == null) {
+        response = builder.build();
+      } else {
+        response = builder.setRSGroupInfo(ProtobufUtil.toProtoGroupInfo(RSGroupInfo)).build();
+      }
+    } catch (IOException e) {
+      ResponseConverter.setControllerException(controller, e);
+    }
+    done.run(response);
+  }
+
+  @Override
+  public void moveServers(RpcController controller,
+                          MoveServersRequest request,
+                          RpcCallback<MoveServersResponse> done) {
+    RSGroupAdminProtos.MoveServersResponse response = null;
+    try {
+      RSGroupAdminProtos.MoveServersResponse.Builder builder =
+          RSGroupAdminProtos.MoveServersResponse.newBuilder();
+      Set<HostAndPort> hostPorts = Sets.newHashSet();
+      for(HBaseProtos.ServerName el: request.getServersList()) {
+        hostPorts.add(HostAndPort.fromParts(el.getHostName(), el.getPort()));
+      }
+      groupAdminServer.moveServers(hostPorts, request.getTargetGroup());
+      response = builder.build();
+    } catch (IOException e) {
+      ResponseConverter.setControllerException(controller, e);
+    }
+    done.run(response);
+  }
+
+  @Override
+  public void moveTables(RpcController controller,
+                         MoveTablesRequest request,
+                         RpcCallback<MoveTablesResponse> done) {
+    MoveTablesResponse response = null;
+    try {
+      MoveTablesResponse.Builder builder =
+          MoveTablesResponse.newBuilder();
+      Set<TableName> tables = new HashSet<TableName>(request.getTableNameList().size());
+      for(HBaseProtos.TableName tableName: request.getTableNameList()) {
+        tables.add(ProtobufUtil.toTableName(tableName));
+      }
+      groupAdminServer.moveTables(tables, request.getTargetGroup());
+      response = builder.build();
+    } catch (IOException e) {
+      ResponseConverter.setControllerException(controller, e);
+    }
+    done.run(response);
+  }
+
+  @Override
+  public void addRSGroup(RpcController controller,
+                       AddRSGroupRequest request,
+                       RpcCallback<AddRSGroupResponse> done) {
+    AddRSGroupResponse response = null;
+    try {
+      AddRSGroupResponse.Builder builder =
+          AddRSGroupResponse.newBuilder();
+      groupAdminServer.addRSGroup(request.getRSGroupName());
+      response = builder.build();
+    } catch (IOException e) {
+      ResponseConverter.setControllerException(controller, e);
+    }
+    done.run(response);
+  }
+
+  @Override
+  public void removeRSGroup(RpcController controller,
+                          RemoveRSGroupRequest request,
+                          RpcCallback<RemoveRSGroupResponse> done) {
+    RemoveRSGroupResponse response = null;
+    try {
+      RemoveRSGroupResponse.Builder builder =
+          RemoveRSGroupResponse.newBuilder();
+      groupAdminServer.removeRSGroup(request.getRSGroupName());
+      response = builder.build();
+    } catch (IOException e) {
+      ResponseConverter.setControllerException(controller, e);
+    }
+    done.run(response);
+  }
+
+  @Override
+  public void balanceRSGroup(RpcController controller,
+                           BalanceRSGroupRequest request,
+                           RpcCallback<BalanceRSGroupResponse> done) {
+    BalanceRSGroupResponse.Builder builder = BalanceRSGroupResponse.newBuilder();
+    try {
+      builder.setBalanceRan(groupAdminServer.balanceRSGroup(request.getRSGroupName()));
+    } catch (IOException e) {
+      ResponseConverter.setControllerException(controller, e);
+      builder.setBalanceRan(false);
+    }
+    done.run(builder.build());
+  }
+
+  @Override
+  public void listRSGroupInfos(RpcController controller,
+                             ListRSGroupInfosRequest request,
+                             RpcCallback<ListRSGroupInfosResponse> done) {
+    ListRSGroupInfosResponse response = null;
+    try {
+      ListRSGroupInfosResponse.Builder builder =
+          ListRSGroupInfosResponse.newBuilder();
+      for(RSGroupInfo RSGroupInfo : groupAdminServer.listRSGroups()) {
+        builder.addRSGroupInfo(ProtobufUtil.toProtoGroupInfo(RSGroupInfo));
+      }
+      response = builder.build();
+    } catch (IOException e) {
+      ResponseConverter.setControllerException(controller, e);
+    }
+    done.run(response);
+  }
+
+  @Override
+  public void getRSGroupInfoOfServer(RpcController controller,
+                                   GetRSGroupInfoOfServerRequest request,
+                                   RpcCallback<GetRSGroupInfoOfServerResponse> done) {
+    GetRSGroupInfoOfServerResponse.Builder builder = GetRSGroupInfoOfServerResponse.newBuilder();
+    try {
+      HostAndPort hp =
+          HostAndPort.fromParts(request.getServer().getHostName(), request.getServer().getPort());
+      RSGroupInfo RSGroupInfo = groupAdminServer.getRSGroupOfServer(hp);
+      if (RSGroupInfo != null) {
+        builder.setRSGroupInfo(ProtobufUtil.toProtoGroupInfo(RSGroupInfo));
+      }
+    } catch (IOException e) {
+      ResponseConverter.setControllerException(controller, e);
+    }
+    done.run(builder.build());
+  }
+
+  @Override
+  public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+      HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
+    groupAdminServer.prepareRSGroupForTable(desc);
+  }
+
+  @Override
+  public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                              TableName tableName) throws IOException {
+    groupAdminServer.cleanupRSGroupForTable(tableName);
+  }
+
+  @Override
+  public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 NamespaceDescriptor ns) throws IOException {
+    String group = ns.getConfigurationValue(RSGroupInfo.NAMESPACEDESC_PROP_GROUP);
+    if(group != null && groupAdminServer.getRSGroupInfo(group) == null) {
+      throw new ConstraintException("Region server group "+group+" does not exit");
+    }
+  }
+
+  @Override
+  public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 NamespaceDescriptor ns) throws IOException {
+    preCreateNamespace(ctx, ns);
+  }
+
+  @Override
+  public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                              HTableDescriptor desc,
+                              HRegionInfo[] regions) throws IOException {
+  }
+
+  @Override
+  public void preCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                    HTableDescriptor desc,
+                                    HRegionInfo[] regions) throws IOException {
+  }
+
+  @Override
+  public void postCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                     HTableDescriptor desc,
+                                     HRegionInfo[] regions) throws IOException {
+  }
+
+  @Override
+  public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                             TableName tableName) throws IOException {
+  }
+
+  @Override
+  public void preDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                    TableName tableName) throws IOException {
+  }
+
+  @Override
+  public void postDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                     TableName tableName) throws IOException {
+  }
+
+  @Override
+  public void preTruncateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               TableName tableName) throws IOException {
+  }
+
+  @Override
+  public void postTruncateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                TableName tableName) throws IOException {
+  }
+
+  @Override
+  public void preTruncateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                      TableName tableName) throws IOException {
+  }
+
+  @Override
+  public void postTruncateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                       TableName tableName) throws IOException {
+  }
+
+  @Override
+  public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                             TableName tableName,
+                             HTableDescriptor htd) throws IOException {
+  }
+
+  @Override
+  public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                              TableName tableName,
+                              HTableDescriptor htd) throws IOException {
+  }
+
+  @Override
+  public void preModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                    TableName tableName,
+                                    HTableDescriptor htd) throws IOException {
+  }
+
+  @Override
+  public void postModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                     TableName tableName,
+                                     HTableDescriptor htd) throws IOException {
+  }
+
+  @Override
+  public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                           TableName tableName,
+                           HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void preAddColumnFamily(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 TableName tableName,
+                                 HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void postAddColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                            TableName tableName,
+                            HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void postAddColumnFamily(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                  TableName tableName,
+                                  HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void preAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                  TableName tableName,
+                                  HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void preAddColumnFamilyHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                        TableName tableName,
+                                        HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void postAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                   TableName tableName,
+                                   HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void postAddColumnFamilyHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                         TableName tableName,
+                                         HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                              TableName tableName,
+                              HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void preModifyColumnFamily(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                    TableName tableName,
+                                    HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void postModifyColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               TableName tableName,
+                               HColumnDescriptor columnFamily) throws IOException {
+  }
+
+  @Override
+  public void postModifyColumnFamily(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName, HColumnDescriptor columnFamily) throws IOException {
+
+  }
+
+  @Override
+  public void preModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName, HColumnDescriptor columnFamily) throws IOException {
+
+  }
+
+  @Override
+  public void preModifyColumnFamilyHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                           TableName tableName, HColumnDescriptor columnFamily)
+      throws IOException {
+
+  }
+
+  @Override
+  public void postModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                      TableName tableName, HColumnDescriptor columnFamily) throws
+      IOException {
+
+  }
+
+  @Override
+  public void postModifyColumnFamilyHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                            TableName tableName, HColumnDescriptor columnFamily)
+      throws IOException {
+
+  }
+
+  @Override
+  public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName, byte[] columnFamily) throws IOException {
+
+  }
+
+  @Override
+  public void preDeleteColumnFamily(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName, byte[] columnFamily) throws IOException {
+
+  }
+
+  @Override
+  public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName, byte[] columnFamily) throws IOException {
+
+  }
+
+  @Override
+  public void postDeleteColumnFamily(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName, byte[] columnFamily) throws IOException {
+
+  }
+
+  @Override
+  public void preDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName, byte[] columnFamily) throws IOException {
+
+  }
+
+  @Override
+  public void preDeleteColumnFamilyHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                           TableName tableName, byte[] columnFamily) throws
+      IOException {
+
+  }
+
+  @Override
+  public void postDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                      TableName tableName, byte[] columnFamily) throws IOException {
+
+  }
+
+  @Override
+  public void postDeleteColumnFamilyHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                            TableName tableName, byte[] columnFamily) throws
+      IOException {
+
+  }
+
+  @Override
+  public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName) throws IOException {
+
+  }
+
+  @Override
+  public void postEnableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName) throws IOException {
+
+  }
+
+  @Override
+  public void preEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName) throws IOException {
+
+  }
+
+  @Override
+  public void postEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName) throws IOException {
+
+  }
+
+  @Override
+  public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName) throws IOException {
+
+  }
+
+  @Override
+  public void postDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName) throws IOException {
+
+  }
+
+  @Override
+  public void preDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName) throws IOException {
+
+  }
+
+  @Override
+  public void postDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                      TableName tableName) throws IOException {
+
+  }
+
+  @Override
+  public void preMove(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo region,
+                      ServerName srcServer, ServerName destServer) throws IOException {
+
+  }
+
+  @Override
+  public void postMove(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo region,
+                       ServerName srcServer, ServerName destServer) throws IOException {
+
+  }
+
+  @Override
+  public void preAssign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo
+      regionInfo) throws IOException {
+
+  }
+
+  @Override
+  public void postAssign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo
+      regionInfo) throws IOException {
+
+  }
+
+  @Override
+  public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo
+      regionInfo, boolean force) throws IOException {
+
+  }
+
+  @Override
+  public void postUnassign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo
+      regionInfo, boolean force) throws IOException {
+
+  }
+
+  @Override
+  public void preRegionOffline(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo
+      regionInfo) throws IOException {
+
+  }
+
+  @Override
+  public void postRegionOffline(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo
+      regionInfo) throws IOException {
+
+  }
+
+  @Override
+  public void preBalance(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+
+  }
+
+  @Override
+  public void postBalance(ObserverContext<MasterCoprocessorEnvironment> ctx, List<RegionPlan>
+      plans) throws IOException {
+
+  }
+
+  @Override
+  public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> ctx, boolean
+      newValue) throws IOException {
+    return newValue;
+  }
+
+  @Override
+  public void postBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> ctx, boolean
+      oldValue, boolean newValue) throws IOException {
+
+  }
+
+  @Override
+  public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+
+  }
+
+  @Override
+  public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+
+  }
+
+  @Override
+  public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws
+      IOException {
+
+  }
+
+  @Override
+  public void preMasterInitialization(ObserverContext<MasterCoprocessorEnvironment> ctx) throws
+      IOException {
+
+  }
+
+  @Override
+  public void preSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription
+      snapshot, HTableDescriptor hTableDescriptor) throws IOException {
+
+  }
+
+  @Override
+  public void postSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription
+      snapshot, HTableDescriptor hTableDescriptor) throws IOException {
+
+  }
+
+  @Override
+  public void preListSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                              SnapshotDescription snapshot) throws IOException {
+
+  }
+
+  @Override
+  public void postListSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               SnapshotDescription snapshot) throws IOException {
+
+  }
+
+  @Override
+  public void preCloneSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               SnapshotDescription snapshot, HTableDescriptor hTableDescriptor)
+      throws IOException {
+
+  }
+
+  @Override
+  public void postCloneSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                SnapshotDescription snapshot, HTableDescriptor hTableDescriptor)
+      throws IOException {
+
+  }
+
+  @Override
+  public void preRestoreSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 SnapshotDescription snapshot, HTableDescriptor hTableDescriptor)
+      throws IOException {
+
+  }
+
+  @Override
+  public void postRestoreSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                  SnapshotDescription snapshot, HTableDescriptor
+      hTableDescriptor) throws IOException {
+
+  }
+
+  @Override
+  public void preDeleteSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                SnapshotDescription snapshot) throws IOException {
+
+  }
+
+  @Override
+  public void postDeleteSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 SnapshotDescription snapshot) throws IOException {
+
+  }
+
+  @Override
+  public void preGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                     List<TableName> tableNamesList, List<HTableDescriptor>
+      descriptors, String regex) throws IOException {
+
+  }
+
+  @Override
+  public void postGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                      List<TableName> tableNamesList, List<HTableDescriptor>
+      descriptors, String regex) throws IOException {
+
+  }
+
+  @Override
+  public void preGetTableNames(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               List<HTableDescriptor> descriptors, String regex) throws
+      IOException {
+
+  }
+
+  @Override
+  public void postGetTableNames(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                List<HTableDescriptor> descriptors, String regex) throws
+      IOException {
+
+  }
+
+  @Override
+  public void postCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                  NamespaceDescriptor ns) throws IOException {
+
+  }
+
+  @Override
+  public void preDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, String
+      namespace) throws IOException {
+
+  }
+
+  @Override
+  public void postDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, String
+      namespace) throws IOException {
+
+  }
+
+  @Override
+  public void postModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                  NamespaceDescriptor ns) throws IOException {
+
+  }
+
+  @Override
+  public void preGetNamespaceDescriptor(ObserverContext<MasterCoprocessorEnvironment> ctx, String
+      namespace) throws IOException {
+
+  }
+
+  @Override
+  public void postGetNamespaceDescriptor(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                         NamespaceDescriptor ns) throws IOException {
+
+  }
+
+  @Override
+  public void preListNamespaceDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                          List<NamespaceDescriptor> descriptors) throws
+      IOException {
+
+  }
+
+  @Override
+  public void postListNamespaceDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                           List<NamespaceDescriptor> descriptors) throws
+      IOException {
+
+  }
+
+  @Override
+  public void preTableFlush(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName) throws IOException {
+
+  }
+
+  @Override
+  public void postTableFlush(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName) throws IOException {
+
+  }
+
+  @Override
+  public void preSetUserQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, String userName,
+                              Quotas quotas) throws IOException {
+
+  }
+
+  @Override
+  public void postSetUserQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, String
+      userName, Quotas quotas) throws IOException {
+
+  }
+
+  @Override
+  public void preSetUserQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, String userName,
+                              TableName tableName, Quotas quotas) throws IOException {
+
+  }
+
+  @Override
+  public void postSetUserQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, String
+      userName, TableName tableName, Quotas quotas) throws IOException {
+
+  }
+
+  @Override
+  public void preSetUserQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, String userName,
+                              String namespace, Quotas quotas) throws IOException {
+
+  }
+
+  @Override
+  public void postSetUserQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, String
+      userName, String namespace, Quotas quotas) throws IOException {
+
+  }
+
+  @Override
+  public void preSetTableQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName, Quotas quotas) throws IOException {
+
+  }
+
+  @Override
+  public void postSetTableQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName
+      tableName, Quotas quotas) throws IOException {
+
+  }
+
+  @Override
+  public void preSetNamespaceQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, String
+      namespace, Quotas quotas) throws IOException {
+
+  }
+
+  @Override
+  public void postSetNamespaceQuota(ObserverContext<MasterCoprocessorEnvironment> ctx, String
+      namespace, Quotas quotas) throws IOException {
+  }
+
+  @Override
+  public void preDispatchMerge(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo
+      regionA, HRegionInfo regionB) throws IOException {
+  }
+
+  @Override
+  public void postDispatchMerge(ObserverContext<MasterCoprocessorEnvironment> c, HRegionInfo
+      regionA, HRegionInfo regionB) throws IOException {
+  }
+
+  @Override
+  public void preMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx, Set<HostAndPort>
+      servers, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void postMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx, Set<HostAndPort>
+      servers, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void preMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx, Set<TableName>
+      tables, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void postMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+      Set<TableName> tables, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void preAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void postAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void preRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void postRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void preBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String groupName)
+      throws IOException {
+  }
+
+  @Override
+  public void postBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 String groupName, boolean balancerRan) throws IOException {
+  }
+
+  @Override
+  public void preAbortProcedure(ObserverContext<MasterCoprocessorEnvironment> ctx,
+      ProcedureExecutor<MasterProcedureEnv> procEnv, long procId) throws IOException {
+  }
+
+  @Override
+  public void postAbortProcedure(ObserverContext<MasterCoprocessorEnvironment> ctx)
+      throws IOException {
+  }
+
+  @Override
+  public void preListProcedures(ObserverContext<MasterCoprocessorEnvironment> ctx)
+      throws IOException {
+  }
+
+  @Override
+  public void postListProcedures(ObserverContext<MasterCoprocessorEnvironment> ctx,
+      List<ProcedureInfo> procInfoList) throws IOException {
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java
new file mode 100644
index 0000000..8725781
--- /dev/null
+++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java
@@ -0,0 +1,501 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.master.AssignmentManager;
+import org.apache.hadoop.hbase.master.LoadBalancer;
+import org.apache.hadoop.hbase.master.MasterServices;
+import org.apache.hadoop.hbase.master.RegionPlan;
+import org.apache.hadoop.hbase.master.RegionState;
+import org.apache.hadoop.hbase.master.ServerManager;
+import org.apache.hadoop.hbase.master.TableLockManager.TableLock;
+
+/**
+ * Service to support Region Server Grouping (HBase-6721)
+ */
+@InterfaceAudience.Private
+public class RSGroupAdminServer extends RSGroupAdmin {
+  private static final Log LOG = LogFactory.getLog(RSGroupAdminServer.class);
+
+  private MasterServices master;
+  //List of servers that are being moved from one group to another
+  //Key=host:port,Value=targetGroup
+  private ConcurrentMap<HostAndPort,String> serversInTransition =
+      new ConcurrentHashMap<HostAndPort, String>();
+  private RSGroupInfoManager RSGroupInfoManager;
+
+  public RSGroupAdminServer(MasterServices master,
+                            RSGroupInfoManager RSGroupInfoManager) throws IOException {
+    this.master = master;
+    this.RSGroupInfoManager = RSGroupInfoManager;
+  }
+
+  @Override
+  public RSGroupInfo getRSGroupInfo(String groupName) throws IOException {
+    return getRSGroupInfoManager().getRSGroup(groupName);
+  }
+
+
+  @Override
+  public RSGroupInfo getRSGroupInfoOfTable(TableName tableName) throws IOException {
+    String groupName = getRSGroupInfoManager().getRSGroupOfTable(tableName);
+    if (groupName == null) {
+      return null;
+    }
+    return getRSGroupInfoManager().getRSGroup(groupName);
+  }
+
+  @Override
+  public void moveServers(Set<HostAndPort> servers, String targetGroupName)
+      throws IOException {
+    if (servers == null) {
+      throw new ConstraintException(
+          "The list of servers cannot be null.");
+    }
+    if (StringUtils.isEmpty(targetGroupName)) {
+      throw new ConstraintException("The target group cannot be null.");
+    }
+    if (servers.size() < 1) {
+      return;
+    }
+
+    RSGroupInfo targetGrp = getRSGroupInfo(targetGroupName);
+    if (targetGrp == null) {
+      throw new ConstraintException("Group does not exist: "+targetGroupName);
+    }
+
+    RSGroupInfoManager manager = getRSGroupInfoManager();
+    synchronized (manager) {
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().preMoveServers(servers, targetGroupName);
+      }
+      HostAndPort firstServer = servers.iterator().next();
+      //we only allow a move from a single source group
+      //so this should be ok
+      RSGroupInfo srcGrp = manager.getRSGroupOfServer(firstServer);
+      //only move online servers (from default)
+      //or servers from other groups
+      //this prevents bogus servers from entering groups
+      if (srcGrp == null) {
+        throw new ConstraintException(
+            "Server "+firstServer+" does not have a group.");
+      }
+      if (RSGroupInfo.DEFAULT_GROUP.equals(srcGrp.getName())) {
+        Set<HostAndPort> onlineServers = new HashSet<HostAndPort>();
+        for(ServerName server: master.getServerManager().getOnlineServers().keySet()) {
+          onlineServers.add(server.getHostPort());
+        }
+        for(HostAndPort el: servers) {
+          if(!onlineServers.contains(el)) {
+            throw new ConstraintException(
+                "Server "+el+" is not an online server in default group.");
+          }
+        }
+      }
+
+      if(srcGrp.getServers().size() <= servers.size() &&
+          srcGrp.getTables().size() > 0) {
+        throw new ConstraintException("Cannot leave a group "+srcGrp.getName()+
+            " that contains tables " +"without servers.");
+      }
+
+      String sourceGroupName = getRSGroupInfoManager()
+          .getRSGroupOfServer(srcGrp.getServers().iterator().next()).getName();
+      if(getRSGroupInfo(targetGroupName) == null) {
+        throw new ConstraintException("Target group does not exist: "+targetGroupName);
+      }
+
+      for(HostAndPort server: servers) {
+        if (serversInTransition.containsKey(server)) {
+          throw new ConstraintException(
+              "Server list contains a server that is already being moved: "+server);
+        }
+        String tmpGroup = getRSGroupInfoManager().getRSGroupOfServer(server).getName();
+        if (sourceGroupName != null && !tmpGroup.equals(sourceGroupName)) {
+          throw new ConstraintException(
+              "Move server request should only come from one source group. "+
+              "Expecting only "+sourceGroupName+" but contains "+tmpGroup);
+        }
+      }
+
+      if(sourceGroupName.equals(targetGroupName)) {
+        throw new ConstraintException(
+            "Target group is the same as source group: "+targetGroupName);
+      }
+
+      try {
+        //update the servers as in transition
+        for (HostAndPort server : servers) {
+          serversInTransition.put(server, targetGroupName);
+        }
+
+        getRSGroupInfoManager().moveServers(servers, sourceGroupName, targetGroupName);
+        boolean found;
+        List<HostAndPort> tmpServers = Lists.newArrayList(servers);
+        do {
+          found = false;
+          for (Iterator<HostAndPort> iter = tmpServers.iterator();
+               iter.hasNext(); ) {
+            HostAndPort rs = iter.next();
+            //get online regions
+            List<HRegionInfo> regions = new LinkedList<HRegionInfo>();
+            for (Map.Entry<HRegionInfo, ServerName> el :
+                master.getAssignmentManager().getRegionStates().getRegionAssignments().entrySet()) {
+              if (el.getValue().getHostPort().equals(rs)) {
+                regions.add(el.getKey());
+              }
+            }
+            for (RegionState state :
+                master.getAssignmentManager().getRegionStates().getRegionsInTransition().values()) {
+              if (state.getServerName().getHostPort().equals(rs)) {
+                regions.add(state.getRegion());
+              }
+            }
+
+            //unassign regions for a server
+            LOG.info("Unassigning " + regions.size() +
+                " regions from server " + rs + " for move to " + targetGroupName);
+            if (regions.size() > 0) {
+              //TODO bulk unassign or throttled unassign?
+              for (HRegionInfo region : regions) {
+                //regions might get assigned from tables of target group
+                //so we need to filter
+                if (!targetGrp.containsTable(region.getTable())) {
+                  master.getAssignmentManager().unassign(region);
+                  found = true;
+                }
+              }
+            }
+            if (!found) {
+              iter.remove();
+            }
+          }
+          try {
+            Thread.sleep(1000);
+          } catch (InterruptedException e) {
+            LOG.warn("Sleep interrupted", e);
+            Thread.currentThread().interrupt();
+          }
+        } while (found);
+      } finally {
+        //remove from transition
+        for (HostAndPort server : servers) {
+          serversInTransition.remove(server);
+        }
+      }
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().postMoveServers(servers, targetGroupName);
+      }
+      LOG.info("Move server done: "+sourceGroupName+"->"+targetGroupName);
+    }
+  }
+
+  @Override
+  public void moveTables(Set<TableName> tables, String targetGroup) throws IOException {
+    if (tables == null) {
+      throw new ConstraintException(
+          "The list of servers cannot be null.");
+    }
+    if(tables.size() < 1) {
+      LOG.debug("moveTables() passed an empty set. Ignoring.");
+      return;
+    }
+    RSGroupInfoManager manager = getRSGroupInfoManager();
+    synchronized (manager) {
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().preMoveTables(tables, targetGroup);
+      }
+
+      if(targetGroup != null) {
+        RSGroupInfo destGroup = manager.getRSGroup(targetGroup);
+        if(destGroup == null) {
+          throw new ConstraintException("Target group does not exist: "+targetGroup);
+        }
+        if(destGroup.getServers().size() < 1) {
+          throw new ConstraintException("Target group must have at least one server.");
+        }
+      }
+
+      for(TableName table : tables) {
+        String srcGroup = manager.getRSGroupOfTable(table);
+        if(srcGroup != null && srcGroup.equals(targetGroup)) {
+          throw new ConstraintException(
+              "Source group is the same as target group for table "+table+" :"+srcGroup);
+        }
+      }
+      manager.moveTables(tables, targetGroup);
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().postMoveTables(tables, targetGroup);
+      }
+    }
+    for(TableName table: tables) {
+      TableLock lock = master.getTableLockManager().writeLock(table, "Group: table move");
+      try {
+        lock.acquire();
+        for (HRegionInfo region :
+            master.getAssignmentManager().getRegionStates().getRegionsOfTable(table)) {
+          master.getAssignmentManager().unassign(region);
+        }
+      } finally {
+        lock.release();
+      }
+    }
+  }
+
+  @Override
+  public void addRSGroup(String name) throws IOException {
+    if (master.getMasterCoprocessorHost() != null) {
+      master.getMasterCoprocessorHost().preAddRSGroup(name);
+    }
+    getRSGroupInfoManager().addRSGroup(new RSGroupInfo(name));
+    if (master.getMasterCoprocessorHost() != null) {
+      master.getMasterCoprocessorHost().postAddRSGroup(name);
+    }
+  }
+
+  @Override
+  public void removeRSGroup(String name) throws IOException {
+    RSGroupInfoManager manager = getRSGroupInfoManager();
+    synchronized (manager) {
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().preRemoveRSGroup(name);
+      }
+      RSGroupInfo RSGroupInfo = getRSGroupInfoManager().getRSGroup(name);
+      if(RSGroupInfo == null) {
+        throw new ConstraintException("Group "+name+" does not exist");
+      }
+      int tableCount = RSGroupInfo.getTables().size();
+      if (tableCount > 0) {
+        throw new ConstraintException("Group "+name+" must have no associated tables: "+tableCount);
+      }
+      int serverCount = RSGroupInfo.getServers().size();
+      if(serverCount > 0) {
+        throw new ConstraintException(
+            "Group "+name+" must have no associated servers: "+serverCount);
+      }
+      for(NamespaceDescriptor ns: master.getClusterSchema().getNamespaces()) {
+        String nsGroup = ns.getConfigurationValue(RSGroupInfo.NAMESPACEDESC_PROP_GROUP);
+        if(nsGroup != null &&  nsGroup.equals(name)) {
+          throw new ConstraintException("Group "+name+" is referenced by namespace: "+ns.getName());
+        }
+      }
+      manager.removeRSGroup(name);
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().postRemoveRSGroup(name);
+      }
+    }
+  }
+
+  @Override
+  public boolean balanceRSGroup(String groupName) throws IOException {
+    ServerManager serverManager = master.getServerManager();
+    AssignmentManager assignmentManager = master.getAssignmentManager();
+    LoadBalancer balancer = master.getLoadBalancer();
+
+    boolean balancerRan;
+    synchronized (balancer) {
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().preBalanceRSGroup(groupName);
+      }
+      if (getRSGroupInfo(groupName) == null) {
+        throw new ConstraintException("Group does not exist: "+groupName);
+      }
+      // Only allow one balance run at at time.
+      Map<String, RegionState> groupRIT = rsGroupGetRegionsInTransition(groupName);
+      if (groupRIT.size() > 0) {
+        LOG.debug("Not running balancer because " +
+          groupRIT.size() +
+          " region(s) in transition: " +
+          StringUtils.abbreviate(
+              master.getAssignmentManager().getRegionStates().getRegionsInTransition().toString(),
+              256));
+        return false;
+      }
+      if (serverManager.areDeadServersInProgress()) {
+        LOG.debug("Not running balancer because processing dead regionserver(s): " +
+            serverManager.getDeadServers());
+        return false;
+      }
+
+      //We balance per group instead of per table
+      List<RegionPlan> plans = new ArrayList<RegionPlan>();
+      for(Map.Entry<TableName, Map<ServerName, List<HRegionInfo>>> tableMap:
+          getRSGroupAssignmentsByTable(groupName).entrySet()) {
+        LOG.info("Creating partial plan for table "+tableMap.getKey()+": "+tableMap.getValue());
+        List<RegionPlan> partialPlans = balancer.balanceCluster(tableMap.getValue());
+        LOG.info("Partial plan for table "+tableMap.getKey()+": "+partialPlans);
+        if (partialPlans != null) {
+          plans.addAll(partialPlans);
+        }
+      }
+      long startTime = System.currentTimeMillis();
+      balancerRan = plans != null;
+      if (plans != null && !plans.isEmpty()) {
+        LOG.info("Group balance "+groupName+" starting with plan count: "+plans.size());
+        for (RegionPlan plan: plans) {
+          LOG.info("balance " + plan);
+          assignmentManager.balance(plan);
+        }
+        LOG.info("Group balance "+groupName+" completed after "+
+            (System.currentTimeMillis()-startTime)+" seconds");
+      }
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().postBalanceRSGroup(groupName, balancerRan);
+      }
+    }
+    return balancerRan;
+  }
+
+  @Override
+  public List<RSGroupInfo> listRSGroups() throws IOException {
+    return getRSGroupInfoManager().listRSGroups();
+  }
+
+  @Override
+  public RSGroupInfo getRSGroupOfServer(HostAndPort hostPort) throws IOException {
+    return getRSGroupInfoManager().getRSGroupOfServer(hostPort);
+  }
+
+  @InterfaceAudience.Private
+  public RSGroupInfoManager getRSGroupInfoManager() throws IOException {
+    return RSGroupInfoManager;
+  }
+
+  private Map<String, RegionState> rsGroupGetRegionsInTransition(String groupName)
+      throws IOException {
+    Map<String, RegionState> rit = Maps.newTreeMap();
+    AssignmentManager am = master.getAssignmentManager();
+    RSGroupInfo RSGroupInfo = getRSGroupInfo(groupName);
+    for(TableName tableName : RSGroupInfo.getTables()) {
+      for(HRegionInfo regionInfo: am.getRegionStates().getRegionsOfTable(tableName)) {
+        RegionState state =
+            master.getAssignmentManager().getRegionStates().getRegionTransitionState(regionInfo);
+        if(state != null) {
+          rit.put(regionInfo.getEncodedName(), state);
+        }
+      }
+    }
+    return rit;
+  }
+
+  private Map<TableName, Map<ServerName, List<HRegionInfo>>>
+      getRSGroupAssignmentsByTable(String groupName) throws IOException {
+    Map<TableName, Map<ServerName, List<HRegionInfo>>> result = Maps.newHashMap();
+    RSGroupInfo RSGroupInfo = getRSGroupInfo(groupName);
+    Map<TableName, Map<ServerName, List<HRegionInfo>>> assignments = Maps.newHashMap();
+    for(Map.Entry<HRegionInfo, ServerName> entry:
+        master.getAssignmentManager().getRegionStates().getRegionAssignments().entrySet()) {
+      TableName currTable = entry.getKey().getTable();
+      ServerName currServer = entry.getValue();
+      HRegionInfo currRegion = entry.getKey();
+      if(RSGroupInfo.getTables().contains(currTable)) {
+        if(!assignments.containsKey(entry.getKey().getTable())) {
+          assignments.put(currTable, new HashMap<ServerName, List<HRegionInfo>>());
+        }
+        if(!assignments.get(currTable).containsKey(currServer)) {
+          assignments.get(currTable).put(currServer, new ArrayList<HRegionInfo>());
+        }
+        assignments.get(currTable).get(currServer).add(currRegion);
+      }
+    }
+
+    Map<ServerName, List<HRegionInfo>> serverMap = Maps.newHashMap();
+    for(ServerName serverName: master.getServerManager().getOnlineServers().keySet()) {
+      if(RSGroupInfo.getServers().contains(serverName.getHostPort())) {
+        serverMap.put(serverName, Collections.EMPTY_LIST);
+      }
+    }
+
+    //add all tables that are members of the group
+    for(TableName tableName : RSGroupInfo.getTables()) {
+      if(assignments.containsKey(tableName)) {
+        result.put(tableName, new HashMap<ServerName, List<HRegionInfo>>());
+        result.get(tableName).putAll(serverMap);
+        result.get(tableName).putAll(assignments.get(tableName));
+        LOG.debug("Adding assignments for "+tableName+": "+assignments.get(tableName));
+      }
+    }
+
+    return result;
+  }
+
+  public void prepareRSGroupForTable(HTableDescriptor desc) throws IOException {
+    String groupName =
+        master.getClusterSchema().getNamespace(desc.getTableName().getNamespaceAsString())
+                .getConfigurationValue(RSGroupInfo.NAMESPACEDESC_PROP_GROUP);
+    if (groupName == null) {
+      groupName = RSGroupInfo.DEFAULT_GROUP;
+    }
+    RSGroupInfo RSGroupInfo = getRSGroupInfo(groupName);
+    if (RSGroupInfo == null) {
+      throw new ConstraintException("RSGroup " + groupName + " does not exist.");
+    }
+    if (!RSGroupInfo.containsTable(desc.getTableName())) {
+      LOG.debug("Pre-moving table " + desc.getTableName() + " to rsgroup " + groupName);
+      moveTables(Sets.newHashSet(desc.getTableName()), groupName);
+    }
+  }
+
+  public void cleanupRSGroupForTable(TableName tableName) throws IOException {
+    try {
+      RSGroupInfo group = getRSGroupInfoOfTable(tableName);
+      if (group != null) {
+        LOG.debug("Removing deleted table from table rsgroup " + group.getName());
+        moveTables(Sets.newHashSet(tableName), null);
+      }
+    } catch (ConstraintException ex) {
+      LOG.debug("Failed to perform rsgroup information cleanup for table: " + tableName, ex);
+    } catch (IOException ex) {
+      LOG.debug("Failed to perform rsgroup information cleanup for table: " + tableName, ex);
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupBasedLoadBalancer.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupBasedLoadBalancer.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupBasedLoadBalancer.java
new file mode 100644
index 0000000..c4b4bf3
--- /dev/null
+++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupBasedLoadBalancer.java
@@ -0,0 +1,422 @@
+/**
+ * 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.
+ */
+
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.net.HostAndPort;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.ClusterStatus;
+import org.apache.hadoop.hbase.HBaseIOException;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.master.LoadBalancer;
+import org.apache.hadoop.hbase.master.MasterServices;
+import org.apache.hadoop.hbase.master.RegionPlan;
+import org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer;
+import org.apache.hadoop.util.ReflectionUtils;
+
+/**
+ * GroupBasedLoadBalancer, used when Region Server Grouping is configured (HBase-6721)
+ * It does region balance based on a table's group membership.
+ *
+ * Most assignment methods contain two exclusive code paths: Online - when the group
+ * table is online and Offline - when it is unavailable.
+ *
+ * During Offline, assignments are assigned based on cached information in zookeeper.
+ * If unavailable (ie bootstrap) then regions are assigned randomly.
+ *
+ * Once the GROUP table has been assigned, the balancer switches to Online and will then
+ * start providing appropriate assignments for user tables.
+ *
+ */
+@InterfaceAudience.Private
+public class RSGroupBasedLoadBalancer implements RSGroupableBalancer, LoadBalancer {
+  /** Config for pluggable load balancers */
+  public static final String HBASE_GROUP_LOADBALANCER_CLASS = "hbase.group.grouploadbalancer.class";
+
+  private static final Log LOG = LogFactory.getLog(RSGroupBasedLoadBalancer.class);
+
+  private Configuration config;
+  private ClusterStatus clusterStatus;
+  private MasterServices masterServices;
+  private RSGroupInfoManager RSGroupInfoManager;
+  private LoadBalancer internalBalancer;
+
+  //used during reflection by LoadBalancerFactory
+  @InterfaceAudience.Private
+  public RSGroupBasedLoadBalancer() {
+  }
+
+  //This constructor should only be used for unit testing
+  @InterfaceAudience.Private
+  public RSGroupBasedLoadBalancer(RSGroupInfoManager RSGroupInfoManager) {
+    this.RSGroupInfoManager = RSGroupInfoManager;
+  }
+
+  @Override
+  public Configuration getConf() {
+    return config;
+  }
+
+  @Override
+  public void setConf(Configuration conf) {
+    this.config = conf;
+  }
+
+  @Override
+  public void setClusterStatus(ClusterStatus st) {
+    this.clusterStatus = st;
+  }
+
+  @Override
+  public void setMasterServices(MasterServices masterServices) {
+    this.masterServices = masterServices;
+  }
+
+  @Override
+  public List<RegionPlan> balanceCluster(TableName tableName, Map<ServerName, List<HRegionInfo>>
+      clusterState) throws HBaseIOException {
+    return balanceCluster(clusterState);
+  }
+
+  @Override
+  public List<RegionPlan> balanceCluster(Map<ServerName, List<HRegionInfo>> clusterState)
+      throws HBaseIOException {
+    if (!isOnline()) {
+      throw new ConstraintException(RSGroupInfoManager.RSGROUP_TABLE_NAME +
+          " is not online, unable to perform balance");
+    }
+
+    Map<ServerName,List<HRegionInfo>> correctedState = correctAssignments(clusterState);
+    List<RegionPlan> regionPlans = new ArrayList<RegionPlan>();
+
+    List<HRegionInfo> misplacedRegions = correctedState.get(LoadBalancer.BOGUS_SERVER_NAME);
+    for (HRegionInfo regionInfo : misplacedRegions) {
+      regionPlans.add(new RegionPlan(regionInfo, null, null));
+    }
+    try {
+      for (RSGroupInfo info : RSGroupInfoManager.listRSGroups()) {
+        Map<ServerName, List<HRegionInfo>> groupClusterState =
+            new HashMap<ServerName, List<HRegionInfo>>();
+        for (HostAndPort sName : info.getServers()) {
+          for(ServerName curr: clusterState.keySet()) {
+            if(curr.getHostPort().equals(sName)) {
+              groupClusterState.put(curr, correctedState.get(curr));
+            }
+          }
+        }
+        List<RegionPlan> groupPlans = this.internalBalancer
+            .balanceCluster(groupClusterState);
+        if (groupPlans != null) {
+          regionPlans.addAll(groupPlans);
+        }
+      }
+    } catch (IOException exp) {
+      LOG.warn("Exception while balancing cluster.", exp);
+      regionPlans.clear();
+    }
+    return regionPlans;
+  }
+
+  @Override
+  public Map<ServerName, List<HRegionInfo>> roundRobinAssignment(
+      List<HRegionInfo> regions, List<ServerName> servers) throws HBaseIOException {
+    Map<ServerName, List<HRegionInfo>> assignments = Maps.newHashMap();
+    ListMultimap<String,HRegionInfo> regionMap = ArrayListMultimap.create();
+    ListMultimap<String,ServerName> serverMap = ArrayListMultimap.create();
+    generateGroupMaps(regions, servers, regionMap, serverMap);
+    for(String groupKey : regionMap.keySet()) {
+      if (regionMap.get(groupKey).size() > 0) {
+        Map<ServerName, List<HRegionInfo>> result =
+            this.internalBalancer.roundRobinAssignment(
+                regionMap.get(groupKey),
+                serverMap.get(groupKey));
+        if(result != null) {
+          assignments.putAll(result);
+        }
+      }
+    }
+    return assignments;
+  }
+
+  @Override
+  public Map<ServerName, List<HRegionInfo>> retainAssignment(
+      Map<HRegionInfo, ServerName> regions, List<ServerName> servers) throws HBaseIOException {
+    try {
+      Map<ServerName, List<HRegionInfo>> assignments = new TreeMap<ServerName, List<HRegionInfo>>();
+      ListMultimap<String, HRegionInfo> groupToRegion = ArrayListMultimap.create();
+      Set<HRegionInfo> misplacedRegions = getMisplacedRegions(regions);
+      for (HRegionInfo region : regions.keySet()) {
+        if (!misplacedRegions.contains(region)) {
+          String groupName = RSGroupInfoManager.getRSGroupOfTable(region.getTable());
+          groupToRegion.put(groupName, region);
+        }
+      }
+      // Now the "groupToRegion" map has only the regions which have correct
+      // assignments.
+      for (String key : groupToRegion.keySet()) {
+        Map<HRegionInfo, ServerName> currentAssignmentMap = new TreeMap<HRegionInfo, ServerName>();
+        List<HRegionInfo> regionList = groupToRegion.get(key);
+        RSGroupInfo info = RSGroupInfoManager.getRSGroup(key);
+        List<ServerName> candidateList = filterOfflineServers(info, servers);
+        for (HRegionInfo region : regionList) {
+          currentAssignmentMap.put(region, regions.get(region));
+        }
+        if(candidateList.size() > 0) {
+          assignments.putAll(this.internalBalancer.retainAssignment(
+              currentAssignmentMap, candidateList));
+        }
+      }
+
+      for (HRegionInfo region : misplacedRegions) {
+        String groupName = RSGroupInfoManager.getRSGroupOfTable(
+            region.getTable());
+        RSGroupInfo info = RSGroupInfoManager.getRSGroup(groupName);
+        List<ServerName> candidateList = filterOfflineServers(info, servers);
+        ServerName server = this.internalBalancer.randomAssignment(region,
+            candidateList);
+        if (server != null && !assignments.containsKey(server)) {
+          assignments.put(server, new ArrayList<HRegionInfo>());
+        } else if (server != null) {
+          assignments.get(server).add(region);
+        } else {
+          //if not server is available assign to bogus so it ends up in RIT
+          if(!assignments.containsKey(LoadBalancer.BOGUS_SERVER_NAME)) {
+            assignments.put(LoadBalancer.BOGUS_SERVER_NAME, new ArrayList<HRegionInfo>());
+          }
+          assignments.get(LoadBalancer.BOGUS_SERVER_NAME).add(region);
+        }
+      }
+      return assignments;
+    } catch (IOException e) {
+      throw new HBaseIOException("Failed to do online retain assignment", e);
+    }
+  }
+
+  @Override
+  public ServerName randomAssignment(HRegionInfo region,
+      List<ServerName> servers) throws HBaseIOException {
+    ListMultimap<String,HRegionInfo> regionMap = LinkedListMultimap.create();
+    ListMultimap<String,ServerName> serverMap = LinkedListMultimap.create();
+    generateGroupMaps(Lists.newArrayList(region), servers, regionMap, serverMap);
+    List<ServerName> filteredServers = serverMap.get(regionMap.keySet().iterator().next());
+    return this.internalBalancer.randomAssignment(region, filteredServers);
+  }
+
+  private void generateGroupMaps(
+    List<HRegionInfo> regions,
+    List<ServerName> servers,
+    ListMultimap<String, HRegionInfo> regionMap,
+    ListMultimap<String, ServerName> serverMap) throws HBaseIOException {
+    try {
+      for (HRegionInfo region : regions) {
+        String groupName = RSGroupInfoManager.getRSGroupOfTable(region.getTable());
+        if(groupName == null) {
+          LOG.warn("Group for table "+region.getTable()+" is null");
+        }
+        regionMap.put(groupName, region);
+      }
+      for (String groupKey : regionMap.keySet()) {
+        RSGroupInfo info = RSGroupInfoManager.getRSGroup(groupKey);
+        serverMap.putAll(groupKey, filterOfflineServers(info, servers));
+        if(serverMap.get(groupKey).size() < 1) {
+          serverMap.put(groupKey, LoadBalancer.BOGUS_SERVER_NAME);
+        }
+      }
+    } catch(IOException e) {
+      throw new HBaseIOException("Failed to generate group maps", e);
+    }
+  }
+
+  private List<ServerName> filterOfflineServers(RSGroupInfo RSGroupInfo,
+                                                List<ServerName> onlineServers) {
+    if (RSGroupInfo != null) {
+      return filterServers(RSGroupInfo.getServers(), onlineServers);
+    } else {
+      LOG.debug("Group Information found to be null. Some regions might be unassigned.");
+      return Collections.EMPTY_LIST;
+    }
+  }
+
+  /**
+   * Filter servers based on the online servers.
+   *
+   * @param servers
+   *          the servers
+   * @param onlineServers
+   *          List of servers which are online.
+   * @return the list
+   */
+  private List<ServerName> filterServers(Collection<HostAndPort> servers,
+      Collection<ServerName> onlineServers) {
+    ArrayList<ServerName> finalList = new ArrayList<ServerName>();
+    for (HostAndPort server : servers) {
+      for(ServerName curr: onlineServers) {
+        if(curr.getHostPort().equals(server)) {
+          finalList.add(curr);
+        }
+      }
+    }
+    return finalList;
+  }
+
+  private ListMultimap<String, HRegionInfo> groupRegions(
+      List<HRegionInfo> regionList) throws IOException {
+    ListMultimap<String, HRegionInfo> regionGroup = ArrayListMultimap
+        .create();
+    for (HRegionInfo region : regionList) {
+      String groupName = RSGroupInfoManager.getRSGroupOfTable(region.getTable());
+      regionGroup.put(groupName, region);
+    }
+    return regionGroup;
+  }
+
+  private Set<HRegionInfo> getMisplacedRegions(
+      Map<HRegionInfo, ServerName> regions) throws IOException {
+    Set<HRegionInfo> misplacedRegions = new HashSet<HRegionInfo>();
+    for (HRegionInfo region : regions.keySet()) {
+      ServerName assignedServer = regions.get(region);
+      RSGroupInfo info =
+          RSGroupInfoManager.getRSGroup(RSGroupInfoManager.getRSGroupOfTable(region.getTable()));
+      if (assignedServer != null &&
+          (info == null || !info.containsServer(assignedServer.getHostPort()))) {
+        LOG.debug("Found misplaced region: " + region.getRegionNameAsString() +
+            " on server: " + assignedServer +
+            " found in group: " +
+            RSGroupInfoManager.getRSGroupOfServer(assignedServer.getHostPort()) +
+            " outside of group: " + info.getName());
+        misplacedRegions.add(region);
+      }
+    }
+    return misplacedRegions;
+  }
+
+  private Map<ServerName, List<HRegionInfo>> correctAssignments(
+       Map<ServerName, List<HRegionInfo>> existingAssignments){
+    Map<ServerName, List<HRegionInfo>> correctAssignments =
+        new TreeMap<ServerName, List<HRegionInfo>>();
+    List<HRegionInfo> misplacedRegions = new LinkedList<HRegionInfo>();
+    correctAssignments.put(LoadBalancer.BOGUS_SERVER_NAME, new LinkedList<HRegionInfo>());
+    for (ServerName sName : existingAssignments.keySet()) {
+      correctAssignments.put(sName, new LinkedList<HRegionInfo>());
+      List<HRegionInfo> regions = existingAssignments.get(sName);
+      for (HRegionInfo region : regions) {
+        RSGroupInfo info = null;
+        try {
+          info = RSGroupInfoManager.getRSGroup(
+              RSGroupInfoManager.getRSGroupOfTable(region.getTable()));
+        }catch(IOException exp){
+          LOG.debug("Group information null for region of table " + region.getTable(),
+              exp);
+        }
+        if ((info == null) || (!info.containsServer(sName.getHostPort()))) {
+          correctAssignments.get(LoadBalancer.BOGUS_SERVER_NAME).add(region);
+        } else {
+          correctAssignments.get(sName).add(region);
+        }
+      }
+    }
+
+    //TODO bulk unassign?
+    //unassign misplaced regions, so that they are assigned to correct groups.
+    for(HRegionInfo info: misplacedRegions) {
+      this.masterServices.getAssignmentManager().unassign(info);
+    }
+    return correctAssignments;
+  }
+
+  @Override
+  public void initialize() throws HBaseIOException {
+    try {
+      if (RSGroupInfoManager == null) {
+        List<RSGroupAdminEndpoint> cps =
+          masterServices.getMasterCoprocessorHost().findCoprocessors(RSGroupAdminEndpoint.class);
+        if (cps.size() != 1) {
+          String msg = "Expected one implementation of GroupAdminEndpoint but found " + cps.size();
+          LOG.error(msg);
+          throw new HBaseIOException(msg);
+        }
+        RSGroupInfoManager = cps.get(0).getGroupInfoManager();
+      }
+    } catch (IOException e) {
+      throw new HBaseIOException("Failed to initialize GroupInfoManagerImpl", e);
+    }
+
+    // Create the balancer
+    Class<? extends LoadBalancer> balancerKlass = config.getClass(
+        HBASE_GROUP_LOADBALANCER_CLASS,
+        StochasticLoadBalancer.class, LoadBalancer.class);
+    internalBalancer = ReflectionUtils.newInstance(balancerKlass, config);
+    internalBalancer.setClusterStatus(clusterStatus);
+    internalBalancer.setMasterServices(masterServices);
+    internalBalancer.setConf(config);
+    internalBalancer.initialize();
+  }
+
+  public boolean isOnline() {
+    return RSGroupInfoManager != null && RSGroupInfoManager.isOnline();
+  }
+
+  @Override
+  public void regionOnline(HRegionInfo regionInfo, ServerName sn) {
+  }
+
+  @Override
+  public void regionOffline(HRegionInfo regionInfo) {
+  }
+
+  @Override
+  public void onConfigurationChange(Configuration conf) {
+    //DO nothing for now
+  }
+
+  @Override
+  public void stop(String why) {
+  }
+
+  @Override
+  public boolean isStopped() {
+    return false;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
new file mode 100644
index 0000000..434c85f
--- /dev/null
+++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
@@ -0,0 +1,132 @@
+/**
+ * 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.
+ */
+
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.net.HostAndPort;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.util.Bytes;
+
+/**
+ * Interface used to manage RSGroupInfo storage. An implementation
+ * has the option to support offline mode.
+ * See {@link RSGroupBasedLoadBalancer}
+ */
+public interface RSGroupInfoManager {
+  //Assigned before user tables
+  public static final TableName RSGROUP_TABLE_NAME =
+      TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "rsgroup");
+  public static final byte[] RSGROUP_TABLE_NAME_BYTES = RSGROUP_TABLE_NAME.toBytes();
+  public static final String rsGroupZNode = "rsgroup";
+  public static final byte[] META_FAMILY_BYTES = Bytes.toBytes("m");
+  public static final byte[] META_QUALIFIER_BYTES = Bytes.toBytes("i");
+  public static final byte[] ROW_KEY = {0};
+
+
+  /**
+   * Adds the group.
+   *
+   * @param rsGroupInfo the group name
+   * @throws java.io.IOException Signals that an I/O exception has occurred.
+   */
+  void addRSGroup(RSGroupInfo rsGroupInfo) throws IOException;
+
+  /**
+   * Remove a region server group.
+   *
+   * @param groupName the group name
+   * @throws java.io.IOException Signals that an I/O exception has occurred.
+   */
+  void removeRSGroup(String groupName) throws IOException;
+
+  /**
+   * move servers to a new group.
+   * @param hostPorts list of servers, must be part of the same group
+   * @param srcGroup groupName being moved from
+   * @param dstGroup groupName being moved to
+   * @return true if move was successful
+   * @throws java.io.IOException on move failure
+   */
+  boolean moveServers(Set<HostAndPort> hostPorts,
+                      String srcGroup, String dstGroup) throws IOException;
+
+  /**
+   * Gets the group info of server.
+   *
+   * @param hostPort the server
+   * @return An instance of RSGroupInfo
+   */
+  RSGroupInfo getRSGroupOfServer(HostAndPort hostPort) throws IOException;
+
+  /**
+   * Gets the group information.
+   *
+   * @param groupName the group name
+   * @return An instance of RSGroupInfo
+   */
+  RSGroupInfo getRSGroup(String groupName) throws IOException;
+
+  /**
+   * Get the group membership of a table
+   * @param tableName name of table to get group membership
+   * @return Group name of table
+   * @throws java.io.IOException on failure to retrive information
+   */
+  String getRSGroupOfTable(TableName tableName) throws IOException;
+
+  /**
+   * Set the group membership of a set of tables
+   *
+   * @param tableNames set of tables to move
+   * @param groupName name of group of tables to move to
+   * @throws java.io.IOException on failure to move
+   */
+  void moveTables(Set<TableName> tableNames, String groupName) throws IOException;
+
+  /**
+   * List the groups
+   *
+   * @return list of RSGroupInfo
+   * @throws java.io.IOException on failure
+   */
+  List<RSGroupInfo> listRSGroups() throws IOException;
+
+  /**
+   * Refresh/reload the group information from
+   * the persistent store
+   *
+   * @throws java.io.IOException on failure to refresh
+   */
+  void refresh() throws IOException;
+
+  /**
+   * Whether the manager is able to fully
+   * return group metadata
+   *
+   * @return whether the manager is in online mode
+   */
+  boolean isOnline();
+}


[2/7] hbase git commit: HBASE-6721 RegionServer Group based Assignment (Francis Liu)

Posted by en...@apache.org.
http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
new file mode 100644
index 0000000..9225e09
--- /dev/null
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java
@@ -0,0 +1,643 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.ClusterStatus;
+import org.apache.hadoop.hbase.HBaseCluster;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.RegionLoad;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.Waiter;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public abstract class TestRSGroupsBase {
+  protected static final Log LOG = LogFactory.getLog(TestRSGroupsBase.class);
+
+  //shared
+  protected final static String groupPrefix = "Group";
+  protected final static String tablePrefix = "Group";
+  protected final static SecureRandom rand = new SecureRandom();
+
+  //shared, cluster type specific
+  protected static HBaseTestingUtility TEST_UTIL;
+  protected static HBaseAdmin admin;
+  protected static HBaseCluster cluster;
+  protected static RSGroupAdmin rsGroupAdmin;
+
+  public final static long WAIT_TIMEOUT = 60000*5;
+  public final static int NUM_SLAVES_BASE = 4; //number of slaves for the smallest cluster
+
+
+
+  protected RSGroupInfo addGroup(RSGroupAdmin gAdmin, String groupName,
+                                 int serverCount) throws IOException, InterruptedException {
+    RSGroupInfo defaultInfo = gAdmin
+        .getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
+    assertTrue(defaultInfo != null);
+    assertTrue(defaultInfo.getServers().size() >= serverCount);
+    gAdmin.addRSGroup(groupName);
+
+    Set<HostAndPort> set = new HashSet<HostAndPort>();
+    for(HostAndPort server: defaultInfo.getServers()) {
+      if(set.size() == serverCount) {
+        break;
+      }
+      set.add(server);
+    }
+    gAdmin.moveServers(set, groupName);
+    RSGroupInfo result = gAdmin.getRSGroupInfo(groupName);
+    assertTrue(result.getServers().size() >= serverCount);
+    return result;
+  }
+
+  static void removeGroup(RSGroupAdminClient groupAdmin, String groupName) throws IOException {
+    RSGroupInfo RSGroupInfo = groupAdmin.getRSGroupInfo(groupName);
+    groupAdmin.moveTables(RSGroupInfo.getTables(), RSGroupInfo.DEFAULT_GROUP);
+    groupAdmin.moveServers(RSGroupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    groupAdmin.removeRSGroup(groupName);
+  }
+
+  protected void deleteTableIfNecessary() throws IOException {
+    for (HTableDescriptor desc : TEST_UTIL.getHBaseAdmin().listTables(tablePrefix+".*")) {
+      TEST_UTIL.deleteTable(desc.getTableName());
+    }
+  }
+
+  protected void deleteNamespaceIfNecessary() throws IOException {
+    for (NamespaceDescriptor desc : TEST_UTIL.getHBaseAdmin().listNamespaceDescriptors()) {
+      if(desc.getName().startsWith(tablePrefix)) {
+        admin.deleteNamespace(desc.getName());
+      }
+    }
+  }
+
+  protected void deleteGroups() throws IOException {
+    RSGroupAdmin groupAdmin = rsGroupAdmin.newClient(TEST_UTIL.getConnection());
+    for(RSGroupInfo group: groupAdmin.listRSGroups()) {
+      if(!group.getName().equals(RSGroupInfo.DEFAULT_GROUP)) {
+        groupAdmin.moveTables(group.getTables(), RSGroupInfo.DEFAULT_GROUP);
+        groupAdmin.moveServers(group.getServers(), RSGroupInfo.DEFAULT_GROUP);
+        groupAdmin.removeRSGroup(group.getName());
+      }
+    }
+  }
+
+  public Map<TableName, List<String>> getTableRegionMap() throws IOException {
+    Map<TableName, List<String>> map = Maps.newTreeMap();
+    Map<TableName, Map<ServerName, List<String>>> tableServerRegionMap
+        = getTableServerRegionMap();
+    for(TableName tableName : tableServerRegionMap.keySet()) {
+      if(!map.containsKey(tableName)) {
+        map.put(tableName, new LinkedList<String>());
+      }
+      for(List<String> subset: tableServerRegionMap.get(tableName).values()) {
+        map.get(tableName).addAll(subset);
+      }
+    }
+    return map;
+  }
+
+  public Map<TableName, Map<ServerName, List<String>>> getTableServerRegionMap()
+      throws IOException {
+    Map<TableName, Map<ServerName, List<String>>> map = Maps.newTreeMap();
+    ClusterStatus status = TEST_UTIL.getHBaseClusterInterface().getClusterStatus();
+    for(ServerName serverName : status.getServers()) {
+      for(RegionLoad rl : status.getLoad(serverName).getRegionsLoad().values()) {
+        TableName tableName = HRegionInfo.getTable(rl.getName());
+        if(!map.containsKey(tableName)) {
+          map.put(tableName, new TreeMap<ServerName, List<String>>());
+        }
+        if(!map.get(tableName).containsKey(serverName)) {
+          map.get(tableName).put(serverName, new LinkedList<String>());
+        }
+        map.get(tableName).get(serverName).add(rl.getNameAsString());
+      }
+    }
+    return map;
+  }
+
+  @Test
+  public void testBogusArgs() throws Exception {
+    assertNull(rsGroupAdmin.getRSGroupInfoOfTable(TableName.valueOf("nonexistent")));
+    assertNull(rsGroupAdmin.getRSGroupOfServer(HostAndPort.fromParts("bogus",123)));
+    assertNull(rsGroupAdmin.getRSGroupInfo("bogus"));
+
+    try {
+      rsGroupAdmin.removeRSGroup("bogus");
+      fail("Expected removing bogus group to fail");
+    } catch(ConstraintException ex) {
+      //expected
+    }
+
+    try {
+      rsGroupAdmin.moveTables(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus");
+      fail("Expected move with bogus group to fail");
+    } catch(ConstraintException ex) {
+      //expected
+    }
+
+    try {
+      rsGroupAdmin.moveServers(Sets.newHashSet(HostAndPort.fromParts("bogus",123)), "bogus");
+      fail("Expected move with bogus group to fail");
+    } catch(ConstraintException ex) {
+      //expected
+    }
+
+    try {
+      rsGroupAdmin.balanceRSGroup("bogus");
+      fail("Expected move with bogus group to fail");
+    } catch(ConstraintException ex) {
+      //expected
+    }
+  }
+
+  @Test
+  public void testCreateMultiRegion() throws IOException {
+    LOG.info("testCreateMultiRegion");
+    TableName tableName = TableName.valueOf(tablePrefix + "_testCreateMultiRegion");
+    byte[] end = {1,3,5,7,9};
+    byte[] start = {0,2,4,6,8};
+    byte[][] f = {Bytes.toBytes("f")};
+    TEST_UTIL.createTable(tableName, f,1,start,end,10);
+  }
+
+  @Test
+  public void testCreateAndDrop() throws Exception {
+    LOG.info("testCreateAndDrop");
+
+    final TableName tableName = TableName.valueOf(tablePrefix + "_testCreateAndDrop");
+    TEST_UTIL.createTable(tableName, Bytes.toBytes("cf"));
+    //wait for created table to be assigned
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return getTableRegionMap().get(tableName) != null;
+      }
+    });
+    TEST_UTIL.deleteTable(tableName);
+  }
+
+
+  @Test
+  public void testSimpleRegionServerMove() throws IOException,
+      InterruptedException {
+    LOG.info("testSimpleRegionServerMove");
+
+    int initNumGroups = rsGroupAdmin.listRSGroups().size();
+    RSGroupInfo appInfo = addGroup(rsGroupAdmin, getGroupName("testSimpleRegionServerMove"), 1);
+    RSGroupInfo adminInfo = addGroup(rsGroupAdmin, getGroupName("testSimpleRegionServerMove"), 1);
+    RSGroupInfo dInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
+    Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size());
+    assertEquals(1, adminInfo.getServers().size());
+    assertEquals(1, appInfo.getServers().size());
+    assertEquals(getNumServers() - 2, dInfo.getServers().size());
+    rsGroupAdmin.moveServers(appInfo.getServers(),
+        RSGroupInfo.DEFAULT_GROUP);
+    rsGroupAdmin.removeRSGroup(appInfo.getName());
+    rsGroupAdmin.moveServers(adminInfo.getServers(),
+        RSGroupInfo.DEFAULT_GROUP);
+    rsGroupAdmin.removeRSGroup(adminInfo.getName());
+    Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups);
+  }
+
+  // return the real number of region servers, excluding the master embedded region server in 2.0+
+  public int getNumServers() throws IOException {
+    ClusterStatus status = admin.getClusterStatus();
+    ServerName master = status.getMaster();
+    int count = 0;
+    for (ServerName sn : status.getServers()) {
+      if (!sn.equals(master)) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  @Test
+  public void testMoveServers() throws Exception {
+    LOG.info("testMoveServers");
+
+    //create groups and assign servers
+    addGroup(rsGroupAdmin, "bar", 3);
+    rsGroupAdmin.addRSGroup("foo");
+
+    RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
+    RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
+    assertEquals(3, barGroup.getServers().size());
+    assertEquals(0, fooGroup.getServers().size());
+
+    //test fail bogus server move
+    try {
+      rsGroupAdmin.moveServers(Sets.newHashSet(HostAndPort.fromString("foo:9999")),"foo");
+      fail("Bogus servers shouldn't have been successfully moved.");
+    } catch(IOException ex) {
+      String exp = "Server foo:9999 does not have a group.";
+      String msg = "Expected '"+exp+"' in exception message: ";
+      assertTrue(msg+" "+ex.getMessage(), ex.getMessage().contains(exp));
+    }
+
+    //test success case
+    LOG.info("moving servers "+barGroup.getServers()+" to group foo");
+    rsGroupAdmin.moveServers(barGroup.getServers(), fooGroup.getName());
+
+    barGroup = rsGroupAdmin.getRSGroupInfo("bar");
+    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
+    assertEquals(0,barGroup.getServers().size());
+    assertEquals(3,fooGroup.getServers().size());
+
+    LOG.info("moving servers "+fooGroup.getServers()+" to group default");
+    rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return getNumServers() ==
+        rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size();
+      }
+    });
+
+    fooGroup = rsGroupAdmin.getRSGroupInfo("foo");
+    assertEquals(0,fooGroup.getServers().size());
+
+    //test group removal
+    LOG.info("Remove group "+barGroup.getName());
+    rsGroupAdmin.removeRSGroup(barGroup.getName());
+    Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(barGroup.getName()));
+    LOG.info("Remove group "+fooGroup.getName());
+    rsGroupAdmin.removeRSGroup(fooGroup.getName());
+    Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName()));
+  }
+
+  @Test
+  public void testTableMoveTruncateAndDrop() throws Exception {
+    LOG.info("testTableMove");
+
+    final TableName tableName = TableName.valueOf(tablePrefix + "_testTableMoveAndDrop");
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    String newGroupName = getGroupName("testTableMove");
+    final RSGroupInfo newGroup = addGroup(rsGroupAdmin, newGroupName, 2);
+
+    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regions = getTableRegionMap().get(tableName);
+        if (regions == null)
+          return false;
+        return getTableRegionMap().get(tableName).size() >= 5;
+      }
+    });
+
+    RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
+    assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
+
+    //change table's group
+    LOG.info("Moving table "+tableName+" to "+newGroup.getName());
+    rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
+
+    //verify group change
+    Assert.assertEquals(newGroup.getName(),
+        rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
+
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        Map<ServerName, List<String>> serverMap = getTableServerRegionMap().get(tableName);
+        int count = 0;
+        if (serverMap != null) {
+          for (ServerName rs : serverMap.keySet()) {
+            if (newGroup.containsServer(rs.getHostPort())) {
+              count += serverMap.get(rs).size();
+            }
+          }
+        }
+        return count == 5;
+      }
+    });
+
+    //test truncate
+    admin.disableTable(tableName);
+    admin.truncateTable(tableName, true);
+    Assert.assertEquals(1, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
+    Assert.assertEquals(tableName, rsGroupAdmin.getRSGroupInfo(
+        newGroup.getName()).getTables().first());
+
+    //verify removed table is removed from group
+    TEST_UTIL.deleteTable(tableName);
+    Assert.assertEquals(0, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
+  }
+
+  @Test
+  public void testGroupBalance() throws Exception {
+    LOG.info("testGroupBalance");
+    String newGroupName = getGroupName("testGroupBalance");
+    final RSGroupInfo newGroup = addGroup(rsGroupAdmin, newGroupName, 3);
+
+    final TableName tableName = TableName.valueOf(tablePrefix+"_ns", "testGroupBalance");
+    admin.createNamespace(
+        NamespaceDescriptor.create(tableName.getNamespaceAsString())
+            .addConfiguration(RSGroupInfo.NAMESPACEDESC_PROP_GROUP, newGroupName).build());
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    final HTableDescriptor desc = new HTableDescriptor(tableName);
+    desc.addFamily(new HColumnDescriptor("f"));
+    byte [] startKey = Bytes.toBytes("aaaaa");
+    byte [] endKey = Bytes.toBytes("zzzzz");
+    admin.createTable(desc, startKey, endKey, 6);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regions = getTableRegionMap().get(tableName);
+        if (regions == null) {
+          return false;
+        }
+        return regions.size() >= 6;
+      }
+    });
+
+    //make assignment uneven, move all regions to one server
+    Map<ServerName,List<String>> assignMap =
+        getTableServerRegionMap().get(tableName);
+    final ServerName first = assignMap.entrySet().iterator().next().getKey();
+    for(HRegionInfo region: admin.getTableRegions(tableName)) {
+      if(!assignMap.get(first).contains(region)) {
+        admin.move(region.getEncodedNameAsBytes(), Bytes.toBytes(first.getServerName()));
+      }
+    }
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        Map<ServerName, List<String>> map = getTableServerRegionMap().get(tableName);
+        if (map == null) {
+          return true;
+        }
+        List<String> regions = map.get(first);
+        if (regions == null) {
+          return true;
+        }
+        return regions.size() >= 6;
+      }
+    });
+
+    //balance the other group and make sure it doesn't affect the new group
+    rsGroupAdmin.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP);
+    assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size());
+
+    rsGroupAdmin.balanceRSGroup(newGroupName);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        for (List<String> regions : getTableServerRegionMap().get(tableName).values()) {
+          if (2 != regions.size()) {
+            return false;
+          }
+        }
+        return true;
+      }
+    });
+  }
+
+  @Test
+  public void testRegionMove() throws Exception {
+    LOG.info("testRegionMove");
+
+    final RSGroupInfo newGroup = addGroup(rsGroupAdmin, getGroupName("testRegionMove"), 1);
+    final TableName tableName = TableName.valueOf(tablePrefix + rand.nextInt());
+    final byte[] familyNameBytes = Bytes.toBytes("f");
+    // All the regions created below will be assigned to the default group.
+    TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 6);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        List<String> regions = getTableRegionMap().get(tableName);
+        if (regions == null)
+          return false;
+        return getTableRegionMap().get(tableName).size() >= 6;
+      }
+    });
+
+    //get target region to move
+    Map<ServerName,List<String>> assignMap =
+        getTableServerRegionMap().get(tableName);
+    String targetRegion = null;
+    for(ServerName server : assignMap.keySet()) {
+      targetRegion = assignMap.get(server).size() > 0 ? assignMap.get(server).get(0) : null;
+      if(targetRegion != null) {
+        break;
+      }
+    }
+    //get server which is not a member of new group
+    ServerName targetServer = null;
+    for(ServerName server : admin.getClusterStatus().getServers()) {
+      if(!newGroup.containsServer(server.getHostPort())) {
+        targetServer = server;
+        break;
+      }
+    }
+
+    final AdminProtos.AdminService.BlockingInterface targetRS =
+        admin.getConnection().getAdmin(targetServer);
+
+    //move target server to group
+    rsGroupAdmin.moveServers(Sets.newHashSet(targetServer.getHostPort()),
+        newGroup.getName());
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return ProtobufUtil.getOnlineRegions(targetRS).size() <= 0;
+      }
+    });
+
+    // Lets move this region to the new group.
+    TEST_UTIL.getHBaseAdmin().move(Bytes.toBytes(HRegionInfo.encodeRegionName(Bytes.toBytes(targetRegion))),
+        Bytes.toBytes(targetServer.getServerName()));
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return
+            getTableRegionMap().get(tableName) != null &&
+                getTableRegionMap().get(tableName).size() == 6 &&
+                admin.getClusterStatus().getRegionsInTransition().size() < 1;
+      }
+    });
+
+    //verify that targetServer didn't open it
+    assertFalse(ProtobufUtil.getOnlineRegions(targetRS).contains(targetRegion));
+  }
+
+  @Test
+  public void testFailRemoveGroup() throws IOException, InterruptedException {
+    LOG.info("testFailRemoveGroup");
+
+    int initNumGroups = rsGroupAdmin.listRSGroups().size();
+    addGroup(rsGroupAdmin, "bar", 3);
+    TableName tableName = TableName.valueOf(tablePrefix+"_my_table");
+    TEST_UTIL.createTable(tableName, Bytes.toBytes("f"));
+    rsGroupAdmin.moveTables(Sets.newHashSet(tableName), "bar");
+    RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
+    //group is not empty therefore it should fail
+    try {
+      rsGroupAdmin.removeRSGroup(barGroup.getName());
+      fail("Expected remove group to fail");
+    } catch(IOException e) {
+    }
+    //group cannot lose all it's servers therefore it should fail
+    try {
+      rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+      fail("Expected move servers to fail");
+    } catch(IOException e) {
+    }
+
+    rsGroupAdmin.moveTables(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP);
+    try {
+      rsGroupAdmin.removeRSGroup(barGroup.getName());
+      fail("Expected move servers to fail");
+    } catch(IOException e) {
+    }
+
+    rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
+    rsGroupAdmin.removeRSGroup(barGroup.getName());
+
+    Assert.assertEquals(initNumGroups, rsGroupAdmin.listRSGroups().size());
+  }
+
+  @Test
+  public void testKillRS() throws Exception {
+    LOG.info("testKillRS");
+    RSGroupInfo appInfo = addGroup(rsGroupAdmin, "appInfo", 1);
+
+
+    final TableName tableName = TableName.valueOf(tablePrefix+"_ns", "_testKillRS");
+    admin.createNamespace(
+        NamespaceDescriptor.create(tableName.getNamespaceAsString())
+            .addConfiguration(RSGroupInfo.NAMESPACEDESC_PROP_GROUP, appInfo.getName()).build());
+    final HTableDescriptor desc = new HTableDescriptor(tableName);
+    desc.addFamily(new HColumnDescriptor("f"));
+    admin.createTable(desc);
+    //wait for created table to be assigned
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return getTableRegionMap().get(desc.getTableName()) != null;
+      }
+    });
+
+    ServerName targetServer = ServerName.parseServerName(
+        appInfo.getServers().iterator().next().toString());
+    AdminProtos.AdminService.BlockingInterface targetRS =
+        admin.getConnection().getAdmin(targetServer);
+    HRegionInfo targetRegion = ProtobufUtil.getOnlineRegions(targetRS).get(0);
+    Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size());
+
+    try {
+      //stopping may cause an exception
+      //due to the connection loss
+      targetRS.stopServer(null,
+          AdminProtos.StopServerRequest.newBuilder().setReason("Die").build());
+    } catch(Exception e) {
+    }
+    assertFalse(cluster.getClusterStatus().getServers().contains(targetServer));
+
+    //wait for created table to be assigned
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return cluster.getClusterStatus().getRegionsInTransition().size() == 0;
+      }
+    });
+    Set<HostAndPort> newServers = Sets.newHashSet();
+    newServers.add(
+        rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().iterator().next());
+    rsGroupAdmin.moveServers(newServers, appInfo.getName());
+
+    //Make sure all the table's regions get reassigned
+    //disabling the table guarantees no conflicting assign/unassign (ie SSH) happens
+    admin.disableTable(tableName);
+    admin.enableTable(tableName);
+
+    //wait for region to be assigned
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return cluster.getClusterStatus().getRegionsInTransition().size() == 0;
+      }
+    });
+
+    targetServer = ServerName.parseServerName(
+        newServers.iterator().next().toString());
+    targetRS =
+        admin.getConnection().getAdmin(targetServer);
+    Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size());
+    Assert.assertEquals(tableName,
+        ProtobufUtil.getOnlineRegions(targetRS).get(0).getTable());
+  }
+
+  @Test
+  public void testValidGroupNames() throws IOException {
+    String[] badNames = {"foo*","foo@","-"};
+    String[] goodNames = {"foo_123"};
+
+    for(String entry: badNames) {
+      try {
+        rsGroupAdmin.addRSGroup(entry);
+        fail("Expected a constraint exception for: "+entry);
+      } catch(ConstraintException ex) {
+        //expected
+      }
+    }
+
+    for(String entry: goodNames) {
+      rsGroupAdmin.addRSGroup(entry);
+    }
+  }
+
+  private String getGroupName(String baseName) {
+    return groupPrefix+"_"+baseName+"_"+rand.nextInt(Integer.MAX_VALUE);
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsOfflineMode.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsOfflineMode.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsOfflineMode.java
new file mode 100644
index 0000000..b89ea0e
--- /dev/null
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsOfflineMode.java
@@ -0,0 +1,187 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Sets;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.HBaseCluster;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.MiniHBaseCluster;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.Waiter;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.master.HMaster;
+import org.apache.hadoop.hbase.master.ServerManager;
+import org.apache.hadoop.hbase.regionserver.HRegionServer;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+
+//This tests that GroupBasedBalancer will use data in zk
+//to do balancing during master startup
+//This does not test retain assignment
+@Category(MediumTests.class)
+public class TestRSGroupsOfflineMode {
+  private static final org.apache.commons.logging.Log LOG =
+      LogFactory.getLog(TestRSGroupsOfflineMode.class);
+  private static HMaster master;
+  private static HBaseAdmin hbaseAdmin;
+  private static HBaseTestingUtility TEST_UTIL;
+  private static HBaseCluster cluster;
+  private static RSGroupAdminEndpoint RSGroupAdminEndpoint;
+  public final static long WAIT_TIMEOUT = 60000*5;
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    TEST_UTIL = new HBaseTestingUtility();
+    TEST_UTIL.getConfiguration().set(
+        HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
+        RSGroupBasedLoadBalancer.class.getName());
+    TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
+        RSGroupAdminEndpoint.class.getName());
+    TEST_UTIL.getConfiguration().set(
+        ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
+        "1");
+    TEST_UTIL.startMiniCluster(2, 3);
+    cluster = TEST_UTIL.getHBaseCluster();
+    master = ((MiniHBaseCluster)cluster).getMaster();
+    master.balanceSwitch(false);
+    hbaseAdmin = TEST_UTIL.getHBaseAdmin();
+    //wait till the balancer is in online mode
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return master.isInitialized() &&
+            ((RSGroupBasedLoadBalancer) master.getLoadBalancer()).isOnline() &&
+            master.getServerManager().getOnlineServersList().size() >= 3;
+      }
+    });
+    RSGroupAdminEndpoint =
+        master.getMasterCoprocessorHost().findCoprocessors(RSGroupAdminEndpoint.class).get(0);
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+  @Test
+  public void testOffline() throws Exception, InterruptedException {
+    //table should be after group table name
+    //so it gets assigned later
+    final TableName failoverTable = TableName.valueOf("testOffline");
+    TEST_UTIL.createTable(failoverTable, Bytes.toBytes("f"));
+
+    RSGroupAdmin groupAdmin = RSGroupAdmin.newClient(TEST_UTIL.getConnection());
+
+    final HRegionServer killRS = ((MiniHBaseCluster)cluster).getRegionServer(0);
+    final HRegionServer groupRS = ((MiniHBaseCluster)cluster).getRegionServer(1);
+    final HRegionServer failoverRS = ((MiniHBaseCluster)cluster).getRegionServer(2);
+
+    String newGroup =  "my_group";
+    groupAdmin.addRSGroup(newGroup);
+    if(master.getAssignmentManager().getRegionStates().getRegionAssignments()
+        .containsValue(failoverRS.getServerName())) {
+      for(HRegionInfo regionInfo: hbaseAdmin.getOnlineRegions(failoverRS.getServerName())) {
+        hbaseAdmin.move(regionInfo.getEncodedNameAsBytes(),
+            Bytes.toBytes(failoverRS.getServerName().getServerName()));
+      }
+      LOG.info("Waiting for region unassignments on failover RS...");
+      TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+        @Override
+        public boolean evaluate() throws Exception {
+          return master.getServerManager().getLoad(failoverRS.getServerName())
+              .getRegionsLoad().size() > 0;
+        }
+      });
+    }
+
+    //move server to group and make sure all tables are assigned
+    groupAdmin.moveServers(Sets.newHashSet(groupRS.getServerName().getHostPort()), newGroup);
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return groupRS.getNumberOfOnlineRegions() < 1 &&
+            master.getAssignmentManager().getRegionStates().getRegionsInTransition().size() < 1;
+      }
+    });
+    //move table to group and wait
+    groupAdmin.moveTables(Sets.newHashSet(RSGroupInfoManager.RSGROUP_TABLE_NAME), newGroup);
+    LOG.info("Waiting for move table...");
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return groupRS.getNumberOfOnlineRegions() == 1;
+      }
+    });
+
+    groupRS.stop("die");
+    //race condition here
+    TEST_UTIL.getHBaseCluster().getMaster().stopMaster();
+    LOG.info("Waiting for offline mode...");
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return TEST_UTIL.getHBaseCluster().getMaster() != null &&
+            TEST_UTIL.getHBaseCluster().getMaster().isActiveMaster() &&
+            TEST_UTIL.getHBaseCluster().getMaster().isInitialized() &&
+            TEST_UTIL.getHBaseCluster().getMaster().getServerManager().getOnlineServers().size()
+                <= 3;
+      }
+    });
+
+
+    RSGroupInfoManager groupMgr = RSGroupAdminEndpoint.getGroupInfoManager();
+    //make sure balancer is in offline mode, since this is what we're testing
+    assertFalse(groupMgr.isOnline());
+    //verify the group affiliation that's loaded from ZK instead of tables
+    assertEquals(newGroup,
+        groupMgr.getRSGroupOfTable(RSGroupInfoManager.RSGROUP_TABLE_NAME));
+    assertEquals(RSGroupInfo.DEFAULT_GROUP, groupMgr.getRSGroupOfTable(failoverTable));
+
+    //kill final regionserver to see the failover happens for all tables
+    //except GROUP table since it's group does not have any online RS
+    killRS.stop("die");
+    master = TEST_UTIL.getHBaseCluster().getMaster();
+    LOG.info("Waiting for new table assignment...");
+    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        return failoverRS.getOnlineRegions(failoverTable).size() >= 1;
+      }
+    });
+    Assert.assertEquals(0, failoverRS.getOnlineRegions(RSGroupInfoManager.RSGROUP_TABLE_NAME).size());
+
+    //need this for minicluster to shutdown cleanly
+    master.stopMaster();
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
new file mode 100644
index 0000000..d1f4898
--- /dev/null
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdminClient.java
@@ -0,0 +1,149 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.hbase.rsgroup;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.ConnectionFactory;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.exceptions.DeserializationException;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
+import org.apache.hadoop.hbase.zookeeper.ZKUtil;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.zookeeper.KeeperException;
+import org.junit.Assert;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class VerifyingRSGroupAdminClient extends RSGroupAdmin {
+  private Table table;
+  private ZooKeeperWatcher zkw;
+  private RSGroupSerDe serDe;
+  private RSGroupAdmin wrapped;
+
+  public VerifyingRSGroupAdminClient(RSGroupAdmin RSGroupAdmin, Configuration conf)
+      throws IOException {
+    wrapped = RSGroupAdmin;
+    table = ConnectionFactory.createConnection(conf).getTable(RSGroupInfoManager.RSGROUP_TABLE_NAME);
+    zkw = new ZooKeeperWatcher(conf, this.getClass().getSimpleName(), null);
+    serDe = new RSGroupSerDe();
+  }
+
+  @Override
+  public void addRSGroup(String groupName) throws IOException {
+    wrapped.addRSGroup(groupName);
+    verify();
+  }
+
+  @Override
+  public RSGroupInfo getRSGroupInfo(String groupName) throws IOException {
+    return wrapped.getRSGroupInfo(groupName);
+  }
+
+  @Override
+  public RSGroupInfo getRSGroupInfoOfTable(TableName tableName) throws IOException {
+    return wrapped.getRSGroupInfoOfTable(tableName);
+  }
+
+  @Override
+  public void moveServers(Set<HostAndPort> servers, String targetGroup) throws IOException {
+    wrapped.moveServers(servers, targetGroup);
+    verify();
+  }
+
+  @Override
+  public void moveTables(Set<TableName> tables, String targetGroup) throws IOException {
+    wrapped.moveTables(tables, targetGroup);
+    verify();
+  }
+
+  @Override
+  public void removeRSGroup(String name) throws IOException {
+    wrapped.removeRSGroup(name);
+    verify();
+  }
+
+  @Override
+  public boolean balanceRSGroup(String name) throws IOException {
+    return wrapped.balanceRSGroup(name);
+  }
+
+  @Override
+  public List<RSGroupInfo> listRSGroups() throws IOException {
+    return wrapped.listRSGroups();
+  }
+
+  @Override
+  public RSGroupInfo getRSGroupOfServer(HostAndPort hostPort) throws IOException {
+    return wrapped.getRSGroupOfServer(hostPort);
+  }
+
+  public void verify() throws IOException {
+    Map<String, RSGroupInfo> groupMap = Maps.newHashMap();
+    Set<RSGroupInfo> zList = Sets.newHashSet();
+
+    for (Result result : table.getScanner(new Scan())) {
+      RSGroupProtos.RSGroupInfo proto =
+          RSGroupProtos.RSGroupInfo.parseFrom(
+              result.getValue(
+                  RSGroupInfoManager.META_FAMILY_BYTES,
+                  RSGroupInfoManager.META_QUALIFIER_BYTES));
+      groupMap.put(proto.getName(), ProtobufUtil.toGroupInfo(proto));
+    }
+    Assert.assertEquals(Sets.newHashSet(groupMap.values()),
+        Sets.newHashSet(wrapped.listRSGroups()));
+    try {
+      String groupBasePath = ZKUtil.joinZNode(zkw.baseZNode, "rsgroup");
+      for(String znode: ZKUtil.listChildrenNoWatch(zkw, groupBasePath)) {
+        byte[] data = ZKUtil.getData(zkw, ZKUtil.joinZNode(groupBasePath, znode));
+        if(data.length > 0) {
+          ProtobufUtil.expectPBMagicPrefix(data);
+          ByteArrayInputStream bis = new ByteArrayInputStream(
+              data, ProtobufUtil.lengthOfPBMagic(), data.length);
+          zList.add(ProtobufUtil.toGroupInfo(RSGroupProtos.RSGroupInfo.parseFrom(bis)));
+        }
+      }
+      Assert.assertEquals(zList.size(), groupMap.size());
+      for(RSGroupInfo RSGroupInfo : zList) {
+        Assert.assertTrue(groupMap.get(RSGroupInfo.getName()).equals(RSGroupInfo));
+      }
+    } catch (KeeperException e) {
+      throw new IOException("ZK verification failed", e);
+    } catch (DeserializationException e) {
+      throw new IOException("ZK verification failed", e);
+    } catch (InterruptedException e) {
+      throw new IOException("ZK verification failed", e);
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/MasterStatusTmpl.jamon
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/MasterStatusTmpl.jamon b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/MasterStatusTmpl.jamon
index 1af3db9..951a95e 100644
--- a/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/MasterStatusTmpl.jamon
+++ b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/MasterStatusTmpl.jamon
@@ -30,30 +30,30 @@ AssignmentManager assignmentManager = null;
 </%args>
 <%import>
 java.util.*;
-org.apache.hadoop.util.StringUtils;
-org.apache.hadoop.hbase.util.Bytes;
-org.apache.hadoop.hbase.util.JvmVersion;
-org.apache.hadoop.hbase.util.FSUtils;
-org.apache.hadoop.hbase.master.HMaster;
-org.apache.hadoop.hbase.master.AssignmentManager;
-org.apache.hadoop.hbase.master.ServerManager;
+org.apache.hadoop.hbase.HBaseConfiguration;
 org.apache.hadoop.hbase.HConstants;
+org.apache.hadoop.hbase.HRegionInfo;
+org.apache.hadoop.hbase.HTableDescriptor;
 org.apache.hadoop.hbase.NamespaceDescriptor;
 org.apache.hadoop.hbase.ServerLoad;
 org.apache.hadoop.hbase.ServerName;
-org.apache.hadoop.hbase.client.Admin;
-org.apache.hadoop.hbase.HRegionInfo;
-org.apache.hadoop.hbase.master.RegionState;
-org.apache.hadoop.hbase.HTableDescriptor;
-org.apache.hadoop.hbase.HBaseConfiguration;
 org.apache.hadoop.hbase.TableName;
-org.apache.hadoop.hbase.tool.Canary;
-org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
+org.apache.hadoop.hbase.client.Admin;
+org.apache.hadoop.hbase.master.AssignmentManager;
 org.apache.hadoop.hbase.master.DeadServer;
+org.apache.hadoop.hbase.master.HMaster;
+org.apache.hadoop.hbase.master.RegionState;
+org.apache.hadoop.hbase.master.ServerManager;
 org.apache.hadoop.hbase.protobuf.ProtobufUtil;
-org.apache.hadoop.hbase.security.visibility.VisibilityConstants;
-org.apache.hadoop.hbase.security.access.AccessControlLists;
+org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
 org.apache.hadoop.hbase.quotas.QuotaUtil;
+org.apache.hadoop.hbase.security.access.AccessControlLists;
+org.apache.hadoop.hbase.security.visibility.VisibilityConstants;
+org.apache.hadoop.hbase.tool.Canary;
+org.apache.hadoop.hbase.util.Bytes;
+org.apache.hadoop.hbase.util.FSUtils;
+org.apache.hadoop.hbase.util.JvmVersion;
+org.apache.hadoop.util.StringUtils;
 </%import>
 
 <%if format.equals("json") %>
@@ -380,6 +380,8 @@ AssignmentManager assignmentManager = master.getAssignmentManager();
         } else if (tableName.equals(QuotaUtil.QUOTA_TABLE_NAME)){
             description = "The hbase:quota table holds quota information about number" +
             " or size of requests in a given time frame.";
+        } else if (tableName.equals(TableName.valueOf("hbase:rsgroup"))){
+            description = "The hbase:rsgroup table holds information about regionserver groups";
         }
     </%java>
     <td><% description %></td>

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterAndRegionObserver.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterAndRegionObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterAndRegionObserver.java
index d601491..4748056 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterAndRegionObserver.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterAndRegionObserver.java
@@ -19,8 +19,11 @@
 
 package org.apache.hadoop.hbase.coprocessor;
 
+import com.google.common.net.HostAndPort;
+
 import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.hadoop.hbase.CoprocessorEnvironment;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
@@ -631,4 +634,54 @@ public abstract class BaseMasterAndRegionObserver extends BaseRegionObserver
   public void postSetNamespaceQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx,
       final String namespace, final Quotas quotas) throws IOException {
   }
+
+  @Override
+  public void postAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void postBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 String groupName, boolean balancerRan) throws IOException {
+  }
+
+  @Override
+  public void postMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx, Set<HostAndPort>
+      servers, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void postMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx, Set<TableName>
+      tables, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void postRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void preAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void preBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String groupName)
+      throws IOException {
+  }
+
+  @Override
+  public void preMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
+      Set<HostAndPort> servers, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void preMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+      Set<TableName> tables, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void preRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java
index 0cdf0ad..0adb179 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java
@@ -19,8 +19,11 @@
 
 package org.apache.hadoop.hbase.coprocessor;
 
+import com.google.common.net.HostAndPort;
+
 import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.hadoop.hbase.CoprocessorEnvironment;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
@@ -634,4 +637,55 @@ public class BaseMasterObserver implements MasterObserver {
   public void postSetNamespaceQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx,
       final String namespace, final Quotas quotas) throws IOException {
   }
+
+  @Override
+  public void preMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx, Set<HostAndPort>
+      servers, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void postMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx, Set<HostAndPort>
+      servers, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void preMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx, Set<TableName>
+      tables, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void postMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+      Set<TableName> tables, String targetGroup) throws IOException {
+  }
+
+  @Override
+  public void preAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void postAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void preRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+
+  }
+
+  @Override
+  public void postRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
+      throws IOException {
+  }
+
+  @Override
+  public void preBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String groupName)
+      throws IOException {
+  }
+
+  @Override
+  public void postBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 String groupName, boolean balancerRan) throws IOException {
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
index b7a535b..82b6ffe 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
@@ -19,8 +19,11 @@
 
 package org.apache.hadoop.hbase.coprocessor;
 
+import com.google.common.net.HostAndPort;
+
 import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.hadoop.hbase.Coprocessor;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
@@ -1238,4 +1241,99 @@ public interface MasterObserver extends Coprocessor {
    */
   void postDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> c,
       final HRegionInfo regionA, final HRegionInfo regionB) throws IOException;
+
+  /**
+   * Called before servers are moved to target region server group
+   * @param ctx the environment to interact with the framework and master
+   * @param servers set of servers to move
+   * @param targetGroup destination group
+   * @throws IOException on failure
+   */
+  void preMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                      Set<HostAndPort> servers, String targetGroup) throws IOException;
+
+  /**
+   * Called after servers are moved to target region server group
+   * @param ctx the environment to interact with the framework and master
+   * @param servers set of servers to move
+   * @param targetGroup name of group
+   * @throws IOException on failure
+   */
+  void postMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                       Set<HostAndPort> servers, String targetGroup) throws IOException;
+
+  /**
+   * Called before tables are moved to target region server group
+   * @param ctx the environment to interact with the framework and master
+   * @param tables set of tables to move
+   * @param targetGroup name of group
+   * @throws IOException on failure
+   */
+  void preMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                     Set<TableName> tables, String targetGroup) throws IOException;
+
+  /**
+   * Called after servers are moved to target region server group
+   * @param ctx the environment to interact with the framework and master
+   * @param tables set of tables to move
+   * @param targetGroup name of group
+   * @throws IOException on failure
+   */
+  void postMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                      Set<TableName> tables, String targetGroup) throws IOException;
+
+  /**
+   * Called before a new region server group is added
+   * @param ctx the environment to interact with the framework and master
+   * @param name group name
+   * @throws IOException on failure
+   */
+  void preAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                     String name) throws IOException;
+
+  /**
+   * Called after a new region server group is added
+   * @param ctx the environment to interact with the framework and master
+   * @param name group name
+   * @throws IOException on failure
+   */
+  void postAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                      String name) throws IOException;
+
+  /**
+   * Called before a region server group is removed
+   * @param ctx the environment to interact with the framework and master
+   * @param name group name
+   * @throws IOException on failure
+   */
+  void preRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                        String name) throws IOException;
+
+  /**
+   * Called after a region server group is removed
+   * @param ctx the environment to interact with the framework and master
+   * @param name group name
+   * @throws IOException on failure
+   */
+  void postRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                         String name) throws IOException;
+
+  /**
+   * Called before a region server group is removed
+   * @param ctx the environment to interact with the framework and master
+   * @param groupName group name
+   * @throws IOException on failure
+   */
+  void preBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                         String groupName) throws IOException;
+
+  /**
+   * Called after a region server group is removed
+   * @param ctx the environment to interact with the framework and master
+   * @param groupName group name
+   * @throws IOException on failure
+   */
+  void postBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                          String groupName, boolean balancerRan) throws IOException;
+
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
index 1110db3..2b7713b 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
@@ -1078,7 +1078,7 @@ public class AssignmentManager {
           return;
         }
         LOG.info("Assigning " + region.getRegionNameAsString() +
-            " to " + plan.getDestination().toString());
+            " to " + plan.getDestination());
         // Transition RegionState to PENDING_OPEN
        regionStates.updateRegionState(region,
           State.PENDING_OPEN, plan.getDestination());
@@ -1267,8 +1267,13 @@ public class AssignmentManager {
           || existingPlan.getDestination() == null
           || !destServers.contains(existingPlan.getDestination())) {
         newPlan = true;
-        randomPlan = new RegionPlan(region, null,
-            balancer.randomAssignment(region, destServers));
+        try {
+          randomPlan = new RegionPlan(region, null,
+              balancer.randomAssignment(region, destServers));
+        } catch (IOException ex) {
+          LOG.warn("Failed to create new plan.",ex);
+          return null;
+        }
         if (!region.isMetaTable() && shouldAssignRegionsWithFavoredNodes) {
           List<HRegionInfo> regions = new ArrayList<HRegionInfo>(1);
           regions.add(region);
@@ -1513,6 +1518,8 @@ public class AssignmentManager {
       throw new IOException("Unable to determine a plan to assign region(s)");
     }
 
+    processBogusAssignments(bulkPlan);
+
     assign(regions.size(), servers.size(),
       "retainAssignment=true", bulkPlan);
   }
@@ -1542,6 +1549,8 @@ public class AssignmentManager {
       throw new IOException("Unable to determine a plan to assign region(s)");
     }
 
+    processBogusAssignments(bulkPlan);
+
     processFavoredNodes(regions);
     assign(regions.size(), servers.size(), "round-robin=true", bulkPlan);
   }
@@ -2954,6 +2963,16 @@ public class AssignmentManager {
     }
   }
 
+  private void processBogusAssignments(Map<ServerName, List<HRegionInfo>> bulkPlan) {
+    if (bulkPlan.containsKey(LoadBalancer.BOGUS_SERVER_NAME)) {
+      // Found no plan for some regions, put those regions in RIT
+      for (HRegionInfo hri : bulkPlan.get(LoadBalancer.BOGUS_SERVER_NAME)) {
+        regionStates.updateRegionState(hri, State.FAILED_OPEN);
+      }
+      bulkPlan.remove(LoadBalancer.BOGUS_SERVER_NAME);
+    }
+  }
+
   /**
    * @return Instance of load balancer
    */

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
index b4bffb4..142502a 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
@@ -18,6 +18,13 @@
  */
 package org.apache.hadoop.hbase.master;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.protobuf.Descriptors;
+import com.google.protobuf.Service;
+
+
 import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.lang.reflect.Constructor;
@@ -165,11 +172,6 @@ import org.mortbay.jetty.Connector;
 import org.mortbay.jetty.nio.SelectChannelConnector;
 import org.mortbay.jetty.servlet.Context;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Maps;
-import com.google.protobuf.Descriptors;
-import com.google.protobuf.Service;
-
 /**
  * HMaster is the "master server" for HBase. An HBase cluster has one active
  * master.  If many masters are started, all compete.  Whichever wins goes on to
@@ -1396,11 +1398,14 @@ public class HMaster extends HRegionServer implements MasterServices {
       final byte[] destServerName) throws HBaseIOException {
     RegionState regionState = assignmentManager.getRegionStates().
       getRegionState(Bytes.toString(encodedRegionName));
-    if (regionState == null) {
+
+    HRegionInfo hri;
+    if (regionState != null) {
+      hri = regionState.getRegion();
+    } else {
       throw new UnknownRegionException(Bytes.toStringBinary(encodedRegionName));
     }
 
-    HRegionInfo hri = regionState.getRegion();
     ServerName dest;
     if (destServerName == null || destServerName.length == 0) {
       LOG.info("Passed destination servername is null/empty so " +
@@ -1413,7 +1418,12 @@ public class HMaster extends HRegionServer implements MasterServices {
         return;
       }
     } else {
-      dest = ServerName.valueOf(Bytes.toString(destServerName));
+      ServerName candidate = ServerName.valueOf(Bytes.toString(destServerName));
+      dest = balancer.randomAssignment(hri, Lists.newArrayList(candidate));
+      if (dest == null) {
+        LOG.debug("Unable to determine a plan to assign " + hri);
+        return;
+      }
       if (dest.equals(serverName) && balancer instanceof BaseLoadBalancer
           && !((BaseLoadBalancer)balancer).shouldBeOnMaster(hri)) {
         // To avoid unnecessary region moving later by balancer. Don't put user
@@ -1476,7 +1486,6 @@ public class HMaster extends HRegionServer implements MasterServices {
     HRegionInfo[] newRegions = ModifyRegionUtils.createHRegionInfos(hTableDescriptor, splitKeys);
     checkInitialized();
     sanityCheckTableDescriptor(hTableDescriptor);
-
     if (cpHost != null) {
       cpHost.preCreateTable(hTableDescriptor, newRegions);
     }
@@ -2827,4 +2836,9 @@ public class HMaster extends HRegionServer implements MasterServices {
   public SplitOrMergeTracker getSplitOrMergeTracker() {
     return splitOrMergeTracker;
   }
+
+  @Override
+  public LoadBalancer getLoadBalancer() {
+    return balancer;
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java
index 6a618e1..5d6be9e 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java
@@ -48,6 +48,9 @@ import org.apache.hadoop.hbase.TableName;
 @InterfaceAudience.Private
 public interface LoadBalancer extends Configurable, Stoppable, ConfigurationObserver {
 
+  //used to signal to the caller that the region(s) cannot be assigned
+  ServerName BOGUS_SERVER_NAME = ServerName.parseServerName("localhost,1,1");
+
   /**
    * Set the current cluster status.  This allows a LoadBalancer to map host name to a server
    * @param st

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
index 5888b3e..b16c232 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
@@ -19,8 +19,11 @@
 
 package org.apache.hadoop.hbase.master;
 
+import com.google.common.net.HostAndPort;
+
 import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.commons.lang.ClassUtils;
 import org.apache.commons.logging.Log;
@@ -63,12 +66,15 @@ public class MasterCoprocessorHost
   static class MasterEnvironment extends CoprocessorHost.Environment
       implements MasterCoprocessorEnvironment {
     private MasterServices masterServices;
+    final boolean supportGroupCPs;
 
     public MasterEnvironment(final Class<?> implClass, final Coprocessor impl,
         final int priority, final int seq, final Configuration conf,
         final MasterServices services) {
       super(impl, priority, seq, conf);
       this.masterServices = services;
+      supportGroupCPs = !useLegacyMethod(impl.getClass(),
+          "preBalanceRSGroup", ObserverContext.class, String.class);
     }
 
     public MasterServices getMasterServices() {
@@ -1170,4 +1176,135 @@ public class MasterCoprocessorHost
     }
     return bypass;
   }
+
+  public void preMoveServers(final Set<HostAndPort> servers, final String targetGroup)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.preMoveServers(ctx, servers, targetGroup);
+        }
+      }
+    });
+  }
+
+  public void postMoveServers(final Set<HostAndPort> servers, final String targetGroup)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.postMoveServers(ctx, servers, targetGroup);
+        }
+      }
+    });
+  }
+
+  public void preMoveTables(final Set<TableName> tables, final String targetGroup)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.preMoveTables(ctx, tables, targetGroup);
+        }
+      }
+    });
+  }
+
+  public void postMoveTables(final Set<TableName> tables, final String targetGroup)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.postMoveTables(ctx, tables, targetGroup);
+        }
+      }
+    });
+  }
+
+  public void preAddRSGroup(final String name)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.preAddRSGroup(ctx, name);
+        }
+      }
+    });
+  }
+
+  public void postAddRSGroup(final String name)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if (((MasterEnvironment) ctx.getEnvironment()).supportGroupCPs) {
+          oserver.postAddRSGroup(ctx, name);
+        }
+      }
+    });
+  }
+
+  public void preRemoveRSGroup(final String name)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.preRemoveRSGroup(ctx, name);
+        }
+      }
+    });
+  }
+
+  public void postRemoveRSGroup(final String name)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.postRemoveRSGroup(ctx, name);
+        }
+      }
+    });
+  }
+
+  public void preBalanceRSGroup(final String name)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.preBalanceRSGroup(ctx, name);
+        }
+      }
+    });
+  }
+
+  public void postBalanceRSGroup(final String name, final boolean balanceRan)
+      throws IOException {
+    execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
+      @Override
+      public void call(MasterObserver oserver,
+          ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
+        if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
+          oserver.postBalanceRSGroup(ctx, name, balanceRan);
+        }
+      }
+    });
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
index d6a53cd..753ecb6 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
@@ -1309,6 +1309,14 @@ public class MasterRpcServices extends RSRpcServices
       }
       Pair<HRegionInfo, ServerName> pair =
         MetaTableAccessor.getRegion(master.getConnection(), regionName);
+      if (Bytes.equals(HRegionInfo.FIRST_META_REGIONINFO.getRegionName(),regionName)) {
+        pair = new Pair<HRegionInfo, ServerName>(HRegionInfo.FIRST_META_REGIONINFO,
+            master.getMetaTableLocator().getMetaRegionLocation(master.getZooKeeper()));
+      }
+      if (pair == null) {
+        throw new UnknownRegionException(Bytes.toString(regionName));
+      }
+
       if (pair == null) throw new UnknownRegionException(Bytes.toString(regionName));
       HRegionInfo hri = pair.getFirst();
       if (master.cpHost != null) {

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java
index 59c7a88..8ce21b0 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java
@@ -328,4 +328,9 @@ public interface MasterServices extends Server {
    * @throws IOException
    */
   public long getLastMajorCompactionTimestampForRegion(byte[] regionName) throws IOException;
+
+  /**
+   * @return load balancer
+   */
+  public LoadBalancer getLoadBalancer();
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
index 43f9833..0a88a86 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
@@ -18,6 +18,8 @@
  */
 package org.apache.hadoop.hbase.security.access;
 
+import com.google.common.net.HostAndPort;
+
 import java.io.IOException;
 import java.net.InetAddress;
 import java.security.PrivilegedExceptionAction;
@@ -2619,4 +2621,34 @@ public class AccessController extends BaseMasterAndRegionObserver
   public void postReplicateLogEntries(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
       List<WALEntry> entries, CellScanner cells) throws IOException {
   }
+
+  @Override
+  public void preMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                             Set<HostAndPort> servers, String targetGroup) throws IOException {
+    requirePermission("moveServers", Action.ADMIN);
+  }
+
+  @Override
+  public void preMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                            Set<TableName> tables, String targetGroup) throws IOException {
+    requirePermission("moveTables", Action.ADMIN);
+  }
+
+  @Override
+  public void preAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                            String name) throws IOException {
+    requirePermission("addRSGroup", Action.ADMIN);
+  }
+
+  @Override
+  public void preRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               String name) throws IOException {
+    requirePermission("removeRSGroup", Action.ADMIN);
+  }
+
+  @Override
+  public void preBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                String groupName) throws IOException {
+    requirePermission("balanceRSGroup", Action.ADMIN);
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
index 7e79b64..3810f1e 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
@@ -28,6 +28,7 @@ import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
 import org.apache.commons.logging.Log;
@@ -74,6 +75,8 @@ import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.rules.TestName;
 
+import com.google.common.net.HostAndPort;
+
 /**
  * Tests invocation of the {@link org.apache.hadoop.hbase.coprocessor.MasterObserver}
  * interface hooks at all appropriate times during normal HMaster operations.
@@ -1312,6 +1315,56 @@ public class TestMasterObserver {
     public void postSetNamespaceQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx,
         final String namespace, final Quotas quotas) throws IOException {
     }
+
+    @Override
+    public void preMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               Set<HostAndPort> servers, String targetGroup) throws IOException {
+    }
+
+    @Override
+    public void postMoveServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                Set<HostAndPort> servers, String targetGroup) throws IOException {
+    }
+
+    @Override
+    public void preMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                              Set<TableName> tables, String targetGroupGroup) throws IOException {
+    }
+
+    @Override
+    public void postMoveTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               Set<TableName> tables, String targetGroup) throws IOException {
+    }
+
+    @Override
+    public void preAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                              String name) throws IOException {
+    }
+
+    @Override
+    public void postAddRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               String name) throws IOException {
+    }
+
+    @Override
+    public void preRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 String name) throws IOException {
+    }
+
+    @Override
+    public void postRemoveRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                  String name) throws IOException {
+    }
+
+    @Override
+    public void preBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                  String groupName) throws IOException {
+    }
+
+    @Override
+    public void postBalanceRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                   String groupName, boolean balancerRan) throws IOException {
+    }
   }
 
   private static HBaseTestingUtility UTIL = new HBaseTestingUtility();

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/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 703d54e..fdfc1b3 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;
@@ -536,7 +540,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();
       master.assignRegion(hri);
@@ -562,6 +566,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)
@@ -1169,7 +1272,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);
@@ -1177,7 +1280,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);
@@ -1195,8 +1298,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/ca816f07/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 123e8b5..2949369 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
@@ -434,6 +434,9 @@ public class TestCatalogJanitor {
         final long nonce) throws IOException {
       return -1;
     }
+    public LoadBalancer getLoadBalancer() {
+      return null;
+    }
 
     @Override
     public long truncateTable(

http://git-wip-us.apache.org/repos/asf/hbase/blob/ca816f07/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 f5f04e9..135aae5 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
@@ -135,7 +135,7 @@ public class TestMasterStatusServlet {
     setupMockTables();
 
     new MasterStatusTmpl()
-      .setMetaLocation(ServerName.valueOf("metaserver:123,12345"))
+      .setMetaLocation(ServerName.valueOf("metaserver,123,12345"))
       .render(new StringWriter(), master);
   }
 
@@ -144,16 +144,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/ca816f07/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 926c816..babf22d 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
@@ -261,7 +261,7 @@ public class TestSimpleRegionNormalizer {
     masterServices = Mockito.mock(MasterServices.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().