#include <iostream>
#include <sstream>
#include <boost/program_options.hpp>
+#include <boost/bind.hpp>
namespace rbd {
namespace action {
namespace at = argument_types;
namespace po = boost::program_options;
+//Optional arguments used only by this set of commands (rbd trash *)
+static const std::string EXPIRES_AT("expires-at");
+static const std::string EXPIRED_BEFORE("expired-before");
+static const std::string THRESHOLD("threshold");
+
+static bool is_not_trash_user(const librbd::trash_image_info_t &trash_info) {
+ return trash_info.source != RBD_TRASH_IMAGE_SOURCE_USER;
+}
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");
+ (EXPIRES_AT.c_str(), po::value<std::string>()->default_value("now"),
+ "set the expiration time of an image so it can be purged when it is stale");
}
-int execute_move(const po::variables_map &vm) {
+int execute_move(const po::variables_map &vm,
+ const std::vector<std::string> &ceph_global_init_args) {
size_t arg_index = 0;
std::string pool_name;
+ std::string namespace_name;
std::string image_name;
std::string snap_name;
int r = utils::get_pool_image_snapshot_names(
- vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
- &snap_name, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE);
+ vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
+ &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
+ utils::SPEC_VALIDATION_NONE);
if (r < 0) {
return r;
}
librados::Rados rados;
librados::IoCtx io_ctx;
- r = utils::init(pool_name, &rados, &io_ctx);
+ r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
if (r < 0) {
return r;
}
- uint64_t delay = 0;
- if (vm.find(at::DELAY) != vm.end()) {
- delay = vm[at::DELAY].as<uint64_t>();
+ utime_t now = ceph_clock_now();
+ utime_t exp_time = now;
+ std::string expires_at;
+ if (vm.find(EXPIRES_AT) != vm.end()) {
+ expires_at = vm[EXPIRES_AT].as<std::string>();
+ r = utime_t::invoke_date(expires_at, &exp_time);
+ if (r < 0) {
+ std::cerr << "rbd: error calling /bin/date: " << cpp_strerror(r)
+ << std::endl;
+ return r;
+ }
+ }
+
+ time_t dt = (exp_time - now).sec();
+ if(dt < 0) {
+ std::cerr << "rbd: cannot use a date in the past as an expiration date"
+ << std::endl;
+ return -EINVAL;
}
librbd::RBD rbd;
- r = rbd.trash_move(io_ctx, image_name.c_str(), delay);
+ r = rbd.trash_move(io_ctx, image_name.c_str(), dt);
if (r < 0) {
std::cerr << "rbd: deferred delete error: " << cpp_strerror(r)
<< std::endl;
}
return r;
-
}
void get_remove_arguments(po::options_description *positional,
po::options_description *options) {
positional->add_options()
- (at::IMAGE_ID.c_str(), "image id\n(example: [<pool-name>/]<image-id>)");
+ (at::IMAGE_ID.c_str(), "image id\n(example: [<pool-name>/[<namespace>/]]<image-id>)");
at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
+ at::add_namespace_option(options, at::ARGUMENT_MODIFIER_NONE);
at::add_image_id_option(options);
at::add_no_progress_option(options);
("force", po::bool_switch(), "force remove of non-expired delayed images");
}
-int execute_remove(const po::variables_map &vm) {
+int execute_remove(const po::variables_map &vm,
+ const std::vector<std::string> &ceph_global_init_args) {
size_t arg_index = 0;
std::string pool_name;
+ std::string namespace_name;
std::string image_id;
- int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &image_id);
+ int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &namespace_name,
+ &image_id);
if (r < 0) {
return r;
}
librados::Rados rados;
librados::IoCtx io_ctx;
- r = utils::init(pool_name, &rados, &io_ctx);
+ r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
if (r < 0) {
return r;
}
+ io_ctx.set_osdmap_full_try();
librbd::RBD rbd;
utils::ProgressContext pc("Removing image", vm[at::NO_PROGRESS].as<bool>());
<< std::endl;
} else if (r == -EMLINK) {
std::cerr << std::endl
- << "Remove the image from the consistency group and try again."
- << std::endl;
+ << "Remove the image from the group and try again."
+ << std::endl;
} else if (r == -EPERM) {
std::cerr << std::endl
<< "Deferment time has not expired, please use --force if you "
}
std::string delete_status(time_t deferment_end_time) {
- time_t now = ceph_clock_gettime();
+ time_t now = time(nullptr);
std::string time_str = ctime(&deferment_end_time);
time_str = time_str.substr(0, time_str.length() - 1);
std::stringstream ss;
if (now < deferment_end_time) {
ss << "protected until " << time_str;
+ } else {
+ ss << "expired at " << time_str;
}
return ss.str();
return r;
}
+ if (!all_flag) {
+ trash_entries.erase(remove_if(trash_entries.begin(),
+ trash_entries.end(),
+ boost::bind(is_not_trash_user, _1)),
+ trash_entries.end());
+ }
+
if (!long_flag) {
if (f) {
f->open_array_section("trash");
}
for (const auto& entry : trash_entries) {
- if (!all_flag &&
- entry.source == RBD_TRASH_IMAGE_SOURCE_MIRRORING) {
- continue;
- }
if (f) {
+ f->open_object_section("image");
f->dump_string("id", entry.id);
f->dump_string("name", entry.name);
+ f->close_section();
} else {
std::cout << entry.id << " " << entry.name << std::endl;
}
tbl.define_column("SOURCE", TextTable::LEFT, TextTable::LEFT);
tbl.define_column("DELETED_AT", TextTable::LEFT, TextTable::LEFT);
tbl.define_column("STATUS", TextTable::LEFT, TextTable::LEFT);
+ tbl.define_column("PARENT", TextTable::LEFT, TextTable::LEFT);
}
for (const auto& entry : trash_entries) {
- if (!all_flag &&
- entry.source == RBD_TRASH_IMAGE_SOURCE_MIRRORING) {
- continue;
- }
librbd::Image im;
r = rbd.open_by_id_read_only(io_ctx, im, entry.id.c_str(), NULL);
case RBD_TRASH_IMAGE_SOURCE_MIRRORING:
del_source = "MIRRORING";
break;
+ case RBD_TRASH_IMAGE_SOURCE_MIGRATION:
+ del_source = "MIGRATION";
+ break;
+ case RBD_TRASH_IMAGE_SOURCE_REMOVING:
+ del_source = "REMOVING";
+ break;
}
std::string time_str = ctime(&entry.deletion_time);
time_str = time_str.substr(0, time_str.length() - 1);
+ bool has_parent = false;
+ std::string parent;
+ librbd::linked_image_spec_t parent_image;
+ librbd::snap_spec_t parent_snap;
+ r = im.get_parent(&parent_image, &parent_snap);
+ if (r == -ENOENT) {
+ r = 0;
+ } else if (r < 0) {
+ return r;
+ } else {
+ parent = parent_image.pool_name + "/";
+ if (!parent_image.pool_namespace.empty()) {
+ parent += parent_image.pool_namespace + "/";
+ }
+ parent += parent_image.image_name + "@" + parent_snap.name;
+ has_parent = true;
+ }
+
if (f) {
f->open_object_section("image");
f->dump_string("id", entry.id);
f->dump_string("deleted_at", time_str);
f->dump_string("status",
delete_status(entry.deferment_end_time));
+ if (has_parent) {
+ f->open_object_section("parent");
+ f->dump_string("pool", parent_image.pool_name);
+ f->dump_string("pool_namespace", parent_image.pool_namespace);
+ f->dump_string("image", parent_image.image_name);
+ f->dump_string("snapshot", parent_snap.name);
+ f->close_section();
+ }
f->close_section();
} else {
tbl << entry.id
<< entry.name
<< del_source
<< time_str
- << delete_status(entry.deferment_end_time)
- << TextTable::endrow;
+ << delete_status(entry.deferment_end_time);
+ if (has_parent)
+ tbl << parent;
+ tbl << TextTable::endrow;
}
}
void get_list_arguments(po::options_description *positional,
po::options_description *options) {
- at::add_pool_options(positional, options);
+ at::add_pool_options(positional, options, true);
options->add_options()
("all,a", po::bool_switch(), "list images from all sources");
options->add_options()
at::add_format_options(options);
}
-int execute_list(const po::variables_map &vm) {
+int execute_list(const po::variables_map &vm,
+ const std::vector<std::string> &ceph_global_init_args) {
+ std::string pool_name;
+ std::string namespace_name;
size_t arg_index = 0;
- std::string pool_name = utils::get_pool_name(vm, &arg_index);
+ int r = utils::get_pool_and_namespace_names(vm, true, false, &pool_name,
+ &namespace_name, &arg_index);
+ if (r < 0) {
+ return r;
+ }
at::Format::Formatter formatter;
- int r = utils::get_formatter(vm, &formatter);
+ r = utils::get_formatter(vm, &formatter);
if (r < 0) {
return r;
}
librados::Rados rados;
librados::IoCtx io_ctx;
- r = utils::init(pool_name, &rados, &io_ctx);
+ r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
if (r < 0) {
return r;
}
+ utils::disable_cache();
+
librbd::RBD rbd;
r = do_list(rbd, io_ctx, vm["long"].as<bool>(), vm["all"].as<bool>(),
formatter.get());
return 0;
}
+void get_purge_arguments(po::options_description *positional,
+ po::options_description *options) {
+ at::add_pool_options(positional, options, true);
+ at::add_no_progress_option(options);
+
+ options->add_options()
+ (EXPIRED_BEFORE.c_str(), po::value<std::string>()->value_name("date"),
+ "purges images that expired before the given date");
+ options->add_options()
+ (THRESHOLD.c_str(), po::value<float>(),
+ "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,
+ const std::vector<std::string> &ceph_global_init_args) {
+ std::string pool_name;
+ std::string namespace_name;
+ size_t arg_index = 0;
+ int r = utils::get_pool_and_namespace_names(vm, true, false, &pool_name,
+ &namespace_name, &arg_index);
+ if (r < 0) {
+ return r;
+ }
+
+ utils::disable_cache();
+
+ librbd::RBD rbd;
+
+ librados::Rados rados;
+ librados::IoCtx io_ctx;
+ r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
+ if (r < 0) {
+ return r;
+ }
+
+ io_ctx.set_osdmap_full_try();
+
+ float threshold = -1;
+ time_t expire_ts = 0;
+
+ if (vm.find(THRESHOLD) != vm.end()) {
+ threshold = vm[THRESHOLD].as<float>();
+ } else {
+ if (vm.find(EXPIRED_BEFORE) != vm.end()) {
+ utime_t new_time;
+ r = utime_t::invoke_date(vm[EXPIRED_BEFORE].as<std::string>(), &new_time);
+ if (r < 0) {
+ std::cerr << "rbd: error calling /bin/date: " << cpp_strerror(r)
+ << std::endl;
+ return r;
+ }
+ expire_ts = new_time.sec();
+ }
+ }
+
+ utils::ProgressContext pc("Removing images", vm[at::NO_PROGRESS].as<bool>());
+ r = rbd.trash_purge_with_progress(io_ctx, expire_ts, threshold, pc);
+ if (r < 0) {
+ pc.fail();
+ } else {
+ pc.finish();
+ }
+
+ return 0;
+}
+
void get_restore_arguments(po::options_description *positional,
po::options_description *options) {
positional->add_options()
(at::IMAGE_ID.c_str(), "image id\n(example: [<pool-name>/]<image-id>)");
at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
+ at::add_namespace_option(options, at::ARGUMENT_MODIFIER_NONE);
at::add_image_id_option(options);
at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE, "");
}
-int execute_restore(const po::variables_map &vm) {
+int execute_restore(const po::variables_map &vm,
+ const std::vector<std::string> &ceph_global_init_args) {
size_t arg_index = 0;
std::string pool_name;
+ std::string namespace_name;
std::string image_id;
- int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &image_id);
+ int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &namespace_name,
+ &image_id);
if (r < 0) {
return r;
}
librados::Rados rados;
librados::IoCtx io_ctx;
- r = utils::init(pool_name, &rados, &io_ctx);
+ r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
if (r < 0) {
return r;
}
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