Skip to content

Commit

Permalink
rbd trash: replace cli delay option, add rbd trash purge command
Browse files Browse the repository at this point in the history
Replaced the delay argument for the trash move
command with a string acceptable by /bin/date, e.g.:

$ rbd trash move --pool foo --image bar --expires-in "2 weeks"

Added a "rbd trash purge" command that deletes any expired
image from the trash, has also a command to alter the current
expiration date with the "--older-than" argument which accepts
again a valid argument for /bin/date, e.g.:

$ rbd trash purge mypool --older-than "2017-08-20"

There is also the "threshold" argument which tries to remove the
oldest trashed images (by deferment end time) until the pool space
is freed up to a percentage point, e.g.:

$ rbd trash purge mypool --threshold 0.9

If mypool uses 1GB it will try to remove trashed images until the
pool usage becomes equal to or lower than 900MB.

Signed-off-by: Theofilos Mouratidis <t.mour@cern.ch>
  • Loading branch information
Theofilos Mouratidis committed Nov 29, 2017
1 parent df43e41 commit 4caeae1
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 43 deletions.
28 changes: 28 additions & 0 deletions src/include/utime.h
Expand Up @@ -24,6 +24,8 @@
#include "include/timegm.h"
#include "common/strtol.h"
#include "common/ceph_time.h"
#include "common/safe_io.h"
#include "common/SubProcess.h"
#include "include/denc.h"


Expand Down Expand Up @@ -333,6 +335,32 @@ class utime_t {
bdt.tm_hour, bdt.tm_min, bdt.tm_sec);
}

static int invoke_date(const std::string& date_str, utime_t *result) {
char buf[256];

SubProcess bin_date("/bin/date", SubProcess::CLOSE, SubProcess::PIPE, SubProcess::KEEP);
bin_date.add_cmd_args("-d", date_str.c_str(), "+%s %N", NULL);

int r = bin_date.spawn();
if (r < 0) return r;

ssize_t n = safe_read(bin_date.get_stdout(), buf, sizeof(buf));

r = bin_date.join();
if (r || n <= 0) return -EINVAL;

uint64_t epoch, nsec;
std::istringstream iss(buf);

iss >> epoch;
iss >> nsec;

*result = utime_t(epoch, nsec);

return 0;
}


static int parse_date(const string& date, uint64_t *epoch, uint64_t *nsec,
string *out_date=NULL, string *out_time=NULL) {
struct tm tm;
Expand Down
3 changes: 0 additions & 3 deletions src/librbd/internal.cc
Expand Up @@ -2355,9 +2355,6 @@ bool compare_by_name(const child_info_t& c1, const child_info_t& c2)
ictx->perfcounter->inc(l_librbd_readahead_bytes, readahead_length);
}
}



}

std::ostream &operator<<(std::ostream &os, const librbd::ImageOptions &opts) {
Expand Down
2 changes: 1 addition & 1 deletion src/librbd/internal.h
Expand Up @@ -107,7 +107,7 @@ namespace librbd {
int remove(librados::IoCtx& io_ctx, const std::string &image_name,
const std::string &image_id, ProgressContext& prog_ctx,
bool force=false, bool from_trash_remove=false);

int trash_move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
const std::string &image_name, uint64_t delay);
int trash_get(IoCtx &io_ctx, const std::string &id, trash_image_info_t *info);
Expand Down
30 changes: 25 additions & 5 deletions src/test/cli/rbd/help.t
Expand Up @@ -101,6 +101,7 @@ Skip test on FreeBSD as it generates different output there.
status Show the status of this image.
trash list (trash ls) List trash images.
trash move (trash mv) Move an image to the trash.
trash purge Remove all expired images from trash.
trash remove (trash rm) Remove an image from trash.
trash restore Restore an image from trash.
unmap Unmap a rbd device that was used by the
Expand Down Expand Up @@ -1488,19 +1489,38 @@ Skip test on FreeBSD as it generates different output there.
--pretty-format pretty formatting (json and xml)

rbd help trash move
usage: rbd trash move [--pool <pool>] [--image <image>] [--delay <delay>]
usage: rbd trash move [--pool <pool>] [--image <image>]
[--expires-in <expires-in>]
<image-spec>

Move an image to the trash.

Positional arguments
<image-spec> image specification
(example: [<pool-name>/]<image-name>)
<image-spec> image specification
(example: [<pool-name>/]<image-name>)

Optional arguments
-p [ --pool ] arg pool name
--image arg image name
--expires-in arg (=1 day) set the expiration time of an image so it can be
purged when it is stale

rbd help trash purge
usage: rbd trash purge [--pool <pool>] [--no-progress]
[--older-than <older-than>] [--threshold <threshold>]
<pool-name>

Remove all expired images from trash.

Positional arguments
<pool-name> pool name

Optional arguments
-p [ --pool ] arg pool name
--image arg image name
--delay arg time delay in seconds until effectively remove the image
--no-progress disable progress output
--older-than date purges images that expired before the given date
--threshold arg purges images until the current pool data usage is
reduced to X%, value range: 0.0-1.0

rbd help trash remove
usage: rbd trash remove [--pool <pool>] [--image-id <image-id>]
Expand Down
4 changes: 3 additions & 1 deletion src/tools/rbd/ArgumentTypes.h
Expand Up @@ -81,7 +81,9 @@ static const std::string PRETTY_FORMAT("pretty-format");
static const std::string VERBOSE("verbose");
static const std::string NO_ERROR("no-error");

static const std::string DELAY("delay");
static const std::string EXPIRES_IN("expires-in");
static const std::string OLDER_THAN("older-than");
static const std::string THRESHOLD("threshold");

static const std::string LIMIT("limit");

Expand Down
191 changes: 158 additions & 33 deletions src/tools/rbd/action/Trash.cc
Expand Up @@ -31,13 +31,14 @@ namespace trash {
namespace at = argument_types;
namespace po = boost::program_options;

typedef librbd::trash_image_info_t trash_info_t;

void get_move_arguments(po::options_description *positional,
po::options_description *options) {
at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
options->add_options()
(at::DELAY.c_str(), po::value<uint64_t>(),
"time delay in seconds until effectively remove the image");
(at::EXPIRES_IN.c_str(), po::value<std::string>()->default_value("1 day"),
"set the expiration time of an image so it can be purged when it is stale");
}

int execute_move(const po::variables_map &vm) {
Expand All @@ -60,20 +61,26 @@ int execute_move(const po::variables_map &vm) {
return r;
}

uint64_t delay = 0;
if (vm.find(at::DELAY) != vm.end()) {
delay = vm[at::DELAY].as<uint64_t>();
std::string expires_in;
if (vm.find(at::EXPIRES_IN) != vm.end()) {
expires_in = vm[at::EXPIRES_IN].as<std::string>();
}

librbd::RBD rbd;
r = rbd.trash_move(io_ctx, image_name.c_str(), delay);

utime_t t_exp, now = ceph_clock_now();
r = utime_t::invoke_date(expires_in, &t_exp);
if (r < 0) {
return r;
}

r = rbd.trash_move(io_ctx, image_name.c_str(), t_exp.sec() - now.sec());
if (r < 0) {
std::cerr << "rbd: deferred delete error: " << cpp_strerror(r)
<< std::endl;
}

return r;

}

void get_remove_arguments(po::options_description *positional,
Expand All @@ -88,28 +95,7 @@ void get_remove_arguments(po::options_description *positional,
("force", po::bool_switch(), "force remove of non-expired delayed images");
}

int execute_remove(const po::variables_map &vm) {
size_t arg_index = 0;
std::string pool_name;
std::string image_id;
int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &image_id);
if (r < 0) {
return r;
}

librados::Rados rados;
librados::IoCtx io_ctx;
r = utils::init(pool_name, &rados, &io_ctx);
if (r < 0) {
return r;
}

librbd::RBD rbd;

utils::ProgressContext pc("Removing image", vm[at::NO_PROGRESS].as<bool>());
r = rbd.trash_remove_with_progress(io_ctx, image_id.c_str(),
vm["force"].as<bool>(), pc);
if (r < 0) {
void remove_error_check(int r) {
if (r == -ENOTEMPTY) {
std::cerr << "rbd: image has snapshots - these must be deleted"
<< " with 'rbd snap purge' before the image can be removed."
Expand All @@ -133,6 +119,31 @@ int execute_remove(const po::variables_map &vm) {
} else {
std::cerr << "rbd: remove error: " << cpp_strerror(r) << std::endl;
}
}

int execute_remove(const po::variables_map &vm) {
size_t arg_index = 0;
std::string pool_name;
std::string image_id;
int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &image_id);
if (r < 0) {
return r;
}

librados::Rados rados;
librados::IoCtx io_ctx;
r = utils::init(pool_name, &rados, &io_ctx);
if (r < 0) {
return r;
}

librbd::RBD rbd;

utils::ProgressContext pc("Removing image", vm[at::NO_PROGRESS].as<bool>());
r = rbd.trash_remove_with_progress(io_ctx, image_id.c_str(),
vm["force"].as<bool>(), pc);
if (r < 0) {
remove_error_check(r);
pc.fail();
return r;
}
Expand All @@ -151,6 +162,8 @@ std::string delete_status(time_t deferment_end_time) {
std::stringstream ss;
if (now < deferment_end_time) {
ss << "protected until " << time_str;
} else {
ss << "expired at " << time_str;
}

return ss.str();
Expand Down Expand Up @@ -300,6 +313,114 @@ int execute_list(const po::variables_map &vm) {
return 0;
}

void get_purge_arguments(po::options_description *positional,
po::options_description *options) {
at::add_pool_options(positional, options);
at::add_no_progress_option(options);

options->add_options()
(at::OLDER_THAN.c_str(), po::value<std::string>()->value_name("date"),
"purges images that expired before the given date");
options->add_options()
(at::THRESHOLD.c_str(), po::value<double>(), "purges images until the current pool data usage is reduced to X%, value range: 0.0-1.0");
}


int execute_purge (const po::variables_map &vm) {
size_t arg_index = 0;
std::string pool_name = utils::get_pool_name(vm, &arg_index);

librados::Rados rados;
librados::IoCtx io_ctx;
int r = utils::init(pool_name, &rados, &io_ctx);
if (r < 0) {
return r;
}

librbd::RBD rbd;

std::vector<trash_info_t> trash_entries;
r = rbd.trash_list(io_ctx, trash_entries);
if (r < 0) { return r; }


utils::ProgressContext pc("Removing images", vm[at::NO_PROGRESS].as<bool>());
std::vector<const char *> to_be_removed;

if (vm.find(at::THRESHOLD) != vm.end()) {
double threshold = vm[at::THRESHOLD].as<double>();
if (threshold < 0 || threshold > 1) {
std::cout << "rbd: argument 'threshold' is out of valid range" << std::endl;
return -EINVAL;
}
std::sort(trash_entries.begin(), trash_entries.end(),
[](trash_info_t a, trash_info_t b) {
return a.deferment_end_time < b.deferment_end_time;
}
);

std::list<std::string> vec = { pool_name };
std::map<std::string,librados::pool_stat_t> stats;
rados.get_pool_stats(vec, stats);

uint64_t bytes_to_free = 0;
uint64_t bytes_threshold = stats[pool_name].num_bytes * (1-threshold);

librbd::Image curr_img;
for (const auto& entry : trash_entries) {
r = utils::open_image_by_id(io_ctx, entry.id, true, &curr_img);
if(r < 0) continue;

uint64_t img_size; curr_img.size(&img_size);
r = curr_img.diff_iterate2(nullptr, 0, img_size, false, false,
[](uint64_t offset, size_t len, int exists, void *arg) {
uint64_t *to_free = reinterpret_cast<uint64_t *>(arg);
if (exists) (*to_free) += len;
return 0;
}, &bytes_to_free
);
if(r < 0) continue;
to_be_removed.push_back(entry.id.c_str());
if(bytes_to_free >= bytes_threshold) break;
}
}
else {
time_t expire_ts = ceph_clock_gettime();
if (vm.find(at::OLDER_THAN) != vm.end()) {
utime_t new_time;
r = utime_t::invoke_date(vm[at::OLDER_THAN].as<std::string>(), &new_time);
if (r < 0) return r;
expire_ts = new_time.sec();
}

for(const auto &entry : trash_entries) {
if (expire_ts >= entry.deferment_end_time) {
to_be_removed.push_back(entry.id.c_str());
}
}
}

uint64_t list_size = to_be_removed.size(), i = 0;

if(list_size == 0) {
std::cout << "rbd: nothing to remove" << std::endl;
} else {
for(const auto &entry_id : to_be_removed) {
r = rbd.trash_remove(io_ctx, entry_id, true);
if (r < 0) {
remove_error_check(r);
pc.fail();
return r;
}
pc.update_progress(++i, list_size);
}
}

pc.finish();

return 0;
}

void get_restore_arguments(po::options_description *positional,
po::options_description *options) {
positional->add_options()
Expand Down Expand Up @@ -351,21 +472,25 @@ int execute_restore(const po::variables_map &vm) {


Shell::Action action_move(
{"trash", "move"}, {"trash", "mv"}, "Move an image to the trash.", "",
&get_move_arguments, &execute_move);
{"trash", "move"}, {"trash", "mv"}, "Move an image to the trash.", "",
&get_move_arguments, &execute_move);

Shell::Action action_remove(
{"trash", "remove"}, {"trash", "rm"}, "Remove an image from trash.", "",
&get_remove_arguments, &execute_remove);

Shell::Action action_purge(
{"trash", "purge"}, {}, "Remove all expired images from trash.", "",
&get_purge_arguments, &execute_purge);

Shell::SwitchArguments switched_arguments({"long", "l"});
Shell::Action action_list(
{"trash", "list"}, {"trash", "ls"}, "List trash images.", "",
&get_list_arguments, &execute_list);

Shell::Action action_restore(
{"trash", "restore"}, {}, "Restore an image from trash.", "",
&get_restore_arguments, &execute_restore);
{"trash", "restore"}, {}, "Restore an image from trash.", "",
&get_restore_arguments, &execute_restore);

} // namespace trash
} // namespace action
Expand Down

0 comments on commit 4caeae1

Please sign in to comment.