Add vitastor-cli pg-list command
Test / buildenv (push) Successful in 12s Details
Test / build (push) Successful in 3m44s Details
Test / npm_lint (push) Successful in 10s Details
Test / test_cas (push) Successful in 11s Details
Test / make_test (push) Successful in 40s Details
Test / test_change_pg_count (push) Successful in 37s Details
Test / test_change_pg_size (push) Successful in 9s Details
Test / test_change_pg_count_ec (push) Successful in 36s Details
Test / test_create_nomaxid (push) Successful in 9s Details
Test / test_etcd_fail (push) Successful in 1m1s Details
Test / test_interrupted_rebalance (push) Successful in 1m47s Details
Test / test_interrupted_rebalance_imm (push) Successful in 1m44s Details
Test / test_add_osd (push) Successful in 3m20s Details
Test / test_failure_domain (push) Successful in 40s Details
Test / test_interrupted_rebalance_ec (push) Successful in 1m47s Details
Test / test_snapshot (push) Successful in 27s Details
Test / test_snapshot_ec (push) Successful in 26s Details
Test / test_minsize_1 (push) Successful in 18s Details
Test / test_interrupted_rebalance_ec_imm (push) Successful in 1m24s Details
Test / test_rm (push) Successful in 15s Details
Test / test_move_reappear (push) Successful in 21s Details
Test / test_snapshot_down (push) Successful in 32s Details
Test / test_snapshot_down_ec (push) Successful in 32s Details
Test / test_splitbrain (push) Successful in 25s Details
Test / test_snapshot_chain (push) Successful in 2m11s Details
Test / test_snapshot_chain_ec (push) Successful in 2m59s Details
Test / test_rebalance_verify_imm (push) Successful in 3m25s Details
Test / test_root_node (push) Successful in 13s Details
Test / test_rebalance_verify (push) Successful in 4m6s Details
Test / test_switch_primary (push) Successful in 37s Details
Test / test_write (push) Successful in 42s Details
Test / test_write_no_same (push) Successful in 21s Details
Test / test_rebalance_verify_ec (push) Successful in 3m52s Details
Test / test_rebalance_verify_ec_imm (push) Successful in 3m2s Details
Test / test_write_xor (push) Successful in 1m24s Details
Test / test_heal_pg_size_2 (push) Successful in 4m31s Details
Test / test_heal_csum_32k_dmj (push) Successful in 4m46s Details
Test / test_heal_csum_32k_dj (push) Successful in 5m50s Details
Test / test_heal_csum_4k_dj (push) Successful in 5m1s Details
Test / test_osd_tags (push) Successful in 19s Details
Test / test_enospc (push) Successful in 46s Details
Test / test_enospc_xor (push) Successful in 1m38s Details
Test / test_heal_csum_4k (push) Successful in 4m7s Details
Test / test_enospc_imm (push) Successful in 56s Details
Test / test_enospc_imm_xor (push) Successful in 56s Details
Test / test_scrub (push) Successful in 28s Details
Test / test_scrub_zero_osd_2 (push) Successful in 30s Details
Test / test_scrub_xor (push) Successful in 30s Details
Test / test_scrub_pg_size_6_pg_minsize_4_osd_count_6_ec (push) Successful in 34s Details
Test / test_nfs (push) Successful in 19s Details
Test / test_scrub_pg_size_3 (push) Successful in 48s Details
Test / test_scrub_ec (push) Successful in 26s Details
Test / test_heal_csum_32k (push) Successful in 4m51s Details
Test / test_heal_csum_4k_dmj (push) Successful in 4m48s Details
Test / test_heal_ec (push) Successful in 4m46s Details

master
Vitaliy Filippov 2024-07-10 02:27:41 +03:00
parent 3aef6682fb
commit 66b438106a
4 changed files with 317 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

288
src/cmd/cli_pg_ls.cpp Normal file
View File

@ -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;
};
}