]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd-mirror: async mirror image refresh state machine
authorJason Dillaman <dillaman@redhat.com>
Tue, 22 Nov 2016 20:11:02 +0000 (15:11 -0500)
committerJason Dillaman <dillaman@redhat.com>
Tue, 14 Feb 2017 22:47:44 +0000 (17:47 -0500)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/test/rbd_mirror/CMakeLists.txt
src/test/rbd_mirror/pool_watcher/test_mock_RefreshImagesRequest.cc [new file with mode: 0644]
src/test/rbd_mirror/test_PoolWatcher.cc
src/tools/rbd_mirror/CMakeLists.txt
src/tools/rbd_mirror/PoolWatcher.cc
src/tools/rbd_mirror/PoolWatcher.h
src/tools/rbd_mirror/Replayer.cc
src/tools/rbd_mirror/Replayer.h
src/tools/rbd_mirror/pool_watcher/RefreshImagesRequest.cc [new file with mode: 0644]
src/tools/rbd_mirror/pool_watcher/RefreshImagesRequest.h [new file with mode: 0644]
src/tools/rbd_mirror/types.h

index 259b2db6fa332b8b5b5251dd8652c3d30406db20..b815780650a329d2d0bfd7b26544b61ec234016b 100644 (file)
@@ -27,6 +27,7 @@ add_executable(unittest_rbd_mirror
   image_sync/test_mock_SnapshotCreateRequest.cc
   image_sync/test_mock_SyncPointCreateRequest.cc
   image_sync/test_mock_SyncPointPruneRequest.cc
+  pool_watcher/test_mock_RefreshImagesRequest.cc
   )
 add_ceph_unittest(unittest_rbd_mirror ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_rbd_mirror)
 set_target_properties(unittest_rbd_mirror PROPERTIES COMPILE_FLAGS
diff --git a/src/test/rbd_mirror/pool_watcher/test_mock_RefreshImagesRequest.cc b/src/test/rbd_mirror/pool_watcher/test_mock_RefreshImagesRequest.cc
new file mode 100644 (file)
index 0000000..4e29dba
--- /dev/null
@@ -0,0 +1,151 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "tools/rbd_mirror/pool_watcher/RefreshImagesRequest.h"
+#include "include/stringify.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public librbd::MockImageCtx {
+  MockTestImageCtx(librbd::ImageCtx &image_ctx)
+    : librbd::MockImageCtx(image_ctx) {
+  }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "tools/rbd_mirror/pool_watcher/RefreshImagesRequest.cc"
+template class rbd::mirror::pool_watcher::RefreshImagesRequest<librbd::MockTestImageCtx>;
+
+namespace rbd {
+namespace mirror {
+namespace pool_watcher {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockPoolWatcherRefreshImagesRequest : public TestMockFixture {
+public:
+  typedef RefreshImagesRequest<librbd::MockTestImageCtx> MockRefreshImagesRequest;
+
+  void expect_mirror_image_list(librados::IoCtx &io_ctx,
+                                const std::map<std::string, std::string> &ids,
+                                int r) {
+    bufferlist bl;
+    ::encode(ids, bl);
+
+    EXPECT_CALL(get_mock_io_ctx(io_ctx),
+                exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_image_list"), _, _, _))
+      .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
+                                          *out_bl = bl;
+                                        })),
+                      Return(r)));
+  }
+
+  void expect_dir_list(librados::IoCtx &io_ctx,
+                       const std::map<std::string, std::string> &ids, int r) {
+    bufferlist bl;
+    ::encode(ids, bl);
+
+    EXPECT_CALL(get_mock_io_ctx(io_ctx),
+                exec(RBD_DIRECTORY, _, StrEq("rbd"), StrEq("dir_list"), _, _, _))
+      .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
+                                          *out_bl = bl;
+                                        })),
+                      Return(r)));
+  }
+};
+
+TEST_F(TestMockPoolWatcherRefreshImagesRequest, Success) {
+  InSequence seq;
+  expect_mirror_image_list(m_remote_io_ctx, {{"local id", "global id"}}, 0);
+  expect_dir_list(m_remote_io_ctx, {{"image name", "local id"}}, 0);
+
+  C_SaferCond ctx;
+  ImageIds image_ids;
+  MockRefreshImagesRequest *req = new MockRefreshImagesRequest(
+    m_remote_io_ctx, &image_ids, &ctx);
+
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+
+  ImageIds expected_image_ids = {{"global id", "local id",
+                                  boost::optional<std::string>{"image name"}}};
+  ASSERT_EQ(expected_image_ids, image_ids);
+}
+
+TEST_F(TestMockPoolWatcherRefreshImagesRequest, LargeDirectory) {
+  InSequence seq;
+  std::map<std::string, std::string> mirror_list;
+  std::map<std::string, std::string> dir_list;
+  ImageIds expected_image_ids;
+  for (uint32_t idx = 1; idx <= 1024; ++idx) {
+    mirror_list.insert(std::make_pair("local id " + stringify(idx),
+                                      "global id " + stringify(idx)));
+    dir_list.insert(std::make_pair("image " + stringify(idx),
+                                   "local id " + stringify(idx)));
+    expected_image_ids.insert({{"global id " + stringify(idx),
+                                "local id " + stringify(idx),
+                                "image " + stringify(idx)}});
+  }
+
+  expect_mirror_image_list(m_remote_io_ctx, mirror_list, 0);
+  expect_mirror_image_list(m_remote_io_ctx, {{"local id", "global id"}}, 0);
+  expect_dir_list(m_remote_io_ctx, dir_list, 0);
+  expect_dir_list(m_remote_io_ctx, {{"image name", "local id"}}, 0);
+
+  C_SaferCond ctx;
+  ImageIds image_ids;
+  MockRefreshImagesRequest *req = new MockRefreshImagesRequest(
+    m_remote_io_ctx, &image_ids, &ctx);
+
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+
+  expected_image_ids.insert({"global id", "local id",
+                             boost::optional<std::string>{"image name"}});
+  ASSERT_EQ(expected_image_ids, image_ids);
+}
+
+TEST_F(TestMockPoolWatcherRefreshImagesRequest, MirrorImageListError) {
+  InSequence seq;
+  expect_mirror_image_list(m_remote_io_ctx, {}, -EINVAL);
+
+  C_SaferCond ctx;
+  ImageIds image_ids;
+  MockRefreshImagesRequest *req = new MockRefreshImagesRequest(
+    m_remote_io_ctx, &image_ids, &ctx);
+
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockPoolWatcherRefreshImagesRequest, DirListError) {
+  InSequence seq;
+  expect_mirror_image_list(m_remote_io_ctx, {{"local id", "global id"}}, 0);
+  expect_dir_list(m_remote_io_ctx, {{"image name", "local id"}}, -EINVAL);
+
+  C_SaferCond ctx;
+  ImageIds image_ids;
+  MockRefreshImagesRequest *req = new MockRefreshImagesRequest(
+    m_remote_io_ctx, &image_ids, &ctx);
+
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace pool_watcher
+} // namespace mirror
+} // namespace rbd
index beb95d1db3a5ea078a15bbafbf74da93a48d2b6a..433873b8586b85a887595f0e05e2ced0bf9feb33 100644 (file)
@@ -26,6 +26,8 @@
 #include <set>
 #include <vector>
 
+using rbd::mirror::ImageId;
+using rbd::mirror::ImageIds;
 using rbd::mirror::PoolWatcher;
 using rbd::mirror::peer_t;
 using rbd::mirror::RadosRef;
@@ -108,7 +110,7 @@ TestPoolWatcher() : m_lock("TestPoolWatcherLock"),
                                                sizeof(mirror_image_info)));
       image.close();
 
-      m_mirrored_images.insert(PoolWatcher::ImageId(
+      m_mirrored_images.insert(ImageId(
         mirror_image_info.global_id, get_image_id(&ioctx, name), name));
     }
     if (image_name != nullptr)
@@ -154,7 +156,7 @@ TestPoolWatcher() : m_lock("TestPoolWatcherLock"),
                                                sizeof(mirror_image_info)));
       image.close();
 
-      m_mirrored_images.insert(PoolWatcher::ImageId(
+      m_mirrored_images.insert(ImageId(
         mirror_image_info.global_id, get_image_id(&cioctx, name), name));
     }
     if (image_name != nullptr)
@@ -173,7 +175,7 @@ TestPoolWatcher() : m_lock("TestPoolWatcherLock"),
   unique_ptr<PoolWatcher> m_pool_watcher;
 
   set<string> m_pools;
-  PoolWatcher::ImageIds m_mirrored_images;
+  ImageIds m_mirrored_images;
 
   uint64_t m_image_number;
   uint64_t m_snap_number;
index faa10b3523bd7c49578a4c120cb423d3b7a21680..10d6c8918d5d5c934eb28aef60538abe575a3abf 100644 (file)
@@ -27,7 +27,8 @@ set(rbd_mirror_internal
   image_sync/SnapshotCopyRequest.cc
   image_sync/SnapshotCreateRequest.cc
   image_sync/SyncPointCreateRequest.cc
-  image_sync/SyncPointPruneRequest.cc)
+  image_sync/SyncPointPruneRequest.cc
+  pool_watcher/RefreshImagesRequest.cc)
 add_library(rbd_mirror_internal STATIC
   ${rbd_mirror_internal})
 
index 23b2cca17bdd2e263d8a1be97c8a5a2988afb6eb..92c60699d56f5cc89632e174068a833886618820 100644 (file)
@@ -54,7 +54,7 @@ bool PoolWatcher::is_blacklisted() const {
   return m_blacklisted;
 }
 
-const PoolWatcher::ImageIds& PoolWatcher::get_images() const
+const ImageIds& PoolWatcher::get_images() const
 {
   assert(m_lock.is_locked());
   return m_images;
@@ -124,7 +124,7 @@ int PoolWatcher::refresh(ImageIds *image_ids) {
       return r;
     }
     for (auto it = mirror_images.begin(); it != mirror_images.end(); ++it) {
-      boost::optional<std::string> image_name(boost::none);
+      std::string image_name;
       auto it2 = image_id_to_name.find(it->first);
       if (it2 != image_id_to_name.end()) {
         image_name = it2->second;
index 9e1e75b64af36c88c2c771ce1da0b269d47bbb46..721106f761edf8e7bc7f55dc4d307ce32e5a5766 100644 (file)
@@ -25,25 +25,6 @@ namespace mirror {
  */
 class PoolWatcher {
 public:
-  struct ImageId {
-    std::string global_id;
-    std::string id;
-    boost::optional<std::string> name;
-
-    ImageId(const std::string &global_id, const std::string &id = "",
-            const boost::optional<std::string> &name = boost::none)
-      : global_id(global_id), id(id), name(name) {
-    }
-
-    inline bool operator==(const ImageId &rhs) const {
-      return (global_id == rhs.global_id && id == rhs.id && name == rhs.name);
-    }
-    inline bool operator<(const ImageId &rhs) const {
-      return global_id < rhs.global_id;
-    }
-  };
-  typedef std::set<ImageId> ImageIds;
-
   PoolWatcher(librados::IoCtx &remote_io_ctx, double interval_seconds,
              Mutex &lock, Cond &cond);
   ~PoolWatcher();
index e067e19b0cefe01c040ce555bfa811d8aa9fab80..d20128c5076b6cd71bfd7723de982ab0ccc2e369 100644 (file)
@@ -605,7 +605,7 @@ void Replayer::set_sources(const ImageIds &image_ids)
   // shut down replayers for non-mirrored images
   for (auto image_it = m_image_replayers.begin();
        image_it != m_image_replayers.end();) {
-    auto image_id_it = image_ids.find(image_it->first);
+    auto image_id_it = image_ids.find(ImageId(image_it->first));
     if (image_id_it == image_ids.end()) {
       if (image_it->second->is_running()) {
         dout(20) << "stop image replayer for remote image "
index c60c86c8834112afee0224443b2487a88d8b5e05..42db5ec964db55c3c3b2a3814a3fd498ce55cbfc 100644 (file)
@@ -55,9 +55,6 @@ public:
   void release_leader();
 
 private:
-  typedef PoolWatcher::ImageId ImageId;
-  typedef PoolWatcher::ImageIds ImageIds;
-
   void init_local_mirroring_images();
   void set_sources(const ImageIds &image_ids);
 
diff --git a/src/tools/rbd_mirror/pool_watcher/RefreshImagesRequest.cc b/src/tools/rbd_mirror/pool_watcher/RefreshImagesRequest.cc
new file mode 100644 (file)
index 0000000..5a2084a
--- /dev/null
@@ -0,0 +1,139 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd_mirror/pool_watcher/RefreshImagesRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/Utils.h"
+#include <map>
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rbd_mirror
+#undef dout_prefix
+#define dout_prefix *_dout << "rbd::mirror::pool_watcher::RefreshImagesRequest " \
+                           << this << " " << __func__ << ": "
+
+namespace rbd {
+namespace mirror {
+namespace pool_watcher {
+
+static const uint32_t MAX_RETURN = 1024;
+
+using librbd::util::create_rados_ack_callback;
+
+template <typename I>
+void RefreshImagesRequest<I>::send() {
+  mirror_image_list();
+}
+
+template <typename I>
+void RefreshImagesRequest<I>::mirror_image_list() {
+  dout(10) << dendl;
+
+  librados::ObjectReadOperation op;
+  librbd::cls_client::mirror_image_list_start(&op, m_start_after, MAX_RETURN);
+
+  librados::AioCompletion *aio_comp = create_rados_ack_callback<
+    RefreshImagesRequest<I>,
+    &RefreshImagesRequest<I>::handle_mirror_image_list>(this);
+  int r = m_remote_io_ctx.aio_operate(RBD_MIRRORING, aio_comp, &op, &m_out_bl);
+  assert(r == 0);
+  aio_comp->release();
+}
+
+template <typename I>
+void RefreshImagesRequest<I>::handle_mirror_image_list(int r) {
+  dout(10) << "r=" << r << dendl;
+
+  std::map<std::string, std::string> ids;
+  if (r == 0) {
+    bufferlist::iterator it = m_out_bl.begin();
+    r = librbd::cls_client::mirror_image_list_finish(&it, &ids);
+  }
+
+  if (r < 0) {
+    derr << "failed to list mirrored images: " << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  if (!ids.empty()) {
+    m_local_to_global_ids.insert(ids.begin(), ids.end());
+    if (ids.size() == MAX_RETURN) {
+      m_start_after = ids.rbegin()->first;
+      mirror_image_list();
+      return;
+    }
+  }
+
+  dir_list();
+}
+
+template <typename I>
+void RefreshImagesRequest<I>::dir_list() {
+  dout(10) << dendl;
+
+  m_out_bl.clear();
+  m_start_after = "";
+
+  librados::ObjectReadOperation op;
+  librbd::cls_client::dir_list_start(&op, m_start_after, MAX_RETURN);
+
+  librados::AioCompletion *aio_comp = create_rados_ack_callback<
+    RefreshImagesRequest<I>,
+    &RefreshImagesRequest<I>::handle_dir_list>(this);
+  int r = m_remote_io_ctx.aio_operate(RBD_DIRECTORY, aio_comp, &op, &m_out_bl);
+  assert(r == 0);
+  aio_comp->release();
+}
+
+template <typename I>
+void RefreshImagesRequest<I>::handle_dir_list(int r) {
+  dout(10) << "r=" << r << dendl;
+
+  std::map<std::string, std::string> name_to_ids;
+  if (r == 0) {
+    bufferlist::iterator it = m_out_bl.begin();
+    r = librbd::cls_client::dir_list_finish(&it, &name_to_ids);
+  }
+
+  if (r < 0) {
+    derr << "failed to list images: " << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  if (!name_to_ids.empty()) {
+    for (auto &pair : name_to_ids) {
+      auto it = m_local_to_global_ids.find(pair.second);
+      if (it != m_local_to_global_ids.end()) {
+        // mirrored image must exist within directory to be treated as
+        // a valid image
+        m_image_ids->insert(ImageId(it->second, it->first, pair.first));
+      }
+    }
+
+    if (name_to_ids.size() == MAX_RETURN) {
+      m_start_after = name_to_ids.rbegin()->first;
+      dir_list();
+      return;
+    }
+  }
+
+  finish(0);
+}
+
+template <typename I>
+void RefreshImagesRequest<I>::finish(int r) {
+  dout(10) << "r=" << r << dendl;
+
+  m_on_finish->complete(r);
+  delete this;
+}
+
+} // namespace pool_watcher
+} // namespace mirror
+} // namespace rbd
+
+template class rbd::mirror::pool_watcher::RefreshImagesRequest<librbd::ImageCtx>;
diff --git a/src/tools/rbd_mirror/pool_watcher/RefreshImagesRequest.h b/src/tools/rbd_mirror/pool_watcher/RefreshImagesRequest.h
new file mode 100644 (file)
index 0000000..38dd1f5
--- /dev/null
@@ -0,0 +1,85 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_RBD_MIRROR_POOL_WATCHER_REFRESH_IMAGES_REQUEST_H
+#define CEPH_RBD_MIRROR_POOL_WATCHER_REFRESH_IMAGES_REQUEST_H
+
+#include "include/buffer.h"
+#include "include/rados/librados.hpp"
+#include "tools/rbd_mirror/types.h"
+#include <string>
+#include <unordered_map>
+
+struct Context;
+
+namespace librbd { struct ImageCtx; }
+
+namespace rbd {
+namespace mirror {
+namespace pool_watcher {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+class RefreshImagesRequest {
+public:
+  RefreshImagesRequest *create(librados::IoCtx &remote_io_ctx,
+                               ImageIds *image_ids, Context *on_finish) {
+    return new RefreshImagesRequest(remote_io_ctx, image_ids, on_finish);
+  }
+
+  RefreshImagesRequest(librados::IoCtx &remote_io_ctx, ImageIds *image_ids,
+                       Context *on_finish)
+    : m_remote_io_ctx(remote_io_ctx), m_image_ids(image_ids),
+      m_on_finish(on_finish) {
+  }
+
+  void send();
+
+private:
+  /**
+   * @verbatim
+   *
+   * <start>
+   *    |
+   *    |   /-------------\
+   *    |   |             |
+   *    v   v             | (more images)
+   * MIRROR_IMAGE_LIST ---/
+   *    |
+   *    |   /-------------\
+   *    |   |             |
+   *    v   v             | (more images)
+   * DIR_LIST ------------/
+   *    |
+   *    v
+   * <finish>
+   *
+   * @endverbatim
+   */
+
+  typedef std::unordered_map<std::string, std::string> LocalToGlobalIds;
+
+  librados::IoCtx &m_remote_io_ctx;
+  ImageIds *m_image_ids;
+  Context *m_on_finish;
+
+  bufferlist m_out_bl;
+  std::string m_start_after;
+  LocalToGlobalIds m_local_to_global_ids;
+
+  void mirror_image_list();
+  void handle_mirror_image_list(int r);
+
+  void dir_list();
+  void handle_dir_list(int r);
+
+  void finish(int r);
+
+};
+
+} // namespace pool_watcher
+} // namespace mirror
+} // namespace rbd
+
+extern template class rbd::mirror::pool_watcher::RefreshImagesRequest<librbd::ImageCtx>;
+
+#endif // CEPH_RBD_MIRROR_POOL_WATCHER_REFRESH_IMAGES_REQUEST_H
index a1181ed192593c17f91fbac5949e87786770ab3c..0a13049f5813c2f3984e27f72aa4597ee8b35880 100644 (file)
@@ -6,8 +6,10 @@
 
 #include <iostream>
 #include <memory>
+#include <set>
 #include <string>
 #include <vector>
+#include <boost/optional.hpp>
 
 #include "include/rbd/librbd.hpp"
 #include "ImageSyncThrottler.h"
@@ -22,6 +24,28 @@ typedef shared_ptr<librbd::Image> ImageRef;
 template <typename I = librbd::ImageCtx>
 using ImageSyncThrottlerRef = std::shared_ptr<ImageSyncThrottler<I>>;
 
+struct ImageId {
+  std::string global_id;
+  std::string id;
+  boost::optional<std::string> name;
+
+  explicit ImageId(const std::string &global_id) : global_id(global_id) {
+  }
+  ImageId(const std::string &global_id, const std::string &id,
+          const boost::optional<std::string> &name = boost::none)
+    : global_id(global_id), id(id), name(name) {
+  }
+
+  inline bool operator==(const ImageId &rhs) const {
+    return (global_id == rhs.global_id && id == rhs.id && name == rhs.name);
+  }
+  inline bool operator<(const ImageId &rhs) const {
+    return global_id < rhs.global_id;
+  }
+};
+
+typedef std::set<ImageId> ImageIds;
+
 struct peer_t {
   peer_t() = default;
   peer_t(const std::string &uuid, const std::string &cluster_name,