From 5af75f7d786c413eb6b11edb8eda5475a4d190ba Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sun, 31 Jul 2022 01:10:05 +0300 Subject: [PATCH] Implement vitastor-cli and vitastor-disk --help --- src/base64.cpp | 79 +++++++++++++++++++++++ src/base64.h | 1 + src/cli.cpp | 157 +++++++++++++++++++++++----------------------- src/disk_tool.cpp | 137 ++++++++++++++++++++-------------------- 4 files changed, 228 insertions(+), 146 deletions(-) diff --git a/src/base64.cpp b/src/base64.cpp index 24ce0902..6b73f84f 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -2,6 +2,7 @@ // License: VNPL-1.1 (see README.md for details) #include +#include #include "base64.h" std::string base64_encode(const std::string &in) @@ -124,3 +125,81 @@ std::string format_size(uint64_t size, bool nobytes) } return std::string(buf); } + +void print_help(const char *help_text, std::string exe_name, std::string cmd, bool all) +{ + if (cmd == "" && all) + { + fwrite(help_text, strlen(help_text), 1, stdout); + exit(0); + } + std::string filtered_text = ""; + const char *head_end = strstr(help_text, "COMMANDS:\n"); + if (head_end) + { + filtered_text += std::string(help_text, head_end-help_text); + head_end += strlen("COMMANDS:\n"); + } + const char *next_line = head_end ? head_end : help_text; + if (cmd != "") + { + const char *cmd_start = NULL; + bool matched = false, started = true, found = false; + while ((next_line = strchr(next_line, '\n'))) + { + next_line++; + if (*next_line && !strncmp(next_line, exe_name.c_str(), exe_name.size())) + { + if (started) + { + if (cmd_start && matched) + filtered_text += std::string(cmd_start, next_line-cmd_start); + cmd_start = next_line; + matched = started = false; + } + const char *var_start = next_line+exe_name.size()+1; + const char *var_end = var_start; + while (*var_end && !isspace(*var_end)) + var_end++; + if ((std::string(var_start, var_end-var_start)+"|").find(cmd+"|") != std::string::npos) + found = matched = true; + } + else if (*next_line && isspace(*next_line)) + started = true; + else if (cmd_start && matched) + filtered_text += std::string(cmd_start, next_line-cmd_start); + } + while (filtered_text.size() > 1 && + filtered_text[filtered_text.size()-1] == '\n' && + filtered_text[filtered_text.size()-2] == '\n') + { + filtered_text.resize(filtered_text.size()-1); + } + if (!found) + { + filtered_text = "Unknown command: "+cmd+". Use "+exe_name+" --help for usage\n"; + } + } + else + { + filtered_text += "COMMANDS:\n\n"; + while ((next_line = strchr(next_line, '\n'))) + { + next_line++; + if (*next_line && !strncmp(next_line, exe_name.c_str(), exe_name.size())) + { + const char *line_end = strchr(next_line, '\n'); + line_end = line_end ? line_end : next_line+strlen(next_line); + filtered_text += " "+(line_end ? std::string(next_line, line_end-next_line) : std::string(next_line)); + filtered_text += "\n"; + } + else if (*next_line && !isspace(next_line[0])) + { + filtered_text += "\n"+std::string(next_line); + break; + } + } + } + fwrite(filtered_text.data(), filtered_text.size(), 1, stdout); + exit(0); +} diff --git a/src/base64.h b/src/base64.h index a1de2717..fd877d21 100644 --- a/src/base64.h +++ b/src/base64.h @@ -10,3 +10,4 @@ std::string base64_decode(const std::string &in); uint64_t parse_size(std::string size_str); uint64_t stoull_full(const std::string & str, int base = 0); std::string format_size(uint64_t size, bool nobytes = false); +void print_help(const char *help_text, std::string exe_name, std::string cmd, bool all); diff --git a/src/cli.cpp b/src/cli.cpp index 564f6361..8439867b 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -16,7 +16,77 @@ static const char *exe_name = NULL; -static void help(); +static const char* help_text = + "Vitastor command-line tool\n" + "(c) Vitaliy Filippov, 2019+ (VNPL-1.1)\n" + "\n" + "COMMANDS:\n" + "\n" + "vitastor-cli status\n" + " Show cluster status\n" + "\n" + "vitastor-cli df\n" + " Show pool space statistics\n" + "\n" + "vitastor-cli ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [ ...]\n" + " List images (only matching patterns if passed).\n" + " -p|--pool POOL Filter images by pool ID or name\n" + " -l|--long Also report allocated size and I/O statistics\n" + " --del Also include delete operation statistics\n" + " --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" + "\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" + " a copy-on-write image clone is created. Parent must be a snapshot (readonly image).\n" + " Pool must be specified if there is more than one pool.\n" + "\n" + "vitastor-cli create --snapshot [-p|--pool ] \n" + "vitastor-cli snap-create [-p|--pool ] @\n" + " Create a snapshot of image . May be used live if only a single writer is active.\n" + "\n" + "vitastor-cli modify [--rename ] [--resize ] [--readonly | --readwrite] [-f|--force]\n" + " Rename, resize image or change its readonly status. Images with children can't be made read-write.\n" + " If the new size is smaller than the old size, extra data will be purged.\n" + " You should resize file system in the image, if present, before shrinking it.\n" + " -f|--force Proceed with shrinking or setting readwrite flag even if the image has children.\n" + "\n" + "vitastor-cli rm [] [--writers-stopped]\n" + " Remove or all layers between and ( must be a child of ),\n" + " rebasing all their children accordingly. --writers-stopped allows merging to be a bit\n" + " more effective in case of a single 'slim' read-write child and 'fat' removed parent:\n" + " the child is merged into parent and parent is renamed to child in that case.\n" + " In other cases parent layers are always merged into children.\n" + "\n" + "vitastor-cli flatten \n" + " Flatten a layer, i.e. merge data and detach it from parents.\n" + "\n" + "vitastor-cli rm-data --pool --inode [--wait-list] [--min-offset ]\n" + " Remove inode data without changing metadata.\n" + " --wait-list Retrieve full objects listings before starting to remove objects.\n" + " Requires more memory, but allows to show correct removal progress.\n" + " --min-offset Purge only data starting with specified offset.\n" + "\n" + "vitastor-cli merge-data [--target ]\n" + " Merge layer data without changing metadata. Merge .. to .\n" + " must be a child of and may be one of the layers between\n" + " and , including and .\n" + "\n" + "vitastor-cli alloc-osd\n" + " Allocate a new OSD number and reserve it by creating empty /osd/stats/ key.\n" + "\n" + "Use vitastor-cli --help for command details or vitastor-cli --help --all for all details.\n" + "\n" + "GLOBAL OPTIONS:\n" + " --etcd_address \n" + " --iodepth N Send N operations in parallel to each OSD when possible (default 32)\n" + " --parallel_osds M Work with M osds in parallel when possible (default 4)\n" + " --progress 1|0 Report progress (default 1)\n" + " --cas 1|0 Use CAS writes for flatten, merge, rm (default is decide automatically)\n" + " --no-color Disable colored output\n" + " --json JSON output\n" +; static json11::Json::object parse_args(int narg, const char *args[]) { @@ -25,9 +95,9 @@ static json11::Json::object parse_args(int narg, const char *args[]) cfg["progress"] = "1"; for (int i = 1; i < narg; i++) { - if (!strcmp(args[i], "-h") || !strcmp(args[i], "--help")) + if (args[i][0] == '-' && args[i][1] == 'h') { - help(); + cfg["help"] = "1"; } else if (args[i][0] == '-' && args[i][1] == 'l') { @@ -60,6 +130,7 @@ static json11::Json::object parse_args(int narg, const char *args[]) !strcmp(opt, "long") || !strcmp(opt, "del") || !strcmp(opt, "no-color") || !strcmp(opt, "readonly") || !strcmp(opt, "readwrite") || !strcmp(opt, "force") || !strcmp(opt, "reverse") || + !strcmp(opt, "help") || !strcmp(opt, "all") || !strcmp(opt, "writers-stopped") && strcmp("1", args[i+1]) != 0 ? "1" : args[++i]; } @@ -68,6 +139,10 @@ static json11::Json::object parse_args(int narg, const char *args[]) cmd.push_back(std::string(args[i])); } } + if (cfg["help"].bool_value()) + { + print_help(help_text, "vitastor-cli", cmd.size() ? cmd[0].string_value() : "", cfg["all"].bool_value()); + } if (!cmd.size()) { std::string exe(exe_name); @@ -80,82 +155,6 @@ static json11::Json::object parse_args(int narg, const char *args[]) return cfg; } -static void help() -{ - printf( - "Vitastor command-line tool\n" - "(c) Vitaliy Filippov, 2019+ (VNPL-1.1)\n" - "\n" - "USAGE:\n" - "%s status\n" - " Show cluster status\n" - "\n" - "%s df\n" - " Show pool space statistics\n" - "\n" - "%s ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [ ...]\n" - " List images (only matching patterns if passed).\n" - " -p|--pool POOL Filter images by pool ID or name\n" - " -l|--long Also report allocated size and I/O statistics\n" - " --del Also include delete operation statistics\n" - " --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" - "\n" - "%s create -s|--size [-p|--pool ] [--parent [@]] \n" - " Create an image. You may use K/M/G/T suffixes for . If --parent is specified,\n" - " a copy-on-write image clone is created. Parent must be a snapshot (readonly image).\n" - " Pool must be specified if there is more than one pool.\n" - "\n" - "%s create --snapshot [-p|--pool ] \n" - "%s snap-create [-p|--pool ] @\n" - " Create a snapshot of image . May be used live if only a single writer is active.\n" - "\n" - "%s modify [--rename ] [--resize ] [--readonly | --readwrite] [-f|--force]\n" - " Rename, resize image or change its readonly status. Images with children can't be made read-write.\n" - " If the new size is smaller than the old size, extra data will be purged.\n" - " You should resize file system in the image, if present, before shrinking it.\n" - " -f|--force Proceed with shrinking or setting readwrite flag even if the image has children.\n" - "\n" - "%s rm [] [--writers-stopped]\n" - " Remove or all layers between and ( must be a child of ),\n" - " rebasing all their children accordingly. --writers-stopped allows merging to be a bit\n" - " more effective in case of a single 'slim' read-write child and 'fat' removed parent:\n" - " the child is merged into parent and parent is renamed to child in that case.\n" - " In other cases parent layers are always merged into children.\n" - "\n" - "%s flatten \n" - " Flatten a layer, i.e. merge data and detach it from parents.\n" - "\n" - "%s rm-data --pool --inode [--wait-list] [--min-offset ]\n" - " Remove inode data without changing metadata.\n" - " --wait-list Retrieve full objects listings before starting to remove objects.\n" - " Requires more memory, but allows to show correct removal progress.\n" - " --min-offset Purge only data starting with specified offset.\n" - "\n" - "%s merge-data [--target ]\n" - " Merge layer data without changing metadata. Merge .. to .\n" - " must be a child of and may be one of the layers between\n" - " and , including and .\n" - "\n" - "%s alloc-osd\n" - " Allocate a new OSD number and reserve it by creating empty /osd/stats/ key.\n" - "\n" - "GLOBAL OPTIONS:\n" - " --etcd_address \n" - " --iodepth N Send N operations in parallel to each OSD when possible (default 32)\n" - " --parallel_osds M Work with M osds in parallel when possible (default 4)\n" - " --progress 1|0 Report progress (default 1)\n" - " --cas 1|0 Use CAS writes for flatten, merge, rm (default is decide automatically)\n" - " --no-color Disable colored output\n" - " --json JSON output\n" - , - exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, - exe_name, exe_name, exe_name, exe_name, exe_name, exe_name - ); - exit(0); -} - static int run(cli_tool_t *p, json11::Json::object cfg) { cli_result_t result = {}; diff --git a/src/disk_tool.cpp b/src/disk_tool.cpp index 0aa4c236..903ede4d 100644 --- a/src/disk_tool.cpp +++ b/src/disk_tool.cpp @@ -21,6 +21,7 @@ #include "blockstore_impl.h" #include "blockstore_disk.h" #include "osd_id.h" +#include "base64.h" #include "crc32c.h" #include "rw_blocking.h" @@ -49,6 +50,73 @@ struct resizer_data_moving_t uint64_t old_loc, new_loc; }; +static const char *help_text = + "Vitastor disk management tool\n" + "(c) Vitaliy Filippov, 2022+ (VNPL-1.1)\n" + "\n" + "COMMANDS:\n" + "\n" + "vitastor-disk resize [--iodepth 32]\n" + " Resize data area and/or rewrite/move journal and metadata\n" + " ALL_OSD_PARAMETERS must include all (at least all disk-related)\n" + " parameters from OSD command line (i.e. from systemd unit).\n" + " NEW_LAYOUT may include new disk layout parameters:\n" + " [--new_data_offset ] resize data area so it starts at \n" + " [--new_data_len ] resize data area to bytes\n" + " [--new_meta_device ] use for new metadata\n" + " [--new_meta_offset ] make new metadata area start at \n" + " [--new_meta_len ] make new metadata area bytes long\n" + " [--new_journal_device ] use for new journal\n" + " [--new_journal_offset ] make new journal area start at \n" + " [--new_journal_len ] make new journal area bytes long\n" + " If any of the new layout parameter options are not specified, old values\n" + " will be used.\n" + "\n" + "vitastor-disk start|stop|restart|enable|disable [--now] [device2 device3 ...]\n" + " Manipulate Vitastor OSDs using systemd by their device paths.\n" + " Commands are passed to systemctl with vitastor-osd@ units as arguments.\n" + " When --now is added to enable/disable, OSDs are also immediately started/stopped.\n" + "\n" + "vitastor-disk read-sb \n" + " Try to read Vitastor OSD superblock from and print it in JSON format.\n" + "\n" + "vitastor-disk write-sb \n" + " Read JSON from STDIN and write it into Vitastor OSD superblock on .\n" + "\n" + "vitastor-disk udev \n" + " Try to read Vitastor OSD superblock from and print variables for udev.\n" + "\n" + "vitastor-disk exec-osd \n" + " Read Vitastor OSD superblock from and start the OSD with parameters from it.\n" + " Intended for use from startup scripts (i.e. from systemd units).\n" + "\n" + "vitastor-disk pre-exec \n" + " Read Vitastor OSD superblock from and perform pre-start checks for the OSD.\n" + " For now, this only checks that device cache is in write-through mode if fsync is disabled.\n" + " Intended for use from startup scripts (i.e. from systemd units).\n" + "\n" + "vitastor-disk dump-journal [--all] [--json] \n" + " Dump journal in human-readable or JSON (if --json is specified) format.\n" + " Without --all, only actual part of the journal is dumped.\n" + " With --all, the whole journal area is scanned for journal entries,\n" + " some of which may be outdated.\n" + "\n" + "vitastor-disk dump-meta \n" + " Dump metadata in JSON format.\n" + "\n" + "vitastor-disk simple-offsets \n" + " Calculate offsets for old simple&stupid (no superblock) OSD deployment. Options:\n" + " --object_size 128k Set blockstore block size\n" + " --bitmap_granularity 4k Set bitmap granularity\n" + " --journal_size 16M Set journal size\n" + " --device_block_size 4k Set device block size\n" + " --journal_offset 0 Set journal offset\n" + " --device_size 0 Set device size\n" + " --format text Result format: json, options, env, or text\n" + "\n" + "Use vitastor-disk --help for command details or vitastor-disk --help --all for all details.\n" +; + struct disk_tool_t { /**** Parameters ****/ @@ -155,8 +223,7 @@ int main(int argc, char *argv[]) } else if (!strcmp(argv[i], "--help")) { - cmd.clear(); - cmd.push_back((char*)"help"); + cmd.insert(cmd.begin(), (char*)"help"); } else if (!strcmp(argv[i], "--now")) { @@ -269,71 +336,7 @@ int main(int argc, char *argv[]) } else { - printf( - "Vitastor disk management tool\n" - "(c) Vitaliy Filippov, 2022+ (VNPL-1.1)\n" - "\n" - "USAGE:\n" - "%s resize [--iodepth 32]\n" - " Resize data area and/or rewrite/move journal and metadata\n" - " ALL_OSD_PARAMETERS must include all (at least all disk-related)\n" - " parameters from OSD command line (i.e. from systemd unit).\n" - " NEW_PARAMETERS include new disk layout parameters:\n" - " [--new_data_offset ] resize data area so it starts at \n" - " [--new_data_len ] resize data area to bytes\n" - " [--new_meta_device ] use for new metadata\n" - " [--new_meta_offset ] make new metadata area start at \n" - " [--new_meta_len ] make new metadata area bytes long\n" - " [--new_journal_device ] use for new journal\n" - " [--new_journal_offset ] make new journal area start at \n" - " [--new_journal_len ] make new journal area bytes long\n" - " If any of the new layout parameter options are not specified, old values\n" - " will be used.\n" - "\n" - "%s start|stop|restart|enable|disable [--now] [device2 device3 ...]\n" - " Manipulate Vitastor OSDs using systemd by their device paths.\n" - " Commands are passed to systemctl with vitastor-osd@ units as arguments.\n" - " When --now is added to enable/disable, OSDs are also immediately started/stopped.\n" - "\n" - "%s read-sb \n" - " Try to read Vitastor OSD superblock from and print it in JSON format.\n" - "\n" - "%s write-sb \n" - " Read JSON from STDIN and write it into Vitastor OSD superblock on .\n" - "\n" - "%s udev \n" - " Try to read Vitastor OSD superblock from and print variables for udev.\n" - "\n" - "%s exec-osd \n" - " Read Vitastor OSD superblock from and start the OSD with parameters from it.\n" - " Intended for use from startup scripts (i.e. from systemd units).\n" - "\n" - "%s pre-exec \n" - " Read Vitastor OSD superblock from and perform pre-start checks for the OSD.\n" - " For now, this only checks that device cache is in write-through mode if fsync is disabled.\n" - " Intended for use from startup scripts (i.e. from systemd units).\n" - "\n" - "%s dump-journal [--all] [--json] \n" - " Dump journal in human-readable or JSON (if --json is specified) format.\n" - " Without --all, only actual part of the journal is dumped.\n" - " With --all, the whole journal area is scanned for journal entries,\n" - " some of which may be outdated.\n" - "\n" - "%s dump-meta \n" - " Dump metadata in JSON format.\n" - "\n" - "%s simple-offsets \n" - " Calculate offsets for old simple&stupid (no superblock) OSD deployment. Options:\n" - " --object_size 128k Set blockstore block size\n" - " --bitmap_granularity 4k Set bitmap granularity\n" - " --journal_size 16M Set journal size\n" - " --device_block_size 4k Set device block size\n" - " --journal_offset 0 Set journal offset\n" - " --device_size 0 Set device size\n" - " --format text Result format: json, options, env, or text\n" - , - argv[0], argv[0], argv[0], argv[0], argv[0], argv[0], argv[0], argv[0], argv[0], argv[0] - ); + print_help(help_text, "vitastor-disk", cmd.size() > 1 ? cmd[1] : "", self.all); } return 0; }