You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by wd...@apache.org on 2017/10/17 23:10:39 UTC
[1/3] kudu git commit: Rename *-path-handlers.* to *_path_handlers.*
Repository: kudu
Updated Branches:
refs/heads/master abbfa49de -> 1c90b2f5c
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/tserver/tserver-path-handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/tserver-path-handlers.cc b/src/kudu/tserver/tserver-path-handlers.cc
deleted file mode 100644
index 49b2df2..0000000
--- a/src/kudu/tserver/tserver-path-handlers.cc
+++ /dev/null
@@ -1,679 +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/tserver/tserver-path-handlers.h"
-
-#include <algorithm>
-#include <cstddef>
-#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/column_predicate.h"
-#include "kudu/common/common.pb.h"
-#include "kudu/common/encoded_key.h"
-#include "kudu/common/iterator.h"
-#include "kudu/common/iterator_stats.h"
-#include "kudu/common/partition.h"
-#include "kudu/common/scan_spec.h"
-#include "kudu/common/schema.h"
-#include "kudu/common/wire_protocol.pb.h"
-#include "kudu/consensus/consensus.pb.h"
-#include "kudu/consensus/log_anchor_registry.h"
-#include "kudu/consensus/metadata.pb.h"
-#include "kudu/consensus/opid.pb.h"
-#include "kudu/consensus/quorum_util.h"
-#include "kudu/consensus/raft_consensus.h"
-#include "kudu/gutil/map-util.h"
-#include "kudu/gutil/ref_counted.h"
-#include "kudu/gutil/stringprintf.h"
-#include "kudu/gutil/strings/human_readable.h"
-#include "kudu/gutil/strings/join.h"
-#include "kudu/gutil/strings/numbers.h"
-#include "kudu/gutil/strings/substitute.h"
-#include "kudu/server/webui_util.h"
-#include "kudu/tablet/metadata.pb.h"
-#include "kudu/tablet/tablet.h"
-#include "kudu/tablet/tablet.pb.h"
-#include "kudu/tablet/tablet_metadata.h"
-#include "kudu/tablet/tablet_replica.h"
-#include "kudu/tablet/transactions/transaction.h"
-#include "kudu/tserver/scanners.h"
-#include "kudu/tserver/tablet_server.h"
-#include "kudu/tserver/ts_tablet_manager.h"
-#include "kudu/util/easy_json.h"
-#include "kudu/util/maintenance_manager.h"
-#include "kudu/util/maintenance_manager.pb.h"
-#include "kudu/util/mem_tracker.h"
-#include "kudu/util/monotime.h"
-#include "kudu/util/pb_util.h"
-#include "kudu/util/url-coding.h"
-#include "kudu/util/web_callback_registry.h"
-
-using kudu::consensus::GetConsensusRole;
-using kudu::consensus::ConsensusStatePB;
-using kudu::consensus::RaftPeerPB;
-using kudu::consensus::TransactionStatusPB;
-using kudu::MaintenanceManagerStatusPB;
-using kudu::MaintenanceManagerStatusPB_MaintenanceOpPB;
-using kudu::MaintenanceManagerStatusPB_OpInstancePB;
-using kudu::pb_util::SecureDebugString;
-using kudu::pb_util::SecureShortDebugString;
-using kudu::tablet::Tablet;
-using kudu::tablet::TabletReplica;
-using kudu::tablet::TabletStatePB;
-using kudu::tablet::TabletStatusPB;
-using kudu::tablet::Transaction;
-using std::endl;
-using std::map;
-using std::ostringstream;
-using std::shared_ptr;
-using std::string;
-using std::vector;
-using strings::Substitute;
-
-namespace kudu {
-namespace tserver {
-
-TabletServerPathHandlers::~TabletServerPathHandlers() {
-}
-
-Status TabletServerPathHandlers::Register(Webserver* server) {
- server->RegisterPrerenderedPathHandler(
- "/scans", "Scans",
- boost::bind(&TabletServerPathHandlers::HandleScansPage, this, _1, _2),
- true /* styled */, false /* is_on_nav_bar */);
- server->RegisterPrerenderedPathHandler(
- "/tablets", "Tablets",
- boost::bind(&TabletServerPathHandlers::HandleTabletsPage, this, _1, _2),
- true /* styled */, true /* is_on_nav_bar */);
- server->RegisterPrerenderedPathHandler(
- "/tablet", "",
- boost::bind(&TabletServerPathHandlers::HandleTabletPage, this, _1, _2),
- true /* styled */, false /* is_on_nav_bar */);
- server->RegisterPrerenderedPathHandler(
- "/transactions", "",
- boost::bind(&TabletServerPathHandlers::HandleTransactionsPage, this, _1, _2),
- true /* styled */, false /* is_on_nav_bar */);
- server->RegisterPrerenderedPathHandler(
- "/tablet-rowsetlayout-svg", "",
- boost::bind(&TabletServerPathHandlers::HandleTabletSVGPage, this, _1, _2),
- true /* styled */, false /* is_on_nav_bar */);
- server->RegisterPrerenderedPathHandler(
- "/tablet-consensus-status", "",
- boost::bind(&TabletServerPathHandlers::HandleConsensusStatusPage, this, _1, _2),
- true /* styled */, false /* is_on_nav_bar */);
- server->RegisterPrerenderedPathHandler(
- "/log-anchors", "",
- boost::bind(&TabletServerPathHandlers::HandleLogAnchorsPage, this, _1, _2),
- true /* styled */, false /* is_on_nav_bar */);
- server->RegisterPrerenderedPathHandler(
- "/dashboards", "Dashboards",
- boost::bind(&TabletServerPathHandlers::HandleDashboardsPage, this, _1, _2),
- true /* styled */, true /* is_on_nav_bar */);
- server->RegisterPathHandler(
- "/maintenance-manager", "",
- boost::bind(&TabletServerPathHandlers::HandleMaintenanceManagerPage, this, _1, _2),
- true /* styled */, false /* is_on_nav_bar */);
-
- return Status::OK();
-}
-
-void TabletServerPathHandlers::HandleTransactionsPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
- bool as_text = ContainsKey(req.parsed_args, "raw");
-
- vector<scoped_refptr<TabletReplica> > replicas;
- tserver_->tablet_manager()->GetTabletReplicas(&replicas);
-
- string arg = FindWithDefault(req.parsed_args, "include_traces", "false");
- Transaction::TraceType trace_type = ParseLeadingBoolValue(
- arg.c_str(), false) ? Transaction::TRACE_TXNS : Transaction::NO_TRACE_TXNS;
-
- if (!as_text) {
- *output << "<h1>Transactions</h1>\n";
- *output << "<table class='table table-striped'>\n";
- *output << " <thead><tr><th>Tablet id</th><th>Op Id</th>"
- "<th>Transaction Type</th><th>"
- "Total time in-flight</th><th>Description</th></tr></thead>\n";
- *output << "<tbody>\n";
- }
-
- for (const scoped_refptr<TabletReplica>& replica : replicas) {
- vector<TransactionStatusPB> inflight;
-
- if (replica->tablet() == nullptr) {
- continue;
- }
-
- replica->GetInFlightTransactions(trace_type, &inflight);
- for (const TransactionStatusPB& inflight_tx : inflight) {
- string total_time_str = Substitute("$0 us.", inflight_tx.running_for_micros());
- string description;
- if (trace_type == Transaction::TRACE_TXNS) {
- description = Substitute("$0, Trace: $1",
- inflight_tx.description(), inflight_tx.trace_buffer());
- } else {
- description = inflight_tx.description();
- }
-
- if (!as_text) {
- *output << Substitute(
- "<tr><th>$0</th><th>$1</th><th>$2</th><th>$3</th><th>$4</th></tr>\n",
- EscapeForHtmlToString(replica->tablet_id()),
- EscapeForHtmlToString(SecureShortDebugString(inflight_tx.op_id())),
- OperationType_Name(inflight_tx.tx_type()),
- total_time_str,
- EscapeForHtmlToString(description));
- } else {
- *output << "Tablet: " << replica->tablet_id() << endl;
- *output << "Op ID: " << SecureShortDebugString(inflight_tx.op_id()) << endl;
- *output << "Type: " << OperationType_Name(inflight_tx.tx_type()) << endl;
- *output << "Running: " << total_time_str;
- *output << description << endl;
- *output << endl;
- }
- }
- }
-
- if (!as_text) {
- *output << "</tbody></table>\n";
- }
-}
-
-namespace {
-string TabletLink(const string& id) {
- return Substitute("<a href=\"/tablet?id=$0\">$1</a>",
- UrlEncodeToString(id),
- EscapeForHtmlToString(id));
-}
-
-} // anonymous namespace
-
-void TabletServerPathHandlers::HandleTabletsPage(const Webserver::WebRequest& /*req*/,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
- vector<scoped_refptr<TabletReplica>> replicas;
- tserver_->tablet_manager()->GetTabletReplicas(&replicas);
-
- // Sort by (table_name, tablet_id) tuples.
- std::sort(replicas.begin(), replicas.end(),
- [](const scoped_refptr<TabletReplica>& rep_a,
- const scoped_refptr<TabletReplica>& rep_b) {
- return std::make_pair(rep_a->tablet_metadata()->table_name(), rep_a->tablet_id()) <
- std::make_pair(rep_b->tablet_metadata()->table_name(), rep_b->tablet_id());
- });
-
- // For assigning ids to table divs;
- int i = 0;
- auto generate_table = [this, &i](const vector<scoped_refptr<TabletReplica>>& replicas,
- std::ostream* output) {
- i++;
-
- *output << "<h4>Summary</h4>\n";
- map<string, int> tablet_statuses;
- for (const scoped_refptr<TabletReplica>& replica : replicas) {
- tablet_statuses[TabletStatePB_Name(replica->state())]++;
- }
- *output << "<table class='table table-striped table-hover'>\n";
- *output << "<thead><tr><th>Status</th><th>Count</th><th>Percentage</th></tr></thead>\n";
- *output << "<tbody>\n";
- for (const auto& entry : tablet_statuses) {
- double percent = replicas.size() == 0 ? 0 : (100.0 * entry.second) / replicas.size();
- *output << Substitute("<tr><td>$0</td><td>$1</td><td>$2</td></tr>\n",
- entry.first,
- entry.second,
- StringPrintf("%.2f", percent));
- }
- *output << "</tbody>\n";
- *output << Substitute("<tfoot><tr><td>Total</td><td>$0</td><td></td></tr></tfoot>\n",
- replicas.size());
- *output << "</table>\n";
-
- *output << "<h4>Detail</h4>";
- *output << Substitute("<a href='#detail$0' data-toggle='collapse'>(toggle)</a>\n", i);
- *output << Substitute("<div id='detail$0' class='collapse'>\n", i);
- *output << "<table class='table table-striped table-hover'>\n";
- *output << "<thead><tr><th>Table name</th><th>Tablet ID</th>"
- "<th>Partition</th><th>State</th><th>Write buffer memory usage</th>"
- "<th>On-disk size</th><th>RaftConfig</th><th>Last status</th></tr></thead>\n";
- *output << "<tbody>\n";
- for (const scoped_refptr<TabletReplica>& replica : replicas) {
- TabletStatusPB status;
- replica->GetTabletStatusPB(&status);
- string id = status.tablet_id();
- string table_name = status.table_name();
- string tablet_id_or_link;
- if (replica->tablet() != nullptr) {
- tablet_id_or_link = TabletLink(id);
- } else {
- tablet_id_or_link = EscapeForHtmlToString(id);
- }
- string mem_bytes = "";
- if (replica->tablet() != nullptr) {
- mem_bytes = HumanReadableNumBytes::ToString(
- replica->tablet()->mem_tracker()->consumption());
- }
- string n_bytes = "";
- if (status.has_estimated_on_disk_size()) {
- n_bytes = HumanReadableNumBytes::ToString(status.estimated_on_disk_size());
- }
- string partition = replica->tablet_metadata()
- ->partition_schema()
- .PartitionDebugString(replica->tablet_metadata()->partition(),
- replica->tablet_metadata()->schema());
-
- shared_ptr<consensus::RaftConsensus> consensus = replica->shared_consensus();
- *output << Substitute(
- // Table name, tablet id, partition
- "<tr><td>$0</td><td>$1</td><td>$2</td>"
- // State, on-disk size, consensus configuration, last status
- "<td>$3</td><td>$4</td><td>$5</td><td>$6</td><td>$7</td></tr>\n",
- EscapeForHtmlToString(table_name), // $0
- tablet_id_or_link, // $1
- EscapeForHtmlToString(partition), // $2
- EscapeForHtmlToString(replica->HumanReadableState()), mem_bytes, n_bytes, // $3, $4, $5
- consensus ? ConsensusStatePBToHtml(consensus->ConsensusState()) : "", // $6
- EscapeForHtmlToString(status.last_status())); // $7
- }
- *output << "<tbody></table>\n</div>\n";
- };
-
- vector<scoped_refptr<TabletReplica>> live_replicas;
- vector<scoped_refptr<TabletReplica>> tombstoned_replicas;
- for (const scoped_refptr<TabletReplica>& replica : replicas) {
- if (replica->HumanReadableState() != "TABLET_DATA_TOMBSTONED") {
- live_replicas.push_back(replica);
- } else {
- tombstoned_replicas.push_back(replica);
- }
- }
-
- if (!live_replicas.empty()) {
- *output << "<h3>Live Tablets</h3>\n";
- generate_table(live_replicas, output);
- }
- if (!tombstoned_replicas.empty()) {
- *output << "<h3>Tombstoned Tablets</h3>\n";
- *output << "<p><small>Tombstoned tablets are tablets that previously "
- "stored a replica on this server.</small></p>";
- generate_table(tombstoned_replicas, output);
- }
-}
-
-namespace {
-
-bool CompareByMemberType(const RaftPeerPB& a, const RaftPeerPB& b) {
- if (!a.has_member_type()) return false;
- if (!b.has_member_type()) return true;
- return a.member_type() < b.member_type();
-}
-
-} // anonymous namespace
-
-string TabletServerPathHandlers::ConsensusStatePBToHtml(const ConsensusStatePB& cstate) const {
- ostringstream html;
-
- html << "<ul>\n";
- std::vector<RaftPeerPB> sorted_peers;
- sorted_peers.assign(cstate.committed_config().peers().begin(),
- cstate.committed_config().peers().end());
- std::sort(sorted_peers.begin(), sorted_peers.end(), &CompareByMemberType);
- for (const RaftPeerPB& peer : sorted_peers) {
- string peer_addr_or_uuid =
- peer.has_last_known_addr() ? Substitute("$0:$1",
- peer.last_known_addr().host(),
- peer.last_known_addr().port())
- : peer.permanent_uuid();
- peer_addr_or_uuid = EscapeForHtmlToString(peer_addr_or_uuid);
- string role_name = RaftPeerPB::Role_Name(GetConsensusRole(peer.permanent_uuid(), cstate));
- string formatted = Substitute("$0: $1", role_name, peer_addr_or_uuid);
- // Make the local peer bold.
- if (peer.permanent_uuid() == tserver_->instance_pb().permanent_uuid()) {
- formatted = Substitute("<b>$0</b>", formatted);
- }
-
- html << Substitute(" <li>$0</li>\n", formatted);
- }
- html << "</ul>\n";
- return html.str();
-}
-
-namespace {
-
-bool GetTabletID(const Webserver::WebRequest& req,
- string* id,
- Webserver::PrerenderedWebResponse* resp) {
- if (!FindCopy(req.parsed_args, "id", id)) {
- resp->status_code = HttpStatusCode::BadRequest;
- *resp->output << "Tablet missing 'id' argument";
- return false;
- }
- return true;
-}
-
-bool GetTabletReplica(TabletServer* tserver, const Webserver::WebRequest& /*req*/,
- scoped_refptr<TabletReplica>* replica, const string& tablet_id,
- Webserver::PrerenderedWebResponse* resp) {
- if (!tserver->tablet_manager()->LookupTablet(tablet_id, replica)) {
- resp->status_code = HttpStatusCode::NotFound;
- *resp->output << "Tablet " << EscapeForHtmlToString(tablet_id) << " not found";
- return false;
- }
- return true;
-}
-
-bool TabletBootstrapping(const scoped_refptr<TabletReplica>& replica, const string& tablet_id,
- Webserver::PrerenderedWebResponse* resp) {
- if (replica->state() == tablet::BOOTSTRAPPING) {
- resp->status_code = HttpStatusCode::ServiceUnavailable;
- *resp->output << "Tablet " << EscapeForHtmlToString(tablet_id) << " is still bootstrapping";
- return true;
- }
- return false;
-}
-
-// Returns true if the tablet_id was properly specified, the
-// tablet is found, and is in a non-bootstrapping state.
-bool LoadTablet(TabletServer* tserver,
- const Webserver::WebRequest& req,
- string* tablet_id, scoped_refptr<TabletReplica>* replica,
- Webserver::PrerenderedWebResponse* resp) {
- return GetTabletID(req, tablet_id, resp) &&
- GetTabletReplica(tserver, req, replica, *tablet_id, resp) &&
- !TabletBootstrapping(*replica, *tablet_id, resp);
-}
-
-} // anonymous namespace
-
-void TabletServerPathHandlers::HandleTabletPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
- string tablet_id;
- scoped_refptr<TabletReplica> replica;
- if (!LoadTablet(tserver_, req, &tablet_id, &replica, resp)) return;
-
- string table_name = replica->tablet_metadata()->table_name();
- RaftPeerPB::Role role = RaftPeerPB::UNKNOWN_ROLE;
- auto consensus = replica->consensus();
- if (consensus) {
- role = consensus->role();
- }
-
- *output << "<h1>Tablet " << EscapeForHtmlToString(tablet_id)
- << " (" << replica->HumanReadableState()
- << "/" << RaftPeerPB::Role_Name(role) << ")</h1>\n";
- *output << "<h3>Table " << EscapeForHtmlToString(table_name) << "</h3>";
-
- // Output schema in tabular format.
- *output << "<h2>Schema</h2>\n";
- const Schema& schema = replica->tablet_metadata()->schema();
- HtmlOutputSchemaTable(schema, output);
-
- *output << "<h2>Other Tablet Info Pages</h2>" << endl;
-
- // List of links to various tablet-specific info pages
- *output << "<ul>";
-
- // Link to output svg of current DiskRowSet layout over keyspace.
- *output << "<li>" << Substitute("<a href=\"/tablet-rowsetlayout-svg?id=$0\">$1</a>",
- UrlEncodeToString(tablet_id),
- "Rowset Layout Diagram")
- << "</li>" << endl;
-
- // Link to consensus status page.
- *output << "<li>" << Substitute("<a href=\"/tablet-consensus-status?id=$0\">$1</a>",
- UrlEncodeToString(tablet_id),
- "Consensus Status")
- << "</li>" << endl;
-
- // Log anchors info page.
- *output << "<li>" << Substitute("<a href=\"/log-anchors?id=$0\">$1</a>",
- UrlEncodeToString(tablet_id),
- "Tablet Log Anchors")
- << "</li>" << endl;
-
- // End list
- *output << "</ul>\n";
-}
-
-void TabletServerPathHandlers::HandleTabletSVGPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
- string id;
- scoped_refptr<TabletReplica> replica;
- if (!LoadTablet(tserver_, req, &id, &replica, resp)) return;
- shared_ptr<Tablet> tablet = replica->shared_tablet();
- if (!tablet) {
- *output << "Tablet " << EscapeForHtmlToString(id) << " not running";
- return;
- }
-
- *output << "<h1>Rowset Layout Diagram for Tablet "
- << TabletLink(id) << "</h1>\n";
- tablet->PrintRSLayout(output);
-
-}
-
-void TabletServerPathHandlers::HandleLogAnchorsPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
- string tablet_id;
- scoped_refptr<TabletReplica> replica;
- if (!LoadTablet(tserver_, req, &tablet_id, &replica, resp)) return;
-
- *output << "<h1>Log Anchors for Tablet " << EscapeForHtmlToString(tablet_id) << "</h1>"
- << std::endl;
-
- string dump = replica->log_anchor_registry()->DumpAnchorInfo();
- *output << "<pre>" << EscapeForHtmlToString(dump) << "</pre>" << std::endl;
-}
-
-void TabletServerPathHandlers::HandleConsensusStatusPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
- string id;
- scoped_refptr<TabletReplica> replica;
- if (!LoadTablet(tserver_, req, &id, &replica, resp)) return;
- shared_ptr<consensus::RaftConsensus> consensus = replica->shared_consensus();
- if (!consensus) {
- *output << "Tablet " << EscapeForHtmlToString(id) << " not initialized";
- return;
- }
- consensus->DumpStatusHtml(*output);
-}
-
-void TabletServerPathHandlers::HandleScansPage(const Webserver::WebRequest& /*req*/,
- Webserver::PrerenderedWebResponse* resp) {
- std::ostringstream* output = resp->output;
- *output << "<h1>Scans</h1>\n";
- *output << "<table class='table table-striped'>\n";
- *output << "<thead><tr><th>Tablet id</th><th>Scanner id</th><th>Total time in-flight</th>"
- "<th>Time since last update</th><th>Requestor</th><th>Iterator Stats</th>"
- "<th>Pushed down key predicates</th><th>Other predicates</th></tr></thead>\n";
- *output << "<tbody>\n";
-
- vector<SharedScanner> scanners;
- tserver_->scanner_manager()->ListScanners(&scanners);
- for (const SharedScanner& scanner : scanners) {
- *output << ScannerToHtml(*scanner);
- }
- *output << "</tbody></table>";
-}
-
-string TabletServerPathHandlers::ScannerToHtml(const Scanner& scanner) const {
- std::ostringstream html;
- uint64_t time_in_flight_us =
- (MonoTime::Now() - scanner.start_time()).ToMicroseconds();
- uint64_t time_since_last_access_us =
- scanner.TimeSinceLastAccess(MonoTime::Now()).ToMicroseconds();
-
- html << Substitute("<tr><td>$0</td><td>$1</td><td>$2 us.</td><td>$3 us.</td><td>$4</td>",
- EscapeForHtmlToString(scanner.tablet_id()), // $0
- EscapeForHtmlToString(scanner.id()), // $1
- time_in_flight_us, time_since_last_access_us, // $2, $3
- EscapeForHtmlToString(scanner.requestor_string())); // $4
-
-
- if (!scanner.IsInitialized()) {
- html << "<td colspan=\"3\"><not yet initialized></td></tr>";
- return html.str();
- }
-
- const Schema* projection = &scanner.iter()->schema();
-
- vector<IteratorStats> stats;
- scanner.GetIteratorStats(&stats);
- CHECK_EQ(stats.size(), projection->num_columns());
- html << Substitute("<td>$0</td>", IteratorStatsToHtml(*projection, stats));
- scoped_refptr<TabletReplica> tablet_replica;
- if (!tserver_->tablet_manager()->LookupTablet(scanner.tablet_id(), &tablet_replica)) {
- html << Substitute("<td colspan=\"2\"><b>Tablet $0 is no longer valid.</b></td></tr>\n",
- scanner.tablet_id());
- } else {
- string range_pred_str;
- vector<string> other_preds;
- const ScanSpec& spec = scanner.spec();
- if (spec.lower_bound_key() || spec.exclusive_upper_bound_key()) {
- range_pred_str = EncodedKey::RangeToString(spec.lower_bound_key(),
- spec.exclusive_upper_bound_key());
- }
- for (const auto& col_pred : scanner.spec().predicates()) {
- int32_t col_idx = projection->find_column(col_pred.first);
- if (col_idx == Schema::kColumnNotFound) {
- other_preds.emplace_back("unknown column");
- } else {
- other_preds.push_back(col_pred.second.ToString());
- }
- }
- string other_pred_str = JoinStrings(other_preds, "\n");
- html << Substitute("<td>$0</td><td>$1</td></tr>\n",
- EscapeForHtmlToString(range_pred_str),
- EscapeForHtmlToString(other_pred_str));
- }
- return html.str();
-}
-
-string TabletServerPathHandlers::IteratorStatsToHtml(const Schema& projection,
- const vector<IteratorStats>& stats) const {
- std::ostringstream html;
- html << "<table>\n";
- html << "<tr><th>Column</th>"
- << "<th>Blocks read from disk</th>"
- << "<th>Bytes read from disk</th>"
- << "<th>Cells read from disk</th>"
- << "</tr>\n";
- for (size_t idx = 0; idx < stats.size(); idx++) {
- // We use 'title' attributes so that if the user hovers over the value, they get a
- // human-readable tooltip.
- html << Substitute("<tr>"
- "<td>$0</td>"
- "<td title=\"$1\">$2</td>"
- "<td title=\"$3\">$4</td>"
- "<td title=\"$5\">$6</td>"
- "</tr>\n",
- EscapeForHtmlToString(projection.column(idx).name()), // $0
- HumanReadableInt::ToString(stats[idx].data_blocks_read_from_disk), // $1
- stats[idx].data_blocks_read_from_disk, // $2
- HumanReadableNumBytes::ToString(stats[idx].bytes_read_from_disk), // $3
- stats[idx].bytes_read_from_disk, // $4
- HumanReadableInt::ToString(stats[idx].cells_read_from_disk), // $5
- stats[idx].cells_read_from_disk); // $6
- }
- html << "</table>\n";
- return html.str();
-}
-
-void TabletServerPathHandlers::HandleDashboardsPage(const Webserver::WebRequest& /*req*/,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
- *output << "<h3>Dashboards</h3>\n";
- *output << "<table class='table table-striped'>\n";
- *output << " <thead><tr><th>Dashboard</th><th>Description</th></tr></thead>\n";
- *output << " <tbody\n";
- *output << GetDashboardLine("scans", "Scans", "List of scanners that are currently running.");
- *output << GetDashboardLine("transactions", "Transactions", "List of transactions that are "
- "currently running.");
- *output << GetDashboardLine("maintenance-manager", "Maintenance Manager",
- "List of operations that are currently running and those "
- "that are registered.");
- *output << "</tbody></table>\n";
-}
-
-string TabletServerPathHandlers::GetDashboardLine(const std::string& link,
- const std::string& text,
- const std::string& desc) {
- return Substitute(" <tr><td><a href=\"$0\">$1</a></td><td>$2</td></tr>\n",
- EscapeForHtmlToString(link),
- EscapeForHtmlToString(text),
- EscapeForHtmlToString(desc));
-}
-
-void TabletServerPathHandlers::HandleMaintenanceManagerPage(const Webserver::WebRequest& req,
- Webserver::WebResponse* resp) {
- EasyJson* output = resp->output;
- MaintenanceManager* manager = tserver_->maintenance_manager();
- MaintenanceManagerStatusPB pb;
- manager->GetMaintenanceManagerStatusDump(&pb);
- if (ContainsKey(req.parsed_args, "raw")) {
- (*output)["raw"] = SecureDebugString(pb);
- return;
- }
-
- EasyJson running_ops = output->Set("running_operations", EasyJson::kArray);
- for (const auto& op_pb : pb.registered_operations()) {
- if (op_pb.running() > 0) {
- EasyJson running_op = running_ops.PushBack(EasyJson::kObject);
- running_op["name"] = op_pb.name();
- running_op["instances_running"] = op_pb.running();
- }
- }
-
- EasyJson completed_ops = output->Set("completed_operations", EasyJson::kArray);
- for (const auto& op_pb : pb.completed_operations()) {
- EasyJson completed_op = completed_ops.PushBack(EasyJson::kObject);
- completed_op["name"] = op_pb.name();
- completed_op["duration"] =
- HumanReadableElapsedTime::ToShortString(op_pb.duration_millis() / 1000.0);
- completed_op["time_since_start"] =
- HumanReadableElapsedTime::ToShortString(op_pb.millis_since_start() / 1000.0);
- }
-
- EasyJson registered_ops = output->Set("registered_operations", EasyJson::kArray);
- for (const auto& op_pb : pb.registered_operations()) {
- EasyJson registered_op = registered_ops.PushBack(EasyJson::kObject);
- registered_op["name"] = op_pb.name();
- registered_op["runnable"] = op_pb.runnable();
- registered_op["ram_anchored"] = HumanReadableNumBytes::ToString(op_pb.ram_anchored_bytes());
- registered_op["logs_retained"] = HumanReadableNumBytes::ToString(op_pb.logs_retained_bytes());
- registered_op["perf"] = op_pb.perf_improvement();
- }
-}
-
-} // namespace tserver
-} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/tserver/tserver-path-handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/tserver-path-handlers.h b/src/kudu/tserver/tserver-path-handlers.h
deleted file mode 100644
index 8c00a6f..0000000
--- a/src/kudu/tserver/tserver-path-handlers.h
+++ /dev/null
@@ -1,84 +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_TSERVER_TSERVER_PATH_HANDLERS_H
-#define KUDU_TSERVER_TSERVER_PATH_HANDLERS_H
-
-#include <string>
-#include <vector>
-
-#include "kudu/gutil/macros.h"
-#include "kudu/server/webserver.h"
-#include "kudu/util/status.h"
-
-namespace kudu {
-
-class Schema;
-struct IteratorStats;
-
-namespace consensus {
-class ConsensusStatePB;
-} // namespace consensus
-
-namespace tserver {
-
-class TabletServer;
-class Scanner;
-
-class TabletServerPathHandlers {
- public:
- explicit TabletServerPathHandlers(TabletServer* tserver)
- : tserver_(tserver) {
- }
-
- ~TabletServerPathHandlers();
-
- Status Register(Webserver* server);
-
- private:
- void HandleScansPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp);
- void HandleTabletsPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp);
- void HandleTabletPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp);
- void HandleTransactionsPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp);
- void HandleTabletSVGPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp);
- void HandleLogAnchorsPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp);
- void HandleConsensusStatusPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp);
- void HandleDashboardsPage(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp);
- void HandleMaintenanceManagerPage(const Webserver::WebRequest& req,
- Webserver::WebResponse* resp);
- std::string ConsensusStatePBToHtml(const consensus::ConsensusStatePB& cstate) const;
- std::string ScannerToHtml(const Scanner& scanner) const;
- std::string IteratorStatsToHtml(const Schema& projection,
- const std::vector<IteratorStats>& stats) const;
- std::string GetDashboardLine(const std::string& link,
- const std::string& text, const std::string& desc);
-
- TabletServer* tserver_;
-
- DISALLOW_COPY_AND_ASSIGN(TabletServerPathHandlers);
-};
-
-} // namespace tserver
-} // namespace kudu
-#endif /* KUDU_TSERVER_TSERVER_PATH_HANDLERS_H */
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/tserver/tserver_path_handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/tserver_path_handlers.cc b/src/kudu/tserver/tserver_path_handlers.cc
new file mode 100644
index 0000000..c982d41
--- /dev/null
+++ b/src/kudu/tserver/tserver_path_handlers.cc
@@ -0,0 +1,677 @@
+// 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/tserver/tserver_path_handlers.h"
+
+#include <algorithm>
+#include <cstddef>
+#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/column_predicate.h"
+#include "kudu/common/common.pb.h"
+#include "kudu/common/encoded_key.h"
+#include "kudu/common/iterator.h"
+#include "kudu/common/iterator_stats.h"
+#include "kudu/common/partition.h"
+#include "kudu/common/scan_spec.h"
+#include "kudu/common/schema.h"
+#include "kudu/common/wire_protocol.pb.h"
+#include "kudu/consensus/consensus.pb.h"
+#include "kudu/consensus/log_anchor_registry.h"
+#include "kudu/consensus/metadata.pb.h"
+#include "kudu/consensus/opid.pb.h"
+#include "kudu/consensus/quorum_util.h"
+#include "kudu/consensus/raft_consensus.h"
+#include "kudu/gutil/map-util.h"
+#include "kudu/gutil/ref_counted.h"
+#include "kudu/gutil/stringprintf.h"
+#include "kudu/gutil/strings/human_readable.h"
+#include "kudu/gutil/strings/join.h"
+#include "kudu/gutil/strings/numbers.h"
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/server/webui_util.h"
+#include "kudu/tablet/metadata.pb.h"
+#include "kudu/tablet/tablet.h"
+#include "kudu/tablet/tablet.pb.h"
+#include "kudu/tablet/tablet_metadata.h"
+#include "kudu/tablet/tablet_replica.h"
+#include "kudu/tablet/transactions/transaction.h"
+#include "kudu/tserver/scanners.h"
+#include "kudu/tserver/tablet_server.h"
+#include "kudu/tserver/ts_tablet_manager.h"
+#include "kudu/util/easy_json.h"
+#include "kudu/util/maintenance_manager.h"
+#include "kudu/util/maintenance_manager.pb.h"
+#include "kudu/util/mem_tracker.h"
+#include "kudu/util/monotime.h"
+#include "kudu/util/pb_util.h"
+#include "kudu/util/url-coding.h"
+#include "kudu/util/web_callback_registry.h"
+
+using kudu::consensus::GetConsensusRole;
+using kudu::consensus::ConsensusStatePB;
+using kudu::consensus::RaftPeerPB;
+using kudu::consensus::TransactionStatusPB;
+using kudu::MaintenanceManagerStatusPB;
+using kudu::pb_util::SecureDebugString;
+using kudu::pb_util::SecureShortDebugString;
+using kudu::tablet::Tablet;
+using kudu::tablet::TabletReplica;
+using kudu::tablet::TabletStatePB;
+using kudu::tablet::TabletStatusPB;
+using kudu::tablet::Transaction;
+using std::endl;
+using std::map;
+using std::ostringstream;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+using strings::Substitute;
+
+namespace kudu {
+namespace tserver {
+
+TabletServerPathHandlers::~TabletServerPathHandlers() {
+}
+
+Status TabletServerPathHandlers::Register(Webserver* server) {
+ server->RegisterPrerenderedPathHandler(
+ "/scans", "Scans",
+ boost::bind(&TabletServerPathHandlers::HandleScansPage, this, _1, _2),
+ true /* styled */, false /* is_on_nav_bar */);
+ server->RegisterPrerenderedPathHandler(
+ "/tablets", "Tablets",
+ boost::bind(&TabletServerPathHandlers::HandleTabletsPage, this, _1, _2),
+ true /* styled */, true /* is_on_nav_bar */);
+ server->RegisterPrerenderedPathHandler(
+ "/tablet", "",
+ boost::bind(&TabletServerPathHandlers::HandleTabletPage, this, _1, _2),
+ true /* styled */, false /* is_on_nav_bar */);
+ server->RegisterPrerenderedPathHandler(
+ "/transactions", "",
+ boost::bind(&TabletServerPathHandlers::HandleTransactionsPage, this, _1, _2),
+ true /* styled */, false /* is_on_nav_bar */);
+ server->RegisterPrerenderedPathHandler(
+ "/tablet-rowsetlayout-svg", "",
+ boost::bind(&TabletServerPathHandlers::HandleTabletSVGPage, this, _1, _2),
+ true /* styled */, false /* is_on_nav_bar */);
+ server->RegisterPrerenderedPathHandler(
+ "/tablet-consensus-status", "",
+ boost::bind(&TabletServerPathHandlers::HandleConsensusStatusPage, this, _1, _2),
+ true /* styled */, false /* is_on_nav_bar */);
+ server->RegisterPrerenderedPathHandler(
+ "/log-anchors", "",
+ boost::bind(&TabletServerPathHandlers::HandleLogAnchorsPage, this, _1, _2),
+ true /* styled */, false /* is_on_nav_bar */);
+ server->RegisterPrerenderedPathHandler(
+ "/dashboards", "Dashboards",
+ boost::bind(&TabletServerPathHandlers::HandleDashboardsPage, this, _1, _2),
+ true /* styled */, true /* is_on_nav_bar */);
+ server->RegisterPathHandler(
+ "/maintenance-manager", "",
+ boost::bind(&TabletServerPathHandlers::HandleMaintenanceManagerPage, this, _1, _2),
+ true /* styled */, false /* is_on_nav_bar */);
+
+ return Status::OK();
+}
+
+void TabletServerPathHandlers::HandleTransactionsPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+ bool as_text = ContainsKey(req.parsed_args, "raw");
+
+ vector<scoped_refptr<TabletReplica> > replicas;
+ tserver_->tablet_manager()->GetTabletReplicas(&replicas);
+
+ string arg = FindWithDefault(req.parsed_args, "include_traces", "false");
+ Transaction::TraceType trace_type = ParseLeadingBoolValue(
+ arg.c_str(), false) ? Transaction::TRACE_TXNS : Transaction::NO_TRACE_TXNS;
+
+ if (!as_text) {
+ *output << "<h1>Transactions</h1>\n";
+ *output << "<table class='table table-striped'>\n";
+ *output << " <thead><tr><th>Tablet id</th><th>Op Id</th>"
+ "<th>Transaction Type</th><th>"
+ "Total time in-flight</th><th>Description</th></tr></thead>\n";
+ *output << "<tbody>\n";
+ }
+
+ for (const scoped_refptr<TabletReplica>& replica : replicas) {
+ vector<TransactionStatusPB> inflight;
+
+ if (replica->tablet() == nullptr) {
+ continue;
+ }
+
+ replica->GetInFlightTransactions(trace_type, &inflight);
+ for (const TransactionStatusPB& inflight_tx : inflight) {
+ string total_time_str = Substitute("$0 us.", inflight_tx.running_for_micros());
+ string description;
+ if (trace_type == Transaction::TRACE_TXNS) {
+ description = Substitute("$0, Trace: $1",
+ inflight_tx.description(), inflight_tx.trace_buffer());
+ } else {
+ description = inflight_tx.description();
+ }
+
+ if (!as_text) {
+ *output << Substitute(
+ "<tr><th>$0</th><th>$1</th><th>$2</th><th>$3</th><th>$4</th></tr>\n",
+ EscapeForHtmlToString(replica->tablet_id()),
+ EscapeForHtmlToString(SecureShortDebugString(inflight_tx.op_id())),
+ OperationType_Name(inflight_tx.tx_type()),
+ total_time_str,
+ EscapeForHtmlToString(description));
+ } else {
+ *output << "Tablet: " << replica->tablet_id() << endl;
+ *output << "Op ID: " << SecureShortDebugString(inflight_tx.op_id()) << endl;
+ *output << "Type: " << OperationType_Name(inflight_tx.tx_type()) << endl;
+ *output << "Running: " << total_time_str;
+ *output << description << endl;
+ *output << endl;
+ }
+ }
+ }
+
+ if (!as_text) {
+ *output << "</tbody></table>\n";
+ }
+}
+
+namespace {
+string TabletLink(const string& id) {
+ return Substitute("<a href=\"/tablet?id=$0\">$1</a>",
+ UrlEncodeToString(id),
+ EscapeForHtmlToString(id));
+}
+
+} // anonymous namespace
+
+void TabletServerPathHandlers::HandleTabletsPage(const Webserver::WebRequest& /*req*/,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+ vector<scoped_refptr<TabletReplica>> replicas;
+ tserver_->tablet_manager()->GetTabletReplicas(&replicas);
+
+ // Sort by (table_name, tablet_id) tuples.
+ std::sort(replicas.begin(), replicas.end(),
+ [](const scoped_refptr<TabletReplica>& rep_a,
+ const scoped_refptr<TabletReplica>& rep_b) {
+ return std::make_pair(rep_a->tablet_metadata()->table_name(), rep_a->tablet_id()) <
+ std::make_pair(rep_b->tablet_metadata()->table_name(), rep_b->tablet_id());
+ });
+
+ // For assigning ids to table divs;
+ int i = 0;
+ auto generate_table = [this, &i](const vector<scoped_refptr<TabletReplica>>& replicas,
+ std::ostream* output) {
+ i++;
+
+ *output << "<h4>Summary</h4>\n";
+ map<string, int> tablet_statuses;
+ for (const scoped_refptr<TabletReplica>& replica : replicas) {
+ tablet_statuses[TabletStatePB_Name(replica->state())]++;
+ }
+ *output << "<table class='table table-striped table-hover'>\n";
+ *output << "<thead><tr><th>Status</th><th>Count</th><th>Percentage</th></tr></thead>\n";
+ *output << "<tbody>\n";
+ for (const auto& entry : tablet_statuses) {
+ double percent = replicas.empty() ? 0 : (100.0 * entry.second) / replicas.size();
+ *output << Substitute("<tr><td>$0</td><td>$1</td><td>$2</td></tr>\n",
+ entry.first,
+ entry.second,
+ StringPrintf("%.2f", percent));
+ }
+ *output << "</tbody>\n";
+ *output << Substitute("<tfoot><tr><td>Total</td><td>$0</td><td></td></tr></tfoot>\n",
+ replicas.size());
+ *output << "</table>\n";
+
+ *output << "<h4>Detail</h4>";
+ *output << Substitute("<a href='#detail$0' data-toggle='collapse'>(toggle)</a>\n", i);
+ *output << Substitute("<div id='detail$0' class='collapse'>\n", i);
+ *output << "<table class='table table-striped table-hover'>\n";
+ *output << "<thead><tr><th>Table name</th><th>Tablet ID</th>"
+ "<th>Partition</th><th>State</th><th>Write buffer memory usage</th>"
+ "<th>On-disk size</th><th>RaftConfig</th><th>Last status</th></tr></thead>\n";
+ *output << "<tbody>\n";
+ for (const scoped_refptr<TabletReplica>& replica : replicas) {
+ TabletStatusPB status;
+ replica->GetTabletStatusPB(&status);
+ string id = status.tablet_id();
+ string table_name = status.table_name();
+ string tablet_id_or_link;
+ if (replica->tablet() != nullptr) {
+ tablet_id_or_link = TabletLink(id);
+ } else {
+ tablet_id_or_link = EscapeForHtmlToString(id);
+ }
+ string mem_bytes = "";
+ if (replica->tablet() != nullptr) {
+ mem_bytes = HumanReadableNumBytes::ToString(
+ replica->tablet()->mem_tracker()->consumption());
+ }
+ string n_bytes = "";
+ if (status.has_estimated_on_disk_size()) {
+ n_bytes = HumanReadableNumBytes::ToString(status.estimated_on_disk_size());
+ }
+ string partition = replica->tablet_metadata()
+ ->partition_schema()
+ .PartitionDebugString(replica->tablet_metadata()->partition(),
+ replica->tablet_metadata()->schema());
+
+ shared_ptr<consensus::RaftConsensus> consensus = replica->shared_consensus();
+ *output << Substitute(
+ // Table name, tablet id, partition
+ "<tr><td>$0</td><td>$1</td><td>$2</td>"
+ // State, on-disk size, consensus configuration, last status
+ "<td>$3</td><td>$4</td><td>$5</td><td>$6</td><td>$7</td></tr>\n",
+ EscapeForHtmlToString(table_name), // $0
+ tablet_id_or_link, // $1
+ EscapeForHtmlToString(partition), // $2
+ EscapeForHtmlToString(replica->HumanReadableState()), mem_bytes, n_bytes, // $3, $4, $5
+ consensus ? ConsensusStatePBToHtml(consensus->ConsensusState()) : "", // $6
+ EscapeForHtmlToString(status.last_status())); // $7
+ }
+ *output << "<tbody></table>\n</div>\n";
+ };
+
+ vector<scoped_refptr<TabletReplica>> live_replicas;
+ vector<scoped_refptr<TabletReplica>> tombstoned_replicas;
+ for (const scoped_refptr<TabletReplica>& replica : replicas) {
+ if (replica->HumanReadableState() != "TABLET_DATA_TOMBSTONED") {
+ live_replicas.push_back(replica);
+ } else {
+ tombstoned_replicas.push_back(replica);
+ }
+ }
+
+ if (!live_replicas.empty()) {
+ *output << "<h3>Live Tablets</h3>\n";
+ generate_table(live_replicas, output);
+ }
+ if (!tombstoned_replicas.empty()) {
+ *output << "<h3>Tombstoned Tablets</h3>\n";
+ *output << "<p><small>Tombstoned tablets are tablets that previously "
+ "stored a replica on this server.</small></p>";
+ generate_table(tombstoned_replicas, output);
+ }
+}
+
+namespace {
+
+bool CompareByMemberType(const RaftPeerPB& a, const RaftPeerPB& b) {
+ if (!a.has_member_type()) return false;
+ if (!b.has_member_type()) return true;
+ return a.member_type() < b.member_type();
+}
+
+} // anonymous namespace
+
+string TabletServerPathHandlers::ConsensusStatePBToHtml(const ConsensusStatePB& cstate) const {
+ ostringstream html;
+
+ html << "<ul>\n";
+ std::vector<RaftPeerPB> sorted_peers;
+ sorted_peers.assign(cstate.committed_config().peers().begin(),
+ cstate.committed_config().peers().end());
+ std::sort(sorted_peers.begin(), sorted_peers.end(), &CompareByMemberType);
+ for (const RaftPeerPB& peer : sorted_peers) {
+ string peer_addr_or_uuid =
+ peer.has_last_known_addr() ? Substitute("$0:$1",
+ peer.last_known_addr().host(),
+ peer.last_known_addr().port())
+ : peer.permanent_uuid();
+ peer_addr_or_uuid = EscapeForHtmlToString(peer_addr_or_uuid);
+ string role_name = RaftPeerPB::Role_Name(GetConsensusRole(peer.permanent_uuid(), cstate));
+ string formatted = Substitute("$0: $1", role_name, peer_addr_or_uuid);
+ // Make the local peer bold.
+ if (peer.permanent_uuid() == tserver_->instance_pb().permanent_uuid()) {
+ formatted = Substitute("<b>$0</b>", formatted);
+ }
+
+ html << Substitute(" <li>$0</li>\n", formatted);
+ }
+ html << "</ul>\n";
+ return html.str();
+}
+
+namespace {
+
+bool GetTabletID(const Webserver::WebRequest& req,
+ string* id,
+ Webserver::PrerenderedWebResponse* resp) {
+ if (!FindCopy(req.parsed_args, "id", id)) {
+ resp->status_code = HttpStatusCode::BadRequest;
+ *resp->output << "Tablet missing 'id' argument";
+ return false;
+ }
+ return true;
+}
+
+bool GetTabletReplica(TabletServer* tserver, const Webserver::WebRequest& /*req*/,
+ scoped_refptr<TabletReplica>* replica, const string& tablet_id,
+ Webserver::PrerenderedWebResponse* resp) {
+ if (!tserver->tablet_manager()->LookupTablet(tablet_id, replica)) {
+ resp->status_code = HttpStatusCode::NotFound;
+ *resp->output << "Tablet " << EscapeForHtmlToString(tablet_id) << " not found";
+ return false;
+ }
+ return true;
+}
+
+bool TabletBootstrapping(const scoped_refptr<TabletReplica>& replica, const string& tablet_id,
+ Webserver::PrerenderedWebResponse* resp) {
+ if (replica->state() == tablet::BOOTSTRAPPING) {
+ resp->status_code = HttpStatusCode::ServiceUnavailable;
+ *resp->output << "Tablet " << EscapeForHtmlToString(tablet_id) << " is still bootstrapping";
+ return true;
+ }
+ return false;
+}
+
+// Returns true if the tablet_id was properly specified, the
+// tablet is found, and is in a non-bootstrapping state.
+bool LoadTablet(TabletServer* tserver,
+ const Webserver::WebRequest& req,
+ string* tablet_id, scoped_refptr<TabletReplica>* replica,
+ Webserver::PrerenderedWebResponse* resp) {
+ return GetTabletID(req, tablet_id, resp) &&
+ GetTabletReplica(tserver, req, replica, *tablet_id, resp) &&
+ !TabletBootstrapping(*replica, *tablet_id, resp);
+}
+
+} // anonymous namespace
+
+void TabletServerPathHandlers::HandleTabletPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+ string tablet_id;
+ scoped_refptr<TabletReplica> replica;
+ if (!LoadTablet(tserver_, req, &tablet_id, &replica, resp)) return;
+
+ string table_name = replica->tablet_metadata()->table_name();
+ RaftPeerPB::Role role = RaftPeerPB::UNKNOWN_ROLE;
+ auto consensus = replica->consensus();
+ if (consensus) {
+ role = consensus->role();
+ }
+
+ *output << "<h1>Tablet " << EscapeForHtmlToString(tablet_id)
+ << " (" << replica->HumanReadableState()
+ << "/" << RaftPeerPB::Role_Name(role) << ")</h1>\n";
+ *output << "<h3>Table " << EscapeForHtmlToString(table_name) << "</h3>";
+
+ // Output schema in tabular format.
+ *output << "<h2>Schema</h2>\n";
+ const Schema& schema = replica->tablet_metadata()->schema();
+ HtmlOutputSchemaTable(schema, output);
+
+ *output << "<h2>Other Tablet Info Pages</h2>" << endl;
+
+ // List of links to various tablet-specific info pages
+ *output << "<ul>";
+
+ // Link to output svg of current DiskRowSet layout over keyspace.
+ *output << "<li>" << Substitute("<a href=\"/tablet-rowsetlayout-svg?id=$0\">$1</a>",
+ UrlEncodeToString(tablet_id),
+ "Rowset Layout Diagram")
+ << "</li>" << endl;
+
+ // Link to consensus status page.
+ *output << "<li>" << Substitute("<a href=\"/tablet-consensus-status?id=$0\">$1</a>",
+ UrlEncodeToString(tablet_id),
+ "Consensus Status")
+ << "</li>" << endl;
+
+ // Log anchors info page.
+ *output << "<li>" << Substitute("<a href=\"/log-anchors?id=$0\">$1</a>",
+ UrlEncodeToString(tablet_id),
+ "Tablet Log Anchors")
+ << "</li>" << endl;
+
+ // End list
+ *output << "</ul>\n";
+}
+
+void TabletServerPathHandlers::HandleTabletSVGPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+ string id;
+ scoped_refptr<TabletReplica> replica;
+ if (!LoadTablet(tserver_, req, &id, &replica, resp)) return;
+ shared_ptr<Tablet> tablet = replica->shared_tablet();
+ if (!tablet) {
+ *output << "Tablet " << EscapeForHtmlToString(id) << " not running";
+ return;
+ }
+
+ *output << "<h1>Rowset Layout Diagram for Tablet "
+ << TabletLink(id) << "</h1>\n";
+ tablet->PrintRSLayout(output);
+
+}
+
+void TabletServerPathHandlers::HandleLogAnchorsPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+ string tablet_id;
+ scoped_refptr<TabletReplica> replica;
+ if (!LoadTablet(tserver_, req, &tablet_id, &replica, resp)) return;
+
+ *output << "<h1>Log Anchors for Tablet " << EscapeForHtmlToString(tablet_id) << "</h1>"
+ << std::endl;
+
+ string dump = replica->log_anchor_registry()->DumpAnchorInfo();
+ *output << "<pre>" << EscapeForHtmlToString(dump) << "</pre>" << std::endl;
+}
+
+void TabletServerPathHandlers::HandleConsensusStatusPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+ string id;
+ scoped_refptr<TabletReplica> replica;
+ if (!LoadTablet(tserver_, req, &id, &replica, resp)) return;
+ shared_ptr<consensus::RaftConsensus> consensus = replica->shared_consensus();
+ if (!consensus) {
+ *output << "Tablet " << EscapeForHtmlToString(id) << " not initialized";
+ return;
+ }
+ consensus->DumpStatusHtml(*output);
+}
+
+void TabletServerPathHandlers::HandleScansPage(const Webserver::WebRequest& /*req*/,
+ Webserver::PrerenderedWebResponse* resp) {
+ std::ostringstream* output = resp->output;
+ *output << "<h1>Scans</h1>\n";
+ *output << "<table class='table table-striped'>\n";
+ *output << "<thead><tr><th>Tablet id</th><th>Scanner id</th><th>Total time in-flight</th>"
+ "<th>Time since last update</th><th>Requestor</th><th>Iterator Stats</th>"
+ "<th>Pushed down key predicates</th><th>Other predicates</th></tr></thead>\n";
+ *output << "<tbody>\n";
+
+ vector<SharedScanner> scanners;
+ tserver_->scanner_manager()->ListScanners(&scanners);
+ for (const SharedScanner& scanner : scanners) {
+ *output << ScannerToHtml(*scanner);
+ }
+ *output << "</tbody></table>";
+}
+
+string TabletServerPathHandlers::ScannerToHtml(const Scanner& scanner) const {
+ std::ostringstream html;
+ uint64_t time_in_flight_us =
+ (MonoTime::Now() - scanner.start_time()).ToMicroseconds();
+ uint64_t time_since_last_access_us =
+ scanner.TimeSinceLastAccess(MonoTime::Now()).ToMicroseconds();
+
+ html << Substitute("<tr><td>$0</td><td>$1</td><td>$2 us.</td><td>$3 us.</td><td>$4</td>",
+ EscapeForHtmlToString(scanner.tablet_id()), // $0
+ EscapeForHtmlToString(scanner.id()), // $1
+ time_in_flight_us, time_since_last_access_us, // $2, $3
+ EscapeForHtmlToString(scanner.requestor_string())); // $4
+
+
+ if (!scanner.IsInitialized()) {
+ html << "<td colspan=\"3\"><not yet initialized></td></tr>";
+ return html.str();
+ }
+
+ const Schema* projection = &scanner.iter()->schema();
+
+ vector<IteratorStats> stats;
+ scanner.GetIteratorStats(&stats);
+ CHECK_EQ(stats.size(), projection->num_columns());
+ html << Substitute("<td>$0</td>", IteratorStatsToHtml(*projection, stats));
+ scoped_refptr<TabletReplica> tablet_replica;
+ if (!tserver_->tablet_manager()->LookupTablet(scanner.tablet_id(), &tablet_replica)) {
+ html << Substitute("<td colspan=\"2\"><b>Tablet $0 is no longer valid.</b></td></tr>\n",
+ scanner.tablet_id());
+ } else {
+ string range_pred_str;
+ vector<string> other_preds;
+ const ScanSpec& spec = scanner.spec();
+ if (spec.lower_bound_key() || spec.exclusive_upper_bound_key()) {
+ range_pred_str = EncodedKey::RangeToString(spec.lower_bound_key(),
+ spec.exclusive_upper_bound_key());
+ }
+ for (const auto& col_pred : scanner.spec().predicates()) {
+ int32_t col_idx = projection->find_column(col_pred.first);
+ if (col_idx == Schema::kColumnNotFound) {
+ other_preds.emplace_back("unknown column");
+ } else {
+ other_preds.push_back(col_pred.second.ToString());
+ }
+ }
+ string other_pred_str = JoinStrings(other_preds, "\n");
+ html << Substitute("<td>$0</td><td>$1</td></tr>\n",
+ EscapeForHtmlToString(range_pred_str),
+ EscapeForHtmlToString(other_pred_str));
+ }
+ return html.str();
+}
+
+string TabletServerPathHandlers::IteratorStatsToHtml(const Schema& projection,
+ const vector<IteratorStats>& stats) const {
+ std::ostringstream html;
+ html << "<table>\n";
+ html << "<tr><th>Column</th>"
+ << "<th>Blocks read from disk</th>"
+ << "<th>Bytes read from disk</th>"
+ << "<th>Cells read from disk</th>"
+ << "</tr>\n";
+ for (size_t idx = 0; idx < stats.size(); idx++) {
+ // We use 'title' attributes so that if the user hovers over the value, they get a
+ // human-readable tooltip.
+ html << Substitute("<tr>"
+ "<td>$0</td>"
+ "<td title=\"$1\">$2</td>"
+ "<td title=\"$3\">$4</td>"
+ "<td title=\"$5\">$6</td>"
+ "</tr>\n",
+ EscapeForHtmlToString(projection.column(idx).name()), // $0
+ HumanReadableInt::ToString(stats[idx].data_blocks_read_from_disk), // $1
+ stats[idx].data_blocks_read_from_disk, // $2
+ HumanReadableNumBytes::ToString(stats[idx].bytes_read_from_disk), // $3
+ stats[idx].bytes_read_from_disk, // $4
+ HumanReadableInt::ToString(stats[idx].cells_read_from_disk), // $5
+ stats[idx].cells_read_from_disk); // $6
+ }
+ html << "</table>\n";
+ return html.str();
+}
+
+void TabletServerPathHandlers::HandleDashboardsPage(const Webserver::WebRequest& /*req*/,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+ *output << "<h3>Dashboards</h3>\n";
+ *output << "<table class='table table-striped'>\n";
+ *output << " <thead><tr><th>Dashboard</th><th>Description</th></tr></thead>\n";
+ *output << " <tbody\n";
+ *output << GetDashboardLine("scans", "Scans", "List of scanners that are currently running.");
+ *output << GetDashboardLine("transactions", "Transactions", "List of transactions that are "
+ "currently running.");
+ *output << GetDashboardLine("maintenance-manager", "Maintenance Manager",
+ "List of operations that are currently running and those "
+ "that are registered.");
+ *output << "</tbody></table>\n";
+}
+
+string TabletServerPathHandlers::GetDashboardLine(const std::string& link,
+ const std::string& text,
+ const std::string& desc) {
+ return Substitute(" <tr><td><a href=\"$0\">$1</a></td><td>$2</td></tr>\n",
+ EscapeForHtmlToString(link),
+ EscapeForHtmlToString(text),
+ EscapeForHtmlToString(desc));
+}
+
+void TabletServerPathHandlers::HandleMaintenanceManagerPage(const Webserver::WebRequest& req,
+ Webserver::WebResponse* resp) {
+ EasyJson* output = resp->output;
+ MaintenanceManager* manager = tserver_->maintenance_manager();
+ MaintenanceManagerStatusPB pb;
+ manager->GetMaintenanceManagerStatusDump(&pb);
+ if (ContainsKey(req.parsed_args, "raw")) {
+ (*output)["raw"] = SecureDebugString(pb);
+ return;
+ }
+
+ EasyJson running_ops = output->Set("running_operations", EasyJson::kArray);
+ for (const auto& op_pb : pb.registered_operations()) {
+ if (op_pb.running() > 0) {
+ EasyJson running_op = running_ops.PushBack(EasyJson::kObject);
+ running_op["name"] = op_pb.name();
+ running_op["instances_running"] = op_pb.running();
+ }
+ }
+
+ EasyJson completed_ops = output->Set("completed_operations", EasyJson::kArray);
+ for (const auto& op_pb : pb.completed_operations()) {
+ EasyJson completed_op = completed_ops.PushBack(EasyJson::kObject);
+ completed_op["name"] = op_pb.name();
+ completed_op["duration"] =
+ HumanReadableElapsedTime::ToShortString(op_pb.duration_millis() / 1000.0);
+ completed_op["time_since_start"] =
+ HumanReadableElapsedTime::ToShortString(op_pb.millis_since_start() / 1000.0);
+ }
+
+ EasyJson registered_ops = output->Set("registered_operations", EasyJson::kArray);
+ for (const auto& op_pb : pb.registered_operations()) {
+ EasyJson registered_op = registered_ops.PushBack(EasyJson::kObject);
+ registered_op["name"] = op_pb.name();
+ registered_op["runnable"] = op_pb.runnable();
+ registered_op["ram_anchored"] = HumanReadableNumBytes::ToString(op_pb.ram_anchored_bytes());
+ registered_op["logs_retained"] = HumanReadableNumBytes::ToString(op_pb.logs_retained_bytes());
+ registered_op["perf"] = op_pb.perf_improvement();
+ }
+}
+
+} // namespace tserver
+} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/tserver/tserver_path_handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/tserver_path_handlers.h b/src/kudu/tserver/tserver_path_handlers.h
new file mode 100644
index 0000000..8c00a6f
--- /dev/null
+++ b/src/kudu/tserver/tserver_path_handlers.h
@@ -0,0 +1,84 @@
+// 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_TSERVER_TSERVER_PATH_HANDLERS_H
+#define KUDU_TSERVER_TSERVER_PATH_HANDLERS_H
+
+#include <string>
+#include <vector>
+
+#include "kudu/gutil/macros.h"
+#include "kudu/server/webserver.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+
+class Schema;
+struct IteratorStats;
+
+namespace consensus {
+class ConsensusStatePB;
+} // namespace consensus
+
+namespace tserver {
+
+class TabletServer;
+class Scanner;
+
+class TabletServerPathHandlers {
+ public:
+ explicit TabletServerPathHandlers(TabletServer* tserver)
+ : tserver_(tserver) {
+ }
+
+ ~TabletServerPathHandlers();
+
+ Status Register(Webserver* server);
+
+ private:
+ void HandleScansPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp);
+ void HandleTabletsPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp);
+ void HandleTabletPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp);
+ void HandleTransactionsPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp);
+ void HandleTabletSVGPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp);
+ void HandleLogAnchorsPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp);
+ void HandleConsensusStatusPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp);
+ void HandleDashboardsPage(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp);
+ void HandleMaintenanceManagerPage(const Webserver::WebRequest& req,
+ Webserver::WebResponse* resp);
+ std::string ConsensusStatePBToHtml(const consensus::ConsensusStatePB& cstate) const;
+ std::string ScannerToHtml(const Scanner& scanner) const;
+ std::string IteratorStatsToHtml(const Schema& projection,
+ const std::vector<IteratorStats>& stats) const;
+ std::string GetDashboardLine(const std::string& link,
+ const std::string& text, const std::string& desc);
+
+ TabletServer* tserver_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabletServerPathHandlers);
+};
+
+} // namespace tserver
+} // namespace kudu
+#endif /* KUDU_TSERVER_TSERVER_PATH_HANDLERS_H */
[3/3] kudu git commit: Rename *-path-handlers.* to *_path_handlers.*
Posted by wd...@apache.org.
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 <ad...@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 <wd...@apache.org>
Authored: Tue Oct 17 10:38:47 2017 -0700
Committer: Will Berkeley <wd...@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(®);
- 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(®);
-
- 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(®);
- 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(®);
+ 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(®);
+
+ 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(®);
+ 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
[2/3] kudu git commit: Rename *-path-handlers.* to *_path_handlers.*
Posted by wd...@apache.org.
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
new file mode 100644
index 0000000..040aaaf
--- /dev/null
+++ b/src/kudu/server/default_path_handlers.cc
@@ -0,0 +1,319 @@
+// 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 <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/bind.hpp> // IWYU pragma: keep
+#include <boost/iterator/iterator_traits.hpp>
+#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/faststring.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
new file mode 100644
index 0000000..82f3e2b
--- /dev/null
+++ b/src/kudu/server/default_path_handlers.h
@@ -0,0 +1,35 @@
+// 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
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/pprof-path-handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/pprof-path-handlers.cc b/src/kudu/server/pprof-path-handlers.cc
deleted file mode 100644
index d668889..0000000
--- a/src/kudu/server/pprof-path-handlers.cc
+++ /dev/null
@@ -1,260 +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/pprof-path-handlers.h"
-
-#include <unistd.h>
-
-#include <cstdint>
-#include <cstdlib>
-#include <fstream>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include <gflags/gflags_declare.h>
-#include <glog/logging.h>
-#include <gperftools/heap-profiler.h>
-#include <gperftools/malloc_extension.h>
-#include <gperftools/profiler.h>
-
-#include "kudu/gutil/map-util.h"
-#include "kudu/gutil/strings/numbers.h"
-#include "kudu/gutil/strings/split.h"
-#include "kudu/gutil/strings/stringpiece.h"
-#include "kudu/gutil/strings/strip.h"
-#include "kudu/gutil/strings/substitute.h"
-#include "kudu/gutil/sysinfo.h"
-#include "kudu/server/webserver.h"
-#include "kudu/util/env.h"
-#include "kudu/util/faststring.h"
-#include "kudu/util/monotime.h"
-#include "kudu/util/spinlock_profiling.h"
-#include "kudu/util/status.h"
-#include "kudu/util/web_callback_registry.h"
-
-DECLARE_bool(enable_process_lifetime_heap_profiling);
-DECLARE_string(heap_profile_path);
-
-
-using std::endl;
-using std::ifstream;
-using std::ostringstream;
-using std::string;
-using std::vector;
-
-// GLog already implements symbolization. Just import their hidden symbol.
-namespace google {
-// Symbolizes a program counter. On success, returns true and write the
-// symbol name to "out". The symbol name is demangled if possible
-// (supports symbols generated by GCC 3.x or newer). Otherwise,
-// returns false.
-bool Symbolize(void *pc, char *out, int out_size);
-}
-
-namespace kudu {
-
-const int kPprofDefaultSampleSecs = 30; // pprof default sample time in seconds.
-
-// pprof asks for the url /pprof/cmdline to figure out what application it's profiling.
-// The server should respond by sending the executable path.
-static void PprofCmdLineHandler(const Webserver::WebRequest& /*req*/,
- Webserver::PrerenderedWebResponse* resp) {
- string executable_path;
- Env* env = Env::Default();
- WARN_NOT_OK(env->GetExecutablePath(&executable_path), "Failed to get executable path");
- *resp->output << executable_path;
-}
-
-// pprof asks for the url /pprof/heap to get heap information. This should be implemented
-// by calling HeapProfileStart(filename), continue to do work, and then, some number of
-// seconds later, call GetHeapProfile() followed by HeapProfilerStop().
-static void PprofHeapHandler(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
-#ifndef TCMALLOC_ENABLED
- *output << "Heap profiling is not available without tcmalloc.";
-#else
- // Remote (on-demand) profiling is disabled if the process is already being profiled.
- if (FLAGS_enable_process_lifetime_heap_profiling) {
- *output << "Heap profiling is running for the process lifetime.";
- return;
- }
-
- auto it = req.parsed_args.find("seconds");
- int seconds = kPprofDefaultSampleSecs;
- if (it != req.parsed_args.end()) {
- seconds = atoi(it->second.c_str());
- }
-
- HeapProfilerStart(FLAGS_heap_profile_path.c_str());
- // Sleep to allow for some samples to be collected.
- SleepFor(MonoDelta::FromSeconds(seconds));
- const char* profile = GetHeapProfile();
- HeapProfilerStop();
- *output << profile;
- delete profile;
-#endif
-}
-
-// pprof asks for the url /pprof/profile?seconds=XX to get cpu-profiling information.
-// The server should respond by calling ProfilerStart(), continuing to do its work,
-// and then, XX seconds later, calling ProfilerStop().
-static void PprofCpuProfileHandler(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
-#ifndef TCMALLOC_ENABLED
- *output << "CPU profiling is not available without tcmalloc.";
-#else
- auto it = req.parsed_args.find("seconds");
- int seconds = kPprofDefaultSampleSecs;
- if (it != req.parsed_args.end()) {
- seconds = atoi(it->second.c_str());
- }
- // Build a temporary file name that is hopefully unique.
- string tmp_prof_file_name = strings::Substitute("/tmp/kudu_cpu_profile.$0.$1", getpid(), rand());
- ProfilerStart(tmp_prof_file_name.c_str());
- SleepFor(MonoDelta::FromSeconds(seconds));
- ProfilerStop();
- ifstream prof_file(tmp_prof_file_name.c_str(), std::ios::in);
- if (!prof_file.is_open()) {
- *output << "Unable to open cpu profile: " << tmp_prof_file_name;
- return;
- }
- *output << prof_file.rdbuf();
- prof_file.close();
-#endif
-}
-
-// pprof asks for the url /pprof/growth to get heap-profiling delta (growth) information.
-// The server should respond by calling:
-// MallocExtension::instance()->GetHeapGrowthStacks(&output);
-static void PprofGrowthHandler(const Webserver::WebRequest& /*req*/,
- Webserver::PrerenderedWebResponse* resp) {
-#ifndef TCMALLOC_ENABLED
- *resp->output << "Growth profiling is not available without tcmalloc.";
-#else
- string heap_growth_stack;
- MallocExtension::instance()->GetHeapGrowthStacks(&heap_growth_stack);
- *resp->output << heap_growth_stack;
-#endif
-}
-
-// Lock contention profiling
-static void PprofContentionHandler(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- ostringstream* output = resp->output;
- string secs_str = FindWithDefault(req.parsed_args, "seconds", "");
- int32_t seconds = ParseLeadingInt32Value(secs_str.c_str(), kPprofDefaultSampleSecs);
- int64_t discarded_samples = 0;
-
- *output << "--- contention" << endl;
- *output << "sampling period = 1" << endl;
- *output << "cycles/second = " << base::CyclesPerSecond() << endl;
-
- MonoTime end = MonoTime::Now() + MonoDelta::FromSeconds(seconds);
- StartSynchronizationProfiling();
- while (MonoTime::Now() < end) {
- SleepFor(MonoDelta::FromMilliseconds(500));
- FlushSynchronizationProfile(output, &discarded_samples);
- }
- StopSynchronizationProfiling();
- FlushSynchronizationProfile(output, &discarded_samples);
-
- // pprof itself ignores this value, but we can at least look at it in the textual
- // output.
- *output << "discarded samples = " << discarded_samples << std::endl;
-
-#if defined(__linux__)
- // procfs only exists on Linux.
- faststring maps;
- ReadFileToString(Env::Default(), "/proc/self/maps", &maps);
- *output << maps.ToString();
-#endif // defined(__linux__)
-}
-
-
-// pprof asks for the url /pprof/symbol to map from hex addresses to variable names.
-// When the server receives a GET request for /pprof/symbol, it should return a line
-// formatted like: num_symbols: ###
-// where ### is the number of symbols found in the binary. For now, the only important
-// distinction is whether the value is 0, which it is for executables that lack debug
-// information, or not-0).
-//
-// In addition to the GET request for this url, the server must accept POST requests.
-// This means that after the HTTP headers, pprof will pass in a list of hex addresses
-// connected by +, like:
-// curl -d '0x0824d061+0x0824d1cf' http://remote_host:80/pprof/symbol
-// The server should read the POST data, which will be in one line, and for each hex value
-// should write one line of output to the output stream, like so:
-// <hex address><tab><function name>
-// For instance:
-// 0x08b2dabd _Update
-static void PprofSymbolHandler(const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- if (req.request_method == "GET") {
- // Per the above comment, pprof doesn't expect to know the actual number of symbols.
- // Any non-zero value indicates that we support symbol lookup.
- *resp->output << "num_symbols: 1";
- return;
- }
-
- int missing_symbols = 0;
- int invalid_addrs = 0;
-
- // Symbolization request.
- vector<StringPiece> pieces = strings::Split(req.post_data, "+");
- for (StringPiece p : pieces) {
- string hex_addr;
- if (!TryStripPrefixString(p, "0x", &hex_addr)) {
- invalid_addrs++;
- continue;
- }
- uint64_t addr;
- if (!safe_strtou64_base(hex_addr.c_str(), &addr, 16)) {
- invalid_addrs++;
- continue;
- }
- char symbol_buf[1024];
- if (google::Symbolize(reinterpret_cast<void*>(addr), symbol_buf, sizeof(symbol_buf))) {
- *resp->output << p << "\t" << symbol_buf << std::endl;
- } else {
- missing_symbols++;
- }
- }
-
- LOG(INFO) << strings::Substitute(
- "Handled request for /pprof/symbol: requested=$0 invalid_addrs=$1 missing=$2",
- pieces.size(), invalid_addrs, missing_symbols);
-}
-
-void AddPprofPathHandlers(Webserver* webserver) {
- // Path handlers for remote pprof profiling. For information see:
- // https://gperftools.googlecode.com/svn/trunk/doc/pprof_remote_servers.html
- webserver->RegisterPrerenderedPathHandler("/pprof/cmdline", "", PprofCmdLineHandler,
- false, false);
- webserver->RegisterPrerenderedPathHandler("/pprof/heap", "", PprofHeapHandler, false, false);
- webserver->RegisterPrerenderedPathHandler("/pprof/growth", "", PprofGrowthHandler, false, false);
- webserver->RegisterPrerenderedPathHandler("/pprof/profile", "", PprofCpuProfileHandler,
- false, false);
- webserver->RegisterPrerenderedPathHandler("/pprof/symbol", "", PprofSymbolHandler, false, false);
- webserver->RegisterPrerenderedPathHandler("/pprof/contention", "", PprofContentionHandler,
- false, false);
-}
-
-} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/pprof-path-handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/server/pprof-path-handlers.h b/src/kudu/server/pprof-path-handlers.h
deleted file mode 100644
index e8c0329..0000000
--- a/src/kudu/server/pprof-path-handlers.h
+++ /dev/null
@@ -1,27 +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_PPROF_DEFAULT_PATH_HANDLERS_H
-#define KUDU_SERVER_PPROF_DEFAULT_PATH_HANDLERS_H
-
-namespace kudu {
-class Webserver;
-
-// Adds set of path handlers to support pprof profiling of a remote server.
-void AddPprofPathHandlers(Webserver* webserver);
-}
-
-#endif // KUDU_SERVER_PPROF_DEFAULT_PATH_HANDLERS_H
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/pprof_path_handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/pprof_path_handlers.cc b/src/kudu/server/pprof_path_handlers.cc
new file mode 100644
index 0000000..1425941
--- /dev/null
+++ b/src/kudu/server/pprof_path_handlers.cc
@@ -0,0 +1,260 @@
+// 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/pprof_path_handlers.h"
+
+#include <unistd.h>
+
+#include <cstdint>
+#include <cstdlib>
+#include <fstream>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <gflags/gflags_declare.h>
+#include <glog/logging.h>
+#include <gperftools/heap-profiler.h>
+#include <gperftools/malloc_extension.h>
+#include <gperftools/profiler.h>
+
+#include "kudu/gutil/map-util.h"
+#include "kudu/gutil/strings/numbers.h"
+#include "kudu/gutil/strings/split.h"
+#include "kudu/gutil/strings/stringpiece.h"
+#include "kudu/gutil/strings/strip.h"
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/gutil/sysinfo.h"
+#include "kudu/server/webserver.h"
+#include "kudu/util/env.h"
+#include "kudu/util/faststring.h"
+#include "kudu/util/monotime.h"
+#include "kudu/util/spinlock_profiling.h"
+#include "kudu/util/status.h"
+#include "kudu/util/web_callback_registry.h"
+
+DECLARE_bool(enable_process_lifetime_heap_profiling);
+DECLARE_string(heap_profile_path);
+
+
+using std::endl;
+using std::ifstream;
+using std::ostringstream;
+using std::string;
+using std::vector;
+
+// GLog already implements symbolization. Just import their hidden symbol.
+namespace google {
+// Symbolizes a program counter. On success, returns true and write the
+// symbol name to "out". The symbol name is demangled if possible
+// (supports symbols generated by GCC 3.x or newer). Otherwise,
+// returns false.
+bool Symbolize(void *pc, char *out, int out_size);
+}
+
+namespace kudu {
+
+const int kPprofDefaultSampleSecs = 30; // pprof default sample time in seconds.
+
+// pprof asks for the url /pprof/cmdline to figure out what application it's profiling.
+// The server should respond by sending the executable path.
+static void PprofCmdLineHandler(const Webserver::WebRequest& /*req*/,
+ Webserver::PrerenderedWebResponse* resp) {
+ string executable_path;
+ Env* env = Env::Default();
+ WARN_NOT_OK(env->GetExecutablePath(&executable_path), "Failed to get executable path");
+ *resp->output << executable_path;
+}
+
+// pprof asks for the url /pprof/heap to get heap information. This should be implemented
+// by calling HeapProfileStart(filename), continue to do work, and then, some number of
+// seconds later, call GetHeapProfile() followed by HeapProfilerStop().
+static void PprofHeapHandler(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+#ifndef TCMALLOC_ENABLED
+ *output << "Heap profiling is not available without tcmalloc.";
+#else
+ // Remote (on-demand) profiling is disabled if the process is already being profiled.
+ if (FLAGS_enable_process_lifetime_heap_profiling) {
+ *output << "Heap profiling is running for the process lifetime.";
+ return;
+ }
+
+ auto it = req.parsed_args.find("seconds");
+ int seconds = kPprofDefaultSampleSecs;
+ if (it != req.parsed_args.end()) {
+ seconds = atoi(it->second.c_str());
+ }
+
+ HeapProfilerStart(FLAGS_heap_profile_path.c_str());
+ // Sleep to allow for some samples to be collected.
+ SleepFor(MonoDelta::FromSeconds(seconds));
+ const char* profile = GetHeapProfile();
+ HeapProfilerStop();
+ *output << profile;
+ delete profile;
+#endif
+}
+
+// pprof asks for the url /pprof/profile?seconds=XX to get cpu-profiling information.
+// The server should respond by calling ProfilerStart(), continuing to do its work,
+// and then, XX seconds later, calling ProfilerStop().
+static void PprofCpuProfileHandler(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+#ifndef TCMALLOC_ENABLED
+ *output << "CPU profiling is not available without tcmalloc.";
+#else
+ auto it = req.parsed_args.find("seconds");
+ int seconds = kPprofDefaultSampleSecs;
+ if (it != req.parsed_args.end()) {
+ seconds = atoi(it->second.c_str());
+ }
+ // Build a temporary file name that is hopefully unique.
+ string tmp_prof_file_name = strings::Substitute("/tmp/kudu_cpu_profile.$0.$1", getpid(), rand());
+ ProfilerStart(tmp_prof_file_name.c_str());
+ SleepFor(MonoDelta::FromSeconds(seconds));
+ ProfilerStop();
+ ifstream prof_file(tmp_prof_file_name.c_str(), std::ios::in);
+ if (!prof_file.is_open()) {
+ *output << "Unable to open cpu profile: " << tmp_prof_file_name;
+ return;
+ }
+ *output << prof_file.rdbuf();
+ prof_file.close();
+#endif
+}
+
+// pprof asks for the url /pprof/growth to get heap-profiling delta (growth) information.
+// The server should respond by calling:
+// MallocExtension::instance()->GetHeapGrowthStacks(&output);
+static void PprofGrowthHandler(const Webserver::WebRequest& /*req*/,
+ Webserver::PrerenderedWebResponse* resp) {
+#ifndef TCMALLOC_ENABLED
+ *resp->output << "Growth profiling is not available without tcmalloc.";
+#else
+ string heap_growth_stack;
+ MallocExtension::instance()->GetHeapGrowthStacks(&heap_growth_stack);
+ *resp->output << heap_growth_stack;
+#endif
+}
+
+// Lock contention profiling
+static void PprofContentionHandler(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ ostringstream* output = resp->output;
+ string secs_str = FindWithDefault(req.parsed_args, "seconds", "");
+ int32_t seconds = ParseLeadingInt32Value(secs_str.c_str(), kPprofDefaultSampleSecs);
+ int64_t discarded_samples = 0;
+
+ *output << "--- contention" << endl;
+ *output << "sampling period = 1" << endl;
+ *output << "cycles/second = " << base::CyclesPerSecond() << endl;
+
+ MonoTime end = MonoTime::Now() + MonoDelta::FromSeconds(seconds);
+ StartSynchronizationProfiling();
+ while (MonoTime::Now() < end) {
+ SleepFor(MonoDelta::FromMilliseconds(500));
+ FlushSynchronizationProfile(output, &discarded_samples);
+ }
+ StopSynchronizationProfiling();
+ FlushSynchronizationProfile(output, &discarded_samples);
+
+ // pprof itself ignores this value, but we can at least look at it in the textual
+ // output.
+ *output << "discarded samples = " << discarded_samples << std::endl;
+
+#if defined(__linux__)
+ // procfs only exists on Linux.
+ faststring maps;
+ ReadFileToString(Env::Default(), "/proc/self/maps", &maps);
+ *output << maps.ToString();
+#endif // defined(__linux__)
+}
+
+
+// pprof asks for the url /pprof/symbol to map from hex addresses to variable names.
+// When the server receives a GET request for /pprof/symbol, it should return a line
+// formatted like: num_symbols: ###
+// where ### is the number of symbols found in the binary. For now, the only important
+// distinction is whether the value is 0, which it is for executables that lack debug
+// information, or not-0).
+//
+// In addition to the GET request for this url, the server must accept POST requests.
+// This means that after the HTTP headers, pprof will pass in a list of hex addresses
+// connected by +, like:
+// curl -d '0x0824d061+0x0824d1cf' http://remote_host:80/pprof/symbol
+// The server should read the POST data, which will be in one line, and for each hex value
+// should write one line of output to the output stream, like so:
+// <hex address><tab><function name>
+// For instance:
+// 0x08b2dabd _Update
+static void PprofSymbolHandler(const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ if (req.request_method == "GET") {
+ // Per the above comment, pprof doesn't expect to know the actual number of symbols.
+ // Any non-zero value indicates that we support symbol lookup.
+ *resp->output << "num_symbols: 1";
+ return;
+ }
+
+ int missing_symbols = 0;
+ int invalid_addrs = 0;
+
+ // Symbolization request.
+ vector<StringPiece> pieces = strings::Split(req.post_data, "+");
+ for (StringPiece p : pieces) {
+ string hex_addr;
+ if (!TryStripPrefixString(p, "0x", &hex_addr)) {
+ invalid_addrs++;
+ continue;
+ }
+ uint64_t addr;
+ if (!safe_strtou64_base(hex_addr.c_str(), &addr, 16)) {
+ invalid_addrs++;
+ continue;
+ }
+ char symbol_buf[1024];
+ if (google::Symbolize(reinterpret_cast<void*>(addr), symbol_buf, sizeof(symbol_buf))) {
+ *resp->output << p << "\t" << symbol_buf << std::endl;
+ } else {
+ missing_symbols++;
+ }
+ }
+
+ LOG(INFO) << strings::Substitute(
+ "Handled request for /pprof/symbol: requested=$0 invalid_addrs=$1 missing=$2",
+ pieces.size(), invalid_addrs, missing_symbols);
+}
+
+void AddPprofPathHandlers(Webserver* webserver) {
+ // Path handlers for remote pprof profiling. For information see:
+ // https://gperftools.googlecode.com/svn/trunk/doc/pprof_remote_servers.html
+ webserver->RegisterPrerenderedPathHandler("/pprof/cmdline", "", PprofCmdLineHandler,
+ false, false);
+ webserver->RegisterPrerenderedPathHandler("/pprof/heap", "", PprofHeapHandler, false, false);
+ webserver->RegisterPrerenderedPathHandler("/pprof/growth", "", PprofGrowthHandler, false, false);
+ webserver->RegisterPrerenderedPathHandler("/pprof/profile", "", PprofCpuProfileHandler,
+ false, false);
+ webserver->RegisterPrerenderedPathHandler("/pprof/symbol", "", PprofSymbolHandler, false, false);
+ webserver->RegisterPrerenderedPathHandler("/pprof/contention", "", PprofContentionHandler,
+ false, false);
+}
+
+} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/pprof_path_handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/server/pprof_path_handlers.h b/src/kudu/server/pprof_path_handlers.h
new file mode 100644
index 0000000..e8c0329
--- /dev/null
+++ b/src/kudu/server/pprof_path_handlers.h
@@ -0,0 +1,27 @@
+// 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_PPROF_DEFAULT_PATH_HANDLERS_H
+#define KUDU_SERVER_PPROF_DEFAULT_PATH_HANDLERS_H
+
+namespace kudu {
+class Webserver;
+
+// Adds set of path handlers to support pprof profiling of a remote server.
+void AddPprofPathHandlers(Webserver* webserver);
+}
+
+#endif // KUDU_SERVER_PPROF_DEFAULT_PATH_HANDLERS_H
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/server_base.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/server_base.cc b/src/kudu/server/server_base.cc
index e17358f..9edd463 100644
--- a/src/kudu/server/server_base.cc
+++ b/src/kudu/server/server_base.cc
@@ -49,7 +49,7 @@
#include "kudu/rpc/rpc_context.h"
#include "kudu/rpc/service_if.h"
#include "kudu/security/init.h"
-#include "kudu/server/default-path-handlers.h"
+#include "kudu/server/default_path_handlers.h"
#include "kudu/server/generic_service.h"
#include "kudu/server/glog_metrics.h"
#include "kudu/server/rpc_server.h"
@@ -57,7 +57,7 @@
#include "kudu/server/server_base.pb.h"
#include "kudu/server/server_base_options.h"
#include "kudu/server/tcmalloc_metrics.h"
-#include "kudu/server/tracing-path-handlers.h"
+#include "kudu/server/tracing_path_handlers.h"
#include "kudu/server/webserver.h"
#include "kudu/util/atomic.h"
#include "kudu/util/env.h"
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/tracing-path-handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/tracing-path-handlers.cc b/src/kudu/server/tracing-path-handlers.cc
deleted file mode 100644
index 7849c13..0000000
--- a/src/kudu/server/tracing-path-handlers.cc
+++ /dev/null
@@ -1,287 +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/tracing-path-handlers.h"
-
-#include <map>
-#include <memory>
-#include <ostream>
-#include <string>
-#include <utility>
-#include <vector>
-
-
-#include <boost/bind.hpp> // IWYU pragma: keep
-#include <glog/logging.h>
-#include <rapidjson/document.h>
-
-#include "kudu/gutil/strings/escaping.h"
-#include "kudu/server/webserver.h"
-#include "kudu/util/debug/trace_event_impl.h"
-#include "kudu/util/jsonwriter.h"
-#include "kudu/util/monotime.h"
-#include "kudu/util/status.h"
-#include "kudu/util/web_callback_registry.h"
-#include "kudu/util/zlib.h"
-
-using std::map;
-using std::ostringstream;
-using std::pair;
-using std::string;
-using std::unique_ptr;
-using std::vector;
-
-using kudu::debug::CategoryFilter;
-using kudu::debug::TraceLog;
-using kudu::debug::TraceResultBuffer;
-
-namespace kudu {
-namespace server {
-
-enum Handler {
- kBeginMonitoring,
- kEndMonitoring,
- kCaptureMonitoring,
- kGetMonitoringStatus,
- kCategories,
- kBeginRecording,
- kGetBufferPercentFull,
- kEndRecording,
- kEndRecordingCompressed,
- kSimpleDump
-};
-
-namespace {
-
-Status ParseBase64JsonRequest(const string& json_base64,
- rapidjson::Document* doc) {
- string json_str;
- if (!strings::Base64Unescape(json_base64, &json_str)) {
- return Status::InvalidArgument("Invalid base64-encoded JSON");
- }
-
- doc->Parse<0>(json_str.c_str());
- if (!doc->IsObject()) {
- return Status::InvalidArgument("Invalid JSON", json_str);
- }
- return Status::OK();
-}
-
-Status GetTracingOptions(const std::string& json_base64,
- std::string* category_filter_string,
- int* tracing_options) {
- rapidjson::Document doc;
- RETURN_NOT_OK(ParseBase64JsonRequest(json_base64, &doc));
-
- bool use_continuous_tracing = false;
- bool use_sampling = false;
-
- if (!doc.HasMember("categoryFilter") ||
- !doc["categoryFilter"].IsString()) {
- return Status::InvalidArgument("missing categoryFilter");
- }
- *category_filter_string = doc["categoryFilter"].GetString();
-
- if (doc.HasMember("useContinuousTracing") &&
- doc["useContinuousTracing"].IsBool()) {
- use_continuous_tracing = doc["useContinuousTracing"].GetBool();
- }
-
- if (doc.HasMember("useSampling") &&
- doc["useSampling"].IsBool()) {
- use_sampling = doc["useSampling"].GetBool();
- }
-
- *tracing_options = 0;
- if (use_sampling)
- *tracing_options |= TraceLog::ENABLE_SAMPLING;
- if (use_continuous_tracing)
- *tracing_options |= TraceLog::RECORD_CONTINUOUSLY;
- return Status::OK();
-}
-
-Status BeginRecording(const Webserver::WebRequest& req,
- TraceLog::Mode mode) {
- string filter_str;
- int options;
- RETURN_NOT_OK(GetTracingOptions(req.query_string, &filter_str, &options));
-
- kudu::debug::TraceLog::GetInstance()->SetEnabled(
- CategoryFilter(filter_str),
- mode,
- static_cast<TraceLog::Options>(options));
- return Status::OK();
-}
-
-Status EndRecording(const Webserver::WebRequest& req,
- bool compressed,
- ostringstream* out) {
- TraceLog* tl = TraceLog::GetInstance();
- tl->SetDisabled();
- string json = TraceResultBuffer::FlushTraceLogToString();
-
- if (compressed) {
- RETURN_NOT_OK_PREPEND(zlib::Compress(json, out),
- "Could not compress output");
- } else {
- *out << json;
- }
-
- return Status::OK();
-}
-
-Status CaptureMonitoring(ostringstream* out) {
- TraceLog* tl = TraceLog::GetInstance();
- if (!tl->IsEnabled()) {
- return Status::IllegalState("monitoring not enabled");
- }
- *out << TraceResultBuffer::FlushTraceLogToStringButLeaveBufferIntact();
- return Status::OK();
-}
-
-void GetCategories(ostringstream* out) {
- vector<string> groups;
- kudu::debug::TraceLog::GetInstance()->GetKnownCategoryGroups(&groups);
- JsonWriter j(out, JsonWriter::COMPACT);
- j.StartArray();
- for (const string& g : groups) {
- j.String(g);
- }
- j.EndArray();
-}
-
-void GetMonitoringStatus(ostringstream* out) {
- TraceLog* tl = TraceLog::GetInstance();
- bool is_monitoring = tl->IsEnabled();
- std::string category_filter = tl->GetCurrentCategoryFilter().ToString();
- int options = static_cast<int>(tl->trace_options());
-
- ostringstream json_out;
- JsonWriter j(&json_out, JsonWriter::COMPACT);
- j.StartObject();
-
- j.String("isMonitoring");
- j.Bool(is_monitoring);
-
- j.String("categoryFilter");
- j.String(category_filter);
-
- j.String("useContinuousTracing");
- j.Bool((options & TraceLog::RECORD_CONTINUOUSLY) != 0);
-
- j.String("useSampling");
- j.Bool((options & TraceLog::ENABLE_SAMPLING) != 0);
-
- j.EndObject();
-
- string encoded;
- strings::Base64Escape(json_out.str(), &encoded);
- *out << encoded;
-}
-
-void HandleTraceJsonPage(const Webserver::ArgumentMap &args,
- std::ostringstream* output) {
- TraceLog* tl = TraceLog::GetInstance();
- tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString),
- TraceLog::RECORDING_MODE,
- TraceLog::RECORD_CONTINUOUSLY);
- SleepFor(MonoDelta::FromSeconds(10));
- tl->SetDisabled();
-
- *output << TraceResultBuffer::FlushTraceLogToString();
-}
-
-Status DoHandleRequest(Handler handler,
- const Webserver::WebRequest& req,
- std::ostringstream* output) {
- VLOG(2) << "Tracing request type=" << handler << ": " << req.query_string;
-
- switch (handler) {
- case kBeginMonitoring:
- RETURN_NOT_OK(BeginRecording(req, TraceLog::MONITORING_MODE));
- break;
- case kCaptureMonitoring:
- RETURN_NOT_OK(CaptureMonitoring(output));
- break;
- case kGetMonitoringStatus:
- GetMonitoringStatus(output);
- break;
- case kCategories:
- GetCategories(output);
- break;
- case kBeginRecording:
- RETURN_NOT_OK(BeginRecording(req, TraceLog::RECORDING_MODE));
- break;
- case kGetBufferPercentFull:
- *output << TraceLog::GetInstance()->GetBufferPercentFull();
- break;
- case kEndMonitoring:
- case kEndRecording:
- RETURN_NOT_OK(EndRecording(req, false, output));
- break;
- case kEndRecordingCompressed:
- RETURN_NOT_OK(EndRecording(req, true, output));
- break;
- case kSimpleDump:
- HandleTraceJsonPage(req.parsed_args, output);
- break;
- }
-
- return Status::OK();
-}
-
-
-void HandleRequest(Handler handler,
- const Webserver::WebRequest& req,
- Webserver::PrerenderedWebResponse* resp) {
- Status s = DoHandleRequest(handler, req, resp->output);
- if (!s.ok()) {
- LOG(WARNING) << "Tracing error for handler " << handler << ": "
- << s.ToString();
- // The trace-viewer JS expects '##ERROR##' to indicate that an error
- // occurred. TODO: change the JS to bubble up the actual error message
- // to the user.
- *resp->output << "##ERROR##";
- }
-}
-} // anonymous namespace
-
-
-void TracingPathHandlers::RegisterHandlers(Webserver* server) {
- // All of the tracing-related hand
- std::map<string, Handler> handlers = {
- { "/tracing/json/begin_monitoring", kBeginMonitoring },
- { "/tracing/json/end_monitoring", kEndMonitoring },
- { "/tracing/json/capture_monitoring", kCaptureMonitoring },
- { "/tracing/json/get_monitoring_status", kGetMonitoringStatus },
- { "/tracing/json/categories", kCategories },
- { "/tracing/json/begin_recording", kBeginRecording },
- { "/tracing/json/get_buffer_percent_full", kGetBufferPercentFull },
- { "/tracing/json/end_recording", kEndRecording },
- { "/tracing/json/end_recording_compressed", kEndRecordingCompressed },
- { "/tracing/json/simple_dump", kSimpleDump } };
-
- typedef pair<const string, Handler> HandlerPair;
- for (const HandlerPair& e : handlers) {
- server->RegisterPrerenderedPathHandler(
- e.first, "",
- boost::bind(&HandleRequest, e.second, _1, _2),
- false /* styled */, false /* is_on_nav_bar */);
- }
-}
-
-} // namespace server
-} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/tracing-path-handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/server/tracing-path-handlers.h b/src/kudu/server/tracing-path-handlers.h
deleted file mode 100644
index 7e1feef..0000000
--- a/src/kudu/server/tracing-path-handlers.h
+++ /dev/null
@@ -1,40 +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_TRACING_PATH_HANDLERS_H
-#define KUDU_SERVER_TRACING_PATH_HANDLERS_H
-
-#include "kudu/gutil/macros.h"
-
-namespace kudu {
-
-class Webserver;
-
-namespace server {
-
-// Web handlers for Chromium tracing.
-// These handlers provide AJAX endpoints for /tracing.html provided by
-// the trace-viewer package.
-class TracingPathHandlers {
- public:
- static void RegisterHandlers(Webserver* server);
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(TracingPathHandlers);
-};
-
-} // namespace server
-} // namespace kudu
-#endif /* KUDU_SERVER_TRACING_PATH_HANDLERS_H */
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/tracing_path_handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/tracing_path_handlers.cc b/src/kudu/server/tracing_path_handlers.cc
new file mode 100644
index 0000000..5e57212
--- /dev/null
+++ b/src/kudu/server/tracing_path_handlers.cc
@@ -0,0 +1,285 @@
+// 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/tracing_path_handlers.h"
+
+#include <map>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+
+#include <boost/bind.hpp> // IWYU pragma: keep
+#include <glog/logging.h>
+#include <rapidjson/document.h>
+
+#include "kudu/gutil/strings/escaping.h"
+#include "kudu/server/webserver.h"
+#include "kudu/util/debug/trace_event_impl.h"
+#include "kudu/util/jsonwriter.h"
+#include "kudu/util/monotime.h"
+#include "kudu/util/status.h"
+#include "kudu/util/web_callback_registry.h"
+#include "kudu/util/zlib.h"
+
+using std::map;
+using std::ostringstream;
+using std::pair;
+using std::string;
+using std::vector;
+
+using kudu::debug::CategoryFilter;
+using kudu::debug::TraceLog;
+using kudu::debug::TraceResultBuffer;
+
+namespace kudu {
+namespace server {
+
+enum Handler {
+ kBeginMonitoring,
+ kEndMonitoring,
+ kCaptureMonitoring,
+ kGetMonitoringStatus,
+ kCategories,
+ kBeginRecording,
+ kGetBufferPercentFull,
+ kEndRecording,
+ kEndRecordingCompressed,
+ kSimpleDump
+};
+
+namespace {
+
+Status ParseBase64JsonRequest(const string& json_base64,
+ rapidjson::Document* doc) {
+ string json_str;
+ if (!strings::Base64Unescape(json_base64, &json_str)) {
+ return Status::InvalidArgument("Invalid base64-encoded JSON");
+ }
+
+ doc->Parse<0>(json_str.c_str());
+ if (!doc->IsObject()) {
+ return Status::InvalidArgument("Invalid JSON", json_str);
+ }
+ return Status::OK();
+}
+
+Status GetTracingOptions(const std::string& json_base64,
+ std::string* category_filter_string,
+ int* tracing_options) {
+ rapidjson::Document doc;
+ RETURN_NOT_OK(ParseBase64JsonRequest(json_base64, &doc));
+
+ bool use_continuous_tracing = false;
+ bool use_sampling = false;
+
+ if (!doc.HasMember("categoryFilter") ||
+ !doc["categoryFilter"].IsString()) {
+ return Status::InvalidArgument("missing categoryFilter");
+ }
+ *category_filter_string = doc["categoryFilter"].GetString();
+
+ if (doc.HasMember("useContinuousTracing") &&
+ doc["useContinuousTracing"].IsBool()) {
+ use_continuous_tracing = doc["useContinuousTracing"].GetBool();
+ }
+
+ if (doc.HasMember("useSampling") &&
+ doc["useSampling"].IsBool()) {
+ use_sampling = doc["useSampling"].GetBool();
+ }
+
+ *tracing_options = 0;
+ if (use_sampling)
+ *tracing_options |= TraceLog::ENABLE_SAMPLING;
+ if (use_continuous_tracing)
+ *tracing_options |= TraceLog::RECORD_CONTINUOUSLY;
+ return Status::OK();
+}
+
+Status BeginRecording(const Webserver::WebRequest& req,
+ TraceLog::Mode mode) {
+ string filter_str;
+ int options;
+ RETURN_NOT_OK(GetTracingOptions(req.query_string, &filter_str, &options));
+
+ kudu::debug::TraceLog::GetInstance()->SetEnabled(
+ CategoryFilter(filter_str),
+ mode,
+ static_cast<TraceLog::Options>(options));
+ return Status::OK();
+}
+
+Status EndRecording(const Webserver::WebRequest& /*req*/,
+ bool compressed,
+ ostringstream* out) {
+ TraceLog* tl = TraceLog::GetInstance();
+ tl->SetDisabled();
+ string json = TraceResultBuffer::FlushTraceLogToString();
+
+ if (compressed) {
+ RETURN_NOT_OK_PREPEND(zlib::Compress(json, out),
+ "Could not compress output");
+ } else {
+ *out << json;
+ }
+
+ return Status::OK();
+}
+
+Status CaptureMonitoring(ostringstream* out) {
+ TraceLog* tl = TraceLog::GetInstance();
+ if (!tl->IsEnabled()) {
+ return Status::IllegalState("monitoring not enabled");
+ }
+ *out << TraceResultBuffer::FlushTraceLogToStringButLeaveBufferIntact();
+ return Status::OK();
+}
+
+void GetCategories(ostringstream* out) {
+ vector<string> groups;
+ kudu::debug::TraceLog::GetInstance()->GetKnownCategoryGroups(&groups);
+ JsonWriter j(out, JsonWriter::COMPACT);
+ j.StartArray();
+ for (const string& g : groups) {
+ j.String(g);
+ }
+ j.EndArray();
+}
+
+void GetMonitoringStatus(ostringstream* out) {
+ TraceLog* tl = TraceLog::GetInstance();
+ bool is_monitoring = tl->IsEnabled();
+ std::string category_filter = tl->GetCurrentCategoryFilter().ToString();
+ int options = static_cast<int>(tl->trace_options());
+
+ ostringstream json_out;
+ JsonWriter j(&json_out, JsonWriter::COMPACT);
+ j.StartObject();
+
+ j.String("isMonitoring");
+ j.Bool(is_monitoring);
+
+ j.String("categoryFilter");
+ j.String(category_filter);
+
+ j.String("useContinuousTracing");
+ j.Bool((options & TraceLog::RECORD_CONTINUOUSLY) != 0);
+
+ j.String("useSampling");
+ j.Bool((options & TraceLog::ENABLE_SAMPLING) != 0);
+
+ j.EndObject();
+
+ string encoded;
+ strings::Base64Escape(json_out.str(), &encoded);
+ *out << encoded;
+}
+
+void HandleTraceJsonPage(const Webserver::ArgumentMap& /*args*/,
+ std::ostringstream* output) {
+ TraceLog* tl = TraceLog::GetInstance();
+ tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString),
+ TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_CONTINUOUSLY);
+ SleepFor(MonoDelta::FromSeconds(10));
+ tl->SetDisabled();
+
+ *output << TraceResultBuffer::FlushTraceLogToString();
+}
+
+Status DoHandleRequest(Handler handler,
+ const Webserver::WebRequest& req,
+ std::ostringstream* output) {
+ VLOG(2) << "Tracing request type=" << handler << ": " << req.query_string;
+
+ switch (handler) {
+ case kBeginMonitoring:
+ RETURN_NOT_OK(BeginRecording(req, TraceLog::MONITORING_MODE));
+ break;
+ case kCaptureMonitoring:
+ RETURN_NOT_OK(CaptureMonitoring(output));
+ break;
+ case kGetMonitoringStatus:
+ GetMonitoringStatus(output);
+ break;
+ case kCategories:
+ GetCategories(output);
+ break;
+ case kBeginRecording:
+ RETURN_NOT_OK(BeginRecording(req, TraceLog::RECORDING_MODE));
+ break;
+ case kGetBufferPercentFull:
+ *output << TraceLog::GetInstance()->GetBufferPercentFull();
+ break;
+ case kEndMonitoring:
+ case kEndRecording:
+ RETURN_NOT_OK(EndRecording(req, false, output));
+ break;
+ case kEndRecordingCompressed:
+ RETURN_NOT_OK(EndRecording(req, true, output));
+ break;
+ case kSimpleDump:
+ HandleTraceJsonPage(req.parsed_args, output);
+ break;
+ }
+
+ return Status::OK();
+}
+
+
+void HandleRequest(Handler handler,
+ const Webserver::WebRequest& req,
+ Webserver::PrerenderedWebResponse* resp) {
+ Status s = DoHandleRequest(handler, req, resp->output);
+ if (!s.ok()) {
+ LOG(WARNING) << "Tracing error for handler " << handler << ": "
+ << s.ToString();
+ // The trace-viewer JS expects '##ERROR##' to indicate that an error
+ // occurred. TODO: change the JS to bubble up the actual error message
+ // to the user.
+ *resp->output << "##ERROR##";
+ }
+}
+} // anonymous namespace
+
+
+void TracingPathHandlers::RegisterHandlers(Webserver* server) {
+ // All of the tracing-related hand
+ std::map<string, Handler> handlers = {
+ { "/tracing/json/begin_monitoring", kBeginMonitoring },
+ { "/tracing/json/end_monitoring", kEndMonitoring },
+ { "/tracing/json/capture_monitoring", kCaptureMonitoring },
+ { "/tracing/json/get_monitoring_status", kGetMonitoringStatus },
+ { "/tracing/json/categories", kCategories },
+ { "/tracing/json/begin_recording", kBeginRecording },
+ { "/tracing/json/get_buffer_percent_full", kGetBufferPercentFull },
+ { "/tracing/json/end_recording", kEndRecording },
+ { "/tracing/json/end_recording_compressed", kEndRecordingCompressed },
+ { "/tracing/json/simple_dump", kSimpleDump } };
+
+ typedef pair<const string, Handler> HandlerPair;
+ for (const HandlerPair& e : handlers) {
+ server->RegisterPrerenderedPathHandler(
+ e.first, "",
+ boost::bind(&HandleRequest, e.second, _1, _2),
+ false /* styled */, false /* is_on_nav_bar */);
+ }
+}
+
+} // namespace server
+} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/tracing_path_handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/server/tracing_path_handlers.h b/src/kudu/server/tracing_path_handlers.h
new file mode 100644
index 0000000..7e1feef
--- /dev/null
+++ b/src/kudu/server/tracing_path_handlers.h
@@ -0,0 +1,40 @@
+// 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_TRACING_PATH_HANDLERS_H
+#define KUDU_SERVER_TRACING_PATH_HANDLERS_H
+
+#include "kudu/gutil/macros.h"
+
+namespace kudu {
+
+class Webserver;
+
+namespace server {
+
+// Web handlers for Chromium tracing.
+// These handlers provide AJAX endpoints for /tracing.html provided by
+// the trace-viewer package.
+class TracingPathHandlers {
+ public:
+ static void RegisterHandlers(Webserver* server);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TracingPathHandlers);
+};
+
+} // namespace server
+} // namespace kudu
+#endif /* KUDU_SERVER_TRACING_PATH_HANDLERS_H */
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/server/webserver-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/webserver-test.cc b/src/kudu/server/webserver-test.cc
index d8b3ef4..ca80822 100644
--- a/src/kudu/server/webserver-test.cc
+++ b/src/kudu/server/webserver-test.cc
@@ -31,7 +31,7 @@
#include "kudu/gutil/strings/util.h"
#include "kudu/security/test/test_certs.h"
#include "kudu/security/test/test_pass.h"
-#include "kudu/server/default-path-handlers.h"
+#include "kudu/server/default_path_handlers.h"
#include "kudu/server/webserver.h"
#include "kudu/server/webserver_options.h"
#include "kudu/util/curl_util.h"
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/tserver/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/CMakeLists.txt b/src/kudu/tserver/CMakeLists.txt
index c848f95..e062e2d 100644
--- a/src/kudu/tserver/CMakeLists.txt
+++ b/src/kudu/tserver/CMakeLists.txt
@@ -114,7 +114,7 @@ set(TSERVER_SRCS
tablet_server_options.cc
tablet_service.cc
ts_tablet_manager.cc
- tserver-path-handlers.cc
+ tserver_path_handlers.cc
)
add_library(tserver ${TSERVER_SRCS})
http://git-wip-us.apache.org/repos/asf/kudu/blob/1c90b2f5/src/kudu/tserver/tablet_server.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/tablet_server.cc b/src/kudu/tserver/tablet_server.cc
index 2beea2b..95b2b1e 100644
--- a/src/kudu/tserver/tablet_server.cc
+++ b/src/kudu/tserver/tablet_server.cc
@@ -36,7 +36,7 @@
#include "kudu/tserver/tablet_copy_service.h"
#include "kudu/tserver/tablet_service.h"
#include "kudu/tserver/ts_tablet_manager.h"
-#include "kudu/tserver/tserver-path-handlers.h"
+#include "kudu/tserver/tserver_path_handlers.h"
#include "kudu/util/maintenance_manager.h"
#include "kudu/util/net/net_util.h"
#include "kudu/util/status.h"