HBASE-19799 Add web UI to rsgroup

Signed-off-by: tedyu <yuzhih...@gmail.com>


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

Branch: refs/heads/HBASE-19064
Commit: cde1f821ebec14a30214a15aebb02c936f27fd87
Parents: 581fabe
Author: Guangxu Cheng <guangxuch...@gmail.com>
Authored: Thu Jan 18 15:16:29 2018 +0800
Committer: tedyu <yuzhih...@gmail.com>
Committed: Thu Jan 18 12:49:43 2018 -0800

----------------------------------------------------------------------
 .../hadoop/hbase/RSGroupTableAccessor.java      |  82 ++++
 .../hadoop/hbase/protobuf/ProtobufUtil.java     |  14 +
 hbase-protocol/src/main/protobuf/RSGroup.proto  |  34 ++
 hbase-rsgroup/src/main/protobuf/RSGroup.proto   |  34 --
 .../hbase/tmpl/master/MasterStatusTmpl.jamon    |   7 +-
 .../hbase/tmpl/master/RSGroupListTmpl.jamon     | 352 +++++++++++++++
 .../resources/hbase-webapps/master/rsgroup.jsp  | 441 +++++++++++++++++++
 7 files changed, 929 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/cde1f821/hbase-client/src/main/java/org/apache/hadoop/hbase/RSGroupTableAccessor.java
----------------------------------------------------------------------
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/RSGroupTableAccessor.java 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/RSGroupTableAccessor.java
new file mode 100644
index 0000000..06a7604
--- /dev/null
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/RSGroupTableAccessor.java
@@ -0,0 +1,82 @@
+/**
+ *
+ * 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;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.client.Get;
+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.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
+import org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Read rs group information from  <code>hbase:rsgroup</code>.
+ */
+@InterfaceAudience.Private
+public final class RSGroupTableAccessor {
+
+  //Assigned before user tables
+  private static final TableName RSGROUP_TABLE_NAME =
+      TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, 
"rsgroup");
+  private static final byte[] META_FAMILY_BYTES = Bytes.toBytes("m");
+  private static final byte[] META_QUALIFIER_BYTES = Bytes.toBytes("i");
+
+  public static List<RSGroupInfo> getAllRSGroupInfo(Connection connection)
+      throws IOException {
+    try (Table rsGroupTable = connection.getTable(RSGROUP_TABLE_NAME)) {
+      List<RSGroupInfo> rsGroupInfos = new ArrayList<>();
+      for (Result result : rsGroupTable.getScanner(new Scan())) {
+        RSGroupInfo rsGroupInfo = getRSGroupInfo(result);
+        if (rsGroupInfo != null) {
+          rsGroupInfos.add(rsGroupInfo);
+        }
+      }
+      return rsGroupInfos;
+    }
+  }
+
+  private static RSGroupInfo getRSGroupInfo(Result result) throws IOException {
+    byte[] rsGroupInfo = result.getValue(META_FAMILY_BYTES, 
META_QUALIFIER_BYTES);
+    if (rsGroupInfo == null) {
+      return null;
+    }
+    RSGroupProtos.RSGroupInfo proto =
+        RSGroupProtos.RSGroupInfo.parseFrom(rsGroupInfo);
+    return ProtobufUtil.toGroupInfo(proto);
+  }
+
+  public static RSGroupInfo getRSGroupInfo(Connection connection, byte[] 
rsGroupName)
+      throws IOException {
+    try (Table rsGroupTable = connection.getTable(RSGROUP_TABLE_NAME)){
+      Result result = rsGroupTable.get(new Get(rsGroupName));
+      return getRSGroupInfo(result);
+    }
+  }
+
+  private RSGroupTableAccessor() {
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/cde1f821/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 2f2dc86..fd263b0 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
@@ -73,6 +73,7 @@ import 
org.apache.hadoop.hbase.exceptions.DeserializationException;
 import org.apache.hadoop.hbase.filter.ByteArrayComparable;
 import org.apache.hadoop.hbase.filter.Filter;
 import org.apache.hadoop.hbase.io.TimeRange;
+import org.apache.hadoop.hbase.net.Address;
 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
 import 
org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetServerInfoRequest;
 import 
org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetServerInfoResponse;
@@ -92,7 +93,9 @@ import 
org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameBytesPair;
 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier;
 import 
org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType;
 import org.apache.hadoop.hbase.protobuf.generated.MapReduceProtos;
+import org.apache.hadoop.hbase.protobuf.generated.RSGroupProtos;
 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
+import org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
 import org.apache.hadoop.hbase.util.Addressing;
 import org.apache.hadoop.hbase.util.ByteStringer;
 import org.apache.hadoop.hbase.util.Bytes;
@@ -1819,4 +1822,15 @@ public final class ProtobufUtil {
     int port = Addressing.parsePort(str);
     return ServerName.valueOf(hostname, port, -1L);
   }
+
+  public static RSGroupInfo toGroupInfo(RSGroupProtos.RSGroupInfo proto) {
+    RSGroupInfo RSGroupInfo = new RSGroupInfo(proto.getName());
+    for(HBaseProtos.ServerName el: proto.getServersList()) {
+      RSGroupInfo.addServer(Address.fromParts(el.getHostName(), el.getPort()));
+    }
+    for(HBaseProtos.TableName pTableName: proto.getTablesList()) {
+      RSGroupInfo.addTable(ProtobufUtil.toTableName(pTableName));
+    }
+    return RSGroupInfo;
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/cde1f821/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/cde1f821/hbase-rsgroup/src/main/protobuf/RSGroup.proto
----------------------------------------------------------------------
diff --git a/hbase-rsgroup/src/main/protobuf/RSGroup.proto 
b/hbase-rsgroup/src/main/protobuf/RSGroup.proto
deleted file mode 100644
index 7358941..0000000
--- a/hbase-rsgroup/src/main/protobuf/RSGroup.proto
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package 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/cde1f821/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 292a668..d0342db 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
@@ -184,7 +184,12 @@ AssignmentManager assignmentManager = 
master.getAssignmentManager();
             or re-run HBCK in repair mode.
           </div>
         </%if>
-
+        <%if 
master.getMasterCoprocessorHost().findCoprocessor("RSGroupAdminEndpoint") != 
null %>
+          <section>
+            <h2>RSGroup</h2>
+            <& RSGroupListTmpl; master= master; serverManager= serverManager&>
+          </section>
+        </%if>
         <section>
             <h2>Region Servers</h2>
             <& RegionServerListTmpl; master= master; servers = servers &>

http://git-wip-us.apache.org/repos/asf/hbase/blob/cde1f821/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/RSGroupListTmpl.jamon
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/RSGroupListTmpl.jamon
 
b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/RSGroupListTmpl.jamon
new file mode 100644
index 0000000..9f9831f
--- /dev/null
+++ 
b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/RSGroupListTmpl.jamon
@@ -0,0 +1,352 @@
+<%doc>
+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.
+ </%doc>
+
+<%args>
+HMaster master;
+ServerManager serverManager;
+</%args>
+
+<%import>
+    java.util.Collections;
+    java.util.List;
+    java.util.Map;
+    java.util.Set;
+    java.util.stream.Collectors;
+    org.apache.hadoop.hbase.master.HMaster;
+    org.apache.hadoop.hbase.ServerLoad;
+    org.apache.hadoop.hbase.RSGroupTableAccessor;
+    org.apache.hadoop.hbase.master.ServerManager;
+    org.apache.hadoop.hbase.net.Address;
+    org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
+    org.apache.hadoop.util.StringUtils.TraditionalBinaryPrefix;
+</%import>
+<%java>
+List<RSGroupInfo> groups = 
RSGroupTableAccessor.getAllRSGroupInfo(master.getConnection());
+</%java>
+
+<%if (groups != null && groups.size() > 0)%>
+
+<%java>
+RSGroupInfo [] rsGroupInfos = groups.toArray(new RSGroupInfo[groups.size()]);
+Map<Address, ServerLoad> collectServers = Collections.emptyMap();
+if (master.getServerManager() != null) {
+  collectServers =
+      master.getServerManager().getOnlineServers().entrySet().stream()
+          .collect(Collectors.toMap(p -> p.getKey().getAddress(), 
Map.Entry::getValue));
+}
+</%java>
+
+<div class="tabbable">
+    <ul class="nav nav-pills">
+        <li class="active"><a href="#tab_rsgroup_baseStats" 
data-toggle="tab">Base Stats</a></li>
+        <li class=""><a href="#tab_rsgroup_memoryStats" 
data-toggle="tab">Memory</a></li>
+        <li class=""><a href="#tab_rsgroup_requestStats" 
data-toggle="tab">Requests</a></li>
+        <li class=""><a href="#tab_rsgroup_storeStats" 
data-toggle="tab">Storefiles</a></li>
+        <li class=""><a href="#tab_rsgroup_compactStats" 
data-toggle="tab">Compactions</a></li>
+    </ul>
+    <div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px 
solid #ddd;">
+        <div class="tab-pane active" id="tab_rsgroup_baseStats">
+            <& rsgroup_baseStats; rsGroupInfos = rsGroupInfos; collectServers= 
collectServers &>
+        </div>
+        <div class="tab-pane" id="tab_rsgroup_memoryStats">
+            <& rsgroup_memoryStats; rsGroupInfos = rsGroupInfos; 
collectServers= collectServers &>
+        </div>
+        <div class="tab-pane" id="tab_rsgroup_requestStats">
+            <& rsgroup_requestStats; rsGroupInfos = rsGroupInfos; 
collectServers= collectServers &>
+        </div>
+        <div class="tab-pane" id="tab_rsgroup_storeStats">
+            <& rsgroup_storeStats; rsGroupInfos = rsGroupInfos; 
collectServers= collectServers &>
+        </div>
+        <div class="tab-pane" id="tab_rsgroup_compactStats">
+            <& rsgroup_compactStats; rsGroupInfos = rsGroupInfos; 
collectServers= collectServers &>
+        </div>
+    </div>
+</div>
+
+</%if>
+
+<%def rsgroup_baseStats>
+<%args>
+    RSGroupInfo [] rsGroupInfos;
+    Map<Address, ServerLoad> collectServers;
+</%args>
+<table class="table table-striped">
+<tr>
+    <th>RSGroup Name</th>
+    <th>Num. Online Servers</th>
+    <th>Num. Dead Servers</th>
+    <th>Num. Tables</th>
+    <th>Requests Per Second</th>
+    <th>Num. Regions</th>
+    <th>Average Load</th>
+</tr>
+<%java>
+    int totalOnlineServers = 0;
+    int totalDeadServers = 0;
+    int totalTables = 0;
+    int totalRequests = 0;
+    int totalRegions = 0;
+    for (RSGroupInfo rsGroupInfo: rsGroupInfos) {
+      String rsGroupName = rsGroupInfo.getName();
+      int onlineServers = 0;
+      int deadServers = 0;
+      int tables = 0;
+      long requestsPerSecond = 0;
+      int numRegionsOnline = 0;
+      Set<Address> servers = rsGroupInfo.getServers();
+      for (Address server : servers) {
+        ServerLoad sl = collectServers.get(server);
+        if (sl != null) {
+          requestsPerSecond += sl.getNumberOfRequests();
+          numRegionsOnline += sl.getNumberOfRegions();
+          //rsgroup total
+          totalRegions += sl.getNumberOfRegions();
+          totalRequests += sl.getNumberOfRequests();
+          totalOnlineServers++;
+          onlineServers++;
+        } else {
+          totalDeadServers++;
+          deadServers++;
+        }
+      }
+      tables = rsGroupInfo.getTables().size();
+      totalTables += tables;
+      double avgLoad = onlineServers == 0 ? 0 :
+            (double)numRegionsOnline / (double)onlineServers;
+</%java>
+<tr>
+    <td><& rsGroupLink; rsGroupName=rsGroupName; &></td>
+    <td><% onlineServers %></td>
+    <td><% deadServers %></td>
+    <td><% tables %></td>
+    <td><% requestsPerSecond %></td>
+    <td><% numRegionsOnline %></td>
+    <td><% avgLoad %></td>
+</tr>
+<%java>
+}
+</%java>
+<tr><td>Total:<% rsGroupInfos.length %></td>
+<td><% totalOnlineServers %></td>
+<td><% totalDeadServers %></td>
+<td><% totalTables %></td>
+<td><% totalRequests %></td>
+<td><% totalRegions %></td>
+<td><% master.getServerManager().getAverageLoad() %></td>
+</tr>
+</table>
+</%def>
+
+<%def rsgroup_memoryStats>
+<%args>
+  RSGroupInfo [] rsGroupInfos;
+  Map<Address, ServerLoad> collectServers;
+</%args>
+<table class="table table-striped">
+<tr>
+    <th>RSGroup Name</th>
+    <th>Used Heap</th>
+    <th>Max Heap</th>
+    <th>Memstore Size</th>
+
+</tr>
+<%java>
+    for (RSGroupInfo rsGroupInfo: rsGroupInfos) {
+      String rsGroupName = rsGroupInfo.getName();
+      long usedHeap = 0;
+      long maxHeap = 0;
+      long memstoreSize = 0;
+      for (Address server : rsGroupInfo.getServers()) {
+        ServerLoad sl = collectServers.get(server);
+        if (sl != null) {
+          usedHeap += sl.getUsedHeapMB();
+          maxHeap += sl.getMaxHeapMB();
+          memstoreSize += sl.getMemstoreSizeInMB();
+        }
+      }
+</%java>
+<tr>
+    <td><& rsGroupLink; rsGroupName=rsGroupName; &></td>
+    <td><% TraditionalBinaryPrefix.long2String(usedHeap
+            * TraditionalBinaryPrefix.MEGA.value, "B", 1) %></td>
+    <td><% TraditionalBinaryPrefix.long2String(maxHeap
+            * TraditionalBinaryPrefix.MEGA.value, "B", 1) %></td>
+    <td><% TraditionalBinaryPrefix.long2String(memstoreSize
+            * TraditionalBinaryPrefix.MEGA.value, "B", 1) %></td>
+
+</tr>
+<%java>
+}
+</%java>
+</table>
+</%def>
+
+<%def rsgroup_requestStats>
+<%args>
+  RSGroupInfo [] rsGroupInfos;
+  Map<Address, ServerLoad> collectServers;
+</%args>
+<table class="table table-striped">
+<tr>
+    <th>RSGroup Name</th>
+    <th>Request Per Second</th>
+    <th>Read Request Count</th>
+    <th>Write Request Count</th>
+</tr>
+<%java>
+    for (RSGroupInfo rsGroupInfo: rsGroupInfos) {
+      String rsGroupName = rsGroupInfo.getName();
+      long requestsPerSecond = 0;
+      long readRequests = 0;
+      long writeRequests = 0;
+      for (Address server : rsGroupInfo.getServers()) {
+        ServerLoad sl = collectServers.get(server);
+        if (sl != null) {
+          requestsPerSecond += sl.getNumberOfRequests();
+          readRequests += sl.getReadRequestsCount();
+          writeRequests += sl.getWriteRequestsCount();
+        }
+      }
+</%java>
+<tr>
+<td><& rsGroupLink; rsGroupName=rsGroupName; &></td>
+<td><% requestsPerSecond %></td>
+<td><% readRequests %></td>
+<td><% writeRequests %></td>
+</tr>
+<%java>
+}
+</%java>
+</table>
+</%def>
+
+
+<%def rsgroup_storeStats>
+<%args>
+  RSGroupInfo [] rsGroupInfos;
+  Map<Address, ServerLoad> collectServers;
+</%args>
+<table class="table table-striped">
+<tr>
+    <th>RSGroup Name</th>
+    <th>Num. Stores</th>
+    <th>Num. Storefiles</th>
+    <th>Storefile Size Uncompressed</th>
+    <th>Storefile Size</th>
+    <th>Index Size</th>
+    <th>Bloom Size</th>
+</tr>
+<%java>
+    for (RSGroupInfo rsGroupInfo: rsGroupInfos) {
+      String rsGroupName = rsGroupInfo.getName();
+      int numStores = 0;
+      long numStorefiles = 0;
+      long uncompressedStorefileSize  = 0;
+      long storefileSize  = 0;
+      long indexSize  = 0;
+      long bloomSize  = 0;
+      int count = 0;
+      for (Address server : rsGroupInfo.getServers()) {
+        ServerLoad sl = collectServers.get(server);
+        if (sl != null) {
+          numStores += sl.getStores();
+          numStorefiles += sl.getStorefiles();
+          uncompressedStorefileSize += sl.getStoreUncompressedSizeMB();
+          storefileSize += sl.getStorefileSizeInMB();
+          indexSize += sl.getTotalStaticIndexSizeKB();
+          bloomSize += sl.getTotalStaticBloomSizeKB();
+          count++;
+        }
+      }
+</%java>
+<tr>
+<td><& rsGroupLink; rsGroupName=rsGroupName; &></td>
+<td><% numStores %></td>
+<td><% numStorefiles %></td>
+<td><% TraditionalBinaryPrefix.long2String(
+    uncompressedStorefileSize * TraditionalBinaryPrefix.MEGA.value, "B", 1) 
%></td>
+<td><% TraditionalBinaryPrefix.long2String(
+    storefileSize * TraditionalBinaryPrefix.MEGA.value, "B", 1) %></td>
+<td><% TraditionalBinaryPrefix.long2String(
+    indexSize * TraditionalBinaryPrefix.KILO.value, "B", 1) %></td>
+<td><% TraditionalBinaryPrefix.long2String(
+    bloomSize * TraditionalBinaryPrefix.KILO.value, "B", 1) %></td>
+</tr>
+<%java>
+}
+</%java>
+</table>
+</%def>
+
+<%def rsgroup_compactStats>
+<%args>
+  RSGroupInfo [] rsGroupInfos;
+  Map<Address, ServerLoad> collectServers;
+</%args>
+<table class="table table-striped">
+<tr>
+    <th>RSGroup Name</th>
+    <th>Num. Compacting KVs</th>
+    <th>Num. Compacted KVs</th>
+    <th>Remaining KVs</th>
+    <th>Compaction Progress</th>
+</tr>
+<%java>
+    for (RSGroupInfo rsGroupInfo: rsGroupInfos) {
+      String rsGroupName = rsGroupInfo.getName();
+      int numStores = 0;
+      long totalCompactingKVs = 0;
+      long numCompactedKVs = 0;
+      long remainingKVs = 0;
+      long compactionProgress  = 0;
+      for (Address server : rsGroupInfo.getServers()) {
+        ServerLoad sl = collectServers.get(server);
+        if (sl != null) {
+          totalCompactingKVs += sl.getTotalCompactingKVs();
+          numCompactedKVs += sl.getCurrentCompactedKVs();
+        }
+      }
+      remainingKVs = totalCompactingKVs - numCompactedKVs;
+      String percentDone = "";
+      if  (totalCompactingKVs > 0) {
+           percentDone = String.format("%.2f", 100 *
+              ((float) numCompactedKVs / totalCompactingKVs)) + "%";
+      }
+</%java>
+<tr>
+<td><& rsGroupLink; rsGroupName=rsGroupName; &></td>
+<td><% totalCompactingKVs %></td>
+<td><% numCompactedKVs %></td>
+<td><% remainingKVs %></td>
+<td><% percentDone %></td>
+</tr>
+<%java>
+}
+</%java>
+</table>
+</%def>
+
+
+<%def rsGroupLink>
+    <%args>
+    String rsGroupName;
+    </%args>
+    <a href=rsgroup.jsp?name=<% rsGroupName %>><% rsGroupName %></a>
+</%def>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hbase/blob/cde1f821/hbase-server/src/main/resources/hbase-webapps/master/rsgroup.jsp
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/resources/hbase-webapps/master/rsgroup.jsp 
b/hbase-server/src/main/resources/hbase-webapps/master/rsgroup.jsp
new file mode 100644
index 0000000..9f95b76
--- /dev/null
+++ b/hbase-server/src/main/resources/hbase-webapps/master/rsgroup.jsp
@@ -0,0 +1,441 @@
+<%--
+/**
+ * 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.
+ */
+--%>
+<%@ page contentType="text/html;charset=UTF-8"
+  import="java.util.ArrayList"
+  import="java.util.Collections"
+  import="java.util.Date"
+  import="java.util.List"
+  import="java.util.Map"
+  import="java.util.regex.Pattern"
+  import="java.util.stream.Stream"
+  import="java.util.stream.Collectors"
+  import="org.apache.hadoop.hbase.HTableDescriptor"
+  import="org.apache.hadoop.hbase.RSGroupTableAccessor"
+  import="org.apache.hadoop.hbase.ServerLoad"
+  import="org.apache.hadoop.hbase.ServerName"
+  import="org.apache.hadoop.hbase.TableName"
+  import="org.apache.hadoop.hbase.client.Admin"
+  import="org.apache.hadoop.hbase.client.RegionInfo"
+  import="org.apache.hadoop.hbase.client.TableState"
+  import="org.apache.hadoop.hbase.client.TableDescriptor"
+  import="org.apache.hadoop.hbase.master.HMaster"
+  import="org.apache.hadoop.hbase.master.RegionState"
+  import="org.apache.hadoop.hbase.net.Address"
+  import="org.apache.hadoop.hbase.rsgroup.RSGroupInfo"
+  import="org.apache.hadoop.hbase.util.Bytes"
+  import="org.apache.hadoop.hbase.util.VersionInfo"
+  import="org.apache.hadoop.util.StringUtils.TraditionalBinaryPrefix"%>
+<%
+  HMaster master = (HMaster)getServletContext().getAttribute(HMaster.MASTER);
+  String rsGroupName = request.getParameter("name");
+  List<Address> rsGroupServers = new ArrayList<>();
+  List<TableName> rsGroupTables = new ArrayList<>();
+  RSGroupInfo rsGroupInfo = null;
+  if (rsGroupName != null && !rsGroupName.isEmpty()) {
+    rsGroupInfo = RSGroupTableAccessor.getRSGroupInfo(
+        master.getConnection(), Bytes.toBytes(rsGroupName));
+    if (rsGroupInfo != null) {
+      rsGroupServers.addAll(rsGroupInfo.getServers());
+      rsGroupTables.addAll(rsGroupInfo.getTables());
+    }
+  }
+  Collections.sort(rsGroupServers);
+    rsGroupTables.sort((o1, o2) -> {
+      int compare = Bytes.compareTo(o1.getNamespace(), o2.getNamespace());
+      if (compare != 0)
+          return compare;
+      compare = Bytes.compareTo(o1.getQualifier(), o2.getQualifier());
+      if (compare != 0)
+          return compare;
+      return 0;
+    });
+
+  Map<Address, ServerLoad> onlineServers = Collections.emptyMap();
+  Map<Address, ServerName> serverMaping = Collections.emptyMap();
+  if (master.getServerManager() != null) {
+    onlineServers = 
master.getServerManager().getOnlineServers().entrySet().stream()
+            .collect(Collectors.toMap(p -> p.getKey().getAddress(), 
Map.Entry::getValue));
+    serverMaping =
+        master.getServerManager().getOnlineServers().entrySet().stream()
+            .collect(Collectors.toMap(p -> p.getKey().getAddress(), 
Map.Entry::getKey));
+  }
+  pageContext.setAttribute("pageTitle", "RSGroup: " + rsGroupName);
+%>
+<jsp:include page="header.jsp">
+    <jsp:param name="pageTitle" value="${pageTitle}"/>
+</jsp:include>
+<div class="container-fluid content">
+  <% if (rsGroupName == null || rsGroupName.isEmpty() || rsGroupInfo == null) 
{ %>
+  <div class="row inner_header">
+      <div class="page-header">
+          <h1>RSGroup: "<%= rsGroupName %>" does not exist</h1>
+      </div>
+  </div>
+  <p>Go <a href="javascript:history.back()">Back</a>, or wait for the redirect.
+  <% } else { %>
+  <div class="container-fluid content">
+    <div class="row">
+      <div class="page-header">
+        <h1>RSGroup: <%= rsGroupName %></h1>
+      </div>
+    </div>
+  </div>
+  <div class="container-fluid content">
+    <div class="row">
+      <div class="inner_header">
+        <h1>Region Servers</h1>
+      </div>
+    </div>
+    <div class="tabbable">
+      <% if (rsGroupServers != null && rsGroupServers.size() > 0) { %>
+        <ul class="nav nav-pills">
+          <li class="active">
+            <a href="#tab_baseStats" data-toggle="tab">Base Stats</a>
+          </li>
+          <li class="">
+            <a href="#tab_memoryStats" data-toggle="tab">Memory</a>
+          </li>
+          <li class="">
+            <a href="#tab_requestStats" data-toggle="tab">Requests</a>
+          </li>
+          <li class="">
+            <a href="#tab_storeStats" data-toggle="tab">Storefiles</a>
+          </li>
+          <li class="">
+            <a href="#tab_compactStats" data-toggle="tab">Compactions</a>
+          </li>
+        </ul>
+
+      <div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px 
solid #ddd;">
+        <div class="tab-pane active" id="tab_baseStats">
+          <table class="table table-striped">
+            <tr>
+              <th>ServerName</th>
+              <th>Start time</th>
+              <th>Last contact</th>
+              <th>Version</th>
+              <th>Requests Per Second</th>
+              <th>Num. Regions</th>
+            </tr>
+            <% int totalRegions = 0;
+               int totalRequests = 0;
+               int inconsistentNodeNum = 0;
+               String masterVersion = VersionInfo.getVersion();
+               for (Address server: rsGroupServers) {
+                 ServerName serverName = serverMaping.get(server);
+                 if (serverName != null) {
+                   ServerLoad sl = onlineServers.get(server);
+                   String version = master.getRegionServerVersion(serverName);
+                   if (!masterVersion.equals(version)) {
+                     inconsistentNodeNum ++;
+                   }
+                   double requestsPerSecond = 0.0;
+                   int numRegionsOnline = 0;
+                   long lastContact = 0;
+                   if (sl != null) {
+                     requestsPerSecond = sl.getRequestsPerSecond();
+                     numRegionsOnline = sl.getNumberOfRegions();
+                     totalRegions += sl.getNumberOfRegions();
+                     totalRequests += sl.getNumberOfRequests();
+                     lastContact = (System.currentTimeMillis() - 
sl.getReportTime())/1000;
+                   }
+                   long startcode = serverName.getStartcode();
+                   int infoPort = master.getRegionServerInfoPort(serverName);
+                   String url = "//" + serverName.getHostname() + ":" + 
infoPort + "/rs-status";%>
+                   <tr>
+                     <td><a href="<%= url %>"><%= serverName.getServerName() 
%></a></td>
+                     <td><%= new Date(startcode) %></td>
+                     <td><%= lastContact %></td>
+                     <td><%= version %></td>
+                     <td><%= String.format("%.0f", requestsPerSecond) %></td>
+                     <td><%= numRegionsOnline %></td>
+                   </tr>
+              <% } else { %>
+                   <tr>
+                     <td style="color:rgb(192,192,192);"><%= server %></td>
+                     <td style="color:rgb(192,192,192);"><%= "Dead" %></td>
+                     <td></td>
+                     <td></td>
+                     <td></td>
+                     <td></td>
+                   </tr>
+              <% } %>
+            <% } %>
+            <tr><td>Total:<%= rsGroupServers.size() %></td>
+            <td></td>
+            <td></td>
+            <%if (inconsistentNodeNum > 0) { %>
+                <td style="color:red;"><%= inconsistentNodeNum %> nodes with 
inconsistent version</td>
+            <%} else { %>
+                <td></td>
+            <%} %>
+            <td><%= totalRequests %></td>
+            <td><%= totalRegions %></td>
+            </tr>
+          </table>
+        </div>
+        <div class="tab-pane" id="tab_memoryStats">
+          <table class="table table-striped">
+            <tr>
+              <th>ServerName</th>
+              <th>Used Heap</th>
+              <th>Max Heap</th>
+              <th>Memstore Size</th>
+            </tr>
+            <% for (Address server: rsGroupServers) {
+                 ServerName serverName = serverMaping.get(server);
+                 ServerLoad sl = onlineServers.get(server);
+                 if (sl != null && serverName != null) {
+                   int infoPort = master.getRegionServerInfoPort(serverName);
+                   String url = "//" + serverName.getHostname() + ":" + 
infoPort + "/rs-status";
+            %>
+                   <tr>
+                     <td><a href="<%= url %>"><%= serverName.getServerName() 
%></a></td>
+                     <td><%= 
TraditionalBinaryPrefix.long2String(sl.getUsedHeapMB()
+                       * TraditionalBinaryPrefix.MEGA.value, "B", 1) %></td>
+                     <td><%= 
TraditionalBinaryPrefix.long2String(sl.getMaxHeapMB()
+                       * TraditionalBinaryPrefix.MEGA.value, "B", 1) %></td>
+                     <td><%= 
TraditionalBinaryPrefix.long2String(sl.getMemstoreSizeInMB()
+                       * TraditionalBinaryPrefix.MEGA.value, "B", 1) %></td>
+                   </tr>
+              <% } else { %>
+                   <tr>
+                     <td style="color:rgb(192,192,192);"><%= server %></td>
+                     <td></td>
+                     <td></td>
+                     <td></td>
+                   </tr>
+              <% }
+               } %>
+          </table>
+        </div>
+        <div class="tab-pane" id="tab_requestStats">
+          <table class="table table-striped">
+            <tr>
+                <th>ServerName</th>
+                <th>Request Per Second</th>
+                <th>Read Request Count</th>
+                <th>Write Request Count</th>
+            </tr>
+            <% for (Address server: rsGroupServers) {
+                 ServerName serverName = serverMaping.get(server);
+                 ServerLoad sl = onlineServers.get(server);
+                 if (sl != null && serverName != null) {
+                   int infoPort = master.getRegionServerInfoPort(serverName);
+                   String url = "//" + serverName.getHostname() + ":" + 
infoPort + "/rs-status";
+            %>
+                   <tr>
+                     <td><a href="<%= url %>"><%= serverName.getServerName() 
%></a></td>
+                     <td><%= String.format("%.0f", sl.getRequestsPerSecond()) 
%></td>
+                     <td><%= sl.getReadRequestsCount() %></td>
+                     <td><%= sl.getWriteRequestsCount() %></td>
+                   </tr>
+              <% } else { %>
+                   <tr>
+                     <td style="color:rgb(192,192,192);"><%= server %></td>
+                     <td></td>
+                     <td></td>
+                     <td></td>
+                   </tr>
+              <% }
+              } %>
+          </table>
+        </div>
+        <div class="tab-pane" id="tab_storeStats">
+          <table class="table table-striped">
+            <tr>
+                <th>ServerName</th>
+                <th>Num. Stores</th>
+                <th>Num. Storefiles</th>
+                <th>Storefile Size Uncompressed</th>
+                <th>Storefile Size</th>
+                <th>Index Size</th>
+                <th>Bloom Size</th>
+            </tr>
+            <%  for (Address server: rsGroupServers) {
+                  ServerName serverName = serverMaping.get(server);
+                  ServerLoad sl = onlineServers.get(server);
+                  if (sl != null && serverName != null) {
+                    int infoPort = master.getRegionServerInfoPort(serverName);
+                    String url = "//" + serverName.getHostname() + ":" + 
infoPort + "/rs-status";
+            %>
+                    <tr>
+                      <td><a href="<%= url %>"><%= serverName.getServerName() 
%></a></td>
+                      <td><%= sl.getStores() %></td>
+                      <td><%= sl.getStorefiles() %></td>
+                      <td><%= TraditionalBinaryPrefix.long2String(
+                          sl.getStoreUncompressedSizeMB() * 
TraditionalBinaryPrefix.MEGA.value, "B", 1) %></td>
+                      <td><%= 
TraditionalBinaryPrefix.long2String(sl.getStorefileSizeInMB()
+                          * TraditionalBinaryPrefix.MEGA.value, "B", 1) %></td>
+                      <td><%= 
TraditionalBinaryPrefix.long2String(sl.getTotalStaticIndexSizeKB()
+                          * TraditionalBinaryPrefix.KILO.value, "B", 1) %></td>
+                      <td><%= 
TraditionalBinaryPrefix.long2String(sl.getTotalStaticBloomSizeKB()
+                          * TraditionalBinaryPrefix.KILO.value, "B", 1) %></td>
+                    </tr>
+               <% } else { %>
+                    <tr>
+                      <td style="color:rgb(192,192,192);"><%= server %></td>
+                      <td></td>
+                      <td></td>
+                      <td></td>
+                      <td></td>
+                      <td></td>
+                      <td></td>
+                    </tr>
+              <% }
+              } %>
+          </table>
+        </div>
+        <div class="tab-pane" id="tab_compactStats">
+          <table class="table table-striped">
+            <tr>
+              <th>ServerName</th>
+              <th>Num. Compacting KVs</th>
+              <th>Num. Compacted KVs</th>
+              <th>Remaining KVs</th>
+              <th>Compaction Progress</th>
+            </tr>
+            <%  for (Address server: rsGroupServers) {
+                  ServerName serverName = serverMaping.get(server);
+                  ServerLoad sl = onlineServers.get(server);
+                  if (sl != null && serverName != null) {
+                    String percentDone = "";
+                    if  (sl.getTotalCompactingKVs() > 0) {
+                         percentDone = String.format("%.2f", 100 *
+                            ((float) sl.getCurrentCompactedKVs() / 
sl.getTotalCompactingKVs())) + "%";
+                    }
+                    int infoPort = master.getRegionServerInfoPort(serverName);
+                    String url = "//" + serverName.getHostname() + ":" + 
infoPort + "/rs-status";
+            %>
+                    <tr>
+                      <td><a href="<%= url %>"><%= serverName.getServerName() 
%></a></td>
+                      <td><%= sl.getTotalCompactingKVs() %></td>
+                      <td><%= sl.getCurrentCompactedKVs() %></td>
+                      <td><%= sl.getTotalCompactingKVs() - 
sl.getCurrentCompactedKVs() %></td>
+                      <td><%= percentDone %></td>
+                    </tr>
+               <% } else { %>
+                    <tr>
+                      <td style="color:rgb(192,192,192);"><%= server %></td>
+                      <td></td>
+                      <td></td>
+                      <td></td>
+                      <td></td>
+                    </tr>
+               <% }
+               } %>
+          </table>
+        </div>
+      </div>
+      <% } else { %>
+      <p> No Region Servers</p>
+      <% } %>
+    </div>
+  </div>
+  <br />
+
+  <div class="container-fluid content">
+    <div class="row inner_header">
+        <div class="page-header">
+            <h1>Tables</h1>
+        </div>
+    </div>
+
+    <% if (rsGroupTables != null && rsGroupTables.size() > 0) {
+        HTableDescriptor[] tables = null;
+        try (Admin admin = master.getConnection().getAdmin()) {
+            tables = master.isInitialized() ? admin.listTables((Pattern)null, 
true) : null;
+        }
+         Map<TableName, HTableDescriptor> tableDescriptors
+            = 
Stream.of(tables).collect(Collectors.toMap(TableDescriptor::getTableName, p -> 
p));
+    %>
+         <table class="table table-striped">
+         <tr>
+             <th>Namespace</th>
+             <th>Table</th>
+             <th>Stats</th>
+             <th>Online Regions</th>
+             <th>Offline Regions</th>
+             <th>Failed Regions</th>
+             <th>Split Regions</th>
+             <th>Other Regions</th>
+             <th>Description</th>
+         </tr>
+         <% for(TableName tableName : rsGroupTables) {
+             HTableDescriptor htDesc = tableDescriptors.get(tableName);
+             if(htDesc == null) {
+         %>
+               <tr>
+                 <td><%= tableName.getNamespaceAsString() %></td>
+                 <td><%= tableName.getQualifierAsString() %></td>
+                 <td style="color:rgb(0,0,255);"><%= "DELETED" %></td>
+                 <td></td>
+                 <td></td>
+                 <td></td>
+                 <td></td>
+                 <td></td>
+                 <td></td>
+               </tr>
+           <% } else { %>
+                <tr>
+                  <td><%= tableName.getNamespaceAsString() %></td>
+                  <td><a href="/table.jsp?name=<%= tableName.getNameAsString() 
%>"><%= tableName.getQualifierAsString() %></a></td>
+              <% TableState.State tableState = 
master.getTableStateManager().getTableState(tableName);
+                  if(TableState.isInStates(tableState,
+                          TableState.State.DISABLED, 
TableState.State.DISABLING)) {
+              %>
+                  <td style="color:red;"><%= tableState.name() %></td>
+              <% } else {  %>
+                  <td><%= tableState.name() %></td>
+              <% } %>
+              <% Map<RegionState.State, List<RegionInfo>> tableRegions =
+                     
master.getAssignmentManager().getRegionStates().getRegionByStateOfTable(tableName);
+                 int openRegionsCount = 
tableRegions.get(RegionState.State.OPEN).size();
+                 int offlineRegionsCount = 
tableRegions.get(RegionState.State.OFFLINE).size();
+                 int splitRegionsCount = 
tableRegions.get(RegionState.State.SPLIT).size();
+                 int failedRegionsCount = 
tableRegions.get(RegionState.State.FAILED_OPEN).size()
+                         + 
tableRegions.get(RegionState.State.FAILED_CLOSE).size();
+                 int otherRegionsCount = 0;
+                 for (List<RegionInfo> list: tableRegions.values()) {
+                     otherRegionsCount += list.size();
+                 }
+                 // now subtract known states
+                 otherRegionsCount = otherRegionsCount - openRegionsCount
+                         - failedRegionsCount - offlineRegionsCount
+                         - splitRegionsCount;
+              %>
+                  <td><%= openRegionsCount %></td>
+                  <td><%= offlineRegionsCount %></td>
+                  <td><%= failedRegionsCount %></td>
+                  <td><%= splitRegionsCount %></td>
+                  <td><%= otherRegionsCount %></td>
+                  <td><%= htDesc.toStringCustomizedValues() %></td>
+                </tr>
+           <% }
+            } %>
+           <p> <%= rsGroupTables.size() %> table(s) in set.</p>
+         </table>
+    <% } else { %>
+      <p> No Tables</p>
+    <% } %>
+  </div>
+<% } %>
+</div>
+<jsp:include page="footer.jsp" />

Reply via email to