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