]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd-mirror: replicate image metadata settings 11168/head
authorMykola Golub <mgolub@mirantis.com>
Wed, 21 Sep 2016 10:58:48 +0000 (13:58 +0300)
committerMykola Golub <mgolub@mirantis.com>
Wed, 28 Sep 2016 13:36:15 +0000 (16:36 +0300)
Fixes: http://tracker.ceph.com/issues/16212
Signed-off-by: Mykola Golub <mgolub@mirantis.com>
20 files changed:
src/librbd/CMakeLists.txt
src/librbd/Operations.cc
src/librbd/Operations.h
src/librbd/internal.cc
src/librbd/internal.h
src/librbd/journal/Replay.cc
src/librbd/journal/Replay.h
src/librbd/journal/Types.cc
src/librbd/journal/Types.h
src/librbd/librbd.cc
src/librbd/operation/MetadataRemoveRequest.cc [new file with mode: 0644]
src/librbd/operation/MetadataRemoveRequest.h [new file with mode: 0644]
src/librbd/operation/MetadataSetRequest.cc [new file with mode: 0644]
src/librbd/operation/MetadataSetRequest.h [new file with mode: 0644]
src/test/librbd/journal/test_Replay.cc
src/test/librbd/journal/test_mock_Replay.cc
src/test/librbd/mock/MockOperations.h
src/test/librbd/test_internal.cc
src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc
src/test/rbd_mirror/test_ImageReplayer.cc

index ef8f0e68c00446edf6d17f41f30002de9cb78de7..4f46c1f2bacde5cb101c8c0d7a819f8958a57d12 100644 (file)
@@ -65,6 +65,8 @@ set(librbd_internal_srcs
   operation/DisableFeaturesRequest.cc
   operation/EnableFeaturesRequest.cc
   operation/FlattenRequest.cc
+  operation/MetadataRemoveRequest.cc
+  operation/MetadataSetRequest.cc
   operation/ObjectMapIterate.cc
   operation/RebuildObjectMapRequest.cc
   operation/RenameRequest.cc
index c774a54d20760a51f97db428ace3c3cdd7030ef4..304545f8b739636064f93fcecf087fd8559668ea 100644 (file)
 #include "librbd/operation/DisableFeaturesRequest.h"
 #include "librbd/operation/EnableFeaturesRequest.h"
 #include "librbd/operation/FlattenRequest.h"
-#include "librbd/operation/RebuildObjectMapRequest.h"
+#include "librbd/operation/MetadataRemoveRequest.h"
+#include "librbd/operation/MetadataSetRequest.h"
 #include "librbd/operation/ObjectMapIterate.h"
+#include "librbd/operation/RebuildObjectMapRequest.h"
 #include "librbd/operation/RenameRequest.h"
 #include "librbd/operation/ResizeRequest.h"
 #include "librbd/operation/SnapshotCreateRequest.h"
@@ -1296,6 +1298,123 @@ void Operations<I>::execute_update_features(uint64_t features, bool enabled,
   }
 }
 
+template <typename I>
+int Operations<I>::metadata_set(const std::string &key,
+                                const std::string &value) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": key=" << key << ", value="
+                << value << dendl;
+
+  string start = m_image_ctx.METADATA_CONF_PREFIX;
+  size_t conf_prefix_len = start.size();
+
+  if (key.size() > conf_prefix_len && !key.compare(0, conf_prefix_len, start)) {
+    string subkey = key.substr(conf_prefix_len, key.size() - conf_prefix_len);
+    int r = cct->_conf->set_val(subkey.c_str(), value);
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0) {
+    return r;
+  }
+
+  if (m_image_ctx.read_only) {
+    return -EROFS;
+  }
+
+  {
+    RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+    C_SaferCond metadata_ctx;
+
+    if (m_image_ctx.exclusive_lock != nullptr &&
+       !m_image_ctx.exclusive_lock->is_lock_owner()) {
+      C_SaferCond lock_ctx;
+
+      m_image_ctx.exclusive_lock->request_lock(&lock_ctx);
+      r = lock_ctx.wait();
+      if (r < 0) {
+       return r;
+      }
+    }
+
+    execute_metadata_set(key, value, &metadata_ctx);
+    r = metadata_ctx.wait();
+  }
+
+  return r;
+}
+
+template <typename I>
+void Operations<I>::execute_metadata_set(const std::string &key,
+                                       const std::string &value,
+                                       Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": key=" << key << ", value="
+                << value << dendl;
+
+  operation::MetadataSetRequest<I> *request =
+    new operation::MetadataSetRequest<I>(m_image_ctx, on_finish, key, value);
+  request->send();
+}
+
+template <typename I>
+int Operations<I>::metadata_remove(const std::string &key) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": key=" << key << dendl;
+
+  if (m_image_ctx.read_only) {
+    return -EROFS;
+  }
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0) {
+    return r;
+  }
+
+  if (m_image_ctx.read_only) {
+    return -EROFS;
+  }
+
+  {
+    RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+    C_SaferCond metadata_ctx;
+
+    if (m_image_ctx.exclusive_lock != nullptr &&
+        !m_image_ctx.exclusive_lock->is_lock_owner()) {
+      C_SaferCond lock_ctx;
+
+      m_image_ctx.exclusive_lock->request_lock(&lock_ctx);
+      r = lock_ctx.wait();
+      if (r < 0) {
+        return r;
+      }
+    }
+
+    execute_metadata_remove(key, &metadata_ctx);
+    r = metadata_ctx.wait();
+  }
+
+  return r;
+}
+
+template <typename I>
+void Operations<I>::execute_metadata_remove(const std::string &key,
+                                           Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": key=" << key << dendl;
+
+  operation::MetadataRemoveRequest<I> *request =
+    new operation::MetadataRemoveRequest<I>(m_image_ctx, on_finish, key);
+  request->send();
+}
+
 template <typename I>
 int Operations<I>::prepare_image_update() {
   assert(m_image_ctx.owner_lock.is_locked() &&
index 64ec781e6da2394778cae290ee7ec56298d60864..6790ffeac3f31c0aa9086fd0106776186d6165fe 100644 (file)
@@ -74,6 +74,13 @@ public:
   void execute_update_features(uint64_t features, bool enabled,
                                Context *on_finish, uint64_t journal_op_tid);
 
+  int metadata_set(const std::string &key, const std::string &value);
+  void execute_metadata_set(const std::string &key, const std::string &value,
+                            Context *on_finish);
+
+  int metadata_remove(const std::string &key);
+  void execute_metadata_remove(const std::string &key, Context *on_finish);
+
   int prepare_image_update();
 
 private:
index 376170211995085de2e181864d0bf3b61866ffba..fe8f0f4680f5d922131c58c573dcebe31f66e621 100644 (file)
@@ -2365,43 +2365,6 @@ int mirror_image_disable_internal(ImageCtx *ictx, bool force,
     return cls_client::metadata_get(&ictx->md_ctx, ictx->header_oid, key, value);
   }
 
-  int metadata_set(ImageCtx *ictx, const string &key, const string &value)
-  {
-    CephContext *cct = ictx->cct;
-    string start = ictx->METADATA_CONF_PREFIX;
-    size_t conf_prefix_len = start.size();
-
-    if(key.size() > conf_prefix_len && !key.compare(0,conf_prefix_len,start)) {
-      string subkey = key.substr(conf_prefix_len, key.size()-conf_prefix_len);
-      int r = cct->_conf->set_val(subkey.c_str(), value);
-      if (r < 0)
-        return r;
-    }
-    ldout(cct, 20) << "metadata_set " << ictx << " key=" << key << " value=" << value << dendl;
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0) {
-      return r;
-    }
-
-    map<string, bufferlist> data;
-    data[key].append(value);
-    return cls_client::metadata_set(&ictx->md_ctx, ictx->header_oid, data);
-  }
-
-  int metadata_remove(ImageCtx *ictx, const string &key)
-  {
-    CephContext *cct = ictx->cct;
-    ldout(cct, 20) << "metadata_remove " << ictx << " key=" << key << dendl;
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0) {
-      return r;
-    }
-
-    return cls_client::metadata_remove(&ictx->md_ctx, ictx->header_oid, key);
-  }
-
   int metadata_list(ImageCtx *ictx, const string &start, uint64_t max, map<string, bufferlist> *pairs)
   {
     CephContext *cct = ictx->cct;
index 2d6e7c5dbf3580b23216a85b25a5eae35365779b..01b95464f6ad94f2ca787b24a8ae00e9ffe4b55c 100644 (file)
@@ -195,8 +195,6 @@ namespace librbd {
   int poll_io_events(ImageCtx *ictx, AioCompletion **comps, int numcomp);
   int metadata_list(ImageCtx *ictx, const string &last, uint64_t max, map<string, bufferlist> *pairs);
   int metadata_get(ImageCtx *ictx, const std::string &key, std::string *value);
-  int metadata_set(ImageCtx *ictx, const std::string &key, const std::string &value);
-  int metadata_remove(ImageCtx *ictx, const std::string &key);
 
   int mirror_mode_get(IoCtx& io_ctx, rbd_mirror_mode_t *mirror_mode);
   int mirror_mode_set(IoCtx& io_ctx, rbd_mirror_mode_t mirror_mode);
index 7e450a26a577ecbb5779071528169aa7e3d54827..9055e0b8974842c8921cbc17a9fe651f1cea6e18 100644 (file)
@@ -94,6 +94,15 @@ struct ExecuteOp : public Context {
                                                  on_op_complete, event.op_tid);
   }
 
+  void execute(const journal::MetadataSetEvent &_) {
+    image_ctx.operations->execute_metadata_set(event.key, event.value,
+                                               on_op_complete);
+  }
+
+  void execute(const journal::MetadataRemoveEvent &_) {
+    image_ctx.operations->execute_metadata_remove(event.key, on_op_complete);
+  }
+
   virtual void finish(int r) override {
     CephContext *cct = image_ctx.cct;
     if (r < 0) {
@@ -688,6 +697,51 @@ void Replay<I>::handle_event(const journal::UpdateFeaturesEvent &event,
   op_event->on_start_ready = on_ready;
 }
 
+template <typename I>
+void Replay<I>::handle_event(const journal::MetadataSetEvent &event,
+                            Context *on_ready, Context *on_safe) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 20) << ": Metadata set event" << dendl;
+
+  Mutex::Locker locker(m_lock);
+  OpEvent *op_event;
+  Context *on_op_complete = create_op_context_callback(event.op_tid, on_ready,
+                                                       on_safe, &op_event);
+  if (on_op_complete == nullptr) {
+    return;
+  }
+
+  op_event->on_op_finish_event = new C_RefreshIfRequired<I>(
+    m_image_ctx, new ExecuteOp<I, journal::MetadataSetEvent>(
+      m_image_ctx, event, on_op_complete));
+
+  on_ready->complete(0);
+}
+
+template <typename I>
+void Replay<I>::handle_event(const journal::MetadataRemoveEvent &event,
+                            Context *on_ready, Context *on_safe) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 20) << ": Metadata remove event" << dendl;
+
+  Mutex::Locker locker(m_lock);
+  OpEvent *op_event;
+  Context *on_op_complete = create_op_context_callback(event.op_tid, on_ready,
+                                                       on_safe, &op_event);
+  if (on_op_complete == nullptr) {
+    return;
+  }
+
+  op_event->on_op_finish_event = new C_RefreshIfRequired<I>(
+    m_image_ctx, new ExecuteOp<I, journal::MetadataRemoveEvent>(
+      m_image_ctx, event, on_op_complete));
+
+  // ignore errors caused due to replay
+  op_event->ignore_error_codes = {-ENOENT};
+
+  on_ready->complete(0);
+}
+
 template <typename I>
 void Replay<I>::handle_event(const journal::UnknownEvent &event,
                             Context *on_ready, Context *on_safe) {
index 468ed32fc3eeeb89810d15e4e9d5c1c780c9b94c..5b7b7e07925dc0d3015c432b38f2d979830caf32 100644 (file)
@@ -161,6 +161,10 @@ private:
                    Context *on_safe);
   void handle_event(const UpdateFeaturesEvent &event, Context *on_ready,
                     Context *on_safe);
+  void handle_event(const MetadataSetEvent &event, Context *on_ready,
+                    Context *on_safe);
+  void handle_event(const MetadataRemoveEvent &event, Context *on_ready,
+                    Context *on_safe);
   void handle_event(const UnknownEvent &event, Context *on_ready,
                     Context *on_safe);
 
index c62e70d027ccb9a94928adae25827f0a3a6636ce..bdd294f467b496c0fabf1cf7664c7aa93d8c2357 100644 (file)
@@ -247,6 +247,39 @@ void UpdateFeaturesEvent::dump(Formatter *f) const {
   f->dump_bool("enabled", enabled);
 }
 
+void MetadataSetEvent::encode(bufferlist& bl) const {
+  OpEventBase::encode(bl);
+  ::encode(key, bl);
+  ::encode(value, bl);
+}
+
+void MetadataSetEvent::decode(__u8 version, bufferlist::iterator& it) {
+  OpEventBase::decode(version, it);
+  ::decode(key, it);
+  ::decode(value, it);
+}
+
+void MetadataSetEvent::dump(Formatter *f) const {
+  OpEventBase::dump(f);
+  f->dump_string("key", key);
+  f->dump_string("value", value);
+}
+
+void MetadataRemoveEvent::encode(bufferlist& bl) const {
+  OpEventBase::encode(bl);
+  ::encode(key, bl);
+}
+
+void MetadataRemoveEvent::decode(__u8 version, bufferlist::iterator& it) {
+  OpEventBase::decode(version, it);
+  ::decode(key, it);
+}
+
+void MetadataRemoveEvent::dump(Formatter *f) const {
+  OpEventBase::dump(f);
+  f->dump_string("key", key);
+}
+
 void UnknownEvent::encode(bufferlist& bl) const {
   assert(false);
 }
@@ -320,6 +353,12 @@ void EventEntry::decode(bufferlist::iterator& it) {
   case EVENT_TYPE_UPDATE_FEATURES:
     event = UpdateFeaturesEvent();
     break;
+  case EVENT_TYPE_METADATA_SET:
+    event = MetadataSetEvent();
+    break;
+  case EVENT_TYPE_METADATA_REMOVE:
+    event = MetadataRemoveEvent();
+    break;
   default:
     event = UnknownEvent();
     break;
@@ -376,6 +415,12 @@ void EventEntry::generate_test_instances(std::list<EventEntry *> &o) {
 
   o.push_back(new EventEntry(UpdateFeaturesEvent()));
   o.push_back(new EventEntry(UpdateFeaturesEvent(123, 127, true)));
+
+  o.push_back(new EventEntry(MetadataSetEvent()));
+  o.push_back(new EventEntry(MetadataSetEvent(123, "key", "value")));
+
+  o.push_back(new EventEntry(MetadataRemoveEvent()));
+  o.push_back(new EventEntry(MetadataRemoveEvent(123, "key")));
 }
 
 // Journal Client
@@ -630,6 +675,12 @@ std::ostream &operator<<(std::ostream &out, const EventType &type) {
   case EVENT_TYPE_UPDATE_FEATURES:
     out << "UpdateFeatures";
     break;
+  case EVENT_TYPE_METADATA_SET:
+    out << "MetadataSet";
+    break;
+  case EVENT_TYPE_METADATA_REMOVE:
+    out << "MetadataRemove";
+    break;
   default:
     out << "Unknown (" << static_cast<uint32_t>(type) << ")";
     break;
index 4d904e4aac716722a15f2273e32fcdb200969b35..37c590556fdc7d549400cbe3db3613df53c87dd5 100644 (file)
@@ -38,6 +38,8 @@ enum EventType {
   EVENT_TYPE_DEMOTE          = 13,
   EVENT_TYPE_SNAP_LIMIT      = 14,
   EVENT_TYPE_UPDATE_FEATURES = 15,
+  EVENT_TYPE_METADATA_SET    = 16,
+  EVENT_TYPE_METADATA_REMOVE = 17,
 };
 
 struct AioDiscardEvent {
@@ -306,6 +308,39 @@ struct UpdateFeaturesEvent : public OpEventBase {
   void dump(Formatter *f) const;
 };
 
+struct MetadataSetEvent : public OpEventBase {
+  static const EventType TYPE = EVENT_TYPE_METADATA_SET;
+
+  string key;
+  string value;
+
+  MetadataSetEvent() {
+  }
+  MetadataSetEvent(uint64_t op_tid, const string &_key, const string &_value)
+    : OpEventBase(op_tid), key(_key), value(_value) {
+  }
+
+  void encode(bufferlist& bl) const;
+  void decode(__u8 version, bufferlist::iterator& it);
+  void dump(Formatter *f) const;
+};
+
+struct MetadataRemoveEvent : public OpEventBase {
+  static const EventType TYPE = EVENT_TYPE_METADATA_REMOVE;
+
+  string key;
+
+  MetadataRemoveEvent() {
+  }
+  MetadataRemoveEvent(uint64_t op_tid, const string &_key)
+    : OpEventBase(op_tid), key(_key) {
+  }
+
+  void encode(bufferlist& bl) const;
+  void decode(__u8 version, bufferlist::iterator& it);
+  void dump(Formatter *f) const;
+};
+
 struct UnknownEvent {
   static const EventType TYPE = static_cast<EventType>(-1);
 
@@ -330,6 +365,8 @@ typedef boost::variant<AioDiscardEvent,
                        DemoteEvent,
                       SnapLimitEvent,
                        UpdateFeaturesEvent,
+                       MetadataSetEvent,
+                       MetadataRemoveEvent,
                        UnknownEvent> Event;
 
 struct EventEntry {
index ac7a3dade7b9a1618c7600c2d4e2c4d7269f8dfe..ebc656a7a9d8ec5b2a7c35d30a938886bb1f8cbd 100644 (file)
@@ -1399,7 +1399,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, metadata_set_enter, ictx, key.c_str(), value.c_str());
-    int r = librbd::metadata_set(ictx, key, value);
+    int r = ictx->operations->metadata_set(key, value);
     tracepoint(librbd, metadata_set_exit, r);
     return r;
   }
@@ -1408,7 +1408,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, metadata_remove_enter, ictx, key.c_str());
-    int r = librbd::metadata_remove(ictx, key);
+    int r = ictx->operations->metadata_remove(key);
     tracepoint(librbd, metadata_remove_exit, r);
     return r;
   }
@@ -2882,7 +2882,7 @@ extern "C" int rbd_metadata_set(rbd_image_t image, const char *key, const char *
 {
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, metadata_set_enter, ictx, key, value);
-  int r = librbd::metadata_set(ictx, key, value);
+  int r = ictx->operations->metadata_set(key, value);
   tracepoint(librbd, metadata_set_exit, r);
   return r;
 }
@@ -2891,7 +2891,7 @@ extern "C" int rbd_metadata_remove(rbd_image_t image, const char *key)
 {
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, metadata_remove_enter, ictx, key);
-  int r = librbd::metadata_remove(ictx, key);
+  int r = ictx->operations->metadata_remove(key);
   tracepoint(librbd, metadata_remove_exit, r);
   return r;
 }
diff --git a/src/librbd/operation/MetadataRemoveRequest.cc b/src/librbd/operation/MetadataRemoveRequest.cc
new file mode 100644 (file)
index 0000000..3c5a744
--- /dev/null
@@ -0,0 +1,60 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/operation/MetadataRemoveRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/ImageCtx.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::MetadataRemoveRequest: "
+
+namespace librbd {
+namespace operation {
+
+template <typename I>
+MetadataRemoveRequest<I>::MetadataRemoveRequest(I &image_ctx,
+                                                Context *on_finish,
+                                                const std::string &key)
+  : Request<I>(image_ctx, on_finish), m_key(key) {
+}
+
+template <typename I>
+void MetadataRemoveRequest<I>::send_op() {
+  send_metadata_remove();
+}
+
+template <typename I>
+bool MetadataRemoveRequest<I>::should_complete(int r) {
+  I &image_ctx = this->m_image_ctx;
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 20) << this << " " << __func__ << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl;
+  }
+  return true;
+}
+
+template <typename I>
+void MetadataRemoveRequest<I>::send_metadata_remove() {
+  I &image_ctx = this->m_image_ctx;
+  assert(image_ctx.owner_lock.is_locked());
+
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 20) << this << " " << __func__ << dendl;
+
+  librados::ObjectWriteOperation op;
+  cls_client::metadata_remove(&op, m_key);
+
+  librados::AioCompletion *comp = this->create_callback_completion();
+  int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, comp, &op);
+  assert(r == 0);
+  comp->release();
+}
+
+} // namespace operation
+} // namespace librbd
+
+template class librbd::operation::MetadataRemoveRequest<librbd::ImageCtx>;
diff --git a/src/librbd/operation/MetadataRemoveRequest.h b/src/librbd/operation/MetadataRemoveRequest.h
new file mode 100644 (file)
index 0000000..0b51e11
--- /dev/null
@@ -0,0 +1,44 @@
+// -*- mode:C++; tab-width:8; c-basic-offremove:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_OPERATION_METADATA_REMOVE_REQUEST_H
+#define CEPH_LIBRBD_OPERATION_METADATA_REMOVE_REQUEST_H
+
+#include "librbd/operation/Request.h"
+#include <iosfwd>
+#include <string>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace operation {
+
+template <typename ImageCtxT = ImageCtx>
+class MetadataRemoveRequest : public Request<ImageCtxT> {
+public:
+  MetadataRemoveRequest(ImageCtxT &image_ctx, Context *on_finish,
+                        const std::string &key);
+
+protected:
+  virtual void send_op();
+  virtual bool should_complete(int r);
+
+  virtual journal::Event create_event(uint64_t op_tid) const {
+    return journal::MetadataRemoveEvent(op_tid, m_key);
+  }
+
+private:
+  std::string m_key;
+
+  void send_metadata_remove();
+};
+
+} // namespace operation
+} // namespace librbd
+
+extern template class librbd::operation::MetadataRemoveRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_OPERATION_METADATA_REMOVE_REQUEST_H
diff --git a/src/librbd/operation/MetadataSetRequest.cc b/src/librbd/operation/MetadataSetRequest.cc
new file mode 100644 (file)
index 0000000..0142d82
--- /dev/null
@@ -0,0 +1,62 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/operation/MetadataSetRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/ImageCtx.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::MetadataSetRequest: "
+
+namespace librbd {
+namespace operation {
+
+template <typename I>
+MetadataSetRequest<I>::MetadataSetRequest(I &image_ctx,
+                                          Context *on_finish,
+                                          const std::string &key,
+                                          const std::string &value)
+  : Request<I>(image_ctx, on_finish), m_key(key), m_value(value) {
+}
+
+template <typename I>
+void MetadataSetRequest<I>::send_op() {
+  send_metadata_set();
+}
+
+template <typename I>
+bool MetadataSetRequest<I>::should_complete(int r) {
+  I &image_ctx = this->m_image_ctx;
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 20) << this << " " << __func__ << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl;
+  }
+  return true;
+}
+
+template <typename I>
+void MetadataSetRequest<I>::send_metadata_set() {
+  I &image_ctx = this->m_image_ctx;
+  assert(image_ctx.owner_lock.is_locked());
+
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 20) << this << " " << __func__ << dendl;
+
+  m_data[m_key].append(m_value);
+  librados::ObjectWriteOperation op;
+  cls_client::metadata_set(&op, m_data);
+
+  librados::AioCompletion *comp = this->create_callback_completion();
+  int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, comp, &op);
+  assert(r == 0);
+  comp->release();
+}
+
+} // namespace operation
+} // namespace librbd
+
+template class librbd::operation::MetadataSetRequest<librbd::ImageCtx>;
diff --git a/src/librbd/operation/MetadataSetRequest.h b/src/librbd/operation/MetadataSetRequest.h
new file mode 100644 (file)
index 0000000..d6abc3b
--- /dev/null
@@ -0,0 +1,47 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_OPERATION_METADATA_SET_REQUEST_H
+#define CEPH_LIBRBD_OPERATION_METADATA_SET_REQUEST_H
+
+#include "librbd/operation/Request.h"
+#include "include/buffer.h"
+#include <string>
+#include <map>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace operation {
+
+template <typename ImageCtxT = ImageCtx>
+class MetadataSetRequest : public Request<ImageCtxT> {
+public:
+  MetadataSetRequest(ImageCtxT &image_ctx, Context *on_finish,
+                     const std::string &key, const std::string &value);
+
+protected:
+  virtual void send_op();
+  virtual bool should_complete(int r);
+
+  virtual journal::Event create_event(uint64_t op_tid) const {
+    return journal::MetadataSetEvent(op_tid, m_key, m_value);
+  }
+
+private:
+  std::string m_key;
+  std::string m_value;
+  std::map<std::string, bufferlist> m_data;
+
+  void send_metadata_set();
+};
+
+} // namespace operation
+} // namespace librbd
+
+extern template class librbd::operation::MetadataSetRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_OPERATION_METADATA_SET_REQUEST_H
index d54267e1c8478b79c1727a5e9d87b0a574f9f3e3..4622c2789d006ed42e26fdb3f9e6803314ae00c7 100644 (file)
@@ -680,6 +680,80 @@ TEST_F(TestJournalReplay, UpdateFeatures) {
   ASSERT_EQ(0, ictx->operations->update_features(features, !enabled));
 }
 
+TEST_F(TestJournalReplay, MetadataSet) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  librbd::ImageCtx *ictx;
+
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, when_acquired_lock(ictx));
+
+  // get current commit position
+  int64_t initial_tag;
+  int64_t initial_entry;
+  get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+  // inject metadata_set op into journal
+  inject_into_journal(ictx, librbd::journal::MetadataSetEvent(1, "key", "value"));
+  inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+  close_image(ictx);
+
+  // replay journal
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, when_acquired_lock(ictx));
+
+  int64_t current_tag;
+  int64_t current_entry;
+  get_journal_commit_position(ictx, &current_tag, &current_entry);
+  ASSERT_EQ(initial_tag + 1, current_tag);
+  ASSERT_EQ(1, current_entry);
+
+  std::string value;
+  ASSERT_EQ(0, librbd::metadata_get(ictx, "key", &value));
+  ASSERT_EQ("value", value);
+
+  // verify lock ordering constraints
+  ASSERT_EQ(0, ictx->operations->metadata_set("key2", "value"));
+}
+
+TEST_F(TestJournalReplay, MetadataRemove) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  librbd::ImageCtx *ictx;
+
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, when_acquired_lock(ictx));
+
+  ASSERT_EQ(0, ictx->operations->metadata_set("key", "value"));
+
+  // get current commit position
+  int64_t initial_tag;
+  int64_t initial_entry;
+  get_journal_commit_position(ictx, &initial_tag, &initial_entry);
+
+  // inject metadata_remove op into journal
+  inject_into_journal(ictx, librbd::journal::MetadataRemoveEvent(1, "key"));
+  inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0));
+  close_image(ictx);
+
+  // replay journal
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, when_acquired_lock(ictx));
+
+  int64_t current_tag;
+  int64_t current_entry;
+  get_journal_commit_position(ictx, &current_tag, &current_entry);
+  ASSERT_EQ(initial_tag, current_tag);
+  ASSERT_EQ(initial_entry + 2, current_entry);
+
+  std::string value;
+  ASSERT_EQ(-ENOENT, librbd::metadata_get(ictx, "key", &value));
+
+  // verify lock ordering constraints
+  ASSERT_EQ(0, ictx->operations->metadata_set("key", "value"));
+  ASSERT_EQ(0, ictx->operations->metadata_remove("key"));
+}
+
 TEST_F(TestJournalReplay, ObjectPosition) {
   REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
index df38b9af058e3c88c84c88e38f48f514b86c22e5..92250645214c749cde2bbe12147dbc9161749844 100644 (file)
@@ -204,6 +204,22 @@ public:
                                   NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
   }
 
+  void expect_metadata_set(MockReplayImageCtx &mock_image_ctx,
+                           Context **on_finish, const char *key,
+                           const char *value) {
+    EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_set(StrEq(key),
+                                                                 StrEq(value), _))
+                  .WillOnce(DoAll(SaveArg<2>(on_finish),
+                                  NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+  }
+
+  void expect_metadata_remove(MockReplayImageCtx &mock_image_ctx,
+                              Context **on_finish, const char *key) {
+    EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_remove(StrEq(key), _))
+                  .WillOnce(DoAll(SaveArg<1>(on_finish),
+                                  NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
+  }
+
   void expect_refresh_image(MockReplayImageCtx &mock_image_ctx, bool required,
                             int r) {
     EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
@@ -1290,6 +1306,102 @@ TEST_F(TestMockJournalReplay, UpdateFeaturesEvent) {
   ASSERT_EQ(0, on_finish_safe.wait());
 }
 
+TEST_F(TestMockJournalReplay, MetadataSetEvent) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockReplayImageCtx mock_image_ctx(*ictx);
+  MockJournalReplay mock_journal_replay(mock_image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  Context *on_finish = nullptr;
+  expect_refresh_image(mock_image_ctx, false, 0);
+  expect_metadata_set(mock_image_ctx, &on_finish, "key", "value");
+
+  C_SaferCond on_start_ready;
+  C_SaferCond on_start_safe;
+  when_process(mock_journal_replay, EventEntry{MetadataSetEvent(123, "key", "value")},
+               &on_start_ready, &on_start_safe);
+  ASSERT_EQ(0, on_start_ready.wait());
+
+  C_SaferCond on_finish_ready;
+  C_SaferCond on_finish_safe;
+  when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+               &on_finish_ready, &on_finish_safe);
+
+  wait_for_op_invoked(&on_finish, 0);
+  ASSERT_EQ(0, on_start_safe.wait());
+  ASSERT_EQ(0, on_finish_ready.wait());
+  ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, MetadataRemoveEvent) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockReplayImageCtx mock_image_ctx(*ictx);
+  MockJournalReplay mock_journal_replay(mock_image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  Context *on_finish = nullptr;
+  expect_refresh_image(mock_image_ctx, false, 0);
+  expect_metadata_remove(mock_image_ctx, &on_finish, "key");
+
+  C_SaferCond on_start_ready;
+  C_SaferCond on_start_safe;
+  when_process(mock_journal_replay, EventEntry{MetadataRemoveEvent(123, "key")},
+               &on_start_ready, &on_start_safe);
+  ASSERT_EQ(0, on_start_ready.wait());
+
+  C_SaferCond on_finish_ready;
+  C_SaferCond on_finish_safe;
+  when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+               &on_finish_ready, &on_finish_safe);
+
+  wait_for_op_invoked(&on_finish, 0);
+  ASSERT_EQ(0, on_start_safe.wait());
+  ASSERT_EQ(0, on_finish_ready.wait());
+  ASSERT_EQ(0, on_finish_safe.wait());
+}
+
+TEST_F(TestMockJournalReplay, MetadataRemoveEventDNE) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockReplayImageCtx mock_image_ctx(*ictx);
+  MockJournalReplay mock_journal_replay(mock_image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  Context *on_finish = nullptr;
+  expect_refresh_image(mock_image_ctx, false, 0);
+  expect_metadata_remove(mock_image_ctx, &on_finish, "key");
+
+  C_SaferCond on_start_ready;
+  C_SaferCond on_start_safe;
+  when_process(mock_journal_replay, EventEntry{MetadataRemoveEvent(123, "key")},
+               &on_start_ready, &on_start_safe);
+  ASSERT_EQ(0, on_start_ready.wait());
+
+  C_SaferCond on_finish_ready;
+  C_SaferCond on_finish_safe;
+  when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+               &on_finish_ready, &on_finish_safe);
+
+  wait_for_op_invoked(&on_finish, -ENOENT);
+  ASSERT_EQ(0, on_start_safe.wait());
+  ASSERT_EQ(0, on_finish_ready.wait());
+  ASSERT_EQ(0, on_finish_safe.wait());
+}
+
 TEST_F(TestMockJournalReplay, UnknownEvent) {
   REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
index f026bc4b5ec103c6a92ece1b64d7d4433d70b0d2..94e39639e83329d03a9ca99e7dec433fca821589 100644 (file)
@@ -49,6 +49,11 @@ struct MockOperations {
   MOCK_METHOD4(execute_update_features, void(uint64_t features, bool enabled,
                                              Context *on_finish,
                                              uint64_t journal_op_tid));
+  MOCK_METHOD3(execute_metadata_set, void(const std::string &key,
+                                          const std::string &value,
+                                          Context *on_finish));
+  MOCK_METHOD2(execute_metadata_remove, void(const std::string &key,
+                                             Context *on_finish));
 };
 
 } // namespace librbd
index 798009aefcacbe0c3cb3ad69ae639e623a250f34..32de41a7efa8e94527d862a0ea204d5ad08020c3 100644 (file)
@@ -408,25 +408,25 @@ TEST_F(TestInternal, Metadata) {
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
-  r = librbd::metadata_set(ictx, it->first, "value1");
+  r = ictx->operations->metadata_set(it->first, "value1");
   ASSERT_EQ(0, r);
   ++it;
-  r = librbd::metadata_set(ictx, it->first, "value2");
+  r = ictx->operations->metadata_set(it->first, "value2");
   ASSERT_EQ(0, r);
   ++it;
-  r = librbd::metadata_set(ictx, it->first, "value3");
+  r = ictx->operations->metadata_set(it->first, "value3");
   ASSERT_EQ(0, r);
-  r = librbd::metadata_set(ictx, "abcd", "value4");
+  r = ictx->operations->metadata_set("abcd", "value4");
   ASSERT_EQ(0, r);
-  r = librbd::metadata_set(ictx, "xyz", "value5");
+  r = ictx->operations->metadata_set("xyz", "value5");
   ASSERT_EQ(0, r);
   map<string, bufferlist> pairs;
   r = librbd::metadata_list(ictx, "", 0, &pairs);
   ASSERT_EQ(0, r);
   ASSERT_EQ(5u, pairs.size());
-  r = librbd::metadata_remove(ictx, "abcd");
+  r = ictx->operations->metadata_remove("abcd");
   ASSERT_EQ(0, r);
-  r = librbd::metadata_remove(ictx, "xyz");
+  r = ictx->operations->metadata_remove("xyz");
   ASSERT_EQ(0, r);
   pairs.clear();
   r = librbd::metadata_list(ictx, "", 0, &pairs);
index b018f16c21b3045be3b8f2c5677a5d846cfc9606..3bb09fb24fa1fd1351d10f4a4d1408f3054b27aa 100644 (file)
@@ -472,8 +472,8 @@ TEST_F(TestMockImageSyncObjectCopyRequest, WriteSnaps) {
 }
 
 TEST_F(TestMockImageSyncObjectCopyRequest, Trim) {
-  ASSERT_EQ(0, metadata_set(m_remote_image_ctx,
-                           "conf_rbd_skip_partial_discard", "false"));
+  ASSERT_EQ(0, m_remote_image_ctx->operations->metadata_set(
+              "conf_rbd_skip_partial_discard", "false"));
   // scribble some data
   interval_set<uint64_t> one;
   scribble(m_remote_image_ctx, 10, 102400, &one);
index 4c39fd1b2bcb52342e6caa9cafdf9430bbb9dba2..24d0709999754144ba5301b11a8c404f7c231a86 100644 (file)
@@ -1005,3 +1005,48 @@ TEST_F(TestImageReplayer, UpdateFeatures)
 
   stop();
 }
+
+TEST_F(TestImageReplayer, MetadataSetRemove)
+{
+  const std::string KEY = "test_key";
+  const std::string VALUE = "test_value";
+
+  librbd::ImageCtx *ictx;
+  std::string value;
+
+  bootstrap();
+
+  start();
+
+  // Test metadata_set replication
+
+  open_remote_image(&ictx);
+  ASSERT_EQ(0, ictx->operations->metadata_set(KEY, VALUE));
+  value.clear();
+  ASSERT_EQ(0, librbd::metadata_get(ictx, KEY, &value));
+  ASSERT_EQ(VALUE, value);
+  close_image(ictx);
+
+  wait_for_replay_complete();
+
+  open_local_image(&ictx);
+  value.clear();
+  ASSERT_EQ(0, librbd::metadata_get(ictx, KEY, &value));
+  ASSERT_EQ(VALUE, value);
+  close_image(ictx);
+
+  // Test metadata_remove replication
+
+  open_remote_image(&ictx);
+  ASSERT_EQ(0, ictx->operations->metadata_remove(KEY));
+  ASSERT_EQ(-ENOENT, librbd::metadata_get(ictx, KEY, &value));
+  close_image(ictx);
+
+  wait_for_replay_complete();
+
+  open_local_image(&ictx);
+  ASSERT_EQ(-ENOENT, librbd::metadata_get(ictx, KEY, &value));
+  close_image(ictx);
+
+  stop();
+}