image/DetachParentRequest.cc
image/ListWatchersRequest.cc
image/OpenRequest.cc
+ image/PreRemoveRequest.cc
image/RefreshParentRequest.cc
image/RefreshRequest.cc
image/RemoveRequest.cc
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/image/PreRemoveRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/Utils.h"
+#include "librbd/image/ListWatchersRequest.h"
+#include "librbd/journal/DisabledPolicy.h"
+#include "librbd/operation/SnapshotRemoveRequest.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::image::PreRemoveRequest: " << this \
+ << " " << __func__ << ": "
+
+namespace librbd {
+namespace image {
+
+namespace {
+
+bool auto_delete_snapshot(const SnapInfo& snap_info) {
+ auto snap_namespace_type = cls::rbd::get_snap_namespace_type(
+ snap_info.snap_namespace);
+ switch (snap_namespace_type) {
+ case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // anonymous namespace
+
+using util::create_context_callback;
+using util::create_rados_callback;
+
+template <typename I>
+void PreRemoveRequest<I>::send() {
+ auto cct = m_image_ctx->cct;
+ if (m_image_ctx->operations_disabled) {
+ lderr(cct) << "image operations disabled due to unsupported op features"
+ << dendl;
+ finish(-EROFS);
+ return;
+ }
+
+ acquire_exclusive_lock();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::acquire_exclusive_lock() {
+ RWLock::RLocker owner_lock(m_image_ctx->owner_lock);
+ if (m_image_ctx->exclusive_lock == nullptr) {
+ validate_image_removal();
+ return;
+ }
+
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ // do not attempt to open the journal when removing the image in case
+ // it's corrupt
+ if (m_image_ctx->test_features(RBD_FEATURE_JOURNALING)) {
+ RWLock::WLocker snap_locker(m_image_ctx->snap_lock);
+ m_image_ctx->set_journal_policy(new journal::DisabledPolicy());
+ }
+
+ if (m_force) {
+ auto ctx = create_context_callback<
+ PreRemoveRequest<I>,
+ &PreRemoveRequest<I>::handle_exclusive_lock_force>(this);
+
+ m_exclusive_lock = m_image_ctx->exclusive_lock;
+ m_exclusive_lock->shut_down(ctx);
+ } else {
+ auto ctx = create_context_callback<
+ PreRemoveRequest<I>, &PreRemoveRequest<I>::handle_exclusive_lock>(this);
+
+ m_image_ctx->exclusive_lock->try_acquire_lock(ctx);
+ }
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_exclusive_lock(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0 || !m_image_ctx->exclusive_lock->is_lock_owner()) {
+ lderr(cct) << "cannot obtain exclusive lock - not removing" << dendl;
+ finish(-EBUSY);
+ return;
+ }
+
+ validate_image_removal();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_exclusive_lock_force(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ delete m_exclusive_lock;
+ m_exclusive_lock = nullptr;
+
+ if (r < 0) {
+ lderr(cct) << "error shutting down exclusive lock: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ ceph_assert(m_image_ctx->exclusive_lock == nullptr);
+ validate_image_removal();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::validate_image_removal() {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ if (!m_image_ctx->ignore_migrating &&
+ m_image_ctx->test_features(RBD_FEATURE_MIGRATING)) {
+ lderr(cct) << "image in migration state - not removing" << dendl;
+ finish(-EBUSY);
+ return;
+ }
+
+ check_image_snaps();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::check_image_snaps() {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ m_image_ctx->snap_lock.get_read();
+ for (auto& snap_info : m_image_ctx->snap_info) {
+ if (auto_delete_snapshot(snap_info.second)) {
+ m_snap_infos.insert(snap_info);
+ } else {
+ m_image_ctx->snap_lock.put_read();
+
+ lderr(cct) << "image has snapshots - not removing" << dendl;
+ finish(-ENOTEMPTY);
+ return;
+ }
+ }
+ m_image_ctx->snap_lock.put_read();
+
+ list_image_watchers();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::list_image_watchers() {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ int flags = LIST_WATCHERS_FILTER_OUT_MY_INSTANCE |
+ LIST_WATCHERS_FILTER_OUT_MIRROR_INSTANCES;
+ auto ctx = create_context_callback<
+ PreRemoveRequest<I>,
+ &PreRemoveRequest<I>::handle_list_image_watchers>(this);
+ auto req = ListWatchersRequest<I>::create(*m_image_ctx, flags, &m_watchers,
+ ctx);
+ req->send();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_list_image_watchers(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "error listing image watchers: " << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
+ check_image_watchers();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::check_image_watchers() {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ if (!m_watchers.empty()) {
+ lderr(cct) << "image has watchers - not removing" << dendl;
+ finish(-EBUSY);
+ return;
+ }
+
+ check_group();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::check_group() {
+ if (m_image_ctx->old_format) {
+ finish(0);
+ return;
+ }
+
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ librados::ObjectReadOperation op;
+ librbd::cls_client::image_group_get_start(&op);
+
+ auto rados_completion = create_rados_callback<
+ PreRemoveRequest<I>, &PreRemoveRequest<I>::handle_check_group>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid,
+ rados_completion, &op, &m_out_bl);
+ ceph_assert(r == 0);
+ rados_completion->release();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_check_group(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ cls::rbd::GroupSpec s;
+ if (r == 0) {
+ auto it = m_out_bl.cbegin();
+ r = librbd::cls_client::image_group_get_finish(&it, &s);
+ }
+ if (r < 0 && r != -EOPNOTSUPP) {
+ lderr(cct) << "error fetching group for image: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ if (s.is_valid()) {
+ lderr(cct) << "image is in a group - not removing" << dendl;
+ finish(-EMLINK);
+ return;
+ }
+
+ remove_snapshot();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::remove_snapshot() {
+ if (m_snap_infos.empty()) {
+ finish(0);
+ return;
+ }
+
+ auto cct = m_image_ctx->cct;
+ auto snap_id = m_snap_infos.begin()->first;
+ auto& snap_info = m_snap_infos.begin()->second;
+ ldout(cct, 20) << "snap_id=" << snap_id << ", "
+ << "snap_name=" << snap_info.name << dendl;
+
+ RWLock::RLocker owner_lock(m_image_ctx->owner_lock);
+ auto ctx = create_context_callback<
+ PreRemoveRequest<I>, &PreRemoveRequest<I>::handle_remove_snapshot>(this);
+ auto req = librbd::operation::SnapshotRemoveRequest<I>::create(
+ *m_image_ctx, snap_info.snap_namespace, snap_info.name,
+ snap_id, ctx);
+ req->send();
+
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_remove_snapshot(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0 && r != -ENOENT) {
+ auto snap_id = m_snap_infos.begin()->first;
+ lderr(cct) << "failed to auto-prune snapshot " << snap_id << ": "
+ << cpp_strerror(r) << dendl;
+
+ if (r == -EBUSY) {
+ r = -ENOTEMPTY;
+ }
+ finish(r);
+ return;
+ }
+
+ ceph_assert(!m_snap_infos.empty());
+ m_snap_infos.erase(m_snap_infos.begin());
+
+ remove_snapshot();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::finish(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ m_on_finish->complete(r);
+ delete this;
+}
+
+} // namespace image
+} // namespace librbd
+
+template class librbd::image::PreRemoveRequest<librbd::ImageCtx>;
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_IMAGE_PRE_REMOVE_REQUEST_H
+#define CEPH_LIBRBD_IMAGE_PRE_REMOVE_REQUEST_H
+
+#include "include/rados/librados.hpp"
+#include "include/buffer.h"
+#include "librbd/ImageCtx.h"
+#include <list>
+#include <map>
+
+class Context;
+
+namespace librbd {
+namespace image {
+
+template <typename ImageCtxT>
+class PreRemoveRequest {
+public:
+
+ static PreRemoveRequest *create(ImageCtxT *image_ctx, bool force,
+ Context *on_finish) {
+ return new PreRemoveRequest(image_ctx, force, on_finish);
+ }
+
+ PreRemoveRequest(ImageCtxT *image_ctx, bool force, Context *on_finish)
+ : m_image_ctx(image_ctx), m_force(force), m_on_finish(on_finish) {
+ }
+
+ void send();
+
+private:
+ /**
+ * @verbatim
+ *
+ * <start>
+ * |
+ * v
+ * CHECK EXCLUSIVE LOCK
+ * |
+ * v (skip if not needed)
+ * ACQUIRE EXCLUSIVE LOCK
+ * |
+ * v
+ * CHECK IMAGE WATCHERS
+ * |
+ * v
+ * CHECK GROUP
+ * |
+ * | /------\
+ * | | |
+ * v v |
+ * REMOVE SNAPS ----/
+ * |
+ * v
+ * <finish>
+ *
+ * @endverbatim
+ */
+
+ ImageCtxT* m_image_ctx;
+ bool m_force;
+ Context* m_on_finish;
+
+ decltype(m_image_ctx->exclusive_lock) m_exclusive_lock = nullptr;
+
+ bufferlist m_out_bl;
+ std::list<obj_watch_t> m_watchers;
+
+ std::map<uint64_t, SnapInfo> m_snap_infos;
+
+ void acquire_exclusive_lock();
+ void handle_exclusive_lock(int r);
+ void handle_exclusive_lock_force(int r);
+
+ void validate_image_removal();
+ void check_image_snaps();
+
+ void list_image_watchers();
+ void handle_list_image_watchers(int r);
+
+ void check_image_watchers();
+
+ void check_group();
+ void handle_check_group(int r);
+
+ void remove_snapshot();
+ void handle_remove_snapshot(int r);
+
+ void finish(int r);
+
+};
+
+} // namespace image
+} // namespace librbd
+
+extern template class librbd::image::PreRemoveRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_IMAGE_PRE_REMOVE_REQUEST_H
#include "librbd/ImageState.h"
#include "librbd/Journal.h"
#include "librbd/ObjectMap.h"
-#include "librbd/ExclusiveLock.h"
#include "librbd/MirroringWatcher.h"
#include "librbd/image/DetachChildRequest.h"
-#include "librbd/image/ListWatchersRequest.h"
-#include "librbd/journal/DisabledPolicy.h"
+#include "librbd/image/PreRemoveRequest.h"
#include "librbd/journal/RemoveRequest.h"
#include "librbd/mirror/DisableRequest.h"
-#include "librbd/operation/SnapshotRemoveRequest.h"
#include "librbd/operation/TrimRequest.h"
#define dout_subsys ceph_subsys_rbd
namespace librbd {
namespace image {
-namespace {
-
-bool auto_delete_snapshot(const SnapInfo& snap_info) {
- auto snap_namespace_type = cls::rbd::get_snap_namespace_type(
- snap_info.snap_namespace);
- switch (snap_namespace_type) {
- case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH:
- return true;
- default:
- return false;
- }
-}
-
-} // anonymous namespace
-
using librados::IoCtx;
using util::create_context_callback;
using util::create_async_context_callback;
template<typename I>
void RemoveRequest<I>::open_image() {
if (m_image_ctx != nullptr) {
- check_exclusive_lock();
+ pre_remove_image();
return;
}
m_old_format = m_image_ctx->old_format;
m_unknown_format = false;
- check_exclusive_lock();
-}
-
-template<typename I>
-void RemoveRequest<I>::check_exclusive_lock() {
- if (m_image_ctx->operations_disabled) {
- lderr(m_cct) << "image operations disabled due to unsupported op features"
- << dendl;
- send_close_image(-EROFS);
- return;
- }
-
- ldout(m_cct, 20) << dendl;
-
- if (m_image_ctx->exclusive_lock == nullptr) {
- validate_image_removal();
- } else {
- acquire_exclusive_lock();
- }
-}
-
-template<typename I>
-void RemoveRequest<I>::acquire_exclusive_lock() {
- ldout(m_cct, 20) << dendl;
-
- // do not attempt to open the journal when removing the image in case
- // it's corrupt
- if (m_image_ctx->test_features(RBD_FEATURE_JOURNALING)) {
- RWLock::WLocker snap_locker(m_image_ctx->snap_lock);
- m_image_ctx->set_journal_policy(new journal::DisabledPolicy());
- }
-
- using klass = RemoveRequest<I>;
- if (m_force) {
- Context *ctx = create_context_callback<
- klass, &klass::handle_exclusive_lock_force>(this);
- m_exclusive_lock = m_image_ctx->exclusive_lock;
- m_exclusive_lock->shut_down(ctx);
- } else {
- Context *ctx = create_context_callback<
- klass, &klass::handle_exclusive_lock>(this);
- RWLock::WLocker owner_lock(m_image_ctx->owner_lock);
- m_image_ctx->exclusive_lock->try_acquire_lock(ctx);
- }
-}
-
-template<typename I>
-void RemoveRequest<I>::handle_exclusive_lock_force(int r) {
- ldout(m_cct, 20) << "r=" << r << dendl;
-
- delete m_exclusive_lock;
- m_exclusive_lock = nullptr;
-
- if (r < 0) {
- lderr(m_cct) << "error shutting down exclusive lock: "
- << cpp_strerror(r) << dendl;
- send_close_image(r);
- return;
- }
-
- ceph_assert(m_image_ctx->exclusive_lock == nullptr);
- validate_image_removal();
+ pre_remove_image();
}
template<typename I>
-void RemoveRequest<I>::handle_exclusive_lock(int r) {
- ldout(m_cct, 20) << "r=" << r << dendl;
-
- if (r < 0 || !m_image_ctx->exclusive_lock->is_lock_owner()) {
- lderr(m_cct) << "cannot obtain exclusive lock - not removing" << dendl;
- send_close_image(-EBUSY);
- return;
- }
-
- validate_image_removal();
-}
-
-template<typename I>
-void RemoveRequest<I>::validate_image_removal() {
- ldout(m_cct, 20) << dendl;
-
- if (!m_image_ctx->ignore_migrating &&
- m_image_ctx->test_features(RBD_FEATURE_MIGRATING)) {
- lderr(m_cct) << "image in migration state - not removing" << dendl;
- send_close_image(-EBUSY);
- return;
- }
+void RemoveRequest<I>::pre_remove_image() {
+ ldout(m_cct, 5) << dendl;
- check_image_snaps();
-}
-
-template<typename I>
-void RemoveRequest<I>::check_image_snaps() {
- ldout(m_cct, 20) << dendl;
-
- m_image_ctx->snap_lock.get_read();
- for (auto& snap_info : m_image_ctx->snap_info) {
- if (auto_delete_snapshot(snap_info.second)) {
- m_snap_infos.insert(snap_info);
- } else {
- m_image_ctx->snap_lock.put_read();
-
- lderr(m_cct) << "image has snapshots - not removing" << dendl;
- send_close_image(-ENOTEMPTY);
- return;
- }
- }
- m_image_ctx->snap_lock.put_read();
-
- list_image_watchers();
-}
-
-template<typename I>
-void RemoveRequest<I>::list_image_watchers() {
- ldout(m_cct, 20) << dendl;
-
- int flags = LIST_WATCHERS_FILTER_OUT_MY_INSTANCE |
- LIST_WATCHERS_FILTER_OUT_MIRROR_INSTANCES;
- using klass = RemoveRequest<I>;
- Context *ctx = create_context_callback<
- klass, &klass::handle_list_image_watchers>(this);
-
- auto req = ListWatchersRequest<I>::create(*m_image_ctx, flags, &m_watchers,
- ctx);
- req->send();
-}
-
-template<typename I>
-void RemoveRequest<I>::handle_list_image_watchers(int r) {
- ldout(m_cct, 20) << "r=" << r << dendl;
-
- if (r < 0) {
- lderr(m_cct) << "error listing image watchers: " << cpp_strerror(r)
- << dendl;
- send_close_image(r);
- return;
- }
-
- check_image_watchers();
-}
-
-template<typename I>
-void RemoveRequest<I>::check_image_watchers() {
- if (!m_watchers.empty()) {
- lderr(m_cct) << "image has watchers - not removing" << dendl;
- send_close_image(-EBUSY);
- return;
- }
-
- check_group();
-}
-
-template<typename I>
-void RemoveRequest<I>::check_group() {
- if (m_old_format) {
- trim_image();
- return;
- }
-
- ldout(m_cct, 20) << dendl;
-
- librados::ObjectReadOperation op;
- librbd::cls_client::image_group_get_start(&op);
-
- using klass = RemoveRequest<I>;
- librados::AioCompletion *rados_completion = create_rados_callback<
- klass, &klass::handle_check_group>(this);
- m_out_bl.clear();
- int r = m_image_ctx->md_ctx.aio_operate(m_header_oid, rados_completion, &op,
- &m_out_bl);
- ceph_assert(r == 0);
- rados_completion->release();
-}
-
-template<typename I>
-void RemoveRequest<I>::handle_check_group(int r) {
- ldout(m_cct, 20) << "r=" << r << dendl;
-
- cls::rbd::GroupSpec s;
- if (r == 0) {
- auto it = m_out_bl.cbegin();
- r = librbd::cls_client::image_group_get_finish(&it, &s);
- }
- if (r < 0 && r != -EOPNOTSUPP) {
- lderr(m_cct) << "error fetching group for image: "
- << cpp_strerror(r) << dendl;
- send_close_image(r);
- return;
- }
-
- if (s.is_valid()) {
- lderr(m_cct) << "image is in a group - not removing" << dendl;
- send_close_image(-EMLINK);
- return;
- }
-
- remove_snapshot();
-}
-
-template<typename I>
-void RemoveRequest<I>::remove_snapshot() {
- if (m_snap_infos.empty()) {
- trim_image();
- return;
- }
-
- auto snap_id = m_snap_infos.begin()->first;
- auto& snap_info = m_snap_infos.begin()->second;
- ldout(m_cct, 20) << "snap_id=" << snap_id << ", "
- << "snap_name=" << snap_info.name << dendl;
-
- RWLock::RLocker owner_lock(m_image_ctx->owner_lock);
auto ctx = create_context_callback<
- RemoveRequest<I>, &RemoveRequest<I>::handle_remove_snapshot>(this);
- auto req = librbd::operation::SnapshotRemoveRequest<I>::create(
- *m_image_ctx, snap_info.snap_namespace, snap_info.name,
- snap_id, ctx);
+ RemoveRequest<I>, &RemoveRequest<I>::handle_pre_remove_image>(this);
+ auto req = PreRemoveRequest<I>::create(m_image_ctx, m_force, ctx);
req->send();
}
template<typename I>
-void RemoveRequest<I>::handle_remove_snapshot(int r) {
- ldout(m_cct, 20) << "r=" << r << dendl;
-
- if (r < 0 && r != -ENOENT) {
- auto snap_id = m_snap_infos.begin()->first;
- lderr(m_cct) << "failed to auto-prune snapshot " << snap_id << ": "
- << cpp_strerror(r) << dendl;
+void RemoveRequest<I>::handle_pre_remove_image(int r) {
+ ldout(m_cct, 5) << "r=" << r << dendl;
- if (r == -EBUSY) {
- r = -ENOTEMPTY;
- }
+ if (r < 0) {
send_close_image(r);
return;
}
- ceph_assert(!m_snap_infos.empty());
- m_snap_infos.erase(m_snap_infos.begin());
-
- remove_snapshot();
+ trim_image();
}
template<typename I>
* (skip if already opened) OPEN IMAGE------------------\
* | |
* v |
- * error CHECK EXCLUSIVE LOCK---\ |
- * /-------<-------\ | | |
- * | | | (acquired)|
- * | | v | |
- * | | ACQUIRE EXCLUSIVE LOCK | |
- * | | / | | |
- * | |------<-------/ | | |
- * | | v | |
- * | | VALIDATE IMAGE REMOVAL<-/ |
- * | | / | v
- * | \------<--------/ | /------\ |
- * | v v | |
- * | REMOVE SNAPS ----/ |
- * | | |
- * | v |
- * | TRIM IMAGE |
- * | | |
- * v v |
- * | DETACH CHILD |
- * | | |
- * | v v
- * \--------->------------------>CLOSE IMAGE |
+ * PRE REMOVE IMAGE * * * |
+ * | * |
+ * v * |
+ * TRIM IMAGE * * * * * |
+ * | * |
+ * v * |
+ * DETACH CHILD * |
+ * | * |
+ * v * v
+ * CLOSE IMAGE < * * * * |
* | |
- * error v |
+ * error v |
* /------<--------\ REMOVE HEADER<--------------/
- * | | / |
- * | |-------<-------/ |
+ * | | / |
+ * | |-------<-------/ |
* | | v
* | | REMOVE JOURNAL
- * | | / |
- * | |-------<-------/ |
+ * | | / |
+ * | |-------<-------/ |
* | | v
- * v ^ REMOVE OBJECTMAP
- * | | / |
- * | |-------<-------/ |
+ * v ^ REMOVE OBJECTMAP
+ * | | / |
+ * | |-------<-------/ |
* | | v
- * | | REMOVE MIRROR IMAGE
- * | | / |
- * | |-------<-------/ |
+ * | | REMOVE MIRROR IMAGE
+ * | | / |
+ * | |-------<-------/ |
* | | v
* | | REMOVE ID OBJECT
- * | | / |
- * | |-------<-------/ |
+ * | | / |
+ * | |-------<-------/ |
* | | v
* | | REMOVE IMAGE
- * | | / |
- * | \-------<-------/ |
+ * | | / |
+ * | \-------<-------/ |
* | v
* \------------------>------------<finish>
*
void mirror_image_remove();
void handle_mirror_image_remove(int r);
- void check_exclusive_lock();
-
- void acquire_exclusive_lock();
- void handle_exclusive_lock(int r);
- void handle_exclusive_lock_force(int r);
-
- void validate_image_removal();
- void check_image_snaps();
-
- void list_image_watchers();
- void handle_list_image_watchers(int r);
-
- void check_image_watchers();
-
- void check_group();
- void handle_check_group(int r);
-
- void remove_snapshot();
- void handle_remove_snapshot(int r);
+ void pre_remove_image();
+ void handle_pre_remove_image(int r);
void trim_image();
void handle_trim_image(int r);
image/test_mock_DetachChildRequest.cc
image/test_mock_DetachParentRequest.cc
image/test_mock_ListWatchersRequest.cc
+ image/test_mock_PreRemoveRequest.cc
image/test_mock_RefreshRequest.cc
image/test_mock_RemoveRequest.cc
io/test_mock_ImageRequest.cc
--- /dev/null
+// -*- mode:c++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/image/ListWatchersRequest.h"
+#include "librbd/image/PreRemoveRequest.h"
+#include "librbd/image/RefreshParentRequest.h"
+#include "librbd/operation/SnapshotRemoveRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+ MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+ }
+};
+
+} // anonymous namespace
+
+namespace operation {
+
+template <>
+class SnapshotRemoveRequest<MockTestImageCtx> {
+public:
+ static SnapshotRemoveRequest *s_instance;
+ static SnapshotRemoveRequest *create(MockTestImageCtx &image_ctx,
+ cls::rbd::SnapshotNamespace sn,
+ std::string name,
+ uint64_t id, Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ Context *on_finish = nullptr;
+
+ SnapshotRemoveRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+SnapshotRemoveRequest<MockTestImageCtx> *SnapshotRemoveRequest<MockTestImageCtx>::s_instance;
+
+} // namespace operation
+
+namespace image {
+
+template<>
+class ListWatchersRequest<MockTestImageCtx> {
+public:
+ static ListWatchersRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static ListWatchersRequest *create(MockTestImageCtx &image_ctx, int flags,
+ std::list<obj_watch_t> *watchers,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ListWatchersRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+ListWatchersRequest<MockTestImageCtx> *ListWatchersRequest<MockTestImageCtx>::s_instance;
+
+} // namespace image
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/PreRemoveRequest.cc"
+
+ACTION_P(TestFeatures, image_ctx) {
+ return ((image_ctx->features & arg0) != 0);
+}
+
+ACTION_P(ShutDownExclusiveLock, image_ctx) {
+ // shutting down exclusive lock will close object map and journal
+ image_ctx->exclusive_lock = nullptr;
+ image_ctx->object_map = nullptr;
+ image_ctx->journal = nullptr;
+}
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockImagePreRemoveRequest : public TestMockFixture {
+public:
+ typedef PreRemoveRequest<MockTestImageCtx> MockPreRemoveRequest;
+ typedef ListWatchersRequest<MockTestImageCtx> MockListWatchersRequest;
+ typedef librbd::operation::SnapshotRemoveRequest<MockTestImageCtx> MockSnapshotRemoveRequest;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ ASSERT_EQ(0, open_image(m_image_name, &m_test_imctx));
+ m_mock_imctx = new MockTestImageCtx(*m_test_imctx);
+ }
+
+ void TearDown() override {
+ delete m_mock_imctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_test_features(MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, test_features(_))
+ .WillRepeatedly(TestFeatures(&mock_image_ctx));
+ }
+
+ void expect_set_journal_policy(MockTestImageCtx &mock_image_ctx) {
+ if (m_test_imctx->test_features(RBD_FEATURE_JOURNALING)) {
+ EXPECT_CALL(mock_image_ctx, set_journal_policy(_))
+ .WillOnce(Invoke([](journal::Policy* policy) {
+ ASSERT_TRUE(policy->journal_disabled());
+ delete policy;
+ }));
+ }
+ }
+
+ void expect_try_acquire_exclusive_lock(MockTestImageCtx &mock_image_ctx,
+ MockExclusiveLock &mock_exclusive_lock,
+ int r) {
+ if (m_mock_imctx->exclusive_lock != nullptr) {
+ EXPECT_CALL(mock_exclusive_lock, try_acquire_lock(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+ }
+
+ void expect_shut_down_exclusive_lock(MockTestImageCtx &mock_image_ctx,
+ MockExclusiveLock &mock_exclusive_lock,
+ int r) {
+ if (m_mock_imctx->exclusive_lock != nullptr) {
+ EXPECT_CALL(mock_exclusive_lock, shut_down(_))
+ .WillOnce(DoAll(ShutDownExclusiveLock(&mock_image_ctx),
+ CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+ }
+ }
+
+ void expect_is_exclusive_lock_owner(MockTestImageCtx &mock_image_ctx,
+ MockExclusiveLock &mock_exclusive_lock,
+ bool is_owner) {
+ if (m_mock_imctx->exclusive_lock != nullptr) {
+ EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillOnce(Return(is_owner));
+ }
+ }
+
+ void expect_list_image_watchers(
+ MockTestImageCtx &mock_image_ctx,
+ MockListWatchersRequest &mock_list_watchers_request, int r) {
+ EXPECT_CALL(mock_list_watchers_request, send())
+ .WillOnce(FinishRequest(&mock_list_watchers_request, r, &mock_image_ctx));
+ }
+
+ void expect_get_group(MockTestImageCtx &mock_image_ctx, int r) {
+ if (mock_image_ctx.old_format) {
+ return;
+ }
+
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+ StrEq("image_group_get"), _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
+ void expect_remove_snap(MockTestImageCtx &mock_image_ctx,
+ MockSnapshotRemoveRequest& mock_snap_remove_request,
+ int r) {
+ EXPECT_CALL(mock_snap_remove_request, send())
+ .WillOnce(FinishRequest(&mock_snap_remove_request, r, &mock_image_ctx));
+ }
+
+ librbd::ImageCtx *m_test_imctx = nullptr;
+ MockTestImageCtx *m_mock_imctx = nullptr;
+};
+
+TEST_F(TestMockImagePreRemoveRequest, Success) {
+ MockExclusiveLock *mock_exclusive_lock = new MockExclusiveLock();
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, 0);
+
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
+
+ expect_get_group(*m_mock_imctx, 0);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, true, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, OperationsDisabled) {
+ REQUIRE_FORMAT_V2();
+
+ m_mock_imctx->operations_disabled = true;
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EROFS, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, ExclusiveLockShutDownFailed) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ MockExclusiveLock *mock_exclusive_lock = new MockExclusiveLock();
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, true, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, ExclusiveLockTryAcquireFailed) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_try_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock,
+ -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, Migration) {
+ m_mock_imctx->features |= RBD_FEATURE_MIGRATING;
+
+ expect_test_features(*m_mock_imctx);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, Snapshots) {
+ m_mock_imctx->snap_info = {
+ {123, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, {}, {}, {}, {}, {}}}};
+
+ expect_test_features(*m_mock_imctx);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-ENOTEMPTY, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, Watchers) {
+ MockExclusiveLock mock_exclusive_lock;
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_try_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, 0);
+ expect_is_exclusive_lock_owner(*m_mock_imctx, mock_exclusive_lock, true);
+
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request,
+ -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, GroupError) {
+ REQUIRE_FORMAT_V2();
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ InSequence seq;
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_try_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, 0);
+ expect_is_exclusive_lock_owner(*m_mock_imctx, mock_exclusive_lock, true);
+
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
+
+ expect_get_group(*m_mock_imctx, -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImagePreRemoveRequest, AutoDeleteSnapshots) {
+ REQUIRE_FORMAT_V2();
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
+ }
+
+ expect_op_work_queue(*m_mock_imctx);
+ expect_test_features(*m_mock_imctx);
+
+ m_mock_imctx->snap_info = {
+ {123, {"snap1", {cls::rbd::TrashSnapshotNamespace{}}, {}, {}, {}, {}, {}}}};
+
+ InSequence seq;
+ expect_set_journal_policy(*m_mock_imctx);
+ expect_try_acquire_exclusive_lock(*m_mock_imctx, mock_exclusive_lock, 0);
+ expect_is_exclusive_lock_owner(*m_mock_imctx, mock_exclusive_lock, true);
+
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
+
+ expect_get_group(*m_mock_imctx, 0);
+
+ MockSnapshotRemoveRequest mock_snap_remove_request;
+ expect_remove_snap(*m_mock_imctx, mock_snap_remove_request, 0);
+
+ C_SaferCond ctx;
+ auto req = MockPreRemoveRequest::create(m_mock_imctx, false, &ctx);
+ req->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
#include "test/librados_test_stub/MockTestMemRadosClient.h"
#include "librbd/ImageState.h"
#include "librbd/internal.h"
-#include "librbd/Operations.h"
#include "librbd/image/TypeTraits.h"
#include "librbd/image/DetachChildRequest.h"
-#include "librbd/image/ListWatchersRequest.h"
+#include "librbd/image/PreRemoveRequest.h"
#include "librbd/image/RemoveRequest.h"
-#include "librbd/image/RefreshParentRequest.h"
#include "librbd/journal/RemoveRequest.h"
#include "librbd/mirror/DisableRequest.h"
-#include "librbd/operation/SnapshotRemoveRequest.h"
#include "librbd/operation/TrimRequest.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
} // anonymous namespace
+
namespace image {
template <>
DetachChildRequest<MockTestImageCtx> *DetachChildRequest<MockTestImageCtx>::s_instance;
-} // namespace image
-
-namespace operation {
-
template <>
-class SnapshotRemoveRequest<MockTestImageCtx> {
+class PreRemoveRequest<MockTestImageCtx> {
public:
- static SnapshotRemoveRequest *s_instance;
- static SnapshotRemoveRequest *create(MockTestImageCtx &image_ctx,
- cls::rbd::SnapshotNamespace sn,
- std::string name,
- uint64_t id, Context *on_finish) {
+ static PreRemoveRequest *s_instance;
+ static PreRemoveRequest *create(MockTestImageCtx* image_ctx, bool force,
+ Context* on_finish) {
ceph_assert(s_instance != nullptr);
s_instance->on_finish = on_finish;
return s_instance;
Context *on_finish = nullptr;
- SnapshotRemoveRequest() {
+ PreRemoveRequest() {
s_instance = this;
}
MOCK_METHOD0(send, void());
};
-SnapshotRemoveRequest<MockTestImageCtx> *SnapshotRemoveRequest<MockTestImageCtx>::s_instance;
+PreRemoveRequest<MockTestImageCtx> *PreRemoveRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+
+namespace operation {
template <>
class TrimRequest<MockTestImageCtx> {
DisableRequest<MockTestImageCtx> *DisableRequest<MockTestImageCtx>::s_instance;
} // namespace mirror
-
-namespace image {
-
-template<>
-class ListWatchersRequest<MockTestImageCtx> {
-public:
- static ListWatchersRequest *s_instance;
- Context *on_finish = nullptr;
-
- static ListWatchersRequest *create(MockTestImageCtx &image_ctx, int flags,
- std::list<obj_watch_t> *watchers,
- Context *on_finish) {
- ceph_assert(s_instance != nullptr);
- s_instance->on_finish = on_finish;
- return s_instance;
- }
-
- ListWatchersRequest() {
- s_instance = this;
- }
-
- MOCK_METHOD0(send, void());
-};
-
-ListWatchersRequest<MockTestImageCtx> *ListWatchersRequest<MockTestImageCtx>::s_instance;
-
-} // namespace image
} // namespace librbd
// template definitions
#include "librbd/image/RemoveRequest.cc"
-ACTION_P(TestFeatures, image_ctx) {
- return ((image_ctx->features & arg0) != 0);
-}
-
-ACTION_P(ShutDownExclusiveLock, image_ctx) {
- // shutting down exclusive lock will close object map and journal
- image_ctx->exclusive_lock = nullptr;
- image_ctx->object_map = nullptr;
- image_ctx->journal = nullptr;
-}
-
namespace librbd {
namespace image {
typedef ::librbd::image::TypeTraits<MockTestImageCtx> TypeTraits;
typedef typename TypeTraits::ContextWQ ContextWQ;
typedef RemoveRequest<MockTestImageCtx> MockRemoveRequest;
+ typedef PreRemoveRequest<MockTestImageCtx> MockPreRemoveRequest;
typedef DetachChildRequest<MockTestImageCtx> MockDetachChildRequest;
- typedef ListWatchersRequest<MockTestImageCtx> MockListWatchersRequest;
- typedef librbd::operation::SnapshotRemoveRequest<MockTestImageCtx> MockSnapshotRemoveRequest;
typedef librbd::operation::TrimRequest<MockTestImageCtx> MockTrimRequest;
typedef librbd::journal::RemoveRequest<MockTestImageCtx> MockJournalRemoveRequest;
typedef librbd::mirror::DisableRequest<MockTestImageCtx> MockMirrorDisableRequest;
librbd::ImageCtx *m_test_imctx = NULL;
MockTestImageCtx *m_mock_imctx = NULL;
-
void SetUp() override {
TestMockFixture::SetUp();
}));
}
- void expect_list_image_watchers(
- MockTestImageCtx &mock_image_ctx,
- MockListWatchersRequest &mock_list_watchers_request, int r) {
- EXPECT_CALL(mock_list_watchers_request, send())
- .WillOnce(FinishRequest(&mock_list_watchers_request, r, &mock_image_ctx));
- }
-
- void expect_get_group(MockTestImageCtx &mock_image_ctx, int r) {
- auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
- exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
- StrEq("image_group_get"), _, _, _));
- if (r < 0) {
- expect.WillOnce(Return(r));
- } else {
- expect.WillOnce(DoDefault());
- }
- }
-
- void expect_remove_snap(MockTestImageCtx &mock_image_ctx,
- MockSnapshotRemoveRequest& mock_snap_remove_request,
- int r) {
- EXPECT_CALL(mock_snap_remove_request, send())
- .WillOnce(FinishRequest(&mock_snap_remove_request, r, &mock_image_ctx));
+ void expect_pre_remove_image(MockTestImageCtx &mock_image_ctx,
+ MockPreRemoveRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(FinishRequest(&mock_request, r, &mock_image_ctx));
}
void expect_trim(MockTestImageCtx &mock_image_ctx,
EXPECT_CALL(mock_request, send())
.WillOnce(FinishRequest(&mock_request, r, &mock_image_ctx));
}
-
- void expect_test_features(MockTestImageCtx &mock_image_ctx) {
- EXPECT_CALL(mock_image_ctx, test_features(_))
- .WillRepeatedly(TestFeatures(&mock_image_ctx));
- }
-
- void expect_set_journal_policy(MockTestImageCtx &mock_image_ctx) {
- if (m_test_imctx->test_features(RBD_FEATURE_JOURNALING)) {
- EXPECT_CALL(mock_image_ctx, set_journal_policy(_))
- .WillOnce(Invoke([](journal::Policy* policy) {
- ASSERT_TRUE(policy->journal_disabled());
- delete policy;
- }));
- }
- }
-
- void expect_shut_down_exclusive_lock(MockTestImageCtx &mock_image_ctx,
- MockExclusiveLock &mock_exclusive_lock,
- int r) {
- if (m_mock_imctx->exclusive_lock != nullptr) {
- EXPECT_CALL(mock_exclusive_lock, shut_down(_))
- .WillOnce(DoAll(ShutDownExclusiveLock(&mock_image_ctx),
- CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
- }
- }
-
};
TEST_F(TestMockImageRemoveRequest, SuccessV1) {
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
- expect_test_features(*m_mock_imctx);
- MockListWatchersRequest mock_list_watchers_request;
- expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
+ MockPreRemoveRequest mock_pre_remove_request;
+ expect_pre_remove_image(*m_mock_imctx, mock_pre_remove_request, 0);
MockTrimRequest mock_trim_request;
expect_trim(*m_mock_imctx, mock_trim_request, 0);
TEST_F(TestMockImageRemoveRequest, SuccessV2CloneV1) {
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
- MockExclusiveLock *mock_exclusive_lock = new MockExclusiveLock();
- if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
- m_mock_imctx->exclusive_lock = mock_exclusive_lock;
- }
-
expect_op_work_queue(*m_mock_imctx);
m_mock_imctx->parent_md.spec.pool_id = m_ioctx.get_id();
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
- expect_test_features(*m_mock_imctx);
-
- if (m_mock_imctx->exclusive_lock != nullptr) {
- expect_test_features(*m_mock_imctx);
- }
- expect_set_journal_policy(*m_mock_imctx);
- expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, 0);
-
- expect_test_features(*m_mock_imctx);
-
- MockListWatchersRequest mock_list_watchers_request;
- expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
- expect_get_group(*m_mock_imctx, 0);
+ MockPreRemoveRequest mock_pre_remove_request;
+ expect_pre_remove_image(*m_mock_imctx, mock_pre_remove_request, 0);
MockTrimRequest mock_trim_request;
expect_trim(*m_mock_imctx, mock_trim_request, 0);
TEST_F(TestMockImageRemoveRequest, SuccessV2CloneV2) {
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
- MockExclusiveLock *mock_exclusive_lock = new MockExclusiveLock();
- if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
- m_mock_imctx->exclusive_lock = mock_exclusive_lock;
- }
-
expect_op_work_queue(*m_mock_imctx);
m_mock_imctx->parent_md.spec.pool_id = m_ioctx.get_id();
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
- expect_test_features(*m_mock_imctx);
- if (m_mock_imctx->exclusive_lock != nullptr) {
- expect_test_features(*m_mock_imctx);
- }
- expect_set_journal_policy(*m_mock_imctx);
- expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, 0);
-
- expect_test_features(*m_mock_imctx);
-
- MockListWatchersRequest mock_list_watchers_request;
- expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
-
- expect_get_group(*m_mock_imctx, 0);
+ MockPreRemoveRequest mock_pre_remove_request;
+ expect_pre_remove_image(*m_mock_imctx, mock_pre_remove_request, 0);
MockTrimRequest mock_trim_request;
expect_trim(*m_mock_imctx, mock_trim_request, 0);
TEST_F(TestMockImageRemoveRequest, NotExistsV2) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
- MockExclusiveLock *mock_exclusive_lock = new MockExclusiveLock();
- if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
- m_mock_imctx->exclusive_lock = mock_exclusive_lock;
- }
-
expect_op_work_queue(*m_mock_imctx);
m_mock_imctx->parent_md.spec.pool_id = m_ioctx.get_id();
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
- expect_test_features(*m_mock_imctx);
-
- expect_test_features(*m_mock_imctx);
- expect_set_journal_policy(*m_mock_imctx);
- expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, 0);
-
- expect_test_features(*m_mock_imctx);
- MockListWatchersRequest mock_list_watchers_request;
- expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
-
- expect_get_group(*m_mock_imctx, 0);
+ MockPreRemoveRequest mock_pre_remove_request;
+ expect_pre_remove_image(*m_mock_imctx, mock_pre_remove_request, 0);
MockTrimRequest mock_trim_request;
expect_trim(*m_mock_imctx, mock_trim_request, 0);
ASSERT_EQ(-ENOENT, ctx.wait());
}
-TEST_F(TestMockImageRemoveRequest, OperationsDisabled) {
- MockExclusiveLock mock_exclusive_lock;
- if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
- m_mock_imctx->exclusive_lock = &mock_exclusive_lock;
- }
-
- m_mock_imctx->operations_disabled = true;
-
- InSequence seq;
- expect_state_open(*m_mock_imctx, 0);
- expect_test_features(*m_mock_imctx);
- expect_state_close(*m_mock_imctx);
-
- C_SaferCond ctx;
- librbd::NoOpProgressContext no_op;
- ContextWQ op_work_queue;
- MockRemoveRequest *req = MockRemoveRequest::create(
- m_ioctx, m_image_name, "", true, false, no_op, &op_work_queue, &ctx);
- req->send();
-
- ASSERT_EQ(-EROFS, ctx.wait());
-}
-
-TEST_F(TestMockImageRemoveRequest, Migration) {
- m_mock_imctx->features |= RBD_FEATURE_MIGRATING;
-
- InSequence seq;
- expect_state_open(*m_mock_imctx, 0);
- expect_test_features(*m_mock_imctx);
- expect_state_close(*m_mock_imctx);
-
- C_SaferCond ctx;
- librbd::NoOpProgressContext no_op;
- ContextWQ op_work_queue;
- MockRemoveRequest *req = MockRemoveRequest::create(
- m_ioctx, m_image_name, "", true, false, no_op, &op_work_queue, &ctx);
- req->send();
-
- ASSERT_EQ(-EBUSY, ctx.wait());
-}
-
-TEST_F(TestMockImageRemoveRequest, Snapshots) {
- m_mock_imctx->snap_info = {
- {123, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, {}, {}, {}, {}, {}}}};
-
- InSequence seq;
- expect_state_open(*m_mock_imctx, 0);
- expect_test_features(*m_mock_imctx);
- expect_state_close(*m_mock_imctx);
-
- C_SaferCond ctx;
- librbd::NoOpProgressContext no_op;
- ContextWQ op_work_queue;
- MockRemoveRequest *req = MockRemoveRequest::create(
- m_ioctx, m_image_name, "", true, false, no_op, &op_work_queue, &ctx);
- req->send();
-
- ASSERT_EQ(-ENOTEMPTY, ctx.wait());
-}
-
-TEST_F(TestMockImageRemoveRequest, AutoDeleteSnapshots) {
- REQUIRE_FORMAT_V2();
-
- MockExclusiveLock *mock_exclusive_lock = new MockExclusiveLock();
- if (m_test_imctx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
- m_mock_imctx->exclusive_lock = mock_exclusive_lock;
- }
-
- expect_op_work_queue(*m_mock_imctx);
-
- m_mock_imctx->snap_info = {
- {123, {"snap1", {cls::rbd::TrashSnapshotNamespace{}}, {}, {}, {}, {}, {}}}};
-
- InSequence seq;
- expect_state_open(*m_mock_imctx, 0);
- expect_test_features(*m_mock_imctx);
-
- if (m_mock_imctx->exclusive_lock != nullptr) {
- expect_test_features(*m_mock_imctx);
- }
- expect_set_journal_policy(*m_mock_imctx);
- expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, 0);
-
- expect_test_features(*m_mock_imctx);
-
- MockListWatchersRequest mock_list_watchers_request;
- expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
-
- expect_get_group(*m_mock_imctx, 0);
-
- MockSnapshotRemoveRequest mock_snap_remove_request;
- expect_remove_snap(*m_mock_imctx, mock_snap_remove_request, 0);
-
- MockTrimRequest mock_trim_request;
- expect_trim(*m_mock_imctx, mock_trim_request, 0);
-
- MockDetachChildRequest mock_detach_child_request;
- expect_detach_child(*m_mock_imctx, mock_detach_child_request, 0);
-
- MockMirrorDisableRequest mock_mirror_disable_request;
- expect_mirror_disable(*m_mock_imctx, mock_mirror_disable_request, 0);
-
- expect_state_close(*m_mock_imctx);
-
- MockJournalRemoveRequest mock_journal_remove_request;
- expect_journal_remove(*m_mock_imctx, mock_journal_remove_request, 0);
-
- expect_remove_mirror_image(m_ioctx, 0);
- expect_dir_remove_image(m_ioctx, 0);
-
- C_SaferCond ctx;
- librbd::NoOpProgressContext no_op;
- ContextWQ op_work_queue;
- MockRemoveRequest *req = MockRemoveRequest::create(
- m_ioctx, m_image_name, "", true, false, no_op, &op_work_queue, &ctx);
- req->send();
-
- ASSERT_EQ(0, ctx.wait());
-
-}
-
} // namespace image
} // namespace librbd