Implement vitastor-cli and vitastor-disk --help <command>

rm-left-on-dead
Vitaliy Filippov 2022-07-31 01:10:05 +03:00
parent 7dc6f10ea1
commit 5af75f7d78
4 changed files with 228 additions and 146 deletions

View File

@ -2,6 +2,7 @@
// License: VNPL-1.1 (see README.md for details)
#include <assert.h>
#include <string.h>
#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);
}

View File

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

View File

@ -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] [<glob> ...]\n"
" List images (only matching <glob> 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, <read|write|delete>_<iops|bps|lat|queue>)\n"
" -r|--reverse Sort in descending order\n"
" -n|--count N Only list first N items\n"
"\n"
"vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>\n"
" Create an image. You may use K/M/G/T suffixes for <size>. 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 <snapshot> [-p|--pool <id|name>] <image>\n"
"vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>\n"
" Create a snapshot of image <name>. May be used live if only a single writer is active.\n"
"\n"
"vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--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 <from> [<to>] [--writers-stopped]\n"
" Remove <from> or all layers between <from> and <to> (<to> must be a child of <from>),\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 <layer>\n"
" Flatten a layer, i.e. merge data and detach it from parents.\n"
"\n"
"vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <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 <from> <to> [--target <target>]\n"
" Merge layer data without changing metadata. Merge <from>..<to> to <target>.\n"
" <to> must be a child of <from> and <target> may be one of the layers between\n"
" <from> and <to>, including <from> and <to>.\n"
"\n"
"vitastor-cli alloc-osd\n"
" Allocate a new OSD number and reserve it by creating empty /osd/stats/<n> key.\n"
"\n"
"Use vitastor-cli --help <command> for command details or vitastor-cli --help --all for all details.\n"
"\n"
"GLOBAL OPTIONS:\n"
" --etcd_address <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] [<glob> ...]\n"
" List images (only matching <glob> 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, <read|write|delete>_<iops|bps|lat|queue>)\n"
" -r|--reverse Sort in descending order\n"
" -n|--count N Only list first N items\n"
"\n"
"%s create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>\n"
" Create an image. You may use K/M/G/T suffixes for <size>. 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 <snapshot> [-p|--pool <id|name>] <image>\n"
"%s snap-create [-p|--pool <id|name>] <image>@<snapshot>\n"
" Create a snapshot of image <name>. May be used live if only a single writer is active.\n"
"\n"
"%s modify <name> [--rename <new-name>] [--resize <size>] [--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 <from> [<to>] [--writers-stopped]\n"
" Remove <from> or all layers between <from> and <to> (<to> must be a child of <from>),\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 <layer>\n"
" Flatten a layer, i.e. merge data and detach it from parents.\n"
"\n"
"%s rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <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 <from> <to> [--target <target>]\n"
" Merge layer data without changing metadata. Merge <from>..<to> to <target>.\n"
" <to> must be a child of <from> and <target> may be one of the layers between\n"
" <from> and <to>, including <from> and <to>.\n"
"\n"
"%s alloc-osd\n"
" Allocate a new OSD number and reserve it by creating empty /osd/stats/<n> key.\n"
"\n"
"GLOBAL OPTIONS:\n"
" --etcd_address <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 = {};

View File

@ -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 <ALL_OSD_PARAMETERS> <NEW_LAYOUT> [--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 <NUMBER>] resize data area so it starts at <NUMBER>\n"
" [--new_data_len <NUMBER>] resize data area to <NUMBER> bytes\n"
" [--new_meta_device <PATH>] use <PATH> for new metadata\n"
" [--new_meta_offset <NUMBER>] make new metadata area start at <NUMBER>\n"
" [--new_meta_len <NUMBER>] make new metadata area <NUMBER> bytes long\n"
" [--new_journal_device <PATH>] use <PATH> for new journal\n"
" [--new_journal_offset <NUMBER>] make new journal area start at <NUMBER>\n"
" [--new_journal_len <NUMBER>] make new journal area <NUMBER> 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] <device> [device2 device3 ...]\n"
" Manipulate Vitastor OSDs using systemd by their device paths.\n"
" Commands are passed to systemctl with vitastor-osd@<num> units as arguments.\n"
" When --now is added to enable/disable, OSDs are also immediately started/stopped.\n"
"\n"
"vitastor-disk read-sb <device>\n"
" Try to read Vitastor OSD superblock from <device> and print it in JSON format.\n"
"\n"
"vitastor-disk write-sb <device>\n"
" Read JSON from STDIN and write it into Vitastor OSD superblock on <device>.\n"
"\n"
"vitastor-disk udev <device>\n"
" Try to read Vitastor OSD superblock from <device> and print variables for udev.\n"
"\n"
"vitastor-disk exec-osd <device>\n"
" Read Vitastor OSD superblock from <device> 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 <device>\n"
" Read Vitastor OSD superblock from <device> 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] <journal_file> <journal_block_size> <offset> <size>\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 <meta_file> <meta_block_size> <offset> <size>\n"
" Dump metadata in JSON format.\n"
"\n"
"vitastor-disk simple-offsets <device>\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 <command> 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 <ALL_OSD_PARAMETERS> <NEW_PARAMETERS> [--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 <NUMBER>] resize data area so it starts at <NUMBER>\n"
" [--new_data_len <NUMBER>] resize data area to <NUMBER> bytes\n"
" [--new_meta_device <PATH>] use <PATH> for new metadata\n"
" [--new_meta_offset <NUMBER>] make new metadata area start at <NUMBER>\n"
" [--new_meta_len <NUMBER>] make new metadata area <NUMBER> bytes long\n"
" [--new_journal_device <PATH>] use <PATH> for new journal\n"
" [--new_journal_offset <NUMBER>] make new journal area start at <NUMBER>\n"
" [--new_journal_len <NUMBER>] make new journal area <NUMBER> 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] <device> [device2 device3 ...]\n"
" Manipulate Vitastor OSDs using systemd by their device paths.\n"
" Commands are passed to systemctl with vitastor-osd@<num> units as arguments.\n"
" When --now is added to enable/disable, OSDs are also immediately started/stopped.\n"
"\n"
"%s read-sb <device>\n"
" Try to read Vitastor OSD superblock from <device> and print it in JSON format.\n"
"\n"
"%s write-sb <device>\n"
" Read JSON from STDIN and write it into Vitastor OSD superblock on <device>.\n"
"\n"
"%s udev <device>\n"
" Try to read Vitastor OSD superblock from <device> and print variables for udev.\n"
"\n"
"%s exec-osd <device>\n"
" Read Vitastor OSD superblock from <device> 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 <device>\n"
" Read Vitastor OSD superblock from <device> 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] <journal_file> <journal_block_size> <offset> <size>\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 <meta_file> <meta_block_size> <offset> <size>\n"
" Dump metadata in JSON format.\n"
"\n"
"%s simple-offsets <device>\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;
}