diff --git a/rdsn b/rdsn index 46e4c48b18..03976025d3 160000 --- a/rdsn +++ b/rdsn @@ -1 +1 @@ -Subproject commit 46e4c48b180030f16bc7c6324f175b42a0d39114 +Subproject commit 03976025d3641986a676e2498cca6b5f81cd3dd1 diff --git a/scripts/py_utils/lib.py b/scripts/py_utils/lib.py index c3579010d5..f6ee83e425 100644 --- a/scripts/py_utils/lib.py +++ b/scripts/py_utils/lib.py @@ -7,6 +7,7 @@ import click import commands import os +import json _global_verbose = False @@ -32,17 +33,11 @@ def __init__(self, cfg_file_name): exit(1) def print_unhealthy_partitions(self): - list_detail = self._run_shell("ls -d").strip() - - read_unhealthy_app_count = int([ - line for line in list_detail.splitlines() - if line.startswith("read_unhealthy_app_count") - ][0].split(":")[1]) - write_unhealthy_app_count = int([ - line for line in list_detail.splitlines() - if line.startswith("write_unhealthy_app_count") - ][0].split(":")[1]) + list_detail = self._run_shell("ls -d -j").strip() + list_detail_json = json.loads(list_detail) + read_unhealthy_app_count = int(list_detail_json["summary"]["read_unhealthy_app_count"]) + write_unhealthy_app_count = int(list_detail_json["summary"]["write_unhealthy_app_count"]) if write_unhealthy_app_count > 0: echo("cluster is write unhealthy, write_unhealthy_app_count = " + str(write_unhealthy_app_count)) @@ -53,18 +48,18 @@ def print_unhealthy_partitions(self): return def print_imbalance_nodes(self): - nodes_detail = self._run_shell("nodes -d").strip() - - primaries_per_node = [] - for line in nodes_detail.splitlines()[1:]: - columns = line.strip().split() - if len(columns) < 5 or not columns[4].isdigit(): - continue - primary_count = int(columns[3]) - primaries_per_node.append(primary_count) - primaries_per_node.sort() - if float(primaries_per_node[0]) / float(primaries_per_node[-1]) < 0.8: - print nodes_detail + nodes_detail = self._run_shell("nodes -d -j").strip() + + primaries_per_node = {} + min_ = 0 + max_ = 0 + for ip_port, node_info in json.loads(nodes_detail)["details"].items(): + primary_count = int(node_info["primary_count"]) + min_ = min(min_, primary_count) + max_ = max(max_, primary_count) + primaries_per_node[ip_port] = primary_count + if float(min_) / float(max_) < 0.8: + print json.dumps(primaries_per_node, indent=4) def get_meta_port(self): with open(self._cfg_file_name) as cfg: diff --git a/src/shell/commands.h b/src/shell/commands.h index 41750e381b..6515a0352e 100644 --- a/src/shell/commands.h +++ b/src/shell/commands.h @@ -33,6 +33,7 @@ using namespace dsn::replication; using tp_alignment = ::dsn::utils::table_printer::alignment; +using tp_output_format = ::dsn::utils::table_printer::output_format; static const char *INDENT = " "; struct list_nodes_helper diff --git a/src/shell/commands/data_operations.cpp b/src/shell/commands/data_operations.cpp index f0f28c223d..77b5b9052a 100644 --- a/src/shell/commands/data_operations.cpp +++ b/src/shell/commands/data_operations.cpp @@ -2564,6 +2564,6 @@ bool calculate_hash_value(command_executor *e, shell_context *sc, arguments args tp.add_row_name_and_data("secondaries", oss.str()); } } - tp.output(std::cout, ": "); + tp.output(std::cout); return true; } diff --git a/src/shell/commands/node_management.cpp b/src/shell/commands/node_management.cpp index b9071acabd..cfc6267055 100644 --- a/src/shell/commands/node_management.cpp +++ b/src/shell/commands/node_management.cpp @@ -19,6 +19,7 @@ bool ls_nodes(command_executor *e, shell_context *sc, arguments args) static struct option long_options[] = {{"detailed", no_argument, 0, 'd'}, {"resolve_ip", no_argument, 0, 'r'}, {"resource_usage", no_argument, 0, 'u'}, + {"json", no_argument, 0, 'j'}, {"status", required_argument, 0, 's'}, {"output", required_argument, 0, 'o'}, {0, 0, 0, 0}}; @@ -28,11 +29,12 @@ bool ls_nodes(command_executor *e, shell_context *sc, arguments args) bool detailed = false; bool resolve_ip = false; bool resource_usage = false; + bool json = false; optind = 0; while (true) { int option_index = 0; int c; - c = getopt_long(args.argc, args.argv, "drus:o:", long_options, &option_index); + c = getopt_long(args.argc, args.argv, "drujs:o:", long_options, &option_index); if (c == -1) break; switch (c) { @@ -45,6 +47,9 @@ bool ls_nodes(command_executor *e, shell_context *sc, arguments args) case 'u': resource_usage = true; break; + case 'j': + json = true; + break; case 's': status = optarg; break; @@ -56,16 +61,15 @@ bool ls_nodes(command_executor *e, shell_context *sc, arguments args) } } + dsn::utils::multi_table_printer mtp; if (!(status.empty() && output_file.empty())) { - std::cout << "[Parameters]" << std::endl; - dsn::utils::table_printer tp; + dsn::utils::table_printer tp("parameters"); if (!status.empty()) tp.add_row_name_and_data("status", status); if (!output_file.empty()) tp.add_row_name_and_data("out_file", output_file); - tp.output(std::cout, ": "); + mtp.add(std::move(tp)); } - std::cout << std::endl << "[Result]" << std::endl; ::dsn::replication::node_status::type s = ::dsn::replication::node_status::NS_INVALID; if (!status.empty() && status != "all") { @@ -212,7 +216,7 @@ bool ls_nodes(command_executor *e, shell_context *sc, arguments args) } std::ostream out(buf); - dsn::utils::table_printer tp; + dsn::utils::table_printer tp("details"); tp.add_title("address"); tp.add_column("status"); if (detailed) { @@ -245,15 +249,15 @@ bool ls_nodes(command_executor *e, shell_context *sc, arguments args) tp.append_data(kv.second.disk_available_min_ratio); } } - tp.output(out); - out << std::endl; + mtp.add(std::move(tp)); - dsn::utils::table_printer tp_count; + dsn::utils::table_printer tp_count("summary"); tp_count.add_row_name_and_data("total_node_count", nodes.size()); tp_count.add_row_name_and_data("alive_node_count", alive_node_count); tp_count.add_row_name_and_data("unalive_node_count", nodes.size() - alive_node_count); - tp_count.output(out, ": "); - out << std::endl; + mtp.add(std::move(tp_count)); + + mtp.output(out, json ? tp_output_format::kJsonPretty : tp_output_format::kTabular); return true; } diff --git a/src/shell/commands/table_management.cpp b/src/shell/commands/table_management.cpp index d9b40c8404..a1c1603a47 100644 --- a/src/shell/commands/table_management.cpp +++ b/src/shell/commands/table_management.cpp @@ -8,19 +8,21 @@ bool ls_apps(command_executor *e, shell_context *sc, arguments args) { static struct option long_options[] = {{"all", no_argument, 0, 'a'}, {"detailed", no_argument, 0, 'd'}, + {"json", no_argument, 0, 'j'}, {"status", required_argument, 0, 's'}, {"output", required_argument, 0, 'o'}, {0, 0, 0, 0}}; bool show_all = false; bool detailed = false; + bool json = false; std::string status; std::string output_file; optind = 0; while (true) { int option_index = 0; int c; - c = getopt_long(args.argc, args.argv, "ads:o:", long_options, &option_index); + c = getopt_long(args.argc, args.argv, "adjs:o:", long_options, &option_index); if (c == -1) break; switch (c) { @@ -30,6 +32,9 @@ bool ls_apps(command_executor *e, shell_context *sc, arguments args) case 'd': detailed = true; break; + case 'j': + json = true; + break; case 's': status = optarg; break; @@ -50,10 +55,8 @@ bool ls_apps(command_executor *e, shell_context *sc, arguments args) "parse %s as app_status::type failed", status.c_str()); } - ::dsn::error_code err = sc->ddl_client->list_apps(s, show_all, detailed, output_file); - if (err == ::dsn::ERR_OK) - std::cout << "list apps succeed" << std::endl; - else + ::dsn::error_code err = sc->ddl_client->list_apps(s, show_all, detailed, json, output_file); + if (err != ::dsn::ERR_OK) std::cout << "list apps failed, error=" << err.to_string() << std::endl; return true; } @@ -98,7 +101,7 @@ bool query_app(command_executor *e, shell_context *sc, arguments args) tp.add_row_name_and_data("out_file", out_file); } tp.add_row_name_and_data("detailed", detailed); - tp.output(std::cout, ": "); + tp.output(std::cout); std::cout << std::endl << "[Result]" << std::endl; @@ -155,7 +158,7 @@ bool app_disk(command_executor *e, shell_context *sc, arguments args) tp_params.add_row_name_and_data("out_file", out_file); } tp_params.add_row_name_and_data("detailed", detailed); - tp_params.output(std::cout, ": "); + tp_params.output(std::cout); std::cout << std::endl << "[Result]" << std::endl; @@ -369,7 +372,7 @@ bool app_disk(command_executor *e, shell_context *sc, arguments args) tp_general.add_row_name_and_data("disk_used_for_primary_replicas(MB)", disk_used_for_primary_replicas); tp_general.add_row_name_and_data("disk_used_for_all_replicas(MB)", disk_used_for_all_replicas); - tp_general.output(out, ": "); + tp_general.output(out); if (detailed) { out << "details" << std::endl; tp_details.output(out); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index a90bea3c81..31810b8290 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -49,14 +49,14 @@ static command_executor commands[] = { { "ls", "list all apps", - "[-a|-all] [-d|--detailed] [-o|--output file_name] " + "[-a|-all] [-d|--detailed] [-j|--json_pretty] [-o|--output file_name]" "[-s|--status all|available|creating|dropping|dropped]", ls_apps, }, { "nodes", "get the node status for this cluster", - "[-d|--detailed] [-r|--resolve_ip] [-u|--resource_usage] " + "[-d|--detailed] [-j|--json_pretty] [-r|--resolve_ip] [-u|--resource_usage]" "[-o|--output file_name] [-s|--status all|alive|unalive]", ls_nodes, },