YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/master/master-path-handlers.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
33
#include "yb/master/master-path-handlers.h"
34
35
#include <algorithm>
36
#include <functional>
37
#include <iomanip>
38
#include <map>
39
#include <sstream>
40
#include <unordered_set>
41
42
#include "yb/common/hybrid_time.h"
43
#include "yb/common/partition.h"
44
#include "yb/common/schema.h"
45
#include "yb/common/transaction.h"
46
#include "yb/common/wire_protocol.h"
47
48
#include "yb/gutil/map-util.h"
49
#include "yb/gutil/stringprintf.h"
50
#include "yb/gutil/strings/join.h"
51
#include "yb/gutil/strings/numbers.h"
52
#include "yb/gutil/strings/substitute.h"
53
54
#include "yb/master/master_fwd.h"
55
#include "yb/master/catalog_entity_info.h"
56
#include "yb/master/catalog_manager_if.h"
57
#include "yb/master/master.h"
58
#include "yb/master/master_cluster.pb.h"
59
#include "yb/master/master_util.h"
60
#include "yb/master/scoped_leader_shared_lock.h"
61
#include "yb/master/sys_catalog.h"
62
#include "yb/master/ts_descriptor.h"
63
#include "yb/master/ts_manager.h"
64
65
#include "yb/server/webserver.h"
66
#include "yb/server/webui_util.h"
67
68
#include "yb/util/curl_util.h"
69
#include "yb/util/jsonwriter.h"
70
#include "yb/util/status_log.h"
71
#include "yb/util/string_case.h"
72
#include "yb/util/timestamp.h"
73
#include "yb/util/url-coding.h"
74
#include "yb/util/version_info.h"
75
#include "yb/util/version_info.pb.h"
76
77
DEFINE_int32(
78
    hide_dead_node_threshold_mins, 60 * 24,
79
    "After this many minutes of no heartbeat from a node, hide it from the UI "
80
    "(we presume it has been removed from the cluster). If -1, this flag is ignored and node is "
81
    "never hidden from the UI");
82
83
DECLARE_int32(ysql_tablespace_info_refresh_secs);
84
85
namespace yb {
86
87
namespace {
88
static constexpr const char* kDBTypeNameUnknown = "unknown";
89
static constexpr const char* kDBTypeNameCql = "ycql";
90
static constexpr const char* kDBTypeNamePgsql = "ysql";
91
static constexpr const char* kDBTypeNameRedis = "yedis";
92
93
const int64_t kCurlTimeoutSec = 180;
94
95
4
const char* DatabaseTypeName(YQLDatabase db) {
96
4
  switch (db) {
97
0
    case YQL_DATABASE_UNKNOWN: break;
98
4
    case YQL_DATABASE_CQL: return kDBTypeNameCql;
99
0
    case YQL_DATABASE_PGSQL: return kDBTypeNamePgsql;
100
0
    case YQL_DATABASE_REDIS: return kDBTypeNameRedis;
101
0
  }
102
0
  CHECK(false) << "Unexpected db type " << db;
103
0
  return kDBTypeNameUnknown;
104
0
}
105
106
0
YQLDatabase DatabaseTypeByName(const string& db_type_name) {
107
0
  static const std::array<pair<const char*, YQLDatabase>, 3> db_types{
108
0
      make_pair(kDBTypeNameCql, YQLDatabase::YQL_DATABASE_CQL),
109
0
      make_pair(kDBTypeNamePgsql, YQLDatabase::YQL_DATABASE_PGSQL),
110
0
      make_pair(kDBTypeNameRedis, YQLDatabase::YQL_DATABASE_REDIS)};
111
0
  for (const auto& db : db_types) {
112
0
    if (db_type_name == db.first) {
113
0
      return db.second;
114
0
    }
115
0
  }
116
0
  return YQLDatabase::YQL_DATABASE_UNKNOWN;
117
0
}
118
119
} // namespace
120
121
using consensus::RaftPeerPB;
122
using std::vector;
123
using std::map;
124
using std::string;
125
using std::stringstream;
126
using std::unique_ptr;
127
using strings::Substitute;
128
using server::MonitoredTask;
129
130
using namespace std::placeholders;
131
132
namespace master {
133
134
92
MasterPathHandlers::~MasterPathHandlers() {
135
92
}
136
137
2
void MasterPathHandlers::TabletCounts::operator+=(const TabletCounts& other) {
138
2
  user_tablet_leaders += other.user_tablet_leaders;
139
2
  user_tablet_followers += other.user_tablet_followers;
140
2
  system_tablet_leaders += other.system_tablet_leaders;
141
2
  system_tablet_followers += other.system_tablet_followers;
142
2
}
143
144
MasterPathHandlers::ZoneTabletCounts::ZoneTabletCounts(
145
  const TabletCounts& tablet_counts,
146
  uint32_t active_tablets_count) : tablet_counts(tablet_counts),
147
3
                                   active_tablets_count(active_tablets_count) {
148
3
}
149
150
2
void MasterPathHandlers::ZoneTabletCounts::operator+=(const ZoneTabletCounts& other) {
151
2
  tablet_counts += other.tablet_counts;
152
2
  node_count += other.node_count;
153
2
  active_tablets_count += other.active_tablets_count;
154
2
}
155
156
// Retrieve the specified URL response from the leader master
157
void MasterPathHandlers::RedirectToLeader(const Webserver::WebRequest& req,
158
0
                                          Webserver::WebResponse* resp) {
159
0
  std::stringstream *output = &resp->output;
160
0
  vector<ServerEntryPB> masters;
161
0
  Status s = master_->ListMasters(&masters);
162
0
  if (!s.ok()) {
163
0
    s = s.CloneAndPrepend("Unable to list masters during web request handling");
164
0
    LOG(WARNING) << s.ToString();
165
0
    *output << "<h2>" << s.ToString() << "</h2>\n";
166
0
    return;
167
0
  }
168
169
0
  string redirect;
170
0
  for (const ServerEntryPB& master : masters) {
171
0
    if (master.has_error()) {
172
0
      continue;
173
0
    }
174
175
0
    if (master.role() == PeerRole::LEADER) {
176
      // URI already starts with a /, so none is needed between $1 and $2.
177
0
      if (master.registration().http_addresses().size() > 0) {
178
0
        redirect = Substitute(
179
0
            "http://$0$1$2",
180
0
            HostPortPBToString(master.registration().http_addresses(0)),
181
0
            req.redirect_uri,
182
0
            req.query_string.empty() ? "?raw" : "?" + req.query_string + "&raw");
183
0
      }
184
0
      break;
185
0
    }
186
0
  }
187
188
0
  if (redirect.empty()) {
189
0
    string error = "Unable to locate leader master to redirect this request: " + redirect;
190
0
    LOG(WARNING) << error;
191
0
    *output << error << "<br>";
192
0
    return;
193
0
  }
194
195
0
  EasyCurl curl;
196
0
  faststring buf;
197
0
  s = curl.FetchURL(redirect, &buf, kCurlTimeoutSec);
198
0
  if (!s.ok()) {
199
0
    LOG(WARNING) << "Error retrieving leader master URL: " << redirect
200
0
                 << ", error :" << s.ToString();
201
0
    *output << "Error retrieving leader master URL: <a href=\"" << redirect
202
0
            << "\">" + redirect + "</a><br> Error: " << s.ToString() << ".<br>";
203
0
    return;
204
0
  }
205
206
0
  *output << buf.ToString();
207
0
}
208
209
void MasterPathHandlers::CallIfLeaderOrPrintRedirect(
210
    const Webserver::WebRequest& req, Webserver::WebResponse* resp,
211
14
    const Webserver::PathHandlerCallback& callback) {
212
14
  string redirect;
213
  // Lock the CatalogManager in a self-contained block, to prevent double-locking on callbacks.
214
14
  {
215
14
    SCOPED_LEADER_SHARED_LOCK(l, master_->catalog_manager_impl());
216
217
    // If we are not the master leader, redirect the URL.
218
14
    if (!l.first_failed_status().ok()) {
219
0
      RedirectToLeader(req, resp);
220
0
      return;
221
0
    }
222
223
    // Handle the request as a leader master.
224
14
    callback(req, resp);
225
14
    return;
226
14
  }
227
14
}
228
229
inline void MasterPathHandlers::TServerTable(std::stringstream* output,
230
2
                                             TServersViewType viewType) {
231
2
  *output << "<table class='table table-striped'>\n";
232
2
  *output << "    <tr>\n"
233
2
          << "      <th>Server</th>\n"
234
2
          << "      <th>Time since </br>heartbeat</th>\n"
235
2
          << "      <th>Status & Uptime</th>\n";
236
237
2
  if (viewType == TServersViewType::kTServersClocksView) {
238
0
    *output << "      <th>Physical Time (UTC)</th>\n"
239
0
            << "      <th>Hybrid Time (UTC)</th>\n"
240
0
            << "      <th>Heartbeat RTT</th>\n";
241
2
  } else {
242
2
    DCHECK_EQ(viewType, TServersViewType::kTServersDefaultView);
243
2
    *output << "      <th>User Tablet-Peers / Leaders</th>\n"
244
2
            << "      <th>RAM Used</th>\n"
245
2
            << "      <th>Num SST Files</th>\n"
246
2
            << "      <th>Total SST Files Size</th>\n"
247
2
            << "      <th>Uncompressed SST </br>Files Size</th>\n"
248
2
            << "      <th>Read ops/sec</th>\n"
249
2
            << "      <th>Write ops/sec</th>\n";
250
2
  }
251
252
2
  *output << "      <th>Cloud</th>\n"
253
2
          << "      <th>Region</th>\n"
254
2
          << "      <th>Zone</th>\n";
255
256
2
  if (viewType == TServersViewType::kTServersDefaultView) {
257
2
    *output << "      <th>System Tablet-Peers / Leaders</th>\n"
258
2
            << "      <th>Active Tablet-Peers</th>\n";
259
2
  }
260
261
2
  *output << "    </tr>\n";
262
2
}
263
264
namespace {
265
266
constexpr int kHoursPerDay = 24;
267
constexpr int kSecondsPerMinute = 60;
268
constexpr int kMinutesPerHour = 60;
269
constexpr int kSecondsPerHour = kSecondsPerMinute * kMinutesPerHour;
270
constexpr int kMinutesPerDay = kMinutesPerHour * kHoursPerDay;
271
constexpr int kSecondsPerDay = kSecondsPerHour * kHoursPerDay;
272
273
9
string UptimeString(uint64_t seconds) {
274
9
  auto days = seconds / kSecondsPerDay;
275
9
  auto hours = (seconds / kSecondsPerHour) - (days * kHoursPerDay);
276
9
  auto mins = (seconds / kSecondsPerMinute) - (days * kMinutesPerDay) - (hours * kMinutesPerHour);
277
278
9
  std::ostringstream uptime_string_stream;
279
9
  uptime_string_stream << " ";
280
9
  if (days > 0) {
281
0
    uptime_string_stream << days << "days, ";
282
0
  }
283
9
  uptime_string_stream << hours << ":" << std::setw(2) << std::setfill('0') << mins <<
284
9
      ":" << std::setw(2) << std::setfill('0') << (seconds % 60);
285
286
9
  return uptime_string_stream.str();
287
9
}
288
289
bool ShouldHideTserverNodeFromDisplay(
290
7
    const TSDescriptor* ts, int hide_dead_node_threshold_mins) {
291
7
  return hide_dead_node_threshold_mins > 0
292
7
      && !ts->IsLive()
293
0
      && ts->TimeSinceHeartbeat().ToMinutes() > hide_dead_node_threshold_mins;
294
7
}
295
296
1
int GetTserverCountForDisplay(const TSManager* ts_manager) {
297
1
  int count = 0;
298
3
  for (const auto& tserver : ts_manager->GetAllDescriptors()) {
299
3
    if (!ShouldHideTserverNodeFromDisplay(tserver.get(), FLAGS_hide_dead_node_threshold_mins)) {
300
3
      count++;
301
3
    }
302
3
  }
303
1
  return count;
304
1
}
305
306
} // anonymous namespace
307
308
string MasterPathHandlers::GetHttpHostPortFromServerRegistration(
309
9
    const ServerRegistrationPB& reg) const {
310
9
  if (reg.http_addresses().size() > 0) {
311
9
    return HostPortPBToString(reg.http_addresses(0));
312
9
  }
313
0
  return "";
314
0
}
315
316
namespace {
317
318
bool TabletServerComparator(
319
3
    const std::shared_ptr<TSDescriptor>& a, const std::shared_ptr<TSDescriptor>& b) {
320
3
  auto a_cloud_info = a->GetRegistration().common().cloud_info();
321
3
  auto b_cloud_info = b->GetRegistration().common().cloud_info();
322
323
3
  if (a_cloud_info.placement_cloud() == b_cloud_info.placement_cloud()) {
324
3
    if (a_cloud_info.placement_region() == b_cloud_info.placement_region()) {
325
3
      if (a_cloud_info.placement_zone() == b_cloud_info.placement_zone()) {
326
3
        return a->permanent_uuid() < b->permanent_uuid();
327
3
      }
328
0
      return a_cloud_info.placement_zone() < b_cloud_info.placement_zone();
329
0
    }
330
0
    return a_cloud_info.placement_region() < b_cloud_info.placement_region();
331
0
  }
332
0
  return a_cloud_info.placement_cloud() < b_cloud_info.placement_cloud();
333
0
}
334
335
} // anonymous namespace
336
337
void MasterPathHandlers::TServerDisplay(const std::string& current_uuid,
338
                                        std::vector<std::shared_ptr<TSDescriptor>>* descs,
339
                                        TabletCountMap* tablet_map,
340
                                        std::stringstream* output,
341
                                        const int hide_dead_node_threshold_mins,
342
2
                                        TServersViewType viewType) {
343
  // Copy vector to avoid changes to the reference descs passed
344
2
  std::vector<std::shared_ptr<TSDescriptor>> local_descs(*descs);
345
346
  // Comparator orders by cloud, region, zone and uuid fields.
347
2
  std::sort(local_descs.begin(), local_descs.end(), &TabletServerComparator);
348
349
4
  for (auto desc : local_descs) {
350
4
    if (desc->placement_uuid() == current_uuid) {
351
4
      if (ShouldHideTserverNodeFromDisplay(desc.get(), hide_dead_node_threshold_mins)) {
352
0
        continue;
353
0
      }
354
4
      const string time_since_hb = StringPrintf("%.1fs", desc->TimeSinceHeartbeat().ToSeconds());
355
4
      TSRegistrationPB reg = desc->GetRegistration();
356
4
      string host_port = GetHttpHostPortFromServerRegistration(reg.common());
357
4
      *output << "  <tr>\n";
358
4
      *output << "  <td>" << RegistrationToHtml(reg.common(), host_port) << "</br>";
359
4
      *output << "  " << desc->permanent_uuid() << "</td>";
360
4
      *output << "<td>" << time_since_hb << "</td>";
361
4
      if (desc->IsLive()) {
362
4
        *output << "    <td style=\"color:Green\">" << kTserverAlive << ":" <<
363
4
                UptimeString(desc->uptime_seconds()) << "</td>";
364
0
      } else {
365
0
        *output << "    <td style=\"color:Red\">" << kTserverDead << "</td>";
366
0
      }
367
368
4
      auto tserver = tablet_map->find(desc->permanent_uuid());
369
4
      bool no_tablets = tserver == tablet_map->end();
370
371
4
      if (viewType == TServersViewType::kTServersClocksView) {
372
        // Render physical time.
373
0
        const Timestamp p_ts(desc->physical_time());
374
0
        *output << "    <td>" << p_ts.ToHumanReadableTime() << "</td>";
375
376
        // Render the physical and logical components of the hybrid time.
377
0
        const HybridTime ht = desc->hybrid_time();
378
0
        const Timestamp h_ts(ht.GetPhysicalValueMicros());
379
0
        *output << "    <td>" << h_ts.ToHumanReadableTime();
380
0
        if (ht.GetLogicalValue()) {
381
0
          *output << " / Logical: " << ht.GetLogicalValue();
382
0
        }
383
0
        *output << "</td>";
384
        // Render the roundtrip time of previous heartbeat.
385
0
        double rtt_ms = desc->heartbeat_rtt().ToMicroseconds()/1000.0;
386
0
        *output << "    <td>" <<  StringPrintf("%.2fms", rtt_ms) << "</td>";
387
4
      } else {
388
4
        DCHECK_EQ(viewType, TServersViewType::kTServersDefaultView);
389
1
        *output << "    <td>" << (no_tablets ? 0
390
3
                : tserver->second.user_tablet_leaders + tserver->second.user_tablet_followers)
391
3
                << " / " << (no_tablets ? 0 : tserver->second.user_tablet_leaders) << "</td>";
392
4
        *output << "    <td>" << HumanizeBytes(desc->total_memory_usage()) << "</td>";
393
4
        *output << "    <td>" << desc->num_sst_files() << "</td>";
394
4
        *output << "    <td>" << HumanizeBytes(desc->total_sst_file_size()) << "</td>";
395
4
        *output << "    <td>" << HumanizeBytes(desc->uncompressed_sst_file_size()) << "</td>";
396
4
        *output << "    <td>" << desc->read_ops_per_sec() << "</td>";
397
4
        *output << "    <td>" << desc->write_ops_per_sec() << "</td>";
398
4
      }
399
400
4
      *output << "    <td>" << reg.common().cloud_info().placement_cloud() << "</td>";
401
4
      *output << "    <td>" << reg.common().cloud_info().placement_region() << "</td>";
402
4
      *output << "    <td>" << reg.common().cloud_info().placement_zone() << "</td>";
403
404
4
      if (viewType == TServersViewType::kTServersDefaultView) {
405
1
        *output << "    <td>" << (no_tablets ? 0
406
3
                : tserver->second.system_tablet_leaders + tserver->second.system_tablet_followers)
407
3
                << " / " << (no_tablets ? 0 : tserver->second.system_tablet_leaders) << "</td>";
408
3
        *output << "    <td>" << (no_tablets ? 0 : desc->num_live_replicas()) << "</td>";
409
4
      }
410
411
4
      *output << "  </tr>\n";
412
4
    }
413
4
  }
414
2
  *output << "</table>\n";
415
2
}
416
417
void MasterPathHandlers::DisplayTabletZonesTable(
418
  const ZoneTabletCounts::CloudTree& cloud_tree,
419
  std::stringstream* output
420
2
) {
421
2
  *output << "<h3>Tablet-Peers by Availability Zone</h3>\n"
422
2
          << "<table class='table table-striped'>\n"
423
2
          << "  <tr>\n"
424
2
          << "    <th>Cloud</th>\n"
425
2
          << "    <th>Region</th>\n"
426
2
          << "    <th>Zone</th>\n"
427
2
          << "    <th>Total Nodes</th>\n"
428
2
          << "    <th>User Tablet-Peers / Leaders</th>\n"
429
2
          << "    <th>System Tablet-Peers / Leaders</th>\n"
430
2
          << "    <th>Active Tablet-Peers</th>\n"
431
2
          << "  </tr>\n";
432
433
2
  for (const auto& cloud_iter : cloud_tree) {
434
2
    const auto& region_tree = cloud_iter.second;
435
2
    bool needs_new_row = false;
436
437
2
    int total_size_rows = 0;
438
2
    for (const auto& region_iter : region_tree) {
439
2
      total_size_rows += region_iter.second.size();
440
2
    }
441
442
2
    *output << "<tr>\n"
443
2
            << "  <td rowspan=\"" << total_size_rows <<"\">" << cloud_iter.first << "</td>\n";
444
445
2
    for (const auto& region_iter : region_tree) {
446
2
      const auto& zone_tree = region_iter.second;
447
448
2
      if (needs_new_row) {
449
0
        *output << "<tr>\n";
450
0
        needs_new_row = false;
451
0
      }
452
453
2
      *output << "  <td rowspan=\"" << zone_tree.size() <<"\">" << region_iter.first
454
2
              << "</td>\n";
455
456
2
      for (const auto& zone_iter : zone_tree) {
457
2
        const auto& counts = zone_iter.second;
458
459
2
        if (needs_new_row) {
460
0
          *output << "<tr>\n";
461
0
        }
462
463
2
        *output << "  <td>" << zone_iter.first << "</td>\n";
464
465
2
        uint32_t user_leaders = counts.tablet_counts.user_tablet_leaders;
466
2
        uint32_t user_total = user_leaders + counts.tablet_counts.user_tablet_followers;
467
2
        uint32_t system_leaders = counts.tablet_counts.system_tablet_leaders;
468
2
        uint32_t system_total = system_leaders + counts.tablet_counts.system_tablet_followers;
469
470
2
        *output << "  <td>" << counts.node_count << "</td>\n"
471
2
                << "  <td>" << user_total << " / " << user_leaders << "</td>\n"
472
2
                << "  <td>" << system_total << " / " << system_leaders << "</td>\n"
473
2
                << "  <td>" << counts.active_tablets_count << "</td>\n"
474
2
                << "</tr>\n";
475
476
2
        needs_new_row = true;
477
2
      }
478
2
    }
479
2
  }
480
481
2
  *output << "</table>\n";
482
2
}
483
484
MasterPathHandlers::ZoneTabletCounts::CloudTree MasterPathHandlers::CalculateTabletCountsTree(
485
  const std::vector<std::shared_ptr<TSDescriptor>>& descriptors,
486
  const TabletCountMap& tablet_count_map
487
2
) {
488
2
  ZoneTabletCounts::CloudTree cloud_tree;
489
490
4
  for (const auto& descriptor : descriptors) {
491
4
    CloudInfoPB cloud_info = descriptor->GetRegistration().common().cloud_info();
492
4
    std::string cloud = cloud_info.placement_cloud();
493
4
    std::string region = cloud_info.placement_region();
494
4
    std::string zone = cloud_info.placement_zone();
495
496
4
    auto tablet_count_search = tablet_count_map.find(descriptor->permanent_uuid());
497
4
    ZoneTabletCounts counts = tablet_count_search == tablet_count_map.end()
498
1
        ? ZoneTabletCounts()
499
3
        : ZoneTabletCounts(tablet_count_search->second, descriptor->num_live_replicas());
500
501
4
    auto cloud_iter = cloud_tree.find(cloud);
502
4
    if (cloud_iter == cloud_tree.end()) {
503
2
      ZoneTabletCounts::RegionTree region_tree;
504
2
      ZoneTabletCounts::ZoneTree zone_tree;
505
506
2
      zone_tree.emplace(zone, std::move(counts));
507
2
      region_tree.emplace(region, std::move(zone_tree));
508
2
      cloud_tree.emplace(cloud, std::move(region_tree));
509
2
    } else {
510
2
      ZoneTabletCounts::RegionTree& region_tree = cloud_iter->second;
511
512
2
      auto region_iter = region_tree.find(region);
513
2
      if (region_iter == region_tree.end()) {
514
0
        ZoneTabletCounts::ZoneTree zone_tree;
515
516
0
        zone_tree.emplace(zone, std::move(counts));
517
0
        region_tree.emplace(region, std::move(zone_tree));
518
2
      } else {
519
2
        ZoneTabletCounts::ZoneTree& zone_tree = region_iter->second;
520
521
2
        auto zone_iter = zone_tree.find(zone);
522
2
        if (zone_iter == zone_tree.end()) {
523
0
          zone_tree.emplace(zone, std::move(counts));
524
2
        } else {
525
2
          zone_iter->second += counts;
526
2
        }
527
2
      }
528
2
    }
529
4
  }
530
531
2
  return cloud_tree;
532
2
}
533
534
void MasterPathHandlers::HandleTabletServers(const Webserver::WebRequest& req,
535
                                             Webserver::WebResponse* resp,
536
2
                                             TServersViewType viewType) {
537
2
  std::stringstream *output = &resp->output;
538
2
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
539
540
2
  auto threshold_arg = req.parsed_args.find("live_threshold_mins");
541
2
  int hide_dead_node_threshold_override = FLAGS_hide_dead_node_threshold_mins;
542
2
  if (threshold_arg != req.parsed_args.end()) {
543
0
    hide_dead_node_threshold_override = atoi(threshold_arg->second.c_str());
544
0
  }
545
546
2
  SysClusterConfigEntryPB config;
547
2
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
548
2
  if (!s.ok()) {
549
0
    *output << "<div class=\"alert alert-warning\">" << s.ToString() << "</div>";
550
0
    return;
551
0
  }
552
553
2
  auto live_id = config.replication_info().live_replicas().placement_uuid();
554
555
2
  vector<std::shared_ptr<TSDescriptor> > descs;
556
2
  const auto& ts_manager = master_->ts_manager();
557
2
  ts_manager->GetAllDescriptors(&descs);
558
559
  // Get user and system tablet leader and follower counts for each TabletServer
560
2
  TabletCountMap tablet_map;
561
2
  CalculateTabletMap(&tablet_map);
562
563
2
  std::unordered_set<string> read_replica_uuids;
564
4
  for (auto desc : descs) {
565
4
    if (!read_replica_uuids.count(desc->placement_uuid()) && desc->placement_uuid() != live_id) {
566
0
      read_replica_uuids.insert(desc->placement_uuid());
567
0
    }
568
4
  }
569
570
2
  *output << std::setprecision(output_precision_);
571
2
  *output << "<h2>Tablet Servers</h2>\n";
572
573
2
  if (!live_id.empty()) {
574
0
    *output << "<h3 style=\"color:" << kYBDarkBlue << "\">Primary Cluster UUID: "
575
0
            << live_id << "</h3>\n";
576
0
  }
577
578
2
  TServerTable(output, viewType);
579
2
  TServerDisplay(live_id, &descs, &tablet_map, output, hide_dead_node_threshold_override,
580
2
                 viewType);
581
582
0
  for (const auto& read_replica_uuid : read_replica_uuids) {
583
0
    *output << "<h3 style=\"color:" << kYBDarkBlue << "\">Read Replica UUID: "
584
0
            << (read_replica_uuid.empty() ? kNoPlacementUUID : read_replica_uuid) << "</h3>\n";
585
0
    TServerTable(output, viewType);
586
0
    TServerDisplay(read_replica_uuid, &descs, &tablet_map, output,
587
0
                   hide_dead_node_threshold_override, viewType);
588
0
  }
589
590
2
  ZoneTabletCounts::CloudTree counts_tree = CalculateTabletCountsTree(descs, tablet_map);
591
2
  DisplayTabletZonesTable(counts_tree, output);
592
2
}
593
594
void MasterPathHandlers::HandleGetTserverStatus(const Webserver::WebRequest& req,
595
0
                                             Webserver::WebResponse* resp) {
596
0
  std::stringstream *output = &resp->output;
597
0
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
598
599
0
  JsonWriter jw(output, JsonWriter::COMPACT);
600
601
0
  SysClusterConfigEntryPB config;
602
0
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
603
0
  if (!s.ok()) {
604
0
    jw.StartObject();
605
0
    jw.String("error");
606
0
    jw.String(s.ToString());
607
0
    return;
608
0
  }
609
610
0
  vector<std::shared_ptr<TSDescriptor> > descs;
611
0
  const auto& ts_manager = master_->ts_manager();
612
0
  ts_manager->GetAllDescriptors(&descs);
613
614
  // Get user and system tablet leader and follower counts for each TabletServer.
615
0
  TabletCountMap tablet_map;
616
0
  CalculateTabletMap(&tablet_map);
617
618
0
  std::unordered_set<string> cluster_uuids;
619
0
  auto primary_uuid = config.replication_info().live_replicas().placement_uuid();
620
0
  cluster_uuids.insert(primary_uuid);
621
0
  for (auto desc : descs) {
622
0
    cluster_uuids.insert(desc->placement_uuid());
623
0
  }
624
625
0
  jw.StartObject();
626
0
  for (const auto& cur_uuid : cluster_uuids) {
627
0
    jw.String(cur_uuid);
628
0
    jw.StartObject();
629
0
    for (auto desc : descs) {
630
0
      if (desc->placement_uuid() == cur_uuid) {
631
0
        TSRegistrationPB reg = desc->GetRegistration();
632
0
        string host_port = GetHttpHostPortFromServerRegistration(reg.common());
633
0
        jw.String(host_port);
634
635
0
        jw.StartObject();
636
637
        // Some stats may be repeated as strings due to backwards compatability.
638
0
        jw.String("time_since_hb");
639
0
        jw.String(StringPrintf("%.1fs", desc->TimeSinceHeartbeat().ToSeconds()));
640
0
        jw.String("time_since_hb_sec");
641
0
        jw.Double(desc->TimeSinceHeartbeat().ToSeconds());
642
643
0
        if (desc->IsLive()) {
644
0
          jw.String("status");
645
0
          jw.String(kTserverAlive);
646
647
0
          jw.String("uptime_seconds");
648
0
          jw.Uint64(desc->uptime_seconds());
649
0
        } else {
650
0
          jw.String("status");
651
0
          jw.String(kTserverDead);
652
653
0
          jw.String("uptime_seconds");
654
0
          jw.Uint(0);
655
0
        }
656
657
0
        jw.String("ram_used");
658
0
        jw.String(HumanizeBytes(desc->total_memory_usage()));
659
0
        jw.String("ram_used_bytes");
660
0
        jw.Uint64(desc->total_memory_usage());
661
662
0
        jw.String("num_sst_files");
663
0
        jw.Uint64(desc->num_sst_files());
664
665
0
        jw.String("total_sst_file_size");
666
0
        jw.String(HumanizeBytes(desc->total_sst_file_size()));
667
0
        jw.String("total_sst_file_size_bytes");
668
0
        jw.Uint64(desc->total_sst_file_size());
669
670
0
        jw.String("uncompressed_sst_file_size");
671
0
        jw.String(HumanizeBytes(desc->uncompressed_sst_file_size()));
672
0
        jw.String("uncompressed_sst_file_size_bytes");
673
0
        jw.Uint64(desc->uncompressed_sst_file_size());
674
675
0
        jw.String("path_metrics");
676
0
        jw.StartArray();
677
0
        for(const auto& path_metric : desc->path_metrics()) {
678
0
          jw.StartObject();
679
0
          jw.String("path");
680
0
          jw.String(path_metric.first);
681
0
          jw.String("space_used");
682
0
          jw.Uint64(path_metric.second.used_space);
683
0
          jw.String("total_space_size");
684
0
          jw.Uint64(path_metric.second.total_space);
685
0
          jw.EndObject();
686
0
        }
687
0
        jw.EndArray();
688
689
0
        jw.String("read_ops_per_sec");
690
0
        jw.Double(desc->read_ops_per_sec());
691
692
0
        jw.String("write_ops_per_sec");
693
0
        jw.Double(desc->write_ops_per_sec());
694
695
0
        auto tserver = tablet_map.find(desc->permanent_uuid());
696
0
        uint user_tablets_total = 0;
697
0
        uint user_tablets_leaders = 0;
698
0
        uint system_tablets_total = 0;
699
0
        uint system_tablets_leaders = 0;
700
0
        int active_tablets = 0;
701
0
        if (!(tserver == tablet_map.end())) {
702
0
          user_tablets_total = tserver->second.user_tablet_leaders +
703
0
            tserver->second.user_tablet_followers;
704
0
          user_tablets_leaders = tserver->second.user_tablet_leaders;
705
0
          system_tablets_total = tserver->second.system_tablet_leaders +
706
0
            tserver->second.system_tablet_followers;
707
0
          system_tablets_leaders = tserver->second.system_tablet_leaders;
708
0
          active_tablets = desc->num_live_replicas();
709
0
        }
710
0
        jw.String("user_tablets_total");
711
0
        jw.Uint(user_tablets_total);
712
713
0
        jw.String("user_tablets_leaders");
714
0
        jw.Uint(user_tablets_leaders);
715
716
0
        jw.String("system_tablets_total");
717
0
        jw.Uint(system_tablets_total);
718
719
0
        jw.String("system_tablets_leaders");
720
0
        jw.Uint(system_tablets_leaders);
721
722
0
        jw.String("active_tablets");
723
0
        jw.Int(active_tablets);
724
725
0
        CloudInfoPB cloud_info = reg.common().cloud_info();
726
727
0
        jw.String("cloud");
728
0
        jw.String(cloud_info.placement_cloud());
729
730
0
        jw.String("region");
731
0
        jw.String(cloud_info.placement_region());
732
733
0
        jw.String("zone");
734
0
        jw.String(cloud_info.placement_zone());
735
736
0
        jw.EndObject();
737
0
      }
738
0
    }
739
0
    jw.EndObject();
740
0
  }
741
0
  jw.EndObject();
742
0
}
743
744
void MasterPathHandlers::HandleHealthCheck(
745
9
    const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
746
  // TODO: Lock not needed since other APIs handle it.  Refactor other functions accordingly
747
9
  std::stringstream *output = &resp->output;
748
9
  JsonWriter jw(output, JsonWriter::COMPACT);
749
750
9
  SysClusterConfigEntryPB config;
751
9
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
752
9
  if (!s.ok()) {
753
0
    jw.StartObject();
754
0
    jw.String("error");
755
0
    jw.String(s.ToString());
756
0
    return;
757
0
  }
758
9
  auto replication_factor = master_->catalog_manager()->GetReplicationFactor();
759
9
  if (!replication_factor.ok()) {
760
0
    jw.StartObject();
761
0
    jw.String("error");
762
0
    jw.String(replication_factor.status().ToString());
763
0
    return;
764
0
  }
765
766
9
  vector<std::shared_ptr<TSDescriptor> > descs;
767
9
  const auto* ts_manager = master_->ts_manager();
768
9
  ts_manager->GetAllDescriptors(&descs);
769
770
9
  const auto& live_placement_uuid = config.replication_info().live_replicas().placement_uuid();
771
  // Ignore read replica health for V1.
772
773
9
  vector<std::shared_ptr<TSDescriptor> > dead_nodes;
774
9
  uint64_t most_recent_uptime = std::numeric_limits<uint64_t>::max();
775
776
9
  jw.StartObject();
777
9
  {
778
    // Iterate TabletServers, looking for health anomalies.
779
37
    for (const auto & desc : descs) {
780
37
      if (desc->placement_uuid() == live_placement_uuid) {
781
37
        if (!desc->IsLive()) {
782
          // 1. Are any of the TS marked dead in the master?
783
6
          dead_nodes.push_back(desc);
784
31
        } else {
785
          // 2. Have any of the servers restarted lately?
786
31
          most_recent_uptime = min(most_recent_uptime, desc->uptime_seconds());
787
31
        }
788
37
      }
789
37
    }
790
791
9
    jw.String("dead_nodes");
792
9
    jw.StartArray();
793
6
    for (auto const & ts_desc : dead_nodes) {
794
6
      jw.String(ts_desc->permanent_uuid());
795
6
    }
796
9
    jw.EndArray();
797
798
9
    jw.String("most_recent_uptime");
799
9
    jw.Uint64(most_recent_uptime);
800
801
9
    auto time_arg = req.parsed_args.find("tserver_death_interval_msecs");
802
9
    int64 death_interval_msecs = 0;
803
9
    if (time_arg != req.parsed_args.end()) {
804
9
      death_interval_msecs = atoi(time_arg->second.c_str());
805
9
    }
806
807
    // Get all the tablets and add the tablet id for each tablet that has
808
    // replication locations lesser than 'replication_factor'.
809
9
    jw.String("under_replicated_tablets");
810
9
    jw.StartArray();
811
812
9
    auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kRunning);
813
162
    for (const auto& table : tables) {
814
      // Ignore tables that are neither user tables nor user indexes.
815
      // However there are a bunch of system tables that still need to be investigated:
816
      // 1. Redis system table.
817
      // 2. Transaction status table.
818
      // 3. Metrics table.
819
162
      if (!master_->catalog_manager()->IsUserTable(*table) &&
820
153
          table->GetTableType() != REDIS_TABLE_TYPE &&
821
153
          table->GetTableType() != TRANSACTION_STATUS_TABLE_TYPE &&
822
153
          !(table->namespace_id() == kSystemNamespaceId &&
823
153
            table->name() == kMetricsSnapshotsTableName)) {
824
153
        continue;
825
153
      }
826
827
9
      TabletInfos tablets = table->GetTablets();
828
829
27
      for (const auto& tablet : tablets) {
830
27
        auto replication_locations = tablet->GetReplicaLocations();
831
832
27
        if (replication_locations->size() < *replication_factor) {
833
          // These tablets don't have the required replication locations needed.
834
0
          jw.String(tablet->tablet_id());
835
0
          continue;
836
0
        }
837
838
        // Check if we have tablets that have replicas on the dead node.
839
27
        if (dead_nodes.size() == 0) {
840
9
          continue;
841
9
        }
842
18
        size_t recent_replica_count = 0;
843
54
        for (const auto& iter : *replication_locations) {
844
54
          if (std::find_if(dead_nodes.begin(),
845
54
                           dead_nodes.end(),
846
54
                           [iter, death_interval_msecs] (const auto& ts) {
847
54
                               return (ts->permanent_uuid() == iter.first &&
848
18
                                       ts->TimeSinceHeartbeat().ToMilliseconds() >
849
18
                                           death_interval_msecs); }) ==
850
39
                  dead_nodes.end()) {
851
39
            ++recent_replica_count;
852
39
          }
853
54
        }
854
18
        if (recent_replica_count < *replication_factor) {
855
15
          jw.String(tablet->tablet_id());
856
15
        }
857
18
      }
858
9
    }
859
9
    jw.EndArray();
860
861
    // TODO: Add these health checks in a subsequent diff
862
    //
863
    // 4. is the load balancer busy moving tablets/leaders around
864
    /* Use: CHECKED_STATUS IsLoadBalancerIdle(const IsLoadBalancerIdleRequestPB* req,
865
                                              IsLoadBalancerIdleResponsePB* resp);
866
     */
867
    // 5. do any of the TS have tablets they were not able to start up
868
9
  }
869
9
  jw.EndObject();
870
9
}
871
872
0
string MasterPathHandlers::GetParentTableOid(scoped_refptr<TableInfo> parent_table) {
873
0
  TableId t_id = parent_table->id();;
874
0
  if (parent_table->IsColocatedParentTable()) {
875
    // No YSQL parent id for colocated database parent table
876
0
    return "";
877
0
  }
878
0
  const auto parent_result = GetPgsqlTablegroupOidByTableId(t_id);
879
0
  RETURN_NOT_OK_RET(ResultToStatus(parent_result), "");
880
0
  return std::to_string(*parent_result);
881
0
}
882
883
void MasterPathHandlers::HandleCatalogManager(const Webserver::WebRequest& req,
884
                                              Webserver::WebResponse* resp,
885
1
                                              bool only_user_tables) {
886
1
  std::stringstream *output = &resp->output;
887
1
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
888
889
1
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kAll);
890
891
1
  bool has_tablegroups = master_->catalog_manager()->HasTablegroups();
892
893
1
  typedef map<string, string> StringMap;
894
895
  // The first stores user tables, the second index tables, and the third system tables.
896
1
  std::unique_ptr<StringMap> ordered_tables[kNumTypes];
897
5
  for (int i = 0; i < kNumTypes; ++i) {
898
4
    ordered_tables[i] = std::make_unique<StringMap>();
899
4
  }
900
901
18
  for (const auto& table : tables) {
902
18
    auto l = table->LockForRead();
903
18
    if (!l->is_running()) {
904
0
      continue;
905
0
    }
906
907
18
    string keyspace = master_->catalog_manager()->GetNamespaceName(table->namespace_id());
908
18
    bool is_platform = keyspace.compare(kSystemPlatformNamespace) == 0;
909
910
18
    TableType table_cat;
911
    // Determine the table category. YugaWare tables should be displayed as system tables.
912
18
    if (is_platform) {
913
0
      table_cat = kSystemTable;
914
18
    } else if (master_->catalog_manager()->IsUserIndex(*table)) {
915
0
      table_cat = kUserIndex;
916
18
    } else if (master_->catalog_manager()->IsUserTable(*table)) {
917
1
      table_cat = kUserTable;
918
17
    } else if (table->IsTablegroupParentTable() ||
919
17
               table->IsColocatedParentTable()) {
920
0
      table_cat = kColocatedParentTable;
921
17
    } else {
922
17
      table_cat = kSystemTable;
923
17
    }
924
    // Skip non-user tables if we should.
925
18
    if (only_user_tables && (table_cat != kUserIndex && table_cat != kUserTable)) {
926
0
      continue;
927
0
    }
928
929
18
    string table_uuid = table->id();
930
18
    string state = SysTablesEntryPB_State_Name(l->pb.state());
931
18
    Capitalize(&state);
932
18
    string ysql_table_oid;
933
18
    string ysql_parent_oid;
934
935
18
    string display_info = Substitute(
936
18
                          "<tr>" \
937
18
                          "<td>$0</td>",
938
18
                          EscapeForHtmlToString(keyspace));
939
940
18
    if (table->GetTableType() == PGSQL_TABLE_TYPE &&
941
0
        !table->IsColocatedParentTable() &&
942
0
        !table->IsTablegroupParentTable()) {
943
0
      const auto result = GetPgsqlTableOid(table_uuid);
944
0
      if (result.ok()) {
945
0
        ysql_table_oid = std::to_string(*result);
946
0
      } else {
947
0
        LOG(ERROR) << "Failed to get OID of '" << table_uuid << "' ysql table";
948
0
      }
949
950
0
      display_info += Substitute(
951
0
                      "<td><a href=\"/table?id=$3\">$0</a></td>" \
952
0
                      "<td>$1</td>" \
953
0
                      "<td>$2</td>" \
954
0
                      "<td>$3</td>" \
955
0
                      "<td>$4</td>",
956
0
                      EscapeForHtmlToString(l->name()),
957
0
                      state,
958
0
                      EscapeForHtmlToString(l->pb.state_msg()),
959
0
                      EscapeForHtmlToString(table_uuid),
960
0
                      ysql_table_oid);
961
962
0
      if (has_tablegroups) {
963
0
        if (table->IsColocatedUserTable()) {
964
0
          const auto parent_table = table->GetColocatedTablet()->table();
965
0
          ysql_parent_oid = GetParentTableOid(parent_table);
966
0
          display_info += Substitute("<td>$0</td>", ysql_parent_oid);
967
0
        } else {
968
0
          display_info += Substitute("<td></td>");
969
0
        }
970
0
      }
971
18
    } else if (table->IsTablegroupParentTable() ||
972
18
               table->IsColocatedParentTable()) {
973
      // Colocated parent table.
974
0
      ysql_table_oid = GetParentTableOid(table);
975
976
      // Insert a newline in id and name to wrap long tablegroup text.
977
0
      std::string parent_name = l->name();
978
0
      display_info += Substitute(
979
0
                      "<td><a href=\"/table?id=$0\">$1</a></td>" \
980
0
                      "<td>$2</td>" \
981
0
                      "<td>$3</td>" \
982
0
                      "<td>$4</td>" \
983
0
                      "<td>$5</td>",
984
0
                      EscapeForHtmlToString(table_uuid),
985
0
                      EscapeForHtmlToString(parent_name.insert(32, "\n")),
986
0
                      state,
987
0
                      EscapeForHtmlToString(l->pb.state_msg()),
988
0
                      EscapeForHtmlToString(table_uuid.insert(32, "\n")),
989
0
                      ysql_table_oid);
990
18
    } else {
991
      // System table - don't include parent table column
992
18
      display_info += Substitute(
993
18
                      "<td><a href=\"/table?id=$3\">$0</a></td>" \
994
18
                      "<td>$1</td>" \
995
18
                      "<td>$2</td>" \
996
18
                      "<td>$3</td>" \
997
18
                      "<td>$4</td>",
998
18
                      EscapeForHtmlToString(l->name()),
999
18
                      state,
1000
18
                      EscapeForHtmlToString(l->pb.state_msg()),
1001
18
                      EscapeForHtmlToString(table_uuid),
1002
18
                      ysql_table_oid);
1003
18
    }
1004
18
    display_info += "</tr>\n";
1005
18
    (*ordered_tables[table_cat])[table_uuid] = display_info;
1006
18
  }
1007
1008
5
  for (int i = 0; i < kNumTypes; ++i) {
1009
4
    if (only_user_tables && (table_type_[i] != "Index" && table_type_[i] != "User")) {
1010
0
      continue;
1011
0
    }
1012
4
    if (ordered_tables[i]->empty() && table_type_[i] == "Colocated") {
1013
1
      continue;
1014
1
    }
1015
1016
3
    (*output) << "<div class='panel panel-default'>\n"
1017
3
              << "<div class='panel-heading'><h2 class='panel-title'>" << table_type_[i]
1018
3
              << " tables</h2></div>\n";
1019
3
    (*output) << "<div class='panel-body table-responsive'>";
1020
1021
3
    if (ordered_tables[i]->empty()) {
1022
1
      (*output) << "There are no " << static_cast<char>(tolower(table_type_[i][0]))
1023
1
                << table_type_[i].substr(1) << " tables.\n";
1024
2
    } else {
1025
2
      *output << "<table class='table table-striped' style='table-layout: fixed;'>\n";
1026
2
      *output << "  <tr><th width='14%'>Keyspace</th>\n"
1027
2
              << "      <th width='21%'>Table Name</th>\n"
1028
2
              << "      <th width='9%'>State</th>\n"
1029
2
              << "      <th width='14%'>Message</th>\n";
1030
2
      if ((table_type_[i] == "User" || table_type_[i] == "Index") && has_tablegroups) {
1031
0
        *output << "      <th width='22%'>UUID</th>\n"
1032
0
                << "      <th width='10%'>YSQL OID</th>\n"
1033
0
                << "      <th width='10%'>Parent OID</th></tr>\n";
1034
2
      } else {
1035
2
        *output << "      <th width='28%'>UUID</th>\n"
1036
2
                << "      <th width='14%'>YSQL OID</th></tr>\n";
1037
2
      }
1038
18
      for (const StringMap::value_type &table : *(ordered_tables[i])) {
1039
18
        *output << table.second;
1040
18
      }
1041
2
      (*output) << "</table>\n";
1042
2
    }
1043
3
    (*output) << "</div> <!-- panel-body -->\n";
1044
3
    (*output) << "</div> <!-- panel -->\n";
1045
3
  }
1046
1
}
1047
1048
namespace {
1049
1050
6
bool CompareByHost(const TabletReplica& a, const TabletReplica& b) {
1051
6
    return a.ts_desc->permanent_uuid() < b.ts_desc->permanent_uuid();
1052
6
}
1053
1054
} // anonymous namespace
1055
1056
1057
void MasterPathHandlers::HandleTablePage(const Webserver::WebRequest& req,
1058
1
                                         Webserver::WebResponse* resp) {
1059
1
  std::stringstream *output = &resp->output;
1060
1
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1061
1062
  // True if table_id, false if (keyspace, table).
1063
1
  const auto arg_end = req.parsed_args.end();
1064
1
  auto id_arg = req.parsed_args.find("id");
1065
1
  auto keyspace_arg = arg_end;
1066
1
  auto table_arg = arg_end;
1067
1
  if (id_arg == arg_end) {
1068
0
    keyspace_arg = req.parsed_args.find("keyspace_name");
1069
0
    table_arg = req.parsed_args.find("table_name");
1070
0
    if (keyspace_arg == arg_end || table_arg == arg_end) {
1071
0
      *output << " Missing 'id' argument or 'keyspace_name, table_name' argument pair.";
1072
0
      *output << " Arguments must either contain the table id or the "
1073
0
                 " (keyspace_name, table_name) pair.";
1074
0
      return;
1075
0
    }
1076
1
  }
1077
1078
1
  scoped_refptr<TableInfo> table;
1079
1080
1
  if (id_arg != arg_end) {
1081
1
    table = master_->catalog_manager()->GetTableInfo(id_arg->second);
1082
0
  } else {
1083
0
    const auto keyspace_type_arg = req.parsed_args.find("keyspace_type");
1084
0
    const auto keyspace_type = (keyspace_type_arg == arg_end
1085
0
        ? GetDefaultDatabaseType(keyspace_arg->second)
1086
0
        : DatabaseTypeByName(keyspace_type_arg->second));
1087
0
    if (keyspace_type == YQLDatabase::YQL_DATABASE_UNKNOWN) {
1088
0
      *output << "Wrong keyspace_type found '" << keyspace_type_arg->second << "'."
1089
0
              << "Possible values are: " << kDBTypeNameCql << ", "
1090
0
              << kDBTypeNamePgsql << ", " << kDBTypeNameRedis << ".";
1091
0
      return;
1092
0
    }
1093
0
    table = master_->catalog_manager()->GetTableInfoFromNamespaceNameAndTableName(
1094
0
        keyspace_type, keyspace_arg->second, table_arg->second);
1095
0
  }
1096
1097
1
  if (table == nullptr) {
1098
0
    *output << "Table not found!";
1099
0
    return;
1100
0
  }
1101
1102
1
  Schema schema;
1103
1
  PartitionSchema partition_schema;
1104
1
  NamespaceName keyspace_name;
1105
1
  TableName table_name;
1106
1
  TabletInfos tablets;
1107
1
  {
1108
1
    auto l = table->LockForRead();
1109
1
    keyspace_name = master_->catalog_manager()->GetNamespaceName(table->namespace_id());
1110
1
    table_name = l->name();
1111
1
    *output << "<h1>Table: "
1112
1
            << EscapeForHtmlToString(server::TableLongName(keyspace_name, table_name))
1113
1
            << " ("<< table->id() <<") </h1>\n";
1114
1115
1
    *output << "<table class='table table-striped'>\n";
1116
1
    *output << "  <tr><td>Version:</td><td>" << l->pb.version() << "</td></tr>\n";
1117
1118
1
    *output << "  <tr><td>Type:</td><td>" << TableType_Name(l->pb.table_type())
1119
1
            << "</td></tr>\n";
1120
1121
1
    string state = SysTablesEntryPB_State_Name(l->pb.state());
1122
1
    Capitalize(&state);
1123
1
    *output << "  <tr><td>State:</td><td>"
1124
1
            << state
1125
1
            << EscapeForHtmlToString(l->pb.state_msg())
1126
1
            << "</td></tr>\n";
1127
1128
1
    TablespaceId tablespace_id;
1129
1
    auto result = master_->catalog_manager()->GetTablespaceForTable(table);
1130
1
    if (result.ok()) {
1131
      // If the table is associated with a tablespace, display tablespace, otherwise
1132
      // just display replication info.
1133
1
      if (result.get()) {
1134
0
        tablespace_id = result.get().value();
1135
0
        *output << "  <tr><td>Tablespace OID:</td><td>"
1136
0
                << GetPgsqlTablespaceOid(tablespace_id)
1137
0
                << "  </td></tr>\n";
1138
0
      }
1139
1
      auto replication_info = CHECK_RESULT(master_->catalog_manager()->GetTableReplicationInfo(
1140
1
            l->pb.replication_info(), tablespace_id));
1141
1
      *output << "  <tr><td>Replication Info:</td><td>"
1142
1
              << "    <pre class=\"prettyprint\">" << replication_info.DebugString() << "</pre>"
1143
1
              << "  </td></tr>\n </table>\n";
1144
0
    } else {
1145
      // The table was associated with a tablespace, but that tablespace was not found.
1146
0
      *output << "  <tr><td>Replication Info:</td><td>";
1147
0
      if (FLAGS_ysql_tablespace_info_refresh_secs > 0) {
1148
0
        *output << "  Tablespace information not available now, please try again after "
1149
0
                << FLAGS_ysql_tablespace_info_refresh_secs << " seconds. ";
1150
0
      } else {
1151
0
        *output << "  Tablespace information is not available as the periodic task "
1152
0
                << "  used to refresh it is disabled.";
1153
1154
0
      }
1155
0
      *output << "  </td></tr>\n </table>\n";
1156
0
    }
1157
1158
1
    Status s = SchemaFromPB(l->pb.schema(), &schema);
1159
1
    if (s.ok()) {
1160
1
      s = PartitionSchema::FromPB(l->pb.partition_schema(), schema, &partition_schema);
1161
1
    }
1162
1
    if (!s.ok()) {
1163
0
      *output << "Unable to decode partition schema: " << s.ToString();
1164
0
      return;
1165
0
    }
1166
1
    tablets = table->GetTablets(IncludeInactive::kTrue);
1167
1
  }
1168
1169
1
  server::HtmlOutputSchemaTable(schema, output);
1170
1171
1
  *output << "<table class='table table-striped'>\n";
1172
1
  *output << "  <tr><th>Tablet ID</th><th>Partition</th><th>SplitDepth</th><th>State</th>"
1173
1
      "<th>Message</th><th>RaftConfig</th></tr>\n";
1174
3
  for (const scoped_refptr<TabletInfo>& tablet : tablets) {
1175
3
    auto locations = tablet->GetReplicaLocations();
1176
3
    vector<TabletReplica> sorted_locations;
1177
3
    AppendValuesFromMap(*locations, &sorted_locations);
1178
3
    std::sort(sorted_locations.begin(), sorted_locations.end(), &CompareByHost);
1179
1180
3
    auto l = tablet->LockForRead();
1181
1182
3
    Partition partition;
1183
3
    Partition::FromPB(l->pb.partition(), &partition);
1184
1185
3
    string state = SysTabletsEntryPB_State_Name(l->pb.state());
1186
3
    Capitalize(&state);
1187
3
    *output << Substitute(
1188
3
        "<tr><th>$0</th><td>$1</td><td>$2</td><td>$3</td><td>$4</td><td>$5</td></tr>\n",
1189
3
        tablet->tablet_id(),
1190
3
        EscapeForHtmlToString(partition_schema.PartitionDebugString(partition, schema)),
1191
3
        l->pb.split_depth(),
1192
3
        state,
1193
3
        EscapeForHtmlToString(l->pb.state_msg()),
1194
3
        RaftConfigToHtml(sorted_locations, tablet->tablet_id()));
1195
3
  }
1196
1
  *output << "</table>\n";
1197
1198
1
  HtmlOutputTasks(table->GetTasks(), output);
1199
1
}
1200
1201
void MasterPathHandlers::HandleTasksPage(const Webserver::WebRequest& req,
1202
0
                                         Webserver::WebResponse* resp) {
1203
0
  std::stringstream *output = &resp->output;
1204
0
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kAll);
1205
0
  *output << "<h3>Active Tasks</h3>\n";
1206
0
  *output << "<table class='table table-striped'>\n";
1207
0
  *output << "  <tr><th>Task Name</th><th>State</th><th>Start "
1208
0
             "Time</th><th>Time</th><th>Description</th></tr>\n";
1209
0
  for (const auto& table : tables) {
1210
0
    for (const auto& task : table->GetTasks()) {
1211
0
      HtmlOutputTask(task, output);
1212
0
    }
1213
0
  }
1214
0
  *output << "</table>\n";
1215
1216
0
  std::vector<std::shared_ptr<MonitoredTask>> jobs =
1217
0
      master_->catalog_manager()->GetRecentJobs();
1218
0
  *output << Substitute(
1219
0
      "<h3>Last $0 user-initiated jobs started in the past $1 "
1220
0
      "hours</h3>\n",
1221
0
      FLAGS_tasks_tracker_num_long_term_tasks,
1222
0
      FLAGS_long_term_tasks_tracker_keep_time_multiplier *
1223
0
          MonoDelta::FromMilliseconds(FLAGS_catalog_manager_bg_task_wait_ms)
1224
0
              .ToSeconds() /
1225
0
          3600);
1226
0
  *output << "<table class='table table-striped'>\n";
1227
0
  *output << "  <tr><th>Job Name</th><th>State</th><th>Start "
1228
0
             "Time</th><th>Duration</th><th>Description</th></tr>\n";
1229
0
  for (std::vector<std::shared_ptr<MonitoredTask>>::reverse_iterator iter =
1230
0
           jobs.rbegin();
1231
0
       iter != jobs.rend(); ++iter) {
1232
0
    HtmlOutputTask(*iter, output);
1233
0
  }
1234
0
  *output << "</table>\n";
1235
1236
0
  std::vector<std::shared_ptr<MonitoredTask> > tasks =
1237
0
    master_->catalog_manager()->GetRecentTasks();
1238
0
  *output << Substitute(
1239
0
      "<h3>Last $0 tasks started in the past $1 seconds</h3>\n",
1240
0
      FLAGS_tasks_tracker_num_tasks,
1241
0
      FLAGS_tasks_tracker_keep_time_multiplier *
1242
0
          MonoDelta::FromMilliseconds(FLAGS_catalog_manager_bg_task_wait_ms)
1243
0
              .ToSeconds());
1244
0
  *output << "<table class='table table-striped'>\n";
1245
0
  *output << "  <tr><th>Task Name</th><th>State</th><th>Start "
1246
0
             "Time</th><th>Duration</th><th>Description</th></tr>\n";
1247
0
  for (std::vector<std::shared_ptr<MonitoredTask>>::reverse_iterator iter =
1248
0
           tasks.rbegin();
1249
0
       iter != tasks.rend(); ++iter) {
1250
0
    HtmlOutputTask(*iter, output);
1251
0
  }
1252
0
  *output << "</table>\n";
1253
0
}
1254
1255
0
std::vector<TabletInfoPtr> MasterPathHandlers::GetNonSystemTablets() {
1256
0
  std::vector<TabletInfoPtr> nonsystem_tablets;
1257
1258
0
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1259
1260
0
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kRunning);
1261
1262
0
  for (const auto& table : tables) {
1263
0
    if (master_->catalog_manager()->IsSystemTable(*table.get())) {
1264
0
      continue;
1265
0
    }
1266
0
    TabletInfos ts = table->GetTablets(IncludeInactive::kTrue);
1267
1268
0
    for (TabletInfoPtr t : ts) {
1269
0
      nonsystem_tablets.push_back(t);
1270
0
    }
1271
0
  }
1272
0
  return nonsystem_tablets;
1273
0
}
1274
1275
0
std::vector<TabletInfoPtr> MasterPathHandlers::GetLeaderlessTablets() {
1276
0
  std::vector<TabletInfoPtr> leaderless_tablets;
1277
1278
0
  auto nonsystem_tablets = GetNonSystemTablets();
1279
1280
0
  for (TabletInfoPtr t : nonsystem_tablets) {
1281
0
    auto rm = t.get()->GetReplicaLocations();
1282
1283
0
    auto has_leader = std::any_of(
1284
0
      rm->begin(), rm->end(),
1285
0
      [](const auto &item) { return item.second.role == PeerRole::LEADER; });
1286
1287
0
    if (!has_leader) {
1288
0
      leaderless_tablets.push_back(t);
1289
0
    }
1290
0
  }
1291
0
  return leaderless_tablets;
1292
0
}
1293
1294
0
Result<std::vector<TabletInfoPtr>> MasterPathHandlers::GetUnderReplicatedTablets() {
1295
0
  std::vector<TabletInfoPtr> underreplicated_tablets;
1296
1297
0
  auto nonsystem_tablets = GetNonSystemTablets();
1298
1299
0
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1300
1301
0
  auto cluster_rf = VERIFY_RESULT_PREPEND(master_->catalog_manager()->GetReplicationFactor(),
1302
0
                                          "Unable to find replication factor");
1303
1304
0
  for (TabletInfoPtr t : nonsystem_tablets) {
1305
0
    auto rm = t.get()->GetReplicaLocations();
1306
1307
    // Find out the tablets which have been replicated less than the replication factor
1308
0
    if(rm->size() < cluster_rf) {
1309
0
      underreplicated_tablets.push_back(t);
1310
0
    }
1311
0
  }
1312
0
  return underreplicated_tablets;
1313
0
}
1314
1315
void MasterPathHandlers::HandleTabletReplicasPage(const Webserver::WebRequest& req,
1316
0
                                                  Webserver::WebResponse* resp) {
1317
0
  std::stringstream *output = &resp->output;
1318
1319
0
  auto leaderless_ts = GetLeaderlessTablets();
1320
0
  auto underreplicated_ts = GetUnderReplicatedTablets();
1321
1322
0
  *output << "<h3>Leaderless Tablets</h3>\n";
1323
0
  *output << "<table class='table table-striped'>\n";
1324
0
  *output << "  <tr><th>Table Name</th><th>Table UUID</th><th>Tablet ID</th></tr>\n";
1325
1326
0
  for (TabletInfoPtr t : leaderless_ts) {
1327
0
    *output << Substitute(
1328
0
        "<tr><td><a href=\"/table?id=$0\">$1</a></td><td>$2</td><th>$3</th></tr>\n",
1329
0
        EscapeForHtmlToString(t->table()->id()),
1330
0
        EscapeForHtmlToString(t->table()->name()),
1331
0
        EscapeForHtmlToString(t->table()->id()),
1332
0
        EscapeForHtmlToString(t.get()->tablet_id()));
1333
0
  }
1334
1335
0
  *output << "</table>\n";
1336
1337
0
  if(!underreplicated_ts.ok()) {
1338
0
    LOG(WARNING) << underreplicated_ts.ToString();
1339
0
    *output << "<h2>Call to get the cluster replication factor failed</h2>\n";
1340
0
    return;
1341
0
  }
1342
1343
0
  *output << "<h3>Underreplicated Tablets</h3>\n";
1344
0
  *output << "<table class='table table-striped'>\n";
1345
0
  *output << "  <tr><th>Table Name</th><th>Table UUID</th><th>Tablet ID</th>"
1346
0
          << "<th>Tablet Replication Count</th></tr>\n";
1347
1348
0
  for (TabletInfoPtr t : *underreplicated_ts) {
1349
0
    auto rm = t.get()->GetReplicaLocations();
1350
1351
0
    *output << Substitute(
1352
0
        "<tr><td><a href=\"/table?id=$0\">$1</a></td><td>$2</td>"
1353
0
        "<td>$3</td><td>$4</td></tr>\n",
1354
0
        EscapeForHtmlToString(t->table()->id()),
1355
0
        EscapeForHtmlToString(t->table()->name()),
1356
0
        EscapeForHtmlToString(t->table()->id()),
1357
0
        EscapeForHtmlToString(t.get()->tablet_id()),
1358
0
        EscapeForHtmlToString(std::to_string(rm->size())));
1359
0
  }
1360
1361
0
  *output << "</table>\n";
1362
0
}
1363
1364
void MasterPathHandlers::HandleGetReplicationStatus(const Webserver::WebRequest& req,
1365
0
                                                    Webserver::WebResponse* resp) {
1366
0
  std::stringstream *output = &resp->output;
1367
0
  JsonWriter jw(output, JsonWriter::COMPACT);
1368
1369
0
  auto leaderless_ts = GetLeaderlessTablets();
1370
1371
0
  jw.StartObject();
1372
0
  jw.String("leaderless_tablets");
1373
0
  jw.StartArray();
1374
1375
0
  for (TabletInfoPtr t : leaderless_ts) {
1376
0
    jw.StartObject();
1377
0
    jw.String("table_uuid");
1378
0
    jw.String(t->table()->id());
1379
0
    jw.String("tablet_uuid");
1380
0
    jw.String(t.get()->tablet_id());
1381
0
    jw.EndObject();
1382
0
  }
1383
1384
0
  jw.EndArray();
1385
0
  jw.EndObject();
1386
0
}
1387
1388
void MasterPathHandlers::HandleGetUnderReplicationStatus(const Webserver::WebRequest& req,
1389
0
                                                    Webserver::WebResponse* resp) {
1390
0
  std::stringstream *output = &resp->output;
1391
0
  JsonWriter jw(output, JsonWriter::COMPACT);
1392
1393
0
  auto underreplicated_ts = GetUnderReplicatedTablets();
1394
1395
0
  if(!underreplicated_ts.ok()) {
1396
0
    jw.StartObject();
1397
0
    jw.String("Error");
1398
0
    jw.String(underreplicated_ts.status().ToString());
1399
0
    jw.EndObject();
1400
0
    return;
1401
0
  }
1402
1403
0
  jw.StartObject();
1404
0
  jw.String("underreplicated_tablets");
1405
0
  jw.StartArray();
1406
1407
0
  for(TabletInfoPtr t : *underreplicated_ts) {
1408
0
    jw.StartObject();
1409
0
    jw.String("table_uuid");
1410
0
    jw.String(t->table()->id());
1411
0
    jw.String("tablet_uuid");
1412
0
    jw.String(t.get()->tablet_id());
1413
0
    jw.EndObject();
1414
0
  }
1415
1416
0
  jw.EndArray();
1417
0
  jw.EndObject();
1418
0
}
1419
1420
void MasterPathHandlers::RootHandler(const Webserver::WebRequest& req,
1421
1
                                     Webserver::WebResponse* resp) {
1422
1
  std::stringstream *output = &resp->output;
1423
  // First check if we are the master leader. If not, make a curl call to the master leader and
1424
  // return that as the UI payload.
1425
1
  SCOPED_LEADER_SHARED_LOCK(l, master_->catalog_manager_impl());
1426
1
  if (!l.first_failed_status().ok()) {
1427
    // We are not the leader master, retrieve the response from the leader master.
1428
0
    RedirectToLeader(req, resp);
1429
0
    return;
1430
0
  }
1431
1432
1
  SysClusterConfigEntryPB config;
1433
1
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
1434
1
  if (!s.ok()) {
1435
0
    *output << "<div class=\"alert alert-warning\">" << s.ToString() << "</div>";
1436
0
    return;
1437
0
  }
1438
1439
  // Get all the tables.
1440
1
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kRunning);
1441
1442
  // Get the list of user tables.
1443
1
  vector<scoped_refptr<TableInfo> > user_tables;
1444
18
  for (scoped_refptr<TableInfo> table : tables) {
1445
18
    if (master_->catalog_manager()->IsUserTable(*table)) {
1446
1
      user_tables.push_back(table);
1447
1
    }
1448
18
  }
1449
  // Get the version info.
1450
1
  VersionInfoPB version_info;
1451
1
  VersionInfo::GetVersionInfoPB(&version_info);
1452
1453
  // Display the overview information.
1454
1
  (*output) << "<h1>YugabyteDB</h1>\n";
1455
1456
1
  (*output) << "<div class='row dashboard-content'>\n";
1457
1458
1
  (*output) << "<div class='col-xs-12 col-md-8 col-lg-6'>\n";
1459
1
  (*output) << "<div class='panel panel-default'>\n"
1460
1
            << "<div class='panel-heading'><h2 class='panel-title'> Overview</h2></div>\n";
1461
1
  (*output) << "<div class='panel-body table-responsive'>";
1462
1
  (*output) << "<table class='table'>\n";
1463
1464
  // Universe UUID.
1465
1
  (*output) << "  <tr>";
1466
1
  (*output) << Substitute(" <td>$0<span class='yb-overview'>$1</span></td>",
1467
1
                          "<i class='fa fa-database yb-dashboard-icon' aria-hidden='true'></i>",
1468
1
                          "Universe UUID ");
1469
1
  (*output) << Substitute(" <td>$0</td>",
1470
1
                          config.cluster_uuid());
1471
1
  (*output) << "  </tr>\n";
1472
1473
  // Replication factor.
1474
1
  (*output) << "  <tr>";
1475
1
  (*output) << Substitute(" <td>$0<span class='yb-overview'>$1</span></td>",
1476
1
                          "<i class='fa fa-files-o yb-dashboard-icon' aria-hidden='true'></i>",
1477
1
                          "Replication Factor ");
1478
1
  auto num_replicas = master_->catalog_manager()->GetReplicationFactor();
1479
1
  if (!num_replicas.ok()) {
1480
0
    num_replicas = num_replicas.status().CloneAndPrepend("Unable to determine Replication factor.");
1481
0
    LOG(WARNING) << num_replicas.status();
1482
0
  }
1483
1
  (*output) << Format(" <td>$0 <a href='$1' class='btn btn-default pull-right'>$2</a></td>",
1484
1
                      num_replicas, "/cluster-config", "See full config &raquo;");
1485
1
  (*output) << "  </tr>\n";
1486
1487
  // Tserver count.
1488
1
  (*output) << "  <tr>";
1489
1
  (*output) << Substitute(" <td>$0<span class='yb-overview'>$1</span></td>",
1490
1
                          "<i class='fa fa-server yb-dashboard-icon' aria-hidden='true'></i>",
1491
1
                          "Num Nodes (TServers) ");
1492
1
  (*output) << Substitute(" <td>$0 <a href='$1' class='btn btn-default pull-right'>$2</a></td>",
1493
1
                          GetTserverCountForDisplay(master_->ts_manager()),
1494
1
                          "/tablet-servers",
1495
1
                          "See all nodes &raquo;");
1496
1
  (*output) << "  </tr>\n";
1497
1498
  // Num user tables.
1499
1
  (*output) << "  <tr>";
1500
1
  (*output) << Substitute(" <tr><td>$0<span class='yb-overview'>$1</span></td>",
1501
1
                          "<i class='fa fa-table yb-dashboard-icon' aria-hidden='true'></i>",
1502
1
                          "Num User Tables ");
1503
1
  (*output) << Substitute(" <td>$0 <a href='$1' class='btn btn-default pull-right'>$2</a></td>",
1504
1
                          user_tables.size(),
1505
1
                          "/tables",
1506
1
                          "See all tables &raquo;");
1507
1
  (*output) << "  </tr>\n";
1508
1509
  // Load balancer status.
1510
1
  bool load_balancer_enabled = master_->catalog_manager()->IsLoadBalancerEnabled();
1511
1
  (*output) << Substitute(" <tr><td>$0<span class='yb-overview'>$1</span></td>"
1512
1
                          "<td><i class='fa $2' aria-hidden='true'> </i></td></tr>\n",
1513
1
                          "<i class='fa fa-tasks yb-dashboard-icon' aria-hidden='true'></i>",
1514
1
                          "Load Balancer Enabled",
1515
1
                          load_balancer_enabled ? "fa-check"
1516
0
                                                : "fa-times label label-danger");
1517
1
  if (load_balancer_enabled) {
1518
1
    IsLoadBalancedRequestPB req;
1519
1
    IsLoadBalancedResponsePB resp;
1520
1
    Status load_balanced = master_->catalog_manager()->IsLoadBalanced(&req, &resp);
1521
1522
1
    (*output) << Substitute(" <tr><td>$0<span class='yb-overview'>$1</span></td>"
1523
1
                            "<td><i class='fa $2' aria-hidden='true'> </i></td></tr>\n",
1524
1
                            "<i class='fa fa-tasks yb-dashboard-icon' aria-hidden='true'></i>",
1525
1
                            "Is Load Balanced?",
1526
0
                            load_balanced.ok() ? "fa-check"
1527
1
                                               : "fa-times label label-danger");
1528
1
  }
1529
1530
  // Build version and type.
1531
1
  (*output) << Substitute("  <tr><td>$0<span class='yb-overview'>$1</span></td><td>$2</td></tr>\n",
1532
1
                          "<i class='fa fa-code-fork yb-dashboard-icon' aria-hidden='true'></i>",
1533
1
                          "YugabyteDB Version ", version_info.version_number());
1534
1
  (*output) << Substitute("  <tr><td>$0<span class='yb-overview'>$1</span></td><td>$2</td></tr>\n",
1535
1
                          "<i class='fa fa-terminal yb-dashboard-icon' aria-hidden='true'></i>",
1536
1
                          "Build Type ", version_info.build_type());
1537
1
  (*output) << "</table>";
1538
1
  (*output) << "</div> <!-- panel-body -->\n";
1539
1
  (*output) << "</div> <!-- panel -->\n";
1540
1
  (*output) << "</div> <!-- col-xs-12 col-md-8 col-lg-6 -->\n";
1541
1542
  // Display the master info.
1543
1
  (*output) << "<div class='col-xs-12 col-md-8 col-lg-6'>\n";
1544
1
  HandleMasters(req, resp);
1545
1
  (*output) << "</div> <!-- col-xs-12 col-md-8 col-lg-6 -->\n";
1546
1
}
1547
1548
void MasterPathHandlers::HandleMasters(const Webserver::WebRequest& req,
1549
3
                                       Webserver::WebResponse* resp) {
1550
3
  std::stringstream *output = &resp->output;
1551
3
  vector<ServerEntryPB> masters;
1552
3
  Status s = master_->ListMasters(&masters);
1553
3
  if (!s.ok()) {
1554
0
    s = s.CloneAndPrepend("Unable to list Masters");
1555
0
    LOG(WARNING) << s.ToString();
1556
0
    *output << "<h1>" << s.ToString() << "</h1>\n";
1557
0
    return;
1558
0
  }
1559
3
  (*output) << "<div class='panel panel-default'>\n"
1560
3
            << "<div class='panel-heading'><h2 class='panel-title'>Masters</h2></div>\n";
1561
3
  (*output) << "<div class='panel-body table-responsive'>";
1562
3
  (*output) << "<table class='table'>\n";
1563
3
  (*output) << "  <tr>\n"
1564
3
            << "    <th>Server</th>\n"
1565
3
            << "    <th>RAFT Role</th>\n"
1566
3
            << "    <th>Uptime</th>\n"
1567
3
            << "    <th>Details</th>\n"
1568
3
            << "  </tr>\n";
1569
1570
5
  for (const ServerEntryPB& master : masters) {
1571
5
    if (master.has_error()) {
1572
0
      string error = StatusFromPB(master.error()).ToString();
1573
0
      *output << "  <tr>\n";
1574
0
      const string kErrStart = "peer ([";
1575
0
      const string kErrEnd = "])";
1576
0
      size_t start_pos = error.find(kErrStart);
1577
0
      size_t end_pos = error.find(kErrEnd);
1578
0
      if (start_pos != string::npos && end_pos != string::npos && (start_pos < end_pos)) {
1579
0
        start_pos = start_pos + kErrStart.length();
1580
0
        string host_port = error.substr(start_pos, end_pos - start_pos);
1581
0
        *output << "<td><font color='red'>" << EscapeForHtmlToString(host_port)
1582
0
                << "</font></td>\n";
1583
0
        *output << "<td><font color='red'>" << PeerRole_Name(PeerRole::UNKNOWN_ROLE)
1584
0
                << "</font></td>\n";
1585
0
      }
1586
0
      *output << Substitute("    <td colspan=2><font color='red'><b>ERROR: $0</b></font></td>\n",
1587
0
                              EscapeForHtmlToString(error));
1588
0
      *output << "  </tr>\n";
1589
0
      continue;
1590
0
    }
1591
5
    auto reg = master.registration();
1592
5
    string host_port = GetHttpHostPortFromServerRegistration(reg);
1593
5
    string reg_text = RegistrationToHtml(reg, host_port);
1594
5
    if (master.instance_id().permanent_uuid() == master_->instance_pb().permanent_uuid()) {
1595
3
      reg_text = Substitute("<b>$0</b>", reg_text);
1596
3
    }
1597
5
    string raft_role = master.has_role() ? PeerRole_Name(master.role()) : "N/A";
1598
5
    auto delta = Env::Default()->NowMicros() - master.instance_id().start_time_us();
1599
5
    string uptime = UptimeString(MonoDelta::FromMicroseconds(delta).ToSeconds());
1600
5
    string cloud = reg.cloud_info().placement_cloud();
1601
5
    string region = reg.cloud_info().placement_region();
1602
5
    string zone = reg.cloud_info().placement_zone();
1603
1604
5
    *output << "  <tr>\n"
1605
5
            << "    <td>" << reg_text << "</td>\n"
1606
5
            << "    <td>" << raft_role << "</td>\n"
1607
5
            << "    <td>" << uptime << "</td>\n"
1608
5
            << "    <td><div><span class='yb-overview'>CLOUD: </span>" << cloud << "</div>\n"
1609
5
            << "        <div><span class='yb-overview'>REGION: </span>" << region << "</div>\n"
1610
5
            << "        <div><span class='yb-overview'>ZONE: </span>" << zone << "</div>\n"
1611
5
            << "        <div><span class='yb-overview'>UUID: </span>"
1612
5
            << master.instance_id().permanent_uuid()
1613
5
            << "</div></td>\n"
1614
5
            << "  </tr>\n";
1615
5
  }
1616
1617
3
  (*output) << "</table>";
1618
3
  (*output) << "</div> <!-- panel-body -->\n";
1619
3
  (*output) << "</div> <!-- panel -->\n";
1620
3
}
1621
1622
namespace {
1623
1624
// Visitor for the catalog table which dumps tables and tablets in a JSON format. This
1625
// dump is interpreted by the CM agent in order to track time series entities in the SMON
1626
// database.
1627
//
1628
// This implementation relies on scanning the catalog table directly instead of using the
1629
// catalog manager APIs. This allows it to work even on a non-leader master, and avoids
1630
// any requirement for locking. For the purposes of metrics entity gathering, it's OK to
1631
// serve a slightly stale snapshot.
1632
//
1633
// It is tempting to directly dump the metadata protobufs using JsonWriter::Protobuf(...),
1634
// but then we would be tying ourselves to textual compatibility of the PB field names in
1635
// our catalog table. Instead, the implementation specifically dumps the fields that we
1636
// care about.
1637
//
1638
// This should be considered a "stable" protocol -- do not rename, remove, or restructure
1639
// without consulting with the CM team.
1640
class JsonDumperBase {
1641
 public:
1642
3
  explicit JsonDumperBase(JsonWriter* jw) : jw_(jw) {}
1643
1644
3
  virtual ~JsonDumperBase() {}
1645
1646
  virtual std::string name() const = 0;
1647
1648
 protected:
1649
  JsonWriter* jw_;
1650
};
1651
1652
class JsonKeyspaceDumper : public Visitor<PersistentNamespaceInfo>, public JsonDumperBase {
1653
 public:
1654
1
  explicit JsonKeyspaceDumper(JsonWriter* jw) : JsonDumperBase(jw) {}
1655
1656
1
  std::string name() const override { return "keyspaces"; }
1657
1658
  virtual Status Visit(const std::string& keyspace_id,
1659
4
                       const SysNamespaceEntryPB& metadata) override {
1660
4
    jw_->StartObject();
1661
4
    jw_->String("keyspace_id");
1662
4
    jw_->String(keyspace_id);
1663
1664
4
    jw_->String("keyspace_name");
1665
4
    jw_->String(metadata.name());
1666
1667
4
    jw_->String("keyspace_type");
1668
4
    jw_->String(DatabaseTypeName((metadata.database_type())));
1669
1670
4
    jw_->EndObject();
1671
4
    return Status::OK();
1672
4
  }
1673
};
1674
1675
class JsonTableDumper : public Visitor<PersistentTableInfo>, public JsonDumperBase {
1676
 public:
1677
1
  explicit JsonTableDumper(JsonWriter* jw) : JsonDumperBase(jw) {}
1678
1679
1
  std::string name() const override { return "tables"; }
1680
1681
18
  Status Visit(const std::string& table_id, const SysTablesEntryPB& metadata) override {
1682
18
    if (metadata.state() != SysTablesEntryPB::RUNNING) {
1683
0
      return Status::OK();
1684
0
    }
1685
1686
18
    jw_->StartObject();
1687
18
    jw_->String("table_id");
1688
18
    jw_->String(table_id);
1689
1690
18
    jw_->String("keyspace_id");
1691
18
    jw_->String(metadata.namespace_id());
1692
1693
18
    jw_->String("table_name");
1694
18
    jw_->String(metadata.name());
1695
1696
18
    jw_->String("state");
1697
18
    jw_->String(SysTablesEntryPB::State_Name(metadata.state()));
1698
1699
18
    jw_->EndObject();
1700
18
    return Status::OK();
1701
18
  }
1702
};
1703
1704
class JsonTabletDumper : public Visitor<PersistentTabletInfo>, public JsonDumperBase {
1705
 public:
1706
1
  explicit JsonTabletDumper(JsonWriter* jw) : JsonDumperBase(jw) {}
1707
1708
1
  std::string name() const override { return "tablets"; }
1709
1710
20
  Status Visit(const std::string& tablet_id, const SysTabletsEntryPB& metadata) override {
1711
20
    const std::string& table_id = metadata.table_id();
1712
20
    if (metadata.state() != SysTabletsEntryPB::RUNNING) {
1713
0
      return Status::OK();
1714
0
    }
1715
1716
20
    jw_->StartObject();
1717
20
    jw_->String("table_id");
1718
20
    jw_->String(table_id);
1719
1720
20
    jw_->String("tablet_id");
1721
20
    jw_->String(tablet_id);
1722
1723
20
    jw_->String("state");
1724
20
    jw_->String(SysTabletsEntryPB::State_Name(metadata.state()));
1725
1726
    // Dump replica UUIDs
1727
20
    if (metadata.has_committed_consensus_state()) {
1728
3
      const consensus::ConsensusStatePB& cs = metadata.committed_consensus_state();
1729
3
      jw_->String("replicas");
1730
3
      jw_->StartArray();
1731
9
      for (const RaftPeerPB& peer : cs.config().peers()) {
1732
9
        jw_->StartObject();
1733
9
        jw_->String("type");
1734
9
        jw_->String(PeerMemberType_Name(peer.member_type()));
1735
1736
9
        jw_->String("server_uuid");
1737
9
        jw_->String(peer.permanent_uuid());
1738
1739
9
        jw_->String("addr");
1740
9
        const auto& host_port = peer.last_known_private_addr()[0];
1741
9
        jw_->String(HostPortPBToString(host_port));
1742
1743
9
        jw_->EndObject();
1744
9
      }
1745
3
      jw_->EndArray();
1746
1747
3
      if (cs.has_leader_uuid()) {
1748
3
        jw_->String("leader");
1749
3
        jw_->String(cs.leader_uuid());
1750
3
      }
1751
3
    }
1752
1753
20
    jw_->EndObject();
1754
20
    return Status::OK();
1755
20
  }
1756
};
1757
1758
template <class Dumper>
1759
3
Status JsonDumpCollection(JsonWriter* jw, Master* master, stringstream* output) {
1760
3
  unique_ptr<Dumper> json_dumper(new Dumper(jw));
1761
3
  jw->String(json_dumper->name());
1762
3
  jw->StartArray();
1763
3
  const Status s = master->catalog_manager()->sys_catalog()->Visit(json_dumper.get());
1764
3
  if (s.ok()) {
1765
    // End the array only if there is no error.
1766
3
    jw->EndArray();
1767
0
  } else {
1768
    // Print just an error message.
1769
0
    output->str("");
1770
0
    JsonWriter jw_err(output, JsonWriter::COMPACT);
1771
0
    jw_err.StartObject();
1772
0
    jw_err.String("error");
1773
0
    jw_err.String(s.ToString());
1774
0
    jw_err.EndObject();
1775
0
  }
1776
3
  return s;
1777
3
}
master-path-handlers.cc:_ZN2yb6master12_GLOBAL__N_118JsonDumpCollectionINS1_18JsonKeyspaceDumperEEENS_6StatusEPNS_10JsonWriterEPNS0_6MasterEPNSt3__118basic_stringstreamIcNS9_11char_traitsIcEENS9_9allocatorIcEEEE
Line
Count
Source
1759
1
Status JsonDumpCollection(JsonWriter* jw, Master* master, stringstream* output) {
1760
1
  unique_ptr<Dumper> json_dumper(new Dumper(jw));
1761
1
  jw->String(json_dumper->name());
1762
1
  jw->StartArray();
1763
1
  const Status s = master->catalog_manager()->sys_catalog()->Visit(json_dumper.get());
1764
1
  if (s.ok()) {
1765
    // End the array only if there is no error.
1766
1
    jw->EndArray();
1767
0
  } else {
1768
    // Print just an error message.
1769
0
    output->str("");
1770
0
    JsonWriter jw_err(output, JsonWriter::COMPACT);
1771
0
    jw_err.StartObject();
1772
0
    jw_err.String("error");
1773
0
    jw_err.String(s.ToString());
1774
0
    jw_err.EndObject();
1775
0
  }
1776
1
  return s;
1777
1
}
master-path-handlers.cc:_ZN2yb6master12_GLOBAL__N_118JsonDumpCollectionINS1_15JsonTableDumperEEENS_6StatusEPNS_10JsonWriterEPNS0_6MasterEPNSt3__118basic_stringstreamIcNS9_11char_traitsIcEENS9_9allocatorIcEEEE
Line
Count
Source
1759
1
Status JsonDumpCollection(JsonWriter* jw, Master* master, stringstream* output) {
1760
1
  unique_ptr<Dumper> json_dumper(new Dumper(jw));
1761
1
  jw->String(json_dumper->name());
1762
1
  jw->StartArray();
1763
1
  const Status s = master->catalog_manager()->sys_catalog()->Visit(json_dumper.get());
1764
1
  if (s.ok()) {
1765
    // End the array only if there is no error.
1766
1
    jw->EndArray();
1767
0
  } else {
1768
    // Print just an error message.
1769
0
    output->str("");
1770
0
    JsonWriter jw_err(output, JsonWriter::COMPACT);
1771
0
    jw_err.StartObject();
1772
0
    jw_err.String("error");
1773
0
    jw_err.String(s.ToString());
1774
0
    jw_err.EndObject();
1775
0
  }
1776
1
  return s;
1777
1
}
master-path-handlers.cc:_ZN2yb6master12_GLOBAL__N_118JsonDumpCollectionINS1_16JsonTabletDumperEEENS_6StatusEPNS_10JsonWriterEPNS0_6MasterEPNSt3__118basic_stringstreamIcNS9_11char_traitsIcEENS9_9allocatorIcEEEE
Line
Count
Source
1759
1
Status JsonDumpCollection(JsonWriter* jw, Master* master, stringstream* output) {
1760
1
  unique_ptr<Dumper> json_dumper(new Dumper(jw));
1761
1
  jw->String(json_dumper->name());
1762
1
  jw->StartArray();
1763
1
  const Status s = master->catalog_manager()->sys_catalog()->Visit(json_dumper.get());
1764
1
  if (s.ok()) {
1765
    // End the array only if there is no error.
1766
1
    jw->EndArray();
1767
0
  } else {
1768
    // Print just an error message.
1769
0
    output->str("");
1770
0
    JsonWriter jw_err(output, JsonWriter::COMPACT);
1771
0
    jw_err.StartObject();
1772
0
    jw_err.String("error");
1773
0
    jw_err.String(s.ToString());
1774
0
    jw_err.EndObject();
1775
0
  }
1776
1
  return s;
1777
1
}
1778
1779
} // anonymous namespace
1780
1781
void MasterPathHandlers::HandleDumpEntities(const Webserver::WebRequest& req,
1782
1
                                            Webserver::WebResponse* resp) {
1783
1
  std::stringstream *output = &resp->output;
1784
1
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1785
1786
1
  JsonWriter jw(output, JsonWriter::COMPACT);
1787
1
  jw.StartObject();
1788
1789
1
  if (JsonDumpCollection<JsonKeyspaceDumper>(&jw, master_, output).ok() &&
1790
1
      JsonDumpCollection<JsonTableDumper>(&jw, master_, output).ok() &&
1791
1
      JsonDumpCollection<JsonTabletDumper>(&jw, master_, output).ok()) {
1792
    // End the object only if there is no error.
1793
1
    jw.EndObject();
1794
1
  }
1795
1
}
1796
1797
void MasterPathHandlers::HandleCheckIfLeader(const Webserver::WebRequest& req,
1798
3
                                              Webserver::WebResponse* resp) {
1799
3
  std::stringstream *output = &resp->output;
1800
3
  JsonWriter jw(output, JsonWriter::COMPACT);
1801
3
  jw.StartObject();
1802
3
  {
1803
3
    SCOPED_LEADER_SHARED_LOCK(l, master_->catalog_manager_impl());
1804
1805
    // If we are not the master leader.
1806
3
    if (!l.first_failed_status().ok()) {
1807
2
      resp->code = 503;
1808
2
      return;
1809
2
    }
1810
1811
1
    jw.String("STATUS");
1812
1
    jw.String(l.leader_status().CodeAsString());
1813
1
    jw.EndObject();
1814
1
    return;
1815
1
  }
1816
1
}
1817
1818
void MasterPathHandlers::HandleGetMastersStatus(const Webserver::WebRequest& req,
1819
3
                                                    Webserver::WebResponse* resp) {
1820
3
  std::stringstream *output = &resp->output;
1821
3
  vector<ServerEntryPB> masters;
1822
3
  Status s = master_->ListMasters(&masters);
1823
3
  ListMastersResponsePB pb_resp;
1824
3
  JsonWriter jw(output, JsonWriter::COMPACT);
1825
3
  if (!s.ok()) {
1826
0
    jw.Protobuf(pb_resp);
1827
0
    return;
1828
0
  }
1829
9
  for (const ServerEntryPB& master : masters) {
1830
9
    pb_resp.add_masters()->CopyFrom(master);
1831
9
  }
1832
3
  jw.Protobuf(pb_resp);
1833
3
}
1834
1835
void MasterPathHandlers::HandleGetClusterConfig(
1836
0
  const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
1837
0
  std::stringstream *output = &resp->output;
1838
0
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1839
1840
0
  *output << "<h1>Current Cluster Config</h1>\n";
1841
0
  SysClusterConfigEntryPB config;
1842
0
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
1843
0
  if (!s.ok()) {
1844
0
    *output << "<div class=\"alert alert-warning\">" << s.ToString() << "</div>";
1845
0
    return;
1846
0
  }
1847
1848
0
  *output << "<div class=\"alert alert-success\">Successfully got cluster config!</div>"
1849
0
  << "<pre class=\"prettyprint\">" << config.DebugString() << "</pre>";
1850
0
}
1851
1852
void MasterPathHandlers::HandleGetClusterConfigJSON(
1853
0
  const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
1854
0
  std::stringstream *output = &resp->output;
1855
0
  JsonWriter jw(output, JsonWriter::COMPACT);
1856
1857
0
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1858
1859
0
  SysClusterConfigEntryPB config;
1860
0
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
1861
0
  if (!s.ok()) {
1862
0
    jw.StartObject();
1863
0
    jw.String("error");
1864
0
    jw.String(s.ToString());
1865
0
    jw.EndObject();
1866
0
    return;
1867
0
  }
1868
1869
  // return cluster config in JSON format
1870
0
  jw.Protobuf(config);
1871
0
}
1872
1873
void MasterPathHandlers::HandleVersionInfoDump(
1874
0
    const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
1875
0
  std::stringstream *output = &resp->output;
1876
0
  JsonWriter jw(output, JsonWriter::PRETTY);
1877
1878
  // Get the version info.
1879
0
  VersionInfoPB version_info;
1880
0
  VersionInfo::GetVersionInfoPB(&version_info);
1881
1882
0
  jw.Protobuf(version_info);
1883
0
}
1884
1885
void MasterPathHandlers::HandlePrettyLB(
1886
0
  const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
1887
1888
0
  std::stringstream *output = &resp->output;
1889
1890
  // Don't render if there are more than 5 tservers.
1891
0
  vector<std::shared_ptr<TSDescriptor>> descs;
1892
0
  const auto& ts_manager = master_->ts_manager();
1893
0
  ts_manager->GetAllDescriptors(&descs);
1894
1895
0
  if (descs.size() > 5) {
1896
0
    *output << "<div class='alert alert-warning'>"
1897
0
            << "Current configuration has more than 5 tservers. Not recommended"
1898
0
            << " to view this pretty display as it might not be rendered properly."
1899
0
            << "</div>";
1900
0
    return;
1901
0
  }
1902
1903
  // Don't render if there is a lot of placement nesting.
1904
0
  std::unordered_set<std::string> clouds;
1905
0
  std::unordered_set<std::string> regions;
1906
  // Map of zone -> {tserver UUIDs}
1907
  // e.g. zone1 -> {ts1uuid, ts2uuid, ts3uuid}.
1908
0
  std::unordered_map<std::string, vector<std::string>> zones;
1909
0
  for (const auto& desc : descs) {
1910
0
    std::string uuid = desc->permanent_uuid();
1911
0
    std::string cloud = desc->GetCloudInfo().placement_cloud();
1912
0
    std::string region = desc->GetCloudInfo().placement_region();
1913
0
    std::string zone = desc->GetCloudInfo().placement_zone();
1914
1915
0
    zones[zone].push_back(uuid);
1916
0
    clouds.insert(cloud);
1917
0
    regions.insert(region);
1918
0
  }
1919
1920
  // If the we have more than 1 cloud or more than 1 region skip this page
1921
  // as currently it might not diplay prettily.
1922
0
  if (clouds.size() > 1 || regions.size() > 1 || zones.size() > 3) {
1923
0
    *output << "<div class='alert alert-warning'>"
1924
0
            << "Current placement has more than 1 cloud provider or 1 region or 3 zones. "
1925
0
            << "Not recommended to view this pretty display as it might not be rendered properly."
1926
0
            << "</div>";
1927
0
    return;
1928
0
  }
1929
1930
  // Get the TServerTree.
1931
  // A map of tserver -> all tables with their tablets.
1932
0
  TServerTree tserver_tree;
1933
0
  Status s = CalculateTServerTree(&tserver_tree);
1934
0
  if (!s.ok()) {
1935
0
    *output << "<div class='alert alert-warning'>"
1936
0
            << "Current placement has more than 4 tables. Not recommended"
1937
0
            << " to view this pretty display as it might not be rendered properly."
1938
0
            << "</div>";
1939
0
    return;
1940
0
  }
1941
1942
0
  BlacklistSet blacklist = master_->catalog_manager()->BlacklistSetFromPB();
1943
1944
  // A single zone.
1945
0
  int color_index = 0;
1946
0
  std::unordered_map<std::string, std::string> tablet_colors;
1947
1948
0
  *output << "<div class='row'>\n";
1949
0
  for (const auto& zone : zones) {
1950
    // Panel for this Zone.
1951
    // Split the zones in proportion of the number of tservers in each zone.
1952
0
    *output << Substitute("<div class='col-lg-$0'>\n", 12*zone.second.size()/descs.size());
1953
1954
    // Change the display of the panel if all tservers in this zone are down.
1955
0
    bool all_tservers_down = true;
1956
0
    for (const auto& tserver : zone.second) {
1957
0
      std::shared_ptr<TSDescriptor> desc;
1958
0
      if (!master_->ts_manager()->LookupTSByUUID(tserver, &desc)) {
1959
0
        continue;
1960
0
      }
1961
0
      all_tservers_down = all_tservers_down && !desc->IsLive();
1962
0
    }
1963
0
    string zone_panel_display = "panel-success";
1964
0
    if (all_tservers_down) {
1965
0
      zone_panel_display = "panel-danger";
1966
0
    }
1967
1968
0
    *output << Substitute("<div class='panel $0'>\n", zone_panel_display);
1969
0
    *output << Substitute("<div class='panel-heading'>"
1970
0
                          "<h6 class='panel-title'>Zone: $0</h6></div>\n", zone.first);
1971
0
    *output << "<div class='row'>\n";
1972
1973
    // Tservers for this panel.
1974
0
    for (const auto& tserver : zone.second) {
1975
      // Split tservers equally.
1976
0
      *output << Substitute("<div class='col-lg-$0'>\n", 12/(zone.second.size()));
1977
0
      std::shared_ptr<TSDescriptor> desc;
1978
0
      if (!master_->ts_manager()->LookupTSByUUID(tserver, &desc)) {
1979
0
        continue;
1980
0
      }
1981
1982
      // Get the state of tserver.
1983
0
      bool ts_live = desc->IsLive();
1984
      // Get whether tserver is blacklisted.
1985
0
      bool blacklisted = desc->IsBlacklisted(blacklist);
1986
0
      string panel_type = "panel-success";
1987
0
      string icon_type = "fa-check";
1988
0
      if (!ts_live || blacklisted) {
1989
0
        panel_type = "panel-danger";
1990
0
        icon_type = "fa-times";
1991
0
      }
1992
0
      *output << Substitute("<div class='panel $0' style='margin-bottom: 0px'>\n", panel_type);
1993
1994
      // Point to the tablet servers link.
1995
0
      TSRegistrationPB reg = desc->GetRegistration();
1996
0
      string host_port = GetHttpHostPortFromServerRegistration(reg.common());
1997
0
      *output << Substitute("<div class='panel-heading'>"
1998
0
                            "<h6 class='panel-title'><a href='http://$0'>TServer - $0    "
1999
0
                            "<i class='fa $1'></i></a></h6></div>\n",
2000
0
                            HostPortPBToString(reg.common().http_addresses(0)),
2001
0
                            icon_type);
2002
2003
0
      *output << "<table class='table table-borderless table-hover'>\n";
2004
0
      for (const auto& table : tserver_tree[tserver]) {
2005
0
        *output << Substitute("<tr height='200px'>\n");
2006
        // Display the table name.
2007
0
        string tname = master_->catalog_manager()->GetTableInfo(table.first)->name();
2008
        // Link the table name to the corresponding table page on the master.
2009
0
        ServerRegistrationPB reg;
2010
0
        if (!master_->GetMasterRegistration(&reg).ok()) {
2011
0
          continue;
2012
0
        }
2013
0
        *output << Substitute("<td><h4><a href='http://$0/table?id=$1'>"
2014
0
                              "<i class='fa fa-table'></i>    $2</a></h4>\n",
2015
0
                              HostPortPBToString(reg.http_addresses(0)),
2016
0
                              table.first,
2017
0
                              tname);
2018
        // Replicas of this table.
2019
0
        for (const auto& replica : table.second) {
2020
          // All the replicas of the same tablet will have the same color, so
2021
          // look it up in the map if assigned, otherwise assign one from the pool.
2022
0
          if (tablet_colors.find(replica.tablet_id) == tablet_colors.end()) {
2023
0
            tablet_colors[replica.tablet_id] = kYBColorList[color_index];
2024
0
            color_index = (color_index + 1)%kYBColorList.size();
2025
0
          }
2026
2027
          // Leaders and followers have different formatting.
2028
          // Leaders need to stand out.
2029
0
          if (replica.role == PeerRole::LEADER) {
2030
0
            *output << Substitute("<button type='button' class='btn btn-default'"
2031
0
                                "style='background-image:none; border: 6px solid $0; "
2032
0
                                "font-weight: bolder'>"
2033
0
                                "L</button>\n",
2034
0
                                tablet_colors[replica.tablet_id]);
2035
0
          } else {
2036
0
            *output << Substitute("<button type='button' class='btn btn-default'"
2037
0
                                "style='background-image:none; border: 4px dotted $0'>"
2038
0
                                "F</button>\n",
2039
0
                                tablet_colors[replica.tablet_id]);
2040
0
          }
2041
0
        }
2042
0
        *output << "</td>\n";
2043
0
        *output << "</tr>\n";
2044
0
      }
2045
0
      *output << "</table><!-- tserver-level-table -->\n";
2046
0
      *output << "</div><!-- tserver-level-panel -->\n";
2047
0
      *output << "</div><!-- tserver-level-spacing -->\n";
2048
0
    }
2049
0
    *output << "</div><!-- tserver-level-row -->\n";
2050
0
    *output << "</div><!-- zone-level-panel -->\n";
2051
0
    *output << "</div><!-- zone-level-spacing -->\n";
2052
0
  }
2053
0
  *output << "</div><!-- zone-level-row -->\n";
2054
0
}
2055
2056
5.42k
Status MasterPathHandlers::Register(Webserver* server) {
2057
5.42k
  bool is_styled = true;
2058
5.42k
  bool is_on_nav_bar = true;
2059
2060
  // The set of handlers visible on the nav bar.
2061
5.42k
  server->RegisterPathHandler(
2062
5.42k
    "/", "Home", std::bind(&MasterPathHandlers::RootHandler, this, _1, _2), is_styled,
2063
5.42k
    is_on_nav_bar, "fa fa-home");
2064
5.42k
  Webserver::PathHandlerCallback cb =
2065
5.42k
      std::bind(&MasterPathHandlers::HandleTabletServers, this, _1, _2,
2066
5.42k
                TServersViewType::kTServersDefaultView);
2067
5.42k
  server->RegisterPathHandler(
2068
5.42k
      "/tablet-servers", "Tablet Servers",
2069
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2070
5.42k
      is_on_nav_bar, "fa fa-server");
2071
5.42k
  cb = std::bind(&MasterPathHandlers::HandleTabletServers, this, _1, _2,
2072
5.42k
                 TServersViewType::kTServersClocksView);
2073
5.42k
  server->RegisterPathHandler(
2074
5.42k
      "/tablet-server-clocks", "Tablet Server Clocks",
2075
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2076
5.42k
      false /* is_on_nav_bar */);
2077
5.42k
  cb = std::bind(&MasterPathHandlers::HandleCatalogManager,
2078
5.42k
      this, _1, _2, false /* only_user_tables */);
2079
5.42k
  server->RegisterPathHandler(
2080
5.42k
      "/tables", "Tables",
2081
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2082
5.42k
      is_on_nav_bar, "fa fa-table");
2083
2084
  // The set of handlers not currently visible on the nav bar.
2085
5.42k
  cb = std::bind(&MasterPathHandlers::HandleTablePage, this, _1, _2);
2086
5.42k
  server->RegisterPathHandler(
2087
5.42k
      "/table", "", std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb),
2088
5.42k
      is_styled, false);
2089
5.42k
  server->RegisterPathHandler(
2090
5.42k
      "/masters", "Masters", std::bind(&MasterPathHandlers::HandleMasters, this, _1, _2), is_styled,
2091
5.42k
      false);
2092
5.42k
  cb = std::bind(&MasterPathHandlers::HandleGetClusterConfig, this, _1, _2);
2093
5.42k
  server->RegisterPathHandler(
2094
5.42k
      "/cluster-config", "Cluster Config",
2095
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2096
5.42k
      false);
2097
5.42k
  cb = std::bind(&MasterPathHandlers::HandleGetClusterConfigJSON, this, _1, _2);
2098
5.42k
  server->RegisterPathHandler(
2099
5.42k
      "/api/v1/cluster-config", "Cluster Config JSON",
2100
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false,
2101
5.42k
      false);
2102
5.42k
  cb = std::bind(&MasterPathHandlers::HandleTasksPage, this, _1, _2);
2103
5.42k
  server->RegisterPathHandler(
2104
5.42k
      "/tasks", "Tasks",
2105
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2106
5.42k
      false);
2107
5.42k
  cb = std::bind(&MasterPathHandlers::HandleTabletReplicasPage, this, _1, _2);
2108
5.42k
  server->RegisterPathHandler(
2109
5.42k
      "/tablet-replication", "Tablet Replication Health",
2110
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2111
5.42k
      false);
2112
5.42k
  cb = std::bind(&MasterPathHandlers::HandlePrettyLB, this, _1, _2);
2113
5.42k
  server->RegisterPathHandler(
2114
5.42k
      "/pretty-lb", "Load balancer Pretty Picture",
2115
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2116
5.42k
      false);
2117
2118
  // JSON Endpoints
2119
5.42k
  cb = std::bind(&MasterPathHandlers::HandleGetTserverStatus, this, _1, _2);
2120
5.42k
  server->RegisterPathHandler(
2121
5.42k
      "/api/v1/tablet-servers", "Tserver Statuses",
2122
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2123
5.42k
  cb = std::bind(&MasterPathHandlers::HandleHealthCheck, this, _1, _2);
2124
5.42k
  server->RegisterPathHandler(
2125
5.42k
      "/api/v1/health-check", "Cluster Health Check",
2126
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2127
5.42k
  cb = std::bind(&MasterPathHandlers::HandleGetReplicationStatus, this, _1, _2);
2128
5.42k
  server->RegisterPathHandler(
2129
5.42k
      "/api/v1/tablet-replication", "Tablet Replication Health",
2130
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2131
5.42k
  cb = std::bind(&MasterPathHandlers::HandleGetUnderReplicationStatus, this, _1, _2);
2132
5.42k
  server->RegisterPathHandler(
2133
5.42k
      "/api/v1/tablet-under-replication", "Tablet UnderReplication Status",
2134
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2135
5.42k
  cb = std::bind(&MasterPathHandlers::HandleDumpEntities, this, _1, _2);
2136
5.42k
  server->RegisterPathHandler(
2137
5.42k
      "/dump-entities", "Dump Entities",
2138
5.42k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2139
5.42k
  server->RegisterPathHandler(
2140
5.42k
      "/api/v1/is-leader", "Leader Check",
2141
5.42k
      std::bind(&MasterPathHandlers::HandleCheckIfLeader, this, _1, _2), false, false);
2142
5.42k
  server->RegisterPathHandler(
2143
5.42k
      "/api/v1/masters", "Master Statuses",
2144
5.42k
      std::bind(&MasterPathHandlers::HandleGetMastersStatus, this, _1, _2), false, false);
2145
5.42k
  server->RegisterPathHandler(
2146
5.42k
      "/api/v1/version", "YB Version Information",
2147
5.42k
      std::bind(&MasterPathHandlers::HandleVersionInfoDump, this, _1, _2), false, false);
2148
5.42k
  return Status::OK();
2149
5.42k
}
2150
2151
string MasterPathHandlers::RaftConfigToHtml(const std::vector<TabletReplica>& locations,
2152
3
                                            const std::string& tablet_id) const {
2153
3
  stringstream html;
2154
2155
3
  html << "<ul>\n";
2156
9
  for (const TabletReplica& location : locations) {
2157
9
    string location_html = TSDescriptorToHtml(*location.ts_desc, tablet_id);
2158
9
    if (location.role == PeerRole::LEADER) {
2159
3
      html << Substitute("  <li><b>LEADER: $0</b></li>\n", location_html);
2160
6
    } else {
2161
6
      html << Substitute("  <li>$0: $1</li>\n",
2162
6
                         PeerRole_Name(location.role), location_html);
2163
6
    }
2164
9
  }
2165
3
  html << "</ul>\n";
2166
3
  return html.str();
2167
3
}
2168
2169
string MasterPathHandlers::TSDescriptorToHtml(const TSDescriptor& desc,
2170
9
                                              const std::string& tablet_id) const {
2171
9
  TSRegistrationPB reg = desc.GetRegistration();
2172
2173
9
  if (reg.common().http_addresses().size() > 0) {
2174
9
    return Substitute(
2175
9
        "<a href=\"http://$0/tablet?id=$1\">$2</a>",
2176
9
        HostPortPBToString(reg.common().http_addresses(0)),
2177
9
        EscapeForHtmlToString(tablet_id),
2178
9
        EscapeForHtmlToString(reg.common().http_addresses(0).host()));
2179
0
  } else {
2180
0
    return EscapeForHtmlToString(desc.permanent_uuid());
2181
0
  }
2182
9
}
2183
2184
string MasterPathHandlers::RegistrationToHtml(
2185
9
    const ServerRegistrationPB& reg, const std::string& link_text) const {
2186
9
  string link_html = EscapeForHtmlToString(link_text);
2187
9
  if (reg.http_addresses().size() > 0) {
2188
9
    link_html = Substitute("<a href=\"http://$0/\">$1</a>",
2189
9
                           HostPortPBToString(reg.http_addresses(0)),
2190
9
                           link_html);
2191
9
  }
2192
9
  return link_html;
2193
9
}
2194
2195
2
void MasterPathHandlers::CalculateTabletMap(TabletCountMap* tablet_map) {
2196
2
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kRunning);
2197
35
  for (const auto& table : tables) {
2198
35
    if (table->IsColocatedUserTable()) {
2199
      // will be taken care of by colocated parent table
2200
0
      continue;
2201
0
    }
2202
2203
35
    TabletInfos tablets = table->GetTablets(IncludeInactive::kTrue);
2204
35
    bool is_user_table = master_->catalog_manager()->IsUserCreatedTable(*table);
2205
2206
37
    for (const auto& tablet : tablets) {
2207
37
      auto replication_locations = tablet->GetReplicaLocations();
2208
2209
9
      for (const auto& replica : *replication_locations) {
2210
9
        if (is_user_table || table->IsColocatedParentTable()
2211
9
                          || table->IsTablegroupParentTable()) {
2212
9
          if (replica.second.role == PeerRole::LEADER) {
2213
3
            (*tablet_map)[replica.first].user_tablet_leaders++;
2214
6
          } else {
2215
6
            (*tablet_map)[replica.first].user_tablet_followers++;
2216
6
          }
2217
0
        } else {
2218
0
          if (replica.second.role == PeerRole::LEADER) {
2219
0
            (*tablet_map)[replica.first].system_tablet_leaders++;
2220
0
          } else {
2221
0
            (*tablet_map)[replica.first].system_tablet_followers++;
2222
0
          }
2223
0
        }
2224
9
      }
2225
37
    }
2226
35
  }
2227
2
}
2228
2229
0
Status MasterPathHandlers::CalculateTServerTree(TServerTree* tserver_tree) {
2230
0
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kRunning);
2231
2232
0
  int count = 0;
2233
0
  for (const auto& table : tables) {
2234
0
    if (!master_->catalog_manager()->IsUserCreatedTable(*table) ||
2235
0
      table->IsColocatedUserTable()) {
2236
0
      continue;
2237
0
    }
2238
2239
0
    count++;
2240
0
    if (count > 4) {
2241
0
      return STATUS(NotSupported, "Not supported for more than 4 tables.");
2242
0
    }
2243
0
  }
2244
2245
0
  for (const auto& table : tables) {
2246
0
    if (!master_->catalog_manager()->IsUserCreatedTable(*table) ||
2247
0
        table->IsColocatedUserTable()) {
2248
      // only display user created tables that are not colocated.
2249
0
      continue;
2250
0
    }
2251
2252
0
    TabletInfos tablets = table->GetTablets(IncludeInactive::kTrue);
2253
2254
0
    for (const auto& tablet : tablets) {
2255
0
      auto replica_locations = tablet->GetReplicaLocations();
2256
0
      for (const auto& replica : *replica_locations) {
2257
0
        (*tserver_tree)[replica.first][tablet->table()->id()].emplace_back(
2258
0
          replica.second.role,
2259
0
          tablet->tablet_id()
2260
0
        );
2261
0
      }
2262
0
    }
2263
0
  }
2264
2265
0
  return Status::OK();
2266
0
}
2267
2268
2269
2270
2271
} // namespace master
2272
} // namespace yb