blob: ca42674cea86b360b16d0265770e8d3835846443 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "kudu/tools/ksck_results.h"
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <map>
#include <memory>
#include <numeric>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "kudu/gutil/map-util.h"
#include "kudu/gutil/strings/join.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/tools/color.h"
#include "kudu/tools/tool.pb.h"
#include "kudu/tools/tool_action_common.h"
#include "kudu/util/jsonwriter.h"
#include "kudu/util/slice.h"
#include "kudu/util/status.h"
DEFINE_uint32(truncate_server_csv_length, 3,
"Maximum length of server CSVs before truncation. Raise this to "
"see more servers with, e.g., unusual flags, at the cost of "
"output with long lines.");
using std::endl;
using std::left;
using std::map;
using std::ostream;
using std::ostringstream;
using std::setw;
using std::shared_ptr;
using std::string;
using std::to_string;
using std::unordered_map;
using std::vector;
using strings::Substitute;
namespace kudu {
namespace tools {
namespace {
// Return a formatted string version of 'config', mapping UUIDs to single-character
// labels using the mapping 'label_by_uuid'.
string format_replicas(const map<string, char>& label_by_uuid, const KsckConsensusState& config) {
constexpr int kPeerWidth = 4;
ostringstream result;
// Sort the output by label for readability.
std::set<std::pair<char, string>> labeled_replicas;
for (const auto& entry : label_by_uuid) {
labeled_replicas.emplace(entry.second, entry.first);
}
for (const auto &entry : labeled_replicas) {
if (!ContainsKey(config.voter_uuids, entry.second) &&
!ContainsKey(config.non_voter_uuids, entry.second)) {
result << setw(kPeerWidth) << left << "";
continue;
}
if (config.leader_uuid && config.leader_uuid == entry.second) {
result << setw(kPeerWidth) << left << Substitute("$0*", entry.first);
} else {
if (ContainsKey(config.non_voter_uuids, entry.second)) {
result << setw(kPeerWidth) << left << Substitute("$0~", entry.first);
} else {
result << setw(kPeerWidth) << left << Substitute("$0", entry.first);
}
}
}
return result.str();
}
void AddToUuidLabelMapping(const std::set<string>& uuids,
map<string, char>* uuid_label_mapping) {
CHECK(uuid_label_mapping);
const char* const labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i = uuid_label_mapping->size() % (sizeof(labels) - 1);
for (const auto& uuid : uuids) {
if (InsertIfNotPresent(uuid_label_mapping, uuid, labels[i])) {
i = (i + 1) % (sizeof(labels) - 1);
}
}
}
void AddRowForCState(const string& label,
const KsckConsensusState& cstate,
const map<string, char> replica_labels,
DataTable* table) {
const string replicas = format_replicas(replica_labels, cstate);
const string opid_index_str = cstate.opid_index ?
std::to_string(*cstate.opid_index) :
"";
const string term = cstate.term ? std::to_string(*cstate.term) : "";
const string committed = cstate.type == KsckConsensusConfigType::PENDING ?
"No" : "Yes";
table->AddRow({ label, replicas, term, opid_index_str, committed });
}
// Sort servers by health, from most healthy to least.
// Useful for making unhealthy servers appear at the bottom of output when
// printing out a collection of server health summaries.
bool ServerByHealthComparator(const KsckServerHealthSummary& left,
const KsckServerHealthSummary& right) {
return std::make_tuple(ServerHealthScore(left.health), left.uuid, left.address) <
std::make_tuple(ServerHealthScore(right.health), right.uuid, right.address);
}
// Produces a possibly-abbreviated CSV of 'servers':
// - If 'servers.size() == server_count', returns a special message indicating
// all servers.
// - If servers.size() > FLAGS_truncate_server_csv_length, returns a csv with
// the first FLAGS_truncate_server_csv_length servers and a final component
// indicating how many are elided.
// Requires that 'servers.size() <= server_count'
string ServerCsv(int server_count, const vector<string>& servers) {
DCHECK_LE(servers.size(), server_count);
if (servers.size() == server_count) {
return Substitute("all $0 server(s) checked", server_count);
}
uint32_t n = FLAGS_truncate_server_csv_length;
if (servers.size() <= n) {
return JoinStrings(servers, ", ");
}
vector<string> first_n;
std::copy_n(servers.begin(), n, std::back_inserter(first_n));
first_n.push_back(Substitute("and $0 other server(s)", servers.size() - n));
return JoinStrings(first_n, ", ");
}
} // anonymous namespace
const char* const KsckCheckResultToString(KsckCheckResult cr) {
switch (cr) {
case KsckCheckResult::HEALTHY:
return "HEALTHY";
case KsckCheckResult::RECOVERING:
return "RECOVERING";
case KsckCheckResult::UNDER_REPLICATED:
return "UNDER_REPLICATED";
case KsckCheckResult::CONSENSUS_MISMATCH:
return "CONSENSUS_MISMATCH";
case KsckCheckResult::UNAVAILABLE:
return "UNAVAILABLE";
default:
LOG(FATAL) << "Unknown CheckResult";
}
}
const char* const ServerTypeToString(KsckServerType type) {
switch (type) {
case KsckServerType::MASTER:
return "Master";
case KsckServerType::TABLET_SERVER:
return "Tablet Server";
default:
LOG(FATAL) << "Unknown ServerType";
}
}
const char* const ServerHealthToString(KsckServerHealth sh) {
switch (sh) {
case KsckServerHealth::HEALTHY:
return "HEALTHY";
case KsckServerHealth::UNAUTHORIZED:
return "UNAUTHORIZED";
case KsckServerHealth::UNAVAILABLE:
return "UNAVAILABLE";
case KsckServerHealth::WRONG_SERVER_UUID:
return "WRONG_SERVER_UUID";
default:
LOG(FATAL) << "Unknown KsckServerHealth";
}
}
int ServerHealthScore(KsckServerHealth sh) {
switch (sh) {
case KsckServerHealth::HEALTHY:
return 0;
case KsckServerHealth::UNAUTHORIZED:
return 1;
case KsckServerHealth::UNAVAILABLE:
return 2;
case KsckServerHealth::WRONG_SERVER_UUID:
return 3;
default:
LOG(FATAL) << "Unknown KsckServerHealth";
}
}
Status KsckResults::PrintTo(PrintMode mode, ostream& out) {
if (mode == PrintMode::JSON_PRETTY || mode == PrintMode::JSON_COMPACT) {
return PrintJsonTo(mode, out);
}
// First, report on the masters and master tablet.
std::sort(master_summaries.begin(), master_summaries.end(), ServerByHealthComparator);
RETURN_NOT_OK(PrintServerHealthSummaries(KsckServerType::MASTER,
master_summaries,
out));
if (mode == PrintMode::PLAIN_FULL || master_consensus_conflict) {
RETURN_NOT_OK(PrintConsensusMatrix(master_uuids,
boost::none,
master_consensus_state_map,
out));
}
out << endl;
RETURN_NOT_OK(PrintFlagTable(KsckServerType::MASTER,
master_summaries.size(),
master_flag_to_servers_map,
master_flag_tags_map,
out));
if (!master_flag_to_servers_map.empty()) {
out << endl;
}
// Then, on the health of the tablet servers.
std::sort(tserver_summaries.begin(), tserver_summaries.end(), ServerByHealthComparator);
RETURN_NOT_OK(PrintServerHealthSummaries(KsckServerType::TABLET_SERVER,
tserver_summaries,
out));
if (!tserver_summaries.empty()) {
out << endl;
}
RETURN_NOT_OK(PrintFlagTable(KsckServerType::TABLET_SERVER,
tserver_summaries.size(),
tserver_flag_to_servers_map,
tserver_flag_tags_map,
out));
if (!tserver_flag_to_servers_map.empty()) {
out << endl;
}
// Finally, in the "server section", print the version summary.
RETURN_NOT_OK(PrintVersionTable(master_summaries, tserver_summaries, out));
out << endl;
// Then, on each tablet.
RETURN_NOT_OK(PrintTabletSummaries(tablet_summaries, mode, out));
// Then, summarize the tablets by table.
// Sort the tables so unhealthy tables are easy to see at the bottom.
std::sort(table_summaries.begin(), table_summaries.end(),
[](const KsckTableSummary& left, const KsckTableSummary& right) {
return std::make_pair(left.TableStatus() != KsckCheckResult::HEALTHY, left.name) <
std::make_pair(right.TableStatus() != KsckCheckResult::HEALTHY, right.name);
});
RETURN_NOT_OK(PrintTableSummaries(table_summaries, out));
if (!table_summaries.empty()) {
out << endl;
}
// Next, report on checksum scans.
RETURN_NOT_OK(PrintChecksumResults(checksum_results, out));
if (!checksum_results.tables.empty()) {
out << endl;
}
// And, add a summary of all the things we checked.
RETURN_NOT_OK(PrintTotalCounts(*this, out));
out << endl;
// Penultimately, print the warnings.
if (!warning_messages.empty()) {
out << "==================" << endl;
out << "Warnings:" << endl;
out << "==================" << endl;
for (const auto& s : warning_messages) {
out << s.message().ToString() << endl;
}
out << endl;
}
// Finally, print a summary of all errors.
if (error_messages.empty()) {
// All good.
out << "OK" << endl;
} else {
// Something went wrong.
out << "==================" << endl;
out << "Errors:" << endl;
out << "==================" << endl;
for (const auto& s : error_messages) {
out << s.ToString() << endl;
}
out << endl;
out << "FAILED" << endl;
}
// Remember, we only return non-OK if printing failed, not if ksck checks failed.
return Status::OK();
}
Status PrintConsensusMatrix(const vector<string>& server_uuids,
const boost::optional<KsckConsensusState> ref_cstate,
const KsckConsensusStateMap& cstates,
ostream& out) {
map<string, char> replica_labels;
for (const auto& uuid : server_uuids) {
AddToUuidLabelMapping({ uuid }, &replica_labels);
}
for (const auto& entry : cstates) {
AddToUuidLabelMapping(entry.second.voter_uuids, &replica_labels);
AddToUuidLabelMapping(entry.second.non_voter_uuids, &replica_labels);
}
out << "All reported replicas are:" << endl;
// Sort the output by label for readability.
std::set<std::pair<char, string>> reported_servers;
for (const auto& entry : replica_labels) {
reported_servers.emplace(entry.second, entry.first);
}
for (const auto& entry : reported_servers) {
out << " " << entry.first << " = " << entry.second << endl;
}
DataTable cmatrix({ "Config source", "Replicas", "Current term",
"Config index", "Committed?"});
if (ref_cstate) {
AddRowForCState("master", *ref_cstate, replica_labels, &cmatrix);
}
for (const auto& uuid : server_uuids) {
const string label(1, FindOrDie(replica_labels, uuid));
if (!ContainsKey(cstates, uuid)) {
cmatrix.AddRow({ label, "[config not available]", "", "", "" });
continue;
}
const auto& cstate = FindOrDie(cstates, uuid);
AddRowForCState(label, cstate, replica_labels, &cmatrix);
}
out << "The consensus matrix is:" << endl;
return cmatrix.PrintTo(out);
}
Status PrintServerHealthSummaries(KsckServerType type,
const vector<KsckServerHealthSummary>& summaries,
ostream& out) {
out << ServerTypeToString(type) << " Summary" << endl;
DataTable table({ "UUID", "Address", "Status" });
for (const auto& s : summaries) {
table.AddRow({ s.uuid, s.address, ServerHealthToString(s.health) });
}
RETURN_NOT_OK(table.PrintTo(out));
// Print out the status message from each server with bad health.
// This isn't done as part of the table because the messages can be quite long.
for (const auto& s : summaries) {
if (s.health == KsckServerHealth::HEALTHY) continue;
out << Substitute("Error from $0: $1 ($2)", s.address, s.status.ToString(),
ServerHealthToString(s.health)) << endl;
}
return Status::OK();
}
Status PrintFlagTable(KsckServerType type,
int num_servers,
const KsckFlagToServersMap& flag_to_servers_map,
const KsckFlagTagsMap& flag_tags_map,
ostream& out) {
if (flag_to_servers_map.empty()) {
return Status::OK();
}
DataTable flags_table({"Flag", "Value", "Tags", ServerTypeToString(type)});
for (const auto& flag : flag_to_servers_map) {
const string& name = flag.first.first;
const string& value = flag.first.second;
flags_table.AddRow({name,
value,
FindOrDie(flag_tags_map, name),
ServerCsv(num_servers, flag.second)});
}
return flags_table.PrintTo(out);
}
Status PrintVersionTable(const vector<KsckServerHealthSummary>& masters,
const vector<KsckServerHealthSummary>& tservers,
ostream& out) {
map<string, vector<string>> version_map;
for (const auto& s : masters) {
if (!s.version) continue;
auto& servers = LookupOrInsert(&version_map, *s.version, {});
servers.push_back(Substitute("master@$0", s.address));
}
for (const auto& s : tservers) {
if (!s.version) continue;
auto& servers = LookupOrInsert(&version_map, *s.version, {});
servers.push_back(Substitute("tserver@$0", s.address));
}
out << "Version Summary" << endl;
DataTable table({"Version", "Servers"});
int num_servers = masters.size() + tservers.size();
for (const auto& entry : version_map) {
table.AddRow({entry.first, ServerCsv(num_servers, entry.second)});
}
return table.PrintTo(out);
}
Status PrintTableSummaries(const vector<KsckTableSummary>& table_summaries,
ostream& out) {
if (table_summaries.empty()) {
out << "The cluster doesn't have any matching tables" << endl;
return Status::OK();
}
out << "Summary by table" << endl;
DataTable table({ "Name", "RF", "Status", "Total Tablets",
"Healthy", "Recovering", "Under-replicated", "Unavailable"});
for (const KsckTableSummary& ts : table_summaries) {
table.AddRow({ ts.name,
to_string(ts.replication_factor), KsckCheckResultToString(ts.TableStatus()),
to_string(ts.TotalTablets()),
to_string(ts.healthy_tablets), to_string(ts.recovering_tablets),
to_string(ts.underreplicated_tablets),
to_string(ts.consensus_mismatch_tablets + ts.unavailable_tablets) });
}
return table.PrintTo(out);
}
Status PrintTabletSummaries(const vector<KsckTabletSummary>& tablet_summaries,
PrintMode mode,
ostream& out) {
if (tablet_summaries.empty()) {
out << "The cluster doesn't have any matching tablets" << endl << endl;
return Status::OK();
}
for (const auto& tablet_summary : tablet_summaries) {
if (mode != PrintMode::PLAIN_FULL &&
tablet_summary.result == KsckCheckResult::HEALTHY) {
continue;
}
out << tablet_summary.status << endl;
for (const KsckReplicaSummary& r : tablet_summary.replicas) {
const char* spec_str = r.is_leader
? " [LEADER]" : (!r.is_voter ? " [NONVOTER]" : "");
const string ts_string = r.ts_address ?
Substitute("$0 ($1)", r.ts_uuid, *r.ts_address) :
r.ts_uuid;
out << " " << ts_string << ": ";
if (!r.ts_healthy) {
out << Color(AnsiCode::YELLOW, "TS unavailable") << spec_str << endl;
continue;
}
if (r.state == tablet::RUNNING) {
out << Color(AnsiCode::GREEN, "RUNNING") << spec_str << endl;
continue;
}
if (r.status_pb == boost::none) {
out << Color(AnsiCode::YELLOW, "missing") << spec_str << endl;
continue;
}
out << Color(AnsiCode::YELLOW, "not running") << spec_str << endl;
out << Substitute(
" State: $0\n"
" Data state: $1\n"
" Last status: $2\n",
Color(AnsiCode::BLUE, tablet::TabletStatePB_Name(r.state)),
Color(AnsiCode::BLUE,
tablet::TabletDataState_Name(r.status_pb->tablet_data_state())),
Color(AnsiCode::BLUE, r.status_pb->last_status()));
}
auto& master_cstate = tablet_summary.master_cstate;
vector<string> ts_uuids;
for (const KsckReplicaSummary& rs : tablet_summary.replicas) {
ts_uuids.push_back(rs.ts_uuid);
}
KsckConsensusStateMap consensus_state_map;
for (const KsckReplicaSummary& rs : tablet_summary.replicas) {
if (rs.consensus_state) {
InsertOrDie(&consensus_state_map, rs.ts_uuid, *rs.consensus_state);
}
}
RETURN_NOT_OK(PrintConsensusMatrix(ts_uuids, master_cstate, consensus_state_map, out));
out << endl;
}
return Status::OK();
}
Status PrintChecksumResults(const KsckChecksumResults& checksum_results,
std::ostream& out) {
if (checksum_results.tables.empty()) {
return Status::OK();
}
if (checksum_results.snapshot_timestamp) {
out << "Using snapshot timestamp: " << *checksum_results.snapshot_timestamp << endl;
}
for (const auto& table_entry : checksum_results.tables) {
const auto& table_name = table_entry.first;
const auto& table_checksums = table_entry.second;
out << "-----------------------" << endl;
out << table_name << endl;
out << "-----------------------" << endl;
for (const auto& tablet_entry : table_checksums) {
const auto& tablet_id = tablet_entry.first;
const auto& tablet_checksum = tablet_entry.second;
for (const auto& replica_entry : tablet_checksum.replica_checksums) {
const auto& ts_uuid = replica_entry.first;
const auto& replica_checksum = replica_entry.second;
const string status_str = replica_checksum.status.ok() ?
Substitute("Checksum: $0", replica_checksum.checksum) :
Substitute("Error: $0", replica_checksum.status.ToString());
out << Substitute("T $0 P $1 ($2): $3",
tablet_id, ts_uuid, replica_checksum.ts_address, status_str)
<< endl;
}
if (tablet_checksum.mismatch) {
out << ">> Mismatch found in table " << table_name
<< " tablet " << tablet_id << endl;
}
}
}
return Status::OK();
}
Status PrintTotalCounts(const KsckResults& results, std::ostream& out) {
// Don't print the results if there's no matching tables.
if (results.table_summaries.empty()) {
return Status::OK();
}
int num_replicas = std::accumulate(results.tablet_summaries.begin(),
results.tablet_summaries.end(),
0,
[](int acc, const KsckTabletSummary& ts) {
return acc + ts.replicas.size();
});
DataTable totals({ "", "Total Count" });
totals.AddRow({ "Masters", to_string(results.master_summaries.size()) });
totals.AddRow({ "Tablet Servers", to_string(results.tserver_summaries.size()) });
totals.AddRow({ "Tables", to_string(results.table_summaries.size()) });
totals.AddRow({ "Tablets", to_string(results.tablet_summaries.size()) });
totals.AddRow({ "Replicas", to_string(num_replicas) });
return totals.PrintTo(out);
}
void KsckServerHealthSummaryToPb(const KsckServerHealthSummary& summary,
KsckServerHealthSummaryPB* pb) {
switch (summary.health) {
case KsckServerHealth::HEALTHY:
pb->set_health(KsckServerHealthSummaryPB_ServerHealth_HEALTHY);
break;
case KsckServerHealth::UNAUTHORIZED:
pb->set_health(KsckServerHealthSummaryPB_ServerHealth_UNAUTHORIZED);
break;
case KsckServerHealth::UNAVAILABLE:
pb->set_health(KsckServerHealthSummaryPB_ServerHealth_UNAVAILABLE);
break;
case KsckServerHealth::WRONG_SERVER_UUID:
pb->set_health(KsckServerHealthSummaryPB_ServerHealth_WRONG_SERVER_UUID);
break;
default:
pb->set_health(KsckServerHealthSummaryPB_ServerHealth_UNKNOWN);
}
pb->set_uuid(summary.uuid);
pb->set_address(summary.address);
if (summary.version) {
pb->set_version(*summary.version);
}
pb->set_status(summary.status.ToString());
}
void KsckConsensusStateToPb(const KsckConsensusState& cstate,
KsckConsensusStatePB* pb) {
switch (cstate.type) {
case KsckConsensusConfigType::MASTER:
pb->set_type(KsckConsensusStatePB_ConfigType_MASTER);
break;
case KsckConsensusConfigType::COMMITTED:
pb->set_type(KsckConsensusStatePB_ConfigType_COMMITTED);
break;
case KsckConsensusConfigType::PENDING:
pb->set_type(KsckConsensusStatePB_ConfigType_PENDING);
break;
default:
pb->set_type(KsckConsensusStatePB_ConfigType_UNKNOWN);
}
if (cstate.term) {
pb->set_term(*cstate.term);
}
if (cstate.opid_index) {
pb->set_opid_index(*cstate.opid_index);
}
if (cstate.leader_uuid) {
pb->set_leader_uuid(*cstate.leader_uuid);
}
for (const auto& voter_uuid : cstate.voter_uuids) {
pb->add_voter_uuids(voter_uuid);
}
for (const auto& non_voter_uuid : cstate.non_voter_uuids) {
pb->add_non_voter_uuids(non_voter_uuid);
}
}
void KsckReplicaSummaryToPb(const KsckReplicaSummary& replica,
KsckReplicaSummaryPB* pb) {
pb->set_ts_uuid(replica.ts_uuid);
if (replica.ts_address) {
pb->set_ts_address(*replica.ts_address);
}
pb->set_ts_healthy(replica.ts_healthy);
pb->set_is_leader(replica.is_leader);
pb->set_is_voter(replica.is_voter);
pb->set_state(replica.state);
if (replica.status_pb) {
pb->mutable_status_pb()->CopyFrom(*replica.status_pb);
}
if (replica.consensus_state) {
KsckConsensusStateToPb(*replica.consensus_state, pb->mutable_consensus_state());
}
}
KsckTabletHealthPB KsckTabletHealthToPB(KsckCheckResult c) {
switch (c) {
case KsckCheckResult::HEALTHY:
return KsckTabletHealthPB::HEALTHY;
case KsckCheckResult::RECOVERING:
return KsckTabletHealthPB::RECOVERING;
case KsckCheckResult::UNDER_REPLICATED:
return KsckTabletHealthPB::UNDER_REPLICATED;
case KsckCheckResult::UNAVAILABLE:
return KsckTabletHealthPB::UNAVAILABLE;
case KsckCheckResult::CONSENSUS_MISMATCH:
return KsckTabletHealthPB::CONSENSUS_MISMATCH;
default:
return KsckTabletHealthPB::UNKNOWN;
}
}
void KsckTabletSummaryToPb(const KsckTabletSummary& tablet,
KsckTabletSummaryPB* pb) {
pb->set_id(tablet.id);
pb->set_table_id(tablet.table_id);
pb->set_table_name(tablet.table_name);
pb->set_health(KsckTabletHealthToPB(tablet.result));
pb->set_status(tablet.status);
KsckConsensusStateToPb(tablet.master_cstate, pb->mutable_master_cstate());
for (const auto& replica : tablet.replicas) {
KsckReplicaSummaryToPb(replica, pb->add_replicas());
}
}
void KsckTableSummaryToPb(const KsckTableSummary& table, KsckTableSummaryPB* pb) {
pb->set_id(table.id);
pb->set_name(table.name);
pb->set_health(KsckTabletHealthToPB(table.TableStatus()));
pb->set_replication_factor(table.replication_factor);
pb->set_total_tablets(table.TotalTablets());
pb->set_healthy_tablets(table.healthy_tablets);
pb->set_recovering_tablets(table.recovering_tablets);
pb->set_underreplicated_tablets(table.underreplicated_tablets);
pb->set_unavailable_tablets(table.unavailable_tablets);
pb->set_consensus_mismatch_tablets(table.consensus_mismatch_tablets);
}
void KsckReplicaChecksumToPb(const string& ts_uuid,
const KsckReplicaChecksum& replica,
KsckReplicaChecksumPB* pb) {
pb->set_ts_uuid(ts_uuid);
pb->set_ts_address(replica.ts_address);
pb->set_checksum(replica.checksum);
pb->set_status(replica.status.ToString());
}
void KsckTabletChecksumToPb(const string& tablet_id,
const KsckTabletChecksum& tablet,
KsckTabletChecksumPB* pb) {
pb->set_tablet_id(tablet_id);
pb->set_mismatch(tablet.mismatch);
for (const auto& entry : tablet.replica_checksums) {
KsckReplicaChecksumToPb(entry.first, entry.second, pb->add_replica_checksums());
}
}
void KsckTableChecksumToPb(const string& name,
const KsckTableChecksum& table,
KsckTableChecksumPB* pb) {
pb->set_name(name);
for (const auto& entry : table) {
KsckTabletChecksumToPb(entry.first, entry.second, pb->add_tablets());
}
}
void KsckChecksumResultsToPb(const KsckChecksumResults& results,
KsckChecksumResultsPB* pb) {
if (results.snapshot_timestamp) {
pb->set_snapshot_timestamp(*results.snapshot_timestamp);
}
for (const auto& entry : results.tables) {
KsckTableChecksumToPb(entry.first, entry.second, pb->add_tables());
}
}
void KsckResults::ToPb(KsckResultsPB* pb) const {
for (const auto& error : error_messages) {
pb->add_errors(error.ToString());
}
for (const auto& master_summary : master_summaries) {
KsckServerHealthSummaryToPb(master_summary, pb->add_master_summaries());
}
for (const auto& tserver_summary : tserver_summaries) {
KsckServerHealthSummaryToPb(tserver_summary, pb->add_tserver_summaries());
}
for (const auto& master_uuid : master_uuids) {
pb->add_master_uuids(master_uuid);
}
pb->set_master_consensus_conflict(master_consensus_conflict);
for (const auto& entry : master_consensus_state_map) {
KsckConsensusStateToPb(entry.second, pb->add_master_consensus_states());
}
for (const auto& tablet : tablet_summaries) {
KsckTabletSummaryToPb(tablet, pb->add_tablet_summaries());
}
for (const auto& table : table_summaries) {
KsckTableSummaryToPb(table, pb->add_table_summaries());
}
if (!checksum_results.tables.empty()) {
KsckChecksumResultsToPb(checksum_results, pb->mutable_checksum_results());
}
}
Status KsckResults::PrintJsonTo(PrintMode mode, ostream& out) const {
CHECK(mode == PrintMode::JSON_PRETTY || mode == PrintMode::JSON_COMPACT);
JsonWriter::Mode jw_mode = JsonWriter::Mode::PRETTY;
if (mode == PrintMode::JSON_COMPACT) {
jw_mode = JsonWriter::Mode::COMPACT;
}
KsckResultsPB pb;
ToPb(&pb);
out << JsonWriter::ToJson(pb, jw_mode) << endl;
return Status::OK();
}
} // namespace tools
} // namespace kudu