YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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
8
const char* DatabaseTypeName(YQLDatabase db) {
96
8
  switch (db) {
97
0
    case YQL_DATABASE_UNKNOWN: break;
98
8
    case YQL_DATABASE_CQL: return kDBTypeNameCql;
99
0
    case YQL_DATABASE_PGSQL: return kDBTypeNamePgsql;
100
0
    case YQL_DATABASE_REDIS: return kDBTypeNameRedis;
101
8
  }
102
0
  CHECK(false) << "Unexpected db type " << db;
103
0
  return kDBTypeNameUnknown;
104
8
}
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
4
void MasterPathHandlers::TabletCounts::operator+=(const TabletCounts& other) {
138
4
  user_tablet_leaders += other.user_tablet_leaders;
139
4
  user_tablet_followers += other.user_tablet_followers;
140
4
  system_tablet_leaders += other.system_tablet_leaders;
141
4
  system_tablet_followers += other.system_tablet_followers;
142
4
}
143
144
MasterPathHandlers::ZoneTabletCounts::ZoneTabletCounts(
145
  const TabletCounts& tablet_counts,
146
  uint32_t active_tablets_count) : tablet_counts(tablet_counts),
147
6
                                   active_tablets_count(active_tablets_count) {
148
6
}
149
150
4
void MasterPathHandlers::ZoneTabletCounts::operator+=(const ZoneTabletCounts& other) {
151
4
  tablet_counts += other.tablet_counts;
152
4
  node_count += other.node_count;
153
4
  active_tablets_count += other.active_tablets_count;
154
4
}
155
156
// Retrieve the specified URL response from the leader master
157
void MasterPathHandlers::RedirectToLeader(const Webserver::WebRequest& req,
158
1
                                          Webserver::WebResponse* resp) {
159
1
  std::stringstream *output = &resp->output;
160
1
  vector<ServerEntryPB> masters;
161
1
  Status s = master_->ListMasters(&masters);
162
1
  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
1
  string redirect;
170
3
  for (const ServerEntryPB& master : masters) {
171
3
    if (master.has_error()) {
172
0
      continue;
173
0
    }
174
175
3
    if (master.role() == PeerRole::LEADER) {
176
      // URI already starts with a /, so none is needed between $1 and $2.
177
1
      if (master.registration().http_addresses().size() > 0) {
178
1
        redirect = Substitute(
179
1
            "http://$0$1$2",
180
1
            HostPortPBToString(master.registration().http_addresses(0)),
181
1
            req.redirect_uri,
182
1
            req.query_string.empty() ? 
"?raw"0
: "?" + req.query_string + "&raw");
183
1
      }
184
1
      break;
185
1
    }
186
3
  }
187
188
1
  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
1
  EasyCurl curl;
196
1
  faststring buf;
197
1
  s = curl.FetchURL(redirect, &buf, kCurlTimeoutSec);
198
1
  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
1
  *output << buf.ToString();
207
1
}
208
209
void MasterPathHandlers::CallIfLeaderOrPrintRedirect(
210
    const Webserver::WebRequest& req, Webserver::WebResponse* resp,
211
18
    const Webserver::PathHandlerCallback& callback) {
212
18
  string redirect;
213
  // Lock the CatalogManager in a self-contained block, to prevent double-locking on callbacks.
214
18
  {
215
18
    SCOPED_LEADER_SHARED_LOCK(l, master_->catalog_manager_impl());
216
217
    // If we are not the master leader, redirect the URL.
218
18
    if (!l.first_failed_status().ok()) {
219
1
      RedirectToLeader(req, resp);
220
1
      return;
221
1
    }
222
223
    // Handle the request as a leader master.
224
17
    callback(req, resp);
225
17
    return;
226
18
  }
227
18
}
228
229
inline void MasterPathHandlers::TServerTable(std::stringstream* output,
230
3
                                             TServersViewType viewType) {
231
3
  *output << "<table class='table table-striped'>\n";
232
3
  *output << "    <tr>\n"
233
3
          << "      <th>Server</th>\n"
234
3
          << "      <th>Time since </br>heartbeat</th>\n"
235
3
          << "      <th>Status & Uptime</th>\n";
236
237
3
  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
3
  } else {
242
3
    DCHECK_EQ(viewType, TServersViewType::kTServersDefaultView);
243
3
    *output << "      <th>User Tablet-Peers / Leaders</th>\n"
244
3
            << "      <th>RAM Used</th>\n"
245
3
            << "      <th>Num SST Files</th>\n"
246
3
            << "      <th>Total SST Files Size</th>\n"
247
3
            << "      <th>Uncompressed SST </br>Files Size</th>\n"
248
3
            << "      <th>Read ops/sec</th>\n"
249
3
            << "      <th>Write ops/sec</th>\n";
250
3
  }
251
252
3
  *output << "      <th>Cloud</th>\n"
253
3
          << "      <th>Region</th>\n"
254
3
          << "      <th>Zone</th>\n";
255
256
3
  if (viewType == TServersViewType::kTServersDefaultView) {
257
3
    *output << "      <th>System Tablet-Peers / Leaders</th>\n"
258
3
            << "      <th>Active Tablet-Peers</th>\n";
259
3
  }
260
261
3
  *output << "    </tr>\n";
262
3
}
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
13
string UptimeString(uint64_t seconds) {
274
13
  auto days = seconds / kSecondsPerDay;
275
13
  auto hours = (seconds / kSecondsPerHour) - (days * kHoursPerDay);
276
13
  auto mins = (seconds / kSecondsPerMinute) - (days * kMinutesPerDay) - (hours * kMinutesPerHour);
277
278
13
  std::ostringstream uptime_string_stream;
279
13
  uptime_string_stream << " ";
280
13
  if (days > 0) {
281
0
    uptime_string_stream << days << "days, ";
282
0
  }
283
13
  uptime_string_stream << hours << ":" << std::setw(2) << std::setfill('0') << mins <<
284
13
      ":" << std::setw(2) << std::setfill('0') << (seconds % 60);
285
286
13
  return uptime_string_stream.str();
287
13
}
288
289
bool ShouldHideTserverNodeFromDisplay(
290
10
    const TSDescriptor* ts, int hide_dead_node_threshold_mins) {
291
10
  return hide_dead_node_threshold_mins > 0
292
10
      && !ts->IsLive()
293
10
      && 
ts->TimeSinceHeartbeat().ToMinutes() > hide_dead_node_threshold_mins0
;
294
10
}
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
13
    const ServerRegistrationPB& reg) const {
310
13
  if (reg.http_addresses().size() > 0) {
311
13
    return HostPortPBToString(reg.http_addresses(0));
312
13
  }
313
0
  return "";
314
13
}
315
316
namespace {
317
318
bool TabletServerComparator(
319
6
    const std::shared_ptr<TSDescriptor>& a, const std::shared_ptr<TSDescriptor>& b) {
320
6
  auto a_cloud_info = a->GetRegistration().common().cloud_info();
321
6
  auto b_cloud_info = b->GetRegistration().common().cloud_info();
322
323
6
  if (a_cloud_info.placement_cloud() == b_cloud_info.placement_cloud()) {
324
6
    if (a_cloud_info.placement_region() == b_cloud_info.placement_region()) {
325
6
      if (a_cloud_info.placement_zone() == b_cloud_info.placement_zone()) {
326
6
        return a->permanent_uuid() < b->permanent_uuid();
327
6
      }
328
0
      return a_cloud_info.placement_zone() < b_cloud_info.placement_zone();
329
6
    }
330
0
    return a_cloud_info.placement_region() < b_cloud_info.placement_region();
331
6
  }
332
0
  return a_cloud_info.placement_cloud() < b_cloud_info.placement_cloud();
333
6
}
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
3
                                        TServersViewType viewType) {
343
  // Copy vector to avoid changes to the reference descs passed
344
3
  std::vector<std::shared_ptr<TSDescriptor>> local_descs(*descs);
345
346
  // Comparator orders by cloud, region, zone and uuid fields.
347
3
  std::sort(local_descs.begin(), local_descs.end(), &TabletServerComparator);
348
349
7
  for (auto desc : local_descs) {
350
7
    if (desc->placement_uuid() == current_uuid) {
351
7
      if (ShouldHideTserverNodeFromDisplay(desc.get(), hide_dead_node_threshold_mins)) {
352
0
        continue;
353
0
      }
354
7
      const string time_since_hb = StringPrintf("%.1fs", desc->TimeSinceHeartbeat().ToSeconds());
355
7
      TSRegistrationPB reg = desc->GetRegistration();
356
7
      string host_port = GetHttpHostPortFromServerRegistration(reg.common());
357
7
      *output << "  <tr>\n";
358
7
      *output << "  <td>" << RegistrationToHtml(reg.common(), host_port) << "</br>";
359
7
      *output << "  " << desc->permanent_uuid() << "</td>";
360
7
      *output << "<td>" << time_since_hb << "</td>";
361
7
      if (desc->IsLive()) {
362
7
        *output << "    <td style=\"color:Green\">" << kTserverAlive << ":" <<
363
7
                UptimeString(desc->uptime_seconds()) << "</td>";
364
7
      } else {
365
0
        *output << "    <td style=\"color:Red\">" << kTserverDead << "</td>";
366
0
      }
367
368
7
      auto tserver = tablet_map->find(desc->permanent_uuid());
369
7
      bool no_tablets = tserver == tablet_map->end();
370
371
7
      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
7
      } else {
388
7
        DCHECK_EQ(viewType, TServersViewType::kTServersDefaultView);
389
7
        *output << "    <td>" << (no_tablets ? 
01
390
7
                : 
tserver->second.user_tablet_leaders + tserver->second.user_tablet_followers6
)
391
7
                << " / " << (no_tablets ? 
01
:
tserver->second.user_tablet_leaders6
) << "</td>";
392
7
        *output << "    <td>" << HumanizeBytes(desc->total_memory_usage()) << "</td>";
393
7
        *output << "    <td>" << desc->num_sst_files() << "</td>";
394
7
        *output << "    <td>" << HumanizeBytes(desc->total_sst_file_size()) << "</td>";
395
7
        *output << "    <td>" << HumanizeBytes(desc->uncompressed_sst_file_size()) << "</td>";
396
7
        *output << "    <td>" << desc->read_ops_per_sec() << "</td>";
397
7
        *output << "    <td>" << desc->write_ops_per_sec() << "</td>";
398
7
      }
399
400
7
      *output << "    <td>" << reg.common().cloud_info().placement_cloud() << "</td>";
401
7
      *output << "    <td>" << reg.common().cloud_info().placement_region() << "</td>";
402
7
      *output << "    <td>" << reg.common().cloud_info().placement_zone() << "</td>";
403
404
7
      if (viewType == TServersViewType::kTServersDefaultView) {
405
7
        *output << "    <td>" << (no_tablets ? 
01
406
7
                : 
tserver->second.system_tablet_leaders + tserver->second.system_tablet_followers6
)
407
7
                << " / " << (no_tablets ? 
01
:
tserver->second.system_tablet_leaders6
) << "</td>";
408
7
        *output << "    <td>" << (no_tablets ? 
01
:
desc->num_live_replicas()6
) << "</td>";
409
7
      }
410
411
7
      *output << "  </tr>\n";
412
7
    }
413
7
  }
414
3
  *output << "</table>\n";
415
3
}
416
417
void MasterPathHandlers::DisplayTabletZonesTable(
418
  const ZoneTabletCounts::CloudTree& cloud_tree,
419
  std::stringstream* output
420
3
) {
421
3
  *output << "<h3>Tablet-Peers by Availability Zone</h3>\n"
422
3
          << "<table class='table table-striped'>\n"
423
3
          << "  <tr>\n"
424
3
          << "    <th>Cloud</th>\n"
425
3
          << "    <th>Region</th>\n"
426
3
          << "    <th>Zone</th>\n"
427
3
          << "    <th>Total Nodes</th>\n"
428
3
          << "    <th>User Tablet-Peers / Leaders</th>\n"
429
3
          << "    <th>System Tablet-Peers / Leaders</th>\n"
430
3
          << "    <th>Active Tablet-Peers</th>\n"
431
3
          << "  </tr>\n";
432
433
3
  for (const auto& cloud_iter : cloud_tree) {
434
3
    const auto& region_tree = cloud_iter.second;
435
3
    bool needs_new_row = false;
436
437
3
    int total_size_rows = 0;
438
3
    for (const auto& region_iter : region_tree) {
439
3
      total_size_rows += region_iter.second.size();
440
3
    }
441
442
3
    *output << "<tr>\n"
443
3
            << "  <td rowspan=\"" << total_size_rows <<"\">" << cloud_iter.first << "</td>\n";
444
445
3
    for (const auto& region_iter : region_tree) {
446
3
      const auto& zone_tree = region_iter.second;
447
448
3
      if (needs_new_row) {
449
0
        *output << "<tr>\n";
450
0
        needs_new_row = false;
451
0
      }
452
453
3
      *output << "  <td rowspan=\"" << zone_tree.size() <<"\">" << region_iter.first
454
3
              << "</td>\n";
455
456
3
      for (const auto& zone_iter : zone_tree) {
457
3
        const auto& counts = zone_iter.second;
458
459
3
        if (needs_new_row) {
460
0
          *output << "<tr>\n";
461
0
        }
462
463
3
        *output << "  <td>" << zone_iter.first << "</td>\n";
464
465
3
        uint32_t user_leaders = counts.tablet_counts.user_tablet_leaders;
466
3
        uint32_t user_total = user_leaders + counts.tablet_counts.user_tablet_followers;
467
3
        uint32_t system_leaders = counts.tablet_counts.system_tablet_leaders;
468
3
        uint32_t system_total = system_leaders + counts.tablet_counts.system_tablet_followers;
469
470
3
        *output << "  <td>" << counts.node_count << "</td>\n"
471
3
                << "  <td>" << user_total << " / " << user_leaders << "</td>\n"
472
3
                << "  <td>" << system_total << " / " << system_leaders << "</td>\n"
473
3
                << "  <td>" << counts.active_tablets_count << "</td>\n"
474
3
                << "</tr>\n";
475
476
3
        needs_new_row = true;
477
3
      }
478
3
    }
479
3
  }
480
481
3
  *output << "</table>\n";
482
3
}
483
484
MasterPathHandlers::ZoneTabletCounts::CloudTree MasterPathHandlers::CalculateTabletCountsTree(
485
  const std::vector<std::shared_ptr<TSDescriptor>>& descriptors,
486
  const TabletCountMap& tablet_count_map
487
3
) {
488
3
  ZoneTabletCounts::CloudTree cloud_tree;
489
490
7
  for (const auto& descriptor : descriptors) {
491
7
    CloudInfoPB cloud_info = descriptor->GetRegistration().common().cloud_info();
492
7
    std::string cloud = cloud_info.placement_cloud();
493
7
    std::string region = cloud_info.placement_region();
494
7
    std::string zone = cloud_info.placement_zone();
495
496
7
    auto tablet_count_search = tablet_count_map.find(descriptor->permanent_uuid());
497
7
    ZoneTabletCounts counts = tablet_count_search == tablet_count_map.end()
498
7
        ? 
ZoneTabletCounts()1
499
7
        : 
ZoneTabletCounts(tablet_count_search->second, descriptor->num_live_replicas())6
;
500
501
7
    auto cloud_iter = cloud_tree.find(cloud);
502
7
    if (cloud_iter == cloud_tree.end()) {
503
3
      ZoneTabletCounts::RegionTree region_tree;
504
3
      ZoneTabletCounts::ZoneTree zone_tree;
505
506
3
      zone_tree.emplace(zone, std::move(counts));
507
3
      region_tree.emplace(region, std::move(zone_tree));
508
3
      cloud_tree.emplace(cloud, std::move(region_tree));
509
4
    } else {
510
4
      ZoneTabletCounts::RegionTree& region_tree = cloud_iter->second;
511
512
4
      auto region_iter = region_tree.find(region);
513
4
      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
4
      } else {
519
4
        ZoneTabletCounts::ZoneTree& zone_tree = region_iter->second;
520
521
4
        auto zone_iter = zone_tree.find(zone);
522
4
        if (zone_iter == zone_tree.end()) {
523
0
          zone_tree.emplace(zone, std::move(counts));
524
4
        } else {
525
4
          zone_iter->second += counts;
526
4
        }
527
4
      }
528
4
    }
529
7
  }
530
531
3
  return cloud_tree;
532
3
}
533
534
void MasterPathHandlers::HandleTabletServers(const Webserver::WebRequest& req,
535
                                             Webserver::WebResponse* resp,
536
3
                                             TServersViewType viewType) {
537
3
  std::stringstream *output = &resp->output;
538
3
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
539
540
3
  auto threshold_arg = req.parsed_args.find("live_threshold_mins");
541
3
  int hide_dead_node_threshold_override = FLAGS_hide_dead_node_threshold_mins;
542
3
  if (threshold_arg != req.parsed_args.end()) {
543
0
    hide_dead_node_threshold_override = atoi(threshold_arg->second.c_str());
544
0
  }
545
546
3
  SysClusterConfigEntryPB config;
547
3
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
548
3
  if (!s.ok()) {
549
0
    *output << "<div class=\"alert alert-warning\">" << s.ToString() << "</div>";
550
0
    return;
551
0
  }
552
553
3
  auto live_id = config.replication_info().live_replicas().placement_uuid();
554
555
3
  vector<std::shared_ptr<TSDescriptor> > descs;
556
3
  const auto& ts_manager = master_->ts_manager();
557
3
  ts_manager->GetAllDescriptors(&descs);
558
559
  // Get user and system tablet leader and follower counts for each TabletServer
560
3
  TabletCountMap tablet_map;
561
3
  CalculateTabletMap(&tablet_map);
562
563
3
  std::unordered_set<string> read_replica_uuids;
564
7
  for (auto desc : descs) {
565
7
    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
7
  }
569
570
3
  *output << std::setprecision(output_precision_);
571
3
  *output << "<h2>Tablet Servers</h2>\n";
572
573
3
  if (!live_id.empty()) {
574
0
    *output << "<h3 style=\"color:" << kYBDarkBlue << "\">Primary Cluster UUID: "
575
0
            << live_id << "</h3>\n";
576
0
  }
577
578
3
  TServerTable(output, viewType);
579
3
  TServerDisplay(live_id, &descs, &tablet_map, output, hide_dead_node_threshold_override,
580
3
                 viewType);
581
582
3
  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
3
  ZoneTabletCounts::CloudTree counts_tree = CalculateTabletCountsTree(descs, tablet_map);
591
3
  DisplayTabletZonesTable(counts_tree, output);
592
3
}
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
36
    for (const auto & desc : descs) {
780
36
      if (desc->placement_uuid() == live_placement_uuid) {
781
36
        if (!desc->IsLive()) {
782
          // 1. Are any of the TS marked dead in the master?
783
6
          dead_nodes.push_back(desc);
784
30
        } else {
785
          // 2. Have any of the servers restarted lately?
786
30
          most_recent_uptime = min(most_recent_uptime, desc->uptime_seconds());
787
30
        }
788
36
      }
789
36
    }
790
791
9
    jw.String("dead_nodes");
792
9
    jw.StartArray();
793
9
    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
162
          
table->GetTableType() != REDIS_TABLE_TYPE153
&&
821
162
          
table->GetTableType() != TRANSACTION_STATUS_TABLE_TYPE153
&&
822
162
          
!(153
table->namespace_id() == kSystemNamespaceId153
&&
823
153
            
table->name() == kMetricsSnapshotsTableName36
)) {
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
54
                                       ts->TimeSinceHeartbeat().ToMilliseconds() >
849
12
                                           death_interval_msecs); }) ==
850
54
                  dead_nodes.end()) {
851
44
            ++recent_replica_count;
852
44
          }
853
54
        }
854
18
        if (recent_replica_count < *replication_factor) {
855
10
          jw.String(tablet->tablet_id());
856
10
        }
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
// TODO(alex): This needs a major rework.
884
void MasterPathHandlers::HandleCatalogManager(const Webserver::WebRequest& req,
885
                                              Webserver::WebResponse* resp,
886
2
                                              bool only_user_tables) {
887
2
  std::stringstream *output = &resp->output;
888
2
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
889
890
2
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kAll);
891
892
2
  bool has_tablegroups = master_->catalog_manager()->HasTablegroups();
893
894
2
  typedef map<string, string> StringMap;
895
896
  // The first stores user tables, the second index tables, and the third system tables.
897
2
  std::unique_ptr<StringMap> ordered_tables[kNumTypes];
898
10
  for (int i = 0; i < kNumTypes; 
++i8
) {
899
8
    ordered_tables[i] = std::make_unique<StringMap>();
900
8
  }
901
902
36
  for (const auto& table : tables) {
903
36
    auto l = table->LockForRead();
904
36
    if (!l->is_running()) {
905
0
      continue;
906
0
    }
907
908
36
    string keyspace = master_->catalog_manager()->GetNamespaceName(table->namespace_id());
909
36
    bool is_platform = keyspace.compare(kSystemPlatformNamespace) == 0;
910
911
36
    TableType table_cat;
912
    // Determine the table category. YugaWare tables should be displayed as system tables.
913
36
    if (is_platform) {
914
0
      table_cat = kSystemTable;
915
36
    } else if (master_->catalog_manager()->IsUserIndex(*table)) {
916
0
      table_cat = kUserIndex;
917
36
    } else if (master_->catalog_manager()->IsUserTable(*table)) {
918
2
      table_cat = kUserTable;
919
34
    } else if (table->IsTablegroupParentTable() ||
920
34
               table->IsColocatedParentTable()) {
921
0
      table_cat = kColocatedParentTable;
922
34
    } else {
923
34
      table_cat = kSystemTable;
924
34
    }
925
    // Skip non-user tables if we should.
926
36
    if (only_user_tables && 
(0
table_cat != kUserIndex0
&&
table_cat != kUserTable0
)) {
927
0
      continue;
928
0
    }
929
930
36
    const auto& schema = l->schema();
931
36
    string table_uuid = table->id();
932
36
    string state = SysTablesEntryPB_State_Name(l->pb.state());
933
36
    Capitalize(&state);
934
935
36
    string ysql_table_oid;
936
36
    if (table_cat == kColocatedParentTable) {
937
      // Colocated parent table.
938
0
      ysql_table_oid = GetParentTableOid(table);
939
36
    } else if (table->GetTableType() == PGSQL_TABLE_TYPE) {
940
0
      const auto result = GetPgsqlTableOid(table_uuid);
941
0
      if (result.ok()) {
942
0
        ysql_table_oid = std::to_string(*result);
943
0
      } else {
944
0
        LOG(ERROR) << "Failed to get OID of '" << table_uuid << "' ysql table";
945
0
      }
946
0
    }
947
948
36
    string display_info = Substitute(
949
36
                          "<tr>" \
950
36
                          "<td>$0</td>",
951
36
                          EscapeForHtmlToString(keyspace));
952
953
36
    if (table->GetTableType() == PGSQL_TABLE_TYPE && 
table_cat != kColocatedParentTable0
) {
954
0
      display_info += Substitute(
955
0
                      "<td><a href=\"/table?id=$3\">$0</a></td>" \
956
0
                      "<td>$1</td>" \
957
0
                      "<td>$2</td>" \
958
0
                      "<td>$3</td>" \
959
0
                      "<td>$4</td>",
960
0
                      EscapeForHtmlToString(l->name()),
961
0
                      state,
962
0
                      EscapeForHtmlToString(l->pb.state_msg()),
963
0
                      EscapeForHtmlToString(table_uuid),
964
0
                      ysql_table_oid);
965
966
0
      if (has_tablegroups) {
967
0
        if (table->IsColocatedUserTable()) {
968
0
          const auto parent_table = table->GetColocatedTablet()->table();
969
0
          const auto ysql_parent_oid = GetParentTableOid(parent_table);
970
0
          display_info += Substitute("<td>$0</td>", ysql_parent_oid);
971
0
        } else {
972
0
          display_info += Substitute("<td></td>");
973
0
        }
974
0
      }
975
976
0
      if (schema.has_colocated_table_id() &&
977
0
          schema.colocated_table_id().has_colocation_id()) {
978
0
        ColocationId colocation_id = schema.colocated_table_id().colocation_id();
979
0
        display_info += Substitute("<td>$0</td>", colocation_id);
980
0
      } else {
981
0
        display_info += "<td></td>";
982
0
      }
983
984
36
    } else if (table_cat == kColocatedParentTable) {
985
      // Colocated parent table.
986
0
      ysql_table_oid = GetParentTableOid(table);
987
988
      // Insert a newline in id and name to wrap long tablegroup text.
989
0
      std::string parent_name = l->name();
990
0
      display_info += Substitute(
991
0
                      "<td><a href=\"/table?id=$0\">$1</a></td>" \
992
0
                      "<td>$2</td>" \
993
0
                      "<td>$3</td>" \
994
0
                      "<td>$4</td>" \
995
0
                      "<td>$5</td>",
996
0
                      EscapeForHtmlToString(table_uuid),
997
0
                      EscapeForHtmlToString(parent_name.insert(32, "\n")),
998
0
                      state,
999
0
                      EscapeForHtmlToString(l->pb.state_msg()),
1000
0
                      EscapeForHtmlToString(table_uuid.insert(32, "\n")),
1001
0
                      ysql_table_oid);
1002
36
    } else {
1003
      // System table - don't include parent table column
1004
36
      display_info += Substitute(
1005
36
                      "<td><a href=\"/table?id=$3\">$0</a></td>" \
1006
36
                      "<td>$1</td>" \
1007
36
                      "<td>$2</td>" \
1008
36
                      "<td>$3</td>" \
1009
36
                      "<td>$4</td>",
1010
36
                      EscapeForHtmlToString(l->name()),
1011
36
                      state,
1012
36
                      EscapeForHtmlToString(l->pb.state_msg()),
1013
36
                      EscapeForHtmlToString(table_uuid),
1014
36
                      ysql_table_oid);
1015
36
    }
1016
36
    display_info += "</tr>\n";
1017
36
    (*ordered_tables[table_cat])[table_uuid] = display_info;
1018
36
  }
1019
1020
10
  for (int tpeIdx = 0; tpeIdx < kNumTypes; 
++tpeIdx8
) {
1021
8
    if (only_user_tables && 
(0
table_type_[tpeIdx] != "Index"0
&&
table_type_[tpeIdx] != "User"0
)) {
1022
0
      continue;
1023
0
    }
1024
8
    if (ordered_tables[tpeIdx]->empty() && 
table_type_[tpeIdx] == "Colocated"4
) {
1025
2
      continue;
1026
2
    }
1027
1028
6
    (*output) << "<div class='panel panel-default'>\n"
1029
6
              << "<div class='panel-heading'><h2 class='panel-title'>" << table_type_[tpeIdx]
1030
6
              << " tables</h2></div>\n";
1031
6
    (*output) << "<div class='panel-body table-responsive'>";
1032
1033
6
    if (ordered_tables[tpeIdx]->empty()) {
1034
2
      (*output) << "There are no " << static_cast<char>(tolower(table_type_[tpeIdx][0]))
1035
2
                << table_type_[tpeIdx].substr(1) << " tables.\n";
1036
4
    } else {
1037
4
      *output << "<table class='table table-striped' style='table-layout: fixed;'>\n";
1038
4
      *output << "  <tr><th width='14%'>Keyspace</th>\n"
1039
4
              << "      <th width='21%'>Table Name</th>\n"
1040
4
              << "      <th width='9%'>State</th>\n"
1041
4
              << "      <th width='14%'>Message</th>\n";
1042
4
      if (tpeIdx == kColocatedParentTable || tpeIdx == kSystemTable) {
1043
2
        *output << "      <th width='28%'>UUID</th>\n"
1044
2
                << "      <th width='14%'>YSQL OID</th>\n";
1045
2
      } else if (has_tablegroups) {
1046
0
        *output << "      <th width='18%'>UUID</th>\n"
1047
0
                << "      <th width='8%'>YSQL OID</th>\n"
1048
0
                << "      <th width='8%'>Parent OID</th>\n"
1049
0
                << "      <th width='10%'>Colocation ID</th>\n";
1050
2
      } else {
1051
        // TODO(alex): We should only show Colocation ID column when there are colocated tables.
1052
2
        *output << "      <th width='22%'>UUID</th>\n"
1053
2
                << "      <th width='10%'>YSQL OID</th>\n"
1054
2
                << "      <th width='10%'>Colocation ID</th>\n";
1055
2
      }
1056
4
      *output << "  </tr>\n";
1057
36
      for (const StringMap::value_type &table : *(ordered_tables[tpeIdx])) {
1058
36
        *output << table.second;
1059
36
      }
1060
4
      (*output) << "</table>\n";
1061
4
    }
1062
6
    (*output) << "</div> <!-- panel-body -->\n";
1063
6
    (*output) << "</div> <!-- panel -->\n";
1064
6
  }
1065
2
}
1066
1067
namespace {
1068
1069
6
bool CompareByHost(const TabletReplica& a, const TabletReplica& b) {
1070
6
    return a.ts_desc->permanent_uuid() < b.ts_desc->permanent_uuid();
1071
6
}
1072
1073
} // anonymous namespace
1074
1075
1076
void MasterPathHandlers::HandleTablePage(const Webserver::WebRequest& req,
1077
1
                                         Webserver::WebResponse* resp) {
1078
1
  std::stringstream *output = &resp->output;
1079
1
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1080
1081
  // True if table_id, false if (keyspace, table).
1082
1
  const auto arg_end = req.parsed_args.end();
1083
1
  auto id_arg = req.parsed_args.find("id");
1084
1
  auto keyspace_arg = arg_end;
1085
1
  auto table_arg = arg_end;
1086
1
  if (id_arg == arg_end) {
1087
0
    keyspace_arg = req.parsed_args.find("keyspace_name");
1088
0
    table_arg = req.parsed_args.find("table_name");
1089
0
    if (keyspace_arg == arg_end || table_arg == arg_end) {
1090
0
      *output << " Missing 'id' argument or 'keyspace_name, table_name' argument pair.";
1091
0
      *output << " Arguments must either contain the table id or the "
1092
0
                 " (keyspace_name, table_name) pair.";
1093
0
      return;
1094
0
    }
1095
0
  }
1096
1097
1
  scoped_refptr<TableInfo> table;
1098
1099
1
  if (id_arg != arg_end) {
1100
1
    table = master_->catalog_manager()->GetTableInfo(id_arg->second);
1101
1
  } else {
1102
0
    const auto keyspace_type_arg = req.parsed_args.find("keyspace_type");
1103
0
    const auto keyspace_type = (keyspace_type_arg == arg_end
1104
0
        ? GetDefaultDatabaseType(keyspace_arg->second)
1105
0
        : DatabaseTypeByName(keyspace_type_arg->second));
1106
0
    if (keyspace_type == YQLDatabase::YQL_DATABASE_UNKNOWN) {
1107
0
      *output << "Wrong keyspace_type found '" << keyspace_type_arg->second << "'."
1108
0
              << "Possible values are: " << kDBTypeNameCql << ", "
1109
0
              << kDBTypeNamePgsql << ", " << kDBTypeNameRedis << ".";
1110
0
      return;
1111
0
    }
1112
0
    table = master_->catalog_manager()->GetTableInfoFromNamespaceNameAndTableName(
1113
0
        keyspace_type, keyspace_arg->second, table_arg->second);
1114
0
  }
1115
1116
1
  if (table == nullptr) {
1117
0
    *output << "Table not found!";
1118
0
    return;
1119
0
  }
1120
1121
1
  Schema schema;
1122
1
  PartitionSchema partition_schema;
1123
1
  NamespaceName keyspace_name;
1124
1
  TableName table_name;
1125
1
  TabletInfos tablets;
1126
1
  {
1127
1
    auto l = table->LockForRead();
1128
1
    keyspace_name = master_->catalog_manager()->GetNamespaceName(table->namespace_id());
1129
1
    table_name = l->name();
1130
1
    *output << "<h1>Table: "
1131
1
            << EscapeForHtmlToString(server::TableLongName(keyspace_name, table_name))
1132
1
            << " ("<< table->id() <<") </h1>\n";
1133
1134
1
    *output << "<table class='table table-striped'>\n";
1135
1
    *output << "  <tr><td>Version:</td><td>" << l->pb.version() << "</td></tr>\n";
1136
1137
1
    *output << "  <tr><td>Type:</td><td>" << TableType_Name(l->pb.table_type())
1138
1
            << "</td></tr>\n";
1139
1140
1
    string state = SysTablesEntryPB_State_Name(l->pb.state());
1141
1
    Capitalize(&state);
1142
1
    *output << "  <tr><td>State:</td><td>"
1143
1
            << state
1144
1
            << EscapeForHtmlToString(l->pb.state_msg())
1145
1
            << "</td></tr>\n";
1146
1147
1
    TablespaceId tablespace_id;
1148
1
    auto result = master_->catalog_manager()->GetTablespaceForTable(table);
1149
1
    if (result.ok()) {
1150
      // If the table is associated with a tablespace, display tablespace, otherwise
1151
      // just display replication info.
1152
1
      if (result.get()) {
1153
0
        tablespace_id = result.get().value();
1154
0
        *output << "  <tr><td>Tablespace OID:</td><td>"
1155
0
                << GetPgsqlTablespaceOid(tablespace_id)
1156
0
                << "  </td></tr>\n";
1157
0
      }
1158
1
      auto replication_info = CHECK_RESULT(master_->catalog_manager()->GetTableReplicationInfo(
1159
1
            l->pb.replication_info(), tablespace_id));
1160
1
      *output << "  <tr><td>Replication Info:</td><td>"
1161
1
              << "    <pre class=\"prettyprint\">" << replication_info.DebugString() << "</pre>"
1162
1
              << "  </td></tr>\n </table>\n";
1163
1
    } else {
1164
      // The table was associated with a tablespace, but that tablespace was not found.
1165
0
      *output << "  <tr><td>Replication Info:</td><td>";
1166
0
      if (FLAGS_ysql_tablespace_info_refresh_secs > 0) {
1167
0
        *output << "  Tablespace information not available now, please try again after "
1168
0
                << FLAGS_ysql_tablespace_info_refresh_secs << " seconds. ";
1169
0
      } else {
1170
0
        *output << "  Tablespace information is not available as the periodic task "
1171
0
                << "  used to refresh it is disabled.";
1172
1173
0
      }
1174
0
      *output << "  </td></tr>\n </table>\n";
1175
0
    }
1176
1177
1
    Status s = SchemaFromPB(l->pb.schema(), &schema);
1178
1
    if (s.ok()) {
1179
1
      s = PartitionSchema::FromPB(l->pb.partition_schema(), schema, &partition_schema);
1180
1
    }
1181
1
    if (!s.ok()) {
1182
0
      *output << "Unable to decode partition schema: " << s.ToString();
1183
0
      return;
1184
0
    }
1185
1
    tablets = table->GetTablets(IncludeInactive::kTrue);
1186
1
  }
1187
1188
0
  server::HtmlOutputSchemaTable(schema, output);
1189
1190
1
  *output << "<table class='table table-striped'>\n";
1191
1
  *output << "  <tr><th>Tablet ID</th><th>Partition</th><th>SplitDepth</th><th>State</th>"
1192
1
      "<th>Message</th><th>RaftConfig</th></tr>\n";
1193
3
  for (const scoped_refptr<TabletInfo>& tablet : tablets) {
1194
3
    auto locations = tablet->GetReplicaLocations();
1195
3
    vector<TabletReplica> sorted_locations;
1196
3
    AppendValuesFromMap(*locations, &sorted_locations);
1197
3
    std::sort(sorted_locations.begin(), sorted_locations.end(), &CompareByHost);
1198
1199
3
    auto l = tablet->LockForRead();
1200
1201
3
    Partition partition;
1202
3
    Partition::FromPB(l->pb.partition(), &partition);
1203
1204
3
    string state = SysTabletsEntryPB_State_Name(l->pb.state());
1205
3
    Capitalize(&state);
1206
3
    *output << Substitute(
1207
3
        "<tr><th>$0</th><td>$1</td><td>$2</td><td>$3</td><td>$4</td><td>$5</td></tr>\n",
1208
3
        tablet->tablet_id(),
1209
3
        EscapeForHtmlToString(partition_schema.PartitionDebugString(partition, schema)),
1210
3
        l->pb.split_depth(),
1211
3
        state,
1212
3
        EscapeForHtmlToString(l->pb.state_msg()),
1213
3
        RaftConfigToHtml(sorted_locations, tablet->tablet_id()));
1214
3
  }
1215
1
  *output << "</table>\n";
1216
1217
1
  HtmlOutputTasks(table->GetTasks(), output);
1218
1
}
1219
1220
void MasterPathHandlers::HandleTasksPage(const Webserver::WebRequest& req,
1221
0
                                         Webserver::WebResponse* resp) {
1222
0
  std::stringstream *output = &resp->output;
1223
0
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kAll);
1224
0
  *output << "<h3>Active Tasks</h3>\n";
1225
0
  *output << "<table class='table table-striped'>\n";
1226
0
  *output << "  <tr><th>Task Name</th><th>State</th><th>Start "
1227
0
             "Time</th><th>Time</th><th>Description</th></tr>\n";
1228
0
  for (const auto& table : tables) {
1229
0
    for (const auto& task : table->GetTasks()) {
1230
0
      HtmlOutputTask(task, output);
1231
0
    }
1232
0
  }
1233
0
  *output << "</table>\n";
1234
1235
0
  std::vector<std::shared_ptr<MonitoredTask>> jobs =
1236
0
      master_->catalog_manager()->GetRecentJobs();
1237
0
  *output << Substitute(
1238
0
      "<h3>Last $0 user-initiated jobs started in the past $1 "
1239
0
      "hours</h3>\n",
1240
0
      FLAGS_tasks_tracker_num_long_term_tasks,
1241
0
      FLAGS_long_term_tasks_tracker_keep_time_multiplier *
1242
0
          MonoDelta::FromMilliseconds(FLAGS_catalog_manager_bg_task_wait_ms)
1243
0
              .ToSeconds() /
1244
0
          3600);
1245
0
  *output << "<table class='table table-striped'>\n";
1246
0
  *output << "  <tr><th>Job Name</th><th>State</th><th>Start "
1247
0
             "Time</th><th>Duration</th><th>Description</th></tr>\n";
1248
0
  for (std::vector<std::shared_ptr<MonitoredTask>>::reverse_iterator iter =
1249
0
           jobs.rbegin();
1250
0
       iter != jobs.rend(); ++iter) {
1251
0
    HtmlOutputTask(*iter, output);
1252
0
  }
1253
0
  *output << "</table>\n";
1254
1255
0
  std::vector<std::shared_ptr<MonitoredTask> > tasks =
1256
0
    master_->catalog_manager()->GetRecentTasks();
1257
0
  *output << Substitute(
1258
0
      "<h3>Last $0 tasks started in the past $1 seconds</h3>\n",
1259
0
      FLAGS_tasks_tracker_num_tasks,
1260
0
      FLAGS_tasks_tracker_keep_time_multiplier *
1261
0
          MonoDelta::FromMilliseconds(FLAGS_catalog_manager_bg_task_wait_ms)
1262
0
              .ToSeconds());
1263
0
  *output << "<table class='table table-striped'>\n";
1264
0
  *output << "  <tr><th>Task Name</th><th>State</th><th>Start "
1265
0
             "Time</th><th>Duration</th><th>Description</th></tr>\n";
1266
0
  for (std::vector<std::shared_ptr<MonitoredTask>>::reverse_iterator iter =
1267
0
           tasks.rbegin();
1268
0
       iter != tasks.rend(); ++iter) {
1269
0
    HtmlOutputTask(*iter, output);
1270
0
  }
1271
0
  *output << "</table>\n";
1272
0
}
1273
1274
0
std::vector<TabletInfoPtr> MasterPathHandlers::GetNonSystemTablets() {
1275
0
  std::vector<TabletInfoPtr> nonsystem_tablets;
1276
1277
0
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1278
1279
0
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kRunning);
1280
1281
0
  for (const auto& table : tables) {
1282
0
    if (master_->catalog_manager()->IsSystemTable(*table.get())) {
1283
0
      continue;
1284
0
    }
1285
0
    TabletInfos ts = table->GetTablets(IncludeInactive::kTrue);
1286
1287
0
    for (TabletInfoPtr t : ts) {
1288
0
      nonsystem_tablets.push_back(t);
1289
0
    }
1290
0
  }
1291
0
  return nonsystem_tablets;
1292
0
}
1293
1294
0
std::vector<TabletInfoPtr> MasterPathHandlers::GetLeaderlessTablets() {
1295
0
  std::vector<TabletInfoPtr> leaderless_tablets;
1296
1297
0
  auto nonsystem_tablets = GetNonSystemTablets();
1298
1299
0
  for (TabletInfoPtr t : nonsystem_tablets) {
1300
0
    auto rm = t.get()->GetReplicaLocations();
1301
1302
0
    auto has_leader = std::any_of(
1303
0
      rm->begin(), rm->end(),
1304
0
      [](const auto &item) { return item.second.role == PeerRole::LEADER; });
1305
1306
0
    if (!has_leader) {
1307
0
      leaderless_tablets.push_back(t);
1308
0
    }
1309
0
  }
1310
0
  return leaderless_tablets;
1311
0
}
1312
1313
0
Result<std::vector<TabletInfoPtr>> MasterPathHandlers::GetUnderReplicatedTablets() {
1314
0
  std::vector<TabletInfoPtr> underreplicated_tablets;
1315
1316
0
  auto nonsystem_tablets = GetNonSystemTablets();
1317
1318
0
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1319
1320
0
  auto cluster_rf = VERIFY_RESULT_PREPEND(master_->catalog_manager()->GetReplicationFactor(),
1321
0
                                          "Unable to find replication factor");
1322
1323
0
  for (TabletInfoPtr t : nonsystem_tablets) {
1324
0
    auto rm = t.get()->GetReplicaLocations();
1325
1326
    // Find out the tablets which have been replicated less than the replication factor
1327
0
    if(rm->size() < cluster_rf) {
1328
0
      underreplicated_tablets.push_back(t);
1329
0
    }
1330
0
  }
1331
0
  return underreplicated_tablets;
1332
0
}
1333
1334
void MasterPathHandlers::HandleTabletReplicasPage(const Webserver::WebRequest& req,
1335
0
                                                  Webserver::WebResponse* resp) {
1336
0
  std::stringstream *output = &resp->output;
1337
1338
0
  auto leaderless_ts = GetLeaderlessTablets();
1339
0
  auto underreplicated_ts = GetUnderReplicatedTablets();
1340
1341
0
  *output << "<h3>Leaderless Tablets</h3>\n";
1342
0
  *output << "<table class='table table-striped'>\n";
1343
0
  *output << "  <tr><th>Table Name</th><th>Table UUID</th><th>Tablet ID</th></tr>\n";
1344
1345
0
  for (TabletInfoPtr t : leaderless_ts) {
1346
0
    *output << Substitute(
1347
0
        "<tr><td><a href=\"/table?id=$0\">$1</a></td><td>$2</td><th>$3</th></tr>\n",
1348
0
        EscapeForHtmlToString(t->table()->id()),
1349
0
        EscapeForHtmlToString(t->table()->name()),
1350
0
        EscapeForHtmlToString(t->table()->id()),
1351
0
        EscapeForHtmlToString(t.get()->tablet_id()));
1352
0
  }
1353
1354
0
  *output << "</table>\n";
1355
1356
0
  if(!underreplicated_ts.ok()) {
1357
0
    LOG(WARNING) << underreplicated_ts.ToString();
1358
0
    *output << "<h2>Call to get the cluster replication factor failed</h2>\n";
1359
0
    return;
1360
0
  }
1361
1362
0
  *output << "<h3>Underreplicated Tablets</h3>\n";
1363
0
  *output << "<table class='table table-striped'>\n";
1364
0
  *output << "  <tr><th>Table Name</th><th>Table UUID</th><th>Tablet ID</th>"
1365
0
          << "<th>Tablet Replication Count</th></tr>\n";
1366
1367
0
  for (TabletInfoPtr t : *underreplicated_ts) {
1368
0
    auto rm = t.get()->GetReplicaLocations();
1369
1370
0
    *output << Substitute(
1371
0
        "<tr><td><a href=\"/table?id=$0\">$1</a></td><td>$2</td>"
1372
0
        "<td>$3</td><td>$4</td></tr>\n",
1373
0
        EscapeForHtmlToString(t->table()->id()),
1374
0
        EscapeForHtmlToString(t->table()->name()),
1375
0
        EscapeForHtmlToString(t->table()->id()),
1376
0
        EscapeForHtmlToString(t.get()->tablet_id()),
1377
0
        EscapeForHtmlToString(std::to_string(rm->size())));
1378
0
  }
1379
1380
0
  *output << "</table>\n";
1381
0
}
1382
1383
void MasterPathHandlers::HandleGetReplicationStatus(const Webserver::WebRequest& req,
1384
0
                                                    Webserver::WebResponse* resp) {
1385
0
  std::stringstream *output = &resp->output;
1386
0
  JsonWriter jw(output, JsonWriter::COMPACT);
1387
1388
0
  auto leaderless_ts = GetLeaderlessTablets();
1389
1390
0
  jw.StartObject();
1391
0
  jw.String("leaderless_tablets");
1392
0
  jw.StartArray();
1393
1394
0
  for (TabletInfoPtr t : leaderless_ts) {
1395
0
    jw.StartObject();
1396
0
    jw.String("table_uuid");
1397
0
    jw.String(t->table()->id());
1398
0
    jw.String("tablet_uuid");
1399
0
    jw.String(t.get()->tablet_id());
1400
0
    jw.EndObject();
1401
0
  }
1402
1403
0
  jw.EndArray();
1404
0
  jw.EndObject();
1405
0
}
1406
1407
void MasterPathHandlers::HandleGetUnderReplicationStatus(const Webserver::WebRequest& req,
1408
0
                                                    Webserver::WebResponse* resp) {
1409
0
  std::stringstream *output = &resp->output;
1410
0
  JsonWriter jw(output, JsonWriter::COMPACT);
1411
1412
0
  auto underreplicated_ts = GetUnderReplicatedTablets();
1413
1414
0
  if(!underreplicated_ts.ok()) {
1415
0
    jw.StartObject();
1416
0
    jw.String("Error");
1417
0
    jw.String(underreplicated_ts.status().ToString());
1418
0
    jw.EndObject();
1419
0
    return;
1420
0
  }
1421
1422
0
  jw.StartObject();
1423
0
  jw.String("underreplicated_tablets");
1424
0
  jw.StartArray();
1425
1426
0
  for(TabletInfoPtr t : *underreplicated_ts) {
1427
0
    jw.StartObject();
1428
0
    jw.String("table_uuid");
1429
0
    jw.String(t->table()->id());
1430
0
    jw.String("tablet_uuid");
1431
0
    jw.String(t.get()->tablet_id());
1432
0
    jw.EndObject();
1433
0
  }
1434
1435
0
  jw.EndArray();
1436
0
  jw.EndObject();
1437
0
}
1438
1439
void MasterPathHandlers::RootHandler(const Webserver::WebRequest& req,
1440
1
                                     Webserver::WebResponse* resp) {
1441
1
  std::stringstream *output = &resp->output;
1442
  // First check if we are the master leader. If not, make a curl call to the master leader and
1443
  // return that as the UI payload.
1444
1
  SCOPED_LEADER_SHARED_LOCK(l, master_->catalog_manager_impl());
1445
1
  if (!l.first_failed_status().ok()) {
1446
    // We are not the leader master, retrieve the response from the leader master.
1447
0
    RedirectToLeader(req, resp);
1448
0
    return;
1449
0
  }
1450
1451
1
  SysClusterConfigEntryPB config;
1452
1
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
1453
1
  if (!s.ok()) {
1454
0
    *output << "<div class=\"alert alert-warning\">" << s.ToString() << "</div>";
1455
0
    return;
1456
0
  }
1457
1458
  // Get all the tables.
1459
1
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kRunning);
1460
1461
  // Get the list of user tables.
1462
1
  vector<scoped_refptr<TableInfo> > user_tables;
1463
18
  for (scoped_refptr<TableInfo> table : tables) {
1464
18
    if (master_->catalog_manager()->IsUserTable(*table)) {
1465
1
      user_tables.push_back(table);
1466
1
    }
1467
18
  }
1468
  // Get the version info.
1469
1
  VersionInfoPB version_info;
1470
1
  VersionInfo::GetVersionInfoPB(&version_info);
1471
1472
  // Display the overview information.
1473
1
  (*output) << "<h1>YugabyteDB</h1>\n";
1474
1475
1
  (*output) << "<div class='row dashboard-content'>\n";
1476
1477
1
  (*output) << "<div class='col-xs-12 col-md-8 col-lg-6'>\n";
1478
1
  (*output) << "<div class='panel panel-default'>\n"
1479
1
            << "<div class='panel-heading'><h2 class='panel-title'> Overview</h2></div>\n";
1480
1
  (*output) << "<div class='panel-body table-responsive'>";
1481
1
  (*output) << "<table class='table'>\n";
1482
1483
  // Universe UUID.
1484
1
  (*output) << "  <tr>";
1485
1
  (*output) << Substitute(" <td>$0<span class='yb-overview'>$1</span></td>",
1486
1
                          "<i class='fa fa-database yb-dashboard-icon' aria-hidden='true'></i>",
1487
1
                          "Universe UUID ");
1488
1
  (*output) << Substitute(" <td>$0</td>",
1489
1
                          config.cluster_uuid());
1490
1
  (*output) << "  </tr>\n";
1491
1492
  // Replication factor.
1493
1
  (*output) << "  <tr>";
1494
1
  (*output) << Substitute(" <td>$0<span class='yb-overview'>$1</span></td>",
1495
1
                          "<i class='fa fa-files-o yb-dashboard-icon' aria-hidden='true'></i>",
1496
1
                          "Replication Factor ");
1497
1
  auto num_replicas = master_->catalog_manager()->GetReplicationFactor();
1498
1
  if (!num_replicas.ok()) {
1499
0
    num_replicas = num_replicas.status().CloneAndPrepend("Unable to determine Replication factor.");
1500
0
    LOG(WARNING) << num_replicas.status();
1501
0
  }
1502
1
  (*output) << Format(" <td>$0 <a href='$1' class='btn btn-default pull-right'>$2</a></td>",
1503
1
                      num_replicas, "/cluster-config", "See full config &raquo;");
1504
1
  (*output) << "  </tr>\n";
1505
1506
  // Tserver count.
1507
1
  (*output) << "  <tr>";
1508
1
  (*output) << Substitute(" <td>$0<span class='yb-overview'>$1</span></td>",
1509
1
                          "<i class='fa fa-server yb-dashboard-icon' aria-hidden='true'></i>",
1510
1
                          "Num Nodes (TServers) ");
1511
1
  (*output) << Substitute(" <td>$0 <a href='$1' class='btn btn-default pull-right'>$2</a></td>",
1512
1
                          GetTserverCountForDisplay(master_->ts_manager()),
1513
1
                          "/tablet-servers",
1514
1
                          "See all nodes &raquo;");
1515
1
  (*output) << "  </tr>\n";
1516
1517
  // Num user tables.
1518
1
  (*output) << "  <tr>";
1519
1
  (*output) << Substitute(" <tr><td>$0<span class='yb-overview'>$1</span></td>",
1520
1
                          "<i class='fa fa-table yb-dashboard-icon' aria-hidden='true'></i>",
1521
1
                          "Num User Tables ");
1522
1
  (*output) << Substitute(" <td>$0 <a href='$1' class='btn btn-default pull-right'>$2</a></td>",
1523
1
                          user_tables.size(),
1524
1
                          "/tables",
1525
1
                          "See all tables &raquo;");
1526
1
  (*output) << "  </tr>\n";
1527
1528
  // Load balancer status.
1529
1
  bool load_balancer_enabled = master_->catalog_manager()->IsLoadBalancerEnabled();
1530
1
  (*output) << Substitute(" <tr><td>$0<span class='yb-overview'>$1</span></td>"
1531
1
                          "<td><i class='fa $2' aria-hidden='true'> </i></td></tr>\n",
1532
1
                          "<i class='fa fa-tasks yb-dashboard-icon' aria-hidden='true'></i>",
1533
1
                          "Load Balancer Enabled",
1534
1
                          load_balancer_enabled ? "fa-check"
1535
1
                                                : 
"fa-times label label-danger"0
);
1536
1
  if (load_balancer_enabled) {
1537
1
    IsLoadBalancedRequestPB req;
1538
1
    IsLoadBalancedResponsePB resp;
1539
1
    Status load_balanced = master_->catalog_manager()->IsLoadBalanced(&req, &resp);
1540
1541
1
    (*output) << Substitute(" <tr><td>$0<span class='yb-overview'>$1</span></td>"
1542
1
                            "<td><i class='fa $2' aria-hidden='true'> </i></td></tr>\n",
1543
1
                            "<i class='fa fa-tasks yb-dashboard-icon' aria-hidden='true'></i>",
1544
1
                            "Is Load Balanced?",
1545
1
                            load_balanced.ok() ? 
"fa-check"0
1546
1
                                               : "fa-times label label-danger");
1547
1
  }
1548
1549
  // Build version and type.
1550
1
  (*output) << Substitute("  <tr><td>$0<span class='yb-overview'>$1</span></td><td>$2</td></tr>\n",
1551
1
                          "<i class='fa fa-code-fork yb-dashboard-icon' aria-hidden='true'></i>",
1552
1
                          "YugabyteDB Version ", version_info.version_number());
1553
1
  (*output) << Substitute("  <tr><td>$0<span class='yb-overview'>$1</span></td><td>$2</td></tr>\n",
1554
1
                          "<i class='fa fa-terminal yb-dashboard-icon' aria-hidden='true'></i>",
1555
1
                          "Build Type ", version_info.build_type());
1556
1
  (*output) << "</table>";
1557
1
  (*output) << "</div> <!-- panel-body -->\n";
1558
1
  (*output) << "</div> <!-- panel -->\n";
1559
1
  (*output) << "</div> <!-- col-xs-12 col-md-8 col-lg-6 -->\n";
1560
1561
  // Display the master info.
1562
1
  (*output) << "<div class='col-xs-12 col-md-8 col-lg-6'>\n";
1563
1
  HandleMasters(req, resp);
1564
1
  (*output) << "</div> <!-- col-xs-12 col-md-8 col-lg-6 -->\n";
1565
1
}
1566
1567
void MasterPathHandlers::HandleMasters(const Webserver::WebRequest& req,
1568
4
                                       Webserver::WebResponse* resp) {
1569
4
  std::stringstream *output = &resp->output;
1570
4
  vector<ServerEntryPB> masters;
1571
4
  Status s = master_->ListMasters(&masters);
1572
4
  if (!s.ok()) {
1573
0
    s = s.CloneAndPrepend("Unable to list Masters");
1574
0
    LOG(WARNING) << s.ToString();
1575
0
    *output << "<h1>" << s.ToString() << "</h1>\n";
1576
0
    return;
1577
0
  }
1578
4
  (*output) << "<div class='panel panel-default'>\n"
1579
4
            << "<div class='panel-heading'><h2 class='panel-title'>Masters</h2></div>\n";
1580
4
  (*output) << "<div class='panel-body table-responsive'>";
1581
4
  (*output) << "<table class='table'>\n";
1582
4
  (*output) << "  <tr>\n"
1583
4
            << "    <th>Server</th>\n"
1584
4
            << "    <th>RAFT Role</th>\n"
1585
4
            << "    <th>Uptime</th>\n"
1586
4
            << "    <th>Details</th>\n"
1587
4
            << "  </tr>\n";
1588
1589
6
  for (const ServerEntryPB& master : masters) {
1590
6
    if (master.has_error()) {
1591
0
      string error = StatusFromPB(master.error()).ToString();
1592
0
      *output << "  <tr>\n";
1593
0
      const string kErrStart = "peer ([";
1594
0
      const string kErrEnd = "])";
1595
0
      size_t start_pos = error.find(kErrStart);
1596
0
      size_t end_pos = error.find(kErrEnd);
1597
0
      if (start_pos != string::npos && end_pos != string::npos && (start_pos < end_pos)) {
1598
0
        start_pos = start_pos + kErrStart.length();
1599
0
        string host_port = error.substr(start_pos, end_pos - start_pos);
1600
0
        *output << "<td><font color='red'>" << EscapeForHtmlToString(host_port)
1601
0
                << "</font></td>\n";
1602
0
        *output << "<td><font color='red'>" << PeerRole_Name(PeerRole::UNKNOWN_ROLE)
1603
0
                << "</font></td>\n";
1604
0
      }
1605
0
      *output << Substitute("    <td colspan=2><font color='red'><b>ERROR: $0</b></font></td>\n",
1606
0
                              EscapeForHtmlToString(error));
1607
0
      *output << "  </tr>\n";
1608
0
      continue;
1609
0
    }
1610
6
    auto reg = master.registration();
1611
6
    string host_port = GetHttpHostPortFromServerRegistration(reg);
1612
6
    string reg_text = RegistrationToHtml(reg, host_port);
1613
6
    if (master.instance_id().permanent_uuid() == master_->instance_pb().permanent_uuid()) {
1614
4
      reg_text = Substitute("<b>$0</b>", reg_text);
1615
4
    }
1616
6
    string raft_role = master.has_role() ? PeerRole_Name(master.role()) : 
"N/A"0
;
1617
6
    auto delta = Env::Default()->NowMicros() - master.instance_id().start_time_us();
1618
6
    string uptime = UptimeString(MonoDelta::FromMicroseconds(delta).ToSeconds());
1619
6
    string cloud = reg.cloud_info().placement_cloud();
1620
6
    string region = reg.cloud_info().placement_region();
1621
6
    string zone = reg.cloud_info().placement_zone();
1622
1623
6
    *output << "  <tr>\n"
1624
6
            << "    <td>" << reg_text << "</td>\n"
1625
6
            << "    <td>" << raft_role << "</td>\n"
1626
6
            << "    <td>" << uptime << "</td>\n"
1627
6
            << "    <td><div><span class='yb-overview'>CLOUD: </span>" << cloud << "</div>\n"
1628
6
            << "        <div><span class='yb-overview'>REGION: </span>" << region << "</div>\n"
1629
6
            << "        <div><span class='yb-overview'>ZONE: </span>" << zone << "</div>\n"
1630
6
            << "        <div><span class='yb-overview'>UUID: </span>"
1631
6
            << master.instance_id().permanent_uuid()
1632
6
            << "</div></td>\n"
1633
6
            << "  </tr>\n";
1634
6
  }
1635
1636
4
  (*output) << "</table>";
1637
4
  (*output) << "</div> <!-- panel-body -->\n";
1638
4
  (*output) << "</div> <!-- panel -->\n";
1639
4
}
1640
1641
namespace {
1642
1643
// Visitor for the catalog table which dumps tables and tablets in a JSON format. This
1644
// dump is interpreted by the CM agent in order to track time series entities in the SMON
1645
// database.
1646
//
1647
// This implementation relies on scanning the catalog table directly instead of using the
1648
// catalog manager APIs. This allows it to work even on a non-leader master, and avoids
1649
// any requirement for locking. For the purposes of metrics entity gathering, it's OK to
1650
// serve a slightly stale snapshot.
1651
//
1652
// It is tempting to directly dump the metadata protobufs using JsonWriter::Protobuf(...),
1653
// but then we would be tying ourselves to textual compatibility of the PB field names in
1654
// our catalog table. Instead, the implementation specifically dumps the fields that we
1655
// care about.
1656
//
1657
// This should be considered a "stable" protocol -- do not rename, remove, or restructure
1658
// without consulting with the CM team.
1659
class JsonDumperBase {
1660
 public:
1661
6
  explicit JsonDumperBase(JsonWriter* jw) : jw_(jw) {}
1662
1663
6
  virtual ~JsonDumperBase() {}
1664
1665
  virtual std::string name() const = 0;
1666
1667
 protected:
1668
  JsonWriter* jw_;
1669
};
1670
1671
class JsonKeyspaceDumper : public Visitor<PersistentNamespaceInfo>, public JsonDumperBase {
1672
 public:
1673
2
  explicit JsonKeyspaceDumper(JsonWriter* jw) : JsonDumperBase(jw) {}
1674
1675
2
  std::string name() const override { return "keyspaces"; }
1676
1677
  virtual Status Visit(const std::string& keyspace_id,
1678
8
                       const SysNamespaceEntryPB& metadata) override {
1679
8
    jw_->StartObject();
1680
8
    jw_->String("keyspace_id");
1681
8
    jw_->String(keyspace_id);
1682
1683
8
    jw_->String("keyspace_name");
1684
8
    jw_->String(metadata.name());
1685
1686
8
    jw_->String("keyspace_type");
1687
8
    jw_->String(DatabaseTypeName((metadata.database_type())));
1688
1689
8
    jw_->EndObject();
1690
8
    return Status::OK();
1691
8
  }
1692
};
1693
1694
class JsonTableDumper : public Visitor<PersistentTableInfo>, public JsonDumperBase {
1695
 public:
1696
2
  explicit JsonTableDumper(JsonWriter* jw) : JsonDumperBase(jw) {}
1697
1698
2
  std::string name() const override { return "tables"; }
1699
1700
36
  Status Visit(const std::string& table_id, const SysTablesEntryPB& metadata) override {
1701
36
    if (metadata.state() != SysTablesEntryPB::RUNNING) {
1702
0
      return Status::OK();
1703
0
    }
1704
1705
36
    jw_->StartObject();
1706
36
    jw_->String("table_id");
1707
36
    jw_->String(table_id);
1708
1709
36
    jw_->String("keyspace_id");
1710
36
    jw_->String(metadata.namespace_id());
1711
1712
36
    jw_->String("table_name");
1713
36
    jw_->String(metadata.name());
1714
1715
36
    jw_->String("state");
1716
36
    jw_->String(SysTablesEntryPB::State_Name(metadata.state()));
1717
1718
36
    jw_->EndObject();
1719
36
    return Status::OK();
1720
36
  }
1721
};
1722
1723
class JsonTabletDumper : public Visitor<PersistentTabletInfo>, public JsonDumperBase {
1724
 public:
1725
2
  explicit JsonTabletDumper(JsonWriter* jw) : JsonDumperBase(jw) {}
1726
1727
2
  std::string name() const override { return "tablets"; }
1728
1729
40
  Status Visit(const std::string& tablet_id, const SysTabletsEntryPB& metadata) override {
1730
40
    const std::string& table_id = metadata.table_id();
1731
40
    if (metadata.state() != SysTabletsEntryPB::RUNNING) {
1732
0
      return Status::OK();
1733
0
    }
1734
1735
40
    jw_->StartObject();
1736
40
    jw_->String("table_id");
1737
40
    jw_->String(table_id);
1738
1739
40
    jw_->String("tablet_id");
1740
40
    jw_->String(tablet_id);
1741
1742
40
    jw_->String("state");
1743
40
    jw_->String(SysTabletsEntryPB::State_Name(metadata.state()));
1744
1745
    // Dump replica UUIDs
1746
40
    if (metadata.has_committed_consensus_state()) {
1747
6
      const consensus::ConsensusStatePB& cs = metadata.committed_consensus_state();
1748
6
      jw_->String("replicas");
1749
6
      jw_->StartArray();
1750
18
      for (const RaftPeerPB& peer : cs.config().peers()) {
1751
18
        jw_->StartObject();
1752
18
        jw_->String("type");
1753
18
        jw_->String(PeerMemberType_Name(peer.member_type()));
1754
1755
18
        jw_->String("server_uuid");
1756
18
        jw_->String(peer.permanent_uuid());
1757
1758
18
        jw_->String("addr");
1759
18
        const auto& host_port = peer.last_known_private_addr()[0];
1760
18
        jw_->String(HostPortPBToString(host_port));
1761
1762
18
        jw_->EndObject();
1763
18
      }
1764
6
      jw_->EndArray();
1765
1766
6
      if (cs.has_leader_uuid()) {
1767
6
        jw_->String("leader");
1768
6
        jw_->String(cs.leader_uuid());
1769
6
      }
1770
6
    }
1771
1772
40
    jw_->EndObject();
1773
40
    return Status::OK();
1774
40
  }
1775
};
1776
1777
template <class Dumper>
1778
6
Status JsonDumpCollection(JsonWriter* jw, Master* master, stringstream* output) {
1779
6
  unique_ptr<Dumper> json_dumper(new Dumper(jw));
1780
6
  jw->String(json_dumper->name());
1781
6
  jw->StartArray();
1782
6
  const Status s = master->catalog_manager()->sys_catalog()->Visit(json_dumper.get());
1783
6
  if (s.ok()) {
1784
    // End the array only if there is no error.
1785
6
    jw->EndArray();
1786
6
  } else {
1787
    // Print just an error message.
1788
0
    output->str("");
1789
0
    JsonWriter jw_err(output, JsonWriter::COMPACT);
1790
0
    jw_err.StartObject();
1791
0
    jw_err.String("error");
1792
0
    jw_err.String(s.ToString());
1793
0
    jw_err.EndObject();
1794
0
  }
1795
6
  return s;
1796
6
}
master-path-handlers.cc:yb::Status yb::master::(anonymous namespace)::JsonDumpCollection<yb::master::(anonymous namespace)::JsonKeyspaceDumper>(yb::JsonWriter*, yb::master::Master*, std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)
Line
Count
Source
1778
2
Status JsonDumpCollection(JsonWriter* jw, Master* master, stringstream* output) {
1779
2
  unique_ptr<Dumper> json_dumper(new Dumper(jw));
1780
2
  jw->String(json_dumper->name());
1781
2
  jw->StartArray();
1782
2
  const Status s = master->catalog_manager()->sys_catalog()->Visit(json_dumper.get());
1783
2
  if (s.ok()) {
1784
    // End the array only if there is no error.
1785
2
    jw->EndArray();
1786
2
  } else {
1787
    // Print just an error message.
1788
0
    output->str("");
1789
0
    JsonWriter jw_err(output, JsonWriter::COMPACT);
1790
0
    jw_err.StartObject();
1791
0
    jw_err.String("error");
1792
0
    jw_err.String(s.ToString());
1793
0
    jw_err.EndObject();
1794
0
  }
1795
2
  return s;
1796
2
}
master-path-handlers.cc:yb::Status yb::master::(anonymous namespace)::JsonDumpCollection<yb::master::(anonymous namespace)::JsonTableDumper>(yb::JsonWriter*, yb::master::Master*, std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)
Line
Count
Source
1778
2
Status JsonDumpCollection(JsonWriter* jw, Master* master, stringstream* output) {
1779
2
  unique_ptr<Dumper> json_dumper(new Dumper(jw));
1780
2
  jw->String(json_dumper->name());
1781
2
  jw->StartArray();
1782
2
  const Status s = master->catalog_manager()->sys_catalog()->Visit(json_dumper.get());
1783
2
  if (s.ok()) {
1784
    // End the array only if there is no error.
1785
2
    jw->EndArray();
1786
2
  } else {
1787
    // Print just an error message.
1788
0
    output->str("");
1789
0
    JsonWriter jw_err(output, JsonWriter::COMPACT);
1790
0
    jw_err.StartObject();
1791
0
    jw_err.String("error");
1792
0
    jw_err.String(s.ToString());
1793
0
    jw_err.EndObject();
1794
0
  }
1795
2
  return s;
1796
2
}
master-path-handlers.cc:yb::Status yb::master::(anonymous namespace)::JsonDumpCollection<yb::master::(anonymous namespace)::JsonTabletDumper>(yb::JsonWriter*, yb::master::Master*, std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)
Line
Count
Source
1778
2
Status JsonDumpCollection(JsonWriter* jw, Master* master, stringstream* output) {
1779
2
  unique_ptr<Dumper> json_dumper(new Dumper(jw));
1780
2
  jw->String(json_dumper->name());
1781
2
  jw->StartArray();
1782
2
  const Status s = master->catalog_manager()->sys_catalog()->Visit(json_dumper.get());
1783
2
  if (s.ok()) {
1784
    // End the array only if there is no error.
1785
2
    jw->EndArray();
1786
2
  } else {
1787
    // Print just an error message.
1788
0
    output->str("");
1789
0
    JsonWriter jw_err(output, JsonWriter::COMPACT);
1790
0
    jw_err.StartObject();
1791
0
    jw_err.String("error");
1792
0
    jw_err.String(s.ToString());
1793
0
    jw_err.EndObject();
1794
0
  }
1795
2
  return s;
1796
2
}
1797
1798
} // anonymous namespace
1799
1800
void MasterPathHandlers::HandleDumpEntities(const Webserver::WebRequest& req,
1801
2
                                            Webserver::WebResponse* resp) {
1802
2
  std::stringstream *output = &resp->output;
1803
2
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1804
1805
2
  JsonWriter jw(output, JsonWriter::COMPACT);
1806
2
  jw.StartObject();
1807
1808
2
  if (JsonDumpCollection<JsonKeyspaceDumper>(&jw, master_, output).ok() &&
1809
2
      JsonDumpCollection<JsonTableDumper>(&jw, master_, output).ok() &&
1810
2
      JsonDumpCollection<JsonTabletDumper>(&jw, master_, output).ok()) {
1811
    // End the object only if there is no error.
1812
2
    jw.EndObject();
1813
2
  }
1814
2
}
1815
1816
void MasterPathHandlers::HandleCheckIfLeader(const Webserver::WebRequest& req,
1817
3
                                              Webserver::WebResponse* resp) {
1818
3
  std::stringstream *output = &resp->output;
1819
3
  JsonWriter jw(output, JsonWriter::COMPACT);
1820
3
  jw.StartObject();
1821
3
  {
1822
3
    SCOPED_LEADER_SHARED_LOCK(l, master_->catalog_manager_impl());
1823
1824
    // If we are not the master leader.
1825
3
    if (!l.first_failed_status().ok()) {
1826
2
      resp->code = 503;
1827
2
      return;
1828
2
    }
1829
1830
1
    jw.String("STATUS");
1831
1
    jw.String(l.leader_status().CodeAsString());
1832
1
    jw.EndObject();
1833
1
    return;
1834
3
  }
1835
3
}
1836
1837
void MasterPathHandlers::HandleGetMastersStatus(const Webserver::WebRequest& req,
1838
3
                                                    Webserver::WebResponse* resp) {
1839
3
  std::stringstream *output = &resp->output;
1840
3
  vector<ServerEntryPB> masters;
1841
3
  Status s = master_->ListMasters(&masters);
1842
3
  ListMastersResponsePB pb_resp;
1843
3
  JsonWriter jw(output, JsonWriter::COMPACT);
1844
3
  if (!s.ok()) {
1845
0
    jw.Protobuf(pb_resp);
1846
0
    return;
1847
0
  }
1848
9
  
for (const ServerEntryPB& master : masters)3
{
1849
9
    pb_resp.add_masters()->CopyFrom(master);
1850
9
  }
1851
3
  jw.Protobuf(pb_resp);
1852
3
}
1853
1854
void MasterPathHandlers::HandleGetClusterConfig(
1855
0
  const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
1856
0
  std::stringstream *output = &resp->output;
1857
0
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1858
1859
0
  *output << "<h1>Current Cluster Config</h1>\n";
1860
0
  SysClusterConfigEntryPB config;
1861
0
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
1862
0
  if (!s.ok()) {
1863
0
    *output << "<div class=\"alert alert-warning\">" << s.ToString() << "</div>";
1864
0
    return;
1865
0
  }
1866
1867
0
  *output << "<div class=\"alert alert-success\">Successfully got cluster config!</div>"
1868
0
  << "<pre class=\"prettyprint\">" << config.DebugString() << "</pre>";
1869
0
}
1870
1871
void MasterPathHandlers::HandleGetClusterConfigJSON(
1872
0
  const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
1873
0
  std::stringstream *output = &resp->output;
1874
0
  JsonWriter jw(output, JsonWriter::COMPACT);
1875
1876
0
  master_->catalog_manager()->AssertLeaderLockAcquiredForReading();
1877
1878
0
  SysClusterConfigEntryPB config;
1879
0
  Status s = master_->catalog_manager()->GetClusterConfig(&config);
1880
0
  if (!s.ok()) {
1881
0
    jw.StartObject();
1882
0
    jw.String("error");
1883
0
    jw.String(s.ToString());
1884
0
    jw.EndObject();
1885
0
    return;
1886
0
  }
1887
1888
  // return cluster config in JSON format
1889
0
  jw.Protobuf(config);
1890
0
}
1891
1892
void MasterPathHandlers::HandleVersionInfoDump(
1893
0
    const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
1894
0
  std::stringstream *output = &resp->output;
1895
0
  JsonWriter jw(output, JsonWriter::PRETTY);
1896
1897
  // Get the version info.
1898
0
  VersionInfoPB version_info;
1899
0
  VersionInfo::GetVersionInfoPB(&version_info);
1900
1901
0
  jw.Protobuf(version_info);
1902
0
}
1903
1904
void MasterPathHandlers::HandlePrettyLB(
1905
0
  const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
1906
1907
0
  std::stringstream *output = &resp->output;
1908
1909
  // Don't render if there are more than 5 tservers.
1910
0
  vector<std::shared_ptr<TSDescriptor>> descs;
1911
0
  const auto& ts_manager = master_->ts_manager();
1912
0
  ts_manager->GetAllDescriptors(&descs);
1913
1914
0
  if (descs.size() > 5) {
1915
0
    *output << "<div class='alert alert-warning'>"
1916
0
            << "Current configuration has more than 5 tservers. Not recommended"
1917
0
            << " to view this pretty display as it might not be rendered properly."
1918
0
            << "</div>";
1919
0
    return;
1920
0
  }
1921
1922
  // Don't render if there is a lot of placement nesting.
1923
0
  std::unordered_set<std::string> clouds;
1924
0
  std::unordered_set<std::string> regions;
1925
  // Map of zone -> {tserver UUIDs}
1926
  // e.g. zone1 -> {ts1uuid, ts2uuid, ts3uuid}.
1927
0
  std::unordered_map<std::string, vector<std::string>> zones;
1928
0
  for (const auto& desc : descs) {
1929
0
    std::string uuid = desc->permanent_uuid();
1930
0
    std::string cloud = desc->GetCloudInfo().placement_cloud();
1931
0
    std::string region = desc->GetCloudInfo().placement_region();
1932
0
    std::string zone = desc->GetCloudInfo().placement_zone();
1933
1934
0
    zones[zone].push_back(uuid);
1935
0
    clouds.insert(cloud);
1936
0
    regions.insert(region);
1937
0
  }
1938
1939
  // If the we have more than 1 cloud or more than 1 region skip this page
1940
  // as currently it might not diplay prettily.
1941
0
  if (clouds.size() > 1 || regions.size() > 1 || zones.size() > 3) {
1942
0
    *output << "<div class='alert alert-warning'>"
1943
0
            << "Current placement has more than 1 cloud provider or 1 region or 3 zones. "
1944
0
            << "Not recommended to view this pretty display as it might not be rendered properly."
1945
0
            << "</div>";
1946
0
    return;
1947
0
  }
1948
1949
  // Get the TServerTree.
1950
  // A map of tserver -> all tables with their tablets.
1951
0
  TServerTree tserver_tree;
1952
0
  Status s = CalculateTServerTree(&tserver_tree);
1953
0
  if (!s.ok()) {
1954
0
    *output << "<div class='alert alert-warning'>"
1955
0
            << "Current placement has more than 4 tables. Not recommended"
1956
0
            << " to view this pretty display as it might not be rendered properly."
1957
0
            << "</div>";
1958
0
    return;
1959
0
  }
1960
1961
0
  auto blacklist_result = master_->catalog_manager()->BlacklistSetFromPB();
1962
0
  BlacklistSet blacklist = blacklist_result.ok() ? *blacklist_result : BlacklistSet();
1963
1964
  // A single zone.
1965
0
  int color_index = 0;
1966
0
  std::unordered_map<std::string, std::string> tablet_colors;
1967
1968
0
  *output << "<div class='row'>\n";
1969
0
  for (const auto& zone : zones) {
1970
    // Panel for this Zone.
1971
    // Split the zones in proportion of the number of tservers in each zone.
1972
0
    *output << Substitute("<div class='col-lg-$0'>\n", 12*zone.second.size()/descs.size());
1973
1974
    // Change the display of the panel if all tservers in this zone are down.
1975
0
    bool all_tservers_down = true;
1976
0
    for (const auto& tserver : zone.second) {
1977
0
      std::shared_ptr<TSDescriptor> desc;
1978
0
      if (!master_->ts_manager()->LookupTSByUUID(tserver, &desc)) {
1979
0
        continue;
1980
0
      }
1981
0
      all_tservers_down = all_tservers_down && !desc->IsLive();
1982
0
    }
1983
0
    string zone_panel_display = "panel-success";
1984
0
    if (all_tservers_down) {
1985
0
      zone_panel_display = "panel-danger";
1986
0
    }
1987
1988
0
    *output << Substitute("<div class='panel $0'>\n", zone_panel_display);
1989
0
    *output << Substitute("<div class='panel-heading'>"
1990
0
                          "<h6 class='panel-title'>Zone: $0</h6></div>\n", zone.first);
1991
0
    *output << "<div class='row'>\n";
1992
1993
    // Tservers for this panel.
1994
0
    for (const auto& tserver : zone.second) {
1995
      // Split tservers equally.
1996
0
      *output << Substitute("<div class='col-lg-$0'>\n", 12/(zone.second.size()));
1997
0
      std::shared_ptr<TSDescriptor> desc;
1998
0
      if (!master_->ts_manager()->LookupTSByUUID(tserver, &desc)) {
1999
0
        continue;
2000
0
      }
2001
2002
      // Get the state of tserver.
2003
0
      bool ts_live = desc->IsLive();
2004
      // Get whether tserver is blacklisted.
2005
0
      bool blacklisted = desc->IsBlacklisted(blacklist);
2006
0
      string panel_type = "panel-success";
2007
0
      string icon_type = "fa-check";
2008
0
      if (!ts_live || blacklisted) {
2009
0
        panel_type = "panel-danger";
2010
0
        icon_type = "fa-times";
2011
0
      }
2012
0
      *output << Substitute("<div class='panel $0' style='margin-bottom: 0px'>\n", panel_type);
2013
2014
      // Point to the tablet servers link.
2015
0
      TSRegistrationPB reg = desc->GetRegistration();
2016
0
      string host_port = GetHttpHostPortFromServerRegistration(reg.common());
2017
0
      *output << Substitute("<div class='panel-heading'>"
2018
0
                            "<h6 class='panel-title'><a href='http://$0'>TServer - $0    "
2019
0
                            "<i class='fa $1'></i></a></h6></div>\n",
2020
0
                            HostPortPBToString(reg.common().http_addresses(0)),
2021
0
                            icon_type);
2022
2023
0
      *output << "<table class='table table-borderless table-hover'>\n";
2024
0
      for (const auto& table : tserver_tree[tserver]) {
2025
0
        *output << Substitute("<tr height='200px'>\n");
2026
        // Display the table name.
2027
0
        string tname = master_->catalog_manager()->GetTableInfo(table.first)->name();
2028
        // Link the table name to the corresponding table page on the master.
2029
0
        ServerRegistrationPB reg;
2030
0
        if (!master_->GetMasterRegistration(&reg).ok()) {
2031
0
          continue;
2032
0
        }
2033
0
        *output << Substitute("<td><h4><a href='http://$0/table?id=$1'>"
2034
0
                              "<i class='fa fa-table'></i>    $2</a></h4>\n",
2035
0
                              HostPortPBToString(reg.http_addresses(0)),
2036
0
                              table.first,
2037
0
                              tname);
2038
        // Replicas of this table.
2039
0
        for (const auto& replica : table.second) {
2040
          // All the replicas of the same tablet will have the same color, so
2041
          // look it up in the map if assigned, otherwise assign one from the pool.
2042
0
          if (tablet_colors.find(replica.tablet_id) == tablet_colors.end()) {
2043
0
            tablet_colors[replica.tablet_id] = kYBColorList[color_index];
2044
0
            color_index = (color_index + 1)%kYBColorList.size();
2045
0
          }
2046
2047
          // Leaders and followers have different formatting.
2048
          // Leaders need to stand out.
2049
0
          if (replica.role == PeerRole::LEADER) {
2050
0
            *output << Substitute("<button type='button' class='btn btn-default'"
2051
0
                                "style='background-image:none; border: 6px solid $0; "
2052
0
                                "font-weight: bolder'>"
2053
0
                                "L</button>\n",
2054
0
                                tablet_colors[replica.tablet_id]);
2055
0
          } else {
2056
0
            *output << Substitute("<button type='button' class='btn btn-default'"
2057
0
                                "style='background-image:none; border: 4px dotted $0'>"
2058
0
                                "F</button>\n",
2059
0
                                tablet_colors[replica.tablet_id]);
2060
0
          }
2061
0
        }
2062
0
        *output << "</td>\n";
2063
0
        *output << "</tr>\n";
2064
0
      }
2065
0
      *output << "</table><!-- tserver-level-table -->\n";
2066
0
      *output << "</div><!-- tserver-level-panel -->\n";
2067
0
      *output << "</div><!-- tserver-level-spacing -->\n";
2068
0
    }
2069
0
    *output << "</div><!-- tserver-level-row -->\n";
2070
0
    *output << "</div><!-- zone-level-panel -->\n";
2071
0
    *output << "</div><!-- zone-level-spacing -->\n";
2072
0
  }
2073
0
  *output << "</div><!-- zone-level-row -->\n";
2074
0
}
2075
2076
8.03k
Status MasterPathHandlers::Register(Webserver* server) {
2077
8.03k
  bool is_styled = true;
2078
8.03k
  bool is_on_nav_bar = true;
2079
2080
  // The set of handlers visible on the nav bar.
2081
8.03k
  server->RegisterPathHandler(
2082
8.03k
    "/", "Home", std::bind(&MasterPathHandlers::RootHandler, this, _1, _2), is_styled,
2083
8.03k
    is_on_nav_bar, "fa fa-home");
2084
8.03k
  Webserver::PathHandlerCallback cb =
2085
8.03k
      std::bind(&MasterPathHandlers::HandleTabletServers, this, _1, _2,
2086
8.03k
                TServersViewType::kTServersDefaultView);
2087
8.03k
  server->RegisterPathHandler(
2088
8.03k
      "/tablet-servers", "Tablet Servers",
2089
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2090
8.03k
      is_on_nav_bar, "fa fa-server");
2091
8.03k
  cb = std::bind(&MasterPathHandlers::HandleTabletServers, this, _1, _2,
2092
8.03k
                 TServersViewType::kTServersClocksView);
2093
8.03k
  server->RegisterPathHandler(
2094
8.03k
      "/tablet-server-clocks", "Tablet Server Clocks",
2095
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2096
8.03k
      false /* is_on_nav_bar */);
2097
8.03k
  cb = std::bind(&MasterPathHandlers::HandleCatalogManager,
2098
8.03k
      this, _1, _2, false /* only_user_tables */);
2099
8.03k
  server->RegisterPathHandler(
2100
8.03k
      "/tables", "Tables",
2101
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2102
8.03k
      is_on_nav_bar, "fa fa-table");
2103
2104
  // The set of handlers not currently visible on the nav bar.
2105
8.03k
  cb = std::bind(&MasterPathHandlers::HandleTablePage, this, _1, _2);
2106
8.03k
  server->RegisterPathHandler(
2107
8.03k
      "/table", "", std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb),
2108
8.03k
      is_styled, false);
2109
8.03k
  server->RegisterPathHandler(
2110
8.03k
      "/masters", "Masters", std::bind(&MasterPathHandlers::HandleMasters, this, _1, _2), is_styled,
2111
8.03k
      false);
2112
8.03k
  cb = std::bind(&MasterPathHandlers::HandleGetClusterConfig, this, _1, _2);
2113
8.03k
  server->RegisterPathHandler(
2114
8.03k
      "/cluster-config", "Cluster Config",
2115
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2116
8.03k
      false);
2117
8.03k
  cb = std::bind(&MasterPathHandlers::HandleGetClusterConfigJSON, this, _1, _2);
2118
8.03k
  server->RegisterPathHandler(
2119
8.03k
      "/api/v1/cluster-config", "Cluster Config JSON",
2120
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false,
2121
8.03k
      false);
2122
8.03k
  cb = std::bind(&MasterPathHandlers::HandleTasksPage, this, _1, _2);
2123
8.03k
  server->RegisterPathHandler(
2124
8.03k
      "/tasks", "Tasks",
2125
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2126
8.03k
      false);
2127
8.03k
  cb = std::bind(&MasterPathHandlers::HandleTabletReplicasPage, this, _1, _2);
2128
8.03k
  server->RegisterPathHandler(
2129
8.03k
      "/tablet-replication", "Tablet Replication Health",
2130
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2131
8.03k
      false);
2132
8.03k
  cb = std::bind(&MasterPathHandlers::HandlePrettyLB, this, _1, _2);
2133
8.03k
  server->RegisterPathHandler(
2134
8.03k
      "/pretty-lb", "Load balancer Pretty Picture",
2135
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), is_styled,
2136
8.03k
      false);
2137
2138
  // JSON Endpoints
2139
8.03k
  cb = std::bind(&MasterPathHandlers::HandleGetTserverStatus, this, _1, _2);
2140
8.03k
  server->RegisterPathHandler(
2141
8.03k
      "/api/v1/tablet-servers", "Tserver Statuses",
2142
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2143
8.03k
  cb = std::bind(&MasterPathHandlers::HandleHealthCheck, this, _1, _2);
2144
8.03k
  server->RegisterPathHandler(
2145
8.03k
      "/api/v1/health-check", "Cluster Health Check",
2146
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2147
8.03k
  cb = std::bind(&MasterPathHandlers::HandleGetReplicationStatus, this, _1, _2);
2148
8.03k
  server->RegisterPathHandler(
2149
8.03k
      "/api/v1/tablet-replication", "Tablet Replication Health",
2150
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2151
8.03k
  cb = std::bind(&MasterPathHandlers::HandleGetUnderReplicationStatus, this, _1, _2);
2152
8.03k
  server->RegisterPathHandler(
2153
8.03k
      "/api/v1/tablet-under-replication", "Tablet UnderReplication Status",
2154
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2155
8.03k
  cb = std::bind(&MasterPathHandlers::HandleDumpEntities, this, _1, _2);
2156
8.03k
  server->RegisterPathHandler(
2157
8.03k
      "/dump-entities", "Dump Entities",
2158
8.03k
      std::bind(&MasterPathHandlers::CallIfLeaderOrPrintRedirect, this, _1, _2, cb), false, false);
2159
8.03k
  server->RegisterPathHandler(
2160
8.03k
      "/api/v1/is-leader", "Leader Check",
2161
8.03k
      std::bind(&MasterPathHandlers::HandleCheckIfLeader, this, _1, _2), false, false);
2162
8.03k
  server->RegisterPathHandler(
2163
8.03k
      "/api/v1/masters", "Master Statuses",
2164
8.03k
      std::bind(&MasterPathHandlers::HandleGetMastersStatus, this, _1, _2), false, false);
2165
8.03k
  server->RegisterPathHandler(
2166
8.03k
      "/api/v1/version", "YB Version Information",
2167
8.03k
      std::bind(&MasterPathHandlers::HandleVersionInfoDump, this, _1, _2), false, false);
2168
8.03k
  return Status::OK();
2169
8.03k
}
2170
2171
string MasterPathHandlers::RaftConfigToHtml(const std::vector<TabletReplica>& locations,
2172
3
                                            const std::string& tablet_id) const {
2173
3
  stringstream html;
2174
2175
3
  html << "<ul>\n";
2176
9
  for (const TabletReplica& location : locations) {
2177
9
    string location_html = TSDescriptorToHtml(*location.ts_desc, tablet_id);
2178
9
    if (location.role == PeerRole::LEADER) {
2179
3
      html << Substitute("  <li><b>LEADER: $0</b></li>\n", location_html);
2180
6
    } else {
2181
6
      html << Substitute("  <li>$0: $1</li>\n",
2182
6
                         PeerRole_Name(location.role), location_html);
2183
6
    }
2184
9
  }
2185
3
  html << "</ul>\n";
2186
3
  return html.str();
2187
3
}
2188
2189
string MasterPathHandlers::TSDescriptorToHtml(const TSDescriptor& desc,
2190
9
                                              const std::string& tablet_id) const {
2191
9
  TSRegistrationPB reg = desc.GetRegistration();
2192
2193
9
  if (reg.common().http_addresses().size() > 0) {
2194
9
    return Substitute(
2195
9
        "<a href=\"http://$0/tablet?id=$1\">$2</a>",
2196
9
        HostPortPBToString(reg.common().http_addresses(0)),
2197
9
        EscapeForHtmlToString(tablet_id),
2198
9
        EscapeForHtmlToString(reg.common().http_addresses(0).host()));
2199
9
  } else {
2200
0
    return EscapeForHtmlToString(desc.permanent_uuid());
2201
0
  }
2202
9
}
2203
2204
string MasterPathHandlers::RegistrationToHtml(
2205
13
    const ServerRegistrationPB& reg, const std::string& link_text) const {
2206
13
  string link_html = EscapeForHtmlToString(link_text);
2207
13
  if (reg.http_addresses().size() > 0) {
2208
13
    link_html = Substitute("<a href=\"http://$0/\">$1</a>",
2209
13
                           HostPortPBToString(reg.http_addresses(0)),
2210
13
                           link_html);
2211
13
  }
2212
13
  return link_html;
2213
13
}
2214
2215
3
void MasterPathHandlers::CalculateTabletMap(TabletCountMap* tablet_map) {
2216
3
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kRunning);
2217
53
  for (const auto& table : tables) {
2218
53
    if (table->IsColocatedUserTable()) {
2219
      // will be taken care of by colocated parent table
2220
0
      continue;
2221
0
    }
2222
2223
53
    TabletInfos tablets = table->GetTablets(IncludeInactive::kTrue);
2224
53
    bool is_user_table = master_->catalog_manager()->IsUserCreatedTable(*table);
2225
2226
57
    for (const auto& tablet : tablets) {
2227
57
      auto replication_locations = tablet->GetReplicaLocations();
2228
2229
57
      for (const auto& replica : *replication_locations) {
2230
18
        if (is_user_table || 
table->IsColocatedParentTable()0
2231
18
                          || 
table->IsTablegroupParentTable()0
) {
2232
18
          if (replica.second.role == PeerRole::LEADER) {
2233
6
            (*tablet_map)[replica.first].user_tablet_leaders++;
2234
12
          } else {
2235
12
            (*tablet_map)[replica.first].user_tablet_followers++;
2236
12
          }
2237
18
        } else {
2238
0
          if (replica.second.role == PeerRole::LEADER) {
2239
0
            (*tablet_map)[replica.first].system_tablet_leaders++;
2240
0
          } else {
2241
0
            (*tablet_map)[replica.first].system_tablet_followers++;
2242
0
          }
2243
0
        }
2244
18
      }
2245
57
    }
2246
53
  }
2247
3
}
2248
2249
0
Status MasterPathHandlers::CalculateTServerTree(TServerTree* tserver_tree) {
2250
0
  auto tables = master_->catalog_manager()->GetTables(GetTablesMode::kRunning);
2251
2252
0
  int count = 0;
2253
0
  for (const auto& table : tables) {
2254
0
    if (!master_->catalog_manager()->IsUserCreatedTable(*table) ||
2255
0
      table->IsColocatedUserTable()) {
2256
0
      continue;
2257
0
    }
2258
2259
0
    count++;
2260
0
    if (count > 4) {
2261
0
      return STATUS(NotSupported, "Not supported for more than 4 tables.");
2262
0
    }
2263
0
  }
2264
2265
0
  for (const auto& table : tables) {
2266
0
    if (!master_->catalog_manager()->IsUserCreatedTable(*table) ||
2267
0
        table->IsColocatedUserTable()) {
2268
      // only display user created tables that are not colocated.
2269
0
      continue;
2270
0
    }
2271
2272
0
    TabletInfos tablets = table->GetTablets(IncludeInactive::kTrue);
2273
2274
0
    for (const auto& tablet : tablets) {
2275
0
      auto replica_locations = tablet->GetReplicaLocations();
2276
0
      for (const auto& replica : *replica_locations) {
2277
0
        (*tserver_tree)[replica.first][tablet->table()->id()].emplace_back(
2278
0
          replica.second.role,
2279
0
          tablet->tablet_id()
2280
0
        );
2281
0
      }
2282
0
    }
2283
0
  }
2284
2285
0
  return Status::OK();
2286
0
}
2287
2288
2289
2290
2291
} // namespace master
2292
} // namespace yb