Prepare CLI functions for integration - return results and errors
parent
98e3528a14
commit
d7e64e6ea1
|
@ -154,7 +154,7 @@ target_link_libraries(vitastor-nbd
|
||||||
|
|
||||||
# vitastor-cli
|
# vitastor-cli
|
||||||
add_executable(vitastor-cli
|
add_executable(vitastor-cli
|
||||||
cli.cpp cli_alloc_osd.cpp cli_simple_offsets.cpp cli_status.cpp cli_df.cpp
|
cli.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
|
cli_ls.cpp cli_create.cpp cli_modify.cpp cli_flatten.cpp cli_merge.cpp cli_rm_data.cpp cli_rm.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(vitastor-cli
|
target_link_libraries(vitastor-cli
|
||||||
|
|
195
src/cli.cpp
195
src/cli.cpp
|
@ -2,8 +2,7 @@
|
||||||
// License: VNPL-1.1 (see README.md for details)
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI tool
|
* CLI tool and also a library for administrative tasks
|
||||||
* Currently can (a) remove inodes and (b) merge snapshot/clone layers
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -17,7 +16,9 @@
|
||||||
|
|
||||||
static const char *exe_name = NULL;
|
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::object cfg;
|
||||||
json11::Json::array cmd;
|
json11::Json::array cmd;
|
||||||
|
@ -79,7 +80,7 @@ json11::Json::object cli_tool_t::parse_args(int narg, const char *args[])
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_tool_t::help()
|
static void help()
|
||||||
{
|
{
|
||||||
printf(
|
printf(
|
||||||
"Vitastor command-line tool\n"
|
"Vitastor command-line tool\n"
|
||||||
|
@ -164,224 +165,126 @@ void cli_tool_t::help()
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_tool_t::change_parent(inode_t cur, inode_t new_parent)
|
static int run(cli_tool_t *p, json11::Json cfg)
|
||||||
{
|
|
||||||
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 : "<unknown>";
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
|
p->parse_config(cfg);
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
|
std::function<bool(cli_result_t &)> action_cb;
|
||||||
if (!cmd.size())
|
if (!cmd.size())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "command is missing\n");
|
fprintf(stderr, "command is missing\n");
|
||||||
exit(1);
|
return EINVAL;
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "status")
|
else if (cmd[0] == "status")
|
||||||
{
|
{
|
||||||
// Show cluster status
|
// Show cluster status
|
||||||
action_cb = start_status(cfg);
|
action_cb = p->start_status(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "df")
|
else if (cmd[0] == "df")
|
||||||
{
|
{
|
||||||
// Show pool space stats
|
// Show pool space stats
|
||||||
action_cb = start_df(cfg);
|
action_cb = p->start_df(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "ls")
|
else if (cmd[0] == "ls")
|
||||||
{
|
{
|
||||||
// List images
|
// List images
|
||||||
action_cb = start_ls(cfg);
|
action_cb = p->start_ls(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "create" || cmd[0] == "snap-create")
|
else if (cmd[0] == "create" || cmd[0] == "snap-create")
|
||||||
{
|
{
|
||||||
// Create image/snapshot
|
// Create image/snapshot
|
||||||
action_cb = start_create(cfg);
|
action_cb = p->start_create(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "modify")
|
else if (cmd[0] == "modify")
|
||||||
{
|
{
|
||||||
// Modify image
|
// Modify image
|
||||||
action_cb = start_modify(cfg);
|
action_cb = p->start_modify(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "rm-data")
|
else if (cmd[0] == "rm-data")
|
||||||
{
|
{
|
||||||
// Delete inode data
|
// Delete inode data
|
||||||
action_cb = start_rm(cfg);
|
action_cb = p->start_rm(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "merge-data")
|
else if (cmd[0] == "merge-data")
|
||||||
{
|
{
|
||||||
// Merge layer data without affecting metadata
|
// Merge layer data without affecting metadata
|
||||||
action_cb = start_merge(cfg);
|
action_cb = p->start_merge(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "flatten")
|
else if (cmd[0] == "flatten")
|
||||||
{
|
{
|
||||||
// Merge layer data without affecting metadata
|
// Merge layer data without affecting metadata
|
||||||
action_cb = start_flatten(cfg);
|
action_cb = p->start_flatten(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "rm")
|
else if (cmd[0] == "rm")
|
||||||
{
|
{
|
||||||
// Remove multiple snapshots and rebase their children
|
// Remove multiple snapshots and rebase their children
|
||||||
action_cb = start_snap_rm(cfg);
|
action_cb = p->start_snap_rm(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "alloc-osd")
|
else if (cmd[0] == "alloc-osd")
|
||||||
{
|
{
|
||||||
// Allocate a new OSD number
|
// Allocate a new OSD number
|
||||||
action_cb = start_alloc_osd(cfg);
|
action_cb = p->start_alloc_osd(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "simple-offsets")
|
else if (cmd[0] == "simple-offsets")
|
||||||
{
|
{
|
||||||
// Calculate offsets for simple & stupid OSD deployment without superblock
|
// Calculate offsets for simple & stupid OSD deployment without superblock
|
||||||
action_cb = simple_offsets(cfg);
|
action_cb = p->simple_offsets(cfg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, "unknown command: %s\n", cmd[0].string_value().c_str());
|
fprintf(stderr, "unknown command: %s\n", cmd[0].string_value().c_str());
|
||||||
exit(1);
|
return EINVAL;
|
||||||
}
|
}
|
||||||
if (action_cb == NULL)
|
if (action_cb == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
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
|
// Create client
|
||||||
ringloop = new ring_loop_t(512);
|
p->ringloop = new ring_loop_t(512);
|
||||||
epmgr = new epoll_manager_t(ringloop);
|
p->epmgr = new epoll_manager_t(p->ringloop);
|
||||||
cli = new cluster_client_t(ringloop, epmgr->tfd, cfg);
|
p->cli = new cluster_client_t(p->ringloop, p->epmgr->tfd, cfg);
|
||||||
// Smaller timeout by default for more interactiveness
|
// Smaller timeout by default for more interactiveness
|
||||||
cli->st_cli.etcd_slow_timeout = cli->st_cli.etcd_quick_timeout;
|
p->cli->st_cli.etcd_slow_timeout = p->cli->st_cli.etcd_quick_timeout;
|
||||||
cli->on_ready([this]()
|
ring_consumer_t consumer;
|
||||||
|
cli_result_t result;
|
||||||
|
p->cli->on_ready([&]()
|
||||||
{
|
{
|
||||||
// Initialize job
|
// Initialize job
|
||||||
consumer.loop = [this]()
|
consumer.loop = [&]()
|
||||||
{
|
{
|
||||||
if (action_cb != NULL)
|
if (action_cb != NULL)
|
||||||
{
|
{
|
||||||
bool done = action_cb();
|
bool done = action_cb(result);
|
||||||
if (done)
|
if (done)
|
||||||
{
|
{
|
||||||
action_cb = NULL;
|
action_cb = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ringloop->submit();
|
p->ringloop->submit();
|
||||||
};
|
};
|
||||||
ringloop->register_consumer(&consumer);
|
p->ringloop->register_consumer(&consumer);
|
||||||
consumer.loop();
|
consumer.loop();
|
||||||
});
|
});
|
||||||
// Loop until it completes
|
// Loop until it completes
|
||||||
while (action_cb != NULL)
|
while (action_cb != NULL)
|
||||||
{
|
{
|
||||||
ringloop->loop();
|
p->ringloop->loop();
|
||||||
if (action_cb != NULL)
|
if (action_cb != NULL)
|
||||||
ringloop->wait();
|
p->ringloop->wait();
|
||||||
|
}
|
||||||
|
// Print result
|
||||||
|
if (result.text != "")
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", result.text.c_str());
|
||||||
}
|
}
|
||||||
// Destroy the client
|
// Destroy the client
|
||||||
delete cli;
|
delete p->cli;
|
||||||
delete epmgr;
|
delete p->epmgr;
|
||||||
delete ringloop;
|
delete p->ringloop;
|
||||||
cli = NULL;
|
p->cli = NULL;
|
||||||
epmgr = NULL;
|
p->epmgr = NULL;
|
||||||
ringloop = NULL;
|
p->ringloop = NULL;
|
||||||
|
return result.err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int narg, const char *args[])
|
int main(int narg, const char *args[])
|
||||||
|
@ -390,7 +293,7 @@ int main(int narg, const char *args[])
|
||||||
setvbuf(stderr, NULL, _IONBF, 0);
|
setvbuf(stderr, NULL, _IONBF, 0);
|
||||||
exe_name = args[0];
|
exe_name = args[0];
|
||||||
cli_tool_t *p = new cli_tool_t();
|
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;
|
delete p;
|
||||||
return 0;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
36
src/cli.h
36
src/cli.h
|
@ -19,6 +19,13 @@ class epoll_manager_t;
|
||||||
class cluster_client_t;
|
class cluster_client_t;
|
||||||
struct inode_config_t;
|
struct inode_config_t;
|
||||||
|
|
||||||
|
struct cli_result_t
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
std::string text;
|
||||||
|
json11::Json data;
|
||||||
|
};
|
||||||
|
|
||||||
class cli_tool_t
|
class cli_tool_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -35,33 +42,28 @@ public:
|
||||||
|
|
||||||
int waiting = 0;
|
int waiting = 0;
|
||||||
json11::Json etcd_result;
|
json11::Json etcd_result;
|
||||||
ring_consumer_t consumer;
|
|
||||||
std::function<bool(void)> 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);
|
||||||
inode_config_t* get_inode_cfg(const std::string & name);
|
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 rm_inode_t;
|
||||||
friend struct snap_merger_t;
|
friend struct snap_merger_t;
|
||||||
friend struct snap_flattener_t;
|
friend struct snap_flattener_t;
|
||||||
friend struct snap_remover_t;
|
friend struct snap_remover_t;
|
||||||
|
|
||||||
std::function<bool(void)> start_status(json11::Json cfg);
|
std::function<bool(cli_result_t &)> start_status(json11::Json);
|
||||||
std::function<bool(void)> start_df(json11::Json);
|
std::function<bool(cli_result_t &)> start_df(json11::Json);
|
||||||
std::function<bool(void)> start_ls(json11::Json);
|
std::function<bool(cli_result_t &)> start_ls(json11::Json);
|
||||||
std::function<bool(void)> start_create(json11::Json);
|
std::function<bool(cli_result_t &)> start_create(json11::Json);
|
||||||
std::function<bool(void)> start_modify(json11::Json);
|
std::function<bool(cli_result_t &)> start_modify(json11::Json);
|
||||||
std::function<bool(void)> start_rm(json11::Json);
|
std::function<bool(cli_result_t &)> start_rm(json11::Json);
|
||||||
std::function<bool(void)> start_merge(json11::Json);
|
std::function<bool(cli_result_t &)> start_merge(json11::Json);
|
||||||
std::function<bool(void)> start_flatten(json11::Json);
|
std::function<bool(cli_result_t &)> start_flatten(json11::Json);
|
||||||
std::function<bool(void)> start_snap_rm(json11::Json);
|
std::function<bool(cli_result_t &)> start_snap_rm(json11::Json);
|
||||||
std::function<bool(void)> start_alloc_osd(json11::Json cfg, uint64_t *out = NULL);
|
std::function<bool(cli_result_t &)> start_alloc_osd(json11::Json cfg);
|
||||||
std::function<bool(void)> simple_offsets(json11::Json cfg);
|
std::function<bool(cli_result_t &)> simple_offsets(json11::Json cfg);
|
||||||
|
|
||||||
void etcd_txn(json11::Json txn);
|
void etcd_txn(json11::Json txn);
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,20 +102,20 @@ struct alloc_osd_t
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_alloc_osd(json11::Json cfg, uint64_t *out)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_alloc_osd(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
auto alloc_osd = new alloc_osd_t();
|
auto alloc_osd = new alloc_osd_t();
|
||||||
alloc_osd->parent = this;
|
alloc_osd->parent = this;
|
||||||
return [alloc_osd, out]()
|
return [alloc_osd](cli_result_t & result)
|
||||||
{
|
{
|
||||||
alloc_osd->loop();
|
alloc_osd->loop();
|
||||||
if (alloc_osd->is_done())
|
if (alloc_osd->is_done())
|
||||||
{
|
{
|
||||||
if (out)
|
result = (cli_result_t){
|
||||||
*out = alloc_osd->new_id;
|
.text = std::to_string(alloc_osd->new_id),
|
||||||
else if (alloc_osd->new_id)
|
.data = json11::Json(alloc_osd->new_id),
|
||||||
printf("%lu\n", alloc_osd->new_id);
|
};
|
||||||
delete alloc_osd;
|
delete alloc_osd;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
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 : "<unknown>";
|
||||||
|
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::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;
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ struct image_creator_t
|
||||||
uint64_t max_id_mod_rev = 0, cfg_mod_rev = 0, idx_mod_rev = 0;
|
uint64_t max_id_mod_rev = 0, cfg_mod_rev = 0, idx_mod_rev = 0;
|
||||||
|
|
||||||
int state = 0;
|
int state = 0;
|
||||||
|
cli_result_t result;
|
||||||
|
|
||||||
bool is_done()
|
bool is_done()
|
||||||
{
|
{
|
||||||
|
@ -43,13 +44,27 @@ struct image_creator_t
|
||||||
{
|
{
|
||||||
if (state >= 1)
|
if (state >= 1)
|
||||||
goto resume_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)
|
if (new_pool_id)
|
||||||
{
|
{
|
||||||
auto & pools = parent->cli->st_cli.pool_config;
|
auto & pools = parent->cli->st_cli.pool_config;
|
||||||
if (pools.find(new_pool_id) == pools.end())
|
if (pools.find(new_pool_id) == pools.end())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Pool %u does not exist\n", new_pool_id);
|
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+std::to_string(new_pool_id)+" does not exist" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (new_pool_name != "")
|
else if (new_pool_name != "")
|
||||||
|
@ -64,8 +79,9 @@ struct image_creator_t
|
||||||
}
|
}
|
||||||
if (!new_pool_id)
|
if (!new_pool_id)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Pool %s does not exist\n", new_pool_name.c_str());
|
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+new_pool_name+" does not exist" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (parent->cli->st_cli.pool_config.size() == 1)
|
else if (parent->cli->st_cli.pool_config.size() == 1)
|
||||||
|
@ -91,8 +107,9 @@ struct image_creator_t
|
||||||
{
|
{
|
||||||
if (ic.second.name == image_name)
|
if (ic.second.name == image_name)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Image %s already exists\n", image_name.c_str());
|
result = (cli_result_t){ .err = EEXIST, .text = "Image "+image_name+" already exists" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (ic.second.name == new_parent)
|
if (ic.second.name == new_parent)
|
||||||
{
|
{
|
||||||
|
@ -109,18 +126,21 @@ struct image_creator_t
|
||||||
}
|
}
|
||||||
if (new_parent != "" && !new_parent_id)
|
if (new_parent != "" && !new_parent_id)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Parent image not found\n");
|
result = (cli_result_t){ .err = ENOENT, .text = "Parent image "+new_parent+" not found" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!new_pool_id)
|
if (!new_pool_id)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Pool name or ID is missing\n");
|
result = (cli_result_t){ .err = EINVAL, .text = "Pool name or ID is missing" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!size)
|
if (!size)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Image size is missing\n");
|
result = (cli_result_t){ .err = EINVAL, .text = "Image size is missing" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -140,14 +160,12 @@ resume_3:
|
||||||
if (!parent->etcd_result["succeeded"].bool_value() &&
|
if (!parent->etcd_result["succeeded"].bool_value() &&
|
||||||
parent->etcd_result["responses"][0]["response_range"]["kvs"].array_items().size() > 0)
|
parent->etcd_result["responses"][0]["response_range"]["kvs"].array_items().size() > 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Image %s already exists\n", image_name.c_str());
|
result = (cli_result_t){ .err = EEXIST, .text = "Image "+image_name+" already exists" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} while (!parent->etcd_result["succeeded"].bool_value());
|
} while (!parent->etcd_result["succeeded"].bool_value());
|
||||||
if (parent->progress)
|
result = (cli_result_t){ .err = 0, .text = "Image "+image_name+" created" };
|
||||||
{
|
|
||||||
printf("Image %s created\n", image_name.c_str());
|
|
||||||
}
|
|
||||||
state = 100;
|
state = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,14 +181,16 @@ resume_3:
|
||||||
{
|
{
|
||||||
if (ic.second.name == image_name+"@"+new_snap)
|
if (ic.second.name == image_name+"@"+new_snap)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Snapshot %s@%s already exists\n", image_name.c_str(), new_snap.c_str());
|
result = (cli_result_t){ .err = EEXIST, .text = "Snapshot "+image_name+"@"+new_snap+" already exists" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (new_parent != "")
|
if (new_parent != "")
|
||||||
{
|
{
|
||||||
fprintf(stderr, "--parent can't be used with snapshots\n");
|
result = (cli_result_t){ .err = EINVAL, .text = "Parent can't be specified for snapshots" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -182,8 +202,9 @@ resume_3:
|
||||||
return;
|
return;
|
||||||
if (!old_id)
|
if (!old_id)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Image %s does not exist\n", image_name.c_str());
|
result = (cli_result_t){ .err = ENOENT, .text = "Image "+image_name+" does not exist" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!new_pool_id)
|
if (!new_pool_id)
|
||||||
{
|
{
|
||||||
|
@ -198,14 +219,12 @@ resume_4:
|
||||||
if (!parent->etcd_result["succeeded"].bool_value() &&
|
if (!parent->etcd_result["succeeded"].bool_value() &&
|
||||||
parent->etcd_result["responses"][0]["response_range"]["kvs"].array_items().size() > 0)
|
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());
|
result = (cli_result_t){ .err = EEXIST, .text = "Snapshot "+image_name+"@"+new_snap+" already exists" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} while (!parent->etcd_result["succeeded"].bool_value());
|
} while (!parent->etcd_result["succeeded"].bool_value());
|
||||||
if (parent->progress)
|
result = (cli_result_t){ .err = 0, .text = "Snapshot "+image_name+"@"+new_snap+" created" };
|
||||||
{
|
|
||||||
printf("Snapshot %s@%s created\n", image_name.c_str(), new_snap.c_str());
|
|
||||||
}
|
|
||||||
state = 100;
|
state = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,8 +307,9 @@ resume_2:
|
||||||
idx_mod_rev = kv.mod_revision;
|
idx_mod_rev = kv.mod_revision;
|
||||||
if (!old_id || !old_pool_id || old_pool_id >= POOL_ID_MAX)
|
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());
|
result = (cli_result_t){ .err = ENOENT, .text = "Invalid pool or inode ID in etcd key "+kv.key };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parent->etcd_txn(json11::Json::object {
|
parent->etcd_txn(json11::Json::object {
|
||||||
|
@ -469,13 +489,12 @@ uint64_t parse_size(std::string size_str)
|
||||||
uint64_t size = json11::Json(size_str).uint64_value() * mul;
|
uint64_t size = json11::Json(size_str).uint64_value() * mul;
|
||||||
if (size == 0 && size_str != "0" && (size_str != "" || mul != 1))
|
if (size == 0 && size_str != "0" && (size_str != "" || mul != 1))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Invalid syntax for size: %s\n", size_str.c_str());
|
return UINT64_MAX;
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_create(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
auto image_creator = new image_creator_t();
|
auto image_creator = new image_creator_t();
|
||||||
|
@ -492,8 +511,12 @@ std::function<bool(void)> cli_tool_t::start_create(json11::Json cfg)
|
||||||
int p = image_creator->image_name.find('@');
|
int p = image_creator->image_name.find('@');
|
||||||
if (p == std::string::npos || p == image_creator->image_name.length()-1)
|
if (p == std::string::npos || p == image_creator->image_name.length()-1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Please specify new snapshot name after @\n");
|
delete image_creator;
|
||||||
exit(1);
|
return [](cli_result_t & result)
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = "Please specify new snapshot name after @" };
|
||||||
|
return true;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
image_creator->new_snap = image_creator->image_name.substr(p + 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->image_name = image_creator->image_name.substr(0, p);
|
||||||
|
@ -502,32 +525,39 @@ std::function<bool(void)> cli_tool_t::start_create(json11::Json cfg)
|
||||||
if (cfg["size"].string_value() != "")
|
if (cfg["size"].string_value() != "")
|
||||||
{
|
{
|
||||||
image_creator->size = parse_size(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)
|
if (image_creator->size % 4096)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Size should be a multiple of 4096\n");
|
delete image_creator;
|
||||||
exit(1);
|
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 != "")
|
if (image_creator->new_snap != "")
|
||||||
{
|
{
|
||||||
fprintf(stderr, "--size can't be specified for snapshots\n");
|
delete image_creator;
|
||||||
exit(1);
|
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 == "")
|
return [image_creator](cli_result_t & result)
|
||||||
{
|
|
||||||
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]()
|
|
||||||
{
|
{
|
||||||
image_creator->loop();
|
image_creator->loop();
|
||||||
if (image_creator->is_done())
|
if (image_creator->is_done())
|
||||||
{
|
{
|
||||||
|
result = image_creator->result;
|
||||||
delete image_creator;
|
delete image_creator;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ struct pool_lister_t
|
||||||
|
|
||||||
int state = 0;
|
int state = 0;
|
||||||
json11::Json space_info;
|
json11::Json space_info;
|
||||||
|
cli_result_t result;
|
||||||
std::map<pool_id_t, json11::Json::object> pool_stats;
|
std::map<pool_id_t, json11::Json::object> pool_stats;
|
||||||
|
|
||||||
bool is_done()
|
bool is_done()
|
||||||
|
@ -150,10 +151,10 @@ resume_1:
|
||||||
get_stats();
|
get_stats();
|
||||||
if (parent->waiting > 0)
|
if (parent->waiting > 0)
|
||||||
return;
|
return;
|
||||||
|
result.data = to_list();
|
||||||
if (parent->json_output)
|
if (parent->json_output)
|
||||||
{
|
{
|
||||||
// JSON output
|
// JSON output
|
||||||
printf("%s\n", json11::Json(to_list()).dump().c_str());
|
|
||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -206,21 +207,22 @@ resume_1:
|
||||||
: 100)+"%";
|
: 100)+"%";
|
||||||
kv.second["eff_fmt"] = format_q(kv.second["space_efficiency"].number_value()*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.text = print_table(result.data, cols, parent->color);
|
||||||
state = 100;
|
state = 100;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_df(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_df(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
auto lister = new pool_lister_t();
|
auto lister = new pool_lister_t();
|
||||||
lister->parent = this;
|
lister->parent = this;
|
||||||
return [lister]()
|
return [lister](cli_result_t & result)
|
||||||
{
|
{
|
||||||
lister->loop();
|
lister->loop();
|
||||||
if (lister->is_done())
|
if (lister->is_done())
|
||||||
{
|
{
|
||||||
|
result = lister->result;
|
||||||
delete lister;
|
delete lister;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,8 @@ struct snap_flattener_t
|
||||||
std::string top_parent_name;
|
std::string top_parent_name;
|
||||||
inode_t target_id = 0;
|
inode_t target_id = 0;
|
||||||
int state = 0;
|
int state = 0;
|
||||||
std::function<bool(void)> merger_cb;
|
std::function<bool(cli_result_t &)> merger_cb;
|
||||||
|
cli_result_t result;
|
||||||
|
|
||||||
void get_merge_parents()
|
void get_merge_parents()
|
||||||
{
|
{
|
||||||
|
@ -37,23 +38,34 @@ struct snap_flattener_t
|
||||||
auto it = parent->cli->st_cli.inode_config.find(cur->parent_id);
|
auto it = parent->cli->st_cli.inode_config.find(cur->parent_id);
|
||||||
if (it == parent->cli->st_cli.inode_config.end())
|
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);
|
result = (cli_result_t){
|
||||||
exit(1);
|
.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;
|
cur = &it->second;
|
||||||
chain_list.push_back(cur->num);
|
chain_list.push_back(cur->num);
|
||||||
}
|
}
|
||||||
if (cur->parent_id != 0)
|
if (cur->parent_id != 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Layer %s has a loop in parents\n", target_name.c_str());
|
result = (cli_result_t){ .err = EBADF, .text = "Layer "+target_name+" has a loop in parents" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
top_parent_name = cur->name;
|
top_parent_name = cur->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_done()
|
bool is_done()
|
||||||
{
|
{
|
||||||
return state == 5;
|
return state == 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
|
@ -64,8 +76,16 @@ struct snap_flattener_t
|
||||||
goto resume_2;
|
goto resume_2;
|
||||||
else if (state == 3)
|
else if (state == 3)
|
||||||
goto resume_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 parent layers
|
||||||
get_merge_parents();
|
get_merge_parents();
|
||||||
|
if (state == 100)
|
||||||
|
return;
|
||||||
// Start merger
|
// Start merger
|
||||||
merger_cb = parent->start_merge(json11::Json::object {
|
merger_cb = parent->start_merge(json11::Json::object {
|
||||||
{ "command", json11::Json::array{ "merge-data", top_parent_name, target_name } },
|
{ "command", json11::Json::array{ "merge-data", top_parent_name, target_name } },
|
||||||
|
@ -76,12 +96,17 @@ struct snap_flattener_t
|
||||||
});
|
});
|
||||||
// Wait for it
|
// Wait for it
|
||||||
resume_1:
|
resume_1:
|
||||||
while (!merger_cb())
|
while (!merger_cb(result))
|
||||||
{
|
{
|
||||||
state = 1;
|
state = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
merger_cb = NULL;
|
merger_cb = NULL;
|
||||||
|
if (result.err)
|
||||||
|
{
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Change parent
|
// Change parent
|
||||||
parent->change_parent(target_id, 0);
|
parent->change_parent(target_id, 0);
|
||||||
// Wait for it to complete
|
// Wait for it to complete
|
||||||
|
@ -92,31 +117,27 @@ resume_2:
|
||||||
state = 3;
|
state = 3;
|
||||||
resume_3:
|
resume_3:
|
||||||
// Done
|
// Done
|
||||||
return;
|
state = 100;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_flatten(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_flatten(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
auto flattener = new snap_flattener_t();
|
auto flattener = new snap_flattener_t();
|
||||||
flattener->parent = this;
|
flattener->parent = this;
|
||||||
flattener->target_name = cmd.size() > 1 ? cmd[1].string_value() : "";
|
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->fsync_interval = cfg["fsync-interval"].uint64_value();
|
flattener->fsync_interval = cfg["fsync-interval"].uint64_value();
|
||||||
if (!flattener->fsync_interval)
|
if (!flattener->fsync_interval)
|
||||||
flattener->fsync_interval = 128;
|
flattener->fsync_interval = 128;
|
||||||
if (!cfg["cas"].is_null())
|
if (!cfg["cas"].is_null())
|
||||||
flattener->use_cas = cfg["cas"].uint64_value() ? 2 : 0;
|
flattener->use_cas = cfg["cas"].uint64_value() ? 2 : 0;
|
||||||
return [flattener]()
|
return [flattener](cli_result_t & result)
|
||||||
{
|
{
|
||||||
flattener->loop();
|
flattener->loop();
|
||||||
if (flattener->is_done())
|
if (flattener->is_done())
|
||||||
{
|
{
|
||||||
|
result = flattener->result;
|
||||||
delete flattener;
|
delete flattener;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ struct image_lister_t
|
||||||
int state = 0;
|
int state = 0;
|
||||||
std::map<inode_t, json11::Json::object> stats;
|
std::map<inode_t, json11::Json::object> stats;
|
||||||
json11::Json space_info;
|
json11::Json space_info;
|
||||||
|
cli_result_t result;
|
||||||
|
|
||||||
bool is_done()
|
bool is_done()
|
||||||
{
|
{
|
||||||
|
@ -44,8 +45,9 @@ struct image_lister_t
|
||||||
}
|
}
|
||||||
if (!list_pool_id)
|
if (!list_pool_id)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Pool %s does not exist\n", list_pool_name.c_str());
|
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+list_pool_name+" does not exist" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto & ic: parent->cli->st_cli.inode_config)
|
for (auto & ic: parent->cli->st_cli.inode_config)
|
||||||
|
@ -246,10 +248,10 @@ resume_1:
|
||||||
if (parent->waiting > 0)
|
if (parent->waiting > 0)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
result.data = to_list();
|
||||||
if (parent->json_output)
|
if (parent->json_output)
|
||||||
{
|
{
|
||||||
// JSON output
|
// JSON output
|
||||||
printf("%s\n", json11::Json(to_list()).dump().c_str());
|
|
||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -359,7 +361,7 @@ resume_1:
|
||||||
kv.second["size_fmt"] = format_size(kv.second["size"].uint64_value());
|
kv.second["size_fmt"] = format_size(kv.second["size"].uint64_value());
|
||||||
kv.second["ro"] = kv.second["readonly"].bool_value() ? "RO" : "-";
|
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;
|
state = 100;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -546,7 +548,7 @@ back:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_ls(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_ls(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
auto lister = new image_lister_t();
|
auto lister = new image_lister_t();
|
||||||
|
@ -562,11 +564,12 @@ std::function<bool(void)> cli_tool_t::start_ls(json11::Json cfg)
|
||||||
{
|
{
|
||||||
lister->only_names.insert(cmd[i].string_value());
|
lister->only_names.insert(cmd[i].string_value());
|
||||||
}
|
}
|
||||||
return [lister]()
|
return [lister](cli_result_t & result)
|
||||||
{
|
{
|
||||||
lister->loop();
|
lister->loop();
|
||||||
if (lister->is_done())
|
if (lister->is_done())
|
||||||
{
|
{
|
||||||
|
result = lister->result;
|
||||||
delete lister;
|
delete lister;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@ struct snap_rw_op_t
|
||||||
cluster_op_t op;
|
cluster_op_t op;
|
||||||
int todo = 0;
|
int todo = 0;
|
||||||
uint32_t start = 0, end = 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:
|
// Layer merge is the base for multiple operations:
|
||||||
|
@ -54,17 +57,27 @@ struct snap_merger_t
|
||||||
uint64_t last_written_offset = 0;
|
uint64_t last_written_offset = 0;
|
||||||
int deleted_unsynced = 0;
|
int deleted_unsynced = 0;
|
||||||
uint64_t processed = 0, to_process = 0;
|
uint64_t processed = 0, to_process = 0;
|
||||||
|
std::string rwo_error;
|
||||||
|
|
||||||
|
cli_result_t result;
|
||||||
|
|
||||||
void start_merge()
|
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;
|
check_delete_source = delete_source || check_delete_source;
|
||||||
inode_config_t *from_cfg = parent->get_inode_cfg(from_name);
|
inode_config_t *from_cfg = parent->get_inode_cfg(from_name);
|
||||||
inode_config_t *to_cfg = parent->get_inode_cfg(to_name);
|
inode_config_t *to_cfg = parent->get_inode_cfg(to_name);
|
||||||
inode_config_t *target_cfg = target_name == "" ? from_cfg : parent->get_inode_cfg(target_name);
|
inode_config_t *target_cfg = target_name == "" ? from_cfg : parent->get_inode_cfg(target_name);
|
||||||
if (to_cfg->num == from_cfg->num)
|
if (to_cfg->num == from_cfg->num)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Only one layer specified, nothing to merge\n");
|
result = (cli_result_t){ .err = EINVAL, .text = "Only one layer specified, nothing to merge" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Check that to_cfg is actually a child of from_cfg and target_cfg is somewhere between them
|
// Check that to_cfg is actually a child of from_cfg and target_cfg is somewhere between them
|
||||||
std::vector<inode_t> chain_list;
|
std::vector<inode_t> chain_list;
|
||||||
|
@ -78,8 +91,18 @@ struct snap_merger_t
|
||||||
auto it = parent->cli->st_cli.inode_config.find(cur->parent_id);
|
auto it = parent->cli->st_cli.inode_config.find(cur->parent_id);
|
||||||
if (it == parent->cli->st_cli.inode_config.end())
|
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);
|
result = (cli_result_t){
|
||||||
exit(1);
|
.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;
|
cur = &it->second;
|
||||||
chain_list.push_back(cur->num);
|
chain_list.push_back(cur->num);
|
||||||
|
@ -87,8 +110,9 @@ struct snap_merger_t
|
||||||
}
|
}
|
||||||
if (cur->parent_id != from_cfg->num)
|
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());
|
result = (cli_result_t){ .err = EINVAL, .text = "Layer "+to_name+" is not a child of "+from_name };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
chain_list.push_back(from_cfg->num);
|
chain_list.push_back(from_cfg->num);
|
||||||
layer_block_size[from_cfg->num] = get_block_size(from_cfg->num);
|
layer_block_size[from_cfg->num] = get_block_size(from_cfg->num);
|
||||||
|
@ -99,8 +123,9 @@ struct snap_merger_t
|
||||||
}
|
}
|
||||||
if (sources.find(target_cfg->num) == sources.end())
|
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());
|
result = (cli_result_t){ .err = EINVAL, .text = "Layer "+target_name+" is not between "+to_name+" and "+from_name };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
target = target_cfg->num;
|
target = target_cfg->num;
|
||||||
target_rank = sources.at(target);
|
target_rank = sources.at(target);
|
||||||
|
@ -130,14 +155,15 @@ struct snap_merger_t
|
||||||
int parent_rank = it->second;
|
int parent_rank = it->second;
|
||||||
if (parent_rank < to_rank && (parent_rank >= target_rank || check_delete_source))
|
if (parent_rank < to_rank && (parent_rank >= target_rank || check_delete_source))
|
||||||
{
|
{
|
||||||
fprintf(
|
result = (cli_result_t){
|
||||||
stderr, "Layers at or above %s, but below %s are not allowed"
|
.err = EINVAL,
|
||||||
" to have other children, but %s is a child of %s\n",
|
.text = "Layers at or above "+(check_delete_source ? from_name : target_name)+
|
||||||
(check_delete_source ? from_name.c_str() : target_name.c_str()),
|
", but below "+to_name+" are not allowed to have other children, but "+
|
||||||
to_name.c_str(), ic.second.name.c_str(),
|
ic.second.name+" is a child of "+
|
||||||
parent->cli->st_cli.inode_config.at(ic.second.parent_id).name.c_str()
|
parent->cli->st_cli.inode_config.at(ic.second.parent_id).name,
|
||||||
);
|
};
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (parent_rank >= to_rank)
|
if (parent_rank >= to_rank)
|
||||||
{
|
{
|
||||||
|
@ -152,11 +178,14 @@ struct snap_merger_t
|
||||||
use_cas = 0;
|
use_cas = 0;
|
||||||
}
|
}
|
||||||
sources.erase(target);
|
sources.erase(target);
|
||||||
printf(
|
if (parent->progress)
|
||||||
"Merging %ld layer(s) into target %s%s (inode %lu in pool %u)\n",
|
{
|
||||||
sources.size(), target_cfg->name.c_str(),
|
printf(
|
||||||
use_cas ? " online (with CAS)" : "", INODE_NO_POOL(target), INODE_POOL(target)
|
"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);
|
target_block_size = get_block_size(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +282,8 @@ struct snap_merger_t
|
||||||
oit = merge_offsets.begin();
|
oit = merge_offsets.begin();
|
||||||
resume_5:
|
resume_5:
|
||||||
// Now read, overwrite and optionally delete offsets one by one
|
// 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++;
|
in_flight++;
|
||||||
read_and_write(*oit);
|
read_and_write(*oit);
|
||||||
|
@ -264,6 +294,15 @@ struct snap_merger_t
|
||||||
printf("\rOverwriting blocks: %lu/%lu", processed, to_process);
|
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())
|
if (in_flight > 0 || oit != merge_offsets.end())
|
||||||
{
|
{
|
||||||
// Wait until overwrites finish
|
// Wait until overwrites finish
|
||||||
|
@ -396,8 +435,9 @@ struct snap_merger_t
|
||||||
{
|
{
|
||||||
if (op->retval != op->len)
|
if (op->retval != op->len)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "error reading target at offset %lx: %s\n", op->offset, strerror(-op->retval));
|
rwo->error_code = -op->retval;
|
||||||
exit(1);
|
rwo->error_offset = op->offset;
|
||||||
|
rwo->error_read = true;
|
||||||
}
|
}
|
||||||
next_write(rwo);
|
next_write(rwo);
|
||||||
};
|
};
|
||||||
|
@ -410,7 +450,7 @@ struct snap_merger_t
|
||||||
// FIXME: Allow to use single write with "holes" (OSDs don't allow it yet)
|
// FIXME: Allow to use single write with "holes" (OSDs don't allow it yet)
|
||||||
uint32_t gran = parent->cli->get_bs_bitmap_granularity();
|
uint32_t gran = parent->cli->get_bs_bitmap_granularity();
|
||||||
uint64_t bitmap_size = target_block_size / gran;
|
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)));
|
auto bit = ((*((uint8_t*)rwo->op.bitmap_buf + (rwo->end >> 3))) & (1 << (rwo->end & 0x7)));
|
||||||
if (!bit)
|
if (!bit)
|
||||||
|
@ -434,7 +474,7 @@ struct snap_merger_t
|
||||||
rwo->end++;
|
rwo->end++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rwo->end > rwo->start)
|
if (rwo->end > rwo->start && !rwo->error_code)
|
||||||
{
|
{
|
||||||
// write start->end
|
// write start->end
|
||||||
rwo->todo++;
|
rwo->todo++;
|
||||||
|
@ -473,8 +513,9 @@ struct snap_merger_t
|
||||||
delete subop;
|
delete subop;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "error writing target at offset %lx: %s\n", subop->offset, strerror(-subop->retval));
|
rwo->error_code = -subop->retval;
|
||||||
exit(1);
|
rwo->error_offset = subop->offset;
|
||||||
|
rwo->error_read = false;
|
||||||
}
|
}
|
||||||
// Increment CAS version
|
// Increment CAS version
|
||||||
rwo->op.version++;
|
rwo->op.version++;
|
||||||
|
@ -510,11 +551,12 @@ struct snap_merger_t
|
||||||
{
|
{
|
||||||
if (!rwo->todo)
|
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;
|
last_written_offset = rwo->op.offset+target_block_size;
|
||||||
}
|
}
|
||||||
if (delete_source)
|
if (!rwo->error_code && delete_source)
|
||||||
{
|
{
|
||||||
deleted_unsynced++;
|
deleted_unsynced++;
|
||||||
if (deleted_unsynced >= fsync_interval)
|
if (deleted_unsynced >= fsync_interval)
|
||||||
|
@ -545,13 +587,20 @@ struct snap_merger_t
|
||||||
}
|
}
|
||||||
free(rwo->buf);
|
free(rwo->buf);
|
||||||
delete rwo;
|
delete rwo;
|
||||||
|
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);
|
||||||
|
}
|
||||||
in_flight--;
|
in_flight--;
|
||||||
continue_merge_reent();
|
continue_merge_reent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_merge(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_merge(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
auto merger = new snap_merger_t();
|
auto merger = new snap_merger_t();
|
||||||
|
@ -559,22 +608,18 @@ std::function<bool(void)> cli_tool_t::start_merge(json11::Json cfg)
|
||||||
merger->from_name = cmd.size() > 1 ? cmd[1].string_value() : "";
|
merger->from_name = cmd.size() > 1 ? cmd[1].string_value() : "";
|
||||||
merger->to_name = cmd.size() > 2 ? cmd[2].string_value() : "";
|
merger->to_name = cmd.size() > 2 ? cmd[2].string_value() : "";
|
||||||
merger->target_name = cfg["target"].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->delete_source = cfg["delete-source"].string_value() != "";
|
||||||
merger->fsync_interval = cfg["fsync-interval"].uint64_value();
|
merger->fsync_interval = cfg["fsync-interval"].uint64_value();
|
||||||
if (!merger->fsync_interval)
|
if (!merger->fsync_interval)
|
||||||
merger->fsync_interval = 128;
|
merger->fsync_interval = 128;
|
||||||
if (!cfg["cas"].is_null())
|
if (!cfg["cas"].is_null())
|
||||||
merger->use_cas = cfg["cas"].uint64_value() ? 2 : 0;
|
merger->use_cas = cfg["cas"].uint64_value() ? 2 : 0;
|
||||||
return [merger]()
|
return [merger](cli_result_t & result)
|
||||||
{
|
{
|
||||||
merger->continue_merge_reent();
|
merger->continue_merge_reent();
|
||||||
if (merger->is_done())
|
if (merger->is_done())
|
||||||
{
|
{
|
||||||
|
result = merger->result;
|
||||||
delete merger;
|
delete merger;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,8 @@ struct image_changer_t
|
||||||
bool has_children = false;
|
bool has_children = false;
|
||||||
|
|
||||||
int state = 0;
|
int state = 0;
|
||||||
std::function<bool(void)> cb;
|
std::function<bool(cli_result_t &)> cb;
|
||||||
|
cli_result_t result;
|
||||||
|
|
||||||
bool is_done()
|
bool is_done()
|
||||||
{
|
{
|
||||||
|
@ -36,6 +37,18 @@ struct image_changer_t
|
||||||
goto resume_1;
|
goto resume_1;
|
||||||
else if (state == 2)
|
else if (state == 2)
|
||||||
goto resume_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))
|
||||||
|
{
|
||||||
|
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)
|
for (auto & ic: parent->cli->st_cli.inode_config)
|
||||||
{
|
{
|
||||||
if (ic.second.name == image_name)
|
if (ic.second.name == image_name)
|
||||||
|
@ -46,14 +59,16 @@ struct image_changer_t
|
||||||
}
|
}
|
||||||
if (new_name != "" && ic.second.name == new_name)
|
if (new_name != "" && ic.second.name == new_name)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Image %s already exists\n", new_name.c_str());
|
result = (cli_result_t){ .err = EEXIST, .text = "Image "+new_name+" already exists" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!inode_num)
|
if (!inode_num)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Image %s does not exist\n", image_name.c_str());
|
result = (cli_result_t){ .err = ENOENT, .text = "Image "+image_name+" does not exist" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
for (auto & ic: parent->cli->st_cli.inode_config)
|
for (auto & ic: parent->cli->st_cli.inode_config)
|
||||||
{
|
{
|
||||||
|
@ -68,7 +83,7 @@ struct image_changer_t
|
||||||
(!new_size || cfg.size == new_size) &&
|
(!new_size || cfg.size == new_size) &&
|
||||||
(new_name == "" || new_name == image_name))
|
(new_name == "" || new_name == image_name))
|
||||||
{
|
{
|
||||||
printf("No change\n");
|
result = (cli_result_t){ .text = "No change" };
|
||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -79,8 +94,9 @@ struct image_changer_t
|
||||||
// Check confirmation when trimming an image with children
|
// Check confirmation when trimming an image with children
|
||||||
if (has_children && !force)
|
if (has_children && !force)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Image %s has children. Refusing to shrink it without --force\n", image_name.c_str());
|
result = (cli_result_t){ .err = EINVAL, .text = "Image "+image_name+" has children. Refusing to shrink it without --force" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Shrink the image first
|
// Shrink the image first
|
||||||
cb = parent->start_rm(json11::Json::object {
|
cb = parent->start_rm(json11::Json::object {
|
||||||
|
@ -90,12 +106,17 @@ struct image_changer_t
|
||||||
{ "min-offset", new_size },
|
{ "min-offset", new_size },
|
||||||
});
|
});
|
||||||
resume_1:
|
resume_1:
|
||||||
while (!cb())
|
while (!cb(result))
|
||||||
{
|
{
|
||||||
state = 1;
|
state = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cb = NULL;
|
cb = NULL;
|
||||||
|
if (result.err)
|
||||||
|
{
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cfg.size = new_size;
|
cfg.size = new_size;
|
||||||
}
|
}
|
||||||
|
@ -109,8 +130,9 @@ resume_1:
|
||||||
// Check confirmation when making an image with children read-write
|
// Check confirmation when making an image with children read-write
|
||||||
if (has_children && !force)
|
if (has_children && !force)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Image %s has children. Refusing to make it read-write without --force\n", image_name.c_str());
|
result = (cli_result_t){ .err = EINVAL, .text = "Image "+image_name+" has children. Refusing to make it read-write without --force" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (new_name != "")
|
if (new_name != "")
|
||||||
|
@ -180,32 +202,23 @@ resume_2:
|
||||||
return;
|
return;
|
||||||
if (!parent->etcd_result["succeeded"].bool_value())
|
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());
|
result = (cli_result_t){ .err = EAGAIN, .text = "Image "+image_name+" was modified by someone else, please repeat your request" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
printf("Image %s modified\n", image_name.c_str());
|
result = (cli_result_t){ .err = 0, .text = "Image "+image_name+" modified" };
|
||||||
state = 100;
|
state = 100;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_modify(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_modify(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
auto changer = new image_changer_t();
|
auto changer = new image_changer_t();
|
||||||
changer->parent = this;
|
changer->parent = this;
|
||||||
changer->image_name = cmd.size() > 1 ? cmd[1].string_value() : "";
|
changer->image_name = cmd.size() > 1 ? cmd[1].string_value() : "";
|
||||||
if (changer->image_name == "")
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Image name is missing\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
changer->new_name = cfg["rename"].string_value();
|
changer->new_name = cfg["rename"].string_value();
|
||||||
changer->new_size = parse_size(cfg["resize"].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->force = cfg["force"].bool_value();
|
changer->force = cfg["force"].bool_value();
|
||||||
changer->set_readonly = cfg["readonly"].bool_value();
|
changer->set_readonly = cfg["readonly"].bool_value();
|
||||||
changer->set_readwrite = cfg["readwrite"].bool_value();
|
changer->set_readwrite = cfg["readwrite"].bool_value();
|
||||||
|
@ -213,11 +226,12 @@ std::function<bool(void)> cli_tool_t::start_modify(json11::Json cfg)
|
||||||
if (!changer->fsync_interval)
|
if (!changer->fsync_interval)
|
||||||
changer->fsync_interval = 128;
|
changer->fsync_interval = 128;
|
||||||
// FIXME Check that the image doesn't have children when shrinking
|
// FIXME Check that the image doesn't have children when shrinking
|
||||||
return [changer]()
|
return [changer](cli_result_t & result)
|
||||||
{
|
{
|
||||||
changer->loop();
|
changer->loop();
|
||||||
if (changer->is_done())
|
if (changer->is_done())
|
||||||
{
|
{
|
||||||
|
result = changer->result;
|
||||||
delete changer;
|
delete changer;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
165
src/cli_rm.cpp
165
src/cli_rm.cpp
|
@ -63,7 +63,9 @@ struct snap_remover_t
|
||||||
inode_t new_parent = 0;
|
inode_t new_parent = 0;
|
||||||
int state = 0;
|
int state = 0;
|
||||||
int current_child = 0;
|
int current_child = 0;
|
||||||
std::function<bool(void)> cb;
|
std::function<bool(cli_result_t &)> cb;
|
||||||
|
|
||||||
|
cli_result_t result;
|
||||||
|
|
||||||
bool is_done()
|
bool is_done()
|
||||||
{
|
{
|
||||||
|
@ -90,11 +92,25 @@ struct snap_remover_t
|
||||||
goto resume_8;
|
goto resume_8;
|
||||||
else if (state == 9)
|
else if (state == 9)
|
||||||
goto resume_9;
|
goto resume_9;
|
||||||
|
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 children to merge
|
||||||
get_merge_children();
|
get_merge_children();
|
||||||
|
if (state == 100)
|
||||||
|
return;
|
||||||
// Try to select an inode for the "inverse" optimized scenario
|
// Try to select an inode for the "inverse" optimized scenario
|
||||||
// Read statistics from etcd to do it
|
// Read statistics from etcd to do it
|
||||||
read_stats();
|
read_stats();
|
||||||
|
if (state == 100)
|
||||||
|
return;
|
||||||
state = 1;
|
state = 1;
|
||||||
resume_1:
|
resume_1:
|
||||||
if (parent->waiting > 0)
|
if (parent->waiting > 0)
|
||||||
|
@ -106,13 +122,20 @@ resume_1:
|
||||||
if (merge_children[current_child] == inverse_child)
|
if (merge_children[current_child] == inverse_child)
|
||||||
continue;
|
continue;
|
||||||
start_merge_child(merge_children[current_child], merge_children[current_child]);
|
start_merge_child(merge_children[current_child], merge_children[current_child]);
|
||||||
|
if (state == 100)
|
||||||
|
return;
|
||||||
resume_2:
|
resume_2:
|
||||||
while (!cb())
|
while (!cb(result))
|
||||||
{
|
{
|
||||||
state = 2;
|
state = 2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cb = NULL;
|
cb = NULL;
|
||||||
|
if (result.err)
|
||||||
|
{
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
parent->change_parent(merge_children[current_child], new_parent);
|
parent->change_parent(merge_children[current_child], new_parent);
|
||||||
state = 3;
|
state = 3;
|
||||||
resume_3:
|
resume_3:
|
||||||
|
@ -123,25 +146,41 @@ resume_3:
|
||||||
if (inverse_child != 0)
|
if (inverse_child != 0)
|
||||||
{
|
{
|
||||||
start_merge_child(inverse_child, inverse_parent);
|
start_merge_child(inverse_child, inverse_parent);
|
||||||
|
if (state == 100)
|
||||||
|
return;
|
||||||
resume_4:
|
resume_4:
|
||||||
while (!cb())
|
while (!cb(result))
|
||||||
{
|
{
|
||||||
state = 4;
|
state = 4;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cb = NULL;
|
cb = NULL;
|
||||||
|
if (result.err)
|
||||||
|
{
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Delete "inverse" child data
|
// Delete "inverse" child data
|
||||||
start_delete_source(inverse_child);
|
start_delete_source(inverse_child);
|
||||||
|
if (state == 100)
|
||||||
|
return;
|
||||||
resume_5:
|
resume_5:
|
||||||
while (!cb())
|
while (!cb(result))
|
||||||
{
|
{
|
||||||
state = 5;
|
state = 5;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cb = NULL;
|
cb = NULL;
|
||||||
|
if (result.err)
|
||||||
|
{
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Delete "inverse" child metadata, rename parent over it,
|
// Delete "inverse" child metadata, rename parent over it,
|
||||||
// and also change parent links of the previous "inverse" child
|
// and also change parent links of the previous "inverse" child
|
||||||
rename_inverse_parent();
|
rename_inverse_parent();
|
||||||
|
if (state == 100)
|
||||||
|
return;
|
||||||
state = 6;
|
state = 6;
|
||||||
resume_6:
|
resume_6:
|
||||||
if (parent->waiting > 0)
|
if (parent->waiting > 0)
|
||||||
|
@ -154,13 +193,20 @@ resume_6:
|
||||||
continue;
|
continue;
|
||||||
start_delete_source(chain_list[current_child]);
|
start_delete_source(chain_list[current_child]);
|
||||||
resume_7:
|
resume_7:
|
||||||
while (!cb())
|
while (!cb(result))
|
||||||
{
|
{
|
||||||
state = 7;
|
state = 7;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cb = NULL;
|
cb = NULL;
|
||||||
|
if (result.err)
|
||||||
|
{
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
delete_inode_config(chain_list[current_child]);
|
delete_inode_config(chain_list[current_child]);
|
||||||
|
if (state == 100)
|
||||||
|
return;
|
||||||
state = 8;
|
state = 8;
|
||||||
resume_8:
|
resume_8:
|
||||||
if (parent->waiting > 0)
|
if (parent->waiting > 0)
|
||||||
|
@ -186,16 +232,19 @@ resume_9:
|
||||||
auto it = parent->cli->st_cli.inode_config.find(cur->parent_id);
|
auto it = parent->cli->st_cli.inode_config.find(cur->parent_id);
|
||||||
if (it == parent->cli->st_cli.inode_config.end())
|
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);
|
char buf[1024];
|
||||||
exit(1);
|
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;
|
cur = &it->second;
|
||||||
chain_list.push_back(cur->num);
|
chain_list.push_back(cur->num);
|
||||||
}
|
}
|
||||||
if (cur->num != from_cfg->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());
|
result = (cli_result_t){ .err = EINVAL, .text = "Layer "+to_name+" is not a child of "+from_name };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
new_parent = from_cfg->parent_id;
|
new_parent = from_cfg->parent_id;
|
||||||
// Calculate ranks
|
// Calculate ranks
|
||||||
|
@ -263,8 +312,9 @@ resume_9:
|
||||||
parent->waiting--;
|
parent->waiting--;
|
||||||
if (err != "")
|
if (err != "")
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error reading layer statistics from etcd: %s\n", err.c_str());
|
result = (cli_result_t){ .err = EIO, .text = "Error reading layer statistics from etcd: "+err };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
for (auto inode_result: data["responses"].array_items())
|
for (auto inode_result: data["responses"].array_items())
|
||||||
{
|
{
|
||||||
|
@ -275,14 +325,16 @@ resume_9:
|
||||||
sscanf(kv.key.c_str() + parent->cli->st_cli.etcd_prefix.length()+13, "%u/%lu%c", &pool_id, &inode, &null_byte);
|
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)
|
if (!inode || null_byte != 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Bad key returned from etcd: %s\n", kv.key.c_str());
|
result = (cli_result_t){ .err = EIO, .text = "Bad key returned from etcd: "+kv.key };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
auto pool_cfg_it = parent->cli->st_cli.pool_config.find(pool_id);
|
auto pool_cfg_it = parent->cli->st_cli.pool_config.find(pool_id);
|
||||||
if (pool_cfg_it == parent->cli->st_cli.pool_config.end())
|
if (pool_cfg_it == parent->cli->st_cli.pool_config.end())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Pool %u does not exist\n", pool_id);
|
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+std::to_string(pool_id)+" does not exist" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
inode = INODE_WITH_POOL(pool_id, inode);
|
inode = INODE_WITH_POOL(pool_id, inode);
|
||||||
auto & pool_cfg = pool_cfg_it->second;
|
auto & pool_cfg = pool_cfg_it->second;
|
||||||
|
@ -324,14 +376,20 @@ resume_9:
|
||||||
auto child_it = parent->cli->st_cli.inode_config.find(inverse_child);
|
auto child_it = parent->cli->st_cli.inode_config.find(inverse_child);
|
||||||
if (child_it == parent->cli->st_cli.inode_config.end())
|
if (child_it == parent->cli->st_cli.inode_config.end())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Inode %ld disappeared\n", inverse_child);
|
char buf[1024];
|
||||||
exit(1);
|
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);
|
auto target_it = parent->cli->st_cli.inode_config.find(inverse_parent);
|
||||||
if (target_it == parent->cli->st_cli.inode_config.end())
|
if (target_it == parent->cli->st_cli.inode_config.end())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Inode %ld disappeared\n", inverse_parent);
|
char buf[1024];
|
||||||
exit(1);
|
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 *child_cfg = &child_it->second;
|
||||||
inode_config_t *target_cfg = &target_it->second;
|
inode_config_t *target_cfg = &target_it->second;
|
||||||
|
@ -422,16 +480,19 @@ resume_9:
|
||||||
parent->waiting--;
|
parent->waiting--;
|
||||||
if (err != "")
|
if (err != "")
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error renaming %s to %s: %s\n", target_name.c_str(), child_name.c_str(), err.c_str());
|
result = (cli_result_t){ .err = EIO, .text = "Error renaming "+target_name+" to "+child_name+": "+err };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!res["succeeded"].bool_value())
|
if (!res["succeeded"].bool_value())
|
||||||
{
|
{
|
||||||
fprintf(
|
result = (cli_result_t){
|
||||||
stderr, "Parent (%s), child (%s), or one of its children"
|
.err = EIO,
|
||||||
" configuration was modified during rename\n", target_name.c_str(), child_name.c_str()
|
.text = "Parent ("+target_name+"), child ("+child_name+"), or one of its children"
|
||||||
);
|
" configuration was modified during rename",
|
||||||
exit(1);
|
};
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
printf("Layer %s renamed to %s\n", target_name.c_str(), child_name.c_str());
|
printf("Layer %s renamed to %s\n", target_name.c_str(), child_name.c_str());
|
||||||
parent->ringloop->wakeup();
|
parent->ringloop->wakeup();
|
||||||
|
@ -443,8 +504,11 @@ resume_9:
|
||||||
auto cur_cfg_it = parent->cli->st_cli.inode_config.find(cur);
|
auto cur_cfg_it = parent->cli->st_cli.inode_config.find(cur);
|
||||||
if (cur_cfg_it == parent->cli->st_cli.inode_config.end())
|
if (cur_cfg_it == parent->cli->st_cli.inode_config.end())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Inode 0x%lx disappeared\n", cur);
|
char buf[1024];
|
||||||
exit(1);
|
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;
|
inode_config_t *cur_cfg = &cur_cfg_it->second;
|
||||||
std::string cur_name = cur_cfg->name;
|
std::string cur_name = cur_cfg->name;
|
||||||
|
@ -480,13 +544,15 @@ resume_9:
|
||||||
parent->waiting--;
|
parent->waiting--;
|
||||||
if (err != "")
|
if (err != "")
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error deleting %s: %s\n", cur_name.c_str(), err.c_str());
|
result = (cli_result_t){ .err = EIO, .text = "Error deleting "+cur_name+": "+err };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!res["succeeded"].bool_value())
|
if (!res["succeeded"].bool_value())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Layer %s configuration was modified during deletion\n", cur_name.c_str());
|
result = (cli_result_t){ .err = EIO, .text = "Layer "+cur_name+" was modified during deletion" };
|
||||||
exit(1);
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
printf("Layer %s deleted\n", cur_name.c_str());
|
printf("Layer %s deleted\n", cur_name.c_str());
|
||||||
parent->ringloop->wakeup();
|
parent->ringloop->wakeup();
|
||||||
|
@ -498,14 +564,20 @@ resume_9:
|
||||||
auto child_it = parent->cli->st_cli.inode_config.find(child_inode);
|
auto child_it = parent->cli->st_cli.inode_config.find(child_inode);
|
||||||
if (child_it == parent->cli->st_cli.inode_config.end())
|
if (child_it == parent->cli->st_cli.inode_config.end())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Inode %ld disappeared\n", child_inode);
|
char buf[1024];
|
||||||
exit(1);
|
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);
|
auto target_it = parent->cli->st_cli.inode_config.find(target_inode);
|
||||||
if (target_it == parent->cli->st_cli.inode_config.end())
|
if (target_it == parent->cli->st_cli.inode_config.end())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Inode %ld disappeared\n", target_inode);
|
char buf[1024];
|
||||||
exit(1);
|
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 {
|
cb = parent->start_merge(json11::Json::object {
|
||||||
{ "command", json11::Json::array{ "merge-data", from_name, child_it->second.name } },
|
{ "command", json11::Json::array{ "merge-data", from_name, child_it->second.name } },
|
||||||
|
@ -521,8 +593,11 @@ resume_9:
|
||||||
auto source = parent->cli->st_cli.inode_config.find(inode);
|
auto source = parent->cli->st_cli.inode_config.find(inode);
|
||||||
if (source == parent->cli->st_cli.inode_config.end())
|
if (source == parent->cli->st_cli.inode_config.end())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Inode %ld disappeared\n", inode);
|
char buf[1024];
|
||||||
exit(1);
|
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(json11::Json::object {
|
||||||
{ "inode", inode },
|
{ "inode", inode },
|
||||||
|
@ -532,22 +607,13 @@ resume_9:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_snap_rm(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_snap_rm(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
auto snap_remover = new snap_remover_t();
|
auto snap_remover = new snap_remover_t();
|
||||||
snap_remover->parent = this;
|
snap_remover->parent = this;
|
||||||
snap_remover->from_name = cmd.size() > 1 ? cmd[1].string_value() : "";
|
snap_remover->from_name = cmd.size() > 1 ? cmd[1].string_value() : "";
|
||||||
snap_remover->to_name = cmd.size() > 2 ? cmd[2].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->fsync_interval = cfg["fsync-interval"].uint64_value();
|
snap_remover->fsync_interval = cfg["fsync-interval"].uint64_value();
|
||||||
if (!snap_remover->fsync_interval)
|
if (!snap_remover->fsync_interval)
|
||||||
snap_remover->fsync_interval = 128;
|
snap_remover->fsync_interval = 128;
|
||||||
|
@ -555,11 +621,12 @@ std::function<bool(void)> cli_tool_t::start_snap_rm(json11::Json cfg)
|
||||||
snap_remover->use_cas = cfg["cas"].uint64_value() ? 2 : 0;
|
snap_remover->use_cas = cfg["cas"].uint64_value() ? 2 : 0;
|
||||||
if (!cfg["writers_stopped"].is_null())
|
if (!cfg["writers_stopped"].is_null())
|
||||||
snap_remover->writers_stopped = true;
|
snap_remover->writers_stopped = true;
|
||||||
return [snap_remover]()
|
return [snap_remover](cli_result_t & result)
|
||||||
{
|
{
|
||||||
snap_remover->loop();
|
snap_remover->loop();
|
||||||
if (snap_remover->is_done())
|
if (snap_remover->is_done())
|
||||||
{
|
{
|
||||||
|
result = snap_remover->result;
|
||||||
delete snap_remover;
|
delete snap_remover;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ struct rm_inode_t
|
||||||
uint64_t pgs_to_list = 0;
|
uint64_t pgs_to_list = 0;
|
||||||
bool lists_done = false;
|
bool lists_done = false;
|
||||||
int state = 0;
|
int state = 0;
|
||||||
|
int error_count = 0;
|
||||||
|
|
||||||
|
cli_result_t result;
|
||||||
|
|
||||||
void start_delete()
|
void start_delete()
|
||||||
{
|
{
|
||||||
|
@ -74,8 +77,13 @@ struct rm_inode_t
|
||||||
});
|
});
|
||||||
if (!lister)
|
if (!lister)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to list inode %lu from pool %u objects\n", INODE_NO_POOL(inode), INODE_POOL(inode));
|
result = (cli_result_t){
|
||||||
exit(1);
|
.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);
|
pgs_to_list = parent->cli->list_pg_count(lister);
|
||||||
parent->cli->list_inode_next(lister, parent->parallel_osds);
|
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",
|
fprintf(stderr, "Failed to remove object %lx:%lx from PG %u (OSD %lu) (retval=%ld)\n",
|
||||||
op->req.rw.inode, op->req.rw.offset,
|
op->req.rw.inode, op->req.rw.offset,
|
||||||
cur_list->pg_num, cur_list->rm_osd_num, op->reply.hdr.retval);
|
cur_list->pg_num, cur_list->rm_osd_num, op->reply.hdr.retval);
|
||||||
|
error_count++;
|
||||||
}
|
}
|
||||||
delete op;
|
delete op;
|
||||||
cur_list->obj_done++;
|
cur_list->obj_done++;
|
||||||
|
@ -161,31 +170,41 @@ struct rm_inode_t
|
||||||
}
|
}
|
||||||
if (lists_done && !lists.size())
|
if (lists_done && !lists.size())
|
||||||
{
|
{
|
||||||
printf("Done, inode %lu in pool %u data removed\n", INODE_NO_POOL(inode), pool_id);
|
result = (cli_result_t){
|
||||||
state = 2;
|
.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 (!pool_id)
|
||||||
{
|
{
|
||||||
start_delete();
|
result = (cli_result_t){ .err = EINVAL, .text = "Pool is not specified" };
|
||||||
state = 1;
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (state == 1)
|
start_delete();
|
||||||
{
|
if (state == 100)
|
||||||
continue_delete();
|
return;
|
||||||
}
|
state = 1;
|
||||||
else if (state == 2)
|
resume_1:
|
||||||
{
|
continue_delete();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_rm(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_rm(json11::Json cfg)
|
||||||
{
|
{
|
||||||
auto remover = new rm_inode_t();
|
auto remover = new rm_inode_t();
|
||||||
remover->parent = this;
|
remover->parent = this;
|
||||||
|
@ -196,16 +215,13 @@ std::function<bool(void)> 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->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);
|
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();
|
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;
|
delete remover;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
// Calculate offsets for a block device and print OSD command line parameters
|
// Calculate offsets for a block device and print OSD command line parameters
|
||||||
std::function<bool(void)> cli_tool_t::simple_offsets(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::simple_offsets(json11::Json cfg)
|
||||||
{
|
{
|
||||||
std::string device = cfg["command"][1].string_value();
|
std::string device = cfg["command"][1].string_value();
|
||||||
uint64_t object_size = parse_size(cfg["object_size"].string_value());
|
uint64_t object_size = parse_size(cfg["object_size"].string_value());
|
||||||
|
|
|
@ -277,16 +277,17 @@ resume_2:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_status(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_status(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
auto printer = new status_printer_t();
|
auto printer = new status_printer_t();
|
||||||
printer->parent = this;
|
printer->parent = this;
|
||||||
return [printer]()
|
return [printer](cli_result_t & result)
|
||||||
{
|
{
|
||||||
printer->loop();
|
printer->loop();
|
||||||
if (printer->is_done())
|
if (printer->is_done())
|
||||||
{
|
{
|
||||||
|
result = { .err = 0 };
|
||||||
delete printer;
|
delete printer;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue