From 3c5b9d2744aa7431e6d5331a550cdea41a8ca2f5 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Wed, 24 Sep 2025 13:09:43 +0300 Subject: [PATCH] Support vitastor-cli ls --tree syntax --- docs/usage/cli.en.md | 2 ++ docs/usage/cli.ru.md | 2 ++ src/cmd/cli.cpp | 3 ++- src/cmd/cli_ls.cpp | 32 +++++++++++++++++++++++++++++++- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/docs/usage/cli.en.md b/docs/usage/cli.en.md index df073459..8a9e293b 100644 --- a/docs/usage/cli.en.md +++ b/docs/usage/cli.en.md @@ -100,12 +100,14 @@ List images (only matching `` pattern(s) if passed). Options: ``` +--exact Do not match glob patterns as names, select only exact name matches. -p|--pool POOL Filter images by pool ID or name -l|--long Also report allocated size and I/O statistics --del Also include delete operation statistics --sort FIELD Sort by specified field (name, size, used_size, _) -r|--reverse Sort in descending order -n|--count N Only list first N items +--tree Show image snapshot/clone tree ``` Example output: diff --git a/docs/usage/cli.ru.md b/docs/usage/cli.ru.md index 106cccde..6614f85a 100644 --- a/docs/usage/cli.ru.md +++ b/docs/usage/cli.ru.md @@ -102,12 +102,14 @@ kaveri 2/1 32 0 B 10 G 0 B 100% 0% Опции: ``` +--exact Не применять ФС-шаблоны к именам, выводить только точные совпадения -p|--pool POOL Фильтровать образы по пулу (ID или имени) -l|--long Также выводить статистику занятого места и ввода-вывода --del Также выводить статистику операций удаления --sort FIELD Сортировать по заданному полю (name, size, used_size, _) -r|--reverse Сортировать в обратном порядке -n|--count N Показывать только первые N записей +--tree Вывести снапшоты и клоны в виде дерева ``` Пример вывода: diff --git a/src/cmd/cli.cpp b/src/cmd/cli.cpp index cad16f28..2c3eaf2f 100644 --- a/src/cmd/cli.cpp +++ b/src/cmd/cli.cpp @@ -37,6 +37,7 @@ static const char* help_text = " --sort FIELD Sort by specified field (name, size, used_size, _)\n" " -r|--reverse Sort in descending order\n" " -n|--count N Only list first N items\n" + " --tree Show image snapshot/clone tree\n" "\n" "vitastor-cli create -s|--size [-p|--pool ] [--parent [@]] \n" " Create an image. You may use K/M/G/T suffixes for . If --parent is specified,\n" @@ -287,7 +288,7 @@ static json11::Json::object parse_args(int narg, const char *args[]) !strcmp(opt, "down-ok") || !strcmp(opt, "down_ok") || !strcmp(opt, "dry-run") || !strcmp(opt, "dry_run") || !strcmp(opt, "help") || !strcmp(opt, "all") || - !strcmp(opt, "exact") || !strcmp(opt, "matching") || + !strcmp(opt, "exact") || !strcmp(opt, "matching") || !strcmp(opt, "tree") || !strcmp(opt, "writers-stopped") || !strcmp(opt, "writers_stopped")) { cfg[opt] = "1"; diff --git a/src/cmd/cli_ls.cpp b/src/cmd/cli_ls.cpp index 1f7b8563..4f7cdff3 100644 --- a/src/cmd/cli_ls.cpp +++ b/src/cmd/cli_ls.cpp @@ -19,6 +19,7 @@ struct image_lister_t std::set only_names; bool reverse = false; bool exact = false; + bool tree = false; int max_count = 0; bool show_stats = false, show_delete = false; @@ -77,6 +78,7 @@ struct image_lister_t ? p_it->second.name : ""; item["parent_pool_id"] = (uint64_t)INODE_POOL(ic.second.parent_id); item["parent_inode_num"] = INODE_NO_POOL(ic.second.parent_id); + item["parent_inode_id"] = ic.second.parent_id; } stats[ic.second.num] = item; } @@ -245,6 +247,33 @@ resume_1: return list; } + json11::Json::array to_tree(const json11::Json::array & array) + { + std::map children; + for (const auto & item: array) + { + uint64_t parent_id = item["parent_inode_id"].uint64_value(); + children[parent_id].push_back(item); + } + json11::Json::array tree; + std::function add_children = [&](uint64_t parent_id, const std::string & prefix) + { + if (children.find(parent_id) != children.end()) + { + for (size_t i = 0; i < children[parent_id].size(); i++) + { + bool is_last = (i == children[parent_id].size()-1); + json11::Json::object new_item = children[parent_id][i].object_items(); + new_item["name"] = prefix + (parent_id == 0 ? "" : (is_last ? "└─ " : "├─ ")) + new_item["name"].string_value(); + tree.push_back(new_item); + add_children(new_item["inode_id"].uint64_value(), prefix + (parent_id == 0 ? "" : (is_last ? " " : "│ "))); + } + } + }; + add_children(0, ""); + return tree; + } + void loop() { if (state == 1) @@ -375,7 +404,7 @@ resume_1: kv.second["ro"] = kv.second["deleted"].bool_value() ? "DEL" : (kv.second["readonly"].bool_value() ? "RO" : "-"); } - result.text = print_table(to_list(), cols, parent->color); + result.text = print_table(tree ? to_tree(to_list()) : to_list(), cols, parent->color); state = 100; } }; @@ -542,6 +571,7 @@ std::function cli_tool_t::start_ls(json11::Json cfg) auto lister = new image_lister_t(); lister->parent = this; lister->exact = cfg["exact"].bool_value(); + lister->tree = cfg["tree"].bool_value(); lister->list_pool_id = cfg["pool"].uint64_value(); lister->list_pool_name = lister->list_pool_id ? "" : cfg["pool"].as_string(); lister->show_stats = cfg["long"].bool_value();