diff --git a/src/cmd/CMakeLists.txt b/src/cmd/CMakeLists.txt index 9ad03e42..be74a1e0 100644 --- a/src/cmd/CMakeLists.txt +++ b/src/cmd/CMakeLists.txt @@ -27,6 +27,7 @@ add_library(vitastor_cli STATIC cli_pool_ls.cpp cli_pool_modify.cpp cli_pool_rm.cpp + cli_raw_ls.cpp ) target_compile_options(vitastor_cli PUBLIC -fPIC) diff --git a/src/cmd/cli.cpp b/src/cmd/cli.cpp index c49eb298..ae7977f8 100644 --- a/src/cmd/cli.cpp +++ b/src/cmd/cli.cpp @@ -126,6 +126,11 @@ static const char* help_text = " --min-offset, --max-offset\n" " Restrict listing to specific offsets inside inodes.\n" "\n" + "vitastor-cli raw-ls [OPTIONS]\n" + " Find object(s) in the cluster using raw secondary listing operations. Options:\n" + " [--min_inode NUM] [--max_inode NUM] [--offset NUM] [--pg_num NUM] [--pg_count COUNT]\n" + " [--pg_stripe_size NUM] [--osds 1,2,3,...]\n" + "\n" "vitastor-cli fix [--objects ] [--bad-osds ] [--part ] [--check no]\n" " Fix inconsistent objects in the cluster by deleting some copies.\n" " --objects \n" @@ -459,6 +464,11 @@ static int run(cli_tool_t *p, json11::Json::object cfg) // Describe unclean objects action_cb = p->start_describe(cfg); } + else if (cmd[0] == "raw-ls") + { + // Run raw listings + action_cb = p->start_raw_ls(cfg); + } else if (cmd[0] == "fix") { // Fix inconsistent objects (by deleting some copies) diff --git a/src/cmd/cli.h b/src/cmd/cli.h index e0e764fe..67277237 100644 --- a/src/cmd/cli.h +++ b/src/cmd/cli.h @@ -62,6 +62,7 @@ public: std::function start_fix(json11::Json); std::function start_flatten(json11::Json); std::function start_ls(json11::Json); + std::function start_raw_ls(json11::Json cfg); std::function start_merge(json11::Json); std::function start_modify(json11::Json); std::function start_modify_osd(json11::Json); diff --git a/src/cmd/cli_raw_ls.cpp b/src/cmd/cli_raw_ls.cpp new file mode 100644 index 00000000..768cbfee --- /dev/null +++ b/src/cmd/cli_raw_ls.cpp @@ -0,0 +1,212 @@ +// Copyright (c) Vitaliy Filippov, 2019+ +// License: VNPL-1.1 (see README.md for details) + +#include "cli_fix.h" +#include "cluster_client.h" +#include "pg_states.h" +#include "str_util.h" +#include "json_util.h" + +std::vector parse_uint64_list(json11::Json val); + +// Find object(s) in the cluster using raw secondary listing operations. +// Only for troubleshooting. +struct cli_raw_ls_t +{ + json11::Json cfg; + pool_id_t pool_id = 0; + pg_num_t pg_num = 0; + pg_num_t pg_count = 0; + uint32_t pg_stripe_size = 0; + std::set osds; + std::vector osd_list; + uint64_t min_inode = 0, max_inode = 0, min_offset = 0, max_offset = 0; + bool offset_set = false; + + cli_tool_t *parent = NULL; + int state = 0; + cli_result_t result; + bool first = true; + size_t osd_pos = 0; + + bool is_done() + { + return state == 100; + } + + void loop() + { + if (state == 1) + goto resume_1; + if (state == 100) + return; + pool_id = cfg["pool_id"].uint64_value(); + pg_num = (pg_num_t)cfg["pg_num"].uint64_value(); + pg_count = (pg_num_t)cfg["pg_count"].uint64_value(); + pg_stripe_size = cfg["pg_stripe_size"].uint64_value(); + if (!pool_id) + { + result = (cli_result_t){ .err = EINVAL, .text = "Pool ID is required" }; + state = 100; + return; + } + if (cfg["min_inode"].is_null() && cfg["max_inode"].is_null()) + { + min_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), 0); + max_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), UINT64_MAX); + } + else + { + min_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), stoull_full(cfg["min_inode"].as_string())); + max_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), stoull_full(cfg["max_inode"].as_string())); + } + if (cfg["offset"].is_null()) + { + if (!pg_num) + { + result = (cli_result_t){ .err = EINVAL, .text = "PG number is required if offset is not specified" }; + state = 100; + return; + } + min_offset = 0; + max_offset = UINT64_MAX; + } + else + { + min_offset = max_offset = stoull_full(cfg["offset"].as_string()); + offset_set = true; + } + for (auto osd_num: parse_uint64_list(cfg["osds"])) + { + osds.insert(osd_num); + } + if (!pg_count || !pg_stripe_size || !osds.size()) + { + auto pool_it = parent->cli->st_cli.pool_config.find(pool_id); + if (pool_it == parent->cli->st_cli.pool_config.end()) + { + result = (cli_result_t){ .err = EINVAL, .text = "pg_count, pg_stripe_size and osds are required if the pool does not exist" }; + state = 100; + return; + } + if (!pg_count) + { + pg_count = pool_it->second.real_pg_count; + } + if (!pg_stripe_size) + { + pg_stripe_size = pool_it->second.pg_stripe_size; + } + if (!osds.size()) + { + for (auto & pgp: pool_it->second.pg_config) + { + for (osd_num_t pg_osd: pgp.second.target_set) + osds.insert(pg_osd); + for (osd_num_t pg_osd: pgp.second.all_peers) + osds.insert(pg_osd); + for (auto & hist_item: pgp.second.target_history) + for (auto pg_osd: hist_item) + osds.insert(pg_osd); + } + osds.erase(0); + } + } + if (offset_set) + { + min_offset = (min_offset / pg_stripe_size) * pg_stripe_size; + max_offset = ((max_offset / pg_stripe_size) * pg_stripe_size) | STRIPE_MASK; + pg_num = (min_offset/pg_stripe_size) % pg_count + 1; // like map_to_pg() + fprintf(stderr, "Selected PG %u\n", pg_num); + } + parent->cli->init_msgr(); + osd_list = std::vector(osds.begin(), osds.end()); + osd_pos = 0; + state = 1; + resume_1: + for (; osd_pos < osd_list.size() && parent->waiting < parent->parallel_osds; osd_pos++) + { + uint64_t osd_num = osd_list[osd_pos]; + if (parent->cli->st_cli.peer_states[osd_num].is_null()) + { + fprintf(stderr, "OSD %ju is unavailable, skipping\n", osd_num); + continue; + } + osd_op_t *op = new osd_op_t; + op->req = (osd_any_op_t){ + .sec_list = { + .header = { + .magic = SECONDARY_OSD_OP_MAGIC, + .opcode = OSD_OP_SEC_LIST, + }, + .list_pg = pg_num, + .pg_count = pg_count, + .pg_stripe_size = pg_stripe_size, + .min_inode = min_inode, + .max_inode = max_inode, + .min_stripe = min_offset, + .max_stripe = max_offset, + }, + }; + op->callback = [this, osd_num](osd_op_t *op) + { + if (op->reply.hdr.retval < 0) + { + fprintf(stderr, "OSD %ju listing failed: retval=%jd\n", osd_num, op->reply.hdr.retval); + } + else + { + for (uint64_t i = 0; i < op->reply.hdr.retval; i++) + { + auto & ov = ((obj_ver_id*)op->buf)[i]; + if (parent->json_output) + { + printf("%s{\"osd\":%ju,\"inode\":\"0x%jx\",\"stripe\":\"0x%jx\",\"version\":%ju,\"stable\":%s}", + first ? "" : ",\n", osd_num, ov.oid.inode, ov.oid.stripe, ov.version, + i < op->reply.sec_list.stable_count ? "true" : "false"); + first = false; + } + else + { + printf("OSD %ju - %jx:%jx v%ju%s\n", + osd_num, ov.oid.inode, ov.oid.stripe, ov.version, + i < op->reply.sec_list.stable_count ? " stable" : ""); + } + } + } + parent->waiting--; + loop(); + delete op; + }; + parent->waiting++; + parent->cli->execute_raw(osd_num, op); + } + if (parent->waiting > 0) + { + return; + } + if (!first) + { + printf("\n"); + } + state = 100; + } +}; + +std::function cli_tool_t::start_raw_ls(json11::Json cfg) +{ + auto raw_ls = new cli_raw_ls_t(); + raw_ls->parent = this; + raw_ls->cfg = cfg; + return [raw_ls](cli_result_t & result) + { + raw_ls->loop(); + if (raw_ls->is_done()) + { + result = raw_ls->result; + delete raw_ls; + return true; + } + return false; + }; +} diff --git a/src/osd/osd_secondary.cpp b/src/osd/osd_secondary.cpp index baa14fe8..e91af4e4 100644 --- a/src/osd/osd_secondary.cpp +++ b/src/osd/osd_secondary.cpp @@ -226,7 +226,7 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op) cur_op->bs_op->min_oid.inode = cur_op->req.sec_list.min_inode; cur_op->bs_op->min_oid.stripe = cur_op->req.sec_list.min_stripe; cur_op->bs_op->max_oid.inode = cur_op->req.sec_list.max_inode; - if (cur_op->req.sec_list.max_inode && cur_op->req.sec_list.max_stripe != UINT64_MAX) + if (cur_op->req.sec_list.max_inode) { cur_op->bs_op->max_oid.stripe = cur_op->req.sec_list.max_stripe ? cur_op->req.sec_list.max_stripe : UINT64_MAX;