Add vitastor-cli pg-list command
This commit is contained in:
@@ -14,6 +14,7 @@ add_library(vitastor_cli STATIC
|
||||
cli_modify.cpp
|
||||
cli_modify_osd.cpp
|
||||
cli_osd_tree.cpp
|
||||
cli_pg_ls.cpp
|
||||
cli_flatten.cpp
|
||||
cli_merge.cpp
|
||||
cli_rm_data.cpp
|
||||
|
||||
@@ -127,6 +127,15 @@ static const char* help_text =
|
||||
"vitastor-cli modify-osd [--tags tag1,tag2,...] [--reweight <number>] [--noout true/false] <osd_number>\n"
|
||||
" Set OSD reweight, tags or noout flag.\n"
|
||||
"\n"
|
||||
"vitastor-cli pg-list|pg-ls|list-pg|ls-pg|ls-pgs [OPTIONS] [state1+state2] [^state3] [...]\n"
|
||||
" List PGs with any of listed state filters (^ or ! in the beginning is negation). Options:\n"
|
||||
" --pool <pool name or number> Only list PGs of the given pool.\n"
|
||||
" --min <min pg number> Only list PGs with number >= min.\n"
|
||||
" --max <max pg number> Only list PGs with number <= max.\n"
|
||||
" Examples:\n"
|
||||
" vitastor-cli pg-list active+degraded\n"
|
||||
" vitastor-cli pg-list ^active\n"
|
||||
"\n"
|
||||
"vitastor-cli create-pool|pool-create <name> (-s <pg_size>|--ec <N>+<K>) -n <pg_count> [OPTIONS]\n"
|
||||
" Create a pool. Required parameters:\n"
|
||||
" -s|--pg_size R Number of replicas for replicated pools\n"
|
||||
@@ -409,6 +418,23 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
||||
cfg["flat"] = true;
|
||||
action_cb = p->start_osd_tree(cfg);
|
||||
}
|
||||
else if (cmd[0] == "modify-osd")
|
||||
{
|
||||
// Modify OSD configuration
|
||||
if (cmd.size() > 1)
|
||||
cfg["osd_num"] = cmd[1];
|
||||
action_cb = p->start_modify_osd(cfg);
|
||||
}
|
||||
else if (cmd[0] == "pg-list" || cmd[0] == "pg-ls" || cmd[0] == "list-pg" || cmd[0] == "ls-pg" || cmd[0] == "ls-pgs")
|
||||
{
|
||||
// Modify OSD configuration
|
||||
if (cmd.size() > 1)
|
||||
{
|
||||
cmd.erase(cmd.begin(), cmd.begin()+1);
|
||||
cfg["pg_state"] = cmd;
|
||||
}
|
||||
action_cb = p->start_pg_list(cfg);
|
||||
}
|
||||
else if (cmd[0] == "create-pool" || cmd[0] == "pool-create")
|
||||
{
|
||||
// Create a new pool
|
||||
|
||||
+2
-1
@@ -65,8 +65,9 @@ public:
|
||||
std::function<bool(cli_result_t &)> start_ls(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_merge(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_modify(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_modify_osd(json11::Json cfg);
|
||||
std::function<bool(cli_result_t &)> start_modify_osd(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_osd_tree(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_pg_list(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_pool_create(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_pool_modify(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_pool_rm(json11::Json);
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2024
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include "cli.h"
|
||||
#include "cluster_client.h"
|
||||
#include "pg_states.h"
|
||||
#include "str_util.h"
|
||||
|
||||
struct pg_lister_t
|
||||
{
|
||||
cli_tool_t *parent;
|
||||
|
||||
uint64_t pool_id = 0;
|
||||
std::string pool_name;
|
||||
std::vector<std::string> pg_state;
|
||||
uint64_t min_pg_num = 0;
|
||||
uint64_t max_pg_num = 0;
|
||||
|
||||
std::map<pool_pg_num_t, json11::Json> pg_stats;
|
||||
|
||||
int state = 0;
|
||||
cli_result_t result;
|
||||
|
||||
bool is_done() { return state == 100; }
|
||||
|
||||
void load_pg_stats()
|
||||
{
|
||||
if (state == 1)
|
||||
goto resume_1;
|
||||
if (pool_name != "")
|
||||
{
|
||||
pool_id = 0;
|
||||
for (auto & pp: parent->cli->st_cli.pool_config)
|
||||
{
|
||||
if (pp.second.name == pool_name)
|
||||
{
|
||||
pool_id = pp.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pool_id)
|
||||
{
|
||||
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+pool_name+" not found" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
}
|
||||
parent->etcd_txn(json11::Json::object {
|
||||
{ "success", json11::Json::array {
|
||||
json11::Json::object {
|
||||
{ "request_range", json11::Json::object {
|
||||
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/pg/stats"+(pool_id ? "/"+std::to_string(pool_id)+"/" : "/")) },
|
||||
{ "range_end", base64_encode(parent->cli->st_cli.etcd_prefix+"/pg/stats"+(pool_id ? "/"+std::to_string(pool_id)+"0" : "0")) },
|
||||
} },
|
||||
},
|
||||
} },
|
||||
});
|
||||
state = 1;
|
||||
resume_1:
|
||||
if (parent->waiting > 0)
|
||||
return;
|
||||
if (parent->etcd_err.err)
|
||||
{
|
||||
result = parent->etcd_err;
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
parent->iterate_kvs_2(parent->etcd_result["responses"][0]["response_range"]["kvs"], "/pg/stats/", [&](pool_id_t pool_id, uint64_t pg_num, json11::Json value)
|
||||
{
|
||||
pg_stats[(pool_pg_num_t){ .pool_id = pool_id, .pg_num = (pg_num_t)pg_num }] = value;
|
||||
});
|
||||
}
|
||||
|
||||
void format_pgs()
|
||||
{
|
||||
uint64_t is_not = ((uint64_t)1 << 63);
|
||||
std::vector<uint64_t> masks;
|
||||
if (pg_state.size())
|
||||
{
|
||||
for (auto & st: pg_state)
|
||||
{
|
||||
if (st.size())
|
||||
{
|
||||
uint64_t mask = 0;
|
||||
size_t pos = 0;
|
||||
if (st[0] == '!' || st[0] == '^')
|
||||
{
|
||||
mask |= is_not;
|
||||
pos++;
|
||||
}
|
||||
size_t prev = pos;
|
||||
while (true)
|
||||
{
|
||||
if (pos < st.size() && (st[pos] >= 'a' && st[pos] <= 'z' || st[pos] == '_'))
|
||||
pos++;
|
||||
else
|
||||
{
|
||||
if (pos > prev)
|
||||
{
|
||||
std::string bit = st.substr(prev, pos-prev);
|
||||
bool found = false;
|
||||
for (int i = 0; i < pg_state_bit_count; i++)
|
||||
{
|
||||
if (pg_state_names[i] == bit)
|
||||
{
|
||||
mask |= (uint64_t)1 << i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Unknown PG state "+bit };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (pos < st.size() && !(st[pos] >= 'a' && st[pos] <= 'z' || st[pos] == '_'))
|
||||
pos++;
|
||||
prev = pos;
|
||||
if (pos >= st.size())
|
||||
break;
|
||||
}
|
||||
}
|
||||
masks.push_back(mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
json11::Json::array pgs;
|
||||
for (auto & pp: parent->cli->st_cli.pool_config)
|
||||
{
|
||||
if ((!pool_id || pp.first == pool_id) && (pool_name == "" || pp.second.name == pool_name))
|
||||
{
|
||||
for (auto & pgp: pp.second.pg_config)
|
||||
{
|
||||
if (min_pg_num && pgp.first < min_pg_num || max_pg_num && pgp.first > max_pg_num)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (masks.size())
|
||||
{
|
||||
bool found = false;
|
||||
for (auto mask: masks)
|
||||
{
|
||||
if ((mask & is_not)
|
||||
? (pgp.second.cur_state & (mask & ~is_not)) != (mask & ~is_not)
|
||||
: ((pgp.second.cur_state & mask) == mask))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
continue;
|
||||
}
|
||||
json11::Json::array state_names;
|
||||
for (int i = 0; i < pg_state_bit_count; i++)
|
||||
{
|
||||
if (pgp.second.cur_state & (1 << i))
|
||||
{
|
||||
state_names.push_back(std::string(pg_state_names[i]));
|
||||
}
|
||||
}
|
||||
if (!pgp.second.cur_state)
|
||||
{
|
||||
state_names.push_back("offline");
|
||||
}
|
||||
auto stat = pg_stats[(pool_pg_num_t){ .pool_id = pp.first, .pg_num = pgp.first }].object_items();
|
||||
stat.erase("write_osd_set");
|
||||
stat["pool_id"] = (uint64_t)pp.first;
|
||||
stat["pool_name"] = pp.second.name;
|
||||
stat["pg_num"] = (uint64_t)pgp.first;
|
||||
stat["pause"] = pgp.second.pause;
|
||||
stat["state"] = state_names;
|
||||
stat["cur_primary"] = pgp.second.cur_primary;
|
||||
stat["target_primary"] = pgp.second.primary;
|
||||
stat["target_set"] = pgp.second.target_set;
|
||||
stat["target_history"] = pgp.second.target_history;
|
||||
stat["all_peers"] = pgp.second.all_peers;
|
||||
stat["epoch"] = pgp.second.epoch;
|
||||
stat["next_scrub"] = pgp.second.next_scrub;
|
||||
if (!parent->json_output)
|
||||
{
|
||||
stat["fmt_state"] = implode("+", state_names);
|
||||
stat["fmt_primary"] = (!pgp.second.primary && !pgp.second.cur_primary
|
||||
? "-"
|
||||
: (std::to_string(pgp.second.cur_primary) + (pgp.second.primary == pgp.second.cur_primary
|
||||
? ""
|
||||
: "->"+std::to_string(pgp.second.primary))));
|
||||
stat["fmt_target_set"] = implode(",", stat["target_set"]);
|
||||
uint64_t pg_block = pp.second.data_block_size * (pp.second.scheme == POOL_SCHEME_REPLICATED
|
||||
? 1 : (pp.second.pg_size-pp.second.parity_chunks));
|
||||
stat["fmt_clean"] = format_size(stat["clean_count"].uint64_value() * pg_block);
|
||||
stat["fmt_misplaced"] = format_size(stat["misplaced_count"].uint64_value() * pg_block);
|
||||
stat["fmt_degraded"] = format_size(stat["degraded_count"].uint64_value() * pg_block);
|
||||
stat["fmt_incomplete"] = format_size(stat["incomplete_count"].uint64_value() * pg_block);
|
||||
}
|
||||
pgs.push_back(stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parent->json_output)
|
||||
{
|
||||
result.data = pgs;
|
||||
return;
|
||||
}
|
||||
json11::Json::array cols;
|
||||
if (!pool_id)
|
||||
{
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "pool_name" },
|
||||
{ "title", "POOL" },
|
||||
});
|
||||
}
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "pg_num" },
|
||||
{ "title", "NUM" },
|
||||
});
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "fmt_target_set" },
|
||||
{ "title", "OSD SET" },
|
||||
});
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "fmt_primary" },
|
||||
{ "title", "PRIMARY" },
|
||||
});
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "fmt_clean" },
|
||||
{ "title", "DATA CLEAN" },
|
||||
});
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "fmt_misplaced" },
|
||||
{ "title", "MISPLACED" },
|
||||
});
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "fmt_misplaced" },
|
||||
{ "title", "DEGRADED" },
|
||||
});
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "fmt_incomplete" },
|
||||
{ "title", "INCOMPLETE" },
|
||||
});
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "fmt_state" },
|
||||
{ "title", "STATE" },
|
||||
});
|
||||
result.text = print_table(pgs, cols, parent->color);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (state == 1)
|
||||
goto resume_1;
|
||||
resume_1:
|
||||
load_pg_stats();
|
||||
if (parent->waiting > 0)
|
||||
return;
|
||||
format_pgs();
|
||||
state = 100;
|
||||
}
|
||||
};
|
||||
|
||||
std::function<bool(cli_result_t &)> cli_tool_t::start_pg_list(json11::Json cfg)
|
||||
{
|
||||
auto pg_lister = new pg_lister_t();
|
||||
pg_lister->parent = this;
|
||||
if (cfg["pool"].uint64_value())
|
||||
pg_lister->pool_id = cfg["pool"].uint64_value();
|
||||
else
|
||||
pg_lister->pool_name = cfg["pool"].string_value();
|
||||
for (auto & st: cfg["pg_state"].array_items())
|
||||
pg_lister->pg_state.push_back(st.string_value());
|
||||
if (cfg["pg_state"].is_string())
|
||||
pg_lister->pg_state.push_back(cfg["pg_state"].string_value());
|
||||
pg_lister->min_pg_num = cfg["min"].uint64_value();
|
||||
pg_lister->max_pg_num = cfg["max"].uint64_value();
|
||||
return [pg_lister](cli_result_t & result)
|
||||
{
|
||||
pg_lister->loop();
|
||||
if (pg_lister->is_done())
|
||||
{
|
||||
result = pg_lister->result;
|
||||
delete pg_lister;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user