]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
librbd: snapshot mirror mode
authorMykola Golub <mgolub@suse.com>
Sun, 1 Sep 2019 08:48:16 +0000 (09:48 +0100)
committerMykola Golub <mgolub@suse.com>
Tue, 10 Dec 2019 15:45:30 +0000 (15:45 +0000)
Enabling mirroring for an image that does not support journaling
assumes snapshot based mirroring, which is supported only when the
pool is in the "image" mirror mode.

Also for the pool in the "image" mirror mode disabling/enabling
journaling feature for a mirroring image will switch
snapshot/journal mirror mode.

Signed-off-by: Mykola Golub <mgolub@suse.com>
29 files changed:
qa/workunits/rbd/rbd_mirror.sh
src/librbd/api/Migration.cc
src/librbd/api/Mirror.cc
src/librbd/api/Trash.cc
src/librbd/image/PreRemoveRequest.cc
src/librbd/mirror/DemoteRequest.cc
src/librbd/mirror/DisableRequest.cc
src/librbd/mirror/DisableRequest.h
src/librbd/mirror/EnableRequest.cc
src/librbd/mirror/EnableRequest.h
src/librbd/mirror/GetInfoRequest.cc
src/librbd/mirror/GetInfoRequest.h
src/librbd/mirror/PromoteRequest.cc
src/librbd/mirror/snapshot/DemoteRequest.h
src/librbd/mirror/snapshot/PromoteRequest.h
src/librbd/operation/EnableFeaturesRequest.cc
src/librbd/operation/EnableFeaturesRequest.h
src/test/librbd/mirror/test_mock_DisableRequest.cc
src/test/librbd/operation/test_mock_DisableFeaturesRequest.cc
src/test/librbd/operation/test_mock_EnableFeaturesRequest.cc
src/test/librbd/test_mirroring.cc
src/test/rbd_mirror/image_replayer/test_mock_BootstrapRequest.cc
src/tools/rbd_mirror/CMakeLists.txt
src/tools/rbd_mirror/image_replayer/BootstrapRequest.cc
src/tools/rbd_mirror/image_replayer/BootstrapRequest.h
src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.cc [deleted file]
src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.h [deleted file]
src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.cc
src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h

index cf0d9f7e300e8883a3ac932092dbd441696a378b..86af089f8a55968d7cb62c30ce3a4049cfb47093 100755 (executable)
@@ -334,7 +334,6 @@ done
 
 set_pool_mirror_mode ${CLUSTER2} ${POOL} 'pool'
 for i in ${image2} ${image4}; do
-  enable_journaling ${CLUSTER2} ${POOL} ${i}
   wait_for_image_present ${CLUSTER1} ${POOL} ${i} 'present'
   wait_for_snap_present ${CLUSTER1} ${POOL} ${i} 'snap2'
   wait_for_image_replay_started ${CLUSTER1} ${POOL} ${i}
@@ -390,7 +389,6 @@ fi
 start_mirrors ${CLUSTER1}
 wait_for_image_present ${CLUSTER1} ${POOL} ${image} 'deleted'
 set_pool_mirror_mode ${CLUSTER2} ${POOL} 'pool'
-enable_journaling ${CLUSTER2} ${POOL} ${image}
 wait_for_image_present ${CLUSTER1} ${POOL} ${image} 'present'
 wait_for_image_replay_started ${CLUSTER1} ${POOL} ${image}
 
index 713b856804e89605ce1f129af6cb202242d31c78..458d15254a7f37ab9d105f089f371d4d491008d0 100644 (file)
@@ -1417,10 +1417,6 @@ template <typename I>
 int Migration<I>::disable_mirroring(I *image_ctx, bool *was_enabled) {
   *was_enabled = false;
 
-  if (!image_ctx->test_features(RBD_FEATURE_JOURNALING)) {
-    return 0;
-  }
-
   cls::rbd::MirrorImage mirror_image;
   int r = cls_client::mirror_image_get(&image_ctx->md_ctx, image_ctx->id,
                                        &mirror_image);
@@ -1458,11 +1454,6 @@ int Migration<I>::disable_mirroring(I *image_ctx, bool *was_enabled) {
 
 template <typename I>
 int Migration<I>::enable_mirroring(I *image_ctx, bool was_enabled) {
-
-  if (!image_ctx->test_features(RBD_FEATURE_JOURNALING)) {
-    return 0;
-  }
-
   cls::rbd::MirrorMode mirror_mode;
   int r = cls_client::mirror_mode_get(&image_ctx->md_ctx, &mirror_mode);
   if (r < 0 && r != -ENOENT) {
index 4f7cb215ea1df3ab9b3b661761d7f2c1d1c39ff9..2fe1682914041a8152792b6cbe7590faa889535b 100644 (file)
@@ -281,7 +281,7 @@ struct C_ImageGetInfo : public Context {
   }
 
   void finish(int r) override {
-    if (r < 0) {
+    if (r < 0 && r != -ENOENT) {
       on_finish->complete(r);
       return;
     }
@@ -311,7 +311,7 @@ struct C_ImageGetGlobalStatus : public C_ImageGetInfo {
   }
 
   void finish(int r) override {
-    if (r < 0) {
+    if (r < 0 && r != -ENOENT) {
       on_finish->complete(r);
       return;
     }
@@ -363,13 +363,8 @@ int Mirror<I>::image_enable(I *ictx, bool relax_same_pool_parent_check) {
     std::shared_lock image_locker{ictx->image_lock};
     ImageCtx *parent = ictx->parent;
     if (parent) {
-      if (relax_same_pool_parent_check &&
-          parent->md_ctx.get_id() == ictx->md_ctx.get_id()) {
-        if (!parent->test_features(RBD_FEATURE_JOURNALING)) {
-          lderr(cct) << "journaling is not enabled for the parent" << dendl;
-          return -EINVAL;
-        }
-      } else {
+      if (parent->md_ctx.get_id() != ictx->md_ctx.get_id() ||
+          !relax_same_pool_parent_check) {
         cls::rbd::MirrorImage mirror_image_internal;
         r = cls_client::mirror_image_get(&(parent->md_ctx), parent->id,
                                          &mirror_image_internal);
@@ -381,18 +376,6 @@ int Mirror<I>::image_enable(I *ictx, bool relax_same_pool_parent_check) {
     }
   }
 
-  if (!ictx->test_features(RBD_FEATURE_JOURNALING)) {
-    uint64_t features = RBD_FEATURE_JOURNALING;
-    if (!ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
-      features |= RBD_FEATURE_EXCLUSIVE_LOCK;
-    }
-    r = ictx->operations->update_features(features, true);
-    if (r < 0) {
-      lderr(cct) << "cannot enable journaling: " << cpp_strerror(r) << dendl;
-      return r;
-    }
-  }
-
   C_SaferCond ctx;
   auto req = mirror::EnableRequest<ImageCtx>::create(ictx, &ctx);
   req->send();
@@ -530,12 +513,6 @@ int Mirror<I>::image_disable(I *ictx, bool force) {
       rollback = true;
       return r;
     }
-
-    r = ictx->operations->update_features(RBD_FEATURE_JOURNALING, false);
-    if (r < 0) {
-      lderr(cct) << "cannot disable journaling: " << cpp_strerror(r) << dendl;
-      // not fatal
-    }
   }
 
   return 0;
@@ -874,6 +851,8 @@ int Mirror<I>::mode_set(librados::IoCtx& io_ctx,
         return r;
       }
 
+      // Enable only journal based mirroring
+
       if ((features & RBD_FEATURE_JOURNALING) != 0) {
         I *img_ctx = I::create("", img_pair.second, nullptr, io_ctx, false);
         r = img_ctx->state->open(0);
index 88de92e2d1759d3ea290990bc604d26e649525b7..d37d3300579c1fb177d2fdbfb9b4e04373874180 100644 (file)
@@ -43,10 +43,6 @@ namespace {
 
 template <typename I>
 int disable_mirroring(I *ictx) {
-  if (!ictx->test_features(RBD_FEATURE_JOURNALING)) {
-    return 0;
-  }
-
   cls::rbd::MirrorImage mirror_image;
   int r = cls_client::mirror_image_get(&ictx->md_ctx, ictx->id, &mirror_image);
   if (r == -ENOENT) {
@@ -88,10 +84,6 @@ int enable_mirroring(IoCtx &io_ctx, const std::string &image_id) {
     return r;
   }
 
-  if ((features & RBD_FEATURE_JOURNALING) == 0) {
-    return 0;
-  }
-
   cls::rbd::MirrorMode mirror_mode;
   r = cls_client::mirror_mode_get(&io_ctx, &mirror_mode);
   if (r < 0 && r != -ENOENT) {
index f67ca99fd0be1e280dd2b526ec9524db3e93410a..5b1b7dd18dcca72e300245335739b7848172ead3 100644 (file)
@@ -26,6 +26,8 @@ bool auto_delete_snapshot(const SnapInfo& snap_info) {
     snap_info.snap_namespace);
   switch (snap_namespace_type) {
   case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH:
+  case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY:
+  case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY:
     return true;
   default:
     return false;
index f075076621ef6554c6d084810097554364c9d2ec..171b86b1f896e63489fc904f8c518815779b6a5e 100644 (file)
@@ -11,6 +11,7 @@
 #include "librbd/Journal.h"
 #include "librbd/Utils.h"
 #include "librbd/mirror/GetInfoRequest.h"
+#include "librbd/mirror/snapshot/DemoteRequest.h"
 
 #define dout_subsys ceph_subsys_rbd
 #undef dout_prefix
@@ -44,7 +45,7 @@ void DemoteRequest<I>::handle_get_info(int r) {
   CephContext *cct = m_image_ctx.cct;
   ldout(cct, 20) << "r=" << r << dendl;
 
-  if (r < 0) {
+  if (r < 0 && r != -ENOENT) {
     lderr(cct) << "failed to retrieve mirroring state: " << cpp_strerror(r)
                << dendl;
     finish(r);
@@ -69,8 +70,12 @@ void DemoteRequest<I>::acquire_lock() {
   m_image_ctx.owner_lock.lock_shared();
   if (m_image_ctx.exclusive_lock == nullptr) {
     m_image_ctx.owner_lock.unlock_shared();
-    lderr(cct) << "exclusive lock is not active" << dendl;
-    finish(-EINVAL);
+    if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+      lderr(cct) << "exclusive lock is not active" << dendl;
+      finish(-EINVAL);
+    } else {
+      demote();
+    }
     return;
   }
 
@@ -126,7 +131,16 @@ void DemoteRequest<I>::demote() {
 
   auto ctx = create_context_callback<
     DemoteRequest<I>, &DemoteRequest<I>::handle_demote>(this);
-  Journal<I>::demote(&m_image_ctx, ctx);
+  if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+    Journal<I>::demote(&m_image_ctx, ctx);
+  } else if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+    auto req = mirror::snapshot::DemoteRequest<I>::create(&m_image_ctx, ctx);
+    req->send();
+  } else {
+    lderr(cct) << "unknown image mirror mode: " << m_mirror_image.mode << dendl;
+    m_ret_val = -EOPNOTSUPP;
+    release_lock();
+  }
 }
 
 template <typename I>
index 929a09685bd7778ae3cd1291ed57bba65819849d..bdbb28504f6f5e8f790726229574031cb77c0e14 100644 (file)
@@ -14,6 +14,7 @@
 #include "librbd/Operations.h"
 #include "librbd/Utils.h"
 #include "librbd/journal/PromoteRequest.h"
+#include "librbd/mirror/GetInfoRequest.h"
 
 #define dout_subsys ceph_subsys_rbd
 #undef dout_prefix
@@ -33,77 +34,42 @@ DisableRequest<I>::DisableRequest(I *image_ctx, bool force, bool remove,
 
 template <typename I>
 void DisableRequest<I>::send() {
-  send_get_mirror_image();
+  send_get_mirror_info();
 }
 
 template <typename I>
-void DisableRequest<I>::send_get_mirror_image() {
+void DisableRequest<I>::send_get_mirror_info() {
   CephContext *cct = m_image_ctx->cct;
   ldout(cct, 10) << this << " " << __func__ << dendl;
 
-  librados::ObjectReadOperation op;
-  cls_client::mirror_image_get_start(&op, m_image_ctx->id);
 
   using klass = DisableRequest<I>;
-  librados::AioCompletion *comp =
-    create_rados_callback<klass, &klass::handle_get_mirror_image>(this);
-  m_out_bl.clear();
-  int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
-  ceph_assert(r == 0);
-  comp->release();
+  Context *ctx = util::create_context_callback<
+      klass, &klass::handle_get_mirror_info>(this);
+
+  auto req = GetInfoRequest<I>::create(*m_image_ctx, &m_mirror_image,
+                                       &m_promotion_state, ctx);
+  req->send();
 }
 
 template <typename I>
-Context *DisableRequest<I>::handle_get_mirror_image(int *result) {
+Context *DisableRequest<I>::handle_get_mirror_info(int *result) {
   CephContext *cct = m_image_ctx->cct;
   ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
 
-  if (*result == 0) {
-    auto iter = m_out_bl.cbegin();
-    *result = cls_client::mirror_image_get_finish(&iter, &m_mirror_image);
-  }
-
   if (*result < 0) {
     if (*result == -ENOENT) {
       ldout(cct, 20) << this << " " << __func__
                      << ": mirroring is not enabled for this image" << dendl;
       *result = 0;
-    } else if (*result == -EOPNOTSUPP) {
-      ldout(cct, 5) << this << " " << __func__
-                    << ": mirroring is not supported by OSD" << dendl;
     } else {
-      lderr(cct) << "failed to retrieve mirror image: " << cpp_strerror(*result)
+      lderr(cct) << "failed to get mirroring info: " << cpp_strerror(*result)
                  << dendl;
     }
     return m_on_finish;
   }
 
-  send_get_tag_owner();
-  return nullptr;
-}
-
-template <typename I>
-void DisableRequest<I>::send_get_tag_owner() {
-  CephContext *cct = m_image_ctx->cct;
-  ldout(cct, 10) << this << " " << __func__ << dendl;
-
-  using klass = DisableRequest<I>;
-  Context *ctx = util::create_context_callback<
-      klass, &klass::handle_get_tag_owner>(this);
-
-  Journal<I>::is_tag_owner(m_image_ctx, &m_is_primary, ctx);
-}
-
-template <typename I>
-Context *DisableRequest<I>::handle_get_tag_owner(int *result) {
-  CephContext *cct = m_image_ctx->cct;
-  ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
-
-  if (*result < 0) {
-    lderr(cct) << "failed to check tag ownership: " << cpp_strerror(*result)
-               << dendl;
-    return m_on_finish;
-  }
+  m_is_primary = (m_promotion_state == PROMOTION_STATE_PRIMARY);
 
   if (!m_is_primary && !m_force) {
     lderr(cct) << "mirrored image is not primary, "
@@ -129,7 +95,6 @@ void DisableRequest<I>::send_set_mirror_image() {
   using klass = DisableRequest<I>;
   librados::AioCompletion *comp =
     create_rados_callback<klass, &klass::handle_set_mirror_image>(this);
-  m_out_bl.clear();
   int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op);
   ceph_assert(r == 0);
   comp->release();
@@ -175,6 +140,33 @@ Context *DisableRequest<I>::handle_notify_mirroring_watcher(int *result) {
     *result = 0;
   }
 
+  if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+    // remove mirroring snapshots
+
+    bool removing_snapshots = false;
+    {
+      std::lock_guard locker{m_lock};
+      std::shared_lock image_locker{m_image_ctx->image_lock};
+
+      for (auto &it : m_image_ctx->snap_info) {
+        auto &snap_info = it.second;
+        auto type = cls::rbd::get_snap_namespace_type(
+          snap_info.snap_namespace);
+        if (type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY ||
+            type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY) {
+          send_remove_snap("", snap_info.snap_namespace, snap_info.name);
+          removing_snapshots = true;
+        }
+      }
+    }
+
+    if (!removing_snapshots) {
+      send_remove_mirror_image();
+    }
+
+    return nullptr;
+  }
+
   send_promote_image();
   return nullptr;
 }
@@ -334,13 +326,22 @@ Context *DisableRequest<I>::handle_remove_snap(int *result,
   m_current_ops[client_id]--;
 
   if (*result < 0 && *result != -ENOENT) {
-    lderr(cct) <<
-      "failed to remove temporary snapshot created by remote peer: "
+    lderr(cct) << "failed to remove mirroring snapshot: "
                << cpp_strerror(*result) << dendl;
     m_ret[client_id] = *result;
   }
 
   if (m_current_ops[client_id] == 0) {
+    if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+      ceph_assert(client_id.empty());
+      m_current_ops.erase(client_id);
+      if (m_ret[client_id] < 0) {
+        return m_on_finish;
+      }
+      send_remove_mirror_image();
+      return nullptr;
+    }
+
     send_unregister_client(client_id);
   }
 
@@ -415,7 +416,6 @@ void DisableRequest<I>::send_remove_mirror_image() {
   using klass = DisableRequest<I>;
   librados::AioCompletion *comp =
     create_rados_callback<klass, &klass::handle_remove_mirror_image>(this);
-  m_out_bl.clear();
   int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op);
   ceph_assert(r == 0);
   comp->release();
index a3eeee786764d53ff9dde51e06c15fd22b98803d..1434d3f3ee8eacea017b0487343b10199d2ff422 100644 (file)
@@ -8,6 +8,8 @@
 #include "common/ceph_mutex.h"
 #include "cls/journal/cls_journal_types.h"
 #include "cls/rbd/cls_rbd_types.h"
+#include "librbd/mirror/Types.h"
+
 #include <map>
 #include <string>
 
@@ -39,10 +41,7 @@ private:
    * <start>
    *    |
    *    v
-   * GET_MIRROR_IMAGE * * * * * * * * * * * * * * * * * * * * * * *
-   *    |                                                         *
-   *    v                                                         *
-   * GET_TAG_OWNER  * * * * * * * * * * * * * * * * * * * * * * * *
+   * GET_MIRROR_INFO  * * * * * * * * * * * * * * * * * * * * * * *
    *    |                                                         *
    *    v                                                         *
    * SET_MIRROR_IMAGE * * * * * * * * * * * * * * * * * * * * * * *
@@ -86,8 +85,8 @@ private:
   Context *m_on_finish;
 
   bool m_is_primary = false;
-  bufferlist m_out_bl;
   cls::rbd::MirrorImage m_mirror_image;
+  PromotionState m_promotion_state = PROMOTION_STATE_NON_PRIMARY;
   std::set<cls::journal::Client> m_clients;
   std::map<std::string, int> m_ret;
   std::map<std::string, int> m_current_ops;
@@ -95,11 +94,8 @@ private:
   mutable ceph::mutex m_lock =
     ceph::make_mutex("mirror::DisableRequest::m_lock");
 
-  void send_get_mirror_image();
-  Context *handle_get_mirror_image(int *result);
-
-  void send_get_tag_owner();
-  Context *handle_get_tag_owner(int *result);
+  void send_get_mirror_info();
+  Context *handle_get_mirror_info(int *result);
 
   void send_set_mirror_image();
   Context *handle_set_mirror_image(int *result);
index b0f7ea18725b825210b2c613d0578176ffbd9b8a..c94ad43bd59d146f31991c91bf9e5f338ea61294 100644 (file)
@@ -79,7 +79,6 @@ Context *EnableRequest<I>::handle_get_mirror_image(int *result) {
   }
 
   *result = 0;
-  m_mirror_image.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
   if (m_non_primary_global_image_id.empty()) {
     uuid_d uuid_gen;
     uuid_gen.generate_random();
@@ -88,13 +87,58 @@ Context *EnableRequest<I>::handle_get_mirror_image(int *result) {
     m_mirror_image.global_image_id = m_non_primary_global_image_id;
   }
 
+  send_get_features();
+  return nullptr;
+}
+
+template <typename I>
+void EnableRequest<I>::send_get_features() {
+  ldout(m_cct, 10) << this << " " << __func__ << dendl;
+
+  librados::ObjectReadOperation op;
+  cls_client::get_features_start(&op, true);
+
+  using klass = EnableRequest<I>;
+  librados::AioCompletion *comp =
+    create_rados_callback<klass, &klass::handle_get_features>(this);
+  m_out_bl.clear();
+  int r = m_io_ctx.aio_operate(util::header_name(m_image_id), comp, &op,
+                               &m_out_bl);
+  ceph_assert(r == 0);
+  comp->release();
+}
+
+template <typename I>
+Context *EnableRequest<I>::handle_get_features(int *result) {
+  ldout(m_cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+  uint64_t features, incompatible_features;
+  if (*result == 0) {
+    auto iter = m_out_bl.cbegin();
+    *result = cls_client::get_features_finish(&iter, &features,
+                                              &incompatible_features);
+  }
+
+  if (*result != 0) {
+    lderr(m_cct) << "failed to retrieve image features: "
+                 << cpp_strerror(*result) << dendl;
+    return m_on_finish;
+  }
+
+  // TODO: be explicit about the image mirror mode
+
+  m_mirror_image.mode = (features & RBD_FEATURE_JOURNALING) != 0 ?
+    cls::rbd::MIRROR_IMAGE_MODE_JOURNAL : cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT;
+  m_mirror_image.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
+
   send_get_tag_owner();
   return nullptr;
 }
 
 template <typename I>
 void EnableRequest<I>::send_get_tag_owner() {
-  if (!m_non_primary_global_image_id.empty()) {
+  if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT ||
+      !m_non_primary_global_image_id.empty()) {
     send_set_mirror_image();
     return;
   }
index 965c2a36b8cc5919612580389288ab9c20afb638..66b0c1f6859ff30cfa169f880f6a945842094479 100644 (file)
@@ -46,9 +46,12 @@ private:
    * GET_MIRROR_IMAGE * * * * * * *
    *    |                         * (on error)
    *    v                         *
-   * GET_TAG_OWNER  * * * * * * * *
+   * GET_FEATURES * * * * * * * * *
    *    |                         *
    *    v                         *
+   * GET_TAG_OWNER  * * * * * * * *
+   *    |  (skip if not needed)   *
+   *    v                         *
    * SET_MIRROR_IMAGE * * * * * * *
    *    |                         *
    *    v                         *
@@ -78,6 +81,9 @@ private:
   void send_get_mirror_image();
   Context *handle_get_mirror_image(int *result);
 
+  void send_get_features();
+  Context *handle_get_features(int *result);
+
   void send_get_tag_owner();
   Context *handle_get_tag_owner(int *result);
 
index 1bd692ae3ea160aef0f6a3835b0b9b314aa259ce..74ca27c59181061c2d80a5cd0b1a96bed95567a1 100644 (file)
@@ -82,10 +82,9 @@ void GetInfoRequest<I>::handle_get_mirror_image(int r) {
     r = cls_client::mirror_image_get_finish(&iter, m_mirror_image);
   }
 
-  if (r == -ENOENT ||
-      m_mirror_image->state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
+  if (r == -ENOENT) {
     ldout(cct, 20) << "mirroring is disabled" << dendl;
-    finish(0);
+    finish(r);
     return;
   } else if (r < 0) {
     lderr(cct) << "failed to retrieve mirroring state: " << cpp_strerror(r)
@@ -94,22 +93,30 @@ void GetInfoRequest<I>::handle_get_mirror_image(int r) {
     return;
   }
 
-  get_tag_owner();
+  if (m_mirror_image->mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+    get_journal_tag_owner();
+  } else if (m_mirror_image->mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+    get_snapshot_promotion_state();
+  } else {
+    ldout(cct, 20) << "unknown mirror image mode: " << m_mirror_image->mode
+                   << dendl;
+    finish(-EOPNOTSUPP);
+  }
 }
 
 template <typename I>
-void GetInfoRequest<I>::get_tag_owner() {
+void GetInfoRequest<I>::get_journal_tag_owner() {
   CephContext *cct = m_image_ctx.cct;
   ldout(cct, 20) << dendl;
 
   auto ctx = create_context_callback<
-    GetInfoRequest<I>, &GetInfoRequest<I>::handle_get_tag_owner>(this);
+    GetInfoRequest<I>, &GetInfoRequest<I>::handle_get_journal_tag_owner>(this);
   Journal<I>::get_tag_owner(m_image_ctx.md_ctx, m_image_ctx.id,
                             &m_mirror_uuid, m_image_ctx.op_work_queue, ctx);
 }
 
 template <typename I>
-void GetInfoRequest<I>::handle_get_tag_owner(int r) {
+void GetInfoRequest<I>::handle_get_journal_tag_owner(int r) {
   CephContext *cct = m_image_ctx.cct;
   ldout(cct, 20) << "r=" << r << dendl;
 
@@ -129,6 +136,41 @@ void GetInfoRequest<I>::handle_get_tag_owner(int r) {
   finish(0);
 }
 
+template <typename I>
+void GetInfoRequest<I>::get_snapshot_promotion_state() {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 20) << dendl;
+
+  *m_promotion_state = PROMOTION_STATE_PRIMARY;
+  {
+    std::shared_lock image_locker{m_image_ctx.image_lock};
+    for (auto it = m_image_ctx.snap_info.rbegin();
+         it != m_image_ctx.snap_info.rend(); it++) {
+      auto primary = boost::get<cls::rbd::MirrorPrimarySnapshotNamespace>(
+        &it->second.snap_namespace);
+      if (primary != nullptr) {
+        if (primary->demoted) {
+          *m_promotion_state = PROMOTION_STATE_ORPHAN;
+        }
+        break;
+      }
+      auto non_primary =
+        boost::get<cls::rbd::MirrorNonPrimarySnapshotNamespace>(
+          &it->second.snap_namespace);
+      if (non_primary != nullptr) {
+        if (non_primary->primary_mirror_uuid.empty()) {
+          *m_promotion_state = PROMOTION_STATE_ORPHAN;
+        } else {
+          *m_promotion_state = PROMOTION_STATE_NON_PRIMARY;
+        }
+        break;
+      }
+    }
+  }
+  finish(0);
+}
+
+
 template <typename I>
 void GetInfoRequest<I>::finish(int r) {
   CephContext *cct = m_image_ctx.cct;
index c37ea581836326a0247ff54c137fc609fdb1dde5..d694bbd3920b7dd4ed92790a168ea813dccd3429 100644 (file)
@@ -49,7 +49,7 @@ private:
    * GET_MIRROR_IMAGE
    *    |
    *    v
-   * GET_TAG_OWNER
+   * GET_JOURNAL_TAG_OWNER (if journal)
    *    |
    *    v
    * <finish>
@@ -71,8 +71,10 @@ private:
   void get_mirror_image();
   void handle_get_mirror_image(int r);
 
-  void get_tag_owner();
-  void handle_get_tag_owner(int r);
+  void get_journal_tag_owner();
+  void handle_get_journal_tag_owner(int r);
+
+  void get_snapshot_promotion_state();
 
   void finish(int r);
 
index 5603cb13423e96c985d2ba4818d4553a4879ae7e..2905d13bff61ad7029aaa9ceef485c1d7696ff03 100644 (file)
@@ -10,6 +10,7 @@
 #include "librbd/Journal.h"
 #include "librbd/Utils.h"
 #include "librbd/mirror/GetInfoRequest.h"
+#include "librbd/mirror/snapshot/PromoteRequest.h"
 
 #define dout_subsys ceph_subsys_rbd
 #undef dout_prefix
@@ -72,7 +73,15 @@ void PromoteRequest<I>::promote() {
 
   auto ctx = create_context_callback<
     PromoteRequest<I>, &PromoteRequest<I>::handle_promote>(this);
-  Journal<I>::promote(&m_image_ctx, ctx);
+  if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+    Journal<I>::promote(&m_image_ctx, ctx);
+  } else if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+    auto req = mirror::snapshot::PromoteRequest<I>::create(&m_image_ctx, ctx);
+    req->send();
+  } else {
+    lderr(cct) << "unknown image mirror mode: " << m_mirror_image.mode << dendl;
+    finish(-EOPNOTSUPP);
+  }
 }
 
 template <typename I>
index 054a9dce12d627511eabefe0e26bd303f5829079..f075561e3efa36bf9d0bd70bd1093ff07e9cf894 100644 (file)
@@ -21,13 +21,12 @@ namespace snapshot {
 template <typename ImageCtxT = librbd::ImageCtx>
 class DemoteRequest {
 public:
-  static DemoteRequest *create(ImageCtxT *image_ctx, bool force,
-                               Context *on_finish) {
-    return new DemoteRequest(image_ctx, force, on_finish);
+  static DemoteRequest *create(ImageCtxT *image_ctx, Context *on_finish) {
+    return new DemoteRequest(image_ctx, on_finish);
   }
 
-  DemoteRequest(ImageCtxT *image_ctx, bool force, Context *on_finish)
-    : m_image_ctx(image_ctx), m_force(force), m_on_finish(on_finish) {
+  DemoteRequest(ImageCtxT *image_ctx, Context *on_finish)
+    : m_image_ctx(image_ctx), m_on_finish(on_finish) {
   }
 
   void send();
@@ -51,7 +50,6 @@ private:
    */
 
   ImageCtxT *m_image_ctx;
-  bool m_force;
   Context *m_on_finish;
 
   void refresh_image();
index ef6e93301d72e202af2c0c039ae6551cd9e96b05..7efd921d8d17bc9d9fd62fcd6795cf0c05020fff 100644 (file)
@@ -21,13 +21,12 @@ namespace snapshot {
 template <typename ImageCtxT = librbd::ImageCtx>
 class PromoteRequest {
 public:
-  static PromoteRequest *create(ImageCtxT *image_ctx, bool force,
-                                Context *on_finish) {
-    return new PromoteRequest(image_ctx, force, on_finish);
+  static PromoteRequest *create(ImageCtxT *image_ctx, Context *on_finish) {
+    return new PromoteRequest(image_ctx, on_finish);
   }
 
-  PromoteRequest(ImageCtxT *image_ctx, bool force, Context *on_finish)
-    : m_image_ctx(image_ctx), m_force(force), m_on_finish(on_finish) {
+  PromoteRequest(ImageCtxT *image_ctx, Context *on_finish)
+    : m_image_ctx(image_ctx), m_on_finish(on_finish) {
   }
 
   void send();
@@ -51,7 +50,6 @@ private:
    */
 
   ImageCtxT *m_image_ctx;
-  bool m_force;
   Context *m_on_finish;
 
   void refresh_image();
index c01cca7ecdc03b3d59dc9455ae26147338cf6d2d..c85c56f86d41a0cd7bb33133cb3efe462f8e7a02 100644 (file)
@@ -12,6 +12,7 @@
 #include "librbd/image/SetFlagsRequest.h"
 #include "librbd/io/ImageRequestWQ.h"
 #include "librbd/journal/CreateRequest.h"
+#include "librbd/mirror/DisableRequest.h"
 #include "librbd/mirror/EnableRequest.h"
 #include "librbd/object_map/CreateRequest.h"
 
@@ -118,10 +119,7 @@ void EnableFeaturesRequest<I>::send_get_mirror_mode() {
   CephContext *cct = image_ctx.cct;
 
   if ((m_features & RBD_FEATURE_JOURNALING) == 0) {
-    Context *ctx = create_context_callback<
-      EnableFeaturesRequest<I>,
-      &EnableFeaturesRequest<I>::handle_get_mirror_mode>(this);
-    ctx->complete(-ENOENT);
+    send_get_mirror_image();
     return;
   }
 
@@ -145,10 +143,9 @@ Context *EnableFeaturesRequest<I>::handle_get_mirror_mode(int *result) {
   CephContext *cct = image_ctx.cct;
   ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl;
 
-  cls::rbd::MirrorMode mirror_mode = cls::rbd::MIRROR_MODE_DISABLED;
   if (*result == 0) {
     auto it = m_out_bl.cbegin();
-    *result = cls_client::mirror_mode_get_finish(&it, &mirror_mode);
+    *result = cls_client::mirror_mode_get_finish(&it, &m_mirror_mode);
   } else if (*result == -ENOENT) {
     *result = 0;
   }
@@ -159,7 +156,67 @@ Context *EnableFeaturesRequest<I>::handle_get_mirror_mode(int *result) {
     return handle_finish(*result);
   }
 
-  m_enable_mirroring = (mirror_mode == cls::rbd::MIRROR_MODE_POOL);
+  if (m_mirror_mode == cls::rbd::MIRROR_MODE_POOL) {
+    m_enable_mirroring = true;
+  }
+
+  send_get_mirror_image();
+  return nullptr;
+}
+
+template <typename I>
+void EnableFeaturesRequest<I>::send_get_mirror_image() {
+  I &image_ctx = this->m_image_ctx;
+  CephContext *cct = image_ctx.cct;
+
+  if (m_mirror_mode != cls::rbd::MIRROR_MODE_IMAGE) {
+    Context *ctx = create_context_callback<
+      EnableFeaturesRequest<I>,
+      &EnableFeaturesRequest<I>::handle_get_mirror_image>(this);
+    ctx->complete(-ENOENT);
+    return;
+  }
+
+  ldout(cct, 20) << this << " " << __func__ << dendl;
+
+  librados::ObjectReadOperation op;
+  cls_client::mirror_image_get_start(&op, image_ctx.id);
+
+  using klass = EnableFeaturesRequest<I>;
+  librados::AioCompletion *comp =
+    create_rados_callback<klass, &klass::handle_get_mirror_image>(this);
+  m_out_bl.clear();
+  int r = image_ctx.md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
+  ceph_assert(r == 0);
+  comp->release();
+}
+
+template <typename I>
+Context *EnableFeaturesRequest<I>::handle_get_mirror_image(int *result) {
+  I &image_ctx = this->m_image_ctx;
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl;
+
+  cls::rbd::MirrorImage mirror_image;
+  if (*result == 0) {
+    auto it = m_out_bl.cbegin();
+    *result = cls_client::mirror_image_get_finish(&it, &mirror_image);
+  } else if (*result == -ENOENT) {
+    *result = 0;
+  }
+
+  if (*result < 0) {
+    lderr(cct) << "failed to retrieve mirror image info: "
+               << cpp_strerror(*result) << dendl;
+    return handle_finish(*result);
+  }
+
+  if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+    lderr(cct) << "cannot enable journaling: image snapshot mirroring enabled"
+               << dendl;
+    *result = -EINVAL;
+    return handle_finish(*result);
+  }
 
   bool create_journal = false;
   do {
@@ -408,13 +465,14 @@ template <typename I>
 void EnableFeaturesRequest<I>::send_enable_mirror_image() {
   I &image_ctx = this->m_image_ctx;
   CephContext *cct = image_ctx.cct;
-  ldout(cct, 20) << this << " " << __func__ << dendl;
 
   if (!m_enable_mirroring) {
     send_notify_update();
     return;
   }
 
+  ldout(cct, 20) << this << " " << __func__ << dendl;
+
   Context *ctx = create_context_callback<
     EnableFeaturesRequest<I>,
     &EnableFeaturesRequest<I>::handle_enable_mirror_image>(this);
index 1c91b4dc72a2304598afdac61cd8d1e093d3beae..d1bfe4ad4241b04fa165edf0093a616243c8135f 100644 (file)
@@ -4,6 +4,8 @@
 #ifndef CEPH_LIBRBD_OPERATION_ENABLE_FEATURES_REQUEST_H
 #define CEPH_LIBRBD_OPERATION_ENABLE_FEATURES_REQUEST_H
 
+#include "cls/rbd/cls_rbd_types.h"
+
 #include "librbd/operation/Request.h"
 
 class Context;
@@ -52,8 +54,11 @@ private:
    * STATE_BLOCK_WRITES
    *    |
    *    v
-   * STATE_GET_MIRROR_MODE
-   *    |
+   * STATE_GET_MIRROR_MODE (skip if not
+   *    |                   enabling journaling)
+   *    v
+   * STATE_GET_MIRROR_IMAGE (skip if mirror mode
+   *    |                    is not IMAGE)
    *    v
    * STATE_CREATE_JOURNAL (skip if not
    *    |                  required)
@@ -70,9 +75,9 @@ private:
    * STATE_CREATE_OBJECT_MAP (skip if not
    *    |                     required)
    *    v
-   * STATE_ENABLE_MIRROR_IMAGE
-   *    |
-   *    V
+   * STATE_ENABLE_MIRROR_IMAGE (skip if not
+   *    |                       required)
+   *    v
    * STATE_NOTIFY_UPDATE
    *    |
    *    | (unblock writes)
@@ -82,6 +87,8 @@ private:
    *
    */
 
+  cls::rbd::MirrorMode m_mirror_mode = cls::rbd::MIRROR_MODE_DISABLED;
+
   uint64_t m_features;
 
   bool m_enable_mirroring = false;
@@ -103,6 +110,9 @@ private:
   void send_get_mirror_mode();
   Context *handle_get_mirror_mode(int *result);
 
+  void send_get_mirror_image();
+  Context *handle_get_mirror_image(int *result);
+
   void send_create_journal();
   Context *handle_create_journal(int *result);
 
index 84ef7cab2c6b462a55320d2f411879abb7cc3a61..688bddb99dcc815bdfca36d89f9ebcc247c65342 100644 (file)
@@ -10,6 +10,7 @@
 #include "librbd/MirroringWatcher.h"
 #include "librbd/journal/PromoteRequest.h"
 #include "librbd/mirror/DisableRequest.h"
+#include "librbd/mirror/GetInfoRequest.h"
 
 namespace librbd {
 
@@ -22,24 +23,6 @@ struct MockTestImageCtx : public MockImageCtx {
 
 } // anonymous namespace
 
-template <>
-struct Journal<librbd::MockTestImageCtx> {
-  static Journal *s_instance;
-  static void is_tag_owner(librbd::MockTestImageCtx *, bool *is_primary,
-                           Context *on_finish) {
-    ceph_assert(s_instance != nullptr);
-    s_instance->is_tag_owner(is_primary, on_finish);
-  }
-
-  Journal() {
-    s_instance = this;
-  }
-
-  MOCK_METHOD2(is_tag_owner, void(bool*, Context*));
-};
-
-Journal<librbd::MockTestImageCtx> *Journal<librbd::MockTestImageCtx>::s_instance = nullptr;
-
 template <>
 struct MirroringWatcher<librbd::MockTestImageCtx> {
   static MirroringWatcher *s_instance;
@@ -89,20 +72,53 @@ PromoteRequest<librbd::MockTestImageCtx> *PromoteRequest<librbd::MockTestImageCt
 
 } // namespace journal
 
+namespace mirror {
+
+template <>
+struct GetInfoRequest<librbd::MockTestImageCtx> {
+  cls::rbd::MirrorImage *mirror_image;
+  PromotionState *promotion_state;
+  Context *on_finish = nullptr;
+  static GetInfoRequest *s_instance;
+  static GetInfoRequest *create(librbd::MockTestImageCtx &,
+                                cls::rbd::MirrorImage *mirror_image,
+                                PromotionState *promotion_state,
+                                Context *on_finish) {
+    ceph_assert(s_instance != nullptr);
+    s_instance->mirror_image = mirror_image;
+    s_instance->promotion_state = promotion_state;
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  GetInfoRequest() {
+    s_instance = this;
+  }
+
+  MOCK_METHOD0(send, void());
+};
+
+GetInfoRequest<librbd::MockTestImageCtx> *GetInfoRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
 } // namespace librbd
 
 // template definitions
 #include "librbd/mirror/DisableRequest.cc"
 template class librbd::mirror::DisableRequest<librbd::MockTestImageCtx>;
 
+ACTION_P(TestFeatures, image_ctx) {
+  return ((image_ctx->features & arg0) != 0);
+}
+
 namespace librbd {
 namespace mirror {
 
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::InSequence;
+using ::testing::Invoke;
 using ::testing::Return;
-using ::testing::SetArgPointee;
 using ::testing::StrEq;
 using ::testing::WithArg;
 
@@ -112,27 +128,24 @@ public:
   typedef Journal<MockTestImageCtx> MockJournal;
   typedef MirroringWatcher<MockTestImageCtx> MockMirroringWatcher;
   typedef journal::PromoteRequest<MockTestImageCtx> MockPromoteRequest;
-
-  void expect_get_mirror_image(MockTestImageCtx &mock_image_ctx,
-                               const cls::rbd::MirrorImage &mirror_image,
-                               int r) {
-    using ceph::encode;
-    bufferlist bl;
-    encode(mirror_image, bl);
-
-    EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
-                exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_image_get"),
-                     _, _, _))
-      .WillOnce(DoAll(WithArg<5>(CopyInBufferlist(bl)),
-                      Return(r)));
-  }
-
-  void expect_is_tag_owner(MockTestImageCtx &mock_image_ctx,
-                           MockJournal &mock_journal,
-                           bool is_primary, int r) {
-    EXPECT_CALL(mock_journal, is_tag_owner(_, _))
-      .WillOnce(DoAll(SetArgPointee<0>(is_primary),
-                      WithArg<1>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))));
+  typedef mirror::GetInfoRequest<MockTestImageCtx> MockGetInfoRequest;
+
+  void expect_get_mirror_info(MockTestImageCtx &mock_image_ctx,
+                              MockGetInfoRequest &mock_get_info_request,
+                              const cls::rbd::MirrorImage &mirror_image,
+                              PromotionState promotion_state, int r) {
+
+    EXPECT_CALL(mock_get_info_request, send())
+      .WillOnce(
+        Invoke([this, &mock_image_ctx, &mock_get_info_request, mirror_image,
+                promotion_state, r]() {
+                 if (r == 0) {
+                   *mock_get_info_request.mirror_image = mirror_image;
+                   *mock_get_info_request.promotion_state = promotion_state;
+                 }
+                 mock_image_ctx.op_work_queue->queue(
+                   mock_get_info_request.on_finish, r);
+               }));
   }
 
   void expect_set_mirror_image(MockTestImageCtx &mock_image_ctx, int r) {
@@ -215,7 +228,6 @@ TEST_F(TestMockMirrorDisableRequest, Success) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
   MockMirroringWatcher mock_mirroring_watcher;
 
   expect_op_work_queue(mock_image_ctx);
@@ -223,10 +235,12 @@ TEST_F(TestMockMirrorDisableRequest, Success) {
   expect_snap_remove(mock_image_ctx, "snap 2", 0);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
   expect_set_mirror_image(mock_image_ctx, 0);
   expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
                               cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
@@ -261,16 +275,17 @@ TEST_F(TestMockMirrorDisableRequest, SuccessNoRemove) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
   MockMirroringWatcher mock_mirroring_watcher;
 
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
   expect_set_mirror_image(mock_image_ctx, 0);
   expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
                               cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
@@ -290,17 +305,18 @@ TEST_F(TestMockMirrorDisableRequest, SuccessNonPrimary) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
   MockMirroringWatcher mock_mirroring_watcher;
   MockPromoteRequest mock_promote_request;
 
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, false, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
   expect_set_mirror_image(mock_image_ctx, 0);
   expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
                               cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
@@ -325,16 +341,17 @@ TEST_F(TestMockMirrorDisableRequest, NonPrimaryError) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
   MockMirroringWatcher mock_mirroring_watcher;
 
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, false, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
 
   C_SaferCond ctx;
   auto req = new MockDisableRequest(&mock_image_ctx, false, false, &ctx);
@@ -342,47 +359,28 @@ TEST_F(TestMockMirrorDisableRequest, NonPrimaryError) {
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
 
-TEST_F(TestMockMirrorDisableRequest, MirrorImageGetError) {
+TEST_F(TestMockMirrorDisableRequest, GetMirrorInfoError) {
   REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
 
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mirror_image(mock_image_ctx, {}, -EBADMSG);
-
-  C_SaferCond ctx;
-  auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
-  req->send();
-  ASSERT_EQ(-EBADMSG, ctx.wait());
-}
-
-TEST_F(TestMockMirrorDisableRequest, IsTagOwnerError) {
-  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
-
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, true, -EBADMSG);
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, -EINVAL);
 
   C_SaferCond ctx;
   auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
   req->send();
-  ASSERT_EQ(-EBADMSG, ctx.wait());
+  ASSERT_EQ(-EINVAL, ctx.wait());
 }
 
 TEST_F(TestMockMirrorDisableRequest, MirrorImageSetError) {
@@ -392,15 +390,16 @@ TEST_F(TestMockMirrorDisableRequest, MirrorImageSetError) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
 
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
   expect_set_mirror_image(mock_image_ctx, -ENOENT);
 
   C_SaferCond ctx;
@@ -416,17 +415,18 @@ TEST_F(TestMockMirrorDisableRequest, JournalPromoteError) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
   MockMirroringWatcher mock_mirroring_watcher;
   MockPromoteRequest mock_promote_request;
 
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, false, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
   expect_set_mirror_image(mock_image_ctx, 0);
   expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
                               cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
@@ -446,16 +446,17 @@ TEST_F(TestMockMirrorDisableRequest, JournalClientListError) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
   MockMirroringWatcher mock_mirroring_watcher;
 
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
   expect_set_mirror_image(mock_image_ctx, 0);
   expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
                               cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
@@ -475,7 +476,6 @@ TEST_F(TestMockMirrorDisableRequest, SnapRemoveError) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
   MockMirroringWatcher mock_mirroring_watcher;
 
   expect_op_work_queue(mock_image_ctx);
@@ -483,10 +483,12 @@ TEST_F(TestMockMirrorDisableRequest, SnapRemoveError) {
   expect_snap_remove(mock_image_ctx, "snap 2", -EPERM);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
   expect_set_mirror_image(mock_image_ctx, 0);
   expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
                               cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
@@ -515,7 +517,6 @@ TEST_F(TestMockMirrorDisableRequest, JournalClientUnregisterError) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
   MockMirroringWatcher mock_mirroring_watcher;
 
   expect_op_work_queue(mock_image_ctx);
@@ -523,10 +524,12 @@ TEST_F(TestMockMirrorDisableRequest, JournalClientUnregisterError) {
   expect_snap_remove(mock_image_ctx, "snap 2", 0);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
   expect_set_mirror_image(mock_image_ctx, 0);
   expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
                               cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
@@ -556,16 +559,17 @@ TEST_F(TestMockMirrorDisableRequest, MirrorImageRemoveError) {
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
   MockTestImageCtx mock_image_ctx(*ictx);
-  MockJournal mock_journal;
   MockMirroringWatcher mock_mirroring_watcher;
 
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mirror_image(
-    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
-                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
-  expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+  MockGetInfoRequest mock_get_info_request;
+  expect_get_mirror_info(
+    mock_image_ctx, mock_get_info_request,
+    {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
   expect_set_mirror_image(mock_image_ctx, 0);
   expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
                               cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
@@ -581,4 +585,3 @@ TEST_F(TestMockMirrorDisableRequest, MirrorImageRemoveError) {
 
 } // namespace mirror
 } // namespace librbd
-
index b437f55b7361dc364a86af343a17645951a12b26..78977ae76555afdc02b159b10501768082784a88 100644 (file)
@@ -6,6 +6,7 @@
 #include "test/librbd/mock/MockImageCtx.h"
 #include "test/librbd/mock/MockJournalPolicy.h"
 #include "cls/rbd/cls_rbd_client.h"
+#include "librbd/api/Mirror.h"
 #include "librbd/internal.h"
 #include "librbd/image/SetFlagsRequest.h"
 #include "librbd/io/AioCompletion.h"
@@ -174,20 +175,21 @@ public:
   typedef librbd::object_map::RemoveRequest<MockOperationImageCtx> MockRemoveObjectMapRequest;
   typedef DisableFeaturesRequest<MockOperationImageCtx> MockDisableFeaturesRequest;
 
-  class PoolMirrorModeEnabler {
+  class MirrorModeEnabler {
   public:
-    PoolMirrorModeEnabler(librados::IoCtx &ioctx) : m_ioctx(ioctx) {
+    MirrorModeEnabler(librados::IoCtx &ioctx, cls::rbd::MirrorMode mirror_mode)
+      : m_ioctx(ioctx), m_mirror_mode(mirror_mode) {
       EXPECT_EQ(0, librbd::cls_client::mirror_uuid_set(&m_ioctx, "test-uuid"));
-      EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
-                 &m_ioctx, cls::rbd::MIRROR_MODE_POOL));
+      EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(&m_ioctx, m_mirror_mode));
     }
 
-    ~PoolMirrorModeEnabler() {
+    ~MirrorModeEnabler() {
       EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
-               &m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
+                &m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
     }
   private:
     librados::IoCtx &m_ioctx;
+    cls::rbd::MirrorMode m_mirror_mode;
   };
 
   void expect_prepare_lock(MockOperationImageCtx &mock_image_ctx) {
@@ -432,9 +434,11 @@ TEST_F(TestMockOperationDisableFeaturesRequest, ObjectMapError) {
   ASSERT_EQ(-EINVAL, cond_ctx.wait());
 }
 
-TEST_F(TestMockOperationDisableFeaturesRequest, Mirroring) {
+TEST_F(TestMockOperationDisableFeaturesRequest, PoolMirroring) {
   REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
+  MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -475,6 +479,46 @@ TEST_F(TestMockOperationDisableFeaturesRequest, Mirroring) {
   ASSERT_EQ(0, cond_ctx.wait());
 }
 
+TEST_F(TestMockOperationDisableFeaturesRequest, ImageMirroring) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_IMAGE);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, librbd::api::Mirror<>::image_enable(ictx, false));
+
+  MockOperationImageCtx mock_image_ctx(*ictx);
+  MockExclusiveLock mock_exclusive_lock;
+  MockJournal mock_journal;
+  MockObjectMap mock_object_map;
+  initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+                     mock_object_map);
+
+  expect_verify_lock_ownership(mock_image_ctx);
+
+  MockRemoveJournalRequest mock_remove_journal_request;
+  MockDisableMirrorRequest mock_disable_mirror_request;
+
+  ::testing::InSequence seq;
+  expect_prepare_lock(mock_image_ctx);
+  expect_block_writes(mock_image_ctx);
+  expect_is_journal_replaying(*mock_image_ctx.journal);
+  expect_block_requests(mock_image_ctx);
+  expect_unblock_requests(mock_image_ctx);
+  expect_unblock_writes(mock_image_ctx);
+  expect_handle_prepare_lock_complete(mock_image_ctx);
+
+  C_SaferCond cond_ctx;
+  MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
+    mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
+  {
+    std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+    req->send();
+  }
+  ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
 TEST_F(TestMockOperationDisableFeaturesRequest, MirroringError) {
   REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
index 86458c75ac86e555b80d272360fc5b2acfa42a6e..f4c4fc5d442556bac6c6c0be73af34ba743b25c2 100644 (file)
@@ -6,9 +6,11 @@
 #include "test/librbd/mock/MockImageCtx.h"
 #include "cls/rbd/cls_rbd_client.h"
 #include "librbd/Operations.h"
+#include "librbd/api/Mirror.h"
 #include "librbd/internal.h"
 #include "librbd/image/SetFlagsRequest.h"
 #include "librbd/io/AioCompletion.h"
+#include "librbd/mirror/DisableRequest.h"
 #include "librbd/mirror/EnableRequest.h"
 #include "librbd/journal/CreateRequest.h"
 #include "librbd/journal/Types.h"
@@ -169,20 +171,21 @@ public:
   typedef librbd::object_map::CreateRequest<MockOperationImageCtx> MockCreateObjectMapRequest;
   typedef EnableFeaturesRequest<MockOperationImageCtx> MockEnableFeaturesRequest;
 
-  class PoolMirrorModeEnabler {
+  class MirrorModeEnabler {
   public:
-    PoolMirrorModeEnabler(librados::IoCtx &ioctx) : m_ioctx(ioctx) {
+    MirrorModeEnabler(librados::IoCtx &ioctx, cls::rbd::MirrorMode mirror_mode)
+      : m_ioctx(ioctx), m_mirror_mode(mirror_mode) {
       EXPECT_EQ(0, librbd::cls_client::mirror_uuid_set(&m_ioctx, "test-uuid"));
-      EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
-                  &m_ioctx, cls::rbd::MIRROR_MODE_POOL));
+      EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(&m_ioctx, m_mirror_mode));
     }
 
-    ~PoolMirrorModeEnabler() {
+    ~MirrorModeEnabler() {
       EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
                 &m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
     }
   private:
     librados::IoCtx &m_ioctx;
+    cls::rbd::MirrorMode m_mirror_mode;
   };
 
   void ensure_features_disabled(librbd::ImageCtx *ictx,
@@ -482,9 +485,11 @@ TEST_F(TestMockOperationEnableFeaturesRequest, SetFlagsError) {
   ASSERT_EQ(-EINVAL, cond_ctx.wait());
 }
 
-TEST_F(TestMockOperationEnableFeaturesRequest, Mirroring) {
+TEST_F(TestMockOperationEnableFeaturesRequest, PoolMirroring) {
   REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
+  MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -493,8 +498,6 @@ TEST_F(TestMockOperationEnableFeaturesRequest, Mirroring) {
 
   ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
 
-  PoolMirrorModeEnabler enabler(m_ioctx);
-
   MockOperationImageCtx mock_image_ctx(*ictx);
   MockExclusiveLock mock_exclusive_lock;
   MockJournal mock_journal;
@@ -530,9 +533,53 @@ TEST_F(TestMockOperationEnableFeaturesRequest, Mirroring) {
   ASSERT_EQ(0, cond_ctx.wait());
 }
 
+TEST_F(TestMockOperationEnableFeaturesRequest, ImageMirroring) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
+
+  MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_IMAGE);
+
+  ASSERT_EQ(0, librbd::api::Mirror<>::image_enable(ictx, false)); // snapshot mode
+
+  uint64_t features;
+  ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+  MockOperationImageCtx mock_image_ctx(*ictx);
+  MockExclusiveLock mock_exclusive_lock;
+  MockJournal mock_journal;
+  MockObjectMap mock_object_map;
+  initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+                      mock_object_map);
+
+  expect_verify_lock_ownership(mock_image_ctx);
+
+  MockCreateJournalRequest mock_create_journal_request;
+  MockEnableMirrorRequest mock_enable_mirror_request;
+
+  ::testing::InSequence seq;
+  expect_prepare_lock(mock_image_ctx);
+  expect_block_writes(mock_image_ctx);
+  expect_unblock_writes(mock_image_ctx);
+  expect_handle_prepare_lock_complete(mock_image_ctx);
+
+  C_SaferCond cond_ctx;
+  MockEnableFeaturesRequest *req = new MockEnableFeaturesRequest(
+    mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING);
+  {
+    std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+    req->send();
+  }
+  ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
 TEST_F(TestMockOperationEnableFeaturesRequest, JournalingError) {
   REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
+  MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -541,8 +588,6 @@ TEST_F(TestMockOperationEnableFeaturesRequest, JournalingError) {
 
   ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
 
-  PoolMirrorModeEnabler enabler(m_ioctx);
-
   MockOperationImageCtx mock_image_ctx(*ictx);
   MockExclusiveLock mock_exclusive_lock;
   MockJournal mock_journal;
@@ -553,7 +598,6 @@ TEST_F(TestMockOperationEnableFeaturesRequest, JournalingError) {
   expect_verify_lock_ownership(mock_image_ctx);
 
   MockCreateJournalRequest mock_create_journal_request;
-  MockEnableMirrorRequest mock_enable_mirror_request;
 
   ::testing::InSequence seq;
   expect_prepare_lock(mock_image_ctx);
@@ -578,6 +622,8 @@ TEST_F(TestMockOperationEnableFeaturesRequest, JournalingError) {
 TEST_F(TestMockOperationEnableFeaturesRequest, MirroringError) {
   REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
+  MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -586,8 +632,6 @@ TEST_F(TestMockOperationEnableFeaturesRequest, MirroringError) {
 
   ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
 
-  PoolMirrorModeEnabler enabler(m_ioctx);
-
   MockOperationImageCtx mock_image_ctx(*ictx);
   MockExclusiveLock mock_exclusive_lock;
   MockJournal mock_journal;
index 4970f675821f9c04d82cf6f40b9ebdb4abcc61d8..fab41185dafe6ae3a5e364f7360938f26bb3b7b2 100644 (file)
@@ -92,10 +92,9 @@ public:
     return 0;
   }
 
-  void check_mirror_image_enable(rbd_mirror_mode_t mirror_mode,
-                                 uint64_t features,
-                                 int expected_r,
-                                 rbd_mirror_image_state_t mirror_state) {
+  void check_mirror_image_enable(
+      rbd_mirror_mode_t mirror_mode, uint64_t features, int expected_r,
+      rbd_mirror_image_state_t mirror_state) {
 
     ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
 
@@ -157,14 +156,6 @@ public:
     ASSERT_EQ(mirror_state == RBD_MIRROR_IMAGE_ENABLED ? -ENOENT : -EINVAL,
               image.mirror_image_get_instance_id(&instance_id));
 
-    if (mirror_mode == RBD_MIRROR_MODE_IMAGE &&
-        mirror_state == RBD_MIRROR_IMAGE_DISABLED) {
-      // disabling image mirroring automatically disables journaling feature
-      uint64_t new_features;
-      ASSERT_EQ(0, image.features(&new_features));
-      ASSERT_EQ(0, new_features & RBD_FEATURE_JOURNALING);
-    }
-
     ASSERT_EQ(0, image.close());
     ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
     ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
@@ -232,11 +223,10 @@ public:
     ASSERT_EQ(mirror_images_new_count, mirror_images_count);
   }
 
-  void check_mirroring_on_update_features(uint64_t init_features,
-                                 bool enable, bool enable_mirroring,
-                                 uint64_t features, int expected_r,
-                                 rbd_mirror_mode_t mirror_mode,
-                                 rbd_mirror_image_state_t mirror_state) {
+  void check_mirroring_on_update_features(
+      uint64_t init_features, bool enable, bool enable_mirroring,
+      uint64_t features, int expected_r, rbd_mirror_mode_t mirror_mode,
+      rbd_mirror_image_state_t mirror_state) {
 
     ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
 
@@ -633,6 +623,15 @@ TEST_F(TestMirroring, EnableJournaling_In_MirrorModeImage) {
                       RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_DISABLED);
 }
 
+TEST_F(TestMirroring, EnableJournaling_In_MirrorModeImage_MirroringEnabled) {
+  uint64_t init_features = 0;
+  init_features |= RBD_FEATURE_OBJECT_MAP;
+  init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+  uint64_t features = RBD_FEATURE_JOURNALING;
+  check_mirroring_on_update_features(init_features, true, true, features,
+                      -EINVAL, RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_ENABLED);
+}
+
 TEST_F(TestMirroring, EnableJournaling_In_MirrorModePool) {
   uint64_t init_features = 0;
   init_features |= RBD_FEATURE_OBJECT_MAP;
@@ -658,8 +657,8 @@ TEST_F(TestMirroring, DisableJournaling_In_MirrorModeImage) {
   init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
   init_features |= RBD_FEATURE_JOURNALING;
   uint64_t features = RBD_FEATURE_JOURNALING;
-  check_mirroring_on_update_features(init_features, false, true, features, -EINVAL,
-                      RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_ENABLED);
+  check_mirroring_on_update_features(init_features, false, true, features,
+                      -EINVAL, RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_ENABLED);
 }
 
 TEST_F(TestMirroring, MirrorModeSet_DisabledMode_To_PoolMode) {
index 341c6373f610cb45185befdadbd29d53d9629a6e..5f6bb43a0d1855293a0fec7d6100dc7195f8465c 100644 (file)
@@ -3,12 +3,12 @@
 
 #include "test/rbd_mirror/test_mock_fixture.h"
 #include "librbd/journal/TypeTraits.h"
+#include "librbd/mirror/GetInfoRequest.h"
 #include "tools/rbd_mirror/InstanceWatcher.h"
 #include "tools/rbd_mirror/Threads.h"
 #include "tools/rbd_mirror/image_replayer/BootstrapRequest.h"
 #include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
 #include "tools/rbd_mirror/image_replayer/CreateImageRequest.h"
-#include "tools/rbd_mirror/image_replayer/IsPrimaryRequest.h"
 #include "tools/rbd_mirror/image_replayer/OpenImageRequest.h"
 #include "tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h"
 #include "test/journal/mock/MockJournaler.h"
@@ -37,6 +37,41 @@ struct TypeTraits<librbd::MockTestImageCtx> {
 
 } // namespace journal
 
+namespace mirror {
+
+template<>
+struct GetInfoRequest<librbd::MockTestImageCtx> {
+  static GetInfoRequest* s_instance;
+  cls::rbd::MirrorImage *mirror_image;
+  PromotionState *promotion_state;
+  Context *on_finish = nullptr;
+
+  static GetInfoRequest* create(librbd::MockTestImageCtx &image_ctx,
+                                cls::rbd::MirrorImage *mirror_image,
+                                PromotionState *promotion_state,
+                                Context *on_finish) {
+    ceph_assert(s_instance != nullptr);
+    s_instance->mirror_image = mirror_image;
+    s_instance->promotion_state = promotion_state;
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  GetInfoRequest() {
+    ceph_assert(s_instance == nullptr);
+    s_instance = this;
+  }
+  ~GetInfoRequest() {
+    s_instance = nullptr;
+  }
+
+  MOCK_METHOD0(send, void());
+};
+
+GetInfoRequest<librbd::MockTestImageCtx>* GetInfoRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+
 namespace util {
 
 static std::string s_image_id;
@@ -166,31 +201,6 @@ struct CreateImageRequest<librbd::MockTestImageCtx> {
   MOCK_METHOD0(send, void());
 };
 
-template<>
-struct IsPrimaryRequest<librbd::MockTestImageCtx> {
-  static IsPrimaryRequest* s_instance;
-  bool *primary = nullptr;
-  Context *on_finish = nullptr;
-
-  static IsPrimaryRequest* create(librbd::MockTestImageCtx *image_ctx,
-                                  bool *primary, Context *on_finish) {
-    ceph_assert(s_instance != nullptr);
-    s_instance->primary = primary;
-    s_instance->on_finish = on_finish;
-    return s_instance;
-  }
-
-  IsPrimaryRequest() {
-    ceph_assert(s_instance == nullptr);
-    s_instance = this;
-  }
-  ~IsPrimaryRequest() {
-    s_instance = nullptr;
-  }
-
-  MOCK_METHOD0(send, void());
-};
-
 template<>
 struct OpenImageRequest<librbd::MockTestImageCtx> {
   static OpenImageRequest* s_instance;
@@ -256,8 +266,6 @@ CloseImageRequest<librbd::MockTestImageCtx>*
   CloseImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
 CreateImageRequest<librbd::MockTestImageCtx>*
   CreateImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
-IsPrimaryRequest<librbd::MockTestImageCtx>*
-  IsPrimaryRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
 OpenImageRequest<librbd::MockTestImageCtx>*
   OpenImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
 OpenLocalImageRequest<librbd::MockTestImageCtx>*
@@ -295,9 +303,9 @@ public:
   typedef CreateImageRequest<librbd::MockTestImageCtx> MockCreateImageRequest;
   typedef ImageSync<librbd::MockTestImageCtx> MockImageSync;
   typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
-  typedef IsPrimaryRequest<librbd::MockTestImageCtx> MockIsPrimaryRequest;
   typedef OpenImageRequest<librbd::MockTestImageCtx> MockOpenImageRequest;
   typedef OpenLocalImageRequest<librbd::MockTestImageCtx> MockOpenLocalImageRequest;
+  typedef librbd::mirror::GetInfoRequest<librbd::MockTestImageCtx> MockGetMirrorInfoRequest;
   typedef std::list<cls::journal::Tag> Tags;
 
   void SetUp() override {
@@ -404,12 +412,17 @@ public:
         }));
   }
 
-  void expect_is_primary(MockIsPrimaryRequest &mock_is_primary_request,
-                        bool primary, int r) {
-    EXPECT_CALL(mock_is_primary_request, send())
-      .WillOnce(Invoke([this, &mock_is_primary_request, primary, r]() {
-          *mock_is_primary_request.primary = primary;
-          m_threads->work_queue->queue(mock_is_primary_request.on_finish, r);
+  void expect_get_remote_mirror_info(
+      MockGetMirrorInfoRequest &mock_get_mirror_info_request,
+      const cls::rbd::MirrorImage &mirror_image,
+      librbd::mirror::PromotionState promotion_state, int r) {
+    EXPECT_CALL(mock_get_mirror_info_request, send())
+      .WillOnce(Invoke([this, &mock_get_mirror_info_request, mirror_image,
+                        promotion_state, r]() {
+          *mock_get_mirror_info_request.mirror_image = mirror_image;
+          *mock_get_mirror_info_request.promotion_state = promotion_state;
+          m_threads->work_queue->queue(
+            mock_get_mirror_info_request.on_finish, r);
         }));
   }
 
@@ -508,8 +521,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, NonPrimaryRemoteSyncingState) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, false, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_NON_PRIMARY, 0);
 
   // switch the state to replaying
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
@@ -559,8 +575,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, NonPrimaryRemoteNotTagOwner) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, false, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_NON_PRIMARY, 0);
 
   // open the local image
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
@@ -618,8 +637,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, RemoteDemotePromote) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, false, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_NON_PRIMARY, 0);
 
   // open the local image
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
@@ -691,8 +713,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, MultipleRemoteDemotePromotes) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, true, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
 
   // open the local image
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
@@ -774,8 +799,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, LocalDemoteRemotePromote) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, true, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
 
   // open the local image
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
@@ -845,8 +873,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, SplitBrainForcePromote) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, true, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
 
   // open the local image
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
@@ -916,8 +947,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, ResyncRequested) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, true, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
 
   // open the local image
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
@@ -976,8 +1010,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, PrimaryRemote) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, true, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
 
   // update client state back to syncing
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
@@ -1049,8 +1086,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, PrimaryRemoteLocalDeleted) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, true, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
 
   // open the missing local image
   MockOpenLocalImageRequest mock_open_local_image_request;
@@ -1066,7 +1106,10 @@ TEST_F(TestMockImageReplayerBootstrapRequest, PrimaryRemoteLocalDeleted) {
   expect_journaler_register_client(mock_journaler, client_data, 0);
 
   // test if remote image is primary
-  expect_is_primary(mock_is_primary_request, true, 0);
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
 
   // update client state back to syncing
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
@@ -1136,8 +1179,11 @@ TEST_F(TestMockImageReplayerBootstrapRequest, LocalImageIdCollision) {
                     mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
 
   // test if remote image is primary
-  MockIsPrimaryRequest mock_is_primary_request;
-  expect_is_primary(mock_is_primary_request, true, 0);
+  MockGetMirrorInfoRequest mock_get_mirror_info_request;
+  expect_get_remote_mirror_info(mock_get_mirror_info_request,
+                                {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+                                 cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+                                librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
 
   // update client state back to syncing
   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
index e5b4fd681e210c30e82bba0fb0aaa88a859eb3ea..d38353bcf1788f358aee737d272119fb35a216b7 100644 (file)
@@ -37,7 +37,6 @@ set(rbd_mirror_internal
   image_replayer/CreateImageRequest.cc
   image_replayer/EventPreprocessor.cc
   image_replayer/GetMirrorImageIdRequest.cc
-  image_replayer/IsPrimaryRequest.cc
   image_replayer/OpenImageRequest.cc
   image_replayer/OpenLocalImageRequest.cc
   image_replayer/PrepareLocalImageRequest.cc
index dd66f9d964a10cf1125c3641a2d4d43cd0e56f25..6b1ea3bcf3f3f15352ff014234439abb1c09c2fc 100644 (file)
@@ -5,7 +5,6 @@
 #include "BootstrapRequest.h"
 #include "CloseImageRequest.h"
 #include "CreateImageRequest.h"
-#include "IsPrimaryRequest.h"
 #include "OpenImageRequest.h"
 #include "OpenLocalImageRequest.h"
 #include "common/debug.h"
@@ -20,6 +19,7 @@
 #include "librbd/Journal.h"
 #include "librbd/Utils.h"
 #include "librbd/journal/Types.h"
+#include "librbd/mirror/GetInfoRequest.h"
 #include "tools/rbd_mirror/ProgressContext.h"
 #include "tools/rbd_mirror/ImageSync.h"
 #include "tools/rbd_mirror/Threads.h"
@@ -173,25 +173,25 @@ void BootstrapRequest<I>::handle_open_remote_image(int r) {
     return;
   }
 
-  is_primary();
+  get_remote_mirror_info();
 }
 
 template <typename I>
-void BootstrapRequest<I>::is_primary() {
+void BootstrapRequest<I>::get_remote_mirror_info() {
   dout(15) << dendl;
 
-  update_progress("OPEN_REMOTE_IMAGE");
+  update_progress("GET_REMOTE_MIRROR_INFO");
 
   Context *ctx = create_context_callback<
-    BootstrapRequest<I>, &BootstrapRequest<I>::handle_is_primary>(
+    BootstrapRequest<I>, &BootstrapRequest<I>::handle_get_remote_mirror_info>(
       this);
-  IsPrimaryRequest<I> *request = IsPrimaryRequest<I>::create(m_remote_image_ctx,
-                                                             &m_primary, ctx);
+  auto request = librbd::mirror::GetInfoRequest<I>::create(
+    *m_remote_image_ctx, &m_mirror_image, &m_promotion_state, ctx);
   request->send();
 }
 
 template <typename I>
-void BootstrapRequest<I>::handle_is_primary(int r) {
+void BootstrapRequest<I>::handle_get_remote_mirror_info(int r) {
   dout(15) << "r=" << r << dendl;
 
   if (r == -ENOENT) {
@@ -207,7 +207,22 @@ void BootstrapRequest<I>::handle_is_primary(int r) {
     return;
   }
 
-  if (!m_primary) {
+  if (m_mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLING) {
+    dout(5) << "remote image mirroring is being disabled" << dendl;
+    m_ret_val = -EREMOTEIO;
+    close_remote_image();
+    return;
+  }
+
+  if (m_mirror_image.mode != cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+    dout(5) << ": remote image is in unsupported mode: " << m_mirror_image.mode
+            << dendl;
+    m_ret_val = -EOPNOTSUPP;
+    close_remote_image();
+    return;
+  }
+
+  if (m_promotion_state != librbd::mirror::PROMOTION_STATE_PRIMARY) {
     if (m_local_image_id.empty()) {
       // no local image and remote isn't primary -- don't sync it
       dout(5) << "remote image is not primary -- not syncing"
@@ -338,7 +353,8 @@ void BootstrapRequest<I>::handle_open_local_image(int r) {
     local_image_ctx->image_lock.unlock_shared();
   }
 
-  if (m_local_tag_data.mirror_uuid != m_remote_mirror_uuid && !m_primary) {
+  if (m_local_tag_data.mirror_uuid != m_remote_mirror_uuid &&
+      m_promotion_state != librbd::mirror::PROMOTION_STATE_PRIMARY) {
     // if the local mirror is not linked to the (now) non-primary image,
     // stop the replay. Otherwise, we ignore that the remote is non-primary
     // so that we can replay the demotion
@@ -428,7 +444,7 @@ void BootstrapRequest<I>::handle_register_client(int r) {
   *m_client_meta = librbd::journal::MirrorPeerClientMeta();
   m_client_meta->state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
 
-  is_primary();
+  get_remote_mirror_info();
 }
 
 template <typename I>
index cc55040a825e8878fb26d5f3a0b66adf38405ddc..93d9c4c75a8991b5ba898961ca827b63463ea698 100644 (file)
@@ -8,8 +8,10 @@
 #include "include/rados/librados.hpp"
 #include "common/ceph_mutex.h"
 #include "cls/journal/cls_journal_types.h"
+#include "cls/rbd/cls_rbd_types.h"
 #include "librbd/journal/Types.h"
 #include "librbd/journal/TypeTraits.h"
+#include "librbd/mirror/Types.h"
 #include "tools/rbd_mirror/BaseRequest.h"
 #include "tools/rbd_mirror/Types.h"
 #include <list>
@@ -101,7 +103,7 @@ private:
    *    |                                                   *
    *    |/--------------------------------------------------*---\
    *    v                                                   *   |
-   * IS_PRIMARY * * * * * * * * * * * * * * * * * * * * *   *   |
+   * GET_REMOTE_MIRROR_INFO * * * * * * * * * * * * * * *   *   |
    *    |                                               *   *   |
    *    | (remote image primary, no local image id)     *   *   |
    *    \----> UPDATE_CLIENT_IMAGE  * * * * * * * * * * *   *   |
@@ -169,7 +171,9 @@ private:
   cls::journal::Client m_client;
   uint64_t m_remote_tag_class = 0;
   ImageCtxT *m_remote_image_ctx = nullptr;
-  bool m_primary = false;
+  cls::rbd::MirrorImage m_mirror_image;
+  librbd::mirror::PromotionState m_promotion_state =
+    librbd::mirror::PROMOTION_STATE_NON_PRIMARY;
   int m_ret_val = 0;
   ImageSync<ImageCtxT> *m_image_sync = nullptr;
 
@@ -184,8 +188,8 @@ private:
   void open_remote_image();
   void handle_open_remote_image(int r);
 
-  void is_primary();
-  void handle_is_primary(int r);
+  void get_remote_mirror_info();
+  void handle_get_remote_mirror_info(int r);
 
   void update_client_state();
   void handle_update_client_state(int r);
diff --git a/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.cc b/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.cc
deleted file mode 100644 (file)
index 67cfade..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-
-#include "IsPrimaryRequest.h"
-#include "common/debug.h"
-#include "common/errno.h"
-#include "common/WorkQueue.h"
-#include "cls/rbd/cls_rbd_client.h"
-#include "librbd/ImageCtx.h"
-#include "librbd/Journal.h"
-#include "librbd/Utils.h"
-#include <type_traits>
-
-#define dout_context g_ceph_context
-#define dout_subsys ceph_subsys_rbd_mirror
-#undef dout_prefix
-#define dout_prefix *_dout << "rbd::mirror::image_replayer::IsPrimaryRequest: " \
-                           << this << " " << __func__ << " "
-
-namespace rbd {
-namespace mirror {
-namespace image_replayer {
-
-using librbd::util::create_context_callback;
-using librbd::util::create_rados_callback;
-
-template <typename I>
-IsPrimaryRequest<I>::IsPrimaryRequest(I *image_ctx, bool *primary,
-                                      Context *on_finish)
-  : m_image_ctx(image_ctx), m_primary(primary), m_on_finish(on_finish) {
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::send() {
-  send_get_mirror_state();
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::send_get_mirror_state() {
-  dout(20) << dendl;
-
-  librados::ObjectReadOperation op;
-  librbd::cls_client::mirror_image_get_start(&op, m_image_ctx->id);
-
-  librados::AioCompletion *aio_comp = create_rados_callback<
-    IsPrimaryRequest<I>, &IsPrimaryRequest<I>::handle_get_mirror_state>(this);
-  int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, aio_comp, &op,
-                                          &m_out_bl);
-  ceph_assert(r == 0);
-  aio_comp->release();
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::handle_get_mirror_state(int r) {
-  dout(20) << ": r=" << r << dendl;
-
-  cls::rbd::MirrorImage mirror_image;
-  if (r == 0) {
-    auto iter = m_out_bl.cbegin();
-    r = librbd::cls_client::mirror_image_get_finish(&iter, &mirror_image);
-    if (r == 0) {
-      if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
-        if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
-          send_is_tag_owner();
-        } else if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
-          // TODO: get primary state from mirroring snapshots
-          ceph_abort();
-          finish(0);
-        }
-        return;
-      } else if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLING) {
-        dout(5) << ": image mirroring is being disabled" << dendl;
-        r = -ENOENT;
-      } else {
-        derr << ": image mirroring is disabled" << dendl;
-        r = -EINVAL;
-      }
-    } else {
-      derr << ": failed to decode image mirror state: " << cpp_strerror(r)
-           << dendl;
-    }
-  } else if (r == -ENOENT) {
-    dout(5) << ": image is not mirrored" << dendl;
-  } else {
-    derr << ": failed to retrieve image mirror state: " << cpp_strerror(r)
-         << dendl;
-  }
-
-  finish(r);
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::send_is_tag_owner() {
-  // deduce the class type for the journal to support unit tests
-  using Journal = typename std::decay<
-    typename std::remove_pointer<decltype(std::declval<I>().journal)>
-    ::type>::type;
-
-  dout(20) << dendl;
-
-  Context *ctx = create_context_callback<
-    IsPrimaryRequest<I>, &IsPrimaryRequest<I>::handle_is_tag_owner>(this);
-
-  Journal::is_tag_owner(m_image_ctx, m_primary, ctx);
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::handle_is_tag_owner(int r) {
-  dout(20) << ": r=" << r << dendl;
-
-  if (r < 0) {
-    derr << ": failed to query remote image tag owner: " << cpp_strerror(r)
-         << dendl;
-  }
-
-  finish(r);
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::finish(int r) {
-  dout(20) << ": r=" << r << dendl;
-
-  m_on_finish->complete(r);
-  delete this;
-}
-
-} // namespace image_replayer
-} // namespace mirror
-} // namespace rbd
-
-template class rbd::mirror::image_replayer::IsPrimaryRequest<librbd::ImageCtx>;
diff --git a/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.h b/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.h
deleted file mode 100644 (file)
index ddb332c..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-
-#ifndef RBD_MIRROR_IMAGE_REPLAYER_IS_PRIMARY_REQUEST_H
-#define RBD_MIRROR_IMAGE_REPLAYER_IS_PRIMARY_REQUEST_H
-
-#include "include/buffer.h"
-
-class Context;
-class ContextWQ;
-namespace librbd { class ImageCtx; }
-
-namespace rbd {
-namespace mirror {
-namespace image_replayer {
-
-template <typename ImageCtxT = librbd::ImageCtx>
-class IsPrimaryRequest {
-public:
-  static IsPrimaryRequest* create(ImageCtxT *image_ctx, bool *primary,
-                                  Context *on_finish) {
-    return new IsPrimaryRequest(image_ctx, primary, on_finish);
-  }
-
-  IsPrimaryRequest(ImageCtxT *image_ctx, bool *primary, Context *on_finish);
-
-  void send();
-
-private:
-  /**
-   * @verbatim
-   *
-   * <start>
-   *    |
-   *    v
-   * GET_MIRROR_STATE * * * * *
-   *    |                     *
-   *    v                     *
-   * IS_TAG_OWNER * * * * * * * (error)
-   *    |                     *
-   *    v                     *
-   * <finish> < * * * * * * * *
-   *
-   * @endverbatim
-   */
-  ImageCtxT *m_image_ctx;
-  bool *m_primary;
-  Context *m_on_finish;
-
-  bufferlist m_out_bl;
-
-  void send_get_mirror_state();
-  void handle_get_mirror_state(int r);
-
-  void send_is_tag_owner();
-  void handle_is_tag_owner(int r);
-
-  void finish(int r);
-};
-
-} // namespace image_replayer
-} // namespace mirror
-} // namespace rbd
-
-extern template class rbd::mirror::image_replayer::IsPrimaryRequest<librbd::ImageCtx>;
-
-#endif // RBD_MIRROR_IMAGE_REPLAYER_IS_PRIMARY_REQUEST_H
index f0f0e3e525291e146e4a94ac5248d8033c566d11..a4969ef6ec1225dc3559b4b3b7a9b407b268b5dd 100644 (file)
@@ -3,7 +3,6 @@
 
 #include "include/compat.h"
 #include "CloseImageRequest.h"
-#include "IsPrimaryRequest.h"
 #include "OpenLocalImageRequest.h"
 #include "common/debug.h"
 #include "common/errno.h"
@@ -15,6 +14,7 @@
 #include "librbd/Utils.h"
 #include "librbd/exclusive_lock/Policy.h"
 #include "librbd/journal/Policy.h"
+#include "librbd/mirror/GetInfoRequest.h"
 #include <type_traits>
 
 #define dout_context g_ceph_context
@@ -146,23 +146,24 @@ void OpenLocalImageRequest<I>::handle_open_image(int r) {
     return;
   }
 
-  send_is_primary();
+  send_get_mirror_info();
 }
 
 template <typename I>
-void OpenLocalImageRequest<I>::send_is_primary() {
+void OpenLocalImageRequest<I>::send_get_mirror_info() {
   dout(20) << dendl;
 
   Context *ctx = create_context_callback<
-    OpenLocalImageRequest<I>, &OpenLocalImageRequest<I>::handle_is_primary>(
+    OpenLocalImageRequest<I>,
+    &OpenLocalImageRequest<I>::handle_get_mirror_info>(
       this);
-  IsPrimaryRequest<I> *request = IsPrimaryRequest<I>::create(*m_local_image_ctx,
-                                                             &m_primary, ctx);
+  auto request = librbd::mirror::GetInfoRequest<I>::create(
+    **m_local_image_ctx, &m_mirror_image, &m_promotion_state, ctx);
   request->send();
 }
 
 template <typename I>
-void OpenLocalImageRequest<I>::handle_is_primary(int r) {
+void OpenLocalImageRequest<I>::handle_get_mirror_info(int r) {
   dout(20) << ": r=" << r << dendl;
 
   if (r == -ENOENT) {
@@ -176,9 +177,22 @@ void OpenLocalImageRequest<I>::handle_is_primary(int r) {
     return;
   }
 
+  if (m_mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLING) {
+    dout(5) << ": local image mirroring is being disabled" << dendl;
+    send_close_image(-ENOENT);
+    return;
+  }
+
+  if (m_mirror_image.mode != cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+    dout(5) << ": local image is in unsupported mode: " << m_mirror_image.mode
+            << dendl;
+    send_close_image(-EOPNOTSUPP);
+    return;
+  }
+
   // if the local image owns the tag -- don't steal the lock since
   // we aren't going to mirror peer data into this image anyway
-  if (m_primary) {
+  if (m_promotion_state == librbd::mirror::PROMOTION_STATE_PRIMARY) {
     dout(10) << ": local image is primary -- skipping image replay" << dendl;
     send_close_image(-EREMOTEIO);
     return;
index 58de545fb3a20724920b9e84575fad9bbf4e3633..7acf349046b63bfbd8d691cfe24606a6a863ebf5 100644 (file)
@@ -5,7 +5,9 @@
 #define RBD_MIRROR_IMAGE_REPLAYER_OPEN_LOCAL_IMAGE_REQUEST_H
 
 #include "include/int_types.h"
+#include "cls/rbd/cls_rbd_types.h"
 #include "librbd/ImageCtx.h"
+#include "librbd/mirror/Types.h"
 #include <string>
 
 class Context;
@@ -46,7 +48,7 @@ private:
    * OPEN_IMAGE * * * * * * * *
    *    |                     *
    *    v                     *
-   * IS_PRIMARY * * * * * * * *
+   * GET_MIRROR_INFO  * * * * *
    *    |                     *
    *    v (skip if primary)   v
    * LOCK_IMAGE * * * > CLOSE_IMAGE
@@ -62,14 +64,16 @@ private:
   ContextWQ *m_work_queue;
   Context *m_on_finish;
 
-  bool m_primary = false;
+  cls::rbd::MirrorImage m_mirror_image;
+  librbd::mirror::PromotionState m_promotion_state =
+    librbd::mirror::PROMOTION_STATE_NON_PRIMARY;
   int m_ret_val = 0;
 
   void send_open_image();
   void handle_open_image(int r);
 
-  void send_is_primary();
-  void handle_is_primary(int r);
+  void send_get_mirror_info();
+  void handle_get_mirror_info(int r);
 
   void send_lock_image();
   void handle_lock_image(int r);