You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by al...@apache.org on 2017/10/17 15:23:19 UTC

[2/5] kudu git commit: [webui] Add templates for master web ui.

[webui]  Add templates for master web ui.

This adds templates to the rest of the master webui endpoints for which
it makes sense:
- /tablet-servers
- /masters

Change-Id: I4520261dd617a160da51cdd089f42f078a0da728
Reviewed-on: http://gerrit.cloudera.org:8080/8227
Tested-by: Kudu Jenkins
Reviewed-by: Todd Lipcon <to...@apache.org>


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

Branch: refs/heads/master
Commit: 33c06692e60d0347063fb92a0affddd471e06b3f
Parents: c18815f
Author: Will Berkeley <wd...@apache.org>
Authored: Sat Sep 16 23:35:21 2017 -0700
Committer: Todd Lipcon <to...@apache.org>
Committed: Tue Oct 17 04:36:24 2017 +0000

----------------------------------------------------------------------
 src/kudu/master/master-path-handlers.cc | 140 +++++++++++----------------
 src/kudu/master/master-path-handlers.h  |  11 +--
 www/masters.mustache                    |  45 +++++++++
 www/tables.mustache                     |   2 +-
 www/tablet-servers.mustache             |  81 ++++++++++++++++
 5 files changed, 185 insertions(+), 94 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/33c06692/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
index 28b604a..397776b 100644
--- a/src/kudu/master/master-path-handlers.cc
+++ b/src/kudu/master/master-path-handlers.cc
@@ -86,63 +86,53 @@ MasterPathHandlers::~MasterPathHandlers() {
 }
 
 void MasterPathHandlers::HandleTabletServers(const Webserver::WebRequest& /*req*/,
-                                             Webserver::PrerenderedWebResponse* resp) {
-  ostringstream* output = resp->output;
-  vector<std::shared_ptr<TSDescriptor> > descs;
+                                             Webserver::WebResponse* resp) {
+  EasyJson* output = resp->output;
+  vector<std::shared_ptr<TSDescriptor>> descs;
   master_->ts_manager()->GetAllDescriptors(&descs);
 
-  *output << "<h1>Tablet Servers</h1>\n";
-  *output << Substitute("<p>There are $0 registered tablet servers.</p>", descs.size());
-
+  (*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;
-  vector<string> live_tserver_rows;
-  vector<string> dead_tserver_rows;
   for (const std::shared_ptr<TSDescriptor>& desc : descs) {
-    const string time_since_hb = StringPrintf("%.1fs", desc->TimeSinceHeartbeat().ToSeconds());
+    string ts_key = desc->PresumedDead() ? "dead_tservers" : "live_tservers";
+    EasyJson ts_json = (*output)[ts_key].PushBack(EasyJson::kObject);
+
     ServerRegistrationPB reg;
     desc->GetRegistration(&reg);
-
-    string row = Substitute("<tr><th>$0</th><td>$1</td><td><pre><code>$2</code></pre></td></tr>\n",
-                            RegistrationToHtml(reg, desc->permanent_uuid()),
-                            time_since_hb,
-                            EscapeForHtmlToString(pb_util::SecureShortDebugString(reg)));
-
-    if (desc->PresumedDead()) {
-      version_counts[reg.software_version()][1]++;
-      dead_tserver_rows.push_back(row);
-    } else {
-      version_counts[reg.software_version()][0]++;
-      live_tserver_rows.push_back(row);
+    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 << "<h3>Version Summary</h3>";
-  *output << "<table class='table table-striped'>\n";
-  *output << "<thead><tr><th>Version</th><th>Count (Live)</th><th>Count (Dead)</th></tr></thead>\n";
-  *output << "<tbody>\n";
+  output->Set("version_counts", EasyJson::kArray);
   for (const auto& entry : version_counts) {
-    *output << Substitute("<tr><td>$0</td><td>$1</td><td>$2</td></tr>\n",
-                          entry.first, entry.second[0], entry.second[1]);
+    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]);
   }
-  *output << "</tbody></table>\n";
-
-  *output << "<h3>" << "Registrations" << "</h3>\n";
-  auto generate_table = [](const vector<string>& rows,
-                           const string& header,
-                           std::ostream* output) {
-    if (!rows.empty()) {
-      *output << "<h4>" << header << "</h4>\n";
-      *output << "<table class='table table-striped'>\n";
-      *output << "<thead><tr><th>UUID</th><th>Time since heartbeat</th>"
-          "<th>Registration</th></tr></thead>\n";
-      *output << "<tbody>\n";
-      *output << JoinStrings(rows, "\n");
-      *output << "</tbody></table>\n";
-    }
-  };
-  generate_table(live_tserver_rows, "Live Tablet Servers", output);
-  generate_table(dead_tserver_rows, "Dead Tablet Servers", output);
 }
 
 namespace {
@@ -387,40 +377,35 @@ void MasterPathHandlers::HandleTablePage(const Webserver::WebRequest& req,
 }
 
 void MasterPathHandlers::HandleMasters(const Webserver::WebRequest& /*req*/,
-                                       Webserver::PrerenderedWebResponse* resp) {
-  ostringstream* output = resp->output;
+                                       Webserver::WebResponse* resp) {
+  EasyJson* output = resp->output;
   vector<ServerEntryPB> masters;
   Status s = master_->ListMasters(&masters);
   if (!s.ok()) {
-    s = s.CloneAndPrepend("Unable to list Masters");
-    LOG(WARNING) << s.ToString();
-    *output << "<h1>" << s.ToString() << "</h1>\n";
+    string msg = s.CloneAndPrepend("Unable to list Masters").ToString();
+    LOG(WARNING) << msg;
+    (*output)["error"] = msg;
     return;
   }
-  *output << "<h1> Masters </h1>\n";
-  *output <<  "<table class='table table-striped'>\n";
-  *output <<  "  <thead><tr><th>UUID</th><th>Role</th><th>Registration</th></tr></thead>\n";
-  *output << "<tbody>\n";
+  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());
-      *output << Substitute("  <tr><td colspan=2><font color='red'><b>$0</b></font></td></tr>\n",
-                            EscapeForHtmlToString(error.ToString()));
+      master_json["error"] = error.ToString();
       continue;
     }
-    string uuid_text = RegistrationToHtml(
-        master.registration(),
-        master.instance_id().permanent_uuid());
-    string reg_str = EscapeForHtmlToString(
-        pb_util::SecureShortDebugString(master.registration()));
-    *output << Substitute(
-        "  <tr><td>$0</td><td>$1</td><td><pre><code>$2</code></pre></td></tr>\n",
-        uuid_text,
-        master.has_role() ? RaftPeerPB_Role_Name(master.role()) : "N/A",
-        reg_str);
+    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());
   }
-
-  *output << "</tbody></table>";
 }
 
 namespace {
@@ -607,7 +592,7 @@ void MasterPathHandlers::HandleDumpEntities(const Webserver::WebRequest& /*req*/
 Status MasterPathHandlers::Register(Webserver* server) {
   bool is_styled = true;
   bool is_on_nav_bar = true;
-  server->RegisterPrerenderedPathHandler(
+  server->RegisterPathHandler(
       "/tablet-servers", "Tablet Servers",
       boost::bind(&MasterPathHandlers::HandleTabletServers, this, _1, _2),
       is_styled, is_on_nav_bar);
@@ -619,7 +604,7 @@ Status MasterPathHandlers::Register(Webserver* server) {
       "/table", "",
       boost::bind(&MasterPathHandlers::HandleTablePage, this, _1, _2),
       is_styled, false);
-  server->RegisterPrerenderedPathHandler(
+  server->RegisterPathHandler(
       "/masters", "Masters",
       boost::bind(&MasterPathHandlers::HandleMasters, this, _1, _2),
       is_styled, is_on_nav_bar);
@@ -646,19 +631,6 @@ pair<string, string> MasterPathHandlers::TSDescToLinkPair(const TSDescriptor& de
   return std::make_pair(std::move(text), std::move(target));
 }
 
-string MasterPathHandlers::RegistrationToHtml(
-    const ServerRegistrationPB& reg,
-    const std::string& link_text) const {
-  string link_html = EscapeForHtmlToString(link_text);
-  if (reg.http_addresses().size() > 0) {
-    link_html = Substitute("<a href=\"$0://$1:$2/\">$3</a>",
-                           reg.https_enabled() ? "https" : "http",
-                           reg.http_addresses(0).host(),
-                           reg.http_addresses(0).port(), link_html);
-  }
-  return link_html;
-}
-
 string MasterPathHandlers::MasterAddrsToCsv() const {
   if (master_->opts().IsDistributed()) {
     vector<string> all_addresses;

http://git-wip-us.apache.org/repos/asf/kudu/blob/33c06692/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
index 280b397..186e6d3 100644
--- a/src/kudu/master/master-path-handlers.h
+++ b/src/kudu/master/master-path-handlers.h
@@ -26,7 +26,6 @@
 
 namespace kudu {
 class EasyJson;
-class ServerRegistrationPB;
 
 namespace master {
 
@@ -46,13 +45,13 @@ class MasterPathHandlers {
 
  private:
   void HandleTabletServers(const Webserver::WebRequest& req,
-                           Webserver::PrerenderedWebResponse* resp);
+                           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::PrerenderedWebResponse* resp);
+                     Webserver::WebResponse* resp);
   void HandleDumpEntities(const Webserver::WebRequest& req,
                           Webserver::PrerenderedWebResponse* resp);
 
@@ -63,12 +62,6 @@ class MasterPathHandlers {
   std::pair<std::string, std::string> TSDescToLinkPair(const TSDescriptor& desc,
                                                        const std::string& tablet_id) const;
 
-  // Convert the specified server registration to HTML, adding a link
-  // to the server's own web server (if specified in 'reg') with
-  // anchor text 'link_text'.
-  std::string RegistrationToHtml(const ServerRegistrationPB& reg,
-                                 const std::string& link_text) const;
-
   // Return a CSV of master addresses suitable for display.
   std::string MasterAddrsToCsv() const;
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/33c06692/www/masters.mustache
----------------------------------------------------------------------
diff --git a/www/masters.mustache b/www/masters.mustache
new file mode 100644
index 0000000..3b79425
--- /dev/null
+++ b/www/masters.mustache
@@ -0,0 +1,45 @@
+{{!
+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.
+}}
+{{#error}}
+  <div class="text-error">{{.}}</div>
+{{/error}}
+{{^error}}
+  <h2>Masters</h2>
+  <table class='table table-striped'>
+    <thead><tr>
+      <th>UUID</th>
+      <th>Role</th>
+      <th>Registration</th>
+    </tr></thead>
+    <tbody>
+    {{#masters}}
+      <tr>
+        {{#error}}
+          <td colspan=2><b style="color:red">{{error}}</b></td>
+        {{/error}}
+        {{^error}}
+          <td>{{#target}}<a href="{{.}}">{{/target}}{{uuid}}{{#target}}</a>{{/target}}</td>
+          <td>{{role}}</td>
+          <td><pre><code>{{registration}}</code></pre></td>
+        {{/error}}
+      </tr>
+    {{/masters}}
+    </tbody>
+  </table>
+{{/error}}

http://git-wip-us.apache.org/repos/asf/kudu/blob/33c06692/www/tables.mustache
----------------------------------------------------------------------
diff --git a/www/tables.mustache b/www/tables.mustache
index e50fd8c..0523aa3 100644
--- a/www/tables.mustache
+++ b/www/tables.mustache
@@ -27,7 +27,7 @@ under the License.
   <div>You can find this page on the <a href="{{{.}}}">leader master's web UI</a>.</div>
 {{/leader_redirect}}
 {{^error}}
-There are {{num_tables}} tables
+There are {{num_tables}} tables.
 <table class="table table-striped">
   <thead><tr>
     <th>Table Name</th>

http://git-wip-us.apache.org/repos/asf/kudu/blob/33c06692/www/tablet-servers.mustache
----------------------------------------------------------------------
diff --git a/www/tablet-servers.mustache b/www/tablet-servers.mustache
new file mode 100644
index 0000000..539c90b
--- /dev/null
+++ b/www/tablet-servers.mustache
@@ -0,0 +1,81 @@
+{{!
+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.
+}}
+{{#error}}
+  <div class="text-error">{{.}}</div>
+{{/error}}
+{{^error}}
+  <h2>Tablet Servers</h2>
+  <p>There are {{num_ts}} registered tablet servers.</p>
+  <h3>Version Summary</h3>
+  <table class='table table-striped'>
+    <thead><tr>
+      <th>Version</th>
+      <th>Count (Live)</th>
+      <th>Count (Dead)</th>
+    </tr></thead>
+    <tbody>
+    {{#version_counts}}
+    <tr>
+      <td>{{version}}</td>
+      <td>{{live}}</td>
+      <td>{{dead}}</td>
+    </tr>
+    {{/version_counts}}
+    </tbody>
+  </table>
+  <h3>Registrations</h3>
+  {{^has_no_live_ts}}
+  <h4>Live Tablet Servers</h4>
+  <table class='table table-striped'>
+    <thead><tr>
+      <th>UUID</th>
+      <th>Time since heartbeat</th>
+      <th>Registration</th>
+    </tr></thead>
+    <tbody>
+    {{#live_tservers}}
+      <tr>
+        <td>{{#target}}<a href="{{.}}">{{/target}}{{uuid}}{{#target}}</a>{{/target}}</td>
+        <td>{{time_since_hb}}</td>
+        <td><pre><code>{{registration}}</code></pre></td>
+      </tr>
+    {{/live_tservers}}
+    </tbody>
+  </table>
+  {{/has_no_live_ts}}
+  {{^has_no_dead_ts}}
+    <h4>Dead Tablet Servers</h4>
+    <table class='table table-striped'>
+      <thead><tr>
+        <th>UUID</th>
+        <th>Time since heartbeat</th>
+        <th>Registration</th>
+      </tr></thead>
+    <tbody>
+    {{#dead_tservers}}
+      <tr>
+        <td>{{#target}}<a href="{{.}}">{{/target}}{{uuid}}{{#target}}</a>{{/target}}</td>
+        <td>{{time_since_hb}}</td>
+        <td><pre><code>{{registration}}</code></pre></td>
+      </tr>
+    {{/dead_tservers}}
+    </tbody>
+    </table>
+  {{/has_no_dead_ts}}
+{{/error}}