]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: behave more gracefully when data pool removed
authorMykola Golub <mgolub@suse.com>
Mon, 12 Aug 2019 13:42:51 +0000 (14:42 +0100)
committerJason Dillaman <dillaman@redhat.com>
Wed, 9 Oct 2019 20:51:23 +0000 (16:51 -0400)
allowing to open the image and do some maintenance operations,
and returning -ENODEV for ops that require data pool.

Fixes: https://tracker.ceph.com/issues/41206
Signed-off-by: Mykola Golub <mgolub@suse.com>
(cherry picked from commit 00b2c86f7c486fbd82e6ce27edbd90f866de88af)

Conflicts:
src/librbd/cache/ObjectCacherObjectDispatch.cc: trivial resolution
src/librbd/cache/ParentCacheObjectDispatch.cc: DNE
src/librbd/image/RefreshRequest.cc: trivial resolution
src/librbd/image/OpenRequest.cc: don't init cache on invalid data_ctx
src/librbd/io/ImageRequestWQ.cc: get reference to AioCompletion before failing

25 files changed:
src/librbd/DeepCopyRequest.cc
src/librbd/ImageCtx.cc
src/librbd/ImageCtx.h
src/librbd/api/DiffIterate.cc
src/librbd/api/Image.cc
src/librbd/api/Image.h
src/librbd/api/Snapshot.cc
src/librbd/cache/ObjectCacherObjectDispatch.cc
src/librbd/deep_copy/ObjectCopyRequest.cc
src/librbd/image/OpenRequest.cc
src/librbd/image/RefreshRequest.cc
src/librbd/image/RemoveRequest.cc
src/librbd/image/RemoveRequest.h
src/librbd/io/CopyupRequest.cc
src/librbd/io/ImageRequestWQ.cc
src/librbd/io/ObjectRequest.cc
src/librbd/librbd.cc
src/librbd/operation/ObjectMapIterate.cc
src/librbd/operation/SnapshotCreateRequest.cc
src/librbd/operation/SnapshotRemoveRequest.cc
src/librbd/operation/SparsifyRequest.cc
src/librbd/operation/TrimRequest.cc
src/test/librbd/image/test_mock_RefreshRequest.cc
src/test/librbd/mock/MockImageCtx.h
src/test/librbd/test_internal.cc

index 45191158e38c4e50f218ad32b3b0d141a5914285..1d56b255d4cbab2e0021b6ffdd20cd6475b9f370 100644 (file)
@@ -50,6 +50,18 @@ DeepCopyRequest<I>::~DeepCopyRequest() {
 
 template <typename I>
 void DeepCopyRequest<I>::send() {
+  if (!m_src_image_ctx->data_ctx.is_valid()) {
+    lderr(m_cct) << "missing data pool for source image" << dendl;
+    finish(-ENODEV);
+    return;
+  }
+
+  if (!m_dst_image_ctx->data_ctx.is_valid()) {
+    lderr(m_cct) << "missing data pool for destination image" << dendl;
+    finish(-ENODEV);
+    return;
+  }
+
   int r = validate_copy_points();
   if (r < 0) {
     finish(r);
index 19d3ce70c9bee57e7deb846b4be9a38771368705..6ad3b7455e471fa0ae917cc3819923934d816770 100644 (file)
@@ -172,7 +172,9 @@ public:
     delete[] format_string;
 
     md_ctx.aio_flush();
-    data_ctx.aio_flush();
+    if (data_ctx.is_valid()) {
+      data_ctx.aio_flush();
+    }
     io_work_queue->drain();
 
     delete io_object_dispatcher;
@@ -191,7 +193,7 @@ public:
     asok_hook = new LibrbdAdminSocketHook(this);
 
     string pname = string("librbd-") + id + string("-") +
-      data_ctx.get_pool_name() + string("-") + name;
+      md_ctx.get_pool_name() + string("-") + name;
     if (!snap_name.empty()) {
       pname += "-";
       pname += snap_name;
@@ -212,7 +214,7 @@ public:
     asok_hook = nullptr;
   }
 
-  void ImageCtx::init_layout()
+  void ImageCtx::init_layout(int64_t pool_id)
   {
     if (stripe_unit == 0 || stripe_count == 0) {
       stripe_unit = 1ull << order;
@@ -229,7 +231,7 @@ public:
     layout.stripe_unit = stripe_unit;
     layout.stripe_count = stripe_count;
     layout.object_size = 1ull << order;
-    layout.pool_id = data_ctx.get_id();  // FIXME: pool id overflow?
+    layout.pool_id = pool_id;  // FIXME: pool id overflow?
 
     delete[] format_string;
     size_t len = object_prefix.length() + 16;
@@ -329,7 +331,9 @@ public:
       snap_namespace = it->second.snap_namespace;
       snap_name = it->second.name;
       snap_exists = true;
-      data_ctx.snap_set_read(snap_id);
+      if (data_ctx.is_valid()) {
+        data_ctx.snap_set_read(snap_id);
+      }
       return 0;
     }
     return -ENOENT;
@@ -342,7 +346,9 @@ public:
     snap_namespace = {};
     snap_name = "";
     snap_exists = true;
-    data_ctx.snap_set_read(snap_id);
+    if (data_ctx.is_valid()) {
+      data_ctx.snap_set_read(snap_id);
+    }
   }
 
   snap_t ImageCtx::get_snap_id(const cls::rbd::SnapshotNamespace& in_snap_namespace,
index fe1c4828cabbd85dc030bf8009e63301bc36f89b..f60dec9886406c259a2fe6ef6452fdc9625821fa 100644 (file)
@@ -223,7 +223,7 @@ namespace librbd {
     ~ImageCtx();
     void init();
     void shutdown();
-    void init_layout();
+    void init_layout(int64_t pool_id);
     void perf_start(std::string name);
     void perf_stop();
     void set_read_flag(unsigned flag);
index 148734b7e50a8eb7e72e40721335fd0dab512500..55fefcc211e7e5c9f4c3038b62a06a03a1a85d92 100644 (file)
@@ -238,6 +238,10 @@ int DiffIterate<I>::diff_iterate(I *ictx,
   ldout(ictx->cct, 20) << "diff_iterate " << ictx << " off = " << off
                 << " len = " << len << dendl;
 
+  if (!ictx->data_ctx.is_valid()) {
+    return -ENODEV;
+  }
+
   // ensure previous writes are visible to listsnaps
   C_SaferCond flush_ctx;
   {
@@ -276,6 +280,8 @@ template <typename I>
 int DiffIterate<I>::execute() {
   CephContext* cct = m_image_ctx.cct;
 
+  ceph_assert(m_image_ctx.data_ctx.is_valid());
+
   librados::IoCtx head_ctx;
   librados::snap_t from_snap_id = 0;
   librados::snap_t end_snap_id;
index 13c5a90d8c547421945af4d0a7bd0484e646abad..bb995e0f6ddbe532982d5cd70122ebb522c4497b 100644 (file)
@@ -76,6 +76,23 @@ int pre_remove_image(librados::IoCtx& io_ctx, const std::string& image_id) {
 
 } // anonymous namespace
 
+template <typename I>
+int64_t Image<I>::get_data_pool_id(I *ictx) {
+  if (ictx->data_ctx.is_valid()) {
+    return ictx->data_ctx.get_id();
+  }
+
+  int64_t pool_id;
+  int r = cls_client::get_data_pool(&ictx->md_ctx, ictx->header_oid, &pool_id);
+  if (r < 0) {
+    CephContext *cct = ictx->cct;
+    lderr(cct) << "error getting data pool ID: " << cpp_strerror(r) << dendl;
+    return r;
+  }
+
+  return pool_id;
+}
+
 template <typename I>
 int Image<I>::get_op_features(I *ictx, uint64_t *op_features) {
   CephContext *cct = ictx->cct;
index 8bfadae70de931f4cd503afc00a76e8eb902a22b..af928c84c06c93f8d1f39407fbcd40b4460b8ad2 100644 (file)
@@ -24,6 +24,8 @@ template <typename ImageCtxT = librbd::ImageCtx>
 struct Image {
   typedef std::map<std::string, std::string> ImageNameToIds;
 
+  static int64_t get_data_pool_id(ImageCtxT *ictx);
+
   static int get_op_features(ImageCtxT *ictx, uint64_t *op_features);
 
   static int list_images(librados::IoCtx& io_ctx,
index 99010bf4d3537840ef5744d9d48dd792f0dc7373..3d29cfb263a2078a239642ffb702fc37e3bbc3be 100644 (file)
@@ -111,7 +111,7 @@ int Snapshot<I>::get_group_namespace(I *ictx, uint64_t snap_id,
     return -ENOENT;
   }
 
-  GetGroupVisitor ggv = GetGroupVisitor(ictx->cct, &ictx->data_ctx, group_snap);
+  GetGroupVisitor ggv = GetGroupVisitor(ictx->cct, &ictx->md_ctx, group_snap);
   r = boost::apply_visitor(ggv, snap_info->snap_namespace);
   if (r < 0) {
     return r;
index f108247b2542350c4a246953bc560b8f939bc9bf..5bced71bfdf365ca55c45a1a9b23be34d50d2f24 100644 (file)
@@ -78,6 +78,7 @@ ObjectCacherObjectDispatch<I>::ObjectCacherObjectDispatch(
   : m_image_ctx(image_ctx),
     m_cache_lock(util::unique_lock_name(
       "librbd::cache::ObjectCacherObjectDispatch::cache_lock", this)) {
+  ceph_assert(m_image_ctx->data_ctx.is_valid());
 }
 
 template <typename I>
index 14acda37edfb696f44e55a9b0d9113cf47b92cfc..92a1750f1152e2c753454673b9f2e21c67d049fe 100644 (file)
@@ -50,6 +50,8 @@ ObjectCopyRequest<I>::ObjectCopyRequest(I *src_image_ctx,
     m_dst_image_ctx(dst_image_ctx), m_cct(dst_image_ctx->cct),
     m_snap_map(snap_map), m_dst_object_number(dst_object_number),
     m_flatten(flatten), m_on_finish(on_finish) {
+  ceph_assert(src_image_ctx->data_ctx.is_valid());
+  ceph_assert(dst_image_ctx->data_ctx.is_valid());
   ceph_assert(!m_snap_map.empty());
 
   m_src_async_op = new io::AsyncOperation();
index dd9e603dc98bdae7d4270eabc9270b702215ddfd..7d4625aea1c28081f157725fd26ad1723fc2ff42 100644 (file)
@@ -473,13 +473,19 @@ Context *OpenRequest<I>::handle_v2_get_data_pool(int *result) {
     *result = util::create_ioctx(m_image_ctx->md_ctx, "data pool", data_pool_id,
                                  {}, &m_image_ctx->data_ctx);
     if (*result < 0) {
-      send_close_image(*result);
-      return nullptr;
+      if (*result != -ENOENT) {
+        send_close_image(*result);
+        return nullptr;
+      }
+      m_image_ctx->data_ctx.close();
+    } else {
+      m_image_ctx->data_ctx.set_namespace(m_image_ctx->md_ctx.get_namespace());
     }
-    m_image_ctx->data_ctx.set_namespace(m_image_ctx->md_ctx.get_namespace());
+  } else {
+    data_pool_id = m_image_ctx->md_ctx.get_id();
   }
 
-  m_image_ctx->init_layout();
+  m_image_ctx->init_layout(data_pool_id);
   send_refresh();
   return nullptr;
 }
@@ -516,7 +522,8 @@ Context *OpenRequest<I>::handle_refresh(int *result) {
 template <typename I>
 Context *OpenRequest<I>::send_init_cache(int *result) {
   // cache is disabled or parent image context
-  if (!m_image_ctx->cache || m_image_ctx->child != nullptr) {
+  if (!m_image_ctx->cache || m_image_ctx->child != nullptr ||
+      !m_image_ctx->data_ctx.is_valid()) {
     return send_register_watch(result);
   }
 
index 664f5b8b793e5f63b9543fcfb52b0cbf473509bd..7ec565d6cafc342f80192ae78d9066ccc552bcec 100644 (file)
@@ -1313,7 +1313,7 @@ void RefreshRequest<I>::apply() {
       m_image_ctx.op_features = 0;
       m_image_ctx.operations_disabled = false;
       m_image_ctx.object_prefix = std::move(m_object_prefix);
-      m_image_ctx.init_layout();
+      m_image_ctx.init_layout(m_image_ctx.md_ctx.get_id());
     } else {
       // HEAD revision doesn't have a defined overlap so it's only
       // applicable to snapshots
@@ -1396,8 +1396,10 @@ void RefreshRequest<I>::apply() {
     if (m_refresh_parent != nullptr) {
       m_refresh_parent->apply();
     }
-    m_image_ctx.data_ctx.selfmanaged_snap_set_write_ctx(m_image_ctx.snapc.seq,
-                                                        m_image_ctx.snaps);
+    if (m_image_ctx.data_ctx.is_valid()) {
+      m_image_ctx.data_ctx.selfmanaged_snap_set_write_ctx(m_image_ctx.snapc.seq,
+                                                          m_image_ctx.snaps);
+    }
 
     // handle dynamically enabled / disabled features
     if (m_image_ctx.exclusive_lock != nullptr &&
index 1cff51b3eae5d777f9fa62079df35e3bfeb098e0..8e029f8b91223527f3c63281ef9661c8bbed0d1b 100644 (file)
@@ -126,6 +126,11 @@ void RemoveRequest<I>::handle_pre_remove_image(int r) {
     return;
   }
 
+  if (!m_image_ctx->data_ctx.is_valid()) {
+    detach_child();
+    return;
+  }
+
   trim_image();
 }
 
index 40a571e5aab85b9e895101638188a83127d01fc8..98d597645a4913bfc418bf46526f96be36de25dd 100644 (file)
@@ -63,7 +63,7 @@ private:
    *                              PRE REMOVE IMAGE * * *        |
    *                                     |             *        |
    *                                     v             *        |
-   *                                TRIM IMAGE * * * * *        |
+   *    (skip if invalid data pool) TRIM IMAGE * * * * *        |
    *                                     |             *        |
    *                                     v             *        |
    *                                DETACH CHILD       *        |
index 3ab44d0ec8599be139c9b4313fb45056e5951fa3..06ef47ca86504af6d32829717b9836c0b0b0fc8f 100644 (file)
@@ -114,6 +114,7 @@ CopyupRequest<I>::CopyupRequest(I *ictx, const std::string &oid,
     m_trace(util::create_trace(*m_image_ctx, "copy-up", parent_trace)),
     m_lock("CopyupRequest", false, false)
 {
+  ceph_assert(m_image_ctx->data_ctx.is_valid());
   m_async_op.start_op(*util::get_image_ctx(m_image_ctx));
 }
 
index 32584640d7ddb8e879aa8d76445f53773484a95c..34f2c2cf2bae49d060d963386d124d48b5b32496 100644 (file)
@@ -844,6 +844,15 @@ int ImageRequestWQ<I>::start_in_flight_io(AioCompletion *c) {
     return false;
   }
 
+  if (!m_image_ctx.data_ctx.is_valid()) {
+    CephContext *cct = m_image_ctx.cct;
+    lderr(cct) << "missing data pool" << dendl;
+
+    c->get();
+    c->fail(-ENODEV);
+    return false;
+  }
+
   m_in_flight_ios++;
   return true;
 }
index 57bd7291c6824575029a6a17a9ac215a72dbcc4c..58b1708455eb09bd1e55d21e5d618464c29ca3d0 100644 (file)
@@ -115,6 +115,7 @@ ObjectRequest<I>::ObjectRequest(I *ictx, const std::string &oid,
   : m_ictx(ictx), m_oid(oid), m_object_no(objectno), m_object_off(off),
     m_object_len(len), m_snap_id(snap_id), m_completion(completion),
     m_trace(util::create_trace(*ictx, "", trace)) {
+  ceph_assert(m_ictx->data_ctx.is_valid());
   if (m_trace.valid()) {
     m_trace.copy_name(trace_name + std::string(" ") + oid);
     m_trace.event("start");
index 605fe133239288c8c28c83241e8d7b23a389ed67..80dcbf8b2f50df6d74316a19fc8ec5623ea01384 100644 (file)
@@ -1457,7 +1457,7 @@ namespace librbd {
   int64_t Image::get_data_pool_id()
   {
     ImageCtx *ictx = reinterpret_cast<ImageCtx *>(ctx);
-    return ictx->data_ctx.get_id();
+    return librbd::api::Image<>::get_data_pool_id(ictx);
   }
 
   int Image::parent_info(string *parent_pool_name, string *parent_name,
@@ -4228,7 +4228,7 @@ extern "C" int rbd_get_block_name_prefix(rbd_image_t image, char *prefix,
 extern "C" int64_t rbd_get_data_pool_id(rbd_image_t image)
 {
   librbd::ImageCtx *ictx = reinterpret_cast<librbd::ImageCtx *>(image);
-  return ictx->data_ctx.get_id();
+  return librbd::api::Image<>::get_data_pool_id(ictx);
 }
 
 extern "C" int rbd_get_parent_info(rbd_image_t image,
index 658dbb4a73762d5c8adbf0a1e81f9df449c4c60e..669584164902e414b9f615a303c2e415f2b94c0d 100644 (file)
@@ -194,6 +194,11 @@ private:
 
 template <typename I>
 void ObjectMapIterateRequest<I>::send() {
+  if (!m_image_ctx.data_ctx.is_valid()) {
+    this->async_complete(-ENODEV);
+    return;
+  }
+
   send_verify_objects();
 }
 
@@ -201,6 +206,12 @@ template <typename I>
 bool ObjectMapIterateRequest<I>::should_complete(int r) {
   CephContext *cct = m_image_ctx.cct;
   ldout(cct, 5) << this << " should_complete: " << " r=" << r << dendl;
+
+  if (r == -ENODEV) {
+    lderr(cct) << "missing data pool" << dendl;
+    return true;
+  }
+
   if (r < 0) {
     lderr(cct) << "object map operation encountered an error: "
               << cpp_strerror(r) << dendl;
index 31553177306f0183335f3abaea910d5b68d42ef8..66293609166e1d1ad90a59a452d1d299aad060c9 100644 (file)
@@ -36,6 +36,15 @@ SnapshotCreateRequest<I>::SnapshotCreateRequest(I &image_ctx,
 
 template <typename I>
 void SnapshotCreateRequest<I>::send_op() {
+  I &image_ctx = this->m_image_ctx;
+  CephContext *cct = image_ctx.cct;
+
+  if (!image_ctx.data_ctx.is_valid()) {
+    lderr(cct) << "missing data pool" << dendl;
+    this->async_complete(-ENODEV);
+    return;
+  }
+
   send_suspend_requests();
 }
 
index 8c34ebf6c5342a8ad1d59bd2ed137fe6fc2b4405..fde41a5f363dc30eb762325bf4a46bb7c4bbc128 100644 (file)
@@ -263,6 +263,11 @@ template <typename I>
 void SnapshotRemoveRequest<I>::release_snap_id() {
   I &image_ctx = this->m_image_ctx;
 
+  if (!image_ctx.data_ctx.is_valid()) {
+    remove_snap();
+    return;
+  }
+
   CephContext *cct = image_ctx.cct;
   ldout(cct, 5) << "snap_name=" << m_snap_name << ", "
                 << "snap_id=" << m_snap_id << dendl;
index 4dc6bde7e3240f747572b3b67c480e4b920887b9..53049f886e7a51443c16e0c8676746098adf1ac0 100644 (file)
@@ -124,6 +124,11 @@ public:
 
     ldout(m_cct, 20) << dendl;
 
+    if (!image_ctx.data_ctx.is_valid()) {
+      lderr(m_cct) << "missing data pool" << dendl;
+      return -ENODEV;
+    }
+
     if (image_ctx.exclusive_lock != nullptr &&
         !image_ctx.exclusive_lock->is_lock_owner()) {
       ldout(m_cct, 1) << "lost exclusive lock during sparsify" << dendl;
index cecf37b3e6138936b3764f6993051b1e2926d204..15f6a5dfd8185eea00ed8239f0409481dc42faa3 100644 (file)
@@ -174,6 +174,15 @@ bool TrimRequest<I>::should_complete(int r)
 
 template <typename I>
 void TrimRequest<I>::send() {
+  I &image_ctx = this->m_image_ctx;
+  CephContext *cct = image_ctx.cct;
+
+  if (!image_ctx.data_ctx.is_valid()) {
+    lderr(cct) << "missing data pool" << dendl;
+    send_finish(-ENODEV);
+    return;
+  }
+
   send_pre_trim();
 }
 
index 5ce1feda777d070413bf9a3148b6929cb8c3fd03..a920948fb11e4d9d41c97bf296246eedbc8a06b2 100644 (file)
@@ -378,7 +378,7 @@ public:
   }
 
   void expect_init_layout(MockRefreshImageCtx &mock_image_ctx) {
-    EXPECT_CALL(mock_image_ctx, init_layout());
+    EXPECT_CALL(mock_image_ctx, init_layout(_));
   }
 
   void expect_test_features(MockRefreshImageCtx &mock_image_ctx) {
index 4c45011d3ce54aa620f09c107ff2103f2802fecb..5c0db6a8fc164c9968e0a4c6216bc577298b484d 100644 (file)
@@ -139,7 +139,7 @@ struct MockImageCtx {
     ctx.wait();
   }
 
-  MOCK_METHOD0(init_layout, void());
+  MOCK_METHOD1(init_layout, void(int64_t));
 
   MOCK_CONST_METHOD1(get_object_name, std::string(uint64_t));
   MOCK_CONST_METHOD0(get_object_size, uint64_t());
index e95be0b8a9f20504cfe8820e4975173b80876720..0caf9ffff431de59f49c353015f4e60937dbe35b 100644 (file)
@@ -1683,3 +1683,72 @@ TEST_F(TestInternal, SparsifyClone) {
   ASSERT_EQ(0, ictx->data_ctx.stat(oid, &size, NULL));
   ASSERT_EQ(0, ictx->data_ctx.read(oid, read_bl, 4096, 0));
 }
+
+TEST_F(TestInternal, MissingDataPool) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+  std::string header_oid = ictx->header_oid;
+  close_image(ictx);
+
+  // emulate non-existent data pool
+  int64_t pool_id = 1234;
+  std::string pool_name;
+  int r;
+  while ((r = _rados.pool_reverse_lookup(pool_id, &pool_name)) == 0) {
+    pool_id++;
+  }
+  ASSERT_EQ(r, -ENOENT);
+  bufferlist bl;
+  using ceph::encode;
+  encode(pool_id, bl);
+  ASSERT_EQ(0, m_ioctx.omap_set(header_oid, {{"data_pool_id", bl}}));
+
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  ASSERT_FALSE(ictx->data_ctx.is_valid());
+  ASSERT_EQ(pool_id, librbd::api::Image<>::get_data_pool_id(ictx));
+
+  librbd::image_info_t info;
+  ASSERT_EQ(0, librbd::info(ictx, info, sizeof(info)));
+
+  vector<librbd::snap_info_t> snaps;
+  EXPECT_EQ(0, librbd::snap_list(ictx, snaps));
+  EXPECT_EQ(1U, snaps.size());
+  EXPECT_EQ("snap1", snaps[0].name);
+
+  bufferptr read_ptr(256);
+  bufferlist read_bl;
+  read_bl.push_back(read_ptr);
+  librbd::io::ReadResult read_result{&read_bl};
+  ASSERT_EQ(-ENODEV,
+            ictx->io_work_queue->read(0, 256,
+                                      librbd::io::ReadResult{read_result}, 0));
+  ASSERT_EQ(-ENODEV,
+            ictx->io_work_queue->write(0, bl.length(), bufferlist{bl}, 0));
+  ASSERT_EQ(-ENODEV, ictx->io_work_queue->discard(0, 1, 256));
+  ASSERT_EQ(-ENODEV,
+            ictx->io_work_queue->writesame(0, bl.length(), bufferlist{bl}, 0));
+  uint64_t mismatch_off;
+  ASSERT_EQ(-ENODEV,
+            ictx->io_work_queue->compare_and_write(0, bl.length(),
+                                                   bufferlist{bl},
+                                                   bufferlist{bl},
+                                                   &mismatch_off, 0));
+  ASSERT_EQ(-ENODEV, ictx->io_work_queue->flush());
+
+  ASSERT_EQ(-ENODEV, snap_create(*ictx, "snap2"));
+  ASSERT_EQ(0, ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
+                                             "snap1"));
+
+  librbd::NoOpProgressContext no_op;
+  ASSERT_EQ(-ENODEV, ictx->operations->resize(0, true, no_op));
+
+  close_image(ictx);
+
+  ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, m_image_name, no_op));
+
+  ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, m_image_name, m_image_size));
+}