From e460640aed07d1f8b12aaea9fed9acfcf201a4c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 13:37:56 +0000 Subject: [PATCH 01/18] Initial plan From 150eace8d262104bf66d8b5987c75a2bd4c4b0f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 13:47:15 +0000 Subject: [PATCH 02/18] Add pl_debug interactive placement debugging tool Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/SConscript | 5 + src/placement/tests/pl_debug.c | 521 +++++++++++++++++++++++++++++++++ 2 files changed, 526 insertions(+) create mode 100644 src/placement/tests/pl_debug.c diff --git a/src/placement/tests/SConscript b/src/placement/tests/SConscript index b60893f268f..9063547857f 100644 --- a/src/placement/tests/SConscript +++ b/src/placement/tests/SConscript @@ -25,6 +25,7 @@ def scons(): 'jump_map_dist.c', 'placement_test.c']) pl_bench_tgt = denv.SharedObject(['pl_bench.c', 'place_obj_common.c']) + pl_debug_tgt = denv.SharedObject(['pl_debug.c', 'place_obj_common.c']) libraries = ['daos', 'daos_common', 'gurt', 'uuid', 'cmocka', 'isal', 'm'] @@ -35,9 +36,13 @@ def scons(): pl_bench = denv.d_program('pl_bench', pl_bench_tgt, LIBS=libraries) + pl_debug = denv.d_program('pl_debug', + pl_debug_tgt + ['../../pool/srv_pool_map.c'], LIBS=libraries) + denv.Install('$PREFIX/bin/', ring_pl_test) denv.Install('$PREFIX/bin/', jump_pl_test) denv.Install('$PREFIX/bin/', pl_bench) + denv.Install('$PREFIX/bin/', pl_debug) if __name__ == "SCons.Script": diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c new file mode 100644 index 00000000000..773bf433716 --- /dev/null +++ b/src/placement/tests/pl_debug.c @@ -0,0 +1,521 @@ +/** + * (C) Copyright 2025 Intel Corporation. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + * + * pl_debug - Interactive placement debugging utility + * + * Usage: pl_debug -n -r -t + * + * Constructs an in-memory pool map and jump placement map from the given + * topology parameters. NODE is used as the fault domain. + * + * Interactive commands: + * obj_class - Set current object class + * gen_layout id= - Generate layout for OID lo= + * set_down rank=|node= - Set rank/node status to DOWN + * set_downout rank=|node= - Set rank/node status to DOWNOUT + * set_up rank=|node= - Set rank/node status to UP + * set_upin rank=|node= - Set rank/node status to UPIN + * help - Show this help text + * quit / exit - Exit the tool + * + * Smoke-test example: + * $ pl_debug -n 4 -r 2 -t 8 + * pl_debug> obj_class OC_EC_4P1GX + * pl_debug> gen_layout id=42 + * pl_debug> set_down rank=0 + * pl_debug> gen_layout id=42 + * pl_debug> quit + */ + +#define D_LOGFAC DD_FAC(tests) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "place_obj_common.h" + +/* Internal pool-server APIs used to apply target state changes */ +#include "../../pool/rpc.h" +#include "../../pool/srv_pool_map.h" + +/* Layout version used by all placement tests */ +#define PLD_LAYOUT_VERSION 2 + +/* ------------------------------------------------------------------ */ +/* Global state */ +/* ------------------------------------------------------------------ */ + +static struct pool_map *g_po_map; +static struct pl_map *g_pl_map; +static daos_oclass_id_t g_obj_class = OC_RP_3GX; + +/* ------------------------------------------------------------------ */ +/* Help */ +/* ------------------------------------------------------------------ */ + +static void +print_help(void) +{ + printf("Commands:\n" + " obj_class Set current object class\n" + " gen_layout id= Generate layout (OID lo=)\n" + " set_down rank=|node= Set rank/node to DOWN\n" + " set_downout rank=|node= Set rank/node to DOWNOUT\n" + " set_up rank=|node= Set rank/node to UP\n" + " set_upin rank=|node= Set rank/node to UPIN\n" + " help Show this help\n" + " quit / exit Exit pl_debug\n"); +} + +/* ------------------------------------------------------------------ */ +/* Placement map refresh */ +/* ------------------------------------------------------------------ */ + +static void +refresh_pl_map(void) +{ + struct pl_map_init_attr mia = {0}; + + if (g_pl_map != NULL) { + pl_map_decref(g_pl_map); + g_pl_map = NULL; + } + mia.ia_type = PL_TYPE_JUMP_MAP; + mia.ia_ring.domain = PO_COMP_TP_NODE; + if (pl_map_create(g_po_map, &mia, &g_pl_map) != 0) + fprintf(stderr, "pl_map_create failed; placement unavailable\n"); +} + +/* ------------------------------------------------------------------ */ +/* Target-list helpers */ +/* ------------------------------------------------------------------ */ + +static int +build_rank_tgt_list(uint32_t rank, struct pool_target_id_list *tgts) +{ + d_rank_t rank_id = rank; + d_rank_list_t rank_list; + int rc; + + rank_list.rl_ranks = &rank_id; + rank_list.rl_nr = 1; + memset(tgts, 0, sizeof(*tgts)); + + rc = pool_map_find_targets_on_ranks(g_po_map, &rank_list, tgts); + if (rc <= 0) { + fprintf(stderr, "No targets found for rank %u (rc=%d)\n", rank, rc); + return rc == 0 ? -DER_NONEXIST : rc; + } + return 0; +} + +static int +build_node_tgt_list(uint32_t node_id, struct pool_target_id_list *tgts) +{ + struct pool_domain *node_dom; + int i, j, rc; + + memset(tgts, 0, sizeof(*tgts)); + rc = pool_map_find_domain(g_po_map, PO_COMP_TP_NODE, node_id, &node_dom); + if (rc != 1) { + fprintf(stderr, "Node %u not found\n", node_id); + return -DER_NONEXIST; + } + + for (i = 0; i < node_dom->do_child_nr; i++) { + struct pool_domain *rank_dom = &node_dom->do_children[i]; + + for (j = 0; j < rank_dom->do_target_nr; j++) { + struct pool_target_id id = {0}; + + id.pti_id = rank_dom->do_targets[j].ta_comp.co_id; + rc = pool_target_id_list_append(tgts, &id); + if (rc != 0) { + pool_target_id_list_free(tgts); + return rc; + } + } + } + + if (tgts->pti_number == 0) { + fprintf(stderr, "No targets found for node %u\n", node_id); + return -DER_NONEXIST; + } + return 0; +} + +/* ------------------------------------------------------------------ */ +/* Pool-map update helper */ +/* ------------------------------------------------------------------ */ + +/* + * Apply opc1 to every target in tgts via ds_pool_map_tgts_update(). + * If opc2 >= 0 apply it as a second step; pass opc2 = -1 to skip. + */ +static int +do_update(struct pool_target_id_list *tgts, int opc1, int opc2) +{ + uuid_t zero_uuid = {0}; + int rc; + + rc = ds_pool_map_tgts_update(zero_uuid, g_po_map, tgts, opc1, + false, NULL, true); + if (rc != 0) { + fprintf(stderr, "ds_pool_map_tgts_update opc=%d failed: %d\n", + opc1, rc); + return rc; + } + if (opc2 >= 0) { + rc = ds_pool_map_tgts_update(zero_uuid, g_po_map, tgts, opc2, + false, NULL, true); + if (rc != 0) { + fprintf(stderr, + "ds_pool_map_tgts_update opc=%d failed: %d\n", + opc2, rc); + return rc; + } + } + return 0; +} + +/* ------------------------------------------------------------------ */ +/* Command implementations */ +/* ------------------------------------------------------------------ */ + +static void +cmd_obj_class(const char *arg) +{ + daos_oclass_id_t cid; + char name[64] = {0}; + char *endp; + + if (arg == NULL || *arg == '\0') { + fprintf(stderr, "Usage: obj_class \n"); + return; + } + + /* Try numeric input first */ + cid = (daos_oclass_id_t)strtoul(arg, &endp, 0); + if (*endp != '\0') { + /* Not a number – treat as class name */ + cid = daos_oclass_name2id(arg); + if (cid == OC_UNKNOWN) { + fprintf(stderr, "Unknown object class: %s\n", arg); + return; + } + } + + g_obj_class = cid; + daos_oclass_id2name(cid, name); + printf("Object class set to: %s (id=%u)\n", name, (unsigned int)cid); +} + +static void +cmd_gen_layout(const char *arg) +{ + daos_obj_id_t oid = {0}; + struct pl_obj_layout *layout = NULL; + struct daos_obj_md md = {0}; + uint64_t lo_val; + char name[64] = {0}; + int grp, sz, index, rc; + + if (arg == NULL || strncmp(arg, "id=", 3) != 0) { + fprintf(stderr, "Usage: gen_layout id=\n"); + return; + } + + if (g_pl_map == NULL) { + fprintf(stderr, "Placement map unavailable\n"); + return; + } + + lo_val = strtoull(arg + 3, NULL, 0); + oid.lo = lo_val; + oid.hi = 0; + + rc = daos_obj_set_oid_by_class(&oid, 0, g_obj_class, 0); + if (rc != 0) { + fprintf(stderr, "daos_obj_set_oid_by_class failed: %d\n", rc); + return; + } + + md.omd_id = oid; + md.omd_ver = pool_map_get_version(g_pl_map->pl_poolmap); + md.omd_pda = 0; + + rc = pl_obj_place(g_pl_map, PLD_LAYOUT_VERSION, &md, 0, NULL, &layout); + if (rc != 0) { + fprintf(stderr, "pl_obj_place failed: %d\n", rc); + return; + } + + daos_oclass_id2name(g_obj_class, name); + printf("Layout for OID lo=%" PRIu64 " class=%s (id=%u):\n", + lo_val, name, (unsigned int)g_obj_class); + printf(" groups=%u group_size=%u total_shards=%u\n", + layout->ol_grp_nr, layout->ol_grp_size, layout->ol_nr); + + for (grp = 0; grp < layout->ol_grp_nr; ++grp) { + printf(" [group %d]\n", grp); + for (sz = 0; sz < layout->ol_grp_size; ++sz) { + struct pl_obj_shard shard; + + index = grp * layout->ol_grp_size + sz; + shard = layout->ol_shards[index]; + printf(" shard %2d: target_id=%4d rank=%4u" + " tgt_idx=%2u fseq=%u%s\n", + shard.po_shard, + shard.po_target, + shard.po_rank, + shard.po_index, + shard.po_fseq, + shard.po_rebuilding ? " [rebuilding]" : ""); + } + } + + pl_obj_layout_free(layout); +} + +/* + * Parse "rank=" or "node=", build the target list, apply + * opc1 (and optionally opc2 < 0 means skip), then refresh pl_map. + */ +static void +cmd_set_state(const char *subcmd, const char *arg, int opc1, int opc2) +{ + struct pool_target_id_list tgts = {0}; + unsigned long id; + char *endp; + int rc; + + if (arg == NULL) { + fprintf(stderr, "Usage: %s rank=|node=\n", subcmd); + return; + } + + if (strncmp(arg, "rank=", 5) == 0) { + id = strtoul(arg + 5, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "Invalid rank number: %s\n", arg + 5); + return; + } + rc = build_rank_tgt_list((uint32_t)id, &tgts); + } else if (strncmp(arg, "node=", 5) == 0) { + id = strtoul(arg + 5, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "Invalid node number: %s\n", arg + 5); + return; + } + rc = build_node_tgt_list((uint32_t)id, &tgts); + } else { + fprintf(stderr, "Usage: %s rank=|node=\n", subcmd); + return; + } + + if (rc != 0) { + fprintf(stderr, "%s: failed to build target list: %d\n", + subcmd, rc); + return; + } + + rc = do_update(&tgts, opc1, opc2); + pool_target_id_list_free(&tgts); + if (rc != 0) { + fprintf(stderr, "%s: pool map update failed: %d\n", subcmd, rc); + return; + } + + refresh_pl_map(); + printf("Pool map updated (version=%u). Placement map refreshed.\n", + pool_map_get_version(g_po_map)); +} + +/* ------------------------------------------------------------------ */ +/* REPL */ +/* ------------------------------------------------------------------ */ + +static void +run_repl(void) +{ + char line[1024]; + char *cmd, *arg, *nl; + + printf("pl_debug interactive shell. Type 'help' for commands.\n"); + + for (;;) { + printf("pl_debug> "); + fflush(stdout); + + if (fgets(line, sizeof(line), stdin) == NULL) + break; + + /* Strip trailing newline */ + nl = strchr(line, '\n'); + if (nl != NULL) + *nl = '\0'; + + /* Skip leading whitespace */ + cmd = line; + while (*cmd == ' ' || *cmd == '\t') + cmd++; + + /* Skip blank lines and comments */ + if (*cmd == '\0' || *cmd == '#') + continue; + + /* Split at the first whitespace boundary */ + arg = cmd; + while (*arg != '\0' && *arg != ' ' && *arg != '\t') + arg++; + if (*arg != '\0') { + *arg = '\0'; + arg++; + while (*arg == ' ' || *arg == '\t') + arg++; + } else { + arg = NULL; + } + + if (strcmp(cmd, "quit") == 0 || strcmp(cmd, "exit") == 0) { + break; + } else if (strcmp(cmd, "help") == 0) { + print_help(); + } else if (strcmp(cmd, "obj_class") == 0) { + cmd_obj_class(arg); + } else if (strcmp(cmd, "gen_layout") == 0) { + cmd_gen_layout(arg); + } else if (strcmp(cmd, "set_down") == 0) { + /* UP/UPIN → DOWN */ + cmd_set_state("set_down", arg, MAP_EXCLUDE, -1); + } else if (strcmp(cmd, "set_downout") == 0) { + /* UP/UPIN → DOWN → DOWNOUT */ + cmd_set_state("set_downout", arg, + MAP_EXCLUDE, MAP_EXCLUDE_OUT); + } else if (strcmp(cmd, "set_up") == 0) { + /* DOWN/DOWNOUT → UP */ + cmd_set_state("set_up", arg, MAP_REINT, -1); + } else if (strcmp(cmd, "set_upin") == 0) { + /* DOWN/DOWNOUT → UP → UPIN */ + cmd_set_state("set_upin", arg, MAP_REINT, MAP_ADD_IN); + } else { + fprintf(stderr, + "Unknown command: '%s' (type 'help')\n", cmd); + } + } + + printf("Exiting pl_debug.\n"); +} + +/* ------------------------------------------------------------------ */ +/* main */ +/* ------------------------------------------------------------------ */ + +static void +usage(const char *prog) +{ + fprintf(stderr, + "Usage: %s -n -r -t \n" + "Options:\n" + " -n Number of nodes\n" + " -r Number of ranks per node\n" + " -t Number of targets per rank\n", + prog); +} + +int +main(int argc, char *argv[]) +{ + int n = 0, r = 0, t = 0; + int opt, rc; + + while ((opt = getopt(argc, argv, "n:r:t:h")) != -1) { + switch (opt) { + case 'n': + n = atoi(optarg); + break; + case 'r': + r = atoi(optarg); + break; + case 't': + t = atoi(optarg); + break; + case 'h': + default: + usage(argv[0]); + return 0; + } + } + + if (n <= 0 || r <= 0 || t <= 0) { + fprintf(stderr, + "Error: -n, -r, and -t must all be positive integers.\n"); + usage(argv[0]); + return 1; + } + + rc = daos_debug_init(DAOS_LOG_DEFAULT); + if (rc != 0) { + fprintf(stderr, "daos_debug_init failed: %d\n", rc); + goto out; + } + + rc = obj_class_init(); + if (rc != 0) { + fprintf(stderr, "obj_class_init failed: %d\n", rc); + goto out_debug; + } + + rc = pl_init(); + if (rc != 0) { + fprintf(stderr, "pl_init failed: %d\n", rc); + goto out_obj_class; + } + + /* + * Build pool map: + * gen_pool_and_placement_map(num_pds, fdoms_per_pd, + * nodes_per_domain, vos_per_target, ...) + * With num_pds=1, fdoms_per_pd=n, nodes_per_domain=r, + * vos_per_target=t we get: + * n nodes × r ranks/node × t targets/rank + * NODE is used as the fault domain. + */ + gen_pool_and_placement_map(1, n, r, t, PL_TYPE_JUMP_MAP, + PO_COMP_TP_NODE, + &g_po_map, &g_pl_map); + if (g_po_map == NULL || g_pl_map == NULL) { + fprintf(stderr, "Failed to create pool/placement map\n"); + rc = -DER_NOMEM; + goto out_pl; + } + + printf("Pool map: %d nodes × %d ranks/node × %d targets/rank" + " = %d total targets\n", n, r, t, n * r * t); + printf("Fault domain: NODE | Map version: %u\n", + pool_map_get_version(g_po_map)); + + run_repl(); + + free_pool_and_placement_map(g_po_map, g_pl_map); + +out_pl: + pl_fini(); +out_obj_class: + obj_class_fini(); +out_debug: + daos_debug_fini(); +out: + return rc != 0 ? 1 : 0; +} From 5e0c352d33fd2bdb7382f24a252cb6839d57b5b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 14:18:19 +0000 Subject: [PATCH 03/18] Add mode= option to gen_layout command in pl_debug Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 83 ++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index 773bf433716..98966d2d389 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -12,7 +12,7 @@ * * Interactive commands: * obj_class - Set current object class - * gen_layout id= - Generate layout for OID lo= + * gen_layout id= [mode=] * set_down rank=|node= - Set rank/node status to DOWN * set_downout rank=|node= - Set rank/node status to DOWNOUT * set_up rank=|node= - Set rank/node status to UP @@ -24,8 +24,9 @@ * $ pl_debug -n 4 -r 2 -t 8 * pl_debug> obj_class OC_EC_4P1GX * pl_debug> gen_layout id=42 + * pl_debug> gen_layout id=42 mode=current * pl_debug> set_down rank=0 - * pl_debug> gen_layout id=42 + * pl_debug> gen_layout id=42 mode=post_rebuild * pl_debug> quit */ @@ -48,6 +49,8 @@ /* Internal pool-server APIs used to apply target state changes */ #include "../../pool/rpc.h" #include "../../pool/srv_pool_map.h" +/* layout_gen_mode enum (PRE_REBUILD / CURRENT / POST_REBUILD) */ +#include "../pl_map.h" /* Layout version used by all placement tests */ #define PLD_LAYOUT_VERSION 2 @@ -69,7 +72,10 @@ print_help(void) { printf("Commands:\n" " obj_class Set current object class\n" - " gen_layout id= Generate layout (OID lo=)\n" + " gen_layout id= [mode=]\n" + " Generate layout (OID lo=)\n" + " mode: pre_rebuild|0, current|1, post_rebuild|2\n" + " (default: pre_rebuild)\n" " set_down rank=|node= Set rank/node to DOWN\n" " set_downout rank=|node= Set rank/node to DOWNOUT\n" " set_up rank=|node= Set rank/node to UP\n" @@ -189,6 +195,9 @@ do_update(struct pool_target_id_list *tgts, int opc1, int opc2) return 0; } +/* Human-readable names for layout_gen_mode values */ +static const char *layout_gen_mode_names[] = {"pre_rebuild", "current", "post_rebuild"}; + /* ------------------------------------------------------------------ */ /* Command implementations */ /* ------------------------------------------------------------------ */ @@ -229,10 +238,14 @@ cmd_gen_layout(const char *arg) struct daos_obj_md md = {0}; uint64_t lo_val; char name[64] = {0}; + char arg_copy[1024]; + char *tok, *save; + bool id_found = false; + enum layout_gen_mode mode = PRE_REBUILD; int grp, sz, index, rc; - if (arg == NULL || strncmp(arg, "id=", 3) != 0) { - fprintf(stderr, "Usage: gen_layout id=\n"); + if (arg == NULL || *arg == '\0') { + fprintf(stderr, "Usage: gen_layout id= [mode=]\n"); return; } @@ -241,7 +254,58 @@ cmd_gen_layout(const char *arg) return; } - lo_val = strtoull(arg + 3, NULL, 0); + /* Tokenise a copy of the argument string on whitespace */ + snprintf(arg_copy, sizeof(arg_copy), "%s", arg); + for (tok = strtok_r(arg_copy, " \t", &save); tok != NULL; + tok = strtok_r(NULL, " \t", &save)) { + if (strncmp(tok, "id=", 3) == 0) { + char *endp; + + lo_val = strtoull(tok + 3, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "Invalid id value: %s\n", tok + 3); + return; + } + id_found = true; + } else if (strncmp(tok, "mode=", 5) == 0) { + const char *val = tok + 5; + char *endp; + long num; + + /* Accept numeric values */ + num = strtol(val, &endp, 0); + if (*endp == '\0') { + if (num < PRE_REBUILD || num > POST_REBUILD) { + fprintf(stderr, + "Invalid mode value %ld; valid: 0 (pre_rebuild), 1 (current), 2 (post_rebuild)\n", + num); + return; + } + mode = (enum layout_gen_mode)num; + } else if (strcasecmp(val, "pre_rebuild") == 0) { + mode = PRE_REBUILD; + } else if (strcasecmp(val, "current") == 0) { + mode = CURRENT; + } else if (strcasecmp(val, "post_rebuild") == 0) { + mode = POST_REBUILD; + } else { + fprintf(stderr, + "Unknown mode '%s'; valid: pre_rebuild, current, post_rebuild\n", + val); + return; + } + } else { + fprintf(stderr, "Unknown gen_layout option: '%s'\n", tok); + fprintf(stderr, "Usage: gen_layout id= [mode=]\n"); + return; + } + } + + if (!id_found) { + fprintf(stderr, "Usage: gen_layout id= [mode=]\n"); + return; + } + oid.lo = lo_val; oid.hi = 0; @@ -255,15 +319,16 @@ cmd_gen_layout(const char *arg) md.omd_ver = pool_map_get_version(g_pl_map->pl_poolmap); md.omd_pda = 0; - rc = pl_obj_place(g_pl_map, PLD_LAYOUT_VERSION, &md, 0, NULL, &layout); + rc = pl_obj_place(g_pl_map, PLD_LAYOUT_VERSION, &md, (unsigned int)mode, + NULL, &layout); if (rc != 0) { fprintf(stderr, "pl_obj_place failed: %d\n", rc); return; } daos_oclass_id2name(g_obj_class, name); - printf("Layout for OID lo=%" PRIu64 " class=%s (id=%u):\n", - lo_val, name, (unsigned int)g_obj_class); + printf("Layout for OID lo=%" PRIu64 " class=%s (id=%u) mode=%s:\n", + lo_val, name, (unsigned int)g_obj_class, layout_gen_mode_names[mode]); printf(" groups=%u group_size=%u total_shards=%u\n", layout->ol_grp_nr, layout->ol_grp_size, layout->ol_nr); From e7817bd3847df4d437cb1678e2f0549ae4de1acd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 14:25:50 +0000 Subject: [PATCH 04/18] Add ver= option to gen_layout command in pl_debug Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index 98966d2d389..d75c94bd99e 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -12,7 +12,7 @@ * * Interactive commands: * obj_class - Set current object class - * gen_layout id= [mode=] + * gen_layout id= [mode=] [ver=] * set_down rank=|node= - Set rank/node status to DOWN * set_downout rank=|node= - Set rank/node status to DOWNOUT * set_up rank=|node= - Set rank/node status to UP @@ -24,7 +24,7 @@ * $ pl_debug -n 4 -r 2 -t 8 * pl_debug> obj_class OC_EC_4P1GX * pl_debug> gen_layout id=42 - * pl_debug> gen_layout id=42 mode=current + * pl_debug> gen_layout id=42 mode=current ver=3 * pl_debug> set_down rank=0 * pl_debug> gen_layout id=42 mode=post_rebuild * pl_debug> quit @@ -72,10 +72,11 @@ print_help(void) { printf("Commands:\n" " obj_class Set current object class\n" - " gen_layout id= [mode=]\n" + " gen_layout id= [mode=] [ver=]\n" " Generate layout (OID lo=)\n" " mode: pre_rebuild|0, current|1, post_rebuild|2\n" " (default: pre_rebuild)\n" + " ver: pool map version (default: current)\n" " set_down rank=|node= Set rank/node to DOWN\n" " set_downout rank=|node= Set rank/node to DOWNOUT\n" " set_up rank=|node= Set rank/node to UP\n" @@ -242,10 +243,11 @@ cmd_gen_layout(const char *arg) char *tok, *save; bool id_found = false; enum layout_gen_mode mode = PRE_REBUILD; + uint32_t ver = 0; /* 0 means "use current map version" */ int grp, sz, index, rc; if (arg == NULL || *arg == '\0') { - fprintf(stderr, "Usage: gen_layout id= [mode=]\n"); + fprintf(stderr, "Usage: gen_layout id= [mode=] [ver=]\n"); return; } @@ -294,15 +296,25 @@ cmd_gen_layout(const char *arg) val); return; } + } else if (strncmp(tok, "ver=", 4) == 0) { + char *endp; + unsigned long v; + + v = strtoul(tok + 4, &endp, 0); + if (*endp != '\0' || v > UINT32_MAX) { + fprintf(stderr, "Invalid ver value: %s\n", tok + 4); + return; + } + ver = (uint32_t)v; } else { fprintf(stderr, "Unknown gen_layout option: '%s'\n", tok); - fprintf(stderr, "Usage: gen_layout id= [mode=]\n"); + fprintf(stderr, "Usage: gen_layout id= [mode=] [ver=]\n"); return; } } if (!id_found) { - fprintf(stderr, "Usage: gen_layout id= [mode=]\n"); + fprintf(stderr, "Usage: gen_layout id= [mode=] [ver=]\n"); return; } @@ -316,7 +328,7 @@ cmd_gen_layout(const char *arg) } md.omd_id = oid; - md.omd_ver = pool_map_get_version(g_pl_map->pl_poolmap); + md.omd_ver = (ver != 0) ? ver : pool_map_get_version(g_pl_map->pl_poolmap); md.omd_pda = 0; rc = pl_obj_place(g_pl_map, PLD_LAYOUT_VERSION, &md, (unsigned int)mode, @@ -327,8 +339,9 @@ cmd_gen_layout(const char *arg) } daos_oclass_id2name(g_obj_class, name); - printf("Layout for OID lo=%" PRIu64 " class=%s (id=%u) mode=%s:\n", - lo_val, name, (unsigned int)g_obj_class, layout_gen_mode_names[mode]); + printf("Layout for OID lo=%" PRIu64 " class=%s (id=%u) mode=%s ver=%u:\n", + lo_val, name, (unsigned int)g_obj_class, + layout_gen_mode_names[mode], md.omd_ver); printf(" groups=%u group_size=%u total_shards=%u\n", layout->ol_grp_nr, layout->ol_grp_size, layout->ol_nr); From 1444f0cc5c93d8a7d34cb06fbfa6bdf963db92d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 15:02:59 +0000 Subject: [PATCH 05/18] Rename opc1/opc2 to opc/opc_second and add PLD_NO_OPC sentinel in pl_debug Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index d75c94bd99e..ecf77644577 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -55,6 +55,9 @@ /* Layout version used by all placement tests */ #define PLD_LAYOUT_VERSION 2 +/* Sentinel for the optional second map_update_opc argument: pass this to skip */ +#define PLD_NO_OPC (-1) + /* ------------------------------------------------------------------ */ /* Global state */ /* ------------------------------------------------------------------ */ @@ -167,29 +170,31 @@ build_node_tgt_list(uint32_t node_id, struct pool_target_id_list *tgts) /* ------------------------------------------------------------------ */ /* - * Apply opc1 to every target in tgts via ds_pool_map_tgts_update(). - * If opc2 >= 0 apply it as a second step; pass opc2 = -1 to skip. + * Apply opc (a map_update_opc value) to every target in tgts via + * ds_pool_map_tgts_update(). If opc_second != PLD_NO_OPC, apply it as a + * second step afterwards (used for two-phase transitions such as DOWN → + * DOWNOUT or UP → UPIN). */ static int -do_update(struct pool_target_id_list *tgts, int opc1, int opc2) +do_update(struct pool_target_id_list *tgts, int opc, int opc_second) { uuid_t zero_uuid = {0}; int rc; - rc = ds_pool_map_tgts_update(zero_uuid, g_po_map, tgts, opc1, + rc = ds_pool_map_tgts_update(zero_uuid, g_po_map, tgts, opc, false, NULL, true); if (rc != 0) { fprintf(stderr, "ds_pool_map_tgts_update opc=%d failed: %d\n", - opc1, rc); + opc, rc); return rc; } - if (opc2 >= 0) { - rc = ds_pool_map_tgts_update(zero_uuid, g_po_map, tgts, opc2, + if (opc_second != PLD_NO_OPC) { + rc = ds_pool_map_tgts_update(zero_uuid, g_po_map, tgts, opc_second, false, NULL, true); if (rc != 0) { fprintf(stderr, "ds_pool_map_tgts_update opc=%d failed: %d\n", - opc2, rc); + opc_second, rc); return rc; } } @@ -368,10 +373,12 @@ cmd_gen_layout(const char *arg) /* * Parse "rank=" or "node=", build the target list, apply - * opc1 (and optionally opc2 < 0 means skip), then refresh pl_map. + * opc (a map_update_opc value) to transition targets to the desired state, + * then optionally apply opc_second for a two-phase transition (e.g. DOWN → + * DOWNOUT or UP → UPIN). Pass PLD_NO_OPC for opc_second to skip it. */ static void -cmd_set_state(const char *subcmd, const char *arg, int opc1, int opc2) +cmd_set_state(const char *subcmd, const char *arg, int opc, int opc_second) { struct pool_target_id_list tgts = {0}; unsigned long id; @@ -408,7 +415,7 @@ cmd_set_state(const char *subcmd, const char *arg, int opc1, int opc2) return; } - rc = do_update(&tgts, opc1, opc2); + rc = do_update(&tgts, opc, opc_second); pool_target_id_list_free(&tgts); if (rc != 0) { fprintf(stderr, "%s: pool map update failed: %d\n", subcmd, rc); @@ -476,14 +483,14 @@ run_repl(void) cmd_gen_layout(arg); } else if (strcmp(cmd, "set_down") == 0) { /* UP/UPIN → DOWN */ - cmd_set_state("set_down", arg, MAP_EXCLUDE, -1); + cmd_set_state("set_down", arg, MAP_EXCLUDE, PLD_NO_OPC); } else if (strcmp(cmd, "set_downout") == 0) { /* UP/UPIN → DOWN → DOWNOUT */ cmd_set_state("set_downout", arg, MAP_EXCLUDE, MAP_EXCLUDE_OUT); } else if (strcmp(cmd, "set_up") == 0) { /* DOWN/DOWNOUT → UP */ - cmd_set_state("set_up", arg, MAP_REINT, -1); + cmd_set_state("set_up", arg, MAP_REINT, PLD_NO_OPC); } else if (strcmp(cmd, "set_upin") == 0) { /* DOWN/DOWNOUT → UP → UPIN */ cmd_set_state("set_upin", arg, MAP_REINT, MAP_ADD_IN); From ac05231daa56d258d552c970c5aa7004f3c06431 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 15:11:07 +0000 Subject: [PATCH 06/18] Add query command to print node/rank/target status in pl_debug Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index ecf77644577..38904fdb92e 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -17,6 +17,7 @@ * set_downout rank=|node= - Set rank/node status to DOWNOUT * set_up rank=|node= - Set rank/node status to UP * set_upin rank=|node= - Set rank/node status to UPIN + * query rank=|node= - Print status of all sub-components and targets * help - Show this help text * quit / exit - Exit the tool * @@ -84,6 +85,7 @@ print_help(void) " set_downout rank=|node= Set rank/node to DOWNOUT\n" " set_up rank=|node= Set rank/node to UP\n" " set_upin rank=|node= Set rank/node to UPIN\n" + " query rank=|node= Print status of all sub-components and targets\n" " help Show this help\n" " quit / exit Exit pl_debug\n"); } @@ -427,6 +429,90 @@ cmd_set_state(const char *subcmd, const char *arg, int opc, int opc_second) pool_map_get_version(g_po_map)); } +/* + * Print the status of the specified rank domain and all of its targets. + */ +static void +print_rank_status(const struct pool_domain *rank_dom) +{ + const struct pool_component *rc = &rank_dom->do_comp; + int j; + + printf(" rank %u id=%u status=%-8s ver=%u targets=%u\n", + rc->co_rank, rc->co_id, + pool_comp_state2str(rc->co_status), + rc->co_ver, rank_dom->do_target_nr); + + for (j = 0; j < (int)rank_dom->do_target_nr; j++) { + const struct pool_component *tc = &rank_dom->do_targets[j].ta_comp; + + printf(" target[%2d] id=%4u idx=%2u status=%-8s ver=%u fseq=%u\n", + j, tc->co_id, tc->co_index, + pool_comp_state2str(tc->co_status), + tc->co_ver, tc->co_fseq); + } +} + +/* + * query rank= - print the rank's status and all its targets. + * query node= - print the node's status, then each rank and its targets. + */ +static void +cmd_query(const char *arg) +{ + unsigned long id; + char *endp; + + if (arg == NULL) { + fprintf(stderr, "Usage: query rank=|node=\n"); + return; + } + + if (strncmp(arg, "rank=", 5) == 0) { + struct pool_domain *rank_dom; + + id = strtoul(arg + 5, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "Invalid rank number: %s\n", arg + 5); + return; + } + rank_dom = pool_map_find_dom_by_rank(g_po_map, (uint32_t)id); + if (rank_dom == NULL) { + fprintf(stderr, "Rank %lu not found\n", id); + return; + } + printf("Pool map version: %u\n", + pool_map_get_version(g_po_map)); + print_rank_status(rank_dom); + } else if (strncmp(arg, "node=", 5) == 0) { + struct pool_domain *node_dom; + int i, rc; + + id = strtoul(arg + 5, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "Invalid node number: %s\n", arg + 5); + return; + } + rc = pool_map_find_domain(g_po_map, PO_COMP_TP_NODE, + (uint32_t)id, &node_dom); + if (rc != 1) { + fprintf(stderr, "Node %lu not found\n", id); + return; + } + printf("Pool map version: %u\n", + pool_map_get_version(g_po_map)); + printf("node %u id=%u status=%-8s ver=%u ranks=%u\n", + (unsigned int)id, node_dom->do_comp.co_id, + pool_comp_state2str(node_dom->do_comp.co_status), + node_dom->do_comp.co_ver, node_dom->do_child_nr); + + for (i = 0; i < (int)node_dom->do_child_nr; i++) + print_rank_status(&node_dom->do_children[i]); + } else { + fprintf(stderr, "Usage: query rank=|node=\n"); + } +} + /* ------------------------------------------------------------------ */ /* REPL */ /* ------------------------------------------------------------------ */ @@ -494,6 +580,8 @@ run_repl(void) } else if (strcmp(cmd, "set_upin") == 0) { /* DOWN/DOWNOUT → UP → UPIN */ cmd_set_state("set_upin", arg, MAP_REINT, MAP_ADD_IN); + } else if (strcmp(cmd, "query") == 0) { + cmd_query(arg); } else { fprintf(stderr, "Unknown command: '%s' (type 'help')\n", cmd); From 346b11b909ad59989e1abd542450483f8344b87e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 15:16:17 +0000 Subject: [PATCH 07/18] Fix duplicate srv_pool_map.c compilation in placement tests SConscript Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/SConscript | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/placement/tests/SConscript b/src/placement/tests/SConscript index 9063547857f..c52b4c7b138 100644 --- a/src/placement/tests/SConscript +++ b/src/placement/tests/SConscript @@ -26,18 +26,19 @@ def scons(): 'placement_test.c']) pl_bench_tgt = denv.SharedObject(['pl_bench.c', 'place_obj_common.c']) pl_debug_tgt = denv.SharedObject(['pl_debug.c', 'place_obj_common.c']) + srv_pool_map_obj = denv.SharedObject(['../../pool/srv_pool_map.c']) libraries = ['daos', 'daos_common', 'gurt', 'uuid', 'cmocka', 'isal', 'm'] ring_pl_test = denv.d_program('ring_pl_map', ring_test_tgt, LIBS=libraries) jump_pl_test = denv.d_program('jump_pl_map', - jump_test_tgt + ['../../pool/srv_pool_map.c'], LIBS=libraries) + jump_test_tgt + srv_pool_map_obj, LIBS=libraries) pl_bench = denv.d_program('pl_bench', pl_bench_tgt, LIBS=libraries) pl_debug = denv.d_program('pl_debug', - pl_debug_tgt + ['../../pool/srv_pool_map.c'], LIBS=libraries) + pl_debug_tgt + srv_pool_map_obj, LIBS=libraries) denv.Install('$PREFIX/bin/', ring_pl_test) denv.Install('$PREFIX/bin/', jump_pl_test) From bc70034f6cf41b21bd5588c58a455db5b01e4784 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 15:23:11 +0000 Subject: [PATCH 08/18] placement/tests: fix srv_pool_map.os duplicate-environment SCons conflict Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/SConscript | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/placement/tests/SConscript b/src/placement/tests/SConscript index c52b4c7b138..849568db5de 100644 --- a/src/placement/tests/SConscript +++ b/src/placement/tests/SConscript @@ -26,7 +26,8 @@ def scons(): 'placement_test.c']) pl_bench_tgt = denv.SharedObject(['pl_bench.c', 'place_obj_common.c']) pl_debug_tgt = denv.SharedObject(['pl_debug.c', 'place_obj_common.c']) - srv_pool_map_obj = denv.SharedObject(['../../pool/srv_pool_map.c']) + srv_pool_map_obj = denv.SharedObject(target='srv_pool_map', + source='../../pool/srv_pool_map.c') libraries = ['daos', 'daos_common', 'gurt', 'uuid', 'cmocka', 'isal', 'm'] From fd48ac2dcd1093541a2bd852bcc296ca486271e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 15:33:20 +0000 Subject: [PATCH 09/18] placement/tests: add --help long option to pl_debug Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index 38904fdb92e..84c197ecf51 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -6,6 +6,7 @@ * pl_debug - Interactive placement debugging utility * * Usage: pl_debug -n -r -t + * [-h|--help] * * Constructs an in-memory pool map and jump placement map from the given * topology parameters. NODE is used as the fault domain. @@ -603,7 +604,8 @@ usage(const char *prog) "Options:\n" " -n Number of nodes\n" " -r Number of ranks per node\n" - " -t Number of targets per rank\n", + " -t Number of targets per rank\n" + " -h, --help Show this help text\n", prog); } @@ -613,7 +615,12 @@ main(int argc, char *argv[]) int n = 0, r = 0, t = 0; int opt, rc; - while ((opt = getopt(argc, argv, "n:r:t:h")) != -1) { + static struct option long_opts[] = { + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0 } + }; + + while ((opt = getopt_long(argc, argv, "n:r:t:h", long_opts, NULL)) != -1) { switch (opt) { case 'n': n = atoi(optarg); From 29c56a7e2ecd66801f60ac4e845e99ee9a5871d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 15:47:21 +0000 Subject: [PATCH 10/18] placement/tests: fix rank domain status not updated after set_down/set_downout Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index 84c197ecf51..5b921168695 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -185,7 +185,7 @@ do_update(struct pool_target_id_list *tgts, int opc, int opc_second) int rc; rc = ds_pool_map_tgts_update(zero_uuid, g_po_map, tgts, opc, - false, NULL, true); + true, NULL, true); if (rc != 0) { fprintf(stderr, "ds_pool_map_tgts_update opc=%d failed: %d\n", opc, rc); @@ -193,7 +193,7 @@ do_update(struct pool_target_id_list *tgts, int opc, int opc_second) } if (opc_second != PLD_NO_OPC) { rc = ds_pool_map_tgts_update(zero_uuid, g_po_map, tgts, opc_second, - false, NULL, true); + true, NULL, true); if (rc != 0) { fprintf(stderr, "ds_pool_map_tgts_update opc=%d failed: %d\n", From 6219f69d7b06ac47fe06a62ce5ae209f38c70afd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 16:02:46 +0000 Subject: [PATCH 11/18] placement/tests: gen_layout error when class unset; add print_obj_class command Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 105 ++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index 5b921168695..0128ef57cfb 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -13,6 +13,7 @@ * * Interactive commands: * obj_class - Set current object class + * print_obj_class [name|id] - Print representative object classes, or name of one class * gen_layout id= [mode=] [ver=] * set_down rank=|node= - Set rank/node status to DOWN * set_downout rank=|node= - Set rank/node status to DOWNOUT @@ -60,13 +61,16 @@ /* Sentinel for the optional second map_update_opc argument: pass this to skip */ #define PLD_NO_OPC (-1) +/* Buffer size for daos_oclass_names_list (covers all currently registered classes) */ +#define PLD_OCLASS_LIST_BUF_SIZE (64 << 10) + /* ------------------------------------------------------------------ */ /* Global state */ /* ------------------------------------------------------------------ */ static struct pool_map *g_po_map; static struct pl_map *g_pl_map; -static daos_oclass_id_t g_obj_class = OC_RP_3GX; +static daos_oclass_id_t g_obj_class = OC_UNKNOWN; /* ------------------------------------------------------------------ */ /* Help */ @@ -77,6 +81,8 @@ print_help(void) { printf("Commands:\n" " obj_class Set current object class\n" + " print_obj_class [name|id] List representative classes (*G1/*G2/*G32/*GX)\n" + " or print the name of a single class\n" " gen_layout id= [mode=] [ver=]\n" " Generate layout (OID lo=)\n" " mode: pre_rebuild|0, current|1, post_rebuild|2\n" @@ -239,6 +245,95 @@ cmd_obj_class(const char *arg) printf("Object class set to: %s (id=%u)\n", name, (unsigned int)cid); } +/* + * Returns true if the string 'str' ends with the suffix 'suffix'. + */ +static bool +str_ends_with(const char *str, const char *suffix) +{ + size_t slen = strlen(str); + size_t plen = strlen(suffix); + + return slen >= plen && strcmp(str + slen - plen, suffix) == 0; +} + +/* + * Returns true if 'name' is a representative object class to display: + * G-family (*G1, *G2, *G32, *GX) and S-family single-replica equivalents. + */ +static bool +is_representative_oclass(const char *name) +{ + /* G-family: classes ending in G1, G2, G32, or GX */ + if (str_ends_with(name, "G1") || str_ends_with(name, "G2") || + str_ends_with(name, "G32") || str_ends_with(name, "GX")) + return true; + + /* S-family (single-replica, no 'G' in name): S1, S2, S32, SX */ + return strcmp(name, "S1") == 0 || strcmp(name, "S2") == 0 || + strcmp(name, "S32") == 0 || strcmp(name, "SX") == 0; +} + +/* + * print_obj_class [name|id] + * + * With no argument: list representative classes (*G1, *G2, *G32, *GX). + * With an argument: look up that class and print its name and ID. + */ +static void +cmd_print_obj_class(const char *arg) +{ + char name[64] = {0}; + daos_oclass_id_t cid; + char *endp; + char *buf; + char *save; + char *tok; + + /* With an argument, look up a single class by name or ID */ + if (arg != NULL && *arg != '\0') { + cid = (daos_oclass_id_t)strtoul(arg, &endp, 0); + if (*endp != '\0') { + cid = daos_oclass_name2id(arg); + if (cid == OC_UNKNOWN) { + fprintf(stderr, "Unknown object class: %s\n", arg); + return; + } + } + if (daos_oclass_id2name(cid, name) < 0) { + fprintf(stderr, "Invalid object class id: %u\n", + (unsigned int)cid); + return; + } + printf("%s (id=%u)\n", name, (unsigned int)cid); + return; + } + + /* No argument: list representative classes */ + buf = malloc(PLD_OCLASS_LIST_BUF_SIZE); + if (buf == NULL) { + fprintf(stderr, "Out of memory\n"); + return; + } + + if (daos_oclass_names_list(PLD_OCLASS_LIST_BUF_SIZE, buf) < 0) { + fprintf(stderr, "daos_oclass_names_list failed\n"); + free(buf); + return; + } + + printf("Representative object classes (*G1, *G2, *G32, *GX):\n"); + for (tok = strtok_r(buf, ", ", &save); tok != NULL; + tok = strtok_r(NULL, ", ", &save)) { + if (is_representative_oclass(tok)) { + cid = daos_oclass_name2id(tok); + printf(" %-20s (id=%u)\n", tok, (unsigned int)cid); + } + } + + free(buf); +} + static void cmd_gen_layout(const char *arg) { @@ -259,6 +354,12 @@ cmd_gen_layout(const char *arg) return; } + if (g_obj_class == OC_UNKNOWN) { + fprintf(stderr, "No object class set; run 'obj_class ' first\n" + " (try 'print_obj_class' to see available classes)\n"); + return; + } + if (g_pl_map == NULL) { fprintf(stderr, "Placement map unavailable\n"); return; @@ -566,6 +667,8 @@ run_repl(void) print_help(); } else if (strcmp(cmd, "obj_class") == 0) { cmd_obj_class(arg); + } else if (strcmp(cmd, "print_obj_class") == 0) { + cmd_print_obj_class(arg); } else if (strcmp(cmd, "gen_layout") == 0) { cmd_gen_layout(arg); } else if (strcmp(cmd, "set_down") == 0) { From e2653b1a39a5bf0afb5f73e38ed62eb1a9fe4de5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 16:21:08 +0000 Subject: [PATCH 12/18] placement/tests: print_obj_class mandatory hint (EC/RP/shard/all) Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 156 +++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 55 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index 0128ef57cfb..c71593ad42f 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -13,7 +13,8 @@ * * Interactive commands: * obj_class - Set current object class - * print_obj_class [name|id] - Print representative object classes, or name of one class + * print_obj_class - Print object classes matching hint + * hint: all | EC | EC(k+p) | RP | RP_ | shard * gen_layout id= [mode=] [ver=] * set_down rank=|node= - Set rank/node status to DOWN * set_downout rank=|node= - Set rank/node status to DOWNOUT @@ -81,8 +82,12 @@ print_help(void) { printf("Commands:\n" " obj_class Set current object class\n" - " print_obj_class [name|id] List representative classes (*G1/*G2/*G32/*GX)\n" - " or print the name of a single class\n" + " print_obj_class List classes matching hint\n" + " hint: all | EC | EC(k+p) | RP | RP_ | shard\n" + " EC: all EC classes; EC(8+2): 8+2 EC classes\n" + " RP: all RP classes; RP_3: 3-way RP classes\n" + " shard: S1/S2/.../SX classes\n" + " all: every registered class\n" " gen_layout id= [mode=] [ver=]\n" " Generate layout (OID lo=)\n" " mode: pre_rebuild|0, current|1, post_rebuild|2\n" @@ -246,70 +251,87 @@ cmd_obj_class(const char *arg) } /* - * Returns true if the string 'str' ends with the suffix 'suffix'. + * Returns true if 'oc_name' is a single-replica shard class (S1, S2, ... SX). + * These names have the form: 'S' followed by a decimal digit or 'X'. */ static bool -str_ends_with(const char *str, const char *suffix) +is_shard_oclass(const char *oc_name) { - size_t slen = strlen(str); - size_t plen = strlen(suffix); - - return slen >= plen && strcmp(str + slen - plen, suffix) == 0; -} - -/* - * Returns true if 'name' is a representative object class to display: - * G-family (*G1, *G2, *G32, *GX) and S-family single-replica equivalents. - */ -static bool -is_representative_oclass(const char *name) -{ - /* G-family: classes ending in G1, G2, G32, or GX */ - if (str_ends_with(name, "G1") || str_ends_with(name, "G2") || - str_ends_with(name, "G32") || str_ends_with(name, "GX")) - return true; - - /* S-family (single-replica, no 'G' in name): S1, S2, S32, SX */ - return strcmp(name, "S1") == 0 || strcmp(name, "S2") == 0 || - strcmp(name, "S32") == 0 || strcmp(name, "SX") == 0; + return oc_name[0] == 'S' && oc_name[1] != '\0' && + (oc_name[1] == 'X' || (oc_name[1] >= '0' && oc_name[1] <= '9')); } /* - * print_obj_class [name|id] + * print_obj_class * - * With no argument: list representative classes (*G1, *G2, *G32, *GX). - * With an argument: look up that class and print its name and ID. + * Print the object classes that match the given hint. The hint is + * mandatory. Supported hints: + * + * all - every registered class + * EC - all Erasure Coding classes (EC_*) + * EC(k+p) - EC classes with k data + p parity cells (e.g. EC(8+2)) + * RP - all Replication classes (RP_*) + * RP_ - replication classes with r replicas (e.g. RP_3) + * shard - single-replica shard classes (S1, S2, ... SX) */ static void cmd_print_obj_class(const char *arg) { - char name[64] = {0}; + char prefix[64]; + char *buf; + char *save; + char *tok; + long k, p, rval; + char *endp; + bool ec_specific = false; + bool rp_specific = false; daos_oclass_id_t cid; - char *endp; - char *buf; - char *save; - char *tok; - /* With an argument, look up a single class by name or ID */ - if (arg != NULL && *arg != '\0') { - cid = (daos_oclass_id_t)strtoul(arg, &endp, 0); - if (*endp != '\0') { - cid = daos_oclass_name2id(arg); - if (cid == OC_UNKNOWN) { - fprintf(stderr, "Unknown object class: %s\n", arg); - return; - } - } - if (daos_oclass_id2name(cid, name) < 0) { - fprintf(stderr, "Invalid object class id: %u\n", - (unsigned int)cid); - return; - } - printf("%s (id=%u)\n", name, (unsigned int)cid); + if (arg == NULL || *arg == '\0') { + fprintf(stderr, + "Usage: print_obj_class \n" + " hint: all | EC | EC(k+p) | RP | RP_ | shard\n" + " Examples:\n" + " print_obj_class EC all EC classes\n" + " print_obj_class EC(8+2) 8+2 EC classes (EC_8P2*)\n" + " print_obj_class RP all replication classes\n" + " print_obj_class RP_3 3-way replication classes\n" + " print_obj_class shard single-replica S1/S2/.../SX\n" + " print_obj_class all every registered class\n"); return; } - /* No argument: list representative classes */ + /* Pre-parse the hint and build a prefix string for specific filters */ + prefix[0] = '\0'; + if (strcasecmp(arg, "all") == 0 || strcasecmp(arg, "EC") == 0 || + strcasecmp(arg, "RP") == 0 || strcasecmp(arg, "shard") == 0) { + /* nothing to pre-parse */ + } else if (strncasecmp(arg, "EC(", 3) == 0) { + const char *hp = arg + 3; + + k = strtol(hp, &endp, 10); + if (endp == hp || *endp != '+') + goto bad_hint; + hp = endp + 1; + p = strtol(hp, &endp, 10); + if (endp == hp || *endp != ')' || endp[1] != '\0') + goto bad_hint; + if (k <= 0 || p <= 0) + goto bad_hint; + snprintf(prefix, sizeof(prefix), "EC_%ldP%ldG", k, p); + ec_specific = true; + } else if (strncasecmp(arg, "RP_", 3) == 0) { + const char *hp = arg + 3; + + rval = strtol(hp, &endp, 10); + if (endp == hp || *endp != '\0' || rval <= 0) + goto bad_hint; + snprintf(prefix, sizeof(prefix), "RP_%ldG", rval); + rp_specific = true; + } else { + goto bad_hint; + } + buf = malloc(PLD_OCLASS_LIST_BUF_SIZE); if (buf == NULL) { fprintf(stderr, "Out of memory\n"); @@ -322,16 +344,40 @@ cmd_print_obj_class(const char *arg) return; } - printf("Representative object classes (*G1, *G2, *G32, *GX):\n"); + printf("Object classes matching '%s':\n", arg); for (tok = strtok_r(buf, ", ", &save); tok != NULL; tok = strtok_r(NULL, ", ", &save)) { - if (is_representative_oclass(tok)) { + bool match; + + if (strcasecmp(arg, "all") == 0) { + match = true; + } else if (strcasecmp(arg, "EC") == 0) { + match = strncmp(tok, "EC_", 3) == 0; + } else if (ec_specific) { + match = strncmp(tok, prefix, strlen(prefix)) == 0; + } else if (strcasecmp(arg, "RP") == 0) { + match = strncmp(tok, "RP_", 3) == 0; + } else if (rp_specific) { + match = strncmp(tok, prefix, strlen(prefix)) == 0; + } else { + /* shard */ + match = is_shard_oclass(tok); + } + + if (match) { cid = daos_oclass_name2id(tok); - printf(" %-20s (id=%u)\n", tok, (unsigned int)cid); + printf(" %-24s (id=%u)\n", tok, (unsigned int)cid); } } free(buf); + return; + +bad_hint: + fprintf(stderr, + "Unknown or invalid hint '%s'\n" + " Valid hints: all | EC | EC(k+p) | RP | RP_ | shard\n" + " Examples: EC, EC(8+2), RP, RP_3, shard, all\n", arg); } static void @@ -356,7 +402,7 @@ cmd_gen_layout(const char *arg) if (g_obj_class == OC_UNKNOWN) { fprintf(stderr, "No object class set; run 'obj_class ' first\n" - " (try 'print_obj_class' to see available classes)\n"); + " (try 'print_obj_class EC' or 'print_obj_class RP' to see available classes)\n"); return; } From bee81f055f0502a5ac0556bd0d2111a60f391d29 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 16:40:24 +0000 Subject: [PATCH 13/18] placement/tests: gen_layout type= and grp= options to override obj_class Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 85 +++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index c71593ad42f..e36a9d35d9d 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -16,6 +16,7 @@ * print_obj_class - Print object classes matching hint * hint: all | EC | EC(k+p) | RP | RP_ | shard * gen_layout id= [mode=] [ver=] + * [type= grp=] * set_down rank=|node= - Set rank/node status to DOWN * set_downout rank=|node= - Set rank/node status to DOWNOUT * set_up rank=|node= - Set rank/node status to UP @@ -89,10 +90,14 @@ print_help(void) " shard: S1/S2/.../SX classes\n" " all: every registered class\n" " gen_layout id= [mode=] [ver=]\n" + " [type= grp=]\n" " Generate layout (OID lo=)\n" " mode: pre_rebuild|0, current|1, post_rebuild|2\n" " (default: pre_rebuild)\n" " ver: pool map version (default: current)\n" + " type+grp: override obj_class for this layout\n" + " e.g. type=EC_8P2 grp=2 -> EC_8P2G2\n" + " type=RP_3 grp=X -> RP_3GX\n" " set_down rank=|node= Set rank/node to DOWN\n" " set_downout rank=|node= Set rank/node to DOWNOUT\n" " set_up rank=|node= Set rank/node to UP\n" @@ -394,15 +399,16 @@ cmd_gen_layout(const char *arg) enum layout_gen_mode mode = PRE_REBUILD; uint32_t ver = 0; /* 0 means "use current map version" */ int grp, sz, index, rc; + char type_str[64] = {0}; /* type= value, e.g. "EC_8P2" */ + char grp_str[16] = {0}; /* grp= value, e.g. "2" or "X" */ + daos_oclass_id_t use_class; - if (arg == NULL || *arg == '\0') { - fprintf(stderr, "Usage: gen_layout id= [mode=] [ver=]\n"); - return; - } +#define GEN_LAYOUT_USAGE \ + "Usage: gen_layout id= [mode=]\n" \ + " [ver=] [type= grp=]\n" - if (g_obj_class == OC_UNKNOWN) { - fprintf(stderr, "No object class set; run 'obj_class ' first\n" - " (try 'print_obj_class EC' or 'print_obj_class RP' to see available classes)\n"); + if (arg == NULL || *arg == '\0') { + fprintf(stderr, GEN_LAYOUT_USAGE); return; } @@ -461,22 +467,76 @@ cmd_gen_layout(const char *arg) return; } ver = (uint32_t)v; + } else if (strncmp(tok, "type=", 5) == 0) { + const char *val = tok + 5; + + if (strlen(val) >= sizeof(type_str)) { + fprintf(stderr, "type= value too long: %s\n", val); + return; + } + snprintf(type_str, sizeof(type_str), "%s", val); + } else if (strncmp(tok, "grp=", 4) == 0) { + const char *val = tok + 4; + char *endp; + + /* Accept a positive integer or "X" / "x" */ + if (strcasecmp(val, "X") == 0) { + snprintf(grp_str, sizeof(grp_str), "X"); + } else { + long v = strtol(val, &endp, 10); + + if (*endp != '\0' || v <= 0) { + fprintf(stderr, + "Invalid grp value '%s'; expected a positive number or X\n", + val); + return; + } + snprintf(grp_str, sizeof(grp_str), "%ld", v); + } } else { fprintf(stderr, "Unknown gen_layout option: '%s'\n", tok); - fprintf(stderr, "Usage: gen_layout id= [mode=] [ver=]\n"); + fprintf(stderr, GEN_LAYOUT_USAGE); return; } } if (!id_found) { - fprintf(stderr, "Usage: gen_layout id= [mode=] [ver=]\n"); + fprintf(stderr, GEN_LAYOUT_USAGE); + return; + } + + /* Resolve object class: type=+grp= override g_obj_class */ + if (type_str[0] != '\0' && grp_str[0] != '\0') { + char class_name[sizeof(type_str) + sizeof(grp_str) + 2]; + + snprintf(class_name, sizeof(class_name), "%sG%s", type_str, grp_str); + use_class = daos_oclass_name2id(class_name); + if (use_class == OC_UNKNOWN) { + fprintf(stderr, + "Unknown object class '%s' (from type=%s grp=%s)\n" + " Use 'print_obj_class EC' or 'print_obj_class RP' to list valid classes\n", + class_name, type_str, grp_str); + return; + } + } else if (type_str[0] != '\0' || grp_str[0] != '\0') { + fprintf(stderr, + "Both type= and grp= must be specified together\n" + " e.g. type=EC_8P2 grp=2 or type=RP_3 grp=X\n"); return; + } else { + if (g_obj_class == OC_UNKNOWN) { + fprintf(stderr, + "No object class set; run 'obj_class ' first\n" + " (or pass type= and grp= to override, e.g. type=EC_8P2 grp=2)\n"); + return; + } + use_class = g_obj_class; } oid.lo = lo_val; oid.hi = 0; - rc = daos_obj_set_oid_by_class(&oid, 0, g_obj_class, 0); + rc = daos_obj_set_oid_by_class(&oid, 0, use_class, 0); if (rc != 0) { fprintf(stderr, "daos_obj_set_oid_by_class failed: %d\n", rc); return; @@ -493,9 +553,9 @@ cmd_gen_layout(const char *arg) return; } - daos_oclass_id2name(g_obj_class, name); + daos_oclass_id2name(use_class, name); printf("Layout for OID lo=%" PRIu64 " class=%s (id=%u) mode=%s ver=%u:\n", - lo_val, name, (unsigned int)g_obj_class, + lo_val, name, (unsigned int)use_class, layout_gen_mode_names[mode], md.omd_ver); printf(" groups=%u group_size=%u total_shards=%u\n", layout->ol_grp_nr, layout->ol_grp_size, layout->ol_nr); @@ -519,6 +579,7 @@ cmd_gen_layout(const char *arg) } pl_obj_layout_free(layout); +#undef GEN_LAYOUT_USAGE } /* From 0a94a8c96b289bd3daee7e897a42d5772ddffb5b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 03:38:27 +0000 Subject: [PATCH 14/18] placement/tests: show co_flags (DOWN2UP etc.) in query output Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 66 ++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index e36a9d35d9d..9380a1ac71e 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -638,6 +638,42 @@ cmd_set_state(const char *subcmd, const char *arg, int opc, int opc_second) pool_map_get_version(g_po_map)); } +/* + * Build a human-readable string for pool_component_flags. + * Returns buf (always NUL-terminated, empty string when flags==0). + */ +static const char * +fmt_comp_flags(uint32_t flags, char *buf, size_t bufsz) +{ + const uint32_t known = PO_COMPF_DOWN2UP | PO_COMPF_DOWN2OUT | + PO_COMPF_CHK_DONE; + int off = 0; + int n; + + buf[0] = '\0'; + if (flags == 0) + return buf; + +#define APPEND(fmt, ...) \ + do { \ + n = snprintf(buf + off, bufsz - off, fmt, ##__VA_ARGS__); \ + if (n > 0) \ + off = (off + n < (int)bufsz) ? off + n : (int)bufsz - 1; \ + } while (0) + + if (flags & PO_COMPF_DOWN2UP) + APPEND("%sDOWN2UP", off ? "|" : ""); + if (flags & PO_COMPF_DOWN2OUT) + APPEND("%sDOWN2OUT", off ? "|" : ""); + if (flags & PO_COMPF_CHK_DONE) + APPEND("%sCHK_DONE", off ? "|" : ""); + if (flags & ~known) + APPEND("%s0x%x", off ? "|" : "", flags & ~known); + +#undef APPEND + return buf; +} + /* * Print the status of the specified rank domain and all of its targets. */ @@ -645,20 +681,28 @@ static void print_rank_status(const struct pool_domain *rank_dom) { const struct pool_component *rc = &rank_dom->do_comp; + char fbuf[64]; int j; - printf(" rank %u id=%u status=%-8s ver=%u targets=%u\n", + printf(" rank %u id=%u status=%-8s ver=%u targets=%u", rc->co_rank, rc->co_id, pool_comp_state2str(rc->co_status), rc->co_ver, rank_dom->do_target_nr); + if (rc->co_flags != 0) + printf(" flags=%s", fmt_comp_flags(rc->co_flags, fbuf, sizeof(fbuf))); + printf("\n"); for (j = 0; j < (int)rank_dom->do_target_nr; j++) { const struct pool_component *tc = &rank_dom->do_targets[j].ta_comp; - printf(" target[%2d] id=%4u idx=%2u status=%-8s ver=%u fseq=%u\n", + printf(" target[%2d] id=%4u idx=%2u status=%-8s ver=%u fseq=%u", j, tc->co_id, tc->co_index, pool_comp_state2str(tc->co_status), tc->co_ver, tc->co_fseq); + if (tc->co_flags != 0) + printf(" flags=%s", + fmt_comp_flags(tc->co_flags, fbuf, sizeof(fbuf))); + printf("\n"); } } @@ -710,10 +754,20 @@ cmd_query(const char *arg) } printf("Pool map version: %u\n", pool_map_get_version(g_po_map)); - printf("node %u id=%u status=%-8s ver=%u ranks=%u\n", - (unsigned int)id, node_dom->do_comp.co_id, - pool_comp_state2str(node_dom->do_comp.co_status), - node_dom->do_comp.co_ver, node_dom->do_child_nr); + { + const struct pool_component *nc = &node_dom->do_comp; + char fbuf[64]; + + printf("node %u id=%u status=%-8s ver=%u ranks=%u", + (unsigned int)id, nc->co_id, + pool_comp_state2str(nc->co_status), + nc->co_ver, node_dom->do_child_nr); + if (nc->co_flags != 0) + printf(" flags=%s", + fmt_comp_flags(nc->co_flags, fbuf, + sizeof(fbuf))); + printf("\n"); + } for (i = 0; i < (int)node_dom->do_child_nr; i++) print_rank_status(&node_dom->do_children[i]); From b4a6e7bb6ab8639ce68af8094a631f9fced81857 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 06:36:00 +0000 Subject: [PATCH 15/18] placement/tests: fix format-truncation warning in grp_str snprintf Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index 9380a1ac71e..d2cd8752064 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -491,7 +491,7 @@ cmd_gen_layout(const char *arg) val); return; } - snprintf(grp_str, sizeof(grp_str), "%ld", v); + snprintf(grp_str, sizeof(grp_str), "%u", (unsigned int)v); } } else { fprintf(stderr, "Unknown gen_layout option: '%s'\n", tok); From 21e664b056006995117fadea66550ef05528030c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:03:51 +0000 Subject: [PATCH 16/18] placement/tests: add diff_layout command showing rebuild shards with target status Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 208 ++++++++++++++++++++++++++++++++- 1 file changed, 207 insertions(+), 1 deletion(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index d2cd8752064..04b2bc9e390 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -17,6 +17,7 @@ * hint: all | EC | EC(k+p) | RP | RP_ | shard * gen_layout id= [mode=] [ver=] * [type= grp=] + * diff_layout id= [ver=] [type= grp=] * set_down rank=|node= - Set rank/node status to DOWN * set_downout rank=|node= - Set rank/node status to DOWNOUT * set_up rank=|node= - Set rank/node status to UP @@ -32,6 +33,7 @@ * pl_debug> gen_layout id=42 mode=current ver=3 * pl_debug> set_down rank=0 * pl_debug> gen_layout id=42 mode=post_rebuild + * pl_debug> diff_layout id=42 * pl_debug> quit */ @@ -94,10 +96,16 @@ print_help(void) " Generate layout (OID lo=)\n" " mode: pre_rebuild|0, current|1, post_rebuild|2\n" " (default: pre_rebuild)\n" - " ver: pool map version (default: current)\n" + " ver: pool map version (default: latest)\n" " type+grp: override obj_class for this layout\n" " e.g. type=EC_8P2 grp=2 -> EC_8P2G2\n" " type=RP_3 grp=X -> RP_3GX\n" + " diff_layout id= [ver=]\n" + " [type= grp=]\n" + " Show shards that need rebuild (pl_obj_find_rebuild)\n" + " ver: pool map version (default: latest)\n" + " Outputs shard ID, target ID, rank, status,\n" + " and DOWN2UP flag for each rebuild shard\n" " set_down rank=|node= Set rank/node to DOWN\n" " set_downout rank=|node= Set rank/node to DOWNOUT\n" " set_up rank=|node= Set rank/node to UP\n" @@ -582,6 +590,202 @@ cmd_gen_layout(const char *arg) #undef GEN_LAYOUT_USAGE } +/* + * diff_layout id= [ver=] [type=<...> grp=<...>] + * + * Calls pl_obj_find_rebuild() for the given object and pool map version + * and prints each rebuild shard together with the target's status and flags. + */ +static void +cmd_diff_layout(const char *arg) +{ + daos_obj_id_t oid = {0}; + struct daos_obj_md md = {0}; + uint64_t lo_val; + char arg_copy[1024]; + char *tok, *save; + bool id_found = false; + uint32_t ver = 0; /* 0 → use latest map version */ + char type_str[64] = {0}; + char grp_str[16] = {0}; + daos_oclass_id_t use_class; + uint32_t *tgt_ids = NULL; + uint32_t *shard_ids = NULL; + int ntgt, i, rc; + unsigned int array_size; + char name[64] = {0}; + +#define DIFF_LAYOUT_USAGE \ + "Usage: diff_layout id= [ver=]\n" \ + " [type= grp=]\n" + + if (arg == NULL || *arg == '\0') { + fprintf(stderr, DIFF_LAYOUT_USAGE); + return; + } + + if (g_pl_map == NULL) { + fprintf(stderr, "Placement map unavailable\n"); + return; + } + + snprintf(arg_copy, sizeof(arg_copy), "%s", arg); + for (tok = strtok_r(arg_copy, " \t", &save); tok != NULL; + tok = strtok_r(NULL, " \t", &save)) { + if (strncmp(tok, "id=", 3) == 0) { + char *endp; + + lo_val = strtoull(tok + 3, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "Invalid id value: %s\n", + tok + 3); + return; + } + id_found = true; + } else if (strncmp(tok, "ver=", 4) == 0) { + char *endp; + unsigned long v; + + v = strtoul(tok + 4, &endp, 0); + if (*endp != '\0' || v > UINT32_MAX) { + fprintf(stderr, "Invalid ver value: %s\n", + tok + 4); + return; + } + ver = (uint32_t)v; + } else if (strncmp(tok, "type=", 5) == 0) { + const char *val = tok + 5; + + if (strlen(val) >= sizeof(type_str)) { + fprintf(stderr, "type= value too long: %s\n", + val); + return; + } + snprintf(type_str, sizeof(type_str), "%s", val); + } else if (strncmp(tok, "grp=", 4) == 0) { + const char *val = tok + 4; + char *endp; + + if (strcasecmp(val, "X") == 0) { + snprintf(grp_str, sizeof(grp_str), "X"); + } else { + long v = strtol(val, &endp, 10); + + if (*endp != '\0' || v <= 0) { + fprintf(stderr, + "Invalid grp value '%s'; expected a positive number or X\n", + val); + return; + } + snprintf(grp_str, sizeof(grp_str), "%u", + (unsigned int)v); + } + } else { + fprintf(stderr, "Unknown diff_layout option: '%s'\n", + tok); + fprintf(stderr, DIFF_LAYOUT_USAGE); + return; + } + } + + if (!id_found) { + fprintf(stderr, DIFF_LAYOUT_USAGE); + return; + } + + /* Resolve object class */ + if (type_str[0] != '\0' && grp_str[0] != '\0') { + char class_name[sizeof(type_str) + sizeof(grp_str) + 2]; /* 'G' + NUL */ + + snprintf(class_name, sizeof(class_name), "%sG%s", type_str, + grp_str); + use_class = daos_oclass_name2id(class_name); + if (use_class == OC_UNKNOWN) { + fprintf(stderr, + "Unknown object class '%s' (from type=%s grp=%s)\n", + class_name, type_str, grp_str); + return; + } + } else if (type_str[0] != '\0' || grp_str[0] != '\0') { + fprintf(stderr, + "Both type= and grp= must be specified together\n"); + return; + } else { + if (g_obj_class == OC_UNKNOWN) { + fprintf(stderr, + "No object class set; run 'obj_class ' first\n"); + return; + } + use_class = g_obj_class; + } + + oid.lo = lo_val; + oid.hi = 0; + rc = daos_obj_set_oid_by_class(&oid, 0, use_class, 0); + if (rc != 0) { + fprintf(stderr, "daos_obj_set_oid_by_class failed: %d\n", rc); + return; + } + + md.omd_id = oid; + md.omd_ver = (ver != 0) ? ver : pool_map_get_version(g_pl_map->pl_poolmap); + md.omd_pda = 0; + + /* Allocate output arrays sized to the total number of targets */ + array_size = pool_map_target_nr(g_po_map); + D_ALLOC_ARRAY(tgt_ids, array_size); + D_ALLOC_ARRAY(shard_ids, array_size); + if (tgt_ids == NULL || shard_ids == NULL) { + fprintf(stderr, "Out of memory\n"); + D_FREE(tgt_ids); + D_FREE(shard_ids); + return; + } + + ntgt = pl_obj_find_rebuild(g_pl_map, PLD_LAYOUT_VERSION, &md, NULL, + md.omd_ver, tgt_ids, shard_ids, array_size); + if (ntgt < 0) { + fprintf(stderr, "pl_obj_find_rebuild failed: %d\n", ntgt); + D_FREE(tgt_ids); + D_FREE(shard_ids); + return; + } + + daos_oclass_id2name(use_class, name); + printf("Rebuild shards for OID lo=%" PRIu64 " class=%s ver=%u: %d shard(s)\n", + lo_val, name, md.omd_ver, ntgt); + + for (i = 0; i < ntgt; i++) { + struct pool_target *tgt = NULL; + char fbuf[64]; + + rc = pool_map_find_target(g_po_map, tgt_ids[i], &tgt); + if (rc != 1 || tgt == NULL) { + printf(" shard %3u: target_id=%4u (target not found)\n", + shard_ids[i], tgt_ids[i]); + continue; + } + + printf(" shard %3u: target_id=%4u rank=%4u idx=%2u" + " status=%-8s", + shard_ids[i], + tgt->ta_comp.co_id, + tgt->ta_comp.co_rank, + tgt->ta_comp.co_index, + pool_comp_state2str(tgt->ta_comp.co_status)); + + if (tgt->ta_comp.co_flags != 0) + printf(" flags=%s", + fmt_comp_flags(tgt->ta_comp.co_flags, fbuf, + sizeof(fbuf))); + printf("\n"); + } + + D_FREE(tgt_ids); + D_FREE(shard_ids); +#undef DIFF_LAYOUT_USAGE +} + /* * Parse "rank=" or "node=", build the target list, apply * opc (a map_update_opc value) to transition targets to the desired state, @@ -832,6 +1036,8 @@ run_repl(void) cmd_print_obj_class(arg); } else if (strcmp(cmd, "gen_layout") == 0) { cmd_gen_layout(arg); + } else if (strcmp(cmd, "diff_layout") == 0) { + cmd_diff_layout(arg); } else if (strcmp(cmd, "set_down") == 0) { /* UP/UPIN → DOWN */ cmd_set_state("set_down", arg, MAP_EXCLUDE, PLD_NO_OPC); From 351d3d4e84e4a73aa7c03856b8dc8da4e3cc83dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 13:23:51 +0000 Subject: [PATCH 17/18] placement/tests: add gen_oid command; simplify gen_layout/diff_layout to use global OID Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 400 ++++++++++++++++----------------- 1 file changed, 198 insertions(+), 202 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index 04b2bc9e390..565ce197580 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -15,9 +15,11 @@ * obj_class - Set current object class * print_obj_class - Print object classes matching hint * hint: all | EC | EC(k+p) | RP | RP_ | shard - * gen_layout id= [mode=] [ver=] - * [type= grp=] - * diff_layout id= [ver=] [type= grp=] + * gen_oid id= [type= grp=] + * - Set current OID (required before gen_layout/diff_layout) + * gen_layout [mode=] [ver=] + * - Generate layout for current OID + * diff_layout [ver=] - Show rebuild shards for current OID * set_down rank=|node= - Set rank/node status to DOWN * set_downout rank=|node= - Set rank/node status to DOWNOUT * set_up rank=|node= - Set rank/node status to UP @@ -29,11 +31,12 @@ * Smoke-test example: * $ pl_debug -n 4 -r 2 -t 8 * pl_debug> obj_class OC_EC_4P1GX - * pl_debug> gen_layout id=42 - * pl_debug> gen_layout id=42 mode=current ver=3 + * pl_debug> gen_oid id=42 + * pl_debug> gen_layout + * pl_debug> gen_layout mode=current ver=3 * pl_debug> set_down rank=0 - * pl_debug> gen_layout id=42 mode=post_rebuild - * pl_debug> diff_layout id=42 + * pl_debug> gen_layout mode=post_rebuild + * pl_debug> diff_layout * pl_debug> quit */ @@ -76,6 +79,17 @@ static struct pool_map *g_po_map; static struct pl_map *g_pl_map; static daos_oclass_id_t g_obj_class = OC_UNKNOWN; +/* Set by gen_oid; required before gen_layout / diff_layout */ +static daos_obj_id_t g_oid = {0}; +static bool g_oid_set = false; +static daos_oclass_id_t g_oid_class = OC_UNKNOWN; + +/* Last layout from gen_layout; overwritten on each gen_layout call */ +static struct pl_obj_layout *g_layout = NULL; + +/* Forward declaration – defined after cmd_set_state */ +static const char *fmt_comp_flags(uint32_t flags, char *buf, size_t bufsz); + /* ------------------------------------------------------------------ */ /* Help */ /* ------------------------------------------------------------------ */ @@ -91,18 +105,18 @@ print_help(void) " RP: all RP classes; RP_3: 3-way RP classes\n" " shard: S1/S2/.../SX classes\n" " all: every registered class\n" - " gen_layout id= [mode=] [ver=]\n" - " [type= grp=]\n" - " Generate layout (OID lo=)\n" + " gen_oid id= [type= grp=]\n" + " Set current OID (required before gen_layout/diff_layout)\n" + " type+grp: override obj_class for this OID\n" + " e.g. type=EC_8P2 grp=2 -> EC_8P2G2\n" + " type=RP_3 grp=X -> RP_3GX\n" + " gen_layout [mode=] [ver=]\n" + " Generate layout for current OID\n" " mode: pre_rebuild|0, current|1, post_rebuild|2\n" " (default: pre_rebuild)\n" " ver: pool map version (default: latest)\n" - " type+grp: override obj_class for this layout\n" - " e.g. type=EC_8P2 grp=2 -> EC_8P2G2\n" - " type=RP_3 grp=X -> RP_3GX\n" - " diff_layout id= [ver=]\n" - " [type= grp=]\n" - " Show shards that need rebuild (pl_obj_find_rebuild)\n" + " Result is stored; overwritten on each call\n" + " diff_layout [ver=] Show shards that need rebuild (pl_obj_find_rebuild)\n" " ver: pool map version (default: latest)\n" " Outputs shard ID, target ID, rank, status,\n" " and DOWN2UP flag for each rebuild shard\n" @@ -393,39 +407,35 @@ cmd_print_obj_class(const char *arg) " Examples: EC, EC(8+2), RP, RP_3, shard, all\n", arg); } +/* + * gen_oid id= [type=<...> grp=<...>] + * + * Sets the current OID (g_oid / g_oid_class) that gen_layout and diff_layout + * will operate on. The object class is taken from type=/grp= if provided, or + * from g_obj_class (set by the obj_class command) otherwise. + */ static void -cmd_gen_layout(const char *arg) +cmd_gen_oid(const char *arg) { - daos_obj_id_t oid = {0}; - struct pl_obj_layout *layout = NULL; - struct daos_obj_md md = {0}; - uint64_t lo_val; - char name[64] = {0}; - char arg_copy[1024]; - char *tok, *save; - bool id_found = false; - enum layout_gen_mode mode = PRE_REBUILD; - uint32_t ver = 0; /* 0 means "use current map version" */ - int grp, sz, index, rc; - char type_str[64] = {0}; /* type= value, e.g. "EC_8P2" */ - char grp_str[16] = {0}; /* grp= value, e.g. "2" or "X" */ - daos_oclass_id_t use_class; + daos_obj_id_t oid = {0}; + uint64_t lo_val; + char arg_copy[1024]; + char *tok, *save; + bool id_found = false; + char type_str[64] = {0}; + char grp_str[16] = {0}; + daos_oclass_id_t use_class; + char name[64] = {0}; + int rc; -#define GEN_LAYOUT_USAGE \ - "Usage: gen_layout id= [mode=]\n" \ - " [ver=] [type= grp=]\n" +#define GEN_OID_USAGE \ + "Usage: gen_oid id= [type= grp=]\n" if (arg == NULL || *arg == '\0') { - fprintf(stderr, GEN_LAYOUT_USAGE); + fprintf(stderr, GEN_OID_USAGE); return; } - if (g_pl_map == NULL) { - fprintf(stderr, "Placement map unavailable\n"); - return; - } - - /* Tokenise a copy of the argument string on whitespace */ snprintf(arg_copy, sizeof(arg_copy), "%s", arg); for (tok = strtok_r(arg_copy, " \t", &save); tok != NULL; tok = strtok_r(NULL, " \t", &save)) { @@ -434,52 +444,17 @@ cmd_gen_layout(const char *arg) lo_val = strtoull(tok + 3, &endp, 0); if (*endp != '\0') { - fprintf(stderr, "Invalid id value: %s\n", tok + 3); + fprintf(stderr, "Invalid id value: %s\n", + tok + 3); return; } id_found = true; - } else if (strncmp(tok, "mode=", 5) == 0) { - const char *val = tok + 5; - char *endp; - long num; - - /* Accept numeric values */ - num = strtol(val, &endp, 0); - if (*endp == '\0') { - if (num < PRE_REBUILD || num > POST_REBUILD) { - fprintf(stderr, - "Invalid mode value %ld; valid: 0 (pre_rebuild), 1 (current), 2 (post_rebuild)\n", - num); - return; - } - mode = (enum layout_gen_mode)num; - } else if (strcasecmp(val, "pre_rebuild") == 0) { - mode = PRE_REBUILD; - } else if (strcasecmp(val, "current") == 0) { - mode = CURRENT; - } else if (strcasecmp(val, "post_rebuild") == 0) { - mode = POST_REBUILD; - } else { - fprintf(stderr, - "Unknown mode '%s'; valid: pre_rebuild, current, post_rebuild\n", - val); - return; - } - } else if (strncmp(tok, "ver=", 4) == 0) { - char *endp; - unsigned long v; - - v = strtoul(tok + 4, &endp, 0); - if (*endp != '\0' || v > UINT32_MAX) { - fprintf(stderr, "Invalid ver value: %s\n", tok + 4); - return; - } - ver = (uint32_t)v; } else if (strncmp(tok, "type=", 5) == 0) { const char *val = tok + 5; if (strlen(val) >= sizeof(type_str)) { - fprintf(stderr, "type= value too long: %s\n", val); + fprintf(stderr, "type= value too long: %s\n", + val); return; } snprintf(type_str, sizeof(type_str), "%s", val); @@ -487,7 +462,6 @@ cmd_gen_layout(const char *arg) const char *val = tok + 4; char *endp; - /* Accept a positive integer or "X" / "x" */ if (strcasecmp(val, "X") == 0) { snprintf(grp_str, sizeof(grp_str), "X"); } else { @@ -499,25 +473,27 @@ cmd_gen_layout(const char *arg) val); return; } - snprintf(grp_str, sizeof(grp_str), "%u", (unsigned int)v); + snprintf(grp_str, sizeof(grp_str), "%u", + (unsigned int)v); } } else { - fprintf(stderr, "Unknown gen_layout option: '%s'\n", tok); - fprintf(stderr, GEN_LAYOUT_USAGE); + fprintf(stderr, "Unknown gen_oid option: '%s'\n", tok); + fprintf(stderr, GEN_OID_USAGE); return; } } if (!id_found) { - fprintf(stderr, GEN_LAYOUT_USAGE); + fprintf(stderr, GEN_OID_USAGE); return; } /* Resolve object class: type=+grp= override g_obj_class */ if (type_str[0] != '\0' && grp_str[0] != '\0') { - char class_name[sizeof(type_str) + sizeof(grp_str) + 2]; + char class_name[sizeof(type_str) + sizeof(grp_str) + 2]; /* 'G' + NUL */ - snprintf(class_name, sizeof(class_name), "%sG%s", type_str, grp_str); + snprintf(class_name, sizeof(class_name), "%sG%s", type_str, + grp_str); use_class = daos_oclass_name2id(class_name); if (use_class == OC_UNKNOWN) { fprintf(stderr, @@ -543,14 +519,107 @@ cmd_gen_layout(const char *arg) oid.lo = lo_val; oid.hi = 0; - rc = daos_obj_set_oid_by_class(&oid, 0, use_class, 0); if (rc != 0) { fprintf(stderr, "daos_obj_set_oid_by_class failed: %d\n", rc); return; } - md.omd_id = oid; + g_oid = oid; + g_oid_class = use_class; + g_oid_set = true; + + daos_oclass_id2name(use_class, name); + printf("OID set: lo=%" PRIu64 " hi=0x%" PRIx64 " class=%s (id=%u)\n", + lo_val, oid.hi, name, (unsigned int)use_class); +#undef GEN_OID_USAGE +} + +/* + * gen_layout [mode=] [ver=] + * + * Generates the placement layout for the current OID (set by gen_oid). + * The result is stored in g_layout, overwriting any previous layout. + */ +static void +cmd_gen_layout(const char *arg) +{ + struct pl_obj_layout *layout = NULL; + struct daos_obj_md md = {0}; + char name[64] = {0}; + char arg_copy[1024]; + char *tok, *save; + enum layout_gen_mode mode = PRE_REBUILD; + uint32_t ver = 0; /* 0 → use latest map version */ + int grp, sz, index, rc; + +#define GEN_LAYOUT_USAGE \ + "Usage: gen_layout [mode=] [ver=]\n" + + if (g_pl_map == NULL) { + fprintf(stderr, "Placement map unavailable\n"); + return; + } + + if (!g_oid_set) { + fprintf(stderr, + "No OID set; run 'gen_oid id=' first\n"); + return; + } + + if (arg != NULL && *arg != '\0') { + snprintf(arg_copy, sizeof(arg_copy), "%s", arg); + for (tok = strtok_r(arg_copy, " \t", &save); tok != NULL; + tok = strtok_r(NULL, " \t", &save)) { + if (strncmp(tok, "mode=", 5) == 0) { + const char *val = tok + 5; + char *endp; + long num; + + num = strtol(val, &endp, 0); + if (*endp == '\0') { + if (num < PRE_REBUILD || num > POST_REBUILD) { + fprintf(stderr, + "Invalid mode value %ld; valid: 0 (pre_rebuild), 1 (current), 2 (post_rebuild)\n", + num); + return; + } + mode = (enum layout_gen_mode)num; + } else if (strcasecmp(val, "pre_rebuild") == 0) { + mode = PRE_REBUILD; + } else if (strcasecmp(val, "current") == 0) { + mode = CURRENT; + } else if (strcasecmp(val, "post_rebuild") == 0) { + mode = POST_REBUILD; + } else { + fprintf(stderr, + "Unknown mode '%s'; valid: pre_rebuild, current, post_rebuild\n", + val); + return; + } + } else if (strncmp(tok, "ver=", 4) == 0) { + char *endp; + unsigned long v; + + v = strtoul(tok + 4, &endp, 0); + if (*endp != '\0' || v > UINT32_MAX) { + fprintf(stderr, + "Invalid ver value: %s\n", + tok + 4); + return; + } + ver = (uint32_t)v; + } else { + fprintf(stderr, + "Unknown gen_layout option: '%s'\n", + tok); + fprintf(stderr, GEN_LAYOUT_USAGE); + return; + } + } + } + + md.omd_id = g_oid; md.omd_ver = (ver != 0) ? ver : pool_map_get_version(g_pl_map->pl_poolmap); md.omd_pda = 0; @@ -561,9 +630,14 @@ cmd_gen_layout(const char *arg) return; } - daos_oclass_id2name(use_class, name); + /* Overwrite previous layout */ + if (g_layout != NULL) + pl_obj_layout_free(g_layout); + g_layout = layout; + + daos_oclass_id2name(g_oid_class, name); printf("Layout for OID lo=%" PRIu64 " class=%s (id=%u) mode=%s ver=%u:\n", - lo_val, name, (unsigned int)use_class, + g_oid.lo, name, (unsigned int)g_oid_class, layout_gen_mode_names[mode], md.omd_ver); printf(" groups=%u group_size=%u total_shards=%u\n", layout->ol_grp_nr, layout->ol_grp_size, layout->ol_nr); @@ -585,30 +659,22 @@ cmd_gen_layout(const char *arg) shard.po_rebuilding ? " [rebuilding]" : ""); } } - - pl_obj_layout_free(layout); #undef GEN_LAYOUT_USAGE } /* - * diff_layout id= [ver=] [type=<...> grp=<...>] + * diff_layout [ver=] * - * Calls pl_obj_find_rebuild() for the given object and pool map version - * and prints each rebuild shard together with the target's status and flags. + * Calls pl_obj_find_rebuild() for the current OID (set by gen_oid) and + * prints each rebuild shard together with the target's status and flags. */ static void cmd_diff_layout(const char *arg) { - daos_obj_id_t oid = {0}; struct daos_obj_md md = {0}; - uint64_t lo_val; char arg_copy[1024]; char *tok, *save; - bool id_found = false; - uint32_t ver = 0; /* 0 → use latest map version */ - char type_str[64] = {0}; - char grp_str[16] = {0}; - daos_oclass_id_t use_class; + uint32_t ver = 0; /* 0 → use latest map version */ uint32_t *tgt_ids = NULL; uint32_t *shard_ids = NULL; int ntgt, i, rc; @@ -616,118 +682,46 @@ cmd_diff_layout(const char *arg) char name[64] = {0}; #define DIFF_LAYOUT_USAGE \ - "Usage: diff_layout id= [ver=]\n" \ - " [type= grp=]\n" - - if (arg == NULL || *arg == '\0') { - fprintf(stderr, DIFF_LAYOUT_USAGE); - return; - } + "Usage: diff_layout [ver=]\n" if (g_pl_map == NULL) { fprintf(stderr, "Placement map unavailable\n"); return; } - snprintf(arg_copy, sizeof(arg_copy), "%s", arg); - for (tok = strtok_r(arg_copy, " \t", &save); tok != NULL; - tok = strtok_r(NULL, " \t", &save)) { - if (strncmp(tok, "id=", 3) == 0) { - char *endp; - - lo_val = strtoull(tok + 3, &endp, 0); - if (*endp != '\0') { - fprintf(stderr, "Invalid id value: %s\n", - tok + 3); - return; - } - id_found = true; - } else if (strncmp(tok, "ver=", 4) == 0) { - char *endp; - unsigned long v; - - v = strtoul(tok + 4, &endp, 0); - if (*endp != '\0' || v > UINT32_MAX) { - fprintf(stderr, "Invalid ver value: %s\n", - tok + 4); - return; - } - ver = (uint32_t)v; - } else if (strncmp(tok, "type=", 5) == 0) { - const char *val = tok + 5; - - if (strlen(val) >= sizeof(type_str)) { - fprintf(stderr, "type= value too long: %s\n", - val); - return; - } - snprintf(type_str, sizeof(type_str), "%s", val); - } else if (strncmp(tok, "grp=", 4) == 0) { - const char *val = tok + 4; - char *endp; + if (!g_oid_set) { + fprintf(stderr, + "No OID set; run 'gen_oid id=' first\n"); + return; + } - if (strcasecmp(val, "X") == 0) { - snprintf(grp_str, sizeof(grp_str), "X"); - } else { - long v = strtol(val, &endp, 10); + if (arg != NULL && *arg != '\0') { + snprintf(arg_copy, sizeof(arg_copy), "%s", arg); + for (tok = strtok_r(arg_copy, " \t", &save); tok != NULL; + tok = strtok_r(NULL, " \t", &save)) { + if (strncmp(tok, "ver=", 4) == 0) { + char *endp; + unsigned long v; - if (*endp != '\0' || v <= 0) { + v = strtoul(tok + 4, &endp, 0); + if (*endp != '\0' || v > UINT32_MAX) { fprintf(stderr, - "Invalid grp value '%s'; expected a positive number or X\n", - val); + "Invalid ver value: %s\n", + tok + 4); return; } - snprintf(grp_str, sizeof(grp_str), "%u", - (unsigned int)v); + ver = (uint32_t)v; + } else { + fprintf(stderr, + "Unknown diff_layout option: '%s'\n", + tok); + fprintf(stderr, DIFF_LAYOUT_USAGE); + return; } - } else { - fprintf(stderr, "Unknown diff_layout option: '%s'\n", - tok); - fprintf(stderr, DIFF_LAYOUT_USAGE); - return; - } - } - - if (!id_found) { - fprintf(stderr, DIFF_LAYOUT_USAGE); - return; - } - - /* Resolve object class */ - if (type_str[0] != '\0' && grp_str[0] != '\0') { - char class_name[sizeof(type_str) + sizeof(grp_str) + 2]; /* 'G' + NUL */ - - snprintf(class_name, sizeof(class_name), "%sG%s", type_str, - grp_str); - use_class = daos_oclass_name2id(class_name); - if (use_class == OC_UNKNOWN) { - fprintf(stderr, - "Unknown object class '%s' (from type=%s grp=%s)\n", - class_name, type_str, grp_str); - return; - } - } else if (type_str[0] != '\0' || grp_str[0] != '\0') { - fprintf(stderr, - "Both type= and grp= must be specified together\n"); - return; - } else { - if (g_obj_class == OC_UNKNOWN) { - fprintf(stderr, - "No object class set; run 'obj_class ' first\n"); - return; } - use_class = g_obj_class; } - oid.lo = lo_val; - oid.hi = 0; - rc = daos_obj_set_oid_by_class(&oid, 0, use_class, 0); - if (rc != 0) { - fprintf(stderr, "daos_obj_set_oid_by_class failed: %d\n", rc); - return; - } - - md.omd_id = oid; + md.omd_id = g_oid; md.omd_ver = (ver != 0) ? ver : pool_map_get_version(g_pl_map->pl_poolmap); md.omd_pda = 0; @@ -751,9 +745,9 @@ cmd_diff_layout(const char *arg) return; } - daos_oclass_id2name(use_class, name); + daos_oclass_id2name(g_oid_class, name); printf("Rebuild shards for OID lo=%" PRIu64 " class=%s ver=%u: %d shard(s)\n", - lo_val, name, md.omd_ver, ntgt); + g_oid.lo, name, md.omd_ver, ntgt); for (i = 0; i < ntgt; i++) { struct pool_target *tgt = NULL; @@ -1034,6 +1028,8 @@ run_repl(void) cmd_obj_class(arg); } else if (strcmp(cmd, "print_obj_class") == 0) { cmd_print_obj_class(arg); + } else if (strcmp(cmd, "gen_oid") == 0) { + cmd_gen_oid(arg); } else if (strcmp(cmd, "gen_layout") == 0) { cmd_gen_layout(arg); } else if (strcmp(cmd, "diff_layout") == 0) { From 28b73fef905ed54f5f65ef5a32936a9ff0301f14 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:11:55 +0000 Subject: [PATCH 18/18] placement/tests: add class= parameter to gen_oid command Co-authored-by: gnailzenh <7268050+gnailzenh@users.noreply.github.com> --- src/placement/tests/pl_debug.c | 67 +++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/src/placement/tests/pl_debug.c b/src/placement/tests/pl_debug.c index 565ce197580..9246fbd228c 100644 --- a/src/placement/tests/pl_debug.c +++ b/src/placement/tests/pl_debug.c @@ -15,7 +15,7 @@ * obj_class - Set current object class * print_obj_class - Print object classes matching hint * hint: all | EC | EC(k+p) | RP | RP_ | shard - * gen_oid id= [type= grp=] + * gen_oid id= [class=] [type= grp=] * - Set current OID (required before gen_layout/diff_layout) * gen_layout [mode=] [ver=] * - Generate layout for current OID @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -105,9 +106,11 @@ print_help(void) " RP: all RP classes; RP_3: 3-way RP classes\n" " shard: S1/S2/.../SX classes\n" " all: every registered class\n" - " gen_oid id= [type= grp=]\n" + " gen_oid id= [class=] [type= grp=]\n" " Set current OID (required before gen_layout/diff_layout)\n" - " type+grp: override obj_class for this OID\n" + " class: object class name or numeric id\n" + " e.g. class=OC_EC_4P1GX or class=256\n" + " type+grp: alternative way to specify class\n" " e.g. type=EC_8P2 grp=2 -> EC_8P2G2\n" " type=RP_3 grp=X -> RP_3GX\n" " gen_layout [mode=] [ver=]\n" @@ -408,11 +411,13 @@ cmd_print_obj_class(const char *arg) } /* - * gen_oid id= [type=<...> grp=<...>] + * gen_oid id= [class=] [type=<...> grp=<...>] * * Sets the current OID (g_oid / g_oid_class) that gen_layout and diff_layout - * will operate on. The object class is taken from type=/grp= if provided, or - * from g_obj_class (set by the obj_class command) otherwise. + * will operate on. The object class is resolved in priority order: + * 1. class= (direct class name or numeric id) + * 2. type=<...> grp=<...> (composite name, e.g. EC_8P2G2) + * 3. g_obj_class (set by the obj_class command) */ static void cmd_gen_oid(const char *arg) @@ -422,14 +427,18 @@ cmd_gen_oid(const char *arg) char arg_copy[1024]; char *tok, *save; bool id_found = false; + char class_str[64] = {0}; /* class= value, e.g. "OC_EC_4P1GX" or "256" */ char type_str[64] = {0}; char grp_str[16] = {0}; daos_oclass_id_t use_class; char name[64] = {0}; + char *endp; + unsigned long num; int rc; #define GEN_OID_USAGE \ - "Usage: gen_oid id= [type= grp=]\n" + "Usage: gen_oid id= [class=]\n" \ + " [type= grp=]\n" if (arg == NULL || *arg == '\0') { fprintf(stderr, GEN_OID_USAGE); @@ -449,6 +458,15 @@ cmd_gen_oid(const char *arg) return; } id_found = true; + } else if (strncmp(tok, "class=", 6) == 0) { + const char *val = tok + 6; + + if (strlen(val) >= sizeof(class_str)) { + fprintf(stderr, "class= value too long: %s\n", + val); + return; + } + snprintf(class_str, sizeof(class_str), "%s", val); } else if (strncmp(tok, "type=", 5) == 0) { const char *val = tok + 5; @@ -488,8 +506,37 @@ cmd_gen_oid(const char *arg) return; } - /* Resolve object class: type=+grp= override g_obj_class */ - if (type_str[0] != '\0' && grp_str[0] != '\0') { + /* Resolve object class: class= > type=+grp= > g_obj_class */ + if (class_str[0] != '\0') { + /* class= is mutually exclusive with type=/grp= */ + if (type_str[0] != '\0' || grp_str[0] != '\0') { + fprintf(stderr, + "class= and type=/grp= are mutually exclusive\n"); + return; + } + /* Accept numeric id or class name */ + errno = 0; + num = strtoul(class_str, &endp, 0); + + if (*endp == '\0') { + if (errno == ERANGE || num > UINT32_MAX) { + fprintf(stderr, + "class= numeric value out of range: %s\n", + class_str); + return; + } + use_class = (daos_oclass_id_t)num; + } else { + use_class = daos_oclass_name2id(class_str); + if (use_class == OC_UNKNOWN) { + fprintf(stderr, + "Unknown object class '%s'\n" + " Use 'print_obj_class all' to list valid classes\n", + class_str); + return; + } + } + } else if (type_str[0] != '\0' && grp_str[0] != '\0') { char class_name[sizeof(type_str) + sizeof(grp_str) + 2]; /* 'G' + NUL */ snprintf(class_name, sizeof(class_name), "%sG%s", type_str, @@ -511,7 +558,7 @@ cmd_gen_oid(const char *arg) if (g_obj_class == OC_UNKNOWN) { fprintf(stderr, "No object class set; run 'obj_class ' first\n" - " (or pass type= and grp= to override, e.g. type=EC_8P2 grp=2)\n"); + " (or use class=, or type= and grp= to specify)\n"); return; } use_class = g_obj_class;