diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a0fca15..1720497d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,6 +124,18 @@ add_library(vitastor_client SHARED cluster_client.cpp cluster_client_list.cpp vitastor_c.cpp + cli_common.cpp + cli_alloc_osd.cpp + cli_simple_offsets.cpp + cli_status.cpp + cli_df.cpp + cli_ls.cpp + cli_create.cpp + cli_modify.cpp + cli_flatten.cpp + cli_merge.cpp + cli_rm_data.cpp + cli_rm.cpp ) set_target_properties(vitastor_client PROPERTIES PUBLIC_HEADER "vitastor_c.h") target_link_libraries(vitastor_client @@ -154,8 +166,7 @@ target_link_libraries(vitastor-nbd # vitastor-cli add_executable(vitastor-cli - cli.cpp cli_alloc_osd.cpp cli_simple_offsets.cpp cli_status.cpp cli_df.cpp - cli_ls.cpp cli_create.cpp cli_modify.cpp cli_flatten.cpp cli_merge.cpp cli_rm_data.cpp cli_rm.cpp + cli.cpp ) target_link_libraries(vitastor-cli vitastor_client diff --git a/src/cli.cpp b/src/cli.cpp index 78f646fa..44af0449 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -2,8 +2,7 @@ // License: VNPL-1.1 (see README.md for details) /** - * CLI tool - * Currently can (a) remove inodes and (b) merge snapshot/clone layers + * CLI tool and also a library for administrative tasks */ #include @@ -17,7 +16,9 @@ static const char *exe_name = NULL; -json11::Json::object cli_tool_t::parse_args(int narg, const char *args[]) +static void help(); + +static json11::Json::object parse_args(int narg, const char *args[]) { json11::Json::object cfg; json11::Json::array cmd; @@ -79,7 +80,7 @@ json11::Json::object cli_tool_t::parse_args(int narg, const char *args[]) return cfg; } -void cli_tool_t::help() +static void help() { printf( "Vitastor command-line tool\n" @@ -164,224 +165,171 @@ void cli_tool_t::help() exit(0); } -void cli_tool_t::change_parent(inode_t cur, inode_t new_parent) -{ - auto cur_cfg_it = cli->st_cli.inode_config.find(cur); - if (cur_cfg_it == cli->st_cli.inode_config.end()) - { - fprintf(stderr, "Inode 0x%lx disappeared\n", cur); - exit(1); - } - inode_config_t new_cfg = cur_cfg_it->second; - std::string cur_name = new_cfg.name; - std::string cur_cfg_key = base64_encode(cli->st_cli.etcd_prefix+ - "/config/inode/"+std::to_string(INODE_POOL(cur))+ - "/"+std::to_string(INODE_NO_POOL(cur))); - new_cfg.parent_id = new_parent; - json11::Json::object cur_cfg_json = cli->st_cli.serialize_inode_cfg(&new_cfg); - waiting++; - cli->st_cli.etcd_txn_slow(json11::Json::object { - { "compare", json11::Json::array { - json11::Json::object { - { "target", "MOD" }, - { "key", cur_cfg_key }, - { "result", "LESS" }, - { "mod_revision", new_cfg.mod_revision+1 }, - }, - } }, - { "success", json11::Json::array { - json11::Json::object { - { "request_put", json11::Json::object { - { "key", cur_cfg_key }, - { "value", base64_encode(json11::Json(cur_cfg_json).dump()) }, - } } - }, - } }, - }, [this, new_parent, cur, cur_name](std::string err, json11::Json res) - { - if (err != "") - { - fprintf(stderr, "Error changing parent of %s: %s\n", cur_name.c_str(), err.c_str()); - exit(1); - } - if (!res["succeeded"].bool_value()) - { - fprintf(stderr, "Inode %s was modified during snapshot deletion\n", cur_name.c_str()); - exit(1); - } - if (new_parent) - { - auto new_parent_it = cli->st_cli.inode_config.find(new_parent); - std::string new_parent_name = new_parent_it != cli->st_cli.inode_config.end() - ? new_parent_it->second.name : ""; - printf( - "Parent of layer %s (inode %lu in pool %u) changed to %s (inode %lu in pool %u)\n", - cur_name.c_str(), INODE_NO_POOL(cur), INODE_POOL(cur), - new_parent_name.c_str(), INODE_NO_POOL(new_parent), INODE_POOL(new_parent) - ); - } - else - { - printf( - "Parent of layer %s (inode %lu in pool %u) detached\n", - cur_name.c_str(), INODE_NO_POOL(cur), INODE_POOL(cur) - ); - } - waiting--; - ringloop->wakeup(); - }); -} - -void cli_tool_t::etcd_txn(json11::Json txn) -{ - waiting++; - cli->st_cli.etcd_txn_slow(txn, [this](std::string err, json11::Json res) - { - waiting--; - if (err != "") - { - fprintf(stderr, "Error reading from etcd: %s\n", err.c_str()); - exit(1); - } - etcd_result = res; - ringloop->wakeup(); - }); -} - -inode_config_t* cli_tool_t::get_inode_cfg(const std::string & name) -{ - for (auto & ic: cli->st_cli.inode_config) - { - if (ic.second.name == name) - { - return &ic.second; - } - } - fprintf(stderr, "Layer %s not found\n", name.c_str()); - exit(1); -} - -void cli_tool_t::run(json11::Json cfg) +static int run(cli_tool_t *p, json11::Json::object cfg) { + cli_result_t result; + p->parse_config(cfg); json11::Json::array cmd = cfg["command"].array_items(); + cfg.erase("command"); + std::function action_cb; if (!cmd.size()) { - fprintf(stderr, "command is missing\n"); - exit(1); + result = { .err = EINVAL, .text = "command is missing" }; } else if (cmd[0] == "status") { // Show cluster status - action_cb = start_status(cfg); + action_cb = p->start_status(cfg); } else if (cmd[0] == "df") { // Show pool space stats - action_cb = start_df(cfg); + action_cb = p->start_df(cfg); } else if (cmd[0] == "ls") { // List images - action_cb = start_ls(cfg); + if (cmd.size() > 1) + { + cmd.erase(cmd.begin(), cmd.begin()+1); + cfg["names"] = cmd; + } + action_cb = p->start_ls(cfg); } - else if (cmd[0] == "create" || cmd[0] == "snap-create") + else if (cmd[0] == "snap-create") + { + // Create snapshot + std::string name = cmd.size() > 1 ? cmd[1].string_value() : ""; + int pos = name.find('@'); + if (pos == std::string::npos || pos == name.length()-1) + { + result = (cli_result_t){ .err = EINVAL, .text = "Please specify new snapshot name after @" }; + } + else + { + cfg["image"] = name.substr(0, pos); + cfg["snapshot"] = name.substr(pos + 1); + action_cb = p->start_create(cfg); + } + } + else if (cmd[0] == "create") { // Create image/snapshot - action_cb = start_create(cfg); + if (cmd.size() > 1) + { + cfg["image"] = cmd[1]; + } + action_cb = p->start_create(cfg); } else if (cmd[0] == "modify") { // Modify image - action_cb = start_modify(cfg); + if (cmd.size() > 1) + { + cfg["image"] = cmd[1]; + } + action_cb = p->start_modify(cfg); } else if (cmd[0] == "rm-data") { // Delete inode data - action_cb = start_rm(cfg); + action_cb = p->start_rm_data(cfg); } else if (cmd[0] == "merge-data") { // Merge layer data without affecting metadata - action_cb = start_merge(cfg); + if (cmd.size() > 1) + { + cfg["from"] = cmd[1]; + if (cmd.size() > 2) + cfg["to"] = cmd[2]; + } + action_cb = p->start_merge(cfg); } else if (cmd[0] == "flatten") { // Merge layer data without affecting metadata - action_cb = start_flatten(cfg); + if (cmd.size() > 1) + { + cfg["image"] = cmd[1]; + } + action_cb = p->start_flatten(cfg); } else if (cmd[0] == "rm") { // Remove multiple snapshots and rebase their children - action_cb = start_snap_rm(cfg); + if (cmd.size() > 1) + { + cfg["from"] = cmd[1]; + if (cmd.size() > 2) + cfg["to"] = cmd[2]; + } + action_cb = p->start_rm(cfg); } else if (cmd[0] == "alloc-osd") { // Allocate a new OSD number - action_cb = start_alloc_osd(cfg); + action_cb = p->start_alloc_osd(cfg); } else if (cmd[0] == "simple-offsets") { // Calculate offsets for simple & stupid OSD deployment without superblock - action_cb = simple_offsets(cfg); + if (cmd.size() > 1) + { + cfg["device"] = cmd[1]; + } + action_cb = p->simple_offsets(cfg); } else { - fprintf(stderr, "unknown command: %s\n", cmd[0].string_value().c_str()); - exit(1); + result = { .err = EINVAL, .text = "unknown command: "+cmd[0].string_value() }; } - if (action_cb == NULL) + if (action_cb != NULL) { - return; - } - color = !cfg["no-color"].bool_value(); - json_output = cfg["json"].bool_value(); - iodepth = cfg["iodepth"].uint64_value(); - if (!iodepth) - iodepth = 32; - parallel_osds = cfg["parallel_osds"].uint64_value(); - if (!parallel_osds) - parallel_osds = 4; - log_level = cfg["log_level"].int64_value(); - progress = cfg["progress"].uint64_value() ? true : false; - list_first = cfg["wait-list"].uint64_value() ? true : false; - // Create client - ringloop = new ring_loop_t(512); - epmgr = new epoll_manager_t(ringloop); - cli = new cluster_client_t(ringloop, epmgr->tfd, cfg); - // Smaller timeout by default for more interactiveness - cli->st_cli.etcd_slow_timeout = cli->st_cli.etcd_quick_timeout; - cli->on_ready([this]() - { - // Initialize job - consumer.loop = [this]() + // Create client + json11::Json cfg_j = cfg; + p->ringloop = new ring_loop_t(512); + p->epmgr = new epoll_manager_t(p->ringloop); + p->cli = new cluster_client_t(p->ringloop, p->epmgr->tfd, cfg_j); + // Smaller timeout by default for more interactiveness + p->cli->st_cli.etcd_slow_timeout = p->cli->st_cli.etcd_quick_timeout; + p->loop_and_wait(action_cb, [&](const cli_result_t & r) { + result = r; + action_cb = NULL; + }); + // Loop until it completes + while (action_cb != NULL) + { + p->ringloop->loop(); if (action_cb != NULL) - { - bool done = action_cb(); - if (done) - { - action_cb = NULL; - } - } - ringloop->submit(); - }; - ringloop->register_consumer(&consumer); - consumer.loop(); - }); - // Loop until it completes - while (action_cb != NULL) - { - ringloop->loop(); - if (action_cb != NULL) - ringloop->wait(); + p->ringloop->wait(); + } + // Destroy the client + delete p->cli; + delete p->epmgr; + delete p->ringloop; + p->cli = NULL; + p->epmgr = NULL; + p->ringloop = NULL; } - // Destroy the client - delete cli; - delete epmgr; - delete ringloop; - cli = NULL; - epmgr = NULL; - ringloop = NULL; + // Print result + if (p->json_output && !result.data.is_null()) + { + printf("%s\n", result.data.dump().c_str()); + } + else if (p->json_output && result.err) + { + printf("%s\n", json11::Json(json11::Json::object { + { "error_code", result.err }, + { "error_text", result.text }, + }).dump().c_str()); + } + else if (result.text != "") + { + fprintf(result.err ? stderr : stdout, result.text[result.text.size()-1] == '\n' ? "%s" : "%s\n", result.text.c_str()); + } + return result.err; } int main(int narg, const char *args[]) @@ -390,7 +338,7 @@ int main(int narg, const char *args[]) setvbuf(stderr, NULL, _IONBF, 0); exe_name = args[0]; cli_tool_t *p = new cli_tool_t(); - p->run(cli_tool_t::parse_args(narg, args)); + int r = run(p, parse_args(narg, args)); delete p; - return 0; + return r; } diff --git a/src/cli.h b/src/cli.h index f7a9b8f3..58bd2812 100644 --- a/src/cli.h +++ b/src/cli.h @@ -19,11 +19,18 @@ class epoll_manager_t; class cluster_client_t; struct inode_config_t; +struct cli_result_t +{ + int err; + std::string text; + json11::Json data; +}; + class cli_tool_t { public: - uint64_t iodepth = 0, parallel_osds = 0; - bool progress = true; + uint64_t iodepth = 4, parallel_osds = 32; + bool progress = false; bool list_first = false; bool json_output = false; int log_level = 0; @@ -34,34 +41,33 @@ public: cluster_client_t *cli = NULL; int waiting = 0; + cli_result_t etcd_err; json11::Json etcd_result; - ring_consumer_t consumer; - std::function action_cb; - void run(json11::Json cfg); + void parse_config(json11::Json cfg); - void change_parent(inode_t cur, inode_t new_parent); + void change_parent(inode_t cur, inode_t new_parent, cli_result_t *result); inode_config_t* get_inode_cfg(const std::string & name); - static json11::Json::object parse_args(int narg, const char *args[]); - static void help(); - friend struct rm_inode_t; friend struct snap_merger_t; friend struct snap_flattener_t; friend struct snap_remover_t; - std::function start_status(json11::Json cfg); - std::function start_df(json11::Json); - std::function start_ls(json11::Json); - std::function start_create(json11::Json); - std::function start_modify(json11::Json); - std::function start_rm(json11::Json); - std::function start_merge(json11::Json); - std::function start_flatten(json11::Json); - std::function start_snap_rm(json11::Json); - std::function start_alloc_osd(json11::Json cfg, uint64_t *out = NULL); - std::function simple_offsets(json11::Json cfg); + std::function start_status(json11::Json); + std::function start_df(json11::Json); + std::function start_ls(json11::Json); + std::function start_create(json11::Json); + std::function start_modify(json11::Json); + std::function start_rm_data(json11::Json); + std::function start_merge(json11::Json); + std::function start_flatten(json11::Json); + std::function start_rm(json11::Json); + std::function start_alloc_osd(json11::Json cfg); + std::function simple_offsets(json11::Json cfg); + + // Should be called like loop_and_wait(start_status(), ) + void loop_and_wait(std::function loop_cb, std::function complete_cb); void etcd_txn(json11::Json txn); }; diff --git a/src/cli_alloc_osd.cpp b/src/cli_alloc_osd.cpp index 676de69b..23a9582c 100644 --- a/src/cli_alloc_osd.cpp +++ b/src/cli_alloc_osd.cpp @@ -16,6 +16,7 @@ struct alloc_osd_t uint64_t new_id = 1; int state = 0; + cli_result_t result; bool is_done() { @@ -62,6 +63,12 @@ struct alloc_osd_t state = 1; if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + result = parent->etcd_err; + state = 100; + return; + } if (!parent->etcd_result["succeeded"].bool_value()) { std::vector used; @@ -99,23 +106,23 @@ struct alloc_osd_t } } while (!parent->etcd_result["succeeded"].bool_value()); state = 100; + result = (cli_result_t){ + .text = std::to_string(new_id), + .data = json11::Json(new_id), + }; } }; -std::function cli_tool_t::start_alloc_osd(json11::Json cfg, uint64_t *out) +std::function cli_tool_t::start_alloc_osd(json11::Json cfg) { - json11::Json::array cmd = cfg["command"].array_items(); auto alloc_osd = new alloc_osd_t(); alloc_osd->parent = this; - return [alloc_osd, out]() + return [alloc_osd](cli_result_t & result) { alloc_osd->loop(); if (alloc_osd->is_done()) { - if (out) - *out = alloc_osd->new_id; - else if (alloc_osd->new_id) - printf("%lu\n", alloc_osd->new_id); + result = alloc_osd->result; delete alloc_osd; return true; } diff --git a/src/cli_common.cpp b/src/cli_common.cpp new file mode 100644 index 00000000..afe61fc4 --- /dev/null +++ b/src/cli_common.cpp @@ -0,0 +1,149 @@ +// Copyright (c) Vitaliy Filippov, 2019+ +// License: VNPL-1.1 (see README.md for details) + +#include "base64.h" +#include "cluster_client.h" +#include "cli.h" + +void cli_tool_t::change_parent(inode_t cur, inode_t new_parent, cli_result_t *result) +{ + auto cur_cfg_it = cli->st_cli.inode_config.find(cur); + if (cur_cfg_it == cli->st_cli.inode_config.end()) + { + char buf[128]; + snprintf(buf, 128, "Inode 0x%lx disappeared", cur); + *result = { .err = EIO, .text = buf }; + return; + } + inode_config_t new_cfg = cur_cfg_it->second; + std::string cur_name = new_cfg.name; + std::string cur_cfg_key = base64_encode(cli->st_cli.etcd_prefix+ + "/config/inode/"+std::to_string(INODE_POOL(cur))+ + "/"+std::to_string(INODE_NO_POOL(cur))); + new_cfg.parent_id = new_parent; + json11::Json::object cur_cfg_json = cli->st_cli.serialize_inode_cfg(&new_cfg); + waiting++; + cli->st_cli.etcd_txn_slow(json11::Json::object { + { "compare", json11::Json::array { + json11::Json::object { + { "target", "MOD" }, + { "key", cur_cfg_key }, + { "result", "LESS" }, + { "mod_revision", new_cfg.mod_revision+1 }, + }, + } }, + { "success", json11::Json::array { + json11::Json::object { + { "request_put", json11::Json::object { + { "key", cur_cfg_key }, + { "value", base64_encode(json11::Json(cur_cfg_json).dump()) }, + } } + }, + } }, + }, [this, result, new_parent, cur, cur_name](std::string err, json11::Json res) + { + if (err != "") + { + *result = { .err = EIO, .text = "Error changing parent of "+cur_name+": "+err }; + } + else if (!res["succeeded"].bool_value()) + { + *result = { .err = EAGAIN, .text = "Image "+cur_name+" was modified during change" }; + } + else if (new_parent) + { + auto new_parent_it = cli->st_cli.inode_config.find(new_parent); + std::string new_parent_name = new_parent_it != cli->st_cli.inode_config.end() + ? new_parent_it->second.name : ""; + *result = { + .text = "Parent of layer "+cur_name+" (inode "+std::to_string(INODE_NO_POOL(cur))+ + " in pool "+std::to_string(INODE_POOL(cur))+") changed to "+new_parent_name+ + " (inode "+std::to_string(INODE_NO_POOL(new_parent))+" in pool "+std::to_string(INODE_POOL(new_parent))+")", + }; + } + else + { + *result = { + .text = "Parent of layer "+cur_name+" (inode "+std::to_string(INODE_NO_POOL(cur))+ + " in pool "+std::to_string(INODE_POOL(cur))+") detached", + }; + } + waiting--; + ringloop->wakeup(); + }); +} + +void cli_tool_t::etcd_txn(json11::Json txn) +{ + waiting++; + cli->st_cli.etcd_txn_slow(txn, [this](std::string err, json11::Json res) + { + waiting--; + if (err != "") + etcd_err = { .err = EIO, .text = "Error communicating with etcd: "+err }; + else + etcd_err = { .err = 0 }; + etcd_result = res; + ringloop->wakeup(); + }); +} + +inode_config_t* cli_tool_t::get_inode_cfg(const std::string & name) +{ + for (auto & ic: cli->st_cli.inode_config) + { + if (ic.second.name == name) + { + return &ic.second; + } + } + return NULL; +} + +void cli_tool_t::parse_config(json11::Json cfg) +{ + color = !cfg["no-color"].bool_value(); + json_output = cfg["json"].bool_value(); + iodepth = cfg["iodepth"].uint64_value(); + if (!iodepth) + iodepth = 32; + parallel_osds = cfg["parallel_osds"].uint64_value(); + if (!parallel_osds) + parallel_osds = 4; + log_level = cfg["log_level"].int64_value(); + progress = cfg["progress"].uint64_value() ? true : false; + list_first = cfg["wait-list"].uint64_value() ? true : false; +} + +struct cli_result_looper_t +{ + ring_consumer_t consumer; + cli_result_t result; + std::function loop_cb; + std::function complete_cb; +}; + +void cli_tool_t::loop_and_wait(std::function loop_cb, std::function complete_cb) +{ + auto *looper = new cli_result_looper_t(); + looper->loop_cb = loop_cb; + looper->complete_cb = complete_cb; + looper->consumer.loop = [this, looper]() + { + bool done = looper->loop_cb(looper->result); + if (done) + { + ringloop->unregister_consumer(&looper->consumer); + looper->loop_cb = NULL; + looper->complete_cb(looper->result); + delete looper; + return; + } + ringloop->submit(); + }; + cli->on_ready([this, looper]() + { + ringloop->register_consumer(&looper->consumer); + ringloop->wakeup(); + }); +} diff --git a/src/cli_create.cpp b/src/cli_create.cpp index 2896c602..15f5ce34 100644 --- a/src/cli_create.cpp +++ b/src/cli_create.cpp @@ -31,8 +31,10 @@ struct image_creator_t inode_t new_parent_id = 0; inode_t new_id = 0, old_id = 0; uint64_t max_id_mod_rev = 0, cfg_mod_rev = 0, idx_mod_rev = 0; + inode_config_t new_cfg; int state = 0; + cli_result_t result; bool is_done() { @@ -43,13 +45,27 @@ struct image_creator_t { if (state >= 1) goto resume_1; + if (image_name == "") + { + // FIXME: EINVAL -> specific codes for every error + result = (cli_result_t){ .err = EINVAL, .text = "Image name is missing" }; + state = 100; + return; + } + if (image_name.find('@') != std::string::npos) + { + result = (cli_result_t){ .err = EINVAL, .text = "Image name can't contain @ character" }; + state = 100; + return; + } if (new_pool_id) { auto & pools = parent->cli->st_cli.pool_config; if (pools.find(new_pool_id) == pools.end()) { - fprintf(stderr, "Pool %u does not exist\n", new_pool_id); - exit(1); + result = (cli_result_t){ .err = ENOENT, .text = "Pool "+std::to_string(new_pool_id)+" does not exist" }; + state = 100; + return; } } else if (new_pool_name != "") @@ -64,8 +80,9 @@ struct image_creator_t } if (!new_pool_id) { - fprintf(stderr, "Pool %s does not exist\n", new_pool_name.c_str()); - exit(1); + result = (cli_result_t){ .err = ENOENT, .text = "Pool "+new_pool_name+" does not exist" }; + state = 100; + return; } } else if (parent->cli->st_cli.pool_config.size() == 1) @@ -91,8 +108,9 @@ struct image_creator_t { if (ic.second.name == image_name) { - fprintf(stderr, "Image %s already exists\n", image_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EEXIST, .text = "Image "+image_name+" already exists" }; + state = 100; + return; } if (ic.second.name == new_parent) { @@ -109,18 +127,21 @@ struct image_creator_t } if (new_parent != "" && !new_parent_id) { - fprintf(stderr, "Parent image not found\n"); - exit(1); + result = (cli_result_t){ .err = ENOENT, .text = "Parent image "+new_parent+" not found" }; + state = 100; + return; } if (!new_pool_id) { - fprintf(stderr, "Pool name or ID is missing\n"); - exit(1); + result = (cli_result_t){ .err = EINVAL, .text = "Pool name or ID is missing" }; + state = 100; + return; } if (!size) { - fprintf(stderr, "Image size is missing\n"); - exit(1); + result = (cli_result_t){ .err = EINVAL, .text = "Image size is missing" }; + state = 100; + return; } do { @@ -131,23 +152,36 @@ struct image_creator_t resume_2: if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + result = parent->etcd_err; + state = 100; + return; + } extract_next_id(parent->etcd_result["responses"][0]); attempt_create(); state = 3; resume_3: if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + result = parent->etcd_err; + state = 100; + return; + } if (!parent->etcd_result["succeeded"].bool_value() && parent->etcd_result["responses"][0]["response_range"]["kvs"].array_items().size() > 0) { - fprintf(stderr, "Image %s already exists\n", image_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EEXIST, .text = "Image "+image_name+" already exists" }; + state = 100; + return; } } while (!parent->etcd_result["succeeded"].bool_value()); - if (parent->progress) - { - printf("Image %s created\n", image_name.c_str()); - } + // Save into inode_config for library users to be able to take it from there immediately + new_cfg.mod_revision = parent->etcd_result["responses"][0]["response_put"]["header"]["revision"].uint64_value(); + parent->cli->st_cli.insert_inode_config(new_cfg); + result = (cli_result_t){ .err = 0, .text = "Image "+image_name+" created" }; state = 100; } @@ -163,14 +197,16 @@ resume_3: { if (ic.second.name == image_name+"@"+new_snap) { - fprintf(stderr, "Snapshot %s@%s already exists\n", image_name.c_str(), new_snap.c_str()); - exit(1); + result = (cli_result_t){ .err = EEXIST, .text = "Snapshot "+image_name+"@"+new_snap+" already exists" }; + state = 100; + return; } } if (new_parent != "") { - fprintf(stderr, "--parent can't be used with snapshots\n"); - exit(1); + result = (cli_result_t){ .err = EINVAL, .text = "Parent can't be specified for snapshots" }; + state = 100; + return; } do { @@ -182,8 +218,9 @@ resume_3: return; if (!old_id) { - fprintf(stderr, "Image %s does not exist\n", image_name.c_str()); - exit(1); + result = (cli_result_t){ .err = ENOENT, .text = "Image "+image_name+" does not exist" }; + state = 100; + return; } if (!new_pool_id) { @@ -195,17 +232,24 @@ resume_3: resume_4: if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + result = parent->etcd_err; + state = 100; + return; + } if (!parent->etcd_result["succeeded"].bool_value() && parent->etcd_result["responses"][0]["response_range"]["kvs"].array_items().size() > 0) { - fprintf(stderr, "Snapshot %s@%s already exists\n", image_name.c_str(), new_snap.c_str()); - exit(1); + result = (cli_result_t){ .err = EEXIST, .text = "Snapshot "+image_name+"@"+new_snap+" already exists" }; + state = 100; + return; } } while (!parent->etcd_result["succeeded"].bool_value()); - if (parent->progress) - { - printf("Snapshot %s@%s created\n", image_name.c_str(), new_snap.c_str()); - } + // Save into inode_config for library users to be able to take it from there immediately + new_cfg.mod_revision = parent->etcd_result["responses"][0]["response_put"]["header"]["revision"].uint64_value(); + parent->cli->st_cli.insert_inode_config(new_cfg); + result = (cli_result_t){ .err = 0, .text = "Snapshot "+image_name+"@"+new_snap+" created" }; state = 100; } @@ -259,6 +303,12 @@ resume_4: resume_2: if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + result = parent->etcd_err; + state = 100; + return; + } extract_next_id(parent->etcd_result["responses"][0]); old_id = 0; old_pool_id = 0; @@ -288,8 +338,9 @@ resume_2: idx_mod_rev = kv.mod_revision; if (!old_id || !old_pool_id || old_pool_id >= POOL_ID_MAX) { - fprintf(stderr, "Invalid pool or inode ID in etcd key %s\n", kv.key.c_str()); - exit(1); + result = (cli_result_t){ .err = ENOENT, .text = "Invalid pool or inode ID in etcd key "+kv.key }; + state = 100; + return; } } parent->etcd_txn(json11::Json::object { @@ -308,6 +359,12 @@ resume_2: resume_3: if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + result = parent->etcd_err; + state = 100; + return; + } { auto kv = parent->cli->st_cli.parse_etcd_kv(parent->etcd_result["responses"][0]["response_range"]["kvs"][0]); size = kv.value["size"].uint64_value(); @@ -324,7 +381,7 @@ resume_3: void attempt_create() { - inode_config_t new_cfg = { + new_cfg = { .num = INODE_WITH_POOL(new_pool_id, new_id), .name = image_name, .size = size, @@ -469,65 +526,59 @@ uint64_t parse_size(std::string size_str) uint64_t size = json11::Json(size_str).uint64_value() * mul; if (size == 0 && size_str != "0" && (size_str != "" || mul != 1)) { - fprintf(stderr, "Invalid syntax for size: %s\n", size_str.c_str()); - exit(1); + return UINT64_MAX; } return size; } -std::function cli_tool_t::start_create(json11::Json cfg) +std::function cli_tool_t::start_create(json11::Json cfg) { - json11::Json::array cmd = cfg["command"].array_items(); auto image_creator = new image_creator_t(); image_creator->parent = this; - image_creator->image_name = cmd.size() > 1 ? cmd[1].string_value() : ""; + image_creator->image_name = cfg["image"].string_value(); image_creator->new_pool_id = cfg["pool"].uint64_value(); image_creator->new_pool_name = cfg["pool"].string_value(); if (cfg["snapshot"].string_value() != "") { image_creator->new_snap = cfg["snapshot"].string_value(); } - else if (cmd[0] == "snap-create") - { - int p = image_creator->image_name.find('@'); - if (p == std::string::npos || p == image_creator->image_name.length()-1) - { - fprintf(stderr, "Please specify new snapshot name after @\n"); - exit(1); - } - image_creator->new_snap = image_creator->image_name.substr(p + 1); - image_creator->image_name = image_creator->image_name.substr(0, p); - } image_creator->new_parent = cfg["parent"].string_value(); if (cfg["size"].string_value() != "") { image_creator->size = parse_size(cfg["size"].string_value()); + if (image_creator->size == UINT64_MAX) + { + return [size = cfg["size"].string_value()](cli_result_t & result) + { + result = (cli_result_t){ .err = EINVAL, .text = "Invalid syntax for size: "+size }; + return true; + }; + } if (image_creator->size % 4096) { - fprintf(stderr, "Size should be a multiple of 4096\n"); - exit(1); + delete image_creator; + return [](cli_result_t & result) + { + result = (cli_result_t){ .err = EINVAL, .text = "Size should be a multiple of 4096" }; + return true; + }; } if (image_creator->new_snap != "") { - fprintf(stderr, "--size can't be specified for snapshots\n"); - exit(1); + delete image_creator; + return [](cli_result_t & result) + { + result = (cli_result_t){ .err = EINVAL, .text = "Size can't be specified for snapshots" }; + return true; + }; } } - if (image_creator->image_name == "") - { - fprintf(stderr, "Image name is missing\n"); - exit(1); - } - if (image_creator->image_name.find('@') != std::string::npos) - { - fprintf(stderr, "Image name can't contain @ character\n"); - exit(1); - } - return [image_creator]() + return [image_creator](cli_result_t & result) { image_creator->loop(); if (image_creator->is_done()) { + result = image_creator->result; delete image_creator; return true; } diff --git a/src/cli_df.cpp b/src/cli_df.cpp index 423bd2f2..e6e7422d 100644 --- a/src/cli_df.cpp +++ b/src/cli_df.cpp @@ -12,6 +12,7 @@ struct pool_lister_t int state = 0; json11::Json space_info; + cli_result_t result; std::map pool_stats; bool is_done() @@ -52,6 +53,12 @@ struct pool_lister_t resume_1: if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + result = parent->etcd_err; + state = 100; + return; + } space_info = parent->etcd_result; std::map osd_free; for (auto & kv_item: space_info["responses"][0]["response_range"]["kvs"].array_items()) @@ -150,10 +157,12 @@ resume_1: get_stats(); if (parent->waiting > 0) return; + if (state == 100) + return; if (parent->json_output) { // JSON output - printf("%s\n", json11::Json(to_list()).dump().c_str()); + result.data = to_list(); state = 100; return; } @@ -206,21 +215,22 @@ resume_1: : 100)+"%"; kv.second["eff_fmt"] = format_q(kv.second["space_efficiency"].number_value()*100)+"%"; } - printf("%s", print_table(to_list(), cols, parent->color).c_str()); + result.data = to_list(); + result.text = print_table(result.data, cols, parent->color); state = 100; } }; -std::function cli_tool_t::start_df(json11::Json cfg) +std::function cli_tool_t::start_df(json11::Json cfg) { - json11::Json::array cmd = cfg["command"].array_items(); auto lister = new pool_lister_t(); lister->parent = this; - return [lister]() + return [lister](cli_result_t & result) { lister->loop(); if (lister->is_done()) { + result = lister->result; delete lister; return true; } diff --git a/src/cli_flatten.cpp b/src/cli_flatten.cpp index bcd97484..262c26b8 100644 --- a/src/cli_flatten.cpp +++ b/src/cli_flatten.cpp @@ -22,12 +22,19 @@ struct snap_flattener_t std::string top_parent_name; inode_t target_id = 0; int state = 0; - std::function merger_cb; + std::function merger_cb; + cli_result_t result; void get_merge_parents() { // Get all parents of target inode_config_t *target_cfg = parent->get_inode_cfg(target_name); + if (!target_cfg) + { + result = (cli_result_t){ .err = ENOENT, .text = "Layer "+target_name+" not found" }; + state = 100; + return; + } target_id = target_cfg->num; std::vector chain_list; inode_config_t *cur = target_cfg; @@ -37,23 +44,34 @@ struct snap_flattener_t auto it = parent->cli->st_cli.inode_config.find(cur->parent_id); if (it == parent->cli->st_cli.inode_config.end()) { - fprintf(stderr, "Parent inode of layer %s (id %ld) not found\n", cur->name.c_str(), cur->parent_id); - exit(1); + result = (cli_result_t){ + .err = ENOENT, + .text = "Parent inode of layer "+cur->name+" (id "+std::to_string(cur->parent_id)+") does not exist", + .data = json11::Json::object { + { "error", "parent-not-found" }, + { "inode_id", cur->num }, + { "inode_name", cur->name }, + { "parent_id", cur->parent_id }, + }, + }; + state = 100; + return; } cur = &it->second; chain_list.push_back(cur->num); } if (cur->parent_id != 0) { - fprintf(stderr, "Layer %s has a loop in parents\n", target_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EBADF, .text = "Layer "+target_name+" has a loop in parents" }; + state = 100; + return; } top_parent_name = cur->name; } bool is_done() { - return state == 5; + return state == 100; } void loop() @@ -64,11 +82,20 @@ struct snap_flattener_t goto resume_2; else if (state == 3) goto resume_3; + if (target_name == "") + { + result = (cli_result_t){ .err = EINVAL, .text = "Layer to flatten not specified" }; + state = 100; + return; + } // Get parent layers get_merge_parents(); + if (state == 100) + return; // Start merger merger_cb = parent->start_merge(json11::Json::object { - { "command", json11::Json::array{ "merge-data", top_parent_name, target_name } }, + { "from", top_parent_name }, + { "to", target_name }, { "target", target_name }, { "delete-source", false }, { "cas", use_cas }, @@ -76,14 +103,19 @@ struct snap_flattener_t }); // Wait for it resume_1: - while (!merger_cb()) + while (!merger_cb(result)) { state = 1; return; } merger_cb = NULL; + if (result.err) + { + state = 100; + return; + } // Change parent - parent->change_parent(target_id, 0); + parent->change_parent(target_id, 0, &result); // Wait for it to complete state = 2; resume_2: @@ -92,31 +124,26 @@ resume_2: state = 3; resume_3: // Done - return; + state = 100; } }; -std::function cli_tool_t::start_flatten(json11::Json cfg) +std::function cli_tool_t::start_flatten(json11::Json cfg) { - json11::Json::array cmd = cfg["command"].array_items(); auto flattener = new snap_flattener_t(); flattener->parent = this; - flattener->target_name = cmd.size() > 1 ? cmd[1].string_value() : ""; - if (flattener->target_name == "") - { - fprintf(stderr, "Layer to flatten argument is missing\n"); - exit(1); - } + flattener->target_name = cfg["image"].string_value(); flattener->fsync_interval = cfg["fsync-interval"].uint64_value(); if (!flattener->fsync_interval) flattener->fsync_interval = 128; if (!cfg["cas"].is_null()) flattener->use_cas = cfg["cas"].uint64_value() ? 2 : 0; - return [flattener]() + return [flattener](cli_result_t & result) { flattener->loop(); if (flattener->is_done()) { + result = flattener->result; delete flattener; return true; } diff --git a/src/cli_ls.cpp b/src/cli_ls.cpp index 4e15aba2..b75484bc 100644 --- a/src/cli_ls.cpp +++ b/src/cli_ls.cpp @@ -24,6 +24,7 @@ struct image_lister_t int state = 0; std::map stats; json11::Json space_info; + cli_result_t result; bool is_done() { @@ -44,8 +45,9 @@ struct image_lister_t } if (!list_pool_id) { - fprintf(stderr, "Pool %s does not exist\n", list_pool_name.c_str()); - exit(1); + result = (cli_result_t){ .err = ENOENT, .text = "Pool "+list_pool_name+" does not exist" }; + state = 100; + return; } } for (auto & ic: parent->cli->st_cli.inode_config) @@ -116,6 +118,12 @@ struct image_lister_t resume_1: if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + result = parent->etcd_err; + state = 100; + return; + } space_info = parent->etcd_result; std::map pool_pg_real_size; for (auto & kv_item: space_info["responses"][0]["response_range"]["kvs"].array_items()) @@ -245,11 +253,13 @@ resume_1: get_stats(); if (parent->waiting > 0) return; + if (state == 100) + return; } + result.data = to_list(); if (parent->json_output) { // JSON output - printf("%s\n", json11::Json(to_list()).dump().c_str()); state = 100; return; } @@ -359,7 +369,7 @@ resume_1: kv.second["size_fmt"] = format_size(kv.second["size"].uint64_value()); kv.second["ro"] = kv.second["readonly"].bool_value() ? "RO" : "-"; } - printf("%s", print_table(to_list(), cols, parent->color).c_str()); + result.text = print_table(to_list(), cols, parent->color); state = 100; } }; @@ -546,9 +556,8 @@ back: return true; } -std::function cli_tool_t::start_ls(json11::Json cfg) +std::function cli_tool_t::start_ls(json11::Json cfg) { - json11::Json::array cmd = cfg["command"].array_items(); auto lister = new image_lister_t(); lister->parent = this; lister->list_pool_id = cfg["pool"].uint64_value(); @@ -558,15 +567,16 @@ std::function cli_tool_t::start_ls(json11::Json cfg) lister->sort_field = cfg["sort"].string_value(); lister->reverse = cfg["reverse"].bool_value(); lister->max_count = cfg["count"].uint64_value(); - for (int i = 1; i < cmd.size(); i++) + for (auto & item: cfg["names"].array_items()) { - lister->only_names.insert(cmd[i].string_value()); + lister->only_names.insert(item.string_value()); } - return [lister]() + return [lister](cli_result_t & result) { lister->loop(); if (lister->is_done()) { + result = lister->result; delete lister; return true; } diff --git a/src/cli_merge.cpp b/src/cli_merge.cpp index 51c947fc..9b333a25 100644 --- a/src/cli_merge.cpp +++ b/src/cli_merge.cpp @@ -12,6 +12,9 @@ struct snap_rw_op_t cluster_op_t op; int todo = 0; uint32_t start = 0, end = 0; + int error_code = 0; + uint64_t error_offset = 0; + bool error_read = false; }; // Layer merge is the base for multiple operations: @@ -54,17 +57,45 @@ struct snap_merger_t uint64_t last_written_offset = 0; int deleted_unsynced = 0; uint64_t processed = 0, to_process = 0; + std::string rwo_error; + + cli_result_t result; void start_merge() { + if (from_name == "" || to_name == "") + { + result = (cli_result_t){ .err = EINVAL, .text = "Beginning or end of the merge sequence is missing" }; + state = 100; + return; + } check_delete_source = delete_source || check_delete_source; inode_config_t *from_cfg = parent->get_inode_cfg(from_name); + if (!from_cfg) + { + result = (cli_result_t){ .err = ENOENT, .text = "Layer "+from_name+" not found" }; + state = 100; + return; + } inode_config_t *to_cfg = parent->get_inode_cfg(to_name); + if (!to_cfg) + { + result = (cli_result_t){ .err = ENOENT, .text = "Layer "+to_name+" not found" }; + state = 100; + return; + } inode_config_t *target_cfg = target_name == "" ? from_cfg : parent->get_inode_cfg(target_name); + if (!target_cfg) + { + result = (cli_result_t){ .err = ENOENT, .text = "Layer "+target_name+" not found" }; + state = 100; + return; + } if (to_cfg->num == from_cfg->num) { - fprintf(stderr, "Only one layer specified, nothing to merge\n"); - exit(1); + result = (cli_result_t){ .err = EINVAL, .text = "Only one layer specified, nothing to merge" }; + state = 100; + return; } // Check that to_cfg is actually a child of from_cfg and target_cfg is somewhere between them std::vector chain_list; @@ -78,8 +109,18 @@ struct snap_merger_t auto it = parent->cli->st_cli.inode_config.find(cur->parent_id); if (it == parent->cli->st_cli.inode_config.end()) { - fprintf(stderr, "Parent inode of layer %s (id %ld) not found\n", cur->name.c_str(), cur->parent_id); - exit(1); + result = (cli_result_t){ + .err = ENOENT, + .text = "Parent inode of layer "+cur->name+" (id "+std::to_string(cur->parent_id)+") does not exist", + .data = json11::Json::object { + { "error", "parent-not-found" }, + { "inode_id", cur->num }, + { "inode_name", cur->name }, + { "parent_id", cur->parent_id }, + }, + }; + state = 100; + return; } cur = &it->second; chain_list.push_back(cur->num); @@ -87,8 +128,9 @@ struct snap_merger_t } if (cur->parent_id != from_cfg->num) { - fprintf(stderr, "Layer %s is not a child of %s\n", to_name.c_str(), from_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EINVAL, .text = "Layer "+to_name+" is not a child of "+from_name }; + state = 100; + return; } chain_list.push_back(from_cfg->num); layer_block_size[from_cfg->num] = get_block_size(from_cfg->num); @@ -99,8 +141,9 @@ struct snap_merger_t } if (sources.find(target_cfg->num) == sources.end()) { - fprintf(stderr, "Layer %s is not between %s and %s\n", target_name.c_str(), to_name.c_str(), from_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EINVAL, .text = "Layer "+target_name+" is not between "+to_name+" and "+from_name }; + state = 100; + return; } target = target_cfg->num; target_rank = sources.at(target); @@ -130,14 +173,15 @@ struct snap_merger_t int parent_rank = it->second; if (parent_rank < to_rank && (parent_rank >= target_rank || check_delete_source)) { - fprintf( - stderr, "Layers at or above %s, but below %s are not allowed" - " to have other children, but %s is a child of %s\n", - (check_delete_source ? from_name.c_str() : target_name.c_str()), - to_name.c_str(), ic.second.name.c_str(), - parent->cli->st_cli.inode_config.at(ic.second.parent_id).name.c_str() - ); - exit(1); + result = (cli_result_t){ + .err = EINVAL, + .text = "Layers at or above "+(check_delete_source ? from_name : target_name)+ + ", but below "+to_name+" are not allowed to have other children, but "+ + ic.second.name+" is a child of "+ + parent->cli->st_cli.inode_config.at(ic.second.parent_id).name, + }; + state = 100; + return; } if (parent_rank >= to_rank) { @@ -152,11 +196,14 @@ struct snap_merger_t use_cas = 0; } sources.erase(target); - printf( - "Merging %ld layer(s) into target %s%s (inode %lu in pool %u)\n", - sources.size(), target_cfg->name.c_str(), - use_cas ? " online (with CAS)" : "", INODE_NO_POOL(target), INODE_POOL(target) - ); + if (parent->progress) + { + printf( + "Merging %ld layer(s) into target %s%s (inode %lu in pool %u)\n", + sources.size(), target_cfg->name.c_str(), + use_cas ? " online (with CAS)" : "", INODE_NO_POOL(target), INODE_POOL(target) + ); + } target_block_size = get_block_size(target); } @@ -179,7 +226,7 @@ struct snap_merger_t bool is_done() { - return state == 6; + return state == 100; } void continue_merge() @@ -194,8 +241,8 @@ struct snap_merger_t goto resume_4; else if (state == 5) goto resume_5; - else if (state == 6) - goto resume_6; + else if (state == 100) + goto resume_100; // Get parents and so on start_merge(); // First list lower layers @@ -253,7 +300,8 @@ struct snap_merger_t oit = merge_offsets.begin(); resume_5: // Now read, overwrite and optionally delete offsets one by one - while (in_flight < parent->iodepth*parent->parallel_osds && oit != merge_offsets.end()) + while (in_flight < parent->iodepth*parent->parallel_osds && + oit != merge_offsets.end() && !rwo_error.size()) { in_flight++; read_and_write(*oit); @@ -264,6 +312,15 @@ struct snap_merger_t printf("\rOverwriting blocks: %lu/%lu", processed, to_process); } } + if (in_flight == 0 && rwo_error.size()) + { + result = (cli_result_t){ + .err = EIO, + .text = rwo_error, + }; + state = 100; + return; + } if (in_flight > 0 || oit != merge_offsets.end()) { // Wait until overwrites finish @@ -274,9 +331,9 @@ struct snap_merger_t printf("\rOverwriting blocks: %lu/%lu\n", to_process, to_process); } // Done - printf("Done, layers from %s to %s merged into %s\n", from_name.c_str(), to_name.c_str(), target_name.c_str()); - state = 6; - resume_6: + result = { .text = "Done, layers from "+from_name+" to "+to_name+" merged into "+target_name }; + state = 100; + resume_100: return; } @@ -314,7 +371,10 @@ struct snap_merger_t if (status & INODE_LIST_DONE) { auto & name = parent->cli->st_cli.inode_config.at(src).name; - printf("Got listing of layer %s (inode %lu in pool %u)\n", name.c_str(), INODE_NO_POOL(src), INODE_POOL(src)); + if (parent->progress) + { + printf("Got listing of layer %s (inode %lu in pool %u)\n", name.c_str(), INODE_NO_POOL(src), INODE_POOL(src)); + } if (delete_source) { // Sort the inode listing @@ -396,8 +456,9 @@ struct snap_merger_t { if (op->retval != op->len) { - fprintf(stderr, "error reading target at offset %lx: %s\n", op->offset, strerror(-op->retval)); - exit(1); + rwo->error_code = -op->retval; + rwo->error_offset = op->offset; + rwo->error_read = true; } next_write(rwo); }; @@ -410,7 +471,7 @@ struct snap_merger_t // FIXME: Allow to use single write with "holes" (OSDs don't allow it yet) uint32_t gran = parent->cli->get_bs_bitmap_granularity(); uint64_t bitmap_size = target_block_size / gran; - while (rwo->end < bitmap_size) + while (rwo->end < bitmap_size && !rwo->error_code) { auto bit = ((*((uint8_t*)rwo->op.bitmap_buf + (rwo->end >> 3))) & (1 << (rwo->end & 0x7))); if (!bit) @@ -434,7 +495,7 @@ struct snap_merger_t rwo->end++; } } - if (rwo->end > rwo->start) + if (rwo->end > rwo->start && !rwo->error_code) { // write start->end rwo->todo++; @@ -473,8 +534,9 @@ struct snap_merger_t delete subop; return; } - fprintf(stderr, "error writing target at offset %lx: %s\n", subop->offset, strerror(-subop->retval)); - exit(1); + rwo->error_code = -subop->retval; + rwo->error_offset = subop->offset; + rwo->error_read = false; } // Increment CAS version rwo->op.version++; @@ -510,11 +572,12 @@ struct snap_merger_t { if (!rwo->todo) { - if (last_written_offset < rwo->op.offset+target_block_size) + if (!rwo->error_code && + last_written_offset < rwo->op.offset+target_block_size) { last_written_offset = rwo->op.offset+target_block_size; } - if (delete_source) + if (!rwo->error_code && delete_source) { deleted_unsynced++; if (deleted_unsynced >= fsync_interval) @@ -544,6 +607,13 @@ struct snap_merger_t } } free(rwo->buf); + if (rwo->error_code) + { + char buf[1024]; + snprintf(buf, 1024, "Error %s target at offset %lx: %s", + rwo->error_read ? "reading" : "writing", rwo->error_offset, strerror(rwo->error_code)); + rwo_error = std::string(buf); + } delete rwo; in_flight--; continue_merge_reent(); @@ -551,30 +621,25 @@ struct snap_merger_t } }; -std::function cli_tool_t::start_merge(json11::Json cfg) +std::function cli_tool_t::start_merge(json11::Json cfg) { - json11::Json::array cmd = cfg["command"].array_items(); auto merger = new snap_merger_t(); merger->parent = this; - merger->from_name = cmd.size() > 1 ? cmd[1].string_value() : ""; - merger->to_name = cmd.size() > 2 ? cmd[2].string_value() : ""; + merger->from_name = cfg["from"].string_value(); + merger->to_name = cfg["to"].string_value(); merger->target_name = cfg["target"].string_value(); - if (merger->from_name == "" || merger->to_name == "") - { - fprintf(stderr, "Beginning or end of the merge sequence is missing\n"); - exit(1); - } merger->delete_source = cfg["delete-source"].string_value() != ""; merger->fsync_interval = cfg["fsync-interval"].uint64_value(); if (!merger->fsync_interval) merger->fsync_interval = 128; if (!cfg["cas"].is_null()) merger->use_cas = cfg["cas"].uint64_value() ? 2 : 0; - return [merger]() + return [merger](cli_result_t & result) { merger->continue_merge_reent(); if (merger->is_done()) { + result = merger->result; delete merger; return true; } diff --git a/src/cli_modify.cpp b/src/cli_modify.cpp index aeb0e839..c721ed07 100644 --- a/src/cli_modify.cpp +++ b/src/cli_modify.cpp @@ -13,6 +13,7 @@ struct image_changer_t std::string image_name; std::string new_name; uint64_t new_size = 0; + bool force_size = false; bool set_readonly = false, set_readwrite = false, force = false; // interval between fsyncs int fsync_interval = 128; @@ -23,7 +24,8 @@ struct image_changer_t bool has_children = false; int state = 0; - std::function cb; + std::function cb; + cli_result_t result; bool is_done() { @@ -36,6 +38,18 @@ struct image_changer_t goto resume_1; else if (state == 2) goto resume_2; + if (image_name == "") + { + result = (cli_result_t){ .err = EINVAL, .text = "Image name is missing" }; + state = 100; + return; + } + if (new_size != 0 && (new_size % 4096) && !force_size) + { + result = (cli_result_t){ .err = EINVAL, .text = "Image size should be a multiple of 4096" }; + state = 100; + return; + } for (auto & ic: parent->cli->st_cli.inode_config) { if (ic.second.name == image_name) @@ -46,14 +60,16 @@ struct image_changer_t } if (new_name != "" && ic.second.name == new_name) { - fprintf(stderr, "Image %s already exists\n", new_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EEXIST, .text = "Image "+new_name+" already exists" }; + state = 100; + return; } } if (!inode_num) { - fprintf(stderr, "Image %s does not exist\n", image_name.c_str()); - exit(1); + result = (cli_result_t){ .err = ENOENT, .text = "Image "+image_name+" does not exist" }; + state = 100; + return; } for (auto & ic: parent->cli->st_cli.inode_config) { @@ -68,7 +84,7 @@ struct image_changer_t (!new_size || cfg.size == new_size) && (new_name == "" || new_name == image_name)) { - printf("No change\n"); + result = (cli_result_t){ .text = "No change" }; state = 100; return; } @@ -79,23 +95,29 @@ struct image_changer_t // Check confirmation when trimming an image with children if (has_children && !force) { - fprintf(stderr, "Image %s has children. Refusing to shrink it without --force\n", image_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EINVAL, .text = "Image "+image_name+" has children. Refusing to shrink it without --force" }; + state = 100; + return; } // Shrink the image first - cb = parent->start_rm(json11::Json::object { + cb = parent->start_rm_data(json11::Json::object { { "inode", INODE_NO_POOL(inode_num) }, { "pool", (uint64_t)INODE_POOL(inode_num) }, { "fsync-interval", fsync_interval }, - { "min-offset", new_size }, + { "min-offset", ((new_size+4095)/4096)*4096 }, }); resume_1: - while (!cb()) + while (!cb(result)) { state = 1; return; } cb = NULL; + if (result.err) + { + state = 100; + return; + } } cfg.size = new_size; } @@ -109,8 +131,9 @@ resume_1: // Check confirmation when making an image with children read-write if (has_children && !force) { - fprintf(stderr, "Image %s has children. Refusing to make it read-write without --force\n", image_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EINVAL, .text = "Image "+image_name+" has children. Refusing to make it read-write without --force" }; + state = 100; + return; } } if (new_name != "") @@ -178,34 +201,38 @@ resume_1: resume_2: if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + result = parent->etcd_err; + state = 100; + return; + } if (!parent->etcd_result["succeeded"].bool_value()) { - fprintf(stderr, "Image %s was modified by someone else, please repeat your request\n", image_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EAGAIN, .text = "Image "+image_name+" was modified by someone else, please repeat your request" }; + state = 100; + return; } - printf("Image %s modified\n", image_name.c_str()); + // Save into inode_config for library users to be able to take it from there immediately + cfg.mod_revision = parent->etcd_result["responses"][0]["response_put"]["header"]["revision"].uint64_value(); + if (new_name != "") + { + parent->cli->st_cli.inode_by_name.erase(image_name); + } + parent->cli->st_cli.insert_inode_config(cfg); + result = (cli_result_t){ .err = 0, .text = "Image "+image_name+" modified" }; state = 100; } }; -std::function cli_tool_t::start_modify(json11::Json cfg) +std::function cli_tool_t::start_modify(json11::Json cfg) { - json11::Json::array cmd = cfg["command"].array_items(); auto changer = new image_changer_t(); changer->parent = this; - changer->image_name = cmd.size() > 1 ? cmd[1].string_value() : ""; - if (changer->image_name == "") - { - fprintf(stderr, "Image name is missing\n"); - exit(1); - } + changer->image_name = cfg["image"].string_value(); changer->new_name = cfg["rename"].string_value(); - changer->new_size = parse_size(cfg["resize"].string_value()); - if (changer->new_size != 0 && (changer->new_size % 4096)) - { - fprintf(stderr, "Image size should be a multiple of 4096\n"); - exit(1); - } + changer->new_size = parse_size(cfg["resize"].as_string()); + changer->force_size = cfg["force_size"].bool_value(); changer->force = cfg["force"].bool_value(); changer->set_readonly = cfg["readonly"].bool_value(); changer->set_readwrite = cfg["readwrite"].bool_value(); @@ -213,11 +240,12 @@ std::function cli_tool_t::start_modify(json11::Json cfg) if (!changer->fsync_interval) changer->fsync_interval = 128; // FIXME Check that the image doesn't have children when shrinking - return [changer]() + return [changer](cli_result_t & result) { changer->loop(); if (changer->is_done()) { + result = changer->result; delete changer; return true; } diff --git a/src/cli_rm.cpp b/src/cli_rm.cpp index a7d1ba4f..974f0cf4 100644 --- a/src/cli_rm.cpp +++ b/src/cli_rm.cpp @@ -63,11 +63,13 @@ struct snap_remover_t inode_t new_parent = 0; int state = 0; int current_child = 0; - std::function cb; + std::function cb; + + cli_result_t result; bool is_done() { - return state == 9; + return state == 100; } void loop() @@ -88,13 +90,28 @@ struct snap_remover_t goto resume_7; else if (state == 8) goto resume_8; - else if (state == 9) - goto resume_9; + else if (state == 100) + goto resume_100; + assert(!state); + if (from_name == "") + { + result = (cli_result_t){ .err = EINVAL, .text = "Layer to remove argument is missing" }; + state = 100; + return; + } + if (to_name == "") + { + to_name = from_name; + } // Get children to merge get_merge_children(); + if (state == 100) + return; // Try to select an inode for the "inverse" optimized scenario // Read statistics from etcd to do it read_stats(); + if (state == 100) + return; state = 1; resume_1: if (parent->waiting > 0) @@ -106,42 +123,72 @@ resume_1: if (merge_children[current_child] == inverse_child) continue; start_merge_child(merge_children[current_child], merge_children[current_child]); + if (state == 100) + return; resume_2: - while (!cb()) + while (!cb(result)) { state = 2; return; } cb = NULL; - parent->change_parent(merge_children[current_child], new_parent); + if (result.err) + { + state = 100; + return; + } + parent->change_parent(merge_children[current_child], new_parent, &result); state = 3; resume_3: if (parent->waiting > 0) return; + if (result.err) + { + state = 100; + return; + } + else if (parent->progress) + printf("%s\n", result.text.c_str()); } // Merge our "inverse" child into our "inverse" parent if (inverse_child != 0) { start_merge_child(inverse_child, inverse_parent); + if (state == 100) + return; resume_4: - while (!cb()) + while (!cb(result)) { state = 4; return; } cb = NULL; + if (result.err) + { + state = 100; + return; + } // Delete "inverse" child data start_delete_source(inverse_child); + if (state == 100) + return; resume_5: - while (!cb()) + while (!cb(result)) { state = 5; return; } cb = NULL; + if (result.err) + { + state = 100; + return; + } // Delete "inverse" child metadata, rename parent over it, // and also change parent links of the previous "inverse" child rename_inverse_parent(); + if (state == 100) + return; state = 6; resume_6: if (parent->waiting > 0) @@ -154,20 +201,27 @@ resume_6: continue; start_delete_source(chain_list[current_child]); resume_7: - while (!cb()) + while (!cb(result)) { state = 7; return; } cb = NULL; + if (result.err) + { + state = 100; + return; + } delete_inode_config(chain_list[current_child]); + if (state == 100) + return; state = 8; resume_8: if (parent->waiting > 0) return; } - state = 9; -resume_9: + state = 100; +resume_100: // Done return; } @@ -176,7 +230,19 @@ resume_9: { // Get all children of from..to inode_config_t *from_cfg = parent->get_inode_cfg(from_name); + if (!from_cfg) + { + result = (cli_result_t){ .err = ENOENT, .text = "Layer "+from_name+" not found" }; + state = 100; + return; + } inode_config_t *to_cfg = parent->get_inode_cfg(to_name); + if (!to_cfg) + { + result = (cli_result_t){ .err = ENOENT, .text = "Layer "+to_name+" not found" }; + state = 100; + return; + } // Check that to_cfg is actually a child of from_cfg // FIXME de-copypaste the following piece of code with snap_merger_t inode_config_t *cur = to_cfg; @@ -186,16 +252,19 @@ resume_9: auto it = parent->cli->st_cli.inode_config.find(cur->parent_id); if (it == parent->cli->st_cli.inode_config.end()) { - fprintf(stderr, "Parent inode of layer %s (id %ld) not found\n", cur->name.c_str(), cur->parent_id); - exit(1); + char buf[1024]; + snprintf(buf, 1024, "Parent inode of layer %s (id 0x%lx) not found", cur->name.c_str(), cur->parent_id); + state = 100; + return; } cur = &it->second; chain_list.push_back(cur->num); } if (cur->num != from_cfg->num) { - fprintf(stderr, "Layer %s is not a child of %s\n", to_name.c_str(), from_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EINVAL, .text = "Layer "+to_name+" is not a child of "+from_name }; + state = 100; + return; } new_parent = from_cfg->parent_id; // Calculate ranks @@ -263,8 +332,9 @@ resume_9: parent->waiting--; if (err != "") { - fprintf(stderr, "Error reading layer statistics from etcd: %s\n", err.c_str()); - exit(1); + result = (cli_result_t){ .err = EIO, .text = "Error reading layer statistics from etcd: "+err }; + state = 100; + return; } for (auto inode_result: data["responses"].array_items()) { @@ -275,14 +345,16 @@ resume_9: sscanf(kv.key.c_str() + parent->cli->st_cli.etcd_prefix.length()+13, "%u/%lu%c", &pool_id, &inode, &null_byte); if (!inode || null_byte != 0) { - fprintf(stderr, "Bad key returned from etcd: %s\n", kv.key.c_str()); - exit(1); + result = (cli_result_t){ .err = EIO, .text = "Bad key returned from etcd: "+kv.key }; + state = 100; + return; } auto pool_cfg_it = parent->cli->st_cli.pool_config.find(pool_id); if (pool_cfg_it == parent->cli->st_cli.pool_config.end()) { - fprintf(stderr, "Pool %u does not exist\n", pool_id); - exit(1); + result = (cli_result_t){ .err = ENOENT, .text = "Pool "+std::to_string(pool_id)+" does not exist" }; + state = 100; + return; } inode = INODE_WITH_POOL(pool_id, inode); auto & pool_cfg = pool_cfg_it->second; @@ -324,14 +396,20 @@ resume_9: auto child_it = parent->cli->st_cli.inode_config.find(inverse_child); if (child_it == parent->cli->st_cli.inode_config.end()) { - fprintf(stderr, "Inode %ld disappeared\n", inverse_child); - exit(1); + char buf[1024]; + snprintf(buf, 1024, "Inode 0x%lx disappeared", inverse_child); + result = (cli_result_t){ .err = EIO, .text = std::string(buf) }; + state = 100; + return; } auto target_it = parent->cli->st_cli.inode_config.find(inverse_parent); if (target_it == parent->cli->st_cli.inode_config.end()) { - fprintf(stderr, "Inode %ld disappeared\n", inverse_parent); - exit(1); + char buf[1024]; + snprintf(buf, 1024, "Inode 0x%lx disappeared", inverse_parent); + result = (cli_result_t){ .err = EIO, .text = std::string(buf) }; + state = 100; + return; } inode_config_t *child_cfg = &child_it->second; inode_config_t *target_cfg = &target_it->second; @@ -422,18 +500,22 @@ resume_9: parent->waiting--; if (err != "") { - fprintf(stderr, "Error renaming %s to %s: %s\n", target_name.c_str(), child_name.c_str(), err.c_str()); - exit(1); + result = (cli_result_t){ .err = EIO, .text = "Error renaming "+target_name+" to "+child_name+": "+err }; + state = 100; + return; } if (!res["succeeded"].bool_value()) { - fprintf( - stderr, "Parent (%s), child (%s), or one of its children" - " configuration was modified during rename\n", target_name.c_str(), child_name.c_str() - ); - exit(1); + result = (cli_result_t){ + .err = EAGAIN, + .text = "Parent ("+target_name+"), child ("+child_name+"), or one of its children" + " configuration was modified during rename", + }; + state = 100; + return; } - printf("Layer %s renamed to %s\n", target_name.c_str(), child_name.c_str()); + if (parent->progress) + printf("Layer %s renamed to %s\n", target_name.c_str(), child_name.c_str()); parent->ringloop->wakeup(); }); } @@ -443,8 +525,11 @@ resume_9: auto cur_cfg_it = parent->cli->st_cli.inode_config.find(cur); if (cur_cfg_it == parent->cli->st_cli.inode_config.end()) { - fprintf(stderr, "Inode 0x%lx disappeared\n", cur); - exit(1); + char buf[1024]; + snprintf(buf, 1024, "Inode 0x%lx disappeared", cur); + result = (cli_result_t){ .err = EIO, .text = std::string(buf) }; + state = 100; + return; } inode_config_t *cur_cfg = &cur_cfg_it->second; std::string cur_name = cur_cfg->name; @@ -475,20 +560,26 @@ resume_9: } }, }, } }, - }, [this, cur_name](std::string err, json11::Json res) + }, [this, cur, cur_name](std::string err, json11::Json res) { parent->waiting--; if (err != "") { - fprintf(stderr, "Error deleting %s: %s\n", cur_name.c_str(), err.c_str()); - exit(1); + result = (cli_result_t){ .err = EIO, .text = "Error deleting "+cur_name+": "+err }; + state = 100; + return; } if (!res["succeeded"].bool_value()) { - fprintf(stderr, "Layer %s configuration was modified during deletion\n", cur_name.c_str()); - exit(1); + result = (cli_result_t){ .err = EAGAIN, .text = "Layer "+cur_name+" was modified during deletion" }; + state = 100; + return; } - printf("Layer %s deleted\n", cur_name.c_str()); + // Modify inode_config for library users to be able to take it from there immediately + parent->cli->st_cli.inode_by_name.erase(cur_name); + parent->cli->st_cli.inode_config.erase(cur); + if (parent->progress) + printf("Layer %s deleted\n", cur_name.c_str()); parent->ringloop->wakeup(); }); } @@ -498,17 +589,24 @@ resume_9: auto child_it = parent->cli->st_cli.inode_config.find(child_inode); if (child_it == parent->cli->st_cli.inode_config.end()) { - fprintf(stderr, "Inode %ld disappeared\n", child_inode); - exit(1); + char buf[1024]; + snprintf(buf, 1024, "Inode 0x%lx disappeared", child_inode); + result = (cli_result_t){ .err = EIO, .text = std::string(buf) }; + state = 100; + return; } auto target_it = parent->cli->st_cli.inode_config.find(target_inode); if (target_it == parent->cli->st_cli.inode_config.end()) { - fprintf(stderr, "Inode %ld disappeared\n", target_inode); - exit(1); + char buf[1024]; + snprintf(buf, 1024, "Inode 0x%lx disappeared", target_inode); + result = (cli_result_t){ .err = EIO, .text = std::string(buf) }; + state = 100; + return; } cb = parent->start_merge(json11::Json::object { - { "command", json11::Json::array{ "merge-data", from_name, child_it->second.name } }, + { "from", from_name }, + { "to", child_it->second.name }, { "target", target_it->second.name }, { "delete-source", false }, { "cas", use_cas }, @@ -521,10 +619,13 @@ resume_9: auto source = parent->cli->st_cli.inode_config.find(inode); if (source == parent->cli->st_cli.inode_config.end()) { - fprintf(stderr, "Inode %ld disappeared\n", inode); - exit(1); + char buf[1024]; + snprintf(buf, 1024, "Inode 0x%lx disappeared", inode); + result = (cli_result_t){ .err = EIO, .text = std::string(buf) }; + state = 100; + return; } - cb = parent->start_rm(json11::Json::object { + cb = parent->start_rm_data(json11::Json::object { { "inode", inode }, { "pool", (uint64_t)INODE_POOL(inode) }, { "fsync-interval", fsync_interval }, @@ -532,22 +633,12 @@ resume_9: } }; -std::function cli_tool_t::start_snap_rm(json11::Json cfg) +std::function cli_tool_t::start_rm(json11::Json cfg) { - json11::Json::array cmd = cfg["command"].array_items(); auto snap_remover = new snap_remover_t(); snap_remover->parent = this; - snap_remover->from_name = cmd.size() > 1 ? cmd[1].string_value() : ""; - snap_remover->to_name = cmd.size() > 2 ? cmd[2].string_value() : ""; - if (snap_remover->from_name == "") - { - fprintf(stderr, "Layer to remove argument is missing\n"); - exit(1); - } - if (snap_remover->to_name == "") - { - snap_remover->to_name = snap_remover->from_name; - } + snap_remover->from_name = cfg["from"].string_value(); + snap_remover->to_name = cfg["to"].string_value(); snap_remover->fsync_interval = cfg["fsync-interval"].uint64_value(); if (!snap_remover->fsync_interval) snap_remover->fsync_interval = 128; @@ -555,11 +646,12 @@ std::function cli_tool_t::start_snap_rm(json11::Json cfg) snap_remover->use_cas = cfg["cas"].uint64_value() ? 2 : 0; if (!cfg["writers_stopped"].is_null()) snap_remover->writers_stopped = true; - return [snap_remover]() + return [snap_remover](cli_result_t & result) { snap_remover->loop(); if (snap_remover->is_done()) { + result = snap_remover->result; delete snap_remover; return true; } diff --git a/src/cli_rm_data.cpp b/src/cli_rm_data.cpp index 5cff8fd7..a61e797b 100644 --- a/src/cli_rm_data.cpp +++ b/src/cli_rm_data.cpp @@ -32,6 +32,9 @@ struct rm_inode_t uint64_t pgs_to_list = 0; bool lists_done = false; int state = 0; + int error_count = 0; + + cli_result_t result; void start_delete() { @@ -74,8 +77,13 @@ struct rm_inode_t }); if (!lister) { - fprintf(stderr, "Failed to list inode %lu from pool %u objects\n", INODE_NO_POOL(inode), INODE_POOL(inode)); - exit(1); + result = (cli_result_t){ + .err = EIO, + .text = "Failed to list objects of inode "+std::to_string(INODE_NO_POOL(inode))+ + " from pool "+std::to_string(INODE_POOL(inode)), + }; + state = 100; + return; } pgs_to_list = parent->cli->list_pg_count(lister); parent->cli->list_inode_next(lister, parent->parallel_osds); @@ -118,6 +126,7 @@ struct rm_inode_t fprintf(stderr, "Failed to remove object %lx:%lx from PG %u (OSD %lu) (retval=%ld)\n", op->req.rw.inode, op->req.rw.offset, cur_list->pg_num, cur_list->rm_osd_num, op->reply.hdr.retval); + error_count++; } delete op; cur_list->obj_done++; @@ -161,31 +170,43 @@ struct rm_inode_t } if (lists_done && !lists.size()) { - printf("Done, inode %lu in pool %u data removed\n", INODE_NO_POOL(inode), pool_id); - state = 2; + result = (cli_result_t){ + .err = error_count > 0 ? EIO : 0, + .text = error_count > 0 ? "Some blocks were not removed" : ( + "Done, inode "+std::to_string(INODE_NO_POOL(inode))+" from pool "+ + std::to_string(pool_id)+" removed"), + }; + state = 100; } } - bool loop() + bool is_done() { - if (state == 0) + return state == 100; + } + + void loop() + { + if (state == 1) + goto resume_1; + if (state == 100) + return; + if (!pool_id) { - start_delete(); - state = 1; + result = (cli_result_t){ .err = EINVAL, .text = "Pool is not specified" }; + state = 100; + return; } - else if (state == 1) - { - continue_delete(); - } - else if (state == 2) - { - return true; - } - return false; + start_delete(); + if (state == 100) + return; + state = 1; + resume_1: + continue_delete(); } }; -std::function cli_tool_t::start_rm(json11::Json cfg) +std::function cli_tool_t::start_rm_data(json11::Json cfg) { auto remover = new rm_inode_t(); remover->parent = this; @@ -196,16 +217,13 @@ std::function cli_tool_t::start_rm(json11::Json cfg) remover->inode = (remover->inode & (((uint64_t)1 << (64-POOL_ID_BITS)) - 1)) | (((uint64_t)remover->pool_id) << (64-POOL_ID_BITS)); } remover->pool_id = INODE_POOL(remover->inode); - if (!remover->pool_id) - { - fprintf(stderr, "pool is missing\n"); - exit(1); - } remover->min_offset = cfg["min-offset"].uint64_value(); - return [remover]() + return [remover](cli_result_t & result) { - if (remover->loop()) + remover->loop(); + if (remover->is_done()) { + result = remover->result; delete remover; return true; } diff --git a/src/cli_simple_offsets.cpp b/src/cli_simple_offsets.cpp index 6751f506..9ce5988b 100644 --- a/src/cli_simple_offsets.cpp +++ b/src/cli_simple_offsets.cpp @@ -11,9 +11,9 @@ #include // Calculate offsets for a block device and print OSD command line parameters -std::function cli_tool_t::simple_offsets(json11::Json cfg) +std::function cli_tool_t::simple_offsets(json11::Json cfg) { - std::string device = cfg["command"][1].string_value(); + std::string device = cfg["device"].string_value(); uint64_t object_size = parse_size(cfg["object_size"].string_value()); uint64_t bitmap_granularity = parse_size(cfg["bitmap_granularity"].string_value()); uint64_t journal_size = parse_size(cfg["journal_size"].string_value()); diff --git a/src/cli_status.cpp b/src/cli_status.cpp index 0fd8a5df..e7040770 100644 --- a/src/cli_status.cpp +++ b/src/cli_status.cpp @@ -83,6 +83,12 @@ resume_1: resume_2: if (parent->waiting > 0) return; + if (parent->etcd_err.err) + { + fprintf(stderr, "%s\n", parent->etcd_err.text.c_str()); + state = 100; + return; + } mon_members = parent->etcd_result["responses"][0]["response_range"]["kvs"].array_items(); osd_stats = parent->etcd_result["responses"][1]["response_range"]["kvs"].array_items(); if (parent->etcd_result["responses"][2]["response_range"]["kvs"].array_items().size() > 0) @@ -277,16 +283,16 @@ resume_2: } }; -std::function cli_tool_t::start_status(json11::Json cfg) +std::function cli_tool_t::start_status(json11::Json cfg) { - json11::Json::array cmd = cfg["command"].array_items(); auto printer = new status_printer_t(); printer->parent = this; - return [printer]() + return [printer](cli_result_t & result) { printer->loop(); if (printer->is_done()) { + result = { .err = 0 }; delete printer; return true; } diff --git a/src/etcd_state_client.cpp b/src/etcd_state_client.cpp index af726a15..8b54c91e 100644 --- a/src/etcd_state_client.cpp +++ b/src/etcd_state_client.cpp @@ -975,26 +975,30 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv) else parent_inode_num |= parent_pool_id << (64-POOL_ID_BITS); } - inode_config_t cfg = (inode_config_t){ + insert_inode_config((inode_config_t){ .num = inode_num, .name = value["name"].string_value(), .size = value["size"].uint64_value(), .parent_id = parent_inode_num, .readonly = value["readonly"].bool_value(), .mod_revision = kv.mod_revision, - }; - this->inode_config[inode_num] = cfg; - if (cfg.name != "") - { - this->inode_by_name[cfg.name] = inode_num; - for (auto w: watches) - { - if (w->name == value["name"].string_value()) - { - w->cfg = cfg; - } - } - } + }); + } + } + } +} + +void etcd_state_client_t::insert_inode_config(const inode_config_t & cfg) +{ + this->inode_config[cfg.num] = cfg; + if (cfg.name != "") + { + this->inode_by_name[cfg.name] = cfg.num; + for (auto w: watches) + { + if (w->name == cfg.name) + { + w->cfg = cfg; } } } diff --git a/src/etcd_state_client.h b/src/etcd_state_client.h index 6ebc2ae7..9c07bf4d 100644 --- a/src/etcd_state_client.h +++ b/src/etcd_state_client.h @@ -122,6 +122,7 @@ public: void load_pgs(); void parse_state(const etcd_kv_t & kv); void parse_config(const json11::Json & config); + void insert_inode_config(const inode_config_t & cfg); inode_watch_t* watch_inode(std::string name); void close_watch(inode_watch_t* watch); int address_count();