kudu-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wdberke...@apache.org
Subject [3/3] kudu git commit: Rename *-path-handlers.* to *_path_handlers.*
Date Tue, 17 Oct 2017 23:10:41 GMT
Rename *-path-handlers.* to *_path_handlers.*

Our naming convention separates words with _'s, except for test files
which are often named {name of file whose content is tested}-test.cc.
This patch brings the various path handler files in line with the
convention.

Change-Id: Id7763d69ad484cf45cca6d77badd565843df2dd8
Reviewed-on: http://gerrit.cloudera.org:8080/8301
Reviewed-by: Adar Dembo <adar@cloudera.com>
Tested-by: Kudu Jenkins


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

Branch: refs/heads/master
Commit: 1c90b2f5c4e998d1db773ff2a2e67d8d93edfeea
Parents: abbfa49
Author: Will Berkeley <wdberkeley@apache.org>
Authored: Tue Oct 17 10:38:47 2017 -0700
Committer: Will Berkeley <wdberkeley@gmail.com>
Committed: Tue Oct 17 22:14:57 2017 +0000

----------------------------------------------------------------------
 src/kudu/master/CMakeLists.txt            |   2 +-
 src/kudu/master/master-path-handlers.cc   | 698 -------------------------
 src/kudu/master/master-path-handlers.h    |  82 ---
 src/kudu/master/master.cc                 |   2 +-
 src/kudu/master/master_path_handlers.cc   | 698 +++++++++++++++++++++++++
 src/kudu/master/master_path_handlers.h    |  82 +++
 src/kudu/server/CMakeLists.txt            |   6 +-
 src/kudu/server/default-path-handlers.cc  | 318 -----------
 src/kudu/server/default-path-handlers.h   |  35 --
 src/kudu/server/default_path_handlers.cc  | 319 +++++++++++
 src/kudu/server/default_path_handlers.h   |  35 ++
 src/kudu/server/pprof-path-handlers.cc    | 260 ---------
 src/kudu/server/pprof-path-handlers.h     |  27 -
 src/kudu/server/pprof_path_handlers.cc    | 260 +++++++++
 src/kudu/server/pprof_path_handlers.h     |  27 +
 src/kudu/server/server_base.cc            |   4 +-
 src/kudu/server/tracing-path-handlers.cc  | 287 ----------
 src/kudu/server/tracing-path-handlers.h   |  40 --
 src/kudu/server/tracing_path_handlers.cc  | 285 ++++++++++
 src/kudu/server/tracing_path_handlers.h   |  40 ++
 src/kudu/server/webserver-test.cc         |   2 +-
 src/kudu/tserver/CMakeLists.txt           |   2 +-
 src/kudu/tserver/tablet_server.cc         |   2 +-
 src/kudu/tserver/tserver-path-handlers.cc | 679 ------------------------
 src/kudu/tserver/tserver-path-handlers.h  |  84 ---
 src/kudu/tserver/tserver_path_handlers.cc | 677 ++++++++++++++++++++++++
 src/kudu/tserver/tserver_path_handlers.h  |  84 +++
 27 files changed, 2517 insertions(+), 2520 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/master/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/master/CMakeLists.txt b/src/kudu/master/CMakeLists.txt
index 53fbbde..5d5fe2c 100644
--- a/src/kudu/master/CMakeLists.txt
+++ b/src/kudu/master/CMakeLists.txt
@@ -34,10 +34,10 @@ ADD_EXPORTABLE_LIBRARY(master_proto
 
 set(MASTER_SRCS
   catalog_manager.cc
-  master-path-handlers.cc
   master.cc
   master_cert_authority.cc
   master_options.cc
+  master_path_handlers.cc
   master_service.cc
   mini_master.cc
   sys_catalog.cc

http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/master/master-path-handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/master-path-handlers.cc b/src/kudu/master/master-path-handlers.cc
deleted file mode 100644
index 397776b..0000000
--- a/src/kudu/master/master-path-handlers.cc
+++ /dev/null
@@ -1,698 +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.
-
-#include "kudu/master/master-path-handlers.h"
-
-#include <algorithm>
-#include <array>
-#include <cstdint>
-#include <iosfwd>
-#include <map>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <type_traits>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include <boost/bind.hpp> // IWYU pragma: keep
-#include <glog/logging.h>
-
-#include "kudu/common/common.pb.h"
-#include "kudu/common/partition.h"
-#include "kudu/common/schema.h"
-#include "kudu/common/wire_protocol.h"
-#include "kudu/common/wire_protocol.pb.h"
-#include "kudu/consensus/metadata.pb.h"
-#include "kudu/consensus/quorum_util.h"
-#include "kudu/gutil/map-util.h"
-#include "kudu/gutil/port.h"
-#include "kudu/gutil/ref_counted.h"
-#include "kudu/gutil/stringprintf.h"
-#include "kudu/gutil/strings/join.h"
-#include "kudu/gutil/strings/numbers.h"
-#include "kudu/gutil/strings/substitute.h"
-#include "kudu/master/catalog_manager.h"
-#include "kudu/master/master.h"
-#include "kudu/master/master.pb.h"
-#include "kudu/master/master_options.h"
-#include "kudu/master/sys_catalog.h"
-#include "kudu/master/ts_descriptor.h"
-#include "kudu/master/ts_manager.h"
-#include "kudu/server/monitored_task.h"
-#include "kudu/server/webui_util.h"
-#include "kudu/util/cow_object.h"
-#include "kudu/util/easy_json.h"
-#include "kudu/util/jsonwriter.h"
-#include "kudu/util/monotime.h"
-#include "kudu/util/net/net_util.h"
-#include "kudu/util/net/sockaddr.h"
-#include "kudu/util/pb_util.h"
-#include "kudu/util/string_case.h"
-#include "kudu/util/url-coding.h"
-#include "kudu/util/web_callback_registry.h"
-
-namespace kudu {
-
-using consensus::ConsensusStatePB;
-using consensus::RaftPeerPB;
-using std::array;
-using std::map;
-using std::ostringstream;
-using std::pair;
-using std::shared_ptr;
-using std::string;
-using std::vector;
-using strings::Substitute;
-
-namespace master {
-
-MasterPathHandlers::~MasterPathHandlers() {
-}
-
-void MasterPathHandlers::HandleTabletServers(const Webserver::WebRequest& /*req*/,
-                                             Webserver::WebResponse* resp) {
-  EasyJson* output = resp->output;
-  vector<std::shared_ptr<TSDescriptor>> descs;
-  master_->ts_manager()->GetAllDescriptors(&descs);
-
-  (*output)["num_ts"] = std::to_string(descs.size());
-
-  // In mustache, when conditionally rendering a section of the template based
-  // on a key, the value becomes the context. In the subcontext, searches for
-  // keys used to display a value (e.g. {{my_key}}) recurse into the parent
-  // context-- but not when used to define a subsection (e.g. {{#my_list}}.
-  // Thus, we use a negative test {{^has_no_live_ts}} to decide if we
-  // should render the live and dead tablet server tables.
-  bool has_no_live_ts = true;
-  bool has_no_dead_ts = true;
-  output->Set("live_tservers", EasyJson::kArray);
-  output->Set("dead_tservers", EasyJson::kArray);
-  map<string, array<int, 2>> version_counts;
-  for (const std::shared_ptr<TSDescriptor>& desc : descs) {
-    string ts_key = desc->PresumedDead() ? "dead_tservers" : "live_tservers";
-    EasyJson ts_json = (*output)[ts_key].PushBack(EasyJson::kObject);
-
-    ServerRegistrationPB reg;
-    desc->GetRegistration(&reg);
-    ts_json["uuid"] = desc->permanent_uuid();
-    if (!reg.http_addresses().empty()) {
-      ts_json["target"] = Substitute("$0://$1:$2/",
-                                     reg.https_enabled() ? "https" : "http",
-                                     reg.http_addresses(0).host(),
-                                     reg.http_addresses(0).port());
-    }
-    ts_json["time_since_hb"] = StringPrintf("%.1fs", desc->TimeSinceHeartbeat().ToSeconds());
-    ts_json["registration"] = pb_util::SecureShortDebugString(reg);
-    version_counts[reg.software_version()][desc->PresumedDead() ? 1 : 0]++;
-    has_no_live_ts &= desc->PresumedDead();
-    has_no_dead_ts &= !desc->PresumedDead();
-  }
-  (*output)["has_no_live_ts"] = has_no_live_ts;
-  (*output)["has_no_dead_ts"] = has_no_dead_ts;
-
-  output->Set("version_counts", EasyJson::kArray);
-  for (const auto& entry : version_counts) {
-    EasyJson version_count_json = (*output)["version_counts"].PushBack(EasyJson::kObject);
-    version_count_json["version"] = entry.first;
-    version_count_json["live"] = Substitute("$0", entry.second[0]);
-    version_count_json["dead"] = Substitute("$0", entry.second[1]);
-  }
-}
-
-namespace {
-
-// Extracts the value of the 'redirects' parameter from 'req'; returns 0 if the
-// parameter doesn't exist or couldn't be parsed.
-int ExtractRedirectsFromRequest(const Webserver::WebRequest& req) {
-  string redirects_str;
-  int redirects = 0;
-  if (FindCopy(req.parsed_args, "redirects", &redirects_str)) {
-    if (!safe_strto32(redirects_str, &redirects)) {
-      return 0;
-    }
-  }
-  return redirects;
-}
-
-} // anonymous namespace
-
-void MasterPathHandlers::HandleCatalogManager(const Webserver::WebRequest& req,
-                                              Webserver::WebResponse* resp) {
-  EasyJson* output = resp->output;
-  CatalogManager::ScopedLeaderSharedLock l(master_->catalog_manager());
-  if (!l.catalog_status().ok()) {
-    (*output)["error"] = Substitute("Master is not ready: $0",  l.catalog_status().ToString());
-    return;
-  }
-  if (!l.leader_status().ok()) {
-    // Track redirects to prevent a redirect loop.
-    int redirects = ExtractRedirectsFromRequest(req);
-    SetupLeaderMasterRedirect("tables?", redirects, output);
-    return;
-  }
-
-  std::vector<scoped_refptr<TableInfo>> tables;
-  master_->catalog_manager()->GetAllTables(&tables);
-  int num_running_tables = 0;
-  EasyJson tables_json = output->Set("tables", EasyJson::kArray);
-  for (const scoped_refptr<TableInfo>& table : tables) {
-    TableMetadataLock l(table.get(), LockMode::READ);
-    if (!l.data().is_running()) {
-      continue;
-    }
-    num_running_tables++; // Table count excluding deleted ones
-    string state = SysTablesEntryPB_State_Name(l.data().pb.state());
-    Capitalize(&state);
-    EasyJson table_json = tables_json.PushBack(EasyJson::kObject);
-    table_json["name"] = EscapeForHtmlToString(l.data().name());
-    table_json["id"] = EscapeForHtmlToString(table->id());
-    table_json["state"] = state;
-    table_json["message"] = EscapeForHtmlToString(l.data().pb.state_msg());
-  }
-  (*output).Set<int64_t>("num_tables", num_running_tables);
-}
-
-namespace {
-
-
-// Holds info about a peer for use in the tablet detail table.
-struct TabletDetailPeerInfo {
-  string text;
-  string target;
-  string role;
-  bool is_leader;
-};
-
-int RoleToSortIndex(RaftPeerPB::Role r) {
-  switch (r) {
-    case RaftPeerPB::LEADER: return 0;
-    default: return 1 + static_cast<int>(r);
-  }
-}
-
-bool CompareByRole(const pair<TabletDetailPeerInfo, RaftPeerPB::Role>& a,
-                   const pair<TabletDetailPeerInfo, RaftPeerPB::Role>& b) {
-  return RoleToSortIndex(a.second) < RoleToSortIndex(b.second);
-}
-
-} // anonymous namespace
-
-
-void MasterPathHandlers::HandleTablePage(const Webserver::WebRequest& req,
-                                         Webserver::WebResponse* resp) {
-  EasyJson* output = resp->output;
-  // Parse argument.
-  string table_id;
-  if (!FindCopy(req.parsed_args, "id", &table_id)) {
-    resp->status_code = HttpStatusCode::BadRequest;
-    (*output)["error"] = "Missing 'id' argument";
-    return;
-  }
-
-  CatalogManager::ScopedLeaderSharedLock l(master_->catalog_manager());
-  if (!l.catalog_status().ok()) {
-    (*output)["error"] = Substitute("Master is not ready: $0", l.catalog_status().ToString());
-    return;
-  }
-  if (!l.leader_status().ok()) {
-    // It's possible to respond 307 Temporary Redirect and automatically redirect with
-    // a Location header, but this would likely confuse users about which master's web ui
-    // they are looking at. Instead, we show a link users can click to go to the leader master.
-    // We track redirects to prevent a redirect loop.
-    int redirects = ExtractRedirectsFromRequest(req);
-    SetupLeaderMasterRedirect(Substitute("table?id=$0", table_id), redirects, output);
-    return;
-  }
-
-  scoped_refptr<TableInfo> table;
-  Status s = master_->catalog_manager()->GetTableInfo(table_id, &table);
-  if (!s.ok()) {
-    resp->status_code = HttpStatusCode::ServiceUnavailable;
-    (*output)["error"] = Substitute("Master is not ready: $0", s.ToString());
-    return;
-  }
-
-  if (!table) {
-    resp->status_code = HttpStatusCode::NotFound;
-    (*output)["error"] = "Table not found";
-    return;
-  }
-
-  Schema schema;
-  PartitionSchema partition_schema;
-  vector<scoped_refptr<TabletInfo>> tablets;
-  {
-    TableMetadataLock l(table.get(), LockMode::READ);
-    (*output)["name"] = l.data().name();
-    (*output)["id"] = table_id;
-    (*output)["version"] = l.data().pb.version();
-
-    string state = SysTablesEntryPB_State_Name(l.data().pb.state());
-    Capitalize(&state);
-    (*output)["state"] = state;
-    string state_msg = l.data().pb.state_msg();
-    if (!state_msg.empty()) {
-      (*output)["state_msg"] = state_msg;
-    }
-
-    s = SchemaFromPB(l.data().pb.schema(), &schema);
-    if (!s.ok()) {
-      (*output)["error"] = Substitute("Unable to decode schema: $0", s.ToString());
-      return;
-    }
-    s = PartitionSchema::FromPB(l.data().pb.partition_schema(), schema, &partition_schema);
-    if (!s.ok()) {
-      (*output)["error"] =
-          Substitute("Unable to decode partition schema: $0", s.ToString());
-      return;
-    }
-    table->GetAllTablets(&tablets);
-  }
-
-  SchemaToJson(schema, output);
-
-  // We have to collate partition schema and tablet information in order to set
-  // up the partition schema, tablet summary, and tablet detail tables.
-  std::vector<string> range_partitions;
-  map<string, int> summary_states;
-  (*output)["detail_partition_schema_header"] = partition_schema.PartitionTableHeader(schema);
-  EasyJson tablets_detail_json = output->Set("tablets_detail", EasyJson::kArray);
-  for (const scoped_refptr<TabletInfo>& tablet : tablets) {
-    vector<pair<TabletDetailPeerInfo, RaftPeerPB::Role>> sorted_replicas;
-    TabletMetadataLock l(tablet.get(), LockMode::READ);
-
-    // Count states for tablet summary.
-    summary_states[SysTabletsEntryPB_State_Name(l.data().pb.state())]++;
-
-    // Collect details about each tablet replica.
-    if (l.data().pb.has_consensus_state()) {
-      const ConsensusStatePB& cstate = l.data().pb.consensus_state();
-      for (const auto& peer : cstate.committed_config().peers()) {
-        TabletDetailPeerInfo peer_info;
-        shared_ptr<TSDescriptor> ts_desc;
-        if (master_->ts_manager()->LookupTSByUUID(peer.permanent_uuid(), &ts_desc)) {
-          auto link_pair = TSDescToLinkPair(*ts_desc.get(), tablet->id());
-          peer_info.text = std::move(link_pair.first);
-          peer_info.target = std::move(link_pair.second);
-        } else {
-          peer_info.text = peer.permanent_uuid();
-        }
-        RaftPeerPB::Role role = GetConsensusRole(peer.permanent_uuid(), cstate);
-        peer_info.role = RaftPeerPB_Role_Name(role);
-        peer_info.is_leader = role == RaftPeerPB::LEADER;
-        sorted_replicas.emplace_back(std::make_pair(peer_info, role));
-      }
-    }
-    std::sort(sorted_replicas.begin(), sorted_replicas.end(), &CompareByRole);
-
-    // Generate a readable description of the partition of each tablet, used
-    // both for each tablet's details and the readable range partition schema.
-    Partition partition;
-    Partition::FromPB(l.data().pb.partition(), &partition);
-
-    // For each unique range partition, add a debug string to range_partitions.
-    // To ensure uniqueness, only use partitions whose hash buckets are all 0.
-    if (std::all_of(partition.hash_buckets().begin(),
-                    partition.hash_buckets().end(),
-                    [] (const int32_t& bucket) { return bucket == 0; })) {
-      range_partitions.emplace_back(
-          partition_schema.RangePartitionDebugString(partition.range_key_start(),
-                                                     partition.range_key_end(),
-                                                     schema));
-    }
-
-    // Combine the tablet details and partition info for each tablet.
-    EasyJson tablet_detail_json = tablets_detail_json.PushBack(EasyJson::kObject);
-    tablet_detail_json["id"] = tablet->id();
-    tablet_detail_json["partition_cols"] = partition_schema.PartitionTableEntry(schema, partition);
-    string state = SysTabletsEntryPB_State_Name(l.data().pb.state());
-    Capitalize(&state);
-    tablet_detail_json["state"] = state;
-    tablet_detail_json["state_msg"] = l.data().pb.state_msg();
-    EasyJson peers_json = tablet_detail_json.Set("peers", EasyJson::kArray);
-    for (const auto& e : sorted_replicas) {
-      EasyJson peer_json = peers_json.PushBack(EasyJson::kObject);
-      peer_json["text"] = e.first.text;
-      if (!e.first.target.empty()) {
-        peer_json["target"] = e.first.target;
-      }
-      peer_json["role"] = e.first.role;
-      peer_json["is_leader"] = e.first.is_leader;
-    }
-  }
-
-  (*output)["partition_schema"] = partition_schema.DisplayString(schema, range_partitions);
-
-  EasyJson summary_json = output->Set("tablets_summary", EasyJson::kArray);
-  for (const auto& entry : summary_states) {
-    EasyJson state_json = summary_json.PushBack(EasyJson::kObject);
-    state_json["state"] = entry.first;
-    state_json["count"] = entry.second;
-    double percentage = (100.0 * entry.second) / tablets.size();
-    state_json["percentage"] = tablets.empty() ? "0.0" : StringPrintf("%.2f", percentage);
-  }
-
-  // Used to make the Impala CREATE TABLE statement.
-  (*output)["master_addresses"] = MasterAddrsToCsv();
-
-  std::vector<scoped_refptr<MonitoredTask>> task_list;
-  table->GetTaskList(&task_list);
-  TaskListToJson(task_list, output);
-}
-
-void MasterPathHandlers::HandleMasters(const Webserver::WebRequest& /*req*/,
-                                       Webserver::WebResponse* resp) {
-  EasyJson* output = resp->output;
-  vector<ServerEntryPB> masters;
-  Status s = master_->ListMasters(&masters);
-  if (!s.ok()) {
-    string msg = s.CloneAndPrepend("Unable to list Masters").ToString();
-    LOG(WARNING) << msg;
-    (*output)["error"] = msg;
-    return;
-  }
-  output->Set("masters", EasyJson::kArray);
-  for (const ServerEntryPB& master : masters) {
-    EasyJson master_json = (*output)["masters"].PushBack(EasyJson::kObject);
-    if (master.has_error()) {
-      Status error = StatusFromPB(master.error());
-      master_json["error"] = error.ToString();
-      continue;
-    }
-    const ServerRegistrationPB& reg = master.registration();
-    master_json["uuid"] = master.instance_id().permanent_uuid();
-    if (!reg.http_addresses().empty()) {
-      master_json["target"] = Substitute("$0://$1:$2/",
-                                         reg.https_enabled() ? "https" : "http",
-                                         reg.http_addresses(0).host(),
-                                         reg.http_addresses(0).port());
-    }
-    master_json["role"] = master.has_role() ? RaftPeerPB_Role_Name(master.role()) : "N/A";
-    master_json["registration"] = pb_util::SecureShortDebugString(master.registration());
-  }
-}
-
-namespace {
-
-// Visitor for the catalog table which dumps tables and tablets in a JSON format. This
-// dump is interpreted by the CM agent in order to track time series entities in the SMON
-// database.
-//
-// This implementation relies on scanning the catalog table directly instead of using the
-// catalog manager APIs. This allows it to work even on a non-leader master, and avoids
-// any requirement for locking. For the purposes of metrics entity gathering, it's OK to
-// serve a slightly stale snapshot.
-//
-// It is tempting to directly dump the metadata protobufs using JsonWriter::Protobuf(...),
-// but then we would be tying ourselves to textual compatibility of the PB field names in
-// our catalog table. Instead, the implementation specifically dumps the fields that we
-// care about.
-//
-// This should be considered a "stable" protocol -- do not rename, remove, or restructure
-// without consulting with the CM team.
-class JsonDumper : public TableVisitor, public TabletVisitor {
- public:
-  explicit JsonDumper(JsonWriter* jw) : jw_(jw) {
-  }
-
-  Status VisitTable(const std::string& table_id,
-                    const SysTablesEntryPB& metadata) OVERRIDE {
-    if (metadata.state() != SysTablesEntryPB::RUNNING) {
-      return Status::OK();
-    }
-
-    jw_->StartObject();
-    jw_->String("table_id");
-    jw_->String(table_id);
-
-    jw_->String("table_name");
-    jw_->String(metadata.name());
-
-    jw_->String("state");
-    jw_->String(SysTablesEntryPB::State_Name(metadata.state()));
-
-    jw_->EndObject();
-    return Status::OK();
-  }
-
-  Status VisitTablet(const std::string& table_id,
-                     const std::string& tablet_id,
-                     const SysTabletsEntryPB& metadata) OVERRIDE {
-    if (metadata.state() != SysTabletsEntryPB::RUNNING) {
-      return Status::OK();
-    }
-
-    jw_->StartObject();
-    jw_->String("table_id");
-    jw_->String(table_id);
-
-    jw_->String("tablet_id");
-    jw_->String(tablet_id);
-
-    jw_->String("state");
-    jw_->String(SysTabletsEntryPB::State_Name(metadata.state()));
-
-    // Dump replica UUIDs
-    if (metadata.has_consensus_state()) {
-      const consensus::ConsensusStatePB& cs = metadata.consensus_state();
-      jw_->String("replicas");
-      jw_->StartArray();
-      for (const RaftPeerPB& peer : cs.committed_config().peers()) {
-        jw_->StartObject();
-        jw_->String("type");
-        jw_->String(RaftPeerPB::MemberType_Name(peer.member_type()));
-
-        jw_->String("server_uuid");
-        jw_->String(peer.permanent_uuid());
-
-        jw_->String("addr");
-        jw_->String(Substitute("$0:$1", peer.last_known_addr().host(),
-                               peer.last_known_addr().port()));
-
-        jw_->EndObject();
-      }
-      jw_->EndArray();
-
-      if (!cs.leader_uuid().empty()) {
-        jw_->String("leader");
-        jw_->String(cs.leader_uuid());
-      }
-    }
-
-    jw_->EndObject();
-    return Status::OK();
-  }
-
- private:
-  JsonWriter* jw_;
-};
-
-void JsonError(const Status& s, ostringstream* out) {
-  out->str("");
-  JsonWriter jw(out, JsonWriter::COMPACT);
-  jw.StartObject();
-  jw.String("error");
-  jw.String(s.ToString());
-  jw.EndObject();
-}
-} // anonymous namespace
-
-void MasterPathHandlers::HandleDumpEntities(const Webserver::WebRequest& /*req*/,
-                                            Webserver::PrerenderedWebResponse* resp) {
-  ostringstream* output = resp->output;
-  Status s = master_->catalog_manager()->CheckOnline();
-  if (!s.ok()) {
-    JsonError(s, output);
-    return;
-  }
-  JsonWriter jw(output, JsonWriter::COMPACT);
-  JsonDumper d(&jw);
-
-  jw.StartObject();
-
-  jw.String("tables");
-  jw.StartArray();
-  s = master_->catalog_manager()->sys_catalog()->VisitTables(&d);
-  if (!s.ok()) {
-    JsonError(s, output);
-    return;
-  }
-  jw.EndArray();
-
-  jw.String("tablets");
-  jw.StartArray();
-  s = master_->catalog_manager()->sys_catalog()->VisitTablets(&d);
-  if (!s.ok()) {
-    JsonError(s, output);
-    return;
-  }
-  jw.EndArray();
-
-  jw.String("tablet_servers");
-  jw.StartArray();
-  vector<std::shared_ptr<TSDescriptor> > descs;
-  master_->ts_manager()->GetAllDescriptors(&descs);
-  for (const std::shared_ptr<TSDescriptor>& desc : descs) {
-    jw.StartObject();
-
-    jw.String("uuid");
-    jw.String(desc->permanent_uuid());
-
-    ServerRegistrationPB reg;
-    desc->GetRegistration(&reg);
-
-    jw.String("rpc_addrs");
-    jw.StartArray();
-    for (const HostPortPB& host_port : reg.rpc_addresses()) {
-      jw.String(Substitute("$0:$1", host_port.host(), host_port.port()));
-    }
-    jw.EndArray();
-
-    jw.String("http_addrs");
-    jw.StartArray();
-    for (const HostPortPB& host_port : reg.http_addresses()) {
-      jw.String(Substitute("$0://$1:$2",
-                           reg.https_enabled() ? "https" : "http",
-                           host_port.host(), host_port.port()));
-    }
-    jw.EndArray();
-
-    jw.String("live");
-    jw.Bool(!desc->PresumedDead());
-
-    jw.String("millis_since_heartbeat");
-    jw.Int64(desc->TimeSinceHeartbeat().ToMilliseconds());
-
-    jw.String("version");
-    jw.String(reg.software_version());
-
-    jw.EndObject();
-  }
-  jw.EndArray();
-
-  jw.EndObject();
-}
-
-Status MasterPathHandlers::Register(Webserver* server) {
-  bool is_styled = true;
-  bool is_on_nav_bar = true;
-  server->RegisterPathHandler(
-      "/tablet-servers", "Tablet Servers",
-      boost::bind(&MasterPathHandlers::HandleTabletServers, this, _1, _2),
-      is_styled, is_on_nav_bar);
-  server->RegisterPathHandler(
-      "/tables", "Tables",
-      boost::bind(&MasterPathHandlers::HandleCatalogManager, this, _1, _2),
-      is_styled, is_on_nav_bar);
-  server->RegisterPathHandler(
-      "/table", "",
-      boost::bind(&MasterPathHandlers::HandleTablePage, this, _1, _2),
-      is_styled, false);
-  server->RegisterPathHandler(
-      "/masters", "Masters",
-      boost::bind(&MasterPathHandlers::HandleMasters, this, _1, _2),
-      is_styled, is_on_nav_bar);
-  server->RegisterPrerenderedPathHandler(
-      "/dump-entities", "Dump Entities",
-      boost::bind(&MasterPathHandlers::HandleDumpEntities, this, _1, _2),
-      false, false);
-  return Status::OK();
-}
-
-pair<string, string> MasterPathHandlers::TSDescToLinkPair(const TSDescriptor& desc,
-                                                          const string& tablet_id) const {
-  ServerRegistrationPB reg;
-  desc.GetRegistration(&reg);
-  if (reg.http_addresses().empty()) {
-    return std::make_pair(desc.permanent_uuid(), "");
-  }
-  string text = Substitute("$0:$1", reg.http_addresses(0).host(), reg.http_addresses(0).port());
-  string target = Substitute("$0://$1:$2/tablet?id=$3",
-                             reg.https_enabled() ? "https" : "http",
-                             reg.http_addresses(0).host(),
-                             reg.http_addresses(0).port(),
-                             tablet_id);
-  return std::make_pair(std::move(text), std::move(target));
-}
-
-string MasterPathHandlers::MasterAddrsToCsv() const {
-  if (master_->opts().IsDistributed()) {
-    vector<string> all_addresses;
-    all_addresses.reserve(master_->opts().master_addresses.size());
-    for (const HostPort& hp : master_->opts().master_addresses) {
-      all_addresses.push_back(hp.ToString());
-    }
-    return JoinElements(all_addresses, ",");
-  }
-  Sockaddr addr = master_->first_rpc_address();
-  HostPort hp;
-  Status s = HostPortFromSockaddrReplaceWildcard(addr, &hp);
-  if (s.ok()) {
-    return hp.ToString();
-  }
-  LOG(WARNING) << "Unable to determine proper local hostname: " << s.ToString();
-  return addr.ToString();
-}
-
-Status MasterPathHandlers::GetLeaderMasterHttpAddr(string* leader_http_addr) const {
-  vector<ServerEntryPB> masters;
-  RETURN_NOT_OK_PREPEND(master_->ListMasters(&masters), "unable to list masters");
-  for (const auto& master : masters) {
-    if (master.has_error()) {
-      continue;
-    }
-    if (master.role() != RaftPeerPB::LEADER) {
-      continue;
-    }
-    const ServerRegistrationPB& reg = master.registration();
-    if (reg.http_addresses().empty()) {
-      return Status::NotFound("leader master has no http address");
-    }
-    *leader_http_addr = Substitute("$0://$1:$2",
-                                   reg.https_enabled() ? "https" : "http",
-                                   reg.http_addresses(0).host(),
-                                   reg.http_addresses(0).port());
-    return Status::OK();
-  }
-  return Status::NotFound("no leader master known to this master");
-}
-
-void MasterPathHandlers::SetupLeaderMasterRedirect(const string& path,
-                                                   int redirects,
-                                                   EasyJson* output) const {
-  // Allow 3 redirects.
-  const int max_redirects = 3;
-  (*output)["error"] = "Master is not the leader.";
-  if (redirects >= max_redirects) {
-    (*output)["redirect_error"] = "Too many redirects attempting to find the leader master.";
-    return;
-  }
-  string leader_http_addr;
-  Status s = GetLeaderMasterHttpAddr(&leader_http_addr);
-  if (!s.ok()) {
-    (*output)["redirect_error"] = Substitute("Unable to redirect to leader master: $0",
-                                             s.ToString());
-    return;
-  }
-  (*output)["leader_redirect"] = Substitute("$0/$1&redirects=$2",
-                                            leader_http_addr, path, redirects + 1);
-}
-
-} // namespace master
-} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/master/master-path-handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/master/master-path-handlers.h b/src/kudu/master/master-path-handlers.h
deleted file mode 100644
index 186e6d3..0000000
--- a/src/kudu/master/master-path-handlers.h
+++ /dev/null
@@ -1,82 +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.
-#ifndef KUDU_MASTER_MASTER_PATH_HANDLERS_H
-#define KUDU_MASTER_MASTER_PATH_HANDLERS_H
-
-#include <string>
-#include <utility>
-
-#include "kudu/gutil/macros.h"
-#include "kudu/server/webserver.h"
-#include "kudu/util/status.h"
-
-namespace kudu {
-class EasyJson;
-
-namespace master {
-
-class Master;
-class TSDescriptor;
-
-// Web page support for the master.
-class MasterPathHandlers {
- public:
-  explicit MasterPathHandlers(Master* master)
-    : master_(master) {
-  }
-
-  ~MasterPathHandlers();
-
-  Status Register(Webserver* server);
-
- private:
-  void HandleTabletServers(const Webserver::WebRequest& req,
-                           Webserver::WebResponse* resp);
-  void HandleCatalogManager(const Webserver::WebRequest& req,
-                            Webserver::WebResponse* resp);
-  void HandleTablePage(const Webserver::WebRequest& req,
-                       Webserver::WebResponse* resp);
-  void HandleMasters(const Webserver::WebRequest& req,
-                     Webserver::WebResponse* resp);
-  void HandleDumpEntities(const Webserver::WebRequest& req,
-                          Webserver::PrerenderedWebResponse* resp);
-
-  // Returns a pair (text, target) given a tserver's TSDescriptor and a tablet id.
-  // - text is the http host and port for the tserver, if available, or the tserver's uuid.
-  // - target is a url to the tablet page for the tablet on the tserver's webui,
-  //   or an empty string if no http address is available for the tserver.
-  std::pair<std::string, std::string> TSDescToLinkPair(const TSDescriptor& desc,
-                                                       const std::string& tablet_id) const;
-
-  // Return a CSV of master addresses suitable for display.
-  std::string MasterAddrsToCsv() const;
-
-  // If a leader master is known and has an http address, place it in leader_http_addr.
-  Status GetLeaderMasterHttpAddr(std::string* leader_http_addr) const;
-
-  // Adds the necessary properties to 'output' to set up a redirect to the leader master, or
-  // provide an error message if no redirect is possible.
-  // The redirect will link to <master web UI url>/path&redirects=(redirects + 1).
-  void SetupLeaderMasterRedirect(const std::string& path, int redirects, EasyJson* output) const;
-
-  Master* master_;
-  DISALLOW_COPY_AND_ASSIGN(MasterPathHandlers);
-};
-
-} // namespace master
-} // namespace kudu
-#endif /* KUDU_MASTER_MASTER_PATH_HANDLERS_H */

http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/master/master.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/master.cc b/src/kudu/master/master.cc
index 617977f..4a88007 100644
--- a/src/kudu/master/master.cc
+++ b/src/kudu/master/master.cc
@@ -35,10 +35,10 @@
 #include "kudu/gutil/move.h"
 #include "kudu/gutil/strings/substitute.h"
 #include "kudu/master/catalog_manager.h"
-#include "kudu/master/master-path-handlers.h"
 #include "kudu/master/master.pb.h"
 #include "kudu/master/master.proxy.h"
 #include "kudu/master/master_cert_authority.h"
+#include "kudu/master/master_path_handlers.h"
 #include "kudu/master/master_service.h"
 #include "kudu/master/ts_manager.h"
 #include "kudu/rpc/messenger.h"

http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/master/master_path_handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/master_path_handlers.cc b/src/kudu/master/master_path_handlers.cc
new file mode 100644
index 0000000..d06ddd1
--- /dev/null
+++ b/src/kudu/master/master_path_handlers.cc
@@ -0,0 +1,698 @@
+// 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 "kudu/master/master_path_handlers.h"
+
+#include <algorithm>
+#include <array>
+#include <cstdint>
+#include <iosfwd>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <boost/bind.hpp> // IWYU pragma: keep
+#include <glog/logging.h>
+
+#include "kudu/common/common.pb.h"
+#include "kudu/common/partition.h"
+#include "kudu/common/schema.h"
+#include "kudu/common/wire_protocol.h"
+#include "kudu/common/wire_protocol.pb.h"
+#include "kudu/consensus/metadata.pb.h"
+#include "kudu/consensus/quorum_util.h"
+#include "kudu/gutil/map-util.h"
+#include "kudu/gutil/port.h"
+#include "kudu/gutil/ref_counted.h"
+#include "kudu/gutil/stringprintf.h"
+#include "kudu/gutil/strings/join.h"
+#include "kudu/gutil/strings/numbers.h"
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/master/catalog_manager.h"
+#include "kudu/master/master.h"
+#include "kudu/master/master.pb.h"
+#include "kudu/master/master_options.h"
+#include "kudu/master/sys_catalog.h"
+#include "kudu/master/ts_descriptor.h"
+#include "kudu/master/ts_manager.h"
+#include "kudu/server/monitored_task.h"
+#include "kudu/server/webui_util.h"
+#include "kudu/util/cow_object.h"
+#include "kudu/util/easy_json.h"
+#include "kudu/util/jsonwriter.h"
+#include "kudu/util/monotime.h"
+#include "kudu/util/net/net_util.h"
+#include "kudu/util/net/sockaddr.h"
+#include "kudu/util/pb_util.h"
+#include "kudu/util/string_case.h"
+#include "kudu/util/url-coding.h"
+#include "kudu/util/web_callback_registry.h"
+
+namespace kudu {
+
+using consensus::ConsensusStatePB;
+using consensus::RaftPeerPB;
+using std::array;
+using std::map;
+using std::ostringstream;
+using std::pair;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+using strings::Substitute;
+
+namespace master {
+
+MasterPathHandlers::~MasterPathHandlers() {
+}
+
+void MasterPathHandlers::HandleTabletServers(const Webserver::WebRequest& /*req*/,
+                                             Webserver::WebResponse* resp) {
+  EasyJson* output = resp->output;
+  vector<std::shared_ptr<TSDescriptor>> descs;
+  master_->ts_manager()->GetAllDescriptors(&descs);
+
+  (*output)["num_ts"] = std::to_string(descs.size());
+
+  // In mustache, when conditionally rendering a section of the template based
+  // on a key, the value becomes the context. In the subcontext, searches for
+  // keys used to display a value (e.g. {{my_key}}) recurse into the parent
+  // context-- but not when used to define a subsection (e.g. {{#my_list}}.
+  // Thus, we use a negative test {{^has_no_live_ts}} to decide if we
+  // should render the live and dead tablet server tables.
+  bool has_no_live_ts = true;
+  bool has_no_dead_ts = true;
+  output->Set("live_tservers", EasyJson::kArray);
+  output->Set("dead_tservers", EasyJson::kArray);
+  map<string, array<int, 2>> version_counts;
+  for (const std::shared_ptr<TSDescriptor>& desc : descs) {
+    string ts_key = desc->PresumedDead() ? "dead_tservers" : "live_tservers";
+    EasyJson ts_json = (*output)[ts_key].PushBack(EasyJson::kObject);
+
+    ServerRegistrationPB reg;
+    desc->GetRegistration(&reg);
+    ts_json["uuid"] = desc->permanent_uuid();
+    if (!reg.http_addresses().empty()) {
+      ts_json["target"] = Substitute("$0://$1:$2/",
+                                     reg.https_enabled() ? "https" : "http",
+                                     reg.http_addresses(0).host(),
+                                     reg.http_addresses(0).port());
+    }
+    ts_json["time_since_hb"] = StringPrintf("%.1fs", desc->TimeSinceHeartbeat().ToSeconds());
+    ts_json["registration"] = pb_util::SecureShortDebugString(reg);
+    version_counts[reg.software_version()][desc->PresumedDead() ? 1 : 0]++;
+    has_no_live_ts &= desc->PresumedDead();
+    has_no_dead_ts &= !desc->PresumedDead();
+  }
+  (*output)["has_no_live_ts"] = has_no_live_ts;
+  (*output)["has_no_dead_ts"] = has_no_dead_ts;
+
+  output->Set("version_counts", EasyJson::kArray);
+  for (const auto& entry : version_counts) {
+    EasyJson version_count_json = (*output)["version_counts"].PushBack(EasyJson::kObject);
+    version_count_json["version"] = entry.first;
+    version_count_json["live"] = Substitute("$0", entry.second[0]);
+    version_count_json["dead"] = Substitute("$0", entry.second[1]);
+  }
+}
+
+namespace {
+
+// Extracts the value of the 'redirects' parameter from 'req'; returns 0 if the
+// parameter doesn't exist or couldn't be parsed.
+int ExtractRedirectsFromRequest(const Webserver::WebRequest& req) {
+  string redirects_str;
+  int redirects = 0;
+  if (FindCopy(req.parsed_args, "redirects", &redirects_str)) {
+    if (!safe_strto32(redirects_str, &redirects)) {
+      return 0;
+    }
+  }
+  return redirects;
+}
+
+} // anonymous namespace
+
+void MasterPathHandlers::HandleCatalogManager(const Webserver::WebRequest& req,
+                                              Webserver::WebResponse* resp) {
+  EasyJson* output = resp->output;
+  CatalogManager::ScopedLeaderSharedLock l(master_->catalog_manager());
+  if (!l.catalog_status().ok()) {
+    (*output)["error"] = Substitute("Master is not ready: $0",  l.catalog_status().ToString());
+    return;
+  }
+  if (!l.leader_status().ok()) {
+    // Track redirects to prevent a redirect loop.
+    int redirects = ExtractRedirectsFromRequest(req);
+    SetupLeaderMasterRedirect("tables?", redirects, output);
+    return;
+  }
+
+  std::vector<scoped_refptr<TableInfo>> tables;
+  master_->catalog_manager()->GetAllTables(&tables);
+  int num_running_tables = 0;
+  EasyJson tables_json = output->Set("tables", EasyJson::kArray);
+  for (const scoped_refptr<TableInfo>& table : tables) {
+    TableMetadataLock l(table.get(), LockMode::READ);
+    if (!l.data().is_running()) {
+      continue;
+    }
+    num_running_tables++; // Table count excluding deleted ones
+    string state = SysTablesEntryPB_State_Name(l.data().pb.state());
+    Capitalize(&state);
+    EasyJson table_json = tables_json.PushBack(EasyJson::kObject);
+    table_json["name"] = EscapeForHtmlToString(l.data().name());
+    table_json["id"] = EscapeForHtmlToString(table->id());
+    table_json["state"] = state;
+    table_json["message"] = EscapeForHtmlToString(l.data().pb.state_msg());
+  }
+  (*output).Set<int64_t>("num_tables", num_running_tables);
+}
+
+namespace {
+
+
+// Holds info about a peer for use in the tablet detail table.
+struct TabletDetailPeerInfo {
+  string text;
+  string target;
+  string role;
+  bool is_leader;
+};
+
+int RoleToSortIndex(RaftPeerPB::Role r) {
+  switch (r) {
+    case RaftPeerPB::LEADER: return 0;
+    default: return 1 + static_cast<int>(r);
+  }
+}
+
+bool CompareByRole(const pair<TabletDetailPeerInfo, RaftPeerPB::Role>& a,
+                   const pair<TabletDetailPeerInfo, RaftPeerPB::Role>& b) {
+  return RoleToSortIndex(a.second) < RoleToSortIndex(b.second);
+}
+
+} // anonymous namespace
+
+
+void MasterPathHandlers::HandleTablePage(const Webserver::WebRequest& req,
+                                         Webserver::WebResponse* resp) {
+  EasyJson* output = resp->output;
+  // Parse argument.
+  string table_id;
+  if (!FindCopy(req.parsed_args, "id", &table_id)) {
+    resp->status_code = HttpStatusCode::BadRequest;
+    (*output)["error"] = "Missing 'id' argument";
+    return;
+  }
+
+  CatalogManager::ScopedLeaderSharedLock l(master_->catalog_manager());
+  if (!l.catalog_status().ok()) {
+    (*output)["error"] = Substitute("Master is not ready: $0", l.catalog_status().ToString());
+    return;
+  }
+  if (!l.leader_status().ok()) {
+    // It's possible to respond 307 Temporary Redirect and automatically redirect with
+    // a Location header, but this would likely confuse users about which master's web ui
+    // they are looking at. Instead, we show a link users can click to go to the leader master.
+    // We track redirects to prevent a redirect loop.
+    int redirects = ExtractRedirectsFromRequest(req);
+    SetupLeaderMasterRedirect(Substitute("table?id=$0", table_id), redirects, output);
+    return;
+  }
+
+  scoped_refptr<TableInfo> table;
+  Status s = master_->catalog_manager()->GetTableInfo(table_id, &table);
+  if (!s.ok()) {
+    resp->status_code = HttpStatusCode::ServiceUnavailable;
+    (*output)["error"] = Substitute("Master is not ready: $0", s.ToString());
+    return;
+  }
+
+  if (!table) {
+    resp->status_code = HttpStatusCode::NotFound;
+    (*output)["error"] = "Table not found";
+    return;
+  }
+
+  Schema schema;
+  PartitionSchema partition_schema;
+  vector<scoped_refptr<TabletInfo>> tablets;
+  {
+    TableMetadataLock l(table.get(), LockMode::READ);
+    (*output)["name"] = l.data().name();
+    (*output)["id"] = table_id;
+    (*output)["version"] = l.data().pb.version();
+
+    string state = SysTablesEntryPB_State_Name(l.data().pb.state());
+    Capitalize(&state);
+    (*output)["state"] = state;
+    string state_msg = l.data().pb.state_msg();
+    if (!state_msg.empty()) {
+      (*output)["state_msg"] = state_msg;
+    }
+
+    s = SchemaFromPB(l.data().pb.schema(), &schema);
+    if (!s.ok()) {
+      (*output)["error"] = Substitute("Unable to decode schema: $0", s.ToString());
+      return;
+    }
+    s = PartitionSchema::FromPB(l.data().pb.partition_schema(), schema, &partition_schema);
+    if (!s.ok()) {
+      (*output)["error"] =
+          Substitute("Unable to decode partition schema: $0", s.ToString());
+      return;
+    }
+    table->GetAllTablets(&tablets);
+  }
+
+  SchemaToJson(schema, output);
+
+  // We have to collate partition schema and tablet information in order to set
+  // up the partition schema, tablet summary, and tablet detail tables.
+  std::vector<string> range_partitions;
+  map<string, int> summary_states;
+  (*output)["detail_partition_schema_header"] = partition_schema.PartitionTableHeader(schema);
+  EasyJson tablets_detail_json = output->Set("tablets_detail", EasyJson::kArray);
+  for (const scoped_refptr<TabletInfo>& tablet : tablets) {
+    vector<pair<TabletDetailPeerInfo, RaftPeerPB::Role>> sorted_replicas;
+    TabletMetadataLock l(tablet.get(), LockMode::READ);
+
+    // Count states for tablet summary.
+    summary_states[SysTabletsEntryPB_State_Name(l.data().pb.state())]++;
+
+    // Collect details about each tablet replica.
+    if (l.data().pb.has_consensus_state()) {
+      const ConsensusStatePB& cstate = l.data().pb.consensus_state();
+      for (const auto& peer : cstate.committed_config().peers()) {
+        TabletDetailPeerInfo peer_info;
+        shared_ptr<TSDescriptor> ts_desc;
+        if (master_->ts_manager()->LookupTSByUUID(peer.permanent_uuid(), &ts_desc)) {
+          auto link_pair = TSDescToLinkPair(*ts_desc.get(), tablet->id());
+          peer_info.text = std::move(link_pair.first);
+          peer_info.target = std::move(link_pair.second);
+        } else {
+          peer_info.text = peer.permanent_uuid();
+        }
+        RaftPeerPB::Role role = GetConsensusRole(peer.permanent_uuid(), cstate);
+        peer_info.role = RaftPeerPB_Role_Name(role);
+        peer_info.is_leader = role == RaftPeerPB::LEADER;
+        sorted_replicas.emplace_back(std::make_pair(peer_info, role));
+      }
+    }
+    std::sort(sorted_replicas.begin(), sorted_replicas.end(), &CompareByRole);
+
+    // Generate a readable description of the partition of each tablet, used
+    // both for each tablet's details and the readable range partition schema.
+    Partition partition;
+    Partition::FromPB(l.data().pb.partition(), &partition);
+
+    // For each unique range partition, add a debug string to range_partitions.
+    // To ensure uniqueness, only use partitions whose hash buckets are all 0.
+    if (std::all_of(partition.hash_buckets().begin(),
+                    partition.hash_buckets().end(),
+                    [] (const int32_t& bucket) { return bucket == 0; })) {
+      range_partitions.emplace_back(
+          partition_schema.RangePartitionDebugString(partition.range_key_start(),
+                                                     partition.range_key_end(),
+                                                     schema));
+    }
+
+    // Combine the tablet details and partition info for each tablet.
+    EasyJson tablet_detail_json = tablets_detail_json.PushBack(EasyJson::kObject);
+    tablet_detail_json["id"] = tablet->id();
+    tablet_detail_json["partition_cols"] = partition_schema.PartitionTableEntry(schema, partition);
+    string state = SysTabletsEntryPB_State_Name(l.data().pb.state());
+    Capitalize(&state);
+    tablet_detail_json["state"] = state;
+    tablet_detail_json["state_msg"] = l.data().pb.state_msg();
+    EasyJson peers_json = tablet_detail_json.Set("peers", EasyJson::kArray);
+    for (const auto& e : sorted_replicas) {
+      EasyJson peer_json = peers_json.PushBack(EasyJson::kObject);
+      peer_json["text"] = e.first.text;
+      if (!e.first.target.empty()) {
+        peer_json["target"] = e.first.target;
+      }
+      peer_json["role"] = e.first.role;
+      peer_json["is_leader"] = e.first.is_leader;
+    }
+  }
+
+  (*output)["partition_schema"] = partition_schema.DisplayString(schema, range_partitions);
+
+  EasyJson summary_json = output->Set("tablets_summary", EasyJson::kArray);
+  for (const auto& entry : summary_states) {
+    EasyJson state_json = summary_json.PushBack(EasyJson::kObject);
+    state_json["state"] = entry.first;
+    state_json["count"] = entry.second;
+    double percentage = (100.0 * entry.second) / tablets.size();
+    state_json["percentage"] = tablets.empty() ? "0.0" : StringPrintf("%.2f", percentage);
+  }
+
+  // Used to make the Impala CREATE TABLE statement.
+  (*output)["master_addresses"] = MasterAddrsToCsv();
+
+  std::vector<scoped_refptr<MonitoredTask>> task_list;
+  table->GetTaskList(&task_list);
+  TaskListToJson(task_list, output);
+}
+
+void MasterPathHandlers::HandleMasters(const Webserver::WebRequest& /*req*/,
+                                       Webserver::WebResponse* resp) {
+  EasyJson* output = resp->output;
+  vector<ServerEntryPB> masters;
+  Status s = master_->ListMasters(&masters);
+  if (!s.ok()) {
+    string msg = s.CloneAndPrepend("Unable to list Masters").ToString();
+    LOG(WARNING) << msg;
+    (*output)["error"] = msg;
+    return;
+  }
+  output->Set("masters", EasyJson::kArray);
+  for (const ServerEntryPB& master : masters) {
+    EasyJson master_json = (*output)["masters"].PushBack(EasyJson::kObject);
+    if (master.has_error()) {
+      Status error = StatusFromPB(master.error());
+      master_json["error"] = error.ToString();
+      continue;
+    }
+    const ServerRegistrationPB& reg = master.registration();
+    master_json["uuid"] = master.instance_id().permanent_uuid();
+    if (!reg.http_addresses().empty()) {
+      master_json["target"] = Substitute("$0://$1:$2/",
+                                         reg.https_enabled() ? "https" : "http",
+                                         reg.http_addresses(0).host(),
+                                         reg.http_addresses(0).port());
+    }
+    master_json["role"] = master.has_role() ? RaftPeerPB_Role_Name(master.role()) : "N/A";
+    master_json["registration"] = pb_util::SecureShortDebugString(master.registration());
+  }
+}
+
+namespace {
+
+// Visitor for the catalog table which dumps tables and tablets in a JSON format. This
+// dump is interpreted by the CM agent in order to track time series entities in the SMON
+// database.
+//
+// This implementation relies on scanning the catalog table directly instead of using the
+// catalog manager APIs. This allows it to work even on a non-leader master, and avoids
+// any requirement for locking. For the purposes of metrics entity gathering, it's OK to
+// serve a slightly stale snapshot.
+//
+// It is tempting to directly dump the metadata protobufs using JsonWriter::Protobuf(...),
+// but then we would be tying ourselves to textual compatibility of the PB field names in
+// our catalog table. Instead, the implementation specifically dumps the fields that we
+// care about.
+//
+// This should be considered a "stable" protocol -- do not rename, remove, or restructure
+// without consulting with the CM team.
+class JsonDumper : public TableVisitor, public TabletVisitor {
+ public:
+  explicit JsonDumper(JsonWriter* jw) : jw_(jw) {
+  }
+
+  Status VisitTable(const std::string& table_id,
+                    const SysTablesEntryPB& metadata) OVERRIDE {
+    if (metadata.state() != SysTablesEntryPB::RUNNING) {
+      return Status::OK();
+    }
+
+    jw_->StartObject();
+    jw_->String("table_id");
+    jw_->String(table_id);
+
+    jw_->String("table_name");
+    jw_->String(metadata.name());
+
+    jw_->String("state");
+    jw_->String(SysTablesEntryPB::State_Name(metadata.state()));
+
+    jw_->EndObject();
+    return Status::OK();
+  }
+
+  Status VisitTablet(const std::string& table_id,
+                     const std::string& tablet_id,
+                     const SysTabletsEntryPB& metadata) OVERRIDE {
+    if (metadata.state() != SysTabletsEntryPB::RUNNING) {
+      return Status::OK();
+    }
+
+    jw_->StartObject();
+    jw_->String("table_id");
+    jw_->String(table_id);
+
+    jw_->String("tablet_id");
+    jw_->String(tablet_id);
+
+    jw_->String("state");
+    jw_->String(SysTabletsEntryPB::State_Name(metadata.state()));
+
+    // Dump replica UUIDs
+    if (metadata.has_consensus_state()) {
+      const consensus::ConsensusStatePB& cs = metadata.consensus_state();
+      jw_->String("replicas");
+      jw_->StartArray();
+      for (const RaftPeerPB& peer : cs.committed_config().peers()) {
+        jw_->StartObject();
+        jw_->String("type");
+        jw_->String(RaftPeerPB::MemberType_Name(peer.member_type()));
+
+        jw_->String("server_uuid");
+        jw_->String(peer.permanent_uuid());
+
+        jw_->String("addr");
+        jw_->String(Substitute("$0:$1", peer.last_known_addr().host(),
+                               peer.last_known_addr().port()));
+
+        jw_->EndObject();
+      }
+      jw_->EndArray();
+
+      if (!cs.leader_uuid().empty()) {
+        jw_->String("leader");
+        jw_->String(cs.leader_uuid());
+      }
+    }
+
+    jw_->EndObject();
+    return Status::OK();
+  }
+
+ private:
+  JsonWriter* jw_;
+};
+
+void JsonError(const Status& s, ostringstream* out) {
+  out->str("");
+  JsonWriter jw(out, JsonWriter::COMPACT);
+  jw.StartObject();
+  jw.String("error");
+  jw.String(s.ToString());
+  jw.EndObject();
+}
+} // anonymous namespace
+
+void MasterPathHandlers::HandleDumpEntities(const Webserver::WebRequest& /*req*/,
+                                            Webserver::PrerenderedWebResponse* resp) {
+  ostringstream* output = resp->output;
+  Status s = master_->catalog_manager()->CheckOnline();
+  if (!s.ok()) {
+    JsonError(s, output);
+    return;
+  }
+  JsonWriter jw(output, JsonWriter::COMPACT);
+  JsonDumper d(&jw);
+
+  jw.StartObject();
+
+  jw.String("tables");
+  jw.StartArray();
+  s = master_->catalog_manager()->sys_catalog()->VisitTables(&d);
+  if (!s.ok()) {
+    JsonError(s, output);
+    return;
+  }
+  jw.EndArray();
+
+  jw.String("tablets");
+  jw.StartArray();
+  s = master_->catalog_manager()->sys_catalog()->VisitTablets(&d);
+  if (!s.ok()) {
+    JsonError(s, output);
+    return;
+  }
+  jw.EndArray();
+
+  jw.String("tablet_servers");
+  jw.StartArray();
+  vector<std::shared_ptr<TSDescriptor> > descs;
+  master_->ts_manager()->GetAllDescriptors(&descs);
+  for (const std::shared_ptr<TSDescriptor>& desc : descs) {
+    jw.StartObject();
+
+    jw.String("uuid");
+    jw.String(desc->permanent_uuid());
+
+    ServerRegistrationPB reg;
+    desc->GetRegistration(&reg);
+
+    jw.String("rpc_addrs");
+    jw.StartArray();
+    for (const HostPortPB& host_port : reg.rpc_addresses()) {
+      jw.String(Substitute("$0:$1", host_port.host(), host_port.port()));
+    }
+    jw.EndArray();
+
+    jw.String("http_addrs");
+    jw.StartArray();
+    for (const HostPortPB& host_port : reg.http_addresses()) {
+      jw.String(Substitute("$0://$1:$2",
+                           reg.https_enabled() ? "https" : "http",
+                           host_port.host(), host_port.port()));
+    }
+    jw.EndArray();
+
+    jw.String("live");
+    jw.Bool(!desc->PresumedDead());
+
+    jw.String("millis_since_heartbeat");
+    jw.Int64(desc->TimeSinceHeartbeat().ToMilliseconds());
+
+    jw.String("version");
+    jw.String(reg.software_version());
+
+    jw.EndObject();
+  }
+  jw.EndArray();
+
+  jw.EndObject();
+}
+
+Status MasterPathHandlers::Register(Webserver* server) {
+  bool is_styled = true;
+  bool is_on_nav_bar = true;
+  server->RegisterPathHandler(
+      "/tablet-servers", "Tablet Servers",
+      boost::bind(&MasterPathHandlers::HandleTabletServers, this, _1, _2),
+      is_styled, is_on_nav_bar);
+  server->RegisterPathHandler(
+      "/tables", "Tables",
+      boost::bind(&MasterPathHandlers::HandleCatalogManager, this, _1, _2),
+      is_styled, is_on_nav_bar);
+  server->RegisterPathHandler(
+      "/table", "",
+      boost::bind(&MasterPathHandlers::HandleTablePage, this, _1, _2),
+      is_styled, false);
+  server->RegisterPathHandler(
+      "/masters", "Masters",
+      boost::bind(&MasterPathHandlers::HandleMasters, this, _1, _2),
+      is_styled, is_on_nav_bar);
+  server->RegisterPrerenderedPathHandler(
+      "/dump-entities", "Dump Entities",
+      boost::bind(&MasterPathHandlers::HandleDumpEntities, this, _1, _2),
+      false, false);
+  return Status::OK();
+}
+
+pair<string, string> MasterPathHandlers::TSDescToLinkPair(const TSDescriptor& desc,
+                                                          const string& tablet_id) const {
+  ServerRegistrationPB reg;
+  desc.GetRegistration(&reg);
+  if (reg.http_addresses().empty()) {
+    return std::make_pair(desc.permanent_uuid(), "");
+  }
+  string text = Substitute("$0:$1", reg.http_addresses(0).host(), reg.http_addresses(0).port());
+  string target = Substitute("$0://$1:$2/tablet?id=$3",
+                             reg.https_enabled() ? "https" : "http",
+                             reg.http_addresses(0).host(),
+                             reg.http_addresses(0).port(),
+                             tablet_id);
+  return std::make_pair(std::move(text), std::move(target));
+}
+
+string MasterPathHandlers::MasterAddrsToCsv() const {
+  if (master_->opts().IsDistributed()) {
+    vector<string> all_addresses;
+    all_addresses.reserve(master_->opts().master_addresses.size());
+    for (const HostPort& hp : master_->opts().master_addresses) {
+      all_addresses.push_back(hp.ToString());
+    }
+    return JoinElements(all_addresses, ",");
+  }
+  Sockaddr addr = master_->first_rpc_address();
+  HostPort hp;
+  Status s = HostPortFromSockaddrReplaceWildcard(addr, &hp);
+  if (s.ok()) {
+    return hp.ToString();
+  }
+  LOG(WARNING) << "Unable to determine proper local hostname: " << s.ToString();
+  return addr.ToString();
+}
+
+Status MasterPathHandlers::GetLeaderMasterHttpAddr(string* leader_http_addr) const {
+  vector<ServerEntryPB> masters;
+  RETURN_NOT_OK_PREPEND(master_->ListMasters(&masters), "unable to list masters");
+  for (const auto& master : masters) {
+    if (master.has_error()) {
+      continue;
+    }
+    if (master.role() != RaftPeerPB::LEADER) {
+      continue;
+    }
+    const ServerRegistrationPB& reg = master.registration();
+    if (reg.http_addresses().empty()) {
+      return Status::NotFound("leader master has no http address");
+    }
+    *leader_http_addr = Substitute("$0://$1:$2",
+                                   reg.https_enabled() ? "https" : "http",
+                                   reg.http_addresses(0).host(),
+                                   reg.http_addresses(0).port());
+    return Status::OK();
+  }
+  return Status::NotFound("no leader master known to this master");
+}
+
+void MasterPathHandlers::SetupLeaderMasterRedirect(const string& path,
+                                                   int redirects,
+                                                   EasyJson* output) const {
+  // Allow 3 redirects.
+  const int max_redirects = 3;
+  (*output)["error"] = "Master is not the leader.";
+  if (redirects >= max_redirects) {
+    (*output)["redirect_error"] = "Too many redirects attempting to find the leader master.";
+    return;
+  }
+  string leader_http_addr;
+  Status s = GetLeaderMasterHttpAddr(&leader_http_addr);
+  if (!s.ok()) {
+    (*output)["redirect_error"] = Substitute("Unable to redirect to leader master: $0",
+                                             s.ToString());
+    return;
+  }
+  (*output)["leader_redirect"] = Substitute("$0/$1&redirects=$2",
+                                            leader_http_addr, path, redirects + 1);
+}
+
+} // namespace master
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/master/master_path_handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/master/master_path_handlers.h b/src/kudu/master/master_path_handlers.h
new file mode 100644
index 0000000..186e6d3
--- /dev/null
+++ b/src/kudu/master/master_path_handlers.h
@@ -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.
+#ifndef KUDU_MASTER_MASTER_PATH_HANDLERS_H
+#define KUDU_MASTER_MASTER_PATH_HANDLERS_H
+
+#include <string>
+#include <utility>
+
+#include "kudu/gutil/macros.h"
+#include "kudu/server/webserver.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+class EasyJson;
+
+namespace master {
+
+class Master;
+class TSDescriptor;
+
+// Web page support for the master.
+class MasterPathHandlers {
+ public:
+  explicit MasterPathHandlers(Master* master)
+    : master_(master) {
+  }
+
+  ~MasterPathHandlers();
+
+  Status Register(Webserver* server);
+
+ private:
+  void HandleTabletServers(const Webserver::WebRequest& req,
+                           Webserver::WebResponse* resp);
+  void HandleCatalogManager(const Webserver::WebRequest& req,
+                            Webserver::WebResponse* resp);
+  void HandleTablePage(const Webserver::WebRequest& req,
+                       Webserver::WebResponse* resp);
+  void HandleMasters(const Webserver::WebRequest& req,
+                     Webserver::WebResponse* resp);
+  void HandleDumpEntities(const Webserver::WebRequest& req,
+                          Webserver::PrerenderedWebResponse* resp);
+
+  // Returns a pair (text, target) given a tserver's TSDescriptor and a tablet id.
+  // - text is the http host and port for the tserver, if available, or the tserver's uuid.
+  // - target is a url to the tablet page for the tablet on the tserver's webui,
+  //   or an empty string if no http address is available for the tserver.
+  std::pair<std::string, std::string> TSDescToLinkPair(const TSDescriptor& desc,
+                                                       const std::string& tablet_id) const;
+
+  // Return a CSV of master addresses suitable for display.
+  std::string MasterAddrsToCsv() const;
+
+  // If a leader master is known and has an http address, place it in leader_http_addr.
+  Status GetLeaderMasterHttpAddr(std::string* leader_http_addr) const;
+
+  // Adds the necessary properties to 'output' to set up a redirect to the leader master, or
+  // provide an error message if no redirect is possible.
+  // The redirect will link to <master web UI url>/path&redirects=(redirects + 1).
+  void SetupLeaderMasterRedirect(const std::string& path, int redirects, EasyJson* output) const;
+
+  Master* master_;
+  DISALLOW_COPY_AND_ASSIGN(MasterPathHandlers);
+};
+
+} // namespace master
+} // namespace kudu
+#endif /* KUDU_MASTER_MASTER_PATH_HANDLERS_H */

http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/server/CMakeLists.txt b/src/kudu/server/CMakeLists.txt
index 4c0d9ab..eb2a0e0 100644
--- a/src/kudu/server/CMakeLists.txt
+++ b/src/kudu/server/CMakeLists.txt
@@ -39,16 +39,16 @@ target_link_libraries(server_base_proto
 #########################################
 
 set(SERVER_PROCESS_SRCS
-  default-path-handlers.cc
+  default_path_handlers.cc
   generic_service.cc
   glog_metrics.cc
-  pprof-path-handlers.cc
+  pprof_path_handlers.cc
   rpcz-path-handler.cc
   rpc_server.cc
   server_base.cc
   server_base_options.cc
   tcmalloc_metrics.cc
-  tracing-path-handlers.cc
+  tracing_path_handlers.cc
   webserver.cc
   webserver_options.cc
   webui_util.cc

http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/default-path-handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/default-path-handlers.cc b/src/kudu/server/default-path-handlers.cc
deleted file mode 100644
index 8260ec8..0000000
--- a/src/kudu/server/default-path-handlers.cc
+++ /dev/null
@@ -1,318 +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.
-
-#include "kudu/server/default-path-handlers.h"
-
-#include <sys/stat.h>
-
-#include <cstddef>
-#include <cstdint>
-#include <fstream>
-#include <map>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/algorithm/string/predicate.hpp>
-#include <boost/bind.hpp> // IWYU pragma: keep
-#include <gflags/gflags.h>
-#include <gflags/gflags_declare.h>
-#include <glog/logging.h>
-#include <gperftools/malloc_extension.h>
-
-#include "kudu/gutil/macros.h"
-#include "kudu/gutil/map-util.h"
-#include "kudu/gutil/stringprintf.h"
-#include "kudu/gutil/strings/human_readable.h"
-#include "kudu/gutil/strings/numbers.h"
-#include "kudu/gutil/strings/split.h"
-#include "kudu/gutil/strings/substitute.h"
-#include "kudu/server/pprof-path-handlers.h"
-#include "kudu/server/webserver.h"
-#include "kudu/util/easy_json.h"
-#include "kudu/util/flag_tags.h"
-#include "kudu/util/flags.h"
-#include "kudu/util/jsonwriter.h"
-#include "kudu/util/logging.h"
-#include "kudu/util/mem_tracker.h"
-#include "kudu/util/metrics.h"
-#include "kudu/util/process_memory.h"
-#include "kudu/util/status.h"
-#include "kudu/util/web_callback_registry.h"
-
-using std::ifstream;
-using std::string;
-using std::vector;
-using strings::Substitute;
-
-DEFINE_int64(web_log_bytes, 1024 * 1024,
-    "The maximum number of bytes to display on the debug webserver's log page");
-TAG_FLAG(web_log_bytes, advanced);
-TAG_FLAG(web_log_bytes, runtime);
-
-// For configuration dashboard
-DECLARE_string(redact);
-DECLARE_string(rpc_encryption);
-DECLARE_string(rpc_authentication);
-DECLARE_string(webserver_certificate_file);
-
-namespace kudu {
-
-using std::shared_ptr;
-
-namespace {
-// Html/Text formatting tags
-struct Tags {
-  string pre_tag, end_pre_tag, line_break, header, end_header;
-
-  // If as_text is true, set the html tags to a corresponding raw text representation.
-  explicit Tags(bool as_text) {
-    if (as_text) {
-      pre_tag = "";
-      end_pre_tag = "\n";
-      line_break = "\n";
-      header = "";
-      end_header = "";
-    } else {
-      pre_tag = "<pre>";
-      end_pre_tag = "</pre>";
-      line_break = "<br/>";
-      header = "<h2>";
-      end_header = "</h2>";
-    }
-  }
-};
-} // anonymous namespace
-
-// Writes the last FLAGS_web_log_bytes of the INFO logfile to a webpage
-// Note to get best performance, set GLOG_logbuflevel=-1 to prevent log buffering
-static void LogsHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
-  EasyJson* output = resp->output;
-  (*output)["raw"] = (req.parsed_args.find("raw") != req.parsed_args.end());
-  string logfile;
-  GetFullLogFilename(google::INFO, &logfile);
-  (*output)["logfile"] = logfile;
-  struct stat file_stat;
-  if (stat(logfile.c_str(), &file_stat) == 0) {
-    size_t size = file_stat.st_size;
-    size_t seekpos = size < FLAGS_web_log_bytes ? 0L : size - FLAGS_web_log_bytes;
-    ifstream log(logfile.c_str(), std::ios::in);
-    // Note if the file rolls between stat and seek, this could fail
-    // (and we could wind up reading the whole file). But because the
-    // file is likely to be small, this is unlikely to be an issue in
-    // practice.
-    log.seekg(seekpos);
-    (*output)["web_log_bytes"] = FLAGS_web_log_bytes;
-    std::ostringstream ss;
-    ss << log.rdbuf();
-    (*output)["log"] = ss.str();
-  }
-}
-
-// Registered to handle "/flags", and prints out all command-line flags and their HTML
-// escaped values. If --redact is set with 'flag', the values of flags tagged as
-// sensitive will be redacted. The values would not be HTML escaped if in the raw text
-// mode, e.g. "/varz?raw".
-static void FlagsHandler(const Webserver::WebRequest& req,
-                         Webserver::PrerenderedWebResponse* resp) {
-  std::ostringstream* output = resp->output;
-  bool as_text = (req.parsed_args.find("raw") != req.parsed_args.end());
-  Tags tags(as_text);
-
-  (*output) << tags.header << "Command-line Flags" << tags.end_header;
-  (*output) << tags.pre_tag
-            << CommandlineFlagsIntoString(as_text ? EscapeMode::NONE : EscapeMode::HTML)
-            << tags.end_pre_tag;
-}
-
-// Registered to handle "/memz", and prints out memory allocation statistics.
-static void MemUsageHandler(const Webserver::WebRequest& req,
-                            Webserver::PrerenderedWebResponse* resp) {
-  std::ostringstream* output = resp->output;
-  bool as_text = (req.parsed_args.find("raw") != req.parsed_args.end());
-  Tags tags(as_text);
-
-  (*output) << tags.pre_tag;
-#ifndef TCMALLOC_ENABLED
-  (*output) << "Memory tracking is not available unless tcmalloc is enabled.";
-#else
-  faststring buf;
-  buf.resize(32 * 1024);
-  MallocExtension::instance()->GetStats(reinterpret_cast<char*>(buf.data()), buf.size());
-  // Replace new lines with <br> for html
-  string tmp(reinterpret_cast<char*>(buf.data()));
-  boost::replace_all(tmp, "\n", tags.line_break);
-  (*output) << tmp << tags.end_pre_tag;
-#endif
-}
-
-// Registered to handle "/mem-trackers", and prints out to handle memory tracker information.
-static void MemTrackersHandler(const Webserver::WebRequest& /*req*/,
-                               Webserver::PrerenderedWebResponse* resp) {
-  std::ostringstream* output = resp->output;
-  int64_t current_consumption = process_memory::CurrentConsumption();
-  int64_t hard_limit = process_memory::HardLimit();
-  *output << "<h1>Process memory usage</h1>\n";
-  *output << "<table class='table table-striped'>\n";
-  *output << Substitute("  <tr><th>Total consumption</th><td>$0</td></tr>\n",
-                        HumanReadableNumBytes::ToString(current_consumption));
-  *output << Substitute("  <tr><th>Memory limit</th><td>$0</td></tr>\n",
-                        HumanReadableNumBytes::ToString(hard_limit));
-  if (hard_limit > 0) {
-    double percentage = 100 * static_cast<double>(current_consumption) / hard_limit;
-    *output << Substitute("  <tr><th>Percentage consumed</th><td>$0%</td></tr>\n",
-                          StringPrintf("%.2f", percentage));
-  }
-  *output << "</table>\n";
-#ifndef TCMALLOC_ENABLED
-  *output << R"(
-      <div class="alert alert-warning">
-        <strong>NOTE:</strong> This build of Kudu has not enabled tcmalloc.
-        The above process memory stats will be inaccurate.
-      </div>
-               )";
-#endif
-
-  *output << "<h1>Memory usage by subsystem</h1>\n";
-  *output << "<table class='table table-striped'>\n";
-  *output << "  <thead><tr><th>Id</th><th>Parent</th><th>Limit</th><th>Current Consumption</th>"
-      "<th>Peak consumption</th></tr></thead>\n";
-  *output << "<tbody>\n";
-
-  vector<shared_ptr<MemTracker> > trackers;
-  MemTracker::ListTrackers(&trackers);
-  for (const shared_ptr<MemTracker>& tracker : trackers) {
-    string parent = tracker->parent() == nullptr ? "none" : tracker->parent()->id();
-    string limit_str = tracker->limit() == -1 ? "none" :
-                       HumanReadableNumBytes::ToString(tracker->limit());
-    string current_consumption_str = HumanReadableNumBytes::ToString(tracker->consumption());
-    string peak_consumption_str = HumanReadableNumBytes::ToString(tracker->peak_consumption());
-    (*output) << Substitute("  <tr><td>$0</td><td>$1</td><td>$2</td>" // id, parent, limit
-                            "<td>$3</td><td>$4</td></tr>\n", // current, peak
-                            tracker->id(), parent, limit_str, current_consumption_str,
-                            peak_consumption_str);
-  }
-  *output << "</tbody></table>\n";
-}
-
-static void ConfigurationHandler(const Webserver::WebRequest& /* req */,
-                                 Webserver::WebResponse* resp) {
-  EasyJson* output = resp->output;
-  EasyJson security_configs = output->Set("security_configs", EasyJson::kArray);
-
-  EasyJson rpc_encryption = security_configs.PushBack(EasyJson::kObject);
-  rpc_encryption["name"] = "RPC Encryption";
-  rpc_encryption["value"] = FLAGS_rpc_encryption;
-  rpc_encryption["secure"] = boost::iequals(FLAGS_rpc_encryption, "required");
-  rpc_encryption["id"] = "rpc_encryption";
-  rpc_encryption["explanation"] = "Configure with --rpc_encryption. Most secure value is "
-                                  "'required'.";
-
-  EasyJson rpc_authentication = security_configs.PushBack(EasyJson::kObject);
-  rpc_authentication["name"] = "RPC Authentication";
-  rpc_authentication["value"] = FLAGS_rpc_authentication;
-  rpc_authentication["secure"] = boost::iequals(FLAGS_rpc_authentication, "required");
-  rpc_authentication["id"] = "rpc_authentication";
-  rpc_authentication["explanation"] = "Configure with --rpc_authentication. Most secure value is "
-                                      "'required'.";
-
-  EasyJson webserver_encryption = security_configs.PushBack(EasyJson::kObject);
-  webserver_encryption["name"] = "Webserver Encryption";
-  webserver_encryption["value"] = FLAGS_webserver_certificate_file.empty() ? "off" : "on";
-  webserver_encryption["secure"] = !FLAGS_webserver_certificate_file.empty();
-  webserver_encryption["id"] = "webserver_encryption";
-  webserver_encryption["explanation"] = "Configure with --webserver_certificate_file and "
-                                        "webserver_private_key_file.";
-
-  EasyJson webserver_redaction = security_configs.PushBack(EasyJson::kObject);
-  webserver_redaction["name"] = "Webserver Redaction";
-  webserver_redaction["value"] = FLAGS_redact;
-  webserver_redaction["secure"] = boost::iequals(FLAGS_redact, "all");
-  webserver_redaction["id"] = "webserver_redaction";
-  webserver_redaction["explanation"] = "Configure with --redact. Most secure value is 'all'.";
-}
-
-void AddDefaultPathHandlers(Webserver* webserver) {
-  bool styled = true;
-  bool on_nav_bar = true;
-  webserver->RegisterPathHandler("/logs", "Logs", LogsHandler, styled, on_nav_bar);
-  webserver->RegisterPrerenderedPathHandler("/varz", "Flags", FlagsHandler, styled, on_nav_bar);
-  webserver->RegisterPrerenderedPathHandler("/memz", "Memory (total)", MemUsageHandler,
-                                            styled, on_nav_bar);
-  webserver->RegisterPrerenderedPathHandler("/mem-trackers", "Memory (detail)", MemTrackersHandler,
-                                            styled, on_nav_bar);
-  webserver->RegisterPathHandler("/config", "Configuration", ConfigurationHandler,
-                                  styled, on_nav_bar);
-
-  AddPprofPathHandlers(webserver);
-}
-
-
-static void WriteMetricsAsJson(const MetricRegistry* const metrics,
-                               const Webserver::WebRequest& req,
-                               Webserver::PrerenderedWebResponse* resp) {
-  std::ostringstream* output = resp->output;
-  const string* requested_metrics_param = FindOrNull(req.parsed_args, "metrics");
-  vector<string> requested_metrics;
-  MetricJsonOptions opts;
-
-  {
-    string arg = FindWithDefault(req.parsed_args, "include_raw_histograms", "false");
-    opts.include_raw_histograms = ParseLeadingBoolValue(arg.c_str(), false);
-  }
-  {
-    string arg = FindWithDefault(req.parsed_args, "include_schema", "false");
-    opts.include_schema_info = ParseLeadingBoolValue(arg.c_str(), false);
-  }
-  JsonWriter::Mode json_mode;
-  {
-    string arg = FindWithDefault(req.parsed_args, "compact", "false");
-    json_mode = ParseLeadingBoolValue(arg.c_str(), false) ?
-      JsonWriter::COMPACT : JsonWriter::PRETTY;
-  }
-
-  JsonWriter writer(output, json_mode);
-
-  if (requested_metrics_param != nullptr) {
-    SplitStringUsing(*requested_metrics_param, ",", &requested_metrics);
-  } else {
-    // Default to including all metrics.
-    requested_metrics.emplace_back("*");
-  }
-
-  WARN_NOT_OK(metrics->WriteAsJson(&writer, requested_metrics, opts),
-              "Couldn't write JSON metrics over HTTP");
-}
-
-void RegisterMetricsJsonHandler(Webserver* webserver, const MetricRegistry* const metrics) {
-  Webserver::PrerenderedPathHandlerCallback callback = boost::bind(WriteMetricsAsJson, metrics,
-                                                                   _1, _2);
-  bool not_styled = false;
-  bool not_on_nav_bar = false;
-  bool is_on_nav_bar = true;
-  webserver->RegisterPrerenderedPathHandler("/metrics", "Metrics", callback,
-                                            not_styled, is_on_nav_bar);
-
-  // The old name -- this is preserved for compatibility with older releases of
-  // monitoring software which expects the old name.
-  webserver->RegisterPrerenderedPathHandler("/jsonmetricz", "Metrics", callback,
-                                            not_styled, not_on_nav_bar);
-}
-
-} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/default-path-handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/server/default-path-handlers.h b/src/kudu/server/default-path-handlers.h
deleted file mode 100644
index 82f3e2b..0000000
--- a/src/kudu/server/default-path-handlers.h
+++ /dev/null
@@ -1,35 +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.
-
-#ifndef KUDU_SERVER_DEFAULT_PATH_HANDLERS_H
-#define KUDU_SERVER_DEFAULT_PATH_HANDLERS_H
-
-namespace kudu {
-
-class MetricRegistry;
-class Webserver;
-
-// Adds a set of default path handlers to the webserver to display
-// logs and configuration flags.
-void AddDefaultPathHandlers(Webserver* webserver);
-
-// Adds an endpoint to get metrics in JSON format.
-void RegisterMetricsJsonHandler(Webserver* webserver, const MetricRegistry* const metrics);
-
-} // namespace kudu
-
-#endif // KUDU_SERVER_DEFAULT_PATH_HANDLERS_H


Mime
View raw message