RBD_IMAGE_OPTION_DATA_POOL = 10
};
+typedef enum {
+ RBD_TRASH_IMAGE_SOURCE_USER = 0,
+ RBD_TRASH_IMAGE_SOURCE_MIRRORING = 1
+} rbd_trash_image_source_t;
+
+typedef struct {
+ char *id;
+ char *name;
+ rbd_trash_image_source_t source;
+ time_t deletion_time;
+ time_t deferment_end_time;
+} rbd_trash_image_info_t;
+
CEPH_RBD_API void rbd_image_options_create(rbd_image_options_t* opts);
CEPH_RBD_API void rbd_image_options_destroy(rbd_image_options_t opts);
CEPH_RBD_API int rbd_image_options_set_string(rbd_image_options_t opts,
CEPH_RBD_API int rbd_remove_with_progress(rados_ioctx_t io, const char *name,
librbd_progress_fn_t cb,
void *cbdata);
+CEPH_RBD_API int rbd_trash_move(rados_ioctx_t io, const char *name,
+ uint64_t delay);
+CEPH_RBD_API int rbd_trash_list(rados_ioctx_t io,
+ rbd_trash_image_info_t *trash_entries,
+ size_t *num_entries);
+CEPH_RBD_API void rbd_trash_list_cleanup(rbd_trash_image_info_t *trash_entries,
+ size_t num_entries);
+CEPH_RBD_API int rbd_trash_remove(rados_ioctx_t io, const char *id, bool force);
+CEPH_RBD_API int rbd_trash_remove_with_progress(rados_ioctx_t io, const char *id,
+ bool force, librbd_progress_fn_t cb,
+ void *cbdata);
+CEPH_RBD_API int rbd_trash_restore(rados_ioctx_t io, const char *id,
+ const char *name);
CEPH_RBD_API int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname,
const char *destname);
virtual int update_progress(uint64_t offset, uint64_t total) = 0;
};
+ typedef struct {
+ std::string id;
+ std::string name;
+ rbd_trash_image_source_t source;
+ time_t deletion_time;
+ time_t deferment_end_time;
+ } trash_image_info_t;
+
class CEPH_RBD_API RBD
{
public:
IoCtx& c_ioctx, const char *c_name, ImageOptions& opts);
int remove(IoCtx& io_ctx, const char *name);
int remove_with_progress(IoCtx& io_ctx, const char *name, ProgressContext& pctx);
+ int trash_move(IoCtx &io_ctx, const char *name, uint64_t delay);
+ int trash_list(IoCtx &io_ctx, std::vector<trash_image_info_t> &entries);
+ int trash_remove(IoCtx &io_ctx, const char *image_id, bool force);
+ int trash_remove_with_progress(IoCtx &io_ctx, const char *image_id, bool force,
+ ProgressContext &pctx);
+ int trash_restore(IoCtx &io_ctx, const char *id, const char *name);
+
int rename(IoCtx& src_io_ctx, const char *srcname, const char *destname);
// RBD pool mirroring support functions
Context *ctx = create_context_callback<klass, &klass::handle_remove>(this);
librbd::image::RemoveRequest<> *req = librbd::image::RemoveRequest<>::create(
- m_ioctx, m_name, m_id, false, m_no_op, m_op_work_queue, ctx);
+ m_ioctx, m_name, m_id, false, false, m_no_op, m_op_work_queue, ctx);
req->send();
}
template<typename I>
RemoveRequest<I>::RemoveRequest(IoCtx &ioctx, const std::string &image_name,
const std::string &image_id, bool force,
+ bool from_trash_remove,
ProgressContext &prog_ctx,
ContextWQ *op_work_queue, Context *on_finish)
: m_ioctx(ioctx), m_image_name(image_name), m_image_id(image_id),
- m_force(force), m_prog_ctx(prog_ctx), m_op_work_queue(op_work_queue),
+ m_force(force), m_from_trash_remove(from_trash_remove),
+ m_prog_ctx(prog_ctx), m_op_work_queue(op_work_queue),
m_on_finish(on_finish) {
m_cct = reinterpret_cast<CephContext *>(m_ioctx.cct());
*result = 0;
}
+ if (m_from_trash_remove) {
+ // both the id object and the directory entry have been removed in
+ // a previous call to trash_move.
+
+ *result = 0;
+ return m_on_finish;
+ }
+
remove_id_object();
return nullptr;
}
static RemoveRequest *create(librados::IoCtx &ioctx,
const std::string &image_name,
const std::string &image_id,
- bool force, ProgressContext &prog_ctx,
+ bool force, bool from_trash_remove,
+ ProgressContext &prog_ctx,
ContextWQ *op_work_queue,
Context *on_finish) {
- return new RemoveRequest(ioctx, image_name, image_id, force, prog_ctx,
- op_work_queue, on_finish);
+ return new RemoveRequest(ioctx, image_name, image_id, force,
+ from_trash_remove, prog_ctx, op_work_queue,
+ on_finish);
}
void send();
*/
RemoveRequest(librados::IoCtx &ioctx, const std::string &image_name,
- const std::string &image_id, bool force,
+ const std::string &image_id, bool force, bool from_trash_remove,
ProgressContext &prog_ctx, ContextWQ *op_work_queue,
Context *on_finish);
std::string m_image_name;
std::string m_image_id;
bool m_force;
+ bool m_from_trash_remove;
ProgressContext &m_prog_ctx;
ContextWQ *m_op_work_queue;
Context *m_on_finish;
int remove(IoCtx& io_ctx, const std::string &image_name,
const std::string &image_id, ProgressContext& prog_ctx,
- bool force)
+ bool force, bool from_trash_remove)
{
CephContext *cct((CephContext *)io_ctx.cct());
ldout(cct, 20) << "remove " << &io_ctx << " "
C_SaferCond cond;
auto req = librbd::image::RemoveRequest<>::create(
- io_ctx, image_name, image_id, force, prog_ctx, op_work_queue, &cond);
+ io_ctx, image_name, image_id, force, from_trash_remove, prog_ctx,
+ op_work_queue, &cond);
req->send();
return cond.wait();
}
+ int trash_move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
+ const std::string &image_name, uint64_t delay) {
+ CephContext *cct((CephContext *)io_ctx.cct());
+ ldout(cct, 20) << "trash_move " << &io_ctx << " " << image_name
+ << dendl;
+
+ std::string image_id;
+ ImageCtx *ictx = new ImageCtx(image_name, "", nullptr, io_ctx, false);
+ int r = ictx->state->open(true);
+ if (r < 0) {
+ ldout(cct, 2) << "error opening image: " << cpp_strerror(-r) << dendl;
+ delete ictx;
+ if (r != -ENOENT) {
+ return r;
+ }
+ } else {
+ if (ictx->old_format) {
+ ictx->state->close();
+ return -EOPNOTSUPP;
+ }
+
+ image_id = ictx->id;
+ ictx->owner_lock.get_read();
+ if (ictx->exclusive_lock != nullptr) {
+ r = ictx->operations->prepare_image_update();
+ if (r < 0 || (ictx->exclusive_lock != nullptr &&
+ !ictx->exclusive_lock->is_lock_owner())) {
+ lderr(cct) << "cannot obtain exclusive lock - not removing" << dendl;
+ ictx->owner_lock.put_read();
+ ictx->state->close();
+ return -EBUSY;
+ }
+ }
+ }
+
+ BOOST_SCOPE_EXIT_ALL(ictx, cct) {
+ bool is_locked = ictx->exclusive_lock != nullptr &&
+ ictx->exclusive_lock->is_lock_owner();
+ if (is_locked) {
+ C_SaferCond ctx;
+ ictx->exclusive_lock->shut_down(&ctx);
+ ictx->owner_lock.put_read();
+ int r = ctx.wait();
+ if (r < 0) {
+ lderr(cct) << "error shutting down exclusive lock" << dendl;
+ }
+ } else {
+ ictx->owner_lock.put_read();
+ }
+ ictx->state->close();
+ };
+
+ ldout(cct, 2) << "adding image entry to rbd_trash" << dendl;
+ utime_t ts = ceph_clock_now();
+ utime_t deferment_end_time = ts;
+ deferment_end_time += (double)delay;
+ cls::rbd::TrashImageSource trash_source =
+ static_cast<cls::rbd::TrashImageSource>(source);
+ cls::rbd::TrashImageSpec trash_spec(trash_source, image_name, ts,
+ deferment_end_time);
+ r = cls_client::trash_add(&io_ctx, image_id, trash_spec);
+ if (r < 0 && r != -EEXIST) {
+ lderr(cct) << "error adding image " << image_name << " to rbd_trash"
+ << dendl;
+ return r;
+ } else if (r == -EEXIST) {
+ ldout(cct, 10) << "found previous unfinished deferred remove for image:"
+ << image_id << dendl;
+ // continue with removing image from directory
+ }
+
+ ldout(cct, 2) << "removing id object..." << dendl;
+ r = io_ctx.remove(util::id_obj_name(image_name));
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "error removing id object: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ ldout(cct, 2) << "removing rbd image from v2 directory..." << dendl;
+ r = cls_client::dir_remove_image(&io_ctx, RBD_DIRECTORY, image_name,
+ image_id);
+ if (r < 0) {
+ if (r != -ENOENT) {
+ lderr(cct) << "error removing image from v2 directory: "
+ << cpp_strerror(-r) << dendl;
+ }
+ return r;
+ }
+
+ return 0;
+ }
+
+ int trash_list(IoCtx &io_ctx, vector<trash_image_info_t> &entries) {
+ CephContext *cct((CephContext *)io_ctx.cct());
+ ldout(cct, 20) << "trash_list " << &io_ctx << dendl;
+
+ map<string, cls::rbd::TrashImageSpec> trash_entries;
+ int r = cls_client::trash_list(&io_ctx, &trash_entries);
+ if (r < 0) {
+ if (r != -ENOENT) {
+ lderr(cct) << "error listing rbd_trash entries: " << cpp_strerror(r)
+ << dendl;
+ }
+ return r;
+ }
+
+ for (const auto &entry : trash_entries) {
+ rbd_trash_image_source_t source =
+ static_cast<rbd_trash_image_source_t>(entry.second.source);
+ entries.push_back({entry.first, entry.second.name, source,
+ entry.second.deletion_time.sec(),
+ entry.second.deferment_end_time.sec()});
+ }
+ return 0;
+ }
+
+ int trash_remove(IoCtx &io_ctx, const std::string &image_id, bool force,
+ ProgressContext& prog_ctx) {
+ CephContext *cct((CephContext *)io_ctx.cct());
+ ldout(cct, 20) << "trash_remove " << &io_ctx << " " << image_id
+ << " " << force << dendl;
+
+ cls::rbd::TrashImageSpec trash_spec;
+ int r = cls_client::trash_get(&io_ctx, image_id, &trash_spec);
+ if (r < 0) {
+ lderr(cct) << "error getting image id " << image_id
+ << " info from trash: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ utime_t now = ceph_clock_now();
+ if (now < trash_spec.deferment_end_time && !force) {
+ lderr(cct) << "error: deferment time has not expired." << dendl;
+ return -EPERM;
+ }
+
+ r = remove(io_ctx, "", image_id, prog_ctx, false, true);
+ if (r < 0) {
+ lderr(cct) << "error removing image " << image_id
+ << ", which is pending deletion" << dendl;
+ return r;
+ }
+ r = cls_client::trash_remove(&io_ctx, image_id);
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "error removing image " << image_id
+ << " from rbd_trash object" << dendl;
+ return r;
+ }
+ return 0;
+ }
+
+ int trash_restore(librados::IoCtx &io_ctx, const std::string &image_id,
+ const std::string &image_new_name) {
+ CephContext *cct((CephContext *)io_ctx.cct());
+ ldout(cct, 20) << "trash_restore " << &io_ctx << " " << image_id << " "
+ << image_new_name << dendl;
+
+ cls::rbd::TrashImageSpec trash_spec;
+ int r = cls_client::trash_get(&io_ctx, image_id, &trash_spec);
+ if (r < 0) {
+ lderr(cct) << "error getting image id " << image_id
+ << " info from trash: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ std::string image_name = image_new_name;
+ if (image_name.empty()) {
+ // if user didn't specify a new name, let's try using the old name
+ image_name = trash_spec.name;
+ ldout(cct, 20) << "restoring image id " << image_id << " with name "
+ << image_name << dendl;
+ }
+
+ // check if no image exists with the same name
+ bool create_id_obj = true;
+ std::string existing_id;
+ r = cls_client::get_id(&io_ctx, util::id_obj_name(image_name), &existing_id);
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "error checking if image " << image_name << " exists: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ } else if (r != -ENOENT){
+ // checking if we are recovering from an incomplete restore
+ if (existing_id != image_id) {
+ ldout(cct, 2) << "an image with the same name already exists" << dendl;
+ return -EEXIST;
+ }
+ create_id_obj = false;
+ }
+
+ if (create_id_obj) {
+ ldout(cct, 2) << "adding id object" << dendl;
+ librados::ObjectWriteOperation op;
+ op.create(true);
+ cls_client::set_id(&op, image_id);
+ r = io_ctx.operate(util::id_obj_name(image_name), &op);
+ if (r < 0) {
+ lderr(cct) << "error adding id object for image " << image_name
+ << ": " << cpp_strerror(r) << dendl;
+ return r;
+ }
+ }
+
+ ldout(cct, 2) << "adding rbd image from v2 directory..." << dendl;
+ r = cls_client::dir_add_image(&io_ctx, RBD_DIRECTORY, image_name,
+ image_id);
+ if (r < 0 && r != -EEXIST) {
+ lderr(cct) << "error adding image to v2 directory: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ ldout(cct, 2) << "removing image from trash..." << dendl;
+ r = cls_client::trash_remove(&io_ctx, image_id);
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "error removing image id " << image_id << " from trash: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ return 0;
+ }
+
int snap_list(ImageCtx *ictx, vector<snap_info_t>& snaps)
{
ldout(ictx->cct, 20) << "snap_list " << ictx << dendl;
int remove(librados::IoCtx& io_ctx, const std::string &image_name,
const std::string &image_id, ProgressContext& prog_ctx,
- bool force=false);
+ 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_list(librados::IoCtx &io_ctx,
+ std::vector<trash_image_info_t> &entries);
+ int trash_remove(librados::IoCtx &io_ctx, const std::string &image_id,
+ bool force, ProgressContext& prog_ctx);
+ int trash_restore(librados::IoCtx &io_ctx, const std::string &image_id,
+ const std::string &image_new_name);
+
int snap_list(ImageCtx *ictx, std::vector<snap_info_t>& snaps);
int snap_exists(ImageCtx *ictx, const cls::rbd::SnapshotNamespace& snap_namespace,
const char *snap_name, bool *exists);
return r;
}
+ int RBD::trash_move(IoCtx &io_ctx, const char *name, uint64_t delay) {
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_move_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), name);
+ int r = librbd::trash_move(io_ctx, RBD_TRASH_IMAGE_SOURCE_USER, name,
+ delay);
+ tracepoint(librbd, trash_move_exit, r);
+ return r;
+ }
+
+ int RBD::trash_list(IoCtx &io_ctx, vector<trash_image_info_t> &entries) {
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_list_enter,
+ io_ctx.get_pool_name().c_str(), io_ctx.get_id());
+ int r = librbd::trash_list(io_ctx, entries);
+ if (r >= 0) {
+ for (const auto& entry : entries) {
+ tracepoint(librbd, trash_list_entry, entry.id.c_str());
+ }
+ }
+ tracepoint(librbd, trash_list_exit, r, r);
+ return r;
+ }
+
+ int RBD::trash_remove(IoCtx &io_ctx, const char *image_id, bool force) {
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_remove_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), image_id, force);
+ librbd::NoOpProgressContext prog_ctx;
+ int r = librbd::trash_remove(io_ctx, image_id, force, prog_ctx);
+ tracepoint(librbd, trash_remove_exit, r);
+ return r;
+ }
+
+ int RBD::trash_remove_with_progress(IoCtx &io_ctx, const char *image_id,
+ bool force, ProgressContext &pctx) {
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_remove_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), image_id, force);
+ int r = librbd::trash_remove(io_ctx, image_id, force, pctx);
+ tracepoint(librbd, trash_remove_exit, r);
+ return r;
+ }
+
+ int RBD::trash_restore(IoCtx &io_ctx, const char *id, const char *name) {
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_undelete_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), id, name);
+ int r = librbd::trash_restore(io_ctx, id, name);
+ tracepoint(librbd, trash_undelete_exit, r);
+ return r;
+ }
+
int RBD::list(IoCtx& io_ctx, vector<string>& names)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
return r;
}
+extern "C" int rbd_trash_move(rados_ioctx_t p, const char *name,
+ uint64_t delay) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_move_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), name);
+ int r = librbd::trash_move(io_ctx, RBD_TRASH_IMAGE_SOURCE_USER, name, delay);
+ tracepoint(librbd, trash_move_exit, r);
+ return r;
+}
+
+extern "C" int rbd_trash_list(rados_ioctx_t p, rbd_trash_image_info_t *entries,
+ size_t *num_entries) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_list_enter,
+ io_ctx.get_pool_name().c_str(), io_ctx.get_id());
+
+ vector<librbd::trash_image_info_t> cpp_entries;
+ int r = librbd::trash_list(io_ctx, cpp_entries);
+ if (r < 0) {
+ tracepoint(librbd, trash_list_exit, r, *num_entries);
+ return r;
+ }
+
+ if (*num_entries < cpp_entries.size()) {
+ *num_entries = cpp_entries.size();
+ tracepoint(librbd, trash_list_exit, -ERANGE, *num_entries);
+ return -ERANGE;
+ }
+
+ int i=0;
+ for (const auto &entry : cpp_entries) {
+ entries[i].id = strdup(entry.id.c_str());
+ entries[i].name = strdup(entry.name.c_str());
+ entries[i].source = entry.source;
+ entries[i].deletion_time = entry.deletion_time;
+ entries[i].deferment_end_time = entry.deferment_end_time;
+ i++;
+ }
+ *num_entries = cpp_entries.size();
+
+ return *num_entries;
+}
+
+extern "C" void rbd_trash_list_cleanup(rbd_trash_image_info_t *entries,
+ size_t num_entries) {
+ for (size_t i=0; i < num_entries; i++) {
+ free(entries[i].id);
+ free(entries[i].name);
+ }
+}
+
+extern "C" int rbd_trash_remove(rados_ioctx_t p, const char *image_id,
+ bool force) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_remove_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), image_id, force);
+ librbd::NoOpProgressContext prog_ctx;
+ int r = librbd::trash_remove(io_ctx, image_id, force, prog_ctx);
+ tracepoint(librbd, trash_remove_exit, r);
+ return r;
+}
+
+extern "C" int rbd_trash_remove_with_progress(rados_ioctx_t p,
+ const char *image_id,
+ bool force,
+ librbd_progress_fn_t cb,
+ void *cbdata) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_remove_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), image_id, force);
+ librbd::CProgressContext prog_ctx(cb, cbdata);
+ int r = librbd::trash_remove(io_ctx, image_id, force, prog_ctx);
+ tracepoint(librbd, trash_remove_exit, r);
+ return r;
+}
+
+extern "C" int rbd_trash_restore(rados_ioctx_t p, const char *id,
+ const char *name) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_undelete_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), id, name);
+ int r = librbd::trash_restore(io_ctx, id, name);
+ tracepoint(librbd, trash_undelete_exit, r);
+ return r;
+}
+
extern "C" int rbd_copy(rbd_image_t image, rados_ioctx_t dest_p,
const char *destname)
{
expect_wq_queue(op_work_queue, 0);
MockRemoveRequest *req = MockRemoveRequest::create(m_ioctx, m_image_name, "",
- true, no_op, &op_work_queue, &ctx);
+ true, false, no_op, &op_work_queue, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_wq_queue(op_work_queue, 0);
MockRemoveRequest *req = MockRemoveRequest::create(m_ioctx, m_image_name, "",
- true, no_op, &op_work_queue, &ctx);
+ true, false, no_op, &op_work_queue, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_dir_remove_image(m_ioctx, 0);
MockRemoveRequest *req = MockRemoveRequest::create(m_ioctx, m_image_name, "",
- true, no_op, &op_work_queue, &ctx);
+ true, false, no_op, &op_work_queue, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_dir_remove_image(m_ioctx, -ENOENT);
MockRemoveRequest *req = MockRemoveRequest::create(m_ioctx, m_image_name, "",
- true, no_op, &op_work_queue, &ctx);
+ true, false, no_op, &op_work_queue, &ctx);
req->send();
ASSERT_EQ(-ENOENT, ctx.wait());
ASSERT_EQ(pair.second, features);
}
}
+
+TEST_F(TestLibRBD, TestTrashMoveAndPurge) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ uint8_t old_format;
+ ASSERT_EQ(0, image.old_format(&old_format));
+
+ if (old_format) {
+ ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name.c_str(), 0));
+ image.close();
+ return;
+ }
+ std::string image_id;
+ ASSERT_EQ(0, image.get_id(&image_id));
+ image.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name.c_str(), 0));
+
+ std::vector<std::string> images;
+ ASSERT_EQ(0, rbd.list(ioctx, images));
+ for (const auto& image : images) {
+ ASSERT_TRUE(image != name);
+ }
+
+ std::vector<librbd::trash_image_info_t> entries;
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_FALSE(entries.empty());
+ ASSERT_EQ(entries.begin()->id, image_id);
+
+ entries.clear();
+ PrintProgress pp;
+ ASSERT_EQ(0, rbd.trash_remove_with_progress(ioctx, image_id.c_str(),
+ false, pp));
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_TRUE(entries.empty());
+}
+
+TEST_F(TestLibRBD, TestTrashMoveAndPurgeNonExpiredDelay) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ uint8_t old_format;
+ ASSERT_EQ(0, image.old_format(&old_format));
+
+ if (old_format) {
+ ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name.c_str(), 0));
+ image.close();
+ return;
+ }
+ std::string image_id;
+ ASSERT_EQ(0, image.get_id(&image_id));
+ image.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name.c_str(), 100));
+
+ PrintProgress pp;
+ ASSERT_EQ(-EPERM, rbd.trash_remove_with_progress(ioctx, image_id.c_str(),
+ false, pp));
+
+ PrintProgress pp2;
+ ASSERT_EQ(0, rbd.trash_remove_with_progress(ioctx, image_id.c_str(),
+ true, pp2));
+}
+
+TEST_F(TestLibRBD, TestTrashMoveAndRestore) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ uint8_t old_format;
+ ASSERT_EQ(0, image.old_format(&old_format));
+
+ if (old_format) {
+ ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name.c_str(), 0));
+ image.close();
+ return;
+ }
+ std::string image_id;
+ ASSERT_EQ(0, image.get_id(&image_id));
+ image.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name.c_str(), 10));
+
+ std::vector<std::string> images;
+ ASSERT_EQ(0, rbd.list(ioctx, images));
+ for (const auto& image : images) {
+ ASSERT_TRUE(image != name);
+ }
+
+ std::vector<librbd::trash_image_info_t> entries;
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_FALSE(entries.empty());
+ ASSERT_EQ(entries.begin()->id, image_id);
+
+ images.clear();
+ ASSERT_EQ(0, rbd.trash_restore(ioctx, image_id.c_str(), ""));
+ ASSERT_EQ(0, rbd.list(ioctx, images));
+ ASSERT_FALSE(images.empty());
+ bool found = false;
+ for (const auto& image : images) {
+ if (image == name) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+}
)
)
+TRACEPOINT_EVENT(librbd, open_image_by_id_enter,
+ TP_ARGS(
+ void*, imagectx,
+ const char*, id,
+ const char*, snap_name,
+ int, read_only),
+ TP_FIELDS(
+ ctf_integer_hex(void*, imagectx, imagectx)
+ ctf_string(id, id)
+ ctf_string(snap_name, snap_name)
+ ctf_integer(uint8_t, read_only, read_only ? 1 : 0)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, open_image_by_id_exit,
+ TP_ARGS(
+ int, retval),
+ TP_FIELDS(
+ ctf_integer(int, retval, retval)
+ )
+)
+
TRACEPOINT_EVENT(librbd, aio_open_image_enter,
TP_ARGS(
void*, imagectx,
)
)
+TRACEPOINT_EVENT(librbd, aio_open_image_by_id_enter,
+ TP_ARGS(
+ void*, imagectx,
+ const char*, id,
+ const char*, snap_name,
+ int, read_only,
+ const void*, completion),
+ TP_FIELDS(
+ ctf_integer_hex(void*, imagectx, imagectx)
+ ctf_string(id, id)
+ ctf_string(snap_name, snap_name)
+ ctf_integer(uint8_t, read_only, read_only ? 1 : 0)
+ ctf_integer_hex(const void*, completion, completion)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, aio_open_image_by_id_exit,
+ TP_ARGS(
+ int, retval),
+ TP_FIELDS(
+ ctf_integer(int, retval, retval)
+ )
+)
+
TRACEPOINT_EVENT(librbd, close_image_enter,
TP_ARGS(
void*, imagectx,
)
)
+TRACEPOINT_EVENT(librbd, trash_move_enter,
+ TP_ARGS(
+ const char*, pool_name,
+ int64_t, id,
+ const char*, imgname),
+ TP_FIELDS(
+ ctf_string(pool_name, pool_name)
+ ctf_integer(int64_t, id, id)
+ ctf_string(imgname, imgname)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, trash_move_exit,
+ TP_ARGS(
+ int, retval),
+ TP_FIELDS(
+ ctf_integer(int, retval, retval)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, trash_list_enter,
+ TP_ARGS(
+ const char*, pool_name,
+ int64_t, id),
+ TP_FIELDS(
+ ctf_string(pool_name, pool_name)
+ ctf_integer(int64_t, id, id)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, trash_list_entry,
+ TP_ARGS(
+ const char*, id),
+ TP_FIELDS(
+ ctf_string(id, id)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, trash_list_exit,
+ TP_ARGS(
+ int, retval,
+ int, size),
+ TP_FIELDS(
+ ctf_integer(int, retval, retval)
+ ctf_integer(size_t, size, size)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, trash_remove_enter,
+ TP_ARGS(
+ const char*, pool_name,
+ int64_t, id,
+ const char*, imgid,
+ uint8_t, force),
+ TP_FIELDS(
+ ctf_string(pool_name, pool_name)
+ ctf_integer(int64_t, id, id)
+ ctf_string(imgid, imgid)
+ ctf_integer(uint8_t, force, force)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, trash_remove_exit,
+ TP_ARGS(
+ int, retval),
+ TP_FIELDS(
+ ctf_integer(int, retval, retval)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, trash_undelete_enter,
+ TP_ARGS(
+ const char*, pool_name,
+ int64_t, id,
+ const char*, imgid,
+ const char*, imgnewname),
+ TP_FIELDS(
+ ctf_string(pool_name, pool_name)
+ ctf_integer(int64_t, id, id)
+ ctf_string(imgid, imgid)
+ ctf_string(imgnewname, imgnewname)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, trash_undelete_exit,
+ TP_ARGS(
+ int, retval),
+ TP_FIELDS(
+ ctf_integer(int, retval, retval)
+ )
+)
+
TRACEPOINT_EVENT(librbd, aio_write_enter,
TP_ARGS(
void*, imagectx,