/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 »"); |
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 »"); |
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 »"); |
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(®).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 |