Skip to content

Commit

Permalink
Add 'kudu tserver list' tool
Browse files Browse the repository at this point in the history
This adds a new tool action to list tablet servers, and associated
information. There are a few output formatting options: the default is a
human-readable table in the psql style; the others are meant for machine
parsing.

The formatting and leader RPC proxy code is abstracted so that
future list tools (e.g. table, tablet, and master) can reuse them.

Change-Id: I047a7675c186a02dd5d8b405431ae885159fcfee
Reviewed-on: http://gerrit.cloudera.org:8080/6654
Tested-by: Kudu Jenkins
Reviewed-by: Adar Dembo <[email protected]>
  • Loading branch information
danburkert committed Apr 20, 2017
1 parent b2843ee commit 319a1bc
Show file tree
Hide file tree
Showing 7 changed files with 447 additions and 28 deletions.
5 changes: 5 additions & 0 deletions src/kudu/client/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class ClientStressTest_TestUniqueClientIds_Test;
class LinkedListTester;
class PartitionSchema;

namespace tools {
class LeaderMasterProxy;
} // namespace tools

namespace client {

class KuduLoggingCallback;
Expand Down Expand Up @@ -520,6 +524,7 @@ class KUDU_EXPORT KuduClient : public sp::enable_shared_from_this<KuduClient> {
friend class KuduTable;
friend class KuduTableAlterer;
friend class KuduTableCreator;
friend class tools::LeaderMasterProxy;

FRIEND_TEST(kudu::ClientStressTest, TestUniqueClientIds);
FRIEND_TEST(ClientTest, TestGetSecurityInfoFromMaster);
Expand Down
59 changes: 58 additions & 1 deletion src/kudu/tools/kudu-tool-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,8 @@ TEST_F(ToolTest, TestModeHelp) {
const vector<string> kTServerModeRegexes = {
"set_flag.*Change a gflag value",
"status.*Get the status",
"timestamp.*Get the current timestamp"
"timestamp.*Get the current timestamp",
"list.*List tablet servers"
};
NO_FATALS(RunTestHelp("tserver", kTServerModeRegexes));
}
Expand Down Expand Up @@ -1357,5 +1358,61 @@ TEST_F(ToolTest, TestLocalReplicaTombstoneDelete) {
}
}

TEST_F(ToolTest, TestTserverList) {
NO_FATALS(StartExternalMiniCluster({}, {}, 1));

string master_addr = cluster_->master()->bound_rpc_addr().ToString();
const auto& tserver = cluster_->tablet_server(0);

{ // TSV
string out;
NO_FATALS(RunActionStdoutString(Substitute("tserver list $0 --columns=uuid --format=tsv",
master_addr),
&out));

ASSERT_EQ(tserver->uuid(), out);
}

{ // JSON
string out;
NO_FATALS(RunActionStdoutString(
Substitute("tserver list $0 --columns=uuid,rpc-addresses --format=json", master_addr),
&out));

ASSERT_EQ(Substitute("[{\"uuid\":\"$0\",\"rpc-addresses\":\"$1\"}]",
tserver->uuid(), tserver->bound_rpc_hostport().ToString()),
out);
}

{ // Pretty
string out;
NO_FATALS(RunActionStdoutString(
Substitute("tserver list $0 --columns=uuid,rpc-addresses", master_addr),
&out));

ASSERT_STR_CONTAINS(out, tserver->uuid());
ASSERT_STR_CONTAINS(out, tserver->bound_rpc_hostport().ToString());
}

{ // Add a tserver
ASSERT_OK(cluster_->AddTabletServer());

vector<string> lines;
NO_FATALS(RunActionStdoutLines(
Substitute("tserver list $0 --columns=uuid --format=space", master_addr),
&lines));

vector<string> expected = {
tserver->uuid(),
cluster_->tablet_server(1)->uuid(),
};

std::sort(lines.begin(), lines.end());
std::sort(expected.begin(), expected.end());

ASSERT_EQ(expected, lines);
}
}

} // namespace tools
} // namespace kudu
61 changes: 43 additions & 18 deletions src/kudu/tools/tool_action.cc
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,17 @@ ActionBuilder& ActionBuilder::AddRequiredVariadicParameter(
return *this;
}

ActionBuilder& ActionBuilder::AddOptionalParameter(const string& param) {
ActionBuilder& ActionBuilder::AddOptionalParameter(string param,
boost::optional<std::string> default_value,
boost::optional<std::string> description) {
#ifndef NDEBUG
// Make sure this gflag exists.
string option;
DCHECK(google::GetCommandLineOption(param.c_str(), &option));
#endif
args_.optional.push_back(param);
args_.optional.emplace_back(ActionArgsDescriptor::Flag({ std::move(param),
std::move(default_value),
std::move(description) }));
return *this;
}

Expand All @@ -244,10 +248,12 @@ unique_ptr<Action> ActionBuilder::Build() {
Status Action::Run(const vector<Mode*>& chain,
const unordered_map<string, string>& required_args,
const vector<string>& variadic_args) const {
SetOptionalParameterDefaultValues();
return runner_({ chain, this, required_args, variadic_args });
}

string Action::BuildHelp(const vector<Mode*>& chain) const {
SetOptionalParameterDefaultValues();
string usage_msg = Substitute("Usage: $0 $1", BuildUsageString(chain), name());
string desc_msg;
for (const auto& param : args_.required) {
Expand All @@ -263,24 +269,28 @@ string Action::BuildHelp(const vector<Mode*>& chain) const {
}
for (const auto& param : args_.optional) {
google::CommandLineFlagInfo gflag_info =
google::GetCommandLineFlagInfoOrDie(param.c_str());
google::GetCommandLineFlagInfoOrDie(param.name.c_str());

if (param.description) {
gflag_info.description = *param.description;
}

if (gflag_info.type == "bool") {
if (gflag_info.default_value == "false") {
usage_msg += Substitute(" [-$0]", param);
usage_msg += Substitute(" [-$0]", param.name);
} else {
usage_msg += Substitute(" [-no$0]", param);
usage_msg += Substitute(" [-no$0]", param.name);
}
} else {
string noun;
string::size_type last_underscore_idx = param.rfind('_');
string::size_type last_underscore_idx = param.name.rfind('_');
if (last_underscore_idx != string::npos &&
last_underscore_idx != param.size() - 1) {
noun = param.substr(last_underscore_idx + 1);
last_underscore_idx != param.name.size() - 1) {
noun = param.name.substr(last_underscore_idx + 1);
} else {
noun = param;
noun = param.name;
}
usage_msg += Substitute(" [-$0=<$1>]", param, noun);
usage_msg += Substitute(" [-$0=<$1>]", param.name, noun);
}
desc_msg += google::DescribeOneFlag(gflag_info);
desc_msg += "\n";
Expand All @@ -299,6 +309,7 @@ string Action::BuildHelp(const vector<Mode*>& chain) const {
}

string Action::BuildHelpXML(const vector<Mode*>& chain) const {
SetOptionalParameterDefaultValues();
string usage = Substitute("$0 $1", BuildUsageString(chain), name());
string xml;
xml += "<action>";
Expand Down Expand Up @@ -333,24 +344,28 @@ string Action::BuildHelpXML(const vector<Mode*>& chain) const {

for (const auto& o : args().optional) {
google::CommandLineFlagInfo gflag_info =
google::GetCommandLineFlagInfoOrDie(o.c_str());
google::GetCommandLineFlagInfoOrDie(o.name.c_str());

if (o.description) {
gflag_info.description = *o.description;
}

if (gflag_info.type == "bool") {
if (gflag_info.default_value == "false") {
usage += Substitute(" [-$0]", o);
usage += Substitute(" [-$0]", o.name);
} else {
usage += Substitute(" [-no$0]", o);
usage += Substitute(" [-no$0]", o.name);
}
} else {
string noun;
string::size_type last_underscore_idx = o.rfind('_');
string::size_type last_underscore_idx = o.name.rfind('_');
if (last_underscore_idx != string::npos &&
last_underscore_idx != o.size() - 1) {
noun = o.substr(last_underscore_idx + 1);
last_underscore_idx != o.name.size() - 1) {
noun = o.name.substr(last_underscore_idx + 1);
} else {
noun = o;
noun = o.name;
}
usage += Substitute(" [-$0=&lt;$1&gt;]", o, noun);
usage += Substitute(" [-$0=&lt;$1&gt;]", o.name, noun);
}

xml += "<argument>";
Expand All @@ -367,5 +382,15 @@ string Action::BuildHelpXML(const vector<Mode*>& chain) const {
return xml;
}

void Action::SetOptionalParameterDefaultValues() const {
for (const auto& param : args_.optional) {
if (param.default_value) {
google::SetCommandLineOptionWithMode(param.name.c_str(),
param.default_value->c_str(),
google::FlagSettingMode::SET_FLAGS_DEFAULT);
}
}
}

} // namespace tools
} // namespace kudu
29 changes: 23 additions & 6 deletions src/kudu/tools/tool_action.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,24 @@ struct ActionArgsDescriptor {
std::string description;
};

// Holds an optional command line argument flag.
struct Flag {
// The gflag name.
std::string name;
// A default value to override the default gflag value.
boost::optional<std::string> default_value;
// A description to override the gflag description.
boost::optional<std::string> description;
};

// Positional (required) command line arguments.
std::vector<Arg> required;

// Key-value command line arguments. These must actually implemented as
// gflags, which means all that must be specified here are the gflag names.
// The gflag definitions themselves will be accessed to get the argument
// descriptions.
// Key-value command line arguments. These must correspond to defined gflags.
//
// Optional by definition, though some are required internally
// (e.g. fs_wal_dir).
std::vector<std::string> optional;
std::vector<Flag> optional;

// Variable length command line argument. There may be at most one per
// Action, and it's always found at the end of the command line.
Expand Down Expand Up @@ -221,7 +228,13 @@ class ActionBuilder {
// provided by the user at any point in the command line. It must match a
// previously-defined gflag; if a gflag with the same name cannot be found,
// the tool will crash.
ActionBuilder& AddOptionalParameter(const std::string& param);
//
// The default value and description of the flag can be optionally overriden,
// for cases where the values are action-dependent. Otherwise, the default
// value and description from the gflag declaration will be used.
ActionBuilder& AddOptionalParameter(std::string param,
boost::optional<std::string> default_value = boost::none,
boost::optional<std::string> description = boost::none);

// Creates an action using builder state.
std::unique_ptr<Action> Build();
Expand Down Expand Up @@ -269,6 +282,10 @@ class Action {

Action() = default;

// Sets optional flag parameter default value in cases where it has been
// overridden from the default gflag value.
void SetOptionalParameterDefaultValues() const;

std::string name_;

std::string description_;
Expand Down
Loading

0 comments on commit 319a1bc

Please sign in to comment.