]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: move shared common clone parent logic to state machine
authorJason Dillaman <dillaman@redhat.com>
Tue, 17 Apr 2018 20:22:06 +0000 (16:22 -0400)
committerJason Dillaman <dillaman@redhat.com>
Tue, 14 Aug 2018 22:29:45 +0000 (18:29 -0400)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/librbd/api/Image.cc
src/librbd/image/CloneRequest.cc
src/librbd/image/CloneRequest.h
src/librbd/internal.cc
src/librbd/internal.h
src/librbd/librbd.cc
src/test/librbd/image/test_mock_CloneRequest.cc
src/test/rbd_mirror/image_replayer/test_mock_CreateImageRequest.cc
src/test/rbd_mirror/test_ImageDeleter.cc
src/tools/rbd_mirror/image_replayer/CreateImageRequest.cc
src/tools/rbd_mirror/image_replayer/CreateImageRequest.h

index a295b59d901d56ff07fe6d2476a6753a264d742d..877027cdef03e40a4786a85c6cb9af2f23eda779 100644 (file)
@@ -248,38 +248,13 @@ int Image<I>::deep_copy(I *src, librados::IoCtx& dest_md_ctx,
     // TODO support clone v2 parent namespaces
     parent_io_ctx.set_namespace(dest_md_ctx.get_namespace());
 
-    ImageCtx *src_parent_image_ctx =
-      new ImageCtx("", parent_spec.image_id, nullptr, parent_io_ctx, false);
-    r = src_parent_image_ctx->state->open(true);
-    if (r < 0) {
-      if (r != -ENOENT) {
-        lderr(cct) << "failed to open source parent image: "
-                   << cpp_strerror(r) << dendl;
-      }
-      return r;
-    }
-
-    C_SaferCond cond;
-    src_parent_image_ctx->state->snap_set(parent_spec.snap_id, &cond);
-    r = cond.wait();
-    if (r < 0) {
-      if (r != -ENOENT) {
-        lderr(cct) << "failed to set snapshot: " << cpp_strerror(r) << dendl;
-      }
-      return r;
-    }
-
     C_SaferCond ctx;
     std::string dest_id = util::generate_image_id(dest_md_ctx);
     auto *req = image::CloneRequest<I>::create(
-      src_parent_image_ctx, dest_md_ctx, destname, dest_id, opts,
-      "", "", src->op_work_queue, &ctx);
+      parent_io_ctx, parent_spec.image_id, "", parent_spec.snap_id, dest_md_ctx,
+      destname, dest_id, opts, "", "", src->op_work_queue, &ctx);
     req->send();
     r = ctx.wait();
-    int close_r = src_parent_image_ctx->state->close();
-    if (r == 0 && close_r < 0) {
-      r = close_r;
-    }
   }
   if (r < 0) {
     lderr(cct) << "header creation failed" << dendl;
index 71b4951a0919560e5c9d3c6164c2f634d4fdbab5..18dbbcd42e46dd17fb9a5b65504688b348e4659b 100644 (file)
@@ -28,16 +28,20 @@ using util::create_context_callback;
 using util::create_async_context_callback;
 
 template <typename I>
-CloneRequest<I>::CloneRequest(I *p_imctx, IoCtx &c_ioctx,
+CloneRequest<I>::CloneRequest(IoCtx& parent_io_ctx,
+                              const std::string& parent_image_id,
+                              const std::string& parent_snap_name,
+                              uint64_t parent_snap_id,
+                              IoCtx &c_ioctx,
                              const std::string &c_name,
                              const std::string &c_id,
                              ImageOptions c_options,
                              const std::string &non_primary_global_image_id,
                              const std::string &primary_mirror_uuid,
                              ContextWQ *op_work_queue, Context *on_finish)
-  : m_p_imctx(p_imctx), m_ioctx(c_ioctx), m_name(c_name), m_id(c_id),
-    m_opts(c_options),
-    m_pspec(m_p_imctx->md_ctx.get_id(), m_p_imctx->id, m_p_imctx->snap_id),
+  : m_parent_io_ctx(parent_io_ctx), m_parent_image_id(parent_image_id),
+    m_parent_snap_name(parent_snap_name), m_parent_snap_id(parent_snap_id),
+    m_ioctx(c_ioctx), m_name(c_name), m_id(c_id), m_opts(c_options),
     m_non_primary_global_image_id(non_primary_global_image_id),
     m_primary_mirror_uuid(primary_mirror_uuid),
     m_op_work_queue(op_work_queue), m_on_finish(on_finish),
@@ -51,10 +55,13 @@ CloneRequest<I>::CloneRequest(I *p_imctx, IoCtx &c_ioctx,
     m_opts.set(RBD_IMAGE_OPTION_FORMAT, static_cast<uint64_t>(2));
   }
 
-  ldout(m_cct, 20) << "clone " << &m_p_imctx->md_ctx << " name " << m_p_imctx->name
-                << " snap " << m_p_imctx->snap_name << " to child " << &m_ioctx
-                << " name " << m_name << " opts = " << &m_opts << dendl;
-  return;
+  ldout(m_cct, 20) << "parent_pool_id=" << parent_io_ctx.get_id() << ", "
+                   << "parent_image_id=" << parent_image_id << ", "
+                  << "parent_snap=" << parent_snap_name << "/"
+                   << parent_snap_id << " clone to "
+                   << "pool_id=" << m_ioctx.get_id() << ", "
+                   << "name=" << m_name << ", "
+                   << "opts=" << m_opts << dendl;
 }
 
 template <typename I>
@@ -104,6 +111,80 @@ void CloneRequest<I>::validate_options() {
     }
   }
 
+  open_parent();
+}
+
+template <typename I>
+void CloneRequest<I>::open_parent() {
+  ldout(m_cct, 20) << dendl;
+
+  m_parent_image_ctx = I::create("", m_parent_image_id, "", m_parent_io_ctx, true);
+
+  Context *ctx = create_context_callback<
+    CloneRequest<I>, &CloneRequest<I>::handle_open_parent>(this);
+  m_parent_image_ctx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT, ctx);
+}
+
+template <typename I>
+void CloneRequest<I>::handle_open_parent(int r) {
+  ldout(m_cct, 20) << "r=" << r << dendl;
+
+  if (r < 0) {
+    m_parent_image_ctx->destroy();
+    m_parent_image_ctx = nullptr;
+
+    lderr(m_cct) << "failed to open parent image: " << cpp_strerror(r) << dendl;
+    complete(r);
+    return;
+  }
+
+  set_parent_snap();
+}
+
+template <typename I>
+void CloneRequest<I>::set_parent_snap() {
+  assert((m_parent_snap_name.empty()) ^ (m_parent_snap_id == CEPH_NOSNAP));
+
+  if (m_parent_snap_id == CEPH_NOSNAP) {
+    // look up user snapshot by name
+    m_parent_image_ctx->snap_lock.get_read();
+    auto it = m_parent_image_ctx->snap_ids.find(
+      {cls::rbd::UserSnapshotNamespace{}, m_parent_snap_name});
+    if (it == m_parent_image_ctx->snap_ids.end()) {
+      m_parent_image_ctx->snap_lock.put_read();
+
+      lderr(m_cct) << "failed to located parent snapshot: " <<
+                   m_parent_snap_name << dendl;
+      m_r_saved = -ENOENT;
+      close_parent();
+      return;
+    }
+
+    m_parent_snap_id = it->second;
+    m_parent_image_ctx->snap_lock.put_read();
+  }
+
+  ldout(m_cct, 20) << "parent_snap_id=" << m_parent_snap_id << dendl;
+
+  Context *ctx = create_context_callback<
+    CloneRequest<I>, &CloneRequest<I>::handle_set_parent_snap>(this);
+  m_parent_image_ctx->state->snap_set(m_parent_snap_id, ctx);
+}
+
+template <typename I>
+void CloneRequest<I>::handle_set_parent_snap(int r) {
+  ldout(m_cct, 20) << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(m_cct) << "failed to set parent snapshot: " << cpp_strerror(r)
+                 << dendl;
+
+    m_r_saved = r;
+    close_parent();
+    return;
+  }
+
+  m_pspec = {m_parent_io_ctx.get_id(), m_parent_image_id, m_parent_snap_id};
   validate_parent();
 }
 
@@ -111,48 +192,57 @@ template <typename I>
 void CloneRequest<I>::validate_parent() {
   ldout(m_cct, 20) << dendl;
 
-  if (m_p_imctx->operations_disabled) {
+  if (m_parent_image_ctx->operations_disabled) {
     lderr(m_cct) << "image operations disabled due to unsupported op features"
                  << dendl;
-    complete(-EROFS);
+    m_r_saved = -EROFS;
+    close_parent();
     return;
   }
 
-  if (m_p_imctx->snap_id == CEPH_NOSNAP) {
+  if (m_parent_image_ctx->snap_id == CEPH_NOSNAP) {
     lderr(m_cct) << "image to be cloned must be a snapshot" << dendl;
-    complete(-EINVAL);
+    m_r_saved = -EINVAL;
+    close_parent();
     return;
   }
 
-  if (m_p_imctx->old_format) {
+  if (m_parent_image_ctx->old_format) {
     lderr(m_cct) << "parent image must be in new format" << dendl;
-    complete(-EINVAL);
+    m_r_saved = -EINVAL;
+    close_parent();
     return;
   }
 
-  int r = 0;
+  m_parent_image_ctx->snap_lock.get_read();
+  uint64_t p_features = m_parent_image_ctx->features;
+  m_size = m_parent_image_ctx->get_image_size(m_parent_image_ctx->snap_id);
+
   bool snap_protected;
-  m_p_imctx->snap_lock.get_read();
-  m_p_features = m_p_imctx->features;
-  m_size = m_p_imctx->get_image_size(m_p_imctx->snap_id);
-  r = m_p_imctx->is_snap_protected(m_p_imctx->snap_id, &snap_protected);
-  m_p_imctx->snap_lock.put_read();
+  int r = m_parent_image_ctx->is_snap_protected(m_parent_image_ctx->snap_id, &snap_protected);
+  m_parent_image_ctx->snap_lock.put_read();
 
-  if ((m_p_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
+  if ((p_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
     lderr(m_cct) << "parent image must support layering" << dendl;
-    complete(-ENOSYS);
+    m_r_saved = -ENOSYS;
+    close_parent();
     return;
   }
+  if (m_use_p_features) {
+    m_features = (p_features & ~RBD_FEATURES_IMPLICIT_ENABLE);
+  }
 
   if (r < 0) {
     lderr(m_cct) << "unable to locate parent's snapshot" << dendl;
-    complete(r);
+    m_r_saved = r;
+    close_parent();
     return;
   }
 
   if (m_clone_format == 1 && !snap_protected) {
     lderr(m_cct) << "parent snapshot must be protected" << dendl;
-    complete(-EINVAL);
+    m_r_saved = -EINVAL;
+    close_parent();
     return;
   }
 
@@ -163,6 +253,13 @@ template <typename I>
 void CloneRequest<I>::validate_child() {
   ldout(m_cct, 20) << dendl;
 
+  if ((m_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
+    lderr(m_cct) << "cloning image must support layering" << dendl;
+    m_r_saved = -ENOSYS;
+    close_parent();
+    return;
+  }
+
   using klass = CloneRequest<I>;
   librados::AioCompletion *comp = create_rados_callback<
     klass, &klass::handle_validate_child>(this);
@@ -182,7 +279,8 @@ void CloneRequest<I>::handle_validate_child(int r) {
 
   if (r != -ENOENT) {
     lderr(m_cct) << "rbd image " << m_name << " already exists" << dendl;
-    complete(r);
+    m_r_saved = r;
+    close_parent();
     return;
   }
 
@@ -193,26 +291,17 @@ template <typename I>
 void CloneRequest<I>::create_child() {
   ldout(m_cct, 20) << dendl;
 
-  if (m_use_p_features) {
-    m_features = (m_p_features & ~RBD_FEATURES_IMPLICIT_ENABLE);
-  }
-
-  uint64_t order = m_p_imctx->order;
+  uint64_t order = m_parent_image_ctx->order;
   if (m_opts.get(RBD_IMAGE_OPTION_ORDER, &order) != 0) {
     m_opts.set(RBD_IMAGE_OPTION_ORDER, order);
   }
-  if ((m_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
-    lderr(m_cct) << "cloning image must support layering" << dendl;
-    complete(-ENOSYS);
-    return;
-  }
   m_opts.set(RBD_IMAGE_OPTION_FEATURES, m_features);
 
   using klass = CloneRequest<I>;
   Context *ctx = create_context_callback<
     klass, &klass::handle_create_child>(this);
 
-  RWLock::RLocker snap_locker(m_p_imctx->snap_lock);
+  RWLock::RLocker snap_locker(m_parent_image_ctx->snap_lock);
   CreateRequest<I> *req = CreateRequest<I>::create(
     m_ioctx, m_name, m_id, m_size, m_opts, m_non_primary_global_image_id,
     m_primary_mirror_uuid, true, m_op_work_queue, ctx);
@@ -225,7 +314,8 @@ void CloneRequest<I>::handle_create_child(int r) {
 
   if (r < 0) {
     lderr(m_cct) << "error creating child: " << cpp_strerror(r) << dendl;
-    complete(r);
+    m_r_saved = r;
+    close_parent();
     return;
   }
   open_child();
@@ -254,6 +344,9 @@ void CloneRequest<I>::handle_open_child(int r) {
   ldout(m_cct, 20) << "r=" << r << dendl;
 
   if (r < 0) {
+    m_imctx->destroy();
+    m_imctx = nullptr;
+
     lderr(m_cct) << "Error opening new image: " << cpp_strerror(r) << dendl;
     m_r_saved = r;
     remove_child();
@@ -331,12 +424,12 @@ void CloneRequest<I>::v2_child_attach() {
   ldout(m_cct, 20) << dendl;
 
   librados::ObjectWriteOperation op;
-  cls_client::child_attach(&op, m_p_imctx->snap_id,
+  cls_client::child_attach(&op, m_parent_image_ctx->snap_id,
                            {m_imctx->md_ctx.get_id(), m_imctx->id});
 
   auto aio_comp = create_rados_callback<
     CloneRequest<I>, &CloneRequest<I>::handle_v2_child_attach>(this);
-  int r = m_p_imctx->md_ctx.aio_operate(m_p_imctx->header_oid, aio_comp, &op);
+  int r = m_parent_image_ctx->md_ctx.aio_operate(m_parent_image_ctx->header_oid, aio_comp, &op);
   assert(r == 0);
   aio_comp->release();
 }
@@ -402,9 +495,9 @@ void CloneRequest<I>::handle_v1_refresh(int r) {
 
   bool snap_protected = false;
   if (r == 0) {
-    m_p_imctx->snap_lock.get_read();
-    r = m_p_imctx->is_snap_protected(m_p_imctx->snap_id, &snap_protected);
-    m_p_imctx->snap_lock.put_read();
+    m_parent_image_ctx->snap_lock.get_read();
+    r = m_parent_image_ctx->is_snap_protected(m_parent_image_ctx->snap_id, &snap_protected);
+    m_parent_image_ctx->snap_lock.put_read();
   }
 
   if (r < 0 || !snap_protected) {
@@ -428,7 +521,7 @@ void CloneRequest<I>::metadata_list() {
   librados::AioCompletion *comp =
     create_rados_callback<klass, &klass::handle_metadata_list>(this);
   m_out_bl.clear();
-  m_p_imctx->md_ctx.aio_operate(m_p_imctx->header_oid,
+  m_parent_image_ctx->md_ctx.aio_operate(m_parent_image_ctx->header_oid,
                                comp, &op, &m_out_bl);
   comp->release();
 }
@@ -594,15 +687,17 @@ void CloneRequest<I>::handle_close_child(int r) {
 
   if (r < 0) {
     lderr(m_cct) << "couldn't close image: " << cpp_strerror(r) << dendl;
-    complete(r);
-    return;
+    if (m_r_saved == 0) {
+      m_r_saved = r;
+    }
   }
 
-  if (m_r_saved == 0) {
-    complete(0);
-  } else {
+  if (m_r_saved < 0) {
     remove_child();
+    return;
   }
+
+  close_parent();
 }
 
 template <typename I>
@@ -626,6 +721,36 @@ void CloneRequest<I>::handle_remove_child(int r) {
     lderr(m_cct) << "Error removing failed clone: "
                 << cpp_strerror(r) << dendl;
   }
+
+  close_parent();
+}
+
+template <typename I>
+void CloneRequest<I>::close_parent() {
+  ldout(m_cct, 20) << dendl;
+  assert(m_parent_image_ctx != nullptr);
+
+  Context *ctx = create_async_context_callback(
+    *m_parent_image_ctx, create_context_callback<
+      CloneRequest<I>, &CloneRequest<I>::handle_close_parent>(this));
+  m_parent_image_ctx->state->close(ctx);
+}
+
+template <typename I>
+void CloneRequest<I>::handle_close_parent(int r) {
+  ldout(m_cct, 20) << "r=" << r << dendl;
+
+  m_parent_image_ctx->destroy();
+  m_parent_image_ctx = nullptr;
+
+  if (r < 0) {
+    lderr(m_cct) << "failed to close parent image: "
+                << cpp_strerror(r) << dendl;
+    if (m_r_saved == 0) {
+      m_r_saved = r;
+    }
+  }
+
   complete(m_r_saved);
 }
 
index 45ac351a7f0c6cb3db972749980fcc96113520f2..d98ebc4f6487facb3d5e6b5fcab6c1a50f89b2c7 100644 (file)
@@ -18,24 +18,33 @@ namespace image {
 template <typename ImageCtxT = ImageCtx>
 class CloneRequest {
 public:
-  static CloneRequest *create(ImageCtxT *p_imctx, IoCtx &c_ioctx,
-                              const std::string &c_name,
+  static CloneRequest *create(IoCtx& parent_io_ctx,
+                              const std::string& parent_image_id,
+                              const std::string& parent_snap_name,
+                              uint64_t parent_snap_id,
+                              IoCtx &c_ioctx, const std::string &c_name,
                               const std::string &c_id, ImageOptions c_options,
                              const std::string &non_primary_global_image_id,
                              const std::string &primary_mirror_uuid,
                              ContextWQ *op_work_queue, Context *on_finish) {
-    return new CloneRequest(p_imctx, c_ioctx, c_name, c_id, c_options,
+    return new CloneRequest(parent_io_ctx, parent_image_id, parent_snap_name,
+                            parent_snap_id, c_ioctx, c_name, c_id, c_options,
                             non_primary_global_image_id, primary_mirror_uuid,
                             op_work_queue, on_finish);
   }
 
-  CloneRequest(ImageCtxT *p_imctx, IoCtx &c_ioctx, const std::string &c_name,
+  CloneRequest(IoCtx& parent_io_ctx,
+               const std::string& parent_image_id,
+               const std::string& parent_snap_name,
+               uint64_t parent_snap_id,
+               IoCtx &c_ioctx, const std::string &c_name,
                const std::string &c_id, ImageOptions c_options,
                const std::string &non_primary_global_image_id,
                const std::string &primary_mirror_uuid,
                ContextWQ *op_work_queue, Context *on_finish);
 
   void send();
+
 private:
   /**
    * @verbatim
@@ -43,16 +52,22 @@ private:
    * <start>
    *    |
    *    v
-   * VALIDATE CHILD
-   *    |
-   *    v
-   * CREATE CHILD                     <finish>
+   * OPEN PARENT                       <finish>
    *    |                                 ^
    *    v                                 |
+   * SET PARENT SNAP  * * * * * * * > CLOSE PARENT
+   *    |                       *         ^
+   *    v                       *         |
+   * VALIDATE CHILD             *         |
+   *    |                       *         |
+   *    v                       *         |
+   * CREATE CHILD * * * * * * * *         |
+   *    |                                 |
+   *    v                                 |
    * OPEN CHILD * * * * * * * * * * > REMOVE CHILD
    *    |                                 ^
    *    v                                 |
-   * SET PARENT * * * * * * * * * * > CLOSE IMAGE
+   * SET PARENT * * * * * * * * * * > CLOSE CHILD
    *    |                               ^
    *    |\--------\                     *
    *    |         |                     *
@@ -81,7 +96,10 @@ private:
    * SET MIRROR ENABLED * * * * * * * * *
    *    |
    *    v
-   * CLOSE IMAGE
+   * CLOSE CHILD
+   *    |
+   *    v
+   * CLOSE PARENT
    *    |
    *    v
    * <finish>
@@ -89,7 +107,12 @@ private:
    * @endverbatim
    */
 
-  ImageCtxT *m_p_imctx;
+  IoCtx &m_parent_io_ctx;
+  std::string m_parent_image_id;
+  std::string m_parent_snap_name;
+  uint64_t m_parent_snap_id;
+  ImageCtxT *m_parent_image_ctx;
+
   IoCtx &m_ioctx;
   std::string m_name;
   std::string m_id;
@@ -106,7 +129,6 @@ private:
   CephContext *m_cct;
   uint32_t m_clone_format = 2;
   bool m_use_p_features;
-  uint64_t m_p_features;
   uint64_t m_features;
   map<string, bufferlist> m_pairs;
   std::string m_last_metadata_key;
@@ -115,6 +137,13 @@ private:
   int m_r_saved = 0;
 
   void validate_options();
+
+  void open_parent();
+  void handle_open_parent(int r);
+
+  void set_parent_snap();
+  void handle_set_parent_snap(int r);
+
   void validate_parent();
 
   void validate_child();
@@ -159,6 +188,9 @@ private:
   void remove_child();
   void handle_remove_child(int r);
 
+  void close_parent();
+  void handle_close_parent(int r);
+
   void complete(int r);
 };
 
index 8cca5a0c5a3344710dff3b67b240c0ed281921c3..97ba292907e4b473d826b488d0e6c664cb1776b0 100644 (file)
@@ -941,17 +941,23 @@ bool compare_by_name(const child_info_t& c1, const child_info_t& c2)
     opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit);
     opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count);
 
-    int r = clone(p_ioctx, p_name, p_snap_name, c_ioctx, c_name, opts);
+    int r = clone(p_ioctx, nullptr, p_name, p_snap_name, c_ioctx, nullptr,
+                  c_name, opts, "", "");
     opts.get(RBD_IMAGE_OPTION_ORDER, &order);
     *c_order = order;
     return r;
   }
 
-  int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snap_name,
-           IoCtx& c_ioctx, const char *c_name, ImageOptions& c_opts)
+  int clone(IoCtx& p_ioctx, const char *p_id, const char *p_name,
+            const char *p_snap_name, IoCtx& c_ioctx, const char *c_id,
+            const char *c_name, ImageOptions& c_opts,
+            const std::string &non_primary_global_image_id,
+            const std::string &primary_mirror_uuid)
   {
+    assert((p_id == nullptr) ^ (p_name == nullptr));
+
     CephContext *cct = (CephContext *)p_ioctx.cct();
-    if (p_snap_name == NULL) {
+    if (p_snap_name == nullptr) {
       lderr(cct) << "image to be cloned must be a snapshot" << dendl;
       return -EINVAL;
     }
@@ -962,42 +968,32 @@ bool compare_by_name(const child_info_t& c1, const child_info_t& c2)
       return -EINVAL;
     }
 
-    // make sure parent snapshot exists
-    ImageCtx *p_imctx = new ImageCtx(p_name, "", p_snap_name, p_ioctx, true);
-    int r = p_imctx->state->open(0);
-    if (r < 0) {
-      lderr(cct) << "error opening parent image: "
-                << cpp_strerror(r) << dendl;
-      return r;
-    }
-
-    r = clone(p_imctx, c_ioctx, c_name, "", c_opts, "", "");
-
-    int close_r = p_imctx->state->close();
-    if (r == 0 && close_r < 0) {
-      r = close_r;
-    }
-
-    if (r < 0) {
-      return r;
+    int r;
+    std::string parent_id;
+    if (p_id == nullptr) {
+      r = cls_client::dir_get_id(&p_ioctx, RBD_DIRECTORY, p_name,
+                                 &parent_id);
+      if (r < 0) {
+        if (r != -ENOENT) {
+          lderr(cct) << "failed to retrieve parent image id: "
+                     << cpp_strerror(r) << dendl;
+        }
+        return r;
+      }
+    } else {
+      parent_id = p_id;
     }
-    return 0;
-  }
 
-  int clone(ImageCtx *p_imctx, IoCtx& c_ioctx, const std::string &c_name,
-            const std::string &c_id, ImageOptions& c_opts,
-            const std::string &non_primary_global_image_id,
-            const std::string &primary_mirror_uuid)
-  {
-    std::string id(c_id);
-    if (id.empty()) {
-      id = util::generate_image_id(c_ioctx);
+    std::string clone_id;
+    if (c_id == nullptr) {
+      clone_id = util::generate_image_id(c_ioctx);
+    } else {
+      clone_id = c_id;
     }
 
-    CephContext *cct = (CephContext *)c_ioctx.cct();
     ldout(cct, 10) << __func__ << " "
                   << "c_name=" << c_name << ", "
-                  << "c_id= " << c_id << ", "
+                  << "c_id= " << clone_id << ", "
                   << "c_opts=" << c_opts << dendl;
 
     ThreadPool *thread_pool;
@@ -1006,11 +1002,17 @@ bool compare_by_name(const child_info_t& c1, const child_info_t& c2)
 
     C_SaferCond cond;
     auto *req = image::CloneRequest<>::create(
-      p_imctx, c_ioctx, c_name, id, c_opts,
-      non_primary_global_image_id, primary_mirror_uuid, op_work_queue, &cond);
+      p_ioctx, parent_id, p_snap_name, CEPH_NOSNAP, c_ioctx, c_name, clone_id,
+      c_opts, non_primary_global_image_id, primary_mirror_uuid, op_work_queue,
+      &cond);
     req->send();
 
-    return cond.wait();
+    r = cond.wait();
+    if (r < 0) {
+      return r;
+    }
+
+    return 0;
   }
 
   int rename(IoCtx& io_ctx, const char *srcname, const char *dstname)
index 3271d902cb0e9543efa09e2ef0d2670367024108..889eff3b3aa417834c65d529960cbcae770db609 100644 (file)
@@ -77,10 +77,9 @@ namespace librbd {
            IoCtx& c_ioctx, const char *c_name,
            uint64_t features, int *c_order,
            uint64_t stripe_unit, int stripe_count);
-  int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snap_name,
-           IoCtx& c_ioctx, const char *c_name, ImageOptions& c_opts);
-  int clone(ImageCtx *p_imctx, IoCtx& c_ioctx, const std::string &c_name,
-            const std::string &c_id, ImageOptions& c_opts,
+  int clone(IoCtx& p_ioctx, const char *p_id, const char *p_name,
+            const char *p_snap_name, IoCtx& c_ioctx, const char *c_id,
+            const char *c_name, ImageOptions& c_opts,
             const std::string &non_primary_global_image_id,
             const std::string &primary_mirror_uuid);
   int rename(librados::IoCtx& io_ctx, const char *srcname, const char *dstname);
index 4b21ec5fe3cd9c7c955abc043b2d97d7d99a64b3..eaf705fa6097fb2a640540081a7cb2a176ef35bf 100644 (file)
@@ -532,8 +532,8 @@ namespace librbd {
   {
     TracepointProvider::initialize<tracepoint_traits>(get_cct(p_ioctx));
     tracepoint(librbd, clone3_enter, p_ioctx.get_pool_name().c_str(), p_ioctx.get_id(), p_name, p_snap_name, c_ioctx.get_pool_name().c_str(), c_ioctx.get_id(), c_name, c_opts.opts);
-    int r = librbd::clone(p_ioctx, p_name, p_snap_name, c_ioctx, c_name,
-                         c_opts);
+    int r = librbd::clone(p_ioctx, nullptr, p_name, p_snap_name, c_ioctx,
+                          nullptr, c_name, c_opts, "", "");
     tracepoint(librbd, clone3_exit, r);
     return r;
   }
@@ -2671,7 +2671,8 @@ extern "C" int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name,
   TracepointProvider::initialize<tracepoint_traits>(get_cct(p_ioc));
   tracepoint(librbd, clone3_enter, p_ioc.get_pool_name().c_str(), p_ioc.get_id(), p_name, p_snap_name, c_ioc.get_pool_name().c_str(), c_ioc.get_id(), c_name, c_opts);
   librbd::ImageOptions c_opts_(c_opts);
-  int r = librbd::clone(p_ioc, p_name, p_snap_name, c_ioc, c_name, c_opts_);
+  int r = librbd::clone(p_ioc, nullptr, p_name, p_snap_name, c_ioc, nullptr,
+                        c_name, c_opts_, "", "");
   tracepoint(librbd, clone3_exit, r);
   return r;
 }
index 16fc92ab7ca7e4417614a47e02245e910c72a628..f149008ebbb5f88052c9d20768b92caa9578876a 100644 (file)
@@ -224,6 +224,17 @@ public:
       .WillOnce(WithArg<1>(Invoke([this, r](Context* ctx) {
                              image_ctx->op_work_queue->queue(ctx, r);
                            })));
+    if (r < 0) {
+      EXPECT_CALL(mock_image_ctx, destroy());
+    }
+  }
+
+  void expect_snap_set(librbd::MockTestImageCtx &mock_image_ctx,
+                       uint64_t snap_id, int r) {
+    EXPECT_CALL(*mock_image_ctx.state, snap_set(snap_id, _))
+      .WillOnce(WithArg<1>(Invoke([this, r](Context *on_finish) {
+          image_ctx->op_work_queue->queue(on_finish, r);
+        })));
   }
 
   void expect_set_parent(MockImageCtx &mock_image_ctx, int r) {
@@ -352,6 +363,9 @@ TEST_F(TestMockImageCloneRequest, SuccessV1) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -379,12 +393,13 @@ TEST_F(TestMockImageCloneRequest, SuccessV1) {
     expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
   }
 
+  expect_close(mock_image_ctx, 0);
   expect_close(mock_image_ctx, 0);
 
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
@@ -397,6 +412,9 @@ TEST_F(TestMockImageCloneRequest, SuccessV2) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -422,12 +440,13 @@ TEST_F(TestMockImageCloneRequest, SuccessV2) {
     expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
   }
 
+  expect_close(mock_image_ctx, 0);
   expect_close(mock_image_ctx, 0);
 
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
@@ -442,6 +461,9 @@ TEST_F(TestMockImageCloneRequest, SuccessAuto) {
 
   InSequence seq;
   expect_get_min_compat_client(1, 0);
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -469,17 +491,56 @@ TEST_F(TestMockImageCloneRequest, SuccessAuto) {
     expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
   }
 
+  expect_close(mock_image_ctx, 0);
   expect_close(mock_image_ctx, 0);
 
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
 
+TEST_F(TestMockImageCloneRequest, OpenParentError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_open(mock_image_ctx, -EINVAL);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, SetParentSnapError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, -EINVAL);
+  expect_close(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
 TEST_F(TestMockImageCloneRequest, CreateError) {
   REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
 
@@ -487,16 +548,21 @@ TEST_F(TestMockImageCloneRequest, CreateError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
   MockCreateRequest mock_create_request;
   expect_create(mock_create_request, -EINVAL);
 
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -509,6 +575,9 @@ TEST_F(TestMockImageCloneRequest, OpenError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -520,10 +589,12 @@ TEST_F(TestMockImageCloneRequest, OpenError) {
   MockRemoveRequest mock_remove_request;
   expect_remove(mock_remove_request, 0);
 
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -536,6 +607,9 @@ TEST_F(TestMockImageCloneRequest, SetParentError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -549,10 +623,12 @@ TEST_F(TestMockImageCloneRequest, SetParentError) {
   MockRemoveRequest mock_remove_request;
   expect_remove(mock_remove_request, 0);
 
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -566,6 +642,9 @@ TEST_F(TestMockImageCloneRequest, AddChildError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -580,10 +659,12 @@ TEST_F(TestMockImageCloneRequest, AddChildError) {
   MockRemoveRequest mock_remove_request;
   expect_remove(mock_remove_request, 0);
 
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -597,6 +678,9 @@ TEST_F(TestMockImageCloneRequest, RefreshError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -615,10 +699,12 @@ TEST_F(TestMockImageCloneRequest, RefreshError) {
   MockRemoveRequest mock_remove_request;
   expect_remove(mock_remove_request, 0);
 
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -632,6 +718,9 @@ TEST_F(TestMockImageCloneRequest, MetadataListError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -653,10 +742,12 @@ TEST_F(TestMockImageCloneRequest, MetadataListError) {
   MockRemoveRequest mock_remove_request;
   expect_remove(mock_remove_request, 0);
 
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -669,6 +760,9 @@ TEST_F(TestMockImageCloneRequest, MetadataSetError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -689,10 +783,12 @@ TEST_F(TestMockImageCloneRequest, MetadataSetError) {
   MockRemoveRequest mock_remove_request;
   expect_remove(mock_remove_request, 0);
 
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -706,6 +802,9 @@ TEST_F(TestMockImageCloneRequest, GetMirrorModeError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -730,10 +829,12 @@ TEST_F(TestMockImageCloneRequest, GetMirrorModeError) {
   MockRemoveRequest mock_remove_request;
   expect_remove(mock_remove_request, 0);
 
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -746,6 +847,9 @@ TEST_F(TestMockImageCloneRequest, MirrorEnableError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -771,10 +875,12 @@ TEST_F(TestMockImageCloneRequest, MirrorEnableError) {
   MockRemoveRequest mock_remove_request;
   expect_remove(mock_remove_request, 0);
 
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -787,6 +893,9 @@ TEST_F(TestMockImageCloneRequest, CloseError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -804,10 +913,15 @@ TEST_F(TestMockImageCloneRequest, CloseError) {
 
   expect_close(mock_image_ctx, -EINVAL);
 
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  expect_close(mock_image_ctx, 0);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
@@ -820,6 +934,9 @@ TEST_F(TestMockImageCloneRequest, RemoveError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -831,10 +948,44 @@ TEST_F(TestMockImageCloneRequest, RemoveError) {
   MockRemoveRequest mock_remove_request;
   expect_remove(mock_remove_request, -EPERM);
 
+  expect_close(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, CloseParentError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_open(mock_image_ctx, 0);
+  expect_snap_set(mock_image_ctx, 123, 0);
+
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, -EINVAL);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  expect_close(mock_image_ctx, -EPERM);
+
   C_SaferCond ctx;
   ImageOptions clone_opts;
-  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
-                                  "clone id", clone_opts, "", "",
+  auto req = new MockCloneRequest(m_ioctx, "parent id", "", 123, m_ioctx,
+                                  "clone name", "clone id", clone_opts, "", "",
                                   image_ctx->op_work_queue, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
index 3d2152ab9319dc9522e98084858fbd156d51ed53..a0715e6c352701d5f14d4188de31255ef984d657 100644 (file)
@@ -72,15 +72,18 @@ struct CloneRequest<librbd::MockTestImageCtx> {
   static CloneRequest *s_instance;
   Context *on_finish = nullptr;
 
-  static CloneRequest *create(librbd::MockTestImageCtx *p_imctx,
+  static CloneRequest *create(IoCtx &p_ioctx, const std::string &p_id,
+                              const std::string &p_snap_name,
+                              uint64_t p_snap_id,
                              IoCtx &c_ioctx, const std::string &c_name,
                              const std::string &c_id, ImageOptions c_options,
                              const std::string &non_primary_global_image_id,
                              const std::string &primary_mirror_uuid,
-                             MockContextWQ *op_work_queue, Context *on_finish) {
+                             MockContextWQ *op_work_queue,
+                              Context *on_finish) {
     assert(s_instance != nullptr);
     s_instance->on_finish = on_finish;
-    s_instance->construct(p_imctx);
+    s_instance->construct();
     return s_instance;
   }
 
@@ -93,7 +96,7 @@ struct CloneRequest<librbd::MockTestImageCtx> {
   }
 
   MOCK_METHOD0(send, void());
-  MOCK_METHOD1(construct, void(librbd::MockTestImageCtx *p_imctx));
+  MOCK_METHOD0(construct, void());
 };
 
 CloneRequest<librbd::MockTestImageCtx>*
@@ -281,22 +284,9 @@ public:
         }));
   }
 
-  void expect_snap_set(librbd::MockTestImageCtx &mock_image_ctx,
-                       const std::string &snap_name, int r) {
-    EXPECT_CALL(*mock_image_ctx.state, snap_set(_, _))
-      .WillOnce(Invoke([this, &mock_image_ctx, snap_name, r](uint64_t snap_id, Context *on_finish) {
-          RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
-          auto id = mock_image_ctx.image_ctx->get_snap_id(
-            cls::rbd::UserSnapshotNamespace{}, snap_name);
-          ASSERT_EQ(snap_id, id);
-          m_threads->work_queue->queue(on_finish, r);
-        }));
-  }
-
   void expect_clone_image(MockCloneRequest &mock_clone_request,
-                         librbd::MockTestImageCtx &mock_parent_imctx,
                           int r) {
-    EXPECT_CALL(mock_clone_request, construct(&mock_parent_imctx));
+    EXPECT_CALL(mock_clone_request, construct());
     EXPECT_CALL(mock_clone_request, send())
       .WillOnce(Invoke([this, &mock_clone_request, r]() {
             m_threads->work_queue->queue(mock_clone_request.on_finish, r);
@@ -372,7 +362,6 @@ TEST_F(TestMockImageReplayerCreateImageRequest, Clone) {
                &remote_clone_image_ctx));
 
   librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
-  librbd::MockTestImageCtx mock_local_parent_image_ctx(*local_image_ctx);
   librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
   MockCloneRequest mock_clone_request;
   MockOpenImageRequest mock_open_image_request;
@@ -386,11 +375,7 @@ TEST_F(TestMockImageReplayerCreateImageRequest, Clone) {
 
   expect_open_image(mock_open_image_request, m_remote_io_ctx,
                     m_remote_image_ctx->id, mock_remote_parent_image_ctx, 0);
-  expect_open_image(mock_open_image_request, m_local_io_ctx,
-                    "local parent id", mock_local_parent_image_ctx, 0);
-  expect_snap_set(mock_local_parent_image_ctx, "snap", 0);
-  expect_clone_image(mock_clone_request, mock_local_parent_image_ctx, 0);
-  expect_close_image(mock_close_image_request, mock_local_parent_image_ctx, 0);
+  expect_clone_image(mock_clone_request, 0);
   expect_close_image(mock_close_image_request, mock_remote_parent_image_ctx, 0);
 
   C_SaferCond ctx;
@@ -484,91 +469,6 @@ TEST_F(TestMockImageReplayerCreateImageRequest, CloneOpenRemoteParentError) {
   ASSERT_EQ(-ENOENT, ctx.wait());
 }
 
-TEST_F(TestMockImageReplayerCreateImageRequest, CloneOpenLocalParentError) {
-  librbd::RBD rbd;
-  librbd::ImageCtx *local_image_ctx;
-  ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
-  ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &local_image_ctx));
-
-  std::string clone_image_name = get_temp_image_name();
-  ASSERT_EQ(0, clone_image(m_remote_image_ctx, "snap", clone_image_name));
-
-  librbd::ImageCtx *remote_clone_image_ctx;
-  ASSERT_EQ(0, open_image(m_remote_io_ctx, clone_image_name,
-               &remote_clone_image_ctx));
-
-  librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
-  librbd::MockTestImageCtx mock_local_parent_image_ctx(*local_image_ctx);
-  librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
-  MockCloneRequest mock_clone_request;
-  MockOpenImageRequest mock_open_image_request;
-  MockCloseImageRequest mock_close_image_request;
-
-  InSequence seq;
-  expect_ioctx_create(m_remote_io_ctx);
-  expect_ioctx_create(m_local_io_ctx);
-  expect_get_parent_global_image_id(m_remote_io_ctx, "global uuid", 0);
-  expect_mirror_image_get_image_id(m_local_io_ctx, "local parent id", 0);
-
-  expect_open_image(mock_open_image_request, m_remote_io_ctx,
-                    m_remote_image_ctx->id, mock_remote_parent_image_ctx, 0);
-  expect_open_image(mock_open_image_request, m_local_io_ctx,
-                    "local parent id", mock_local_parent_image_ctx, -ENOENT);
-  expect_close_image(mock_close_image_request, mock_remote_parent_image_ctx, 0);
-
-  C_SaferCond ctx;
-  MockCreateImageRequest *request = create_request("global uuid", "remote uuid",
-                                                   "image name", "101241a7c4c9",
-                                                   mock_remote_clone_image_ctx,
-                                                   &ctx);
-  request->send();
-  ASSERT_EQ(-ENOENT, ctx.wait());
-}
-
-TEST_F(TestMockImageReplayerCreateImageRequest, CloneSnapSetError) {
-  librbd::RBD rbd;
-  librbd::ImageCtx *local_image_ctx;
-  ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
-  ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &local_image_ctx));
-  snap_create(local_image_ctx, "snap");
-
-  std::string clone_image_name = get_temp_image_name();
-  ASSERT_EQ(0, clone_image(m_remote_image_ctx, "snap", clone_image_name));
-
-  librbd::ImageCtx *remote_clone_image_ctx;
-  ASSERT_EQ(0, open_image(m_remote_io_ctx, clone_image_name,
-               &remote_clone_image_ctx));
-
-  librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
-  librbd::MockTestImageCtx mock_local_parent_image_ctx(*local_image_ctx);
-  librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
-  MockCloneRequest mock_clone_request;
-  MockOpenImageRequest mock_open_image_request;
-  MockCloseImageRequest mock_close_image_request;
-
-  InSequence seq;
-  expect_ioctx_create(m_remote_io_ctx);
-  expect_ioctx_create(m_local_io_ctx);
-  expect_get_parent_global_image_id(m_remote_io_ctx, "global uuid", 0);
-  expect_mirror_image_get_image_id(m_local_io_ctx, "local parent id", 0);
-
-  expect_open_image(mock_open_image_request, m_remote_io_ctx,
-                    m_remote_image_ctx->id, mock_remote_parent_image_ctx, 0);
-  expect_open_image(mock_open_image_request, m_local_io_ctx,
-                    "local parent id", mock_local_parent_image_ctx, 0);
-  expect_snap_set(mock_local_parent_image_ctx, "snap", -ENOENT);
-  expect_close_image(mock_close_image_request, mock_local_parent_image_ctx, 0);
-  expect_close_image(mock_close_image_request, mock_remote_parent_image_ctx, 0);
-
-  C_SaferCond ctx;
-  MockCreateImageRequest *request = create_request("global uuid", "remote uuid",
-                                                   "image name", "101241a7c4c9",
-                                                   mock_remote_clone_image_ctx,
-                                                   &ctx);
-  request->send();
-  ASSERT_EQ(-ENOENT, ctx.wait());
-}
-
 TEST_F(TestMockImageReplayerCreateImageRequest, CloneError) {
   librbd::RBD rbd;
   librbd::ImageCtx *local_image_ctx;
@@ -584,7 +484,6 @@ TEST_F(TestMockImageReplayerCreateImageRequest, CloneError) {
                &remote_clone_image_ctx));
 
   librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
-  librbd::MockTestImageCtx mock_local_parent_image_ctx(*local_image_ctx);
   librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
   MockCloneRequest mock_clone_request;
   MockOpenImageRequest mock_open_image_request;
@@ -598,11 +497,7 @@ TEST_F(TestMockImageReplayerCreateImageRequest, CloneError) {
 
   expect_open_image(mock_open_image_request, m_remote_io_ctx,
                     m_remote_image_ctx->id, mock_remote_parent_image_ctx, 0);
-  expect_open_image(mock_open_image_request, m_local_io_ctx,
-                    "local parent id", mock_local_parent_image_ctx, 0);
-  expect_snap_set(mock_local_parent_image_ctx, "snap", 0);
-  expect_clone_image(mock_clone_request, mock_local_parent_image_ctx, -EINVAL);
-  expect_close_image(mock_close_image_request, mock_local_parent_image_ctx, 0);
+  expect_clone_image(mock_clone_request, -EINVAL);
   expect_close_image(mock_close_image_request, mock_remote_parent_image_ctx, 0);
 
   C_SaferCond ctx;
@@ -614,51 +509,6 @@ TEST_F(TestMockImageReplayerCreateImageRequest, CloneError) {
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
 
-TEST_F(TestMockImageReplayerCreateImageRequest, CloneLocalParentCloseError) {
-  librbd::RBD rbd;
-  librbd::ImageCtx *local_image_ctx;
-  ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
-  ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &local_image_ctx));
-  snap_create(local_image_ctx, "snap");
-
-  std::string clone_image_name = get_temp_image_name();
-  ASSERT_EQ(0, clone_image(m_remote_image_ctx, "snap", clone_image_name));
-
-  librbd::ImageCtx *remote_clone_image_ctx;
-  ASSERT_EQ(0, open_image(m_remote_io_ctx, clone_image_name,
-               &remote_clone_image_ctx));
-
-  librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
-  librbd::MockTestImageCtx mock_local_parent_image_ctx(*local_image_ctx);
-  librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
-  MockCloneRequest mock_clone_request;
-  MockOpenImageRequest mock_open_image_request;
-  MockCloseImageRequest mock_close_image_request;
-
-  InSequence seq;
-  expect_ioctx_create(m_remote_io_ctx);
-  expect_ioctx_create(m_local_io_ctx);
-  expect_get_parent_global_image_id(m_remote_io_ctx, "global uuid", 0);
-  expect_mirror_image_get_image_id(m_local_io_ctx, "local parent id", 0);
-
-  expect_open_image(mock_open_image_request, m_remote_io_ctx,
-                    m_remote_image_ctx->id, mock_remote_parent_image_ctx, 0);
-  expect_open_image(mock_open_image_request, m_local_io_ctx,
-                    "local parent id", mock_local_parent_image_ctx, 0);
-  expect_snap_set(mock_local_parent_image_ctx, "snap", 0);
-  expect_clone_image(mock_clone_request, mock_local_parent_image_ctx, 0);
-  expect_close_image(mock_close_image_request, mock_local_parent_image_ctx, -EINVAL);
-  expect_close_image(mock_close_image_request, mock_remote_parent_image_ctx, 0);
-
-  C_SaferCond ctx;
-  MockCreateImageRequest *request = create_request("global uuid", "remote uuid",
-                                                   "image name", "101241a7c4c9",
-                                                   mock_remote_clone_image_ctx,
-                                                   &ctx);
-  request->send();
-  ASSERT_EQ(0, ctx.wait());
-}
-
 TEST_F(TestMockImageReplayerCreateImageRequest, CloneRemoteParentCloseError) {
   librbd::RBD rbd;
   librbd::ImageCtx *local_image_ctx;
@@ -674,7 +524,6 @@ TEST_F(TestMockImageReplayerCreateImageRequest, CloneRemoteParentCloseError) {
                &remote_clone_image_ctx));
 
   librbd::MockTestImageCtx mock_remote_parent_image_ctx(*m_remote_image_ctx);
-  librbd::MockTestImageCtx mock_local_parent_image_ctx(*local_image_ctx);
   librbd::MockTestImageCtx mock_remote_clone_image_ctx(*remote_clone_image_ctx);
   MockCloneRequest mock_clone_request;
   MockOpenImageRequest mock_open_image_request;
@@ -688,11 +537,7 @@ TEST_F(TestMockImageReplayerCreateImageRequest, CloneRemoteParentCloseError) {
 
   expect_open_image(mock_open_image_request, m_remote_io_ctx,
                     m_remote_image_ctx->id, mock_remote_parent_image_ctx, 0);
-  expect_open_image(mock_open_image_request, m_local_io_ctx,
-                    "local parent id", mock_local_parent_image_ctx, 0);
-  expect_snap_set(mock_local_parent_image_ctx, "snap", 0);
-  expect_clone_image(mock_clone_request, mock_local_parent_image_ctx, 0);
-  expect_close_image(mock_close_image_request, mock_local_parent_image_ctx, 0);
+  expect_clone_image(mock_clone_request, 0);
   expect_close_image(mock_close_image_request, mock_remote_parent_image_ctx, -EINVAL);
 
   C_SaferCond ctx;
index 69f7ba9ef13f60cccb0d7c889ee09ddddaa20e36..ee62ced0f57d2ecfe723ca2cca8b5099b2868bd8 100644 (file)
@@ -193,9 +193,10 @@ public:
     std::string clone_id = librbd::util::generate_image_id(m_local_io_ctx);
     librbd::ImageOptions clone_opts;
     clone_opts.set(RBD_IMAGE_OPTION_FEATURES, ictx->features);
-    EXPECT_EQ(0, librbd::clone(ictx, m_local_io_ctx, "clone1", clone_id,
-                               clone_opts, GLOBAL_CLONE_IMAGE_ID,
-                               m_remote_mirror_uuid));
+    EXPECT_EQ(0, librbd::clone(m_local_io_ctx, m_local_image_id.c_str(),
+                               nullptr, "snap1", m_local_io_ctx,
+                               clone_id.c_str(), "clone1", clone_opts,
+                               GLOBAL_CLONE_IMAGE_ID, m_remote_mirror_uuid));
 
     cls::rbd::MirrorImage mirror_image(
       GLOBAL_CLONE_IMAGE_ID, MirrorImageState::MIRROR_IMAGE_STATE_ENABLED);
index 11831c193368c441b2c3afb4c869cd2c3285c994..453471a0a9e62e4d6977cbbaaf0877525058a205 100644 (file)
@@ -202,38 +202,13 @@ void CreateImageRequest<I>::handle_open_remote_parent_image(int r) {
     return;
   }
 
-  open_local_parent_image();
+  clone_image();
 }
 
 template <typename I>
-void CreateImageRequest<I>::open_local_parent_image() {
+void CreateImageRequest<I>::clone_image() {
   dout(20) << dendl;
 
-  Context *ctx = create_context_callback<
-    CreateImageRequest<I>,
-    &CreateImageRequest<I>::handle_open_local_parent_image>(this);
-  OpenImageRequest<I> *request = OpenImageRequest<I>::create(
-    m_local_parent_io_ctx, &m_local_parent_image_ctx,
-    m_local_parent_spec.image_id, true, ctx);
-  request->send();
-}
-
-template <typename I>
-void CreateImageRequest<I>::handle_open_local_parent_image(int r) {
-  dout(20) << ": r=" << r << dendl;
-  if (r < 0) {
-    derr << ": failed to open local parent image " << m_parent_pool_name << "/"
-         << m_local_parent_spec.image_id << dendl;
-    m_ret_val = r;
-    close_remote_parent_image();
-    return;
-  }
-
-  set_local_parent_snap();
-}
-
-template <typename I>
-void CreateImageRequest<I>::set_local_parent_snap() {
   std::string snap_name;
   cls::rbd::SnapshotNamespace snap_namespace;
   {
@@ -246,66 +221,18 @@ void CreateImageRequest<I>::set_local_parent_snap() {
     }
   }
 
-  if (snap_name.empty()) {
-    dout(15) << ": failed to locate remote parent snapshot" << dendl;
-    m_ret_val = -ENOENT;
-    close_local_parent_image();
-    return;
-  }
-
-  m_local_parent_spec.snap_id = CEPH_NOSNAP;
-  {
-    RWLock::RLocker local_snap_locker(m_local_parent_image_ctx->snap_lock);
-    auto it = m_local_parent_image_ctx->snap_ids.find(
-      {snap_namespace, snap_name});
-    if (it != m_local_parent_image_ctx->snap_ids.end()) {
-      m_local_parent_spec.snap_id = it->second;
-    }
-  }
-
-  if (m_local_parent_spec.snap_id == CEPH_NOSNAP) {
-    dout(15) << ": failed to locate local parent snapshot" << dendl;
-    m_ret_val = -ENOENT;
-    close_local_parent_image();
-    return;
-  }
-
-  dout(15) << ": local_snap_id=" << m_local_parent_spec.snap_id << dendl;
-
-  Context *ctx = create_context_callback<
-    CreateImageRequest<I>,
-    &CreateImageRequest<I>::handle_set_local_parent_snap>(this);
-  m_local_parent_image_ctx->state->snap_set(m_local_parent_spec.snap_id, ctx);
-}
-
-template <typename I>
-void CreateImageRequest<I>::handle_set_local_parent_snap(int r) {
-  dout(20) << ": r=" << r << dendl;
-  if (r < 0) {
-    derr << ": failed to set parent snapshot " << m_local_parent_spec.snap_id
-         << ": " << cpp_strerror(r) << dendl;
-    m_ret_val = r;
-    close_local_parent_image();
-    return;
-  }
-
-  clone_image();
-}
-
-template <typename I>
-void CreateImageRequest<I>::clone_image() {
-  dout(20) << dendl;
-
   librbd::ImageOptions opts;
   populate_image_options(&opts);
 
   using klass = CreateImageRequest<I>;
-  Context *ctx = create_context_callback<klass, &klass::handle_clone_image>(this);
+  Context *ctx = create_context_callback<
+    klass, &klass::handle_clone_image>(this);
 
   librbd::image::CloneRequest<I> *req = librbd::image::CloneRequest<I>::create(
-    m_local_parent_image_ctx, m_local_io_ctx, m_local_image_name,
-    m_local_image_id, opts, m_global_image_id, m_remote_mirror_uuid,
-    m_remote_image_ctx->op_work_queue, ctx);
+    m_local_parent_io_ctx, m_local_parent_spec.image_id, snap_name, CEPH_NOSNAP,
+    m_local_io_ctx, m_local_image_name, m_local_image_id, opts,
+    m_global_image_id, m_remote_mirror_uuid, m_remote_image_ctx->op_work_queue,
+    ctx);
   req->send();
 }
 
@@ -314,33 +241,11 @@ void CreateImageRequest<I>::handle_clone_image(int r) {
   dout(20) << ": r=" << r << dendl;
   if (r < 0) {
     derr << ": failed to clone image " << m_parent_pool_name << "/"
-         << m_local_parent_image_ctx->name << " to "
+         << m_remote_parent_spec.image_id << " to "
          << m_local_image_name << dendl;
     m_ret_val = r;
   }
 
-  close_local_parent_image();
-}
-
-template <typename I>
-void CreateImageRequest<I>::close_local_parent_image() {
-  dout(20) << dendl;
-  Context *ctx = create_context_callback<
-    CreateImageRequest<I>,
-    &CreateImageRequest<I>::handle_close_local_parent_image>(this);
-  CloseImageRequest<I> *request = CloseImageRequest<I>::create(
-    &m_local_parent_image_ctx, ctx);
-  request->send();
-}
-
-template <typename I>
-void CreateImageRequest<I>::handle_close_local_parent_image(int r) {
-  dout(20) << ": r=" << r << dendl;
-  if (r < 0) {
-    derr << ": error encountered closing local parent image: "
-         << cpp_strerror(r) << dendl;
-  }
-
   close_remote_parent_image();
 }
 
index 0e6b8e10560f6efa5a8b3d0e658e2645a5f4dd13..104a132f22b12491bb454b10e656f13f423a00d2 100644 (file)
@@ -64,19 +64,10 @@ private:
    *                    OPEN_REMOTE_PARENT  * * * * * * * | * * *   *
    *                        |                             |       * *
    *                        v                             |         *
-   *                    OPEN_LOCAL_PARENT * * * * * * *   |         *
-   *                        |                         *   |         *
-   *                        v                         *   |         *
-   *                    SET_LOCAL_PARENT_SNAP         *   |         *
-   *                        |         *               *   |         *
-   *                        v         *               *   |         *
-   *                    CLONE_IMAGE   *               *   |         *
-   *                        |         *               *   |         *
-   *                        v         v               *   |         *
-   *                    CLOSE_LOCAL_PARENT            *   |         *
-   *                        |                         *   |         *
-   *                        v                         *   |         *
-   *                    CLOSE_REMOTE_PARENT < * * * * *   |         *
+   *                    CLONE_IMAGE                       |         *
+   *                        |                             |         *
+   *                        v                             |         *
+   *                    CLOSE_REMOTE_PARENT               |         *
    *                        |                             v         *
    *                        \------------------------> <finish> < * *
    * @endverbatim
@@ -96,7 +87,6 @@ private:
   librbd::ParentSpec m_remote_parent_spec;
 
   librados::IoCtx m_local_parent_io_ctx;
-  ImageCtxT *m_local_parent_image_ctx = nullptr;
   librbd::ParentSpec m_local_parent_spec;
 
   bufferlist m_out_bl;
@@ -116,18 +106,9 @@ private:
   void open_remote_parent_image();
   void handle_open_remote_parent_image(int r);
 
-  void open_local_parent_image();
-  void handle_open_local_parent_image(int r);
-
-  void set_local_parent_snap();
-  void handle_set_local_parent_snap(int r);
-
   void clone_image();
   void handle_clone_image(int r);
 
-  void close_local_parent_image();
-  void handle_close_local_parent_image(int r);
-
   void close_remote_parent_image();
   void handle_close_remote_parent_image(int r);