typedef enum {
RBD_TRASH_IMAGE_SOURCE_USER = 0,
RBD_TRASH_IMAGE_SOURCE_MIRRORING = 1,
- RBD_TRASH_IMAGE_SOURCE_MIGRATION = 2
+ RBD_TRASH_IMAGE_SOURCE_MIGRATION = 2,
+ RBD_TRASH_IMAGE_SOURCE_REMOVE = 3
} rbd_trash_image_source_t;
typedef struct {
ConfigProxy config(cct->_conf);
Config<I>::apply_pool_overrides(io_ctx, &config);
- if (config.get_val<bool>("rbd_move_to_trash_on_remove")) {
- return Trash<I>::move(
- io_ctx, RBD_TRASH_IMAGE_SOURCE_USER, image_name,
- config.get_val<uint64_t>("rbd_move_to_trash_on_remove_expire_seconds"));
+ std::string image_id;
+ int r = cls_client::dir_get_id(&io_ctx, RBD_DIRECTORY, image_name,
+ &image_id);
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to retrieve image id: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ if(r == 0) {
+ auto expire_seconds = config.get_val<uint64_t>(
+ "rbd_move_to_trash_on_remove_expire_seconds");
+
+ if (config.get_val<bool>("rbd_move_to_trash_on_remove")) {
+ return Trash<I>::move_by_id(
+ io_ctx, RBD_TRASH_IMAGE_SOURCE_USER, image_id, image_name,
+ expire_seconds, true);
+ } else {
+ r = Trash<I>::move_by_id(
+ io_ctx, RBD_TRASH_IMAGE_SOURCE_REMOVE, image_id, image_name,
+ expire_seconds, true);
+
+ if (r == -ENOENT) {
+ // check if it already exists in trash from an aborted
+ // remove attempt
+ std::vector<trash_image_info_t> trash_entries;
+ r = Trash<I>::list(io_ctx, trash_entries);
+ if (r < 0) {
+ return r;
+ }
+
+ for (auto& entry : trash_entries) {
+ if (entry.name == image_name &&
+ entry.source == RBD_TRASH_IMAGE_SOURCE_REMOVE) {
+ return Trash<I>::remove(io_ctx, image_id, force, prog_ctx);
+ }
+ }
+ return -ENOENT;
+ } else if (r < 0) {
+ return r;
+ }
+
+ return Trash<I>::remove(io_ctx, image_id, force, prog_ctx);
+ }
}
}
#include "librbd/mirror/EnableRequest.h"
#include "librbd/trash/MoveRequest.h"
#include <json_spirit/json_spirit.h>
+#include "librbd/journal/DisabledPolicy.h"
+#include "librbd/image/ListWatchersRequest.h"
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
} // anonymous namespace
template <typename I>
-int Trash<I>::move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
- const std::string &image_name, uint64_t delay) {
+int Trash<I>::move_by_id(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
+ const std::string &image_id, const std::string &image_name,
+ uint64_t delay, bool check_for_watchers) {
CephContext *cct((CephContext *)io_ctx.cct());
- ldout(cct, 20) << "trash_move " << &io_ctx << " " << image_name
+ ldout(cct, 20) << &io_ctx << " " << (image_id.empty() ? image_name : image_id)
<< dendl;
- // try to get image id from the directory
- std::string image_id;
- int r = cls_client::dir_get_id(&io_ctx, RBD_DIRECTORY, image_name,
- &image_id);
- if (r < 0 && r != -ENOENT) {
- lderr(cct) << "failed to retrieve image id: " << cpp_strerror(r) << dendl;
- return r;
- }
-
ImageCtx *ictx = new ImageCtx((image_id.empty() ? image_name : ""),
image_id, nullptr, io_ctx, false);
- r = ictx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT);
- if (r == -ENOENT) {
- return r;
- } else if (r < 0) {
+ int r = ictx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT);
+
+ if (r < 0 && r != -ENOENT) {
lderr(cct) << "failed to open image: " << cpp_strerror(r) << dendl;
return r;
} else if (ictx->old_format) {
return -EOPNOTSUPP;
}
- image_id = ictx->id;
- ictx->owner_lock.get_read();
- if (ictx->exclusive_lock != nullptr) {
- ictx->exclusive_lock->block_requests(0);
+ std::string id = ictx->id;
+
+ if (id.empty()) {
+ return r;
+ }
+
+ if (r == 0) {
+ if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ RWLock::WLocker snap_locker(ictx->snap_lock);
+ ictx->set_journal_policy(new journal::DisabledPolicy());
+ }
+
+ ictx->owner_lock.get_read();
+ if (ictx->exclusive_lock != nullptr) {
+ ictx->exclusive_lock->block_requests(0);
+
+ r = ictx->operations->prepare_image_update(false);
+ if (r < 0) {
+ lderr(cct) << "cannot obtain exclusive lock - not removing" << dendl;
+ ictx->owner_lock.put_read();
+ ictx->state->close();
+ return -EBUSY;
+ }
+ }
+ ictx->owner_lock.put_read();
+
+ if (!ictx->migration_info.empty()) {
+ lderr(cct) << "cannot move migrating image to trash" << dendl;
+ ictx->state->close();
+ return -EINVAL;
+ }
- r = ictx->operations->prepare_image_update(false);
+ r = disable_mirroring<I>(ictx);
if (r < 0) {
- lderr(cct) << "cannot obtain exclusive lock - not removing" << dendl;
- ictx->owner_lock.put_read();
ictx->state->close();
- return -EBUSY;
+ return r;
}
- }
- ictx->owner_lock.put_read();
- if (!ictx->migration_info.empty()) {
- lderr(cct) << "cannot move migrating image to trash" << dendl;
- ictx->state->close();
- return -EINVAL;
- }
+ if (check_for_watchers) {
+ std::list<obj_watch_t> t_watchers;
+ int flags = librbd::image::LIST_WATCHERS_FILTER_OUT_MY_INSTANCE |
+ librbd::image::LIST_WATCHERS_FILTER_OUT_MIRROR_INSTANCES;
+ C_SaferCond t_on_list_watchers;
+ auto list_req = librbd::image::ListWatchersRequest<I>::create(
+ *ictx, flags, &t_watchers, &t_on_list_watchers);
+ list_req->send();
+ r = t_on_list_watchers.wait();
+ if (r < 0) {
+ lderr(cct) << "failed listing watchers:" << cpp_strerror(r) << dendl;
+ ictx->state->close();
+ return r;
+ }
+ if (!t_watchers.empty()) {
+ lderr(cct) << "image has watchers - not moving" << dendl;
+ ictx->state->close();
+ return -EBUSY;
+ }
+ }
- r = disable_mirroring<I>(ictx);
- if (r < 0) {
- return r;
+ ictx->state->close();
}
utime_t delete_time{ceph_clock_now()};
utime_t deferment_end_time{delete_time};
deferment_end_time += delay;
cls::rbd::TrashImageSpec trash_image_spec{
- static_cast<cls::rbd::TrashImageSource>(source), ictx->name,
+ static_cast<cls::rbd::TrashImageSource>(source), image_name,
delete_time, deferment_end_time};
trash_image_spec.state = cls::rbd::TRASH_IMAGE_STATE_MOVING;
C_SaferCond ctx;
- auto req = trash::MoveRequest<I>::create(io_ctx, ictx->id, trash_image_spec,
+ auto req = trash::MoveRequest<I>::create(io_ctx, id, trash_image_spec,
&ctx);
req->send();
r = ctx.wait();
- ictx->state->close();
trash_image_spec.state = cls::rbd::TRASH_IMAGE_STATE_NORMAL;
- int ret = cls_client::trash_state_set(&io_ctx, image_id,
+ int ret = cls_client::trash_state_set(&io_ctx, id,
trash_image_spec.state,
cls::rbd::TRASH_IMAGE_STATE_MOVING);
if (ret < 0 && ret != -EOPNOTSUPP) {
}
C_SaferCond notify_ctx;
- TrashWatcher<I>::notify_image_added(io_ctx, image_id, trash_image_spec,
+ TrashWatcher<I>::notify_image_added(io_ctx, id, trash_image_spec,
¬ify_ctx);
r = notify_ctx.wait();
if (r < 0) {
return 0;
}
+template <typename I>
+int Trash<I>::move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
+ const std::string &image_name, uint64_t delay,
+ bool check_for_watchers) {
+ CephContext *cct((CephContext *)io_ctx.cct());
+ ldout(cct, 20) << &io_ctx << " " << image_name
+ << dendl;
+
+ // try to get image id from the directory
+ std::string image_id;
+ int r = cls_client::dir_get_id(&io_ctx, RBD_DIRECTORY, image_name,
+ &image_id);
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to retrieve image id: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ return Trash<I>::move_by_id(io_ctx, source, image_id, image_name, delay,
+ check_for_watchers);
+}
+
template <typename I>
int Trash<I>::get(IoCtx &io_ctx, const std::string &id,
trash_image_info_t *info) {
template <typename ImageCtxT = librbd::ImageCtx>
struct Trash {
+ static int move_by_id(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
+ const std::string &image_id, const std::string &image_name,
+ uint64_t delay, bool check_for_watchers=false);
static int move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
- const std::string &image_name, uint64_t delay);
+ const std::string &image_name, uint64_t delay,
+ bool check_for_watchers=false);
static int get(librados::IoCtx &io_ctx, const std::string &id,
trash_image_info_t *info);
static int list(librados::IoCtx &io_ctx,
_RBD_TRASH_IMAGE_SOURCE_USER "RBD_TRASH_IMAGE_SOURCE_USER",
_RBD_TRASH_IMAGE_SOURCE_MIRRORING "RBD_TRASH_IMAGE_SOURCE_MIRRORING",
_RBD_TRASH_IMAGE_SOURCE_MIGRATION "RBD_TRASH_IMAGE_SOURCE_MIGRATION"
+ _RBD_TRASH_IMAGE_SOURCE_REMOVE "RBD_TRASH_IMAGE_SOURCE_REMOVE"
ctypedef struct rbd_trash_image_info_t:
char *id
librbd::ImageCtx *ictx;
librbd::ImageCtx *ictx2 = nullptr;
std::string clone_name = get_temp_image_name();
+ std::string image_id;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, snap_create(*ictx, "snap"));
}
librbd::NoOpProgressContext no_op;
- ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, "", no_op));
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, "", image_id, no_op));
ASSERT_EQ(0, ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(), "snap"));
};
clone_name.c_str(), ictx->features, &order, 0, 0));
ASSERT_EQ(0, open_image(clone_name, &ictx2));
+ image_id = ictx2->id;
MockRefreshImageCtx mock_image_ctx(*ictx2);
MockRefreshParentRequest *mock_refresh_parent_request = new MockRefreshParentRequest();
librbd::ImageCtx *ictx;
librbd::ImageCtx *ictx2 = nullptr;
+ std::string image_id;
std::string clone_name = get_temp_image_name();
ASSERT_EQ(0, open_image(m_image_name, &ictx));
}
librbd::NoOpProgressContext no_op;
- ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, "", no_op));
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, "", image_id, no_op));
ASSERT_EQ(0, ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(), "snap"));
};
clone_name.c_str(), ictx->features, &order, 0, 0));
ASSERT_EQ(0, open_image(clone_name, &ictx2));
+ image_id = ictx2->id;
MockRefreshImageCtx mock_image_ctx(*ictx2);
MockExclusiveLock mock_exclusive_lock;
librados::IoCtx ioctx3;
ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx3));
+ ASSERT_EQ(0, rbd_close(image3));
ASSERT_EQ(0, rbd.trash_move(ioctx3, child_name3.c_str(), 0));
test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
pool_name1.c_str(), child_name2.c_str());
child_id3, pool_name2.c_str(), child_name3.c_str(), false,
child_id4, pool_name2.c_str(), child_name4.c_str(), false);
- ASSERT_EQ(0, rbd_close(image3));
ASSERT_EQ(0, rbd_remove(ioctx2, child_name3.c_str()));
test_list_children(parent, 2,
pool_name1.c_str(), child_name2.c_str(),
librados::IoCtx ioctx3;
ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx3));
+ ASSERT_EQ(0, rbd_close(image3));
ASSERT_EQ(0, rbd.trash_move(ioctx3, child_name3.c_str(), 0));
test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
pool_name1.c_str(), child_name2.c_str());
child_id3, pool_name2.c_str(), child_name3.c_str(), false,
child_id4, pool_name2.c_str(), child_name4.c_str(), false);
- ASSERT_EQ(0, rbd_close(image3));
ASSERT_EQ(0, rbd_remove(ioctx2, child_name3.c_str()));
test_list_children(parent, 2,
pool_name1.c_str(), child_name2.c_str(),
ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_PREPARED);
rbd_migration_status_cleanup(&status);
- ASSERT_EQ(-EBUSY, rbd_remove(ioctx, name.c_str()));
+ ASSERT_EQ(-EINVAL, rbd_remove(ioctx, name.c_str()));
ASSERT_EQ(-EINVAL, rbd_trash_move(ioctx, name.c_str(), 0));
ASSERT_EQ(0, rbd_migration_execute(ioctx, name.c_str()));
ASSERT_EQ(0, rbd_migration_prepare(ioctx, name.c_str(), ioctx,
new_name.c_str(), image_options));
- ASSERT_EQ(-EBUSY, rbd_remove(ioctx, new_name.c_str()));
+ ASSERT_EQ(-EINVAL, rbd_remove(ioctx, new_name.c_str()));
ASSERT_EQ(-EINVAL, rbd_trash_move(ioctx, new_name.c_str(), 0));
ASSERT_EQ(0, rbd_migration_abort(ioctx, name.c_str()));
ASSERT_NE(status.dest_image_id, "");
ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_PREPARED);
- ASSERT_EQ(-EBUSY, rbd.remove(ioctx, name.c_str()));
+ ASSERT_EQ(-EINVAL, rbd.remove(ioctx, name.c_str()));
ASSERT_EQ(-EINVAL, rbd.trash_move(ioctx, name.c_str(), 0));
ASSERT_EQ(0, rbd.migration_execute(ioctx, name.c_str()));
ASSERT_EQ(0, rbd.migration_prepare(ioctx, name.c_str(), ioctx,
new_name.c_str(), image_options));
- ASSERT_EQ(-EBUSY, rbd.remove(ioctx, new_name.c_str()));
+ ASSERT_EQ(-EINVAL, rbd.remove(ioctx, new_name.c_str()));
ASSERT_EQ(-EINVAL, rbd.trash_move(ioctx, new_name.c_str(), 0));
ASSERT_EQ(0, rbd.migration_abort(ioctx, name.c_str()));
#include "librbd/io/ImageRequest.h"
#include "librbd/io/ImageRequestWQ.h"
#include "librbd/journal/Types.h"
+#include "librbd/api/Image.h"
#include "journal/Journaler.h"
#include "journal/Settings.h"
#include <boost/scope_exit.hpp>
std::string image_id;
ASSERT_EQ(0, image.get_id(&image_id));
+ image.close();
ASSERT_EQ(0, m_rbd.trash_move(m_ioctx, image_name.c_str(), 100));
+ ASSERT_EQ(0, m_rbd.open_by_id(m_ioctx, image, image_id.c_str(), NULL));
+
librbd::mirror_image_info_t mirror_image;
ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
ASSERT_EQ(mirror_image.state, RBD_MIRROR_IMAGE_DISABLED);
ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL));
uint64_t features = RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING;
+ //uint64_t features = 0;
int order = 20;
ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
&order));
librbd::Image image;
ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
- ASSERT_EQ(-EBUSY, m_rbd.remove(m_ioctx, image_name.c_str()));
+ std::string image_id;
+ ASSERT_EQ(0, image.get_id(&image_id));
+
+ librbd::NoOpProgressContext no_op;
+ ASSERT_EQ(-EBUSY, librbd::api::Image<>::remove(m_ioctx, "", image_id, no_op));
// simulate the image is open by rbd-mirror bootstrap
uint64_t handle;
ASSERT_EQ(0, m_ioctx.create(RBD_MIRRORING, false));
ASSERT_EQ(0, m_ioctx.watch2(RBD_MIRRORING, &handle, &watcher));
// now remove should succeed
- ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, "", image_id, no_op));
ASSERT_EQ(0, m_ioctx.unwatch2(handle));
ASSERT_TRUE(watcher.m_notified);
ASSERT_EQ(0, image.close());
case RBD_TRASH_IMAGE_SOURCE_MIGRATION:
del_source = "MIGRATION";
break;
+ case RBD_TRASH_IMAGE_SOURCE_REMOVE:
+ del_source = "REMOVE";
+ break;
}
std::string time_str = ctime(&entry.deletion_time);