2021-09-26 00:48:08 +03:00
|
|
|
// Copyright (c) Vitaliy Filippov, 2019+
|
|
|
|
// License: VNPL-1.1 (see README.md for details)
|
|
|
|
|
|
|
|
#include "cli.h"
|
|
|
|
#include "cluster_client.h"
|
2022-01-18 10:30:53 +03:00
|
|
|
#include <sys/stat.h>
|
2021-09-26 00:48:08 +03:00
|
|
|
|
|
|
|
// Flatten a layer: merge all parents into a layer and break the connection completely
|
|
|
|
struct snap_flattener_t
|
|
|
|
{
|
|
|
|
cli_tool_t *parent;
|
|
|
|
|
|
|
|
// target to flatten
|
|
|
|
std::string target_name;
|
|
|
|
// writers are stopped, we can safely change writable layers
|
|
|
|
bool writers_stopped = false;
|
|
|
|
// use CAS writes (0 = never, 1 = auto, 2 = always)
|
|
|
|
int use_cas = 1;
|
|
|
|
// interval between fsyncs
|
|
|
|
int fsync_interval = 128;
|
|
|
|
|
|
|
|
std::string top_parent_name;
|
|
|
|
inode_t target_id = 0;
|
|
|
|
int state = 0;
|
2022-04-03 20:14:51 +03:00
|
|
|
std::function<bool(cli_result_t &)> merger_cb;
|
|
|
|
cli_result_t result;
|
2021-09-26 00:48:08 +03:00
|
|
|
|
|
|
|
void get_merge_parents()
|
|
|
|
{
|
|
|
|
// Get all parents of target
|
|
|
|
inode_config_t *target_cfg = parent->get_inode_cfg(target_name);
|
2022-04-03 20:14:51 +03:00
|
|
|
if (!target_cfg)
|
|
|
|
{
|
|
|
|
result = (cli_result_t){ .err = ENOENT, .text = "Layer "+target_name+" not found" };
|
|
|
|
state = 100;
|
|
|
|
return;
|
|
|
|
}
|
2021-09-26 00:48:08 +03:00
|
|
|
target_id = target_cfg->num;
|
|
|
|
std::vector<inode_t> chain_list;
|
|
|
|
inode_config_t *cur = target_cfg;
|
|
|
|
chain_list.push_back(cur->num);
|
|
|
|
while (cur->parent_id != 0 && cur->parent_id != target_cfg->num)
|
|
|
|
{
|
|
|
|
auto it = parent->cli->st_cli.inode_config.find(cur->parent_id);
|
|
|
|
if (it == parent->cli->st_cli.inode_config.end())
|
|
|
|
{
|
2022-04-03 20:14:51 +03:00
|
|
|
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;
|
2021-09-26 00:48:08 +03:00
|
|
|
}
|
|
|
|
cur = &it->second;
|
|
|
|
chain_list.push_back(cur->num);
|
|
|
|
}
|
|
|
|
if (cur->parent_id != 0)
|
|
|
|
{
|
2022-04-03 20:14:51 +03:00
|
|
|
result = (cli_result_t){ .err = EBADF, .text = "Layer "+target_name+" has a loop in parents" };
|
|
|
|
state = 100;
|
|
|
|
return;
|
2021-09-26 00:48:08 +03:00
|
|
|
}
|
|
|
|
top_parent_name = cur->name;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_done()
|
|
|
|
{
|
2022-04-03 20:14:51 +03:00
|
|
|
return state == 100;
|
2021-09-26 00:48:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void loop()
|
|
|
|
{
|
|
|
|
if (state == 1)
|
|
|
|
goto resume_1;
|
|
|
|
else if (state == 2)
|
|
|
|
goto resume_2;
|
|
|
|
else if (state == 3)
|
|
|
|
goto resume_3;
|
2022-04-03 20:14:51 +03:00
|
|
|
if (target_name == "")
|
|
|
|
{
|
|
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Layer to flatten not specified" };
|
|
|
|
state = 100;
|
|
|
|
return;
|
|
|
|
}
|
2021-09-26 00:48:08 +03:00
|
|
|
// Get parent layers
|
|
|
|
get_merge_parents();
|
2022-04-03 20:14:51 +03:00
|
|
|
if (state == 100)
|
|
|
|
return;
|
2021-09-26 00:48:08 +03:00
|
|
|
// Start merger
|
|
|
|
merger_cb = parent->start_merge(json11::Json::object {
|
2022-04-03 20:14:51 +03:00
|
|
|
{ "from", top_parent_name },
|
|
|
|
{ "to", target_name },
|
2021-09-26 00:48:08 +03:00
|
|
|
{ "target", target_name },
|
|
|
|
{ "delete-source", false },
|
|
|
|
{ "cas", use_cas },
|
|
|
|
{ "fsync-interval", fsync_interval },
|
|
|
|
});
|
|
|
|
// Wait for it
|
|
|
|
resume_1:
|
2022-04-03 20:14:51 +03:00
|
|
|
while (!merger_cb(result))
|
2021-09-26 00:48:08 +03:00
|
|
|
{
|
|
|
|
state = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
merger_cb = NULL;
|
2022-04-03 20:14:51 +03:00
|
|
|
if (result.err)
|
|
|
|
{
|
|
|
|
state = 100;
|
|
|
|
return;
|
|
|
|
}
|
2021-09-26 00:48:08 +03:00
|
|
|
// Change parent
|
2022-04-03 20:14:51 +03:00
|
|
|
parent->change_parent(target_id, 0, &result);
|
2021-09-26 00:48:08 +03:00
|
|
|
// Wait for it to complete
|
|
|
|
state = 2;
|
|
|
|
resume_2:
|
|
|
|
if (parent->waiting > 0)
|
|
|
|
return;
|
|
|
|
state = 3;
|
|
|
|
resume_3:
|
|
|
|
// Done
|
2022-04-03 20:14:51 +03:00
|
|
|
state = 100;
|
2021-09-26 00:48:08 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-04-03 20:14:51 +03:00
|
|
|
std::function<bool(cli_result_t &)> cli_tool_t::start_flatten(json11::Json cfg)
|
2021-09-26 00:48:08 +03:00
|
|
|
{
|
|
|
|
auto flattener = new snap_flattener_t();
|
|
|
|
flattener->parent = this;
|
2022-04-03 20:14:51 +03:00
|
|
|
flattener->target_name = cfg["image"].string_value();
|
2021-09-26 00:48:08 +03:00
|
|
|
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;
|
2022-04-03 20:14:51 +03:00
|
|
|
return [flattener](cli_result_t & result)
|
2021-09-26 00:48:08 +03:00
|
|
|
{
|
|
|
|
flattener->loop();
|
|
|
|
if (flattener->is_done())
|
|
|
|
{
|
2022-04-03 20:14:51 +03:00
|
|
|
result = flattener->result;
|
2021-09-26 00:48:08 +03:00
|
|
|
delete flattener;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
}
|