]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: add methods to set and get snapshot limits 9151/head
authorDouglas Fuller <dfuller@redhat.com>
Mon, 9 May 2016 18:21:21 +0000 (11:21 -0700)
committerDouglas Fuller <dfuller@redhat.com>
Tue, 7 Jun 2016 20:44:24 +0000 (13:44 -0700)
Add a subcommand, rbd snap limit set <image> --limit <integer>, to
limit the number of snapshots that may be made of a given image.
To clear, use rbd snap limit clear <image>. Add an omap
key, 'snap_limit' to the RBD header for implementation. Add object
classes to set and query the limit. Older OSDs will ignore the limit.

Fixes: http://tracker.ceph.com/issues/15706
Signed-off-by: Douglas Fuller <dfuller@redhat.com>
31 files changed:
doc/man/8/rbd.rst
src/cls/rbd/cls_rbd.cc
src/cls/rbd/cls_rbd_client.cc
src/cls/rbd/cls_rbd_client.h
src/include/rbd/librbd.h
src/include/rbd/librbd.hpp
src/librbd/CMakeLists.txt
src/librbd/Makefile.am
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/SnapshotLimitRequest.cc [new file with mode: 0644]
src/librbd/operation/SnapshotLimitRequest.h [new file with mode: 0644]
src/pybind/rbd/rbd.pyx
src/test/cli/rbd/help.t
src/test/cls_rbd/test_cls_rbd.cc
src/test/librbd/mock/MockOperations.h
src/test/librbd/test_librbd.cc
src/test/pybind/test_rbd.py
src/test/run-rbd-unit-tests.sh
src/tools/rbd/ArgumentTypes.cc
src/tools/rbd/ArgumentTypes.h
src/tools/rbd/action/Info.cc
src/tools/rbd/action/Snap.cc
src/tracing/librbd.tp

index 6a327e99888e6b01c605f76487c63277a6b30956..42ef2c850373eb4eba54e1ec5a0a03c74f1d68ee 100644 (file)
@@ -159,6 +159,10 @@ Parameters
    by examining the in-memory object map instead of querying RADOS for each
    object within the image.
 
+.. option:: --limit
+
+   Specifies the limit for the number of snapshots permitted.
+
 Commands
 ========
 
@@ -313,6 +317,13 @@ Commands
 
   This requires image format 2.
 
+:command:`snap limit set` [--limit] *limit* *image-spec*
+  Set a limit for the number of snapshots allowed on an image.
+
+:command:`snap limit clear` *image-spec*
+  Remove any previously set limit on the number of snapshots allowed on
+  an image.
+
 :command:`map` [-o | --options *map-options* ] [--read-only] *image-spec* | *snap-spec*
   Maps the specified image to a block device via the rbd kernel module.
 
index 78fe30808342e66accc0ba9af1559e5bf1fce3cf..979f81668ace61da2714f617b158931f4b1c96db 100644 (file)
@@ -108,6 +108,8 @@ cls_method_handle_t h_metadata_set;
 cls_method_handle_t h_metadata_remove;
 cls_method_handle_t h_metadata_list;
 cls_method_handle_t h_metadata_get;
+cls_method_handle_t h_snapshot_get_limit;
+cls_method_handle_t h_snapshot_set_limit;
 cls_method_handle_t h_old_snapshots_list;
 cls_method_handle_t h_old_snapshot_add;
 cls_method_handle_t h_old_snapshot_remove;
@@ -1499,6 +1501,7 @@ int snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
 {
   bufferlist snap_namebl, snap_idbl;
   cls_rbd_snap snap_meta;
+  uint64_t snap_limit;
 
   try {
     bufferlist::iterator iter = in->begin();
@@ -1542,7 +1545,16 @@ int snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
     return r;
   }
 
+  r = read_key(hctx, "snap_limit", &snap_limit);
+  if (r == -ENOENT) {
+    snap_limit = UINT64_MAX;
+  } else if (r < 0) {
+    CLS_ERR("Could not read snapshot limit off disk: %s", cpp_strerror(r).c_str());
+    return r;
+  }
+
   int max_read = RBD_MAX_KEYS_READ;
+  uint64_t total_read = 0;
   string last_read = RBD_SNAP_KEY_PREFIX;
   do {
     map<string, bufferlist> vals;
@@ -1551,6 +1563,12 @@ int snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
     if (r < 0)
       return r;
 
+    total_read += vals.size();
+    if (total_read >= snap_limit) {
+      CLS_ERR("Attempt to create snapshot over limit of %lu", snap_limit);
+      return -EDQUOT;
+    }
+
     for (map<string, bufferlist>::iterator it = vals.begin();
         it != vals.end(); ++it) {
       cls_rbd_snap old_meta;
@@ -2675,6 +2693,50 @@ int metadata_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
   return 0;
 }
 
+int snapshot_get_limit(cls_method_context_t hctx, bufferlist *in,
+                      bufferlist *out)
+{
+  int rc;
+  uint64_t snap_limit;
+
+  rc = read_key(hctx, "snap_limit", &snap_limit);
+  if (rc == -ENOENT) {
+    rc = 0;
+    ::encode(UINT64_MAX, *out);
+  } else {
+    ::encode(snap_limit, *out);
+  }
+
+  CLS_LOG(20, "read snapshot limit %lu", snap_limit);
+  return rc;
+}
+
+int snapshot_set_limit(cls_method_context_t hctx, bufferlist *in,
+                      bufferlist *out)
+{
+  int rc;
+  uint64_t new_limit;
+  bufferlist bl;
+
+  try {
+    bufferlist::iterator iter = in->begin();
+    ::decode(new_limit, iter);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+
+  if (new_limit == UINT64_MAX) {
+    CLS_LOG(20, "remove snapshot limit\n");
+    rc = cls_cxx_map_remove_key(hctx, "snap_limit");
+  } else {
+    CLS_LOG(20, "set snapshot limit to %lu\n", new_limit);
+    ::encode(new_limit, bl);
+    rc = cls_cxx_map_set_val(hctx, "snap_limit", &bl);
+  }
+
+  return rc;
+}
+
 
 /****************************** Old format *******************************/
 
@@ -2746,6 +2808,17 @@ int old_snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
   if (header->snap_seq > snap_id)
     return -ESTALE;
 
+  uint64_t snap_limit;
+  rc = read_key(hctx, "snap_limit", &snap_limit);
+  if (rc == -ENOENT) {
+    snap_limit = UINT64_MAX;
+  } else if (rc < 0) {
+    return rc;
+  }
+
+  if (header->snap_count >= snap_limit)
+    return -EDQUOT;
+
   const char *cur_snap_name;
   for (cur_snap_name = snap_names; cur_snap_name < end; cur_snap_name += strlen(cur_snap_name) + 1) {
     if (strncmp(cur_snap_name, snap_name, end - cur_snap_name) == 0)
@@ -2967,6 +3040,7 @@ int old_snapshot_rename(cls_method_context_t hctx, bufferlist *in, bufferlist *o
   return 0;
 }
 
+
 namespace mirror {
 
 static const std::string UUID("mirror_uuid");
@@ -4215,6 +4289,12 @@ void __cls_init()
   cls_register_cxx_method(h_class, "metadata_get",
                           CLS_METHOD_RD,
                          metadata_get, &h_metadata_get);
+  cls_register_cxx_method(h_class, "snapshot_get_limit",
+                         CLS_METHOD_RD,
+                         snapshot_get_limit, &h_snapshot_get_limit);
+  cls_register_cxx_method(h_class, "snapshot_set_limit",
+                         CLS_METHOD_WR,
+                         snapshot_set_limit, &h_snapshot_set_limit);
 
   /* methods for the rbd_children object */
   cls_register_cxx_method(h_class, "add_child",
index ec57f7f8daae2faba81896d7818568132d28f381..0a51f924c107e8d64cffa792b26eeb51f2fd06db 100644 (file)
@@ -653,6 +653,33 @@ namespace librbd {
       op->exec("rbd", "set_protection_status", in);
     }
 
+    int snapshot_get_limit(librados::IoCtx *ioctx, const std::string &oid,
+                          uint64_t *limit)
+    {
+      bufferlist in, out;
+      int r =  ioctx->exec(oid, "rbd", "snapshot_get_limit", in, out);
+
+      if (r < 0) {
+       return r;
+      }
+
+      try {
+       bufferlist::iterator iter = out.begin();
+       ::decode(*limit, iter);
+      } catch (const buffer::error &err) {
+       return -EBADMSG;
+      }
+
+      return 0;
+    }
+
+    void snapshot_set_limit(librados::ObjectWriteOperation *op, uint64_t limit)
+    {
+      bufferlist in;
+      ::encode(limit, in);
+      op->exec("rbd", "snapshot_set_limit", in);
+    }
+
     void get_stripe_unit_count_start(librados::ObjectReadOperation *op) {
       bufferlist empty_bl;
       op->exec("rbd", "get_stripe_unit_count", empty_bl);
index b3dd22eb3753b22309f91179edb34fa71ac7bcee..0696bd1e9f2ad1faed5d4286d8ee64855d780164 100644 (file)
@@ -122,6 +122,10 @@ namespace librbd {
                              snapid_t snap_id, uint8_t protection_status);
     void set_protection_status(librados::ObjectWriteOperation *op,
                                snapid_t snap_id, uint8_t protection_status);
+    int snapshot_get_limit(librados::IoCtx *ioctx, const std::string &oid,
+                          uint64_t *limit);
+    void snapshot_set_limit(librados::ObjectWriteOperation *op,
+                           uint64_t limit);
 
     void get_stripe_unit_count_start(librados::ObjectReadOperation *op);
     int get_stripe_unit_count_finish(bufferlist::iterator *it,
index fb61b8f70bd3cd0b3b345d54deff5d4e6f986573..c5616715044529249af466b6be8459876fe51729 100644 (file)
@@ -354,6 +354,23 @@ CEPH_RBD_API int rbd_snap_unprotect(rbd_image_t image, const char *snap_name);
  */
 CEPH_RBD_API int rbd_snap_is_protected(rbd_image_t image, const char *snap_name,
                                       int *is_protected);
+/**
+ * Get the current snapshot limit for an image. If no limit is set,
+ * UINT64_MAX is returned.
+ *
+ * @param limit pointer where the limit will be stored on success
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_snap_get_limit(rbd_image_t image, uint64_t *limit);
+
+/**
+ * Set a limit for the number of snapshots that may be taken of an image.
+ *
+ * @param limit the maximum number of snapshots allowed in the future.
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_snap_set_limit(rbd_image_t image, uint64_t limit);
+
 CEPH_RBD_API int rbd_snap_set(rbd_image_t image, const char *snapname);
 
 CEPH_RBD_API int rbd_flatten(rbd_image_t image);
index e4c434ab490f478f7174fff845f46421d07560cc..78c5a50b62c301a205f9f1f2f0a6117b428020f2 100644 (file)
@@ -247,6 +247,8 @@ public:
   int snap_is_protected(const char *snap_name, bool *is_protected);
   int snap_set(const char *snap_name);
   int snap_rename(const char *srcname, const char *dstname);
+  int snap_get_limit(uint64_t *limit);
+  int snap_set_limit(uint64_t limit);
 
   /* I/O */
   ssize_t read(uint64_t ofs, size_t len, ceph::bufferlist& bl);
index e75407ce031b845243639aea5da46d4bfa56bec5..5f8eba51dd6e9c4c05b02d263156c493c71d7bfa 100644 (file)
@@ -59,6 +59,7 @@ set(librbd_internal_srcs
   operation/SnapshotRenameRequest.cc
   operation/SnapshotRollbackRequest.cc
   operation/SnapshotUnprotectRequest.cc
+  operation/SnapshotLimitRequest.cc
   operation/TrimRequest.cc)
 
 add_library(rbd_api STATIC librbd.cc)
index 08c9738806ee21246fd2241625b4c9a675116c5d..4cf7f5559736f7f7e51856041bbeef2acdf48a0f 100644 (file)
@@ -64,6 +64,7 @@ librbd_internal_la_SOURCES = \
        librbd/operation/SnapshotRenameRequest.cc \
        librbd/operation/SnapshotRollbackRequest.cc \
        librbd/operation/SnapshotUnprotectRequest.cc \
+       librbd/operation/SnapshotLimitRequest.cc \
        librbd/operation/TrimRequest.cc
 noinst_LTLIBRARIES += librbd_internal.la
 
@@ -154,6 +155,7 @@ noinst_HEADERS += \
        librbd/operation/SnapshotRenameRequest.h \
        librbd/operation/SnapshotRollbackRequest.h \
        librbd/operation/SnapshotUnprotectRequest.h \
+       librbd/operation/SnapshotLimitRequest.h \
        librbd/operation/TrimRequest.h
 
 endif # WITH_RBD
index c523b912680d3d2a36a2139dbb57207edad72fdd..8e314e759453bf66d408f7012238332d11939402 100644 (file)
@@ -21,6 +21,7 @@
 #include "librbd/operation/SnapshotRenameRequest.h"
 #include "librbd/operation/SnapshotRollbackRequest.h"
 #include "librbd/operation/SnapshotUnprotectRequest.h"
+#include "librbd/operation/SnapshotLimitRequest.h"
 #include <set>
 #include <boost/bind.hpp>
 
@@ -1023,6 +1024,56 @@ void Operations<I>::execute_snap_unprotect(const char *snap_name,
   request->send();
 }
 
+template <typename I>
+int Operations<I>::snap_set_limit(uint64_t limit) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": limit=" << limit << dendl;
+
+  if (m_image_ctx.read_only) {
+    return -EROFS;
+  }
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0) {
+    return r;
+  }
+
+  {
+    RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+    C_SaferCond limit_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_snap_set_limit(limit, &limit_ctx);
+    r = limit_ctx.wait();
+  }
+
+  return r;
+}
+
+template <typename I>
+void Operations<I>::execute_snap_set_limit(const uint64_t limit,
+                                          Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": limit=" << limit
+                << dendl;
+
+  operation::SnapshotLimitRequest<I> *request =
+    new operation::SnapshotLimitRequest<I>(m_image_ctx, on_finish, limit);
+  request->send();
+}
+
 template <typename I>
 int Operations<I>::prepare_image_update() {
   assert(m_image_ctx.owner_lock.is_locked() &&
index 95af4dcf1e96369c1b4c5f046d368c4c9abb2e1b..fb0f8379a1eac510eb2503c573c5c8ab71e3636c 100644 (file)
@@ -58,6 +58,9 @@ public:
   int snap_unprotect(const char *snap_name);
   void execute_snap_unprotect(const char *snap_name, Context *on_finish);
 
+  int snap_set_limit(uint64_t limit);
+  void execute_snap_set_limit(uint64_t limit, Context *on_finish);
+
   int prepare_image_update();
 
 private:
index 92fab7f4cf12b9d3e27cae6851ed3423f96466a1..817de81f4169fb82d9fc11f66a645a794e5a471c 100644 (file)
@@ -2148,6 +2148,17 @@ remove_mirroring_image:
     return 0;
   }
 
+  int snap_get_limit(ImageCtx *ictx, uint64_t *limit)
+  {
+    return cls_client::snapshot_get_limit(&ictx->md_ctx, ictx->header_oid,
+                                         limit);
+  }
+
+  int snap_set_limit(ImageCtx *ictx, uint64_t limit)
+  {
+    return ictx->operations->snap_set_limit(limit);
+  }
+
   struct CopyProgressCtx {
     explicit CopyProgressCtx(ProgressContext &p)
       : destictx(NULL), src_size(0), prog_ctx(p)
index dcb03509d50989df95ecd2117f4f42fd09e08a0a..74f4b98dca9df25876d655ebf10bc8d4dbad0747 100644 (file)
@@ -128,6 +128,8 @@ namespace librbd {
             ProgressContext& prog_ctx);
   int snap_list(ImageCtx *ictx, std::vector<snap_info_t>& snaps);
   int snap_exists(ImageCtx *ictx, const char *snap_name, bool *exists);
+  int snap_get_limit(ImageCtx *ictx, uint64_t *limit);
+  int snap_set_limit(ImageCtx *ictx, uint64_t limit);
   int snap_is_protected(ImageCtx *ictx, const char *snap_name,
                        bool *is_protected);
   int copy(ImageCtx *ictx, IoCtx& dest_md_ctx, const char *destname,
index c6c4f55b602f1a25e0ab6c4e1391ff37c3462fc8..4bc714ce9fefe26fde49b306e096af7d501cb86f 100644 (file)
@@ -86,6 +86,10 @@ struct ExecuteOp : public Context {
                                           on_op_complete);
   }
 
+  void execute(const journal::SnapLimitEvent &_) {
+    image_ctx.operations->execute_snap_set_limit(event.limit, on_op_complete);
+  }
+
   virtual void finish(int r) override {
     RWLock::RLocker owner_locker(image_ctx.owner_lock);
     execute(event);
@@ -599,6 +603,29 @@ void Replay<I>::handle_event(const journal::DemoteEvent &event,
   on_safe->complete(0);
 }
 
+template <typename I>
+void Replay<I>::handle_event(const journal::SnapLimitEvent &event,
+                            Context *on_ready, Context *on_safe) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 20) << this << " " << __func__ << ": Snap limit 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::SnapLimitEvent>(m_image_ctx,
+                                                          event,
+                                                          on_op_complete));
+
+  on_ready->complete(0);
+}
+
 template <typename I>
 void Replay<I>::handle_event(const journal::UnknownEvent &event,
                             Context *on_ready, Context *on_safe) {
index aeca5ba26db681f453eda6385c5c18bad34431ec..ceb67466b011c7ef5946e223ce0e80e112c1a056 100644 (file)
@@ -154,6 +154,8 @@ private:
                     Context *on_safe);
   void handle_event(const DemoteEvent &event, Context *on_ready,
                     Context *on_safe);
+  void handle_event(const SnapLimitEvent &event, Context *on_ready,
+                   Context *on_safe);
   void handle_event(const UnknownEvent &event, Context *on_ready,
                     Context *on_safe);
 
index 8f9f942733acf10622e8461ff8ea9a8467a35bbd..91a9b678e8f932c7eb550c550464eaacd79d3d9a 100644 (file)
@@ -154,6 +154,21 @@ void SnapEventBase::dump(Formatter *f) const {
   f->dump_string("snap_name", snap_name);
 }
 
+void SnapLimitEvent::encode(bufferlist &bl) const {
+  OpEventBase::encode(bl);
+  ::encode(limit, bl);
+}
+
+void SnapLimitEvent::decode(__u8 version, bufferlist::iterator& it) {
+  OpEventBase::decode(version, it);
+  ::decode(limit, it);
+}
+
+void SnapLimitEvent::dump(Formatter *f) const {
+  OpEventBase::dump(f);
+  f->dump_unsigned("limit", limit);
+}
+
 void SnapRenameEvent::encode(bufferlist& bl) const {
   SnapEventBase::encode(bl);
   ::encode(snap_id, bl);
index 4008a0f15bfaf719adb5a1380ad067b5ad7b5de6..11a0621917c201d866ec51f9063c3ec6c1d734aa 100644 (file)
@@ -35,7 +35,8 @@ enum EventType {
   EVENT_TYPE_RENAME         = 10,
   EVENT_TYPE_RESIZE         = 11,
   EVENT_TYPE_FLATTEN        = 12,
-  EVENT_TYPE_DEMOTE         = 13
+  EVENT_TYPE_DEMOTE         = 13,
+  EVENT_TYPE_SNAP_LIMIT     = 14
 };
 
 struct AioDiscardEvent {
@@ -202,6 +203,21 @@ struct SnapUnprotectEvent : public SnapEventBase {
   using SnapEventBase::dump;
 };
 
+struct SnapLimitEvent : public OpEventBase {
+  static const EventType TYPE = EVENT_TYPE_SNAP_LIMIT;
+  uint64_t limit;
+
+  SnapLimitEvent() {
+  }
+  SnapLimitEvent(uint64_t op_tid, const uint64_t _limit)
+    : OpEventBase(op_tid), limit(_limit) {
+  }
+
+  void encode(bufferlist& bl) const;
+  void decode(__u8 version, bufferlist::iterator& it);
+  void dump(Formatter *f) const;
+};
+
 struct SnapRollbackEvent : public SnapEventBase {
   static const EventType TYPE = EVENT_TYPE_SNAP_ROLLBACK;
 
@@ -291,6 +307,7 @@ typedef boost::variant<AioDiscardEvent,
                        ResizeEvent,
                        FlattenEvent,
                        DemoteEvent,
+                      SnapLimitEvent,
                        UnknownEvent> Event;
 
 struct EventEntry {
index 2cb5132ed24bf927fc8cec4fc55464eb137a83c5..ab7c04f478107bf002c3750d90b88cc5ebde576d 100644 (file)
@@ -983,6 +983,25 @@ namespace librbd {
     return r;
   }
 
+  int Image::snap_get_limit(uint64_t *limit)
+  {
+    ImageCtx *ictx = (ImageCtx *)ctx;
+    tracepoint(librbd, snap_get_limit_enter, ictx, ictx->name.c_str());
+    int r = librbd::snap_get_limit(ictx, limit);
+    tracepoint(librbd, snap_get_limit_exit, r, *limit);
+    return r;
+  }
+
+  int Image::snap_set_limit(uint64_t limit)
+  {
+    ImageCtx *ictx = (ImageCtx *)ctx;
+
+    tracepoint(librbd, snap_set_limit_enter, ictx, ictx->name.c_str(), limit);
+    int r = ictx->operations->snap_set_limit(limit);
+    tracepoint(librbd, snap_set_limit_exit, r);
+    return r;
+  }
+
   int Image::snap_set(const char *snap_name)
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
@@ -2224,6 +2243,24 @@ extern "C" int rbd_snap_is_protected(rbd_image_t image, const char *snap_name,
   return 0;
 }
 
+extern "C" int rbd_snap_get_limit(rbd_image_t image, uint64_t *limit)
+{
+  librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
+  tracepoint(librbd, snap_get_limit_enter, ictx, ictx->name.c_str());
+  int r = librbd::snap_get_limit(ictx, limit);
+  tracepoint(librbd, snap_get_limit_exit, r, *limit);
+  return r;
+}
+
+extern "C" int rbd_snap_set_limit(rbd_image_t image, uint64_t limit)
+{
+  librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
+  tracepoint(librbd, snap_set_limit_enter, ictx, ictx->name.c_str(), limit);
+  int r = librbd::snap_set_limit(ictx, limit);
+  tracepoint(librbd, snap_set_limit_exit, r);
+  return r;
+}
+
 extern "C" int rbd_snap_set(rbd_image_t image, const char *snap_name)
 {
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
diff --git a/src/librbd/operation/SnapshotLimitRequest.cc b/src/librbd/operation/SnapshotLimitRequest.cc
new file mode 100644 (file)
index 0000000..670e0f0
--- /dev/null
@@ -0,0 +1,67 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/operation/SnapshotLimitRequest.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::SnapshotLimitRequest: "
+
+namespace librbd {
+namespace operation {
+
+template <typename I>
+SnapshotLimitRequest<I>::SnapshotLimitRequest(I &image_ctx,
+                                             Context *on_finish,
+                                             uint64_t limit)
+  : Request<I>(image_ctx, on_finish), m_snap_limit(limit) {
+}
+
+template <typename I>
+void SnapshotLimitRequest<I>::send_op() {
+  send_limit_snaps();
+}
+
+template <typename I>
+bool SnapshotLimitRequest<I>::should_complete(int r) {
+  I &image_ctx = this->m_image_ctx;
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl;
+  }
+  return true;
+}
+
+template <typename I>
+void SnapshotLimitRequest<I>::send_limit_snaps() {
+  I &image_ctx = this->m_image_ctx;
+  assert(image_ctx.owner_lock.is_locked());
+
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << dendl;
+
+  {
+    RWLock::RLocker md_locker(image_ctx.md_lock);
+    RWLock::RLocker snap_locker(image_ctx.snap_lock);
+
+    librados::ObjectWriteOperation op;
+    cls_client::snapshot_set_limit(&op, m_snap_limit);
+
+    librados::AioCompletion *rados_completion =
+      this->create_callback_completion();
+    int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, rados_completion,
+                                        &op);
+    assert(r == 0);
+    rados_completion->release();
+  }
+}
+
+} // namespace operation
+} // namespace librbd
+
+template class librbd::operation::SnapshotLimitRequest<librbd::ImageCtx>;
diff --git a/src/librbd/operation/SnapshotLimitRequest.h b/src/librbd/operation/SnapshotLimitRequest.h
new file mode 100644 (file)
index 0000000..bcd205b
--- /dev/null
@@ -0,0 +1,44 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_OPERATION_SNAPSHOT_LIMIT_REQUEST_H
+#define CEPH_LIBRBD_OPERATION_SNAPSHOT_LIMIT_REQUEST_H
+
+#include "librbd/operation/Request.h"
+#include <iosfwd>
+#include <string>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace operation {
+
+template <typename ImageCtxT = ImageCtx>
+class SnapshotLimitRequest : public Request<ImageCtxT> {
+public:
+  SnapshotLimitRequest(ImageCtxT &image_ctx, Context *on_finish,
+                      uint64_t limit);
+
+protected:
+  virtual void send_op();
+  virtual bool should_complete(int r);
+
+  virtual journal::Event create_event(uint64_t op_tid) const {
+    return journal::SnapLimitEvent(op_tid, m_snap_limit);
+  }
+
+private:
+  uint64_t m_snap_limit;
+
+  void send_limit_snaps();
+};
+
+} // namespace operation
+} // namespace librbd
+
+extern template class librbd::operation::SnapshotLimitRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_OPERATION_SNAPSHOT_LIMIT_REQUEST_H
index 23a9895cfcb8f8dfc1976b31332ec5e9255fe9d9..ba8910ebfa6e12bc7d7cd453c74ce098a7cd1026 100644 (file)
@@ -35,6 +35,9 @@ cdef extern from "Python.h":
 
 ctypedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void* ptr)
 
+cdef extern from "limits.h":
+    cdef uint64_t INT64_MAX
+
 cdef extern from "rbd/librbd.h" nogil:
     enum:
         _RBD_FEATURE_LAYERING "RBD_FEATURE_LAYERING"
@@ -148,6 +151,8 @@ cdef extern from "rbd/librbd.h" nogil:
     int rbd_snap_unprotect(rbd_image_t image, const char *snap_name)
     int rbd_snap_is_protected(rbd_image_t image, const char *snap_name,
                               int *is_protected)
+    int rbd_snap_get_limit(rbd_image_t image, uint64_t *limit)
+    int rbd_snap_set_limit(rbd_image_t image, uint64_t limit)
     int rbd_snap_set(rbd_image_t image, const char *snapname)
     int rbd_flatten(rbd_image_t image)
     int rbd_rebuild_object_map(rbd_image_t image, librbd_progress_fn_t cb,
@@ -266,6 +271,9 @@ class ConnectionShutdown(Error):
 class Timeout(Error):
     pass
 
+class DiskQuotaExceeded(Error):
+    pass
+
 
 cdef errno_to_exception = {
     errno.EPERM     : PermissionError,
@@ -281,6 +289,7 @@ cdef errno_to_exception = {
     errno.EDOM      : ArgumentOutOfRange,
     errno.ESHUTDOWN : ConnectionShutdown,
     errno.ETIMEDOUT : Timeout,
+    errno.EDQUOT    : DiskQuotaExceeded,
 }
 
 cdef make_ex(ret, msg):
@@ -1031,6 +1040,45 @@ cdef class Image(object):
             raise make_ex(ret, 'error checking if snapshot %s@%s is protected' % (self.name, name))
         return is_protected == 1
 
+    def get_snap_limit(self):
+        """
+        Get the snapshot limit for an image.
+        """
+
+        cdef:
+            uint64_t limit
+        with nogil:
+            ret = rbd_snap_get_limit(self.image, &limit)
+        if ret != 0:
+            raise make_ex(ret, 'error getting snapshot limit for %s' % self.name)
+        return limit
+
+    def set_snap_limit(self, limit):
+        """
+        Set the snapshot limit for an image.
+
+        :param limit: the new limit to set
+        """
+
+        cdef:
+            uint64_t _limit = limit
+        with nogil:
+            ret = rbd_snap_set_limit(self.image, _limit)
+        if ret != 0:
+            raise make_ex(ret, 'error setting snapshot limit for %s' % self.name)
+        return ret
+
+    def remove_snap_limit(self):
+        """
+        Remove the snapshot limit for an image, essentially setting
+        the limit to the maximum size allowed by the implementation.
+        """
+        with nogil:
+            ret = rbd_snap_set_limit(self.image, UINT64_MAX)
+        if ret != 0:
+            raise make_ex(ret, 'error removing snapshot limit for %s' % self.name)
+        return ret
+
     def set_snap(self, name):
         """
         Set the snapshot to read from. Writes will raise ReadOnlyImage
index 4848370feb4a7e239e83c737d7103891681ce53d..de34cee4c346306e0f5e8b8208eb9ec9bd7d3180 100644 (file)
@@ -66,6 +66,8 @@
       resize                      Resize (expand or shrink) image.
       showmapped                  Show the rbd images mapped by the kernel.
       snap create (snap add)      Create a snapshot.
+      snap limit clear            Remove snapshot limit.
+      snap limit set              Limit the number of snapshots.
       snap list (snap ls)         Dump list of image snapshots.
       snap protect                Prevent a snapshot from being deleted.
       snap purge                  Deletes all snapshots.
     --image arg          image name
     --snap arg           snapshot name
   
+  rbd help snap limit clear
+  usage: rbd snap limit clear [--pool <pool>] [--image <image>] 
+                              <image-spec> 
+  
+  Remove snapshot limit.
+  
+  Positional arguments
+    <image-spec>         image specification
+                         (example: [<pool-name>/]<image-name>)
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --image arg          image name
+  
+  rbd help snap limit set
+  usage: rbd snap limit set [--pool <pool>] [--image <image>] [--limit <limit>] 
+                            <image-spec> 
+  
+  Limit the number of snapshots.
+  
+  Positional arguments
+    <image-spec>         image specification
+                         (example: [<pool-name>/]<image-name>)
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --image arg          image name
+    --limit arg          maximum allowed snapshot count
+  
   rbd help snap list
   usage: rbd snap list [--pool <pool>] [--image <image>] [--format <format>] 
                        [--pretty-format] 
index f438b0fac6471f8c50153ec5b0f5dc9036c0da6e..4552bf38426a4fe403b0ffdc9f34422c23d115d8 100644 (file)
@@ -1,4 +1,4 @@
-// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 // vim: ts=8 sw=2 smarttab
 
 #include "common/ceph_context.h"
@@ -519,6 +519,36 @@ TEST_F(TestClsRbd, protection_status)
   ioctx.close();
 }
 
+TEST_F(TestClsRbd, snapshot_limits)
+{
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  librados::ObjectWriteOperation op;
+  string oid = get_temp_image_name();
+  uint64_t limit;
+  
+  ASSERT_EQ(-ENOENT, snapshot_get_limit(&ioctx, oid, &limit));
+  
+  ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, RBD_FEATURE_LAYERING, oid));
+
+  snapshot_set_limit(&op, 2);
+
+  ASSERT_EQ(0, ioctx.operate(oid, &op));
+
+  ASSERT_EQ(0, snapshot_get_limit(&ioctx, oid, &limit));
+  ASSERT_EQ(2, limit);
+
+  ASSERT_EQ(0, snapshot_add(&ioctx, oid, 10, "snap1"));
+  ASSERT_EQ(0, snapshot_add(&ioctx, oid, 20, "snap2"));
+  ASSERT_EQ(-EDQUOT, snapshot_add(&ioctx, oid, 30, "snap3"));
+
+  ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 10));
+  ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 20));
+
+  ioctx.close();
+}
+
 TEST_F(TestClsRbd, parents)
 {
   librados::IoCtx ioctx;
index 5bbd9b364968e6eccd05bbce4165b33b0f956ac9..5d4e2221e537dab29beac544f75d80e5ebf1a3ea 100644 (file)
@@ -39,6 +39,8 @@ struct MockOperations {
                                           Context *on_finish));
   MOCK_METHOD2(execute_snap_unprotect, void(const char *snap_name,
                                             Context *on_finish));
+  MOCK_METHOD2(execute_snap_set_limit, void(uint64_t limit,
+                                           Context *on_finish));
 };
 
 } // namespace librbd
index c3754610b12d05469d9f1f68e004dbcbfe0da8b9..44149b45869a10103e7845ded0296cf8c2586458 100644 (file)
@@ -1,4 +1,4 @@
-// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 // vim: ts=8 sw=2 smarttab
 /*
  * Ceph - scalable distributed file system
@@ -2985,6 +2985,69 @@ TEST_F(TestLibRBD, Flatten)
   ASSERT_PASSED(validate_object_map, clone_image);
 }
 
+TEST_F(TestLibRBD, SnapshotLimit)
+{
+  rados_ioctx_t ioctx;
+  rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+  rbd_image_t image;
+  int order = 0;
+  std::string name = get_temp_image_name();
+  uint64_t size = 2 << 20;
+  uint64_t limit;
+
+  ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+  ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+  ASSERT_EQ(0, rbd_snap_get_limit(image, &limit));
+  ASSERT_EQ(UINT64_MAX, limit);
+  ASSERT_EQ(0, rbd_snap_set_limit(image, 2));
+  ASSERT_EQ(0, rbd_snap_get_limit(image, &limit));
+  ASSERT_EQ(2, limit);
+
+  ASSERT_EQ(0, rbd_snap_create(image, "snap1"));
+  ASSERT_EQ(0, rbd_snap_create(image, "snap2"));
+  ASSERT_EQ(-EDQUOT, rbd_snap_create(image, "snap3"));
+  ASSERT_EQ(0, rbd_snap_set_limit(image, UINT64_MAX));
+  ASSERT_EQ(0, rbd_snap_create(image, "snap3"));
+  ASSERT_EQ(0, rbd_close(image));
+
+  rados_ioctx_destroy(ioctx);
+}
+  
+
+TEST_F(TestLibRBD, SnapshotLimitPP)
+{
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+  {
+    librbd::RBD rbd;
+    librbd::Image image;
+    std::string name = get_temp_image_name();
+    uint64_t size = 2 << 20;
+    int order = 0;
+    uint64_t limit;
+
+    ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+    ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+    ASSERT_EQ(0, image.snap_get_limit(&limit));
+    ASSERT_EQ(UINT64_MAX, limit);
+    ASSERT_EQ(0, image.snap_set_limit(2));
+    ASSERT_EQ(0, image.snap_get_limit(&limit));
+    ASSERT_EQ(2, limit);
+
+    ASSERT_EQ(0, image.snap_create("snap1"));
+    ASSERT_EQ(0, image.snap_create("snap2"));
+    ASSERT_EQ(-EDQUOT, image.snap_create("snap3"));
+    ASSERT_EQ(0, image.snap_set_limit(UINT64_MAX));
+    ASSERT_EQ(0, image.snap_create("snap3"));
+  }
+
+  ioctx.close();
+}
+
 TEST_F(TestLibRBD, RebuildObjectMapViaLockOwner)
 {
   REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_OBJECT_MAP);
index 04a3a3820da920861e7cb1f4d0f66bb311ea2f9e..bc34b08406c326d99da7d3fde301b3c32de07e83 100644 (file)
@@ -12,7 +12,7 @@ from rados import (Rados,
                    LIBRADOS_OP_FLAG_FADVISE_RANDOM)
 from rbd import (RBD, Image, ImageNotFound, InvalidArgument, ImageExists,
                  ImageBusy, ImageHasSnapshots, ReadOnlyImage,
-                 FunctionNotSupported, ArgumentOutOfRange,
+                 FunctionNotSupported, ArgumentOutOfRange, DiskQuotaExceeded,
                  RBD_FEATURE_LAYERING, RBD_FEATURE_STRIPINGV2,
                  RBD_FEATURE_EXCLUSIVE_LOCK)
 
@@ -502,6 +502,19 @@ class TestImage(object):
         assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1')
         assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1')
 
+    def test_limit_snaps(self):
+        self.image.set_snap_limit(2)
+        eq(2, self.image.get_snap_limit())
+        self.image.create_snap('snap1')
+        self.image.create_snap('snap2')
+        assert_raises(DiskQuotaExceeded, self.image.create_snap, 'snap3')
+        self.image.remove_snap_limit()
+        self.image.create_snap('snap3')
+
+        self.image.remove_snap('snap1')
+        self.image.remove_snap('snap2')
+        self.image.remove_snap('snap3')
+
     @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
     def test_remove_with_exclusive_lock(self):
         assert_raises(ImageBusy, remove_image)
index b143436303873373fb5b40fe2f479a805b342202..be1c6d814f6a948372d268ff87b525537f6a167e 100755 (executable)
@@ -5,7 +5,9 @@
 source $(dirname $0)/detect-build-env-vars.sh
 PATH="$CEPH_BIN:$PATH"
 
+unset RBD_FEATURES
 unittest_librbd
+
 for i in 0 1 61 109
 do
     RBD_FEATURES=$i unittest_librbd
index 94355bbb63f3590043595d382a7ec6ad2ca8b1fe..ec0aa3e0507b8744893ab124065ba9c71fb47f5f 100644 (file)
@@ -255,6 +255,13 @@ void add_path_options(boost::program_options::options_description *pos,
     (PATH.c_str(), po::value<std::string>(), description.c_str());
 }
 
+void add_limit_option(po::options_description *opt) {
+  std::string description = "maximum allowed snapshot count";
+
+  opt->add_options()
+    (LIMIT.c_str(), po::value<uint64_t>(), description.c_str());
+}
+
 void add_no_progress_option(boost::program_options::options_description *opt) {
   opt->add_options()
     (NO_PROGRESS.c_str(), po::bool_switch(), "disable progress output");
index 50c74aa8ff3bc76e8f9271500e12f047a564ddf4..d8935ae9fcbe777ddcca396771d82dc8fd64dc68 100644 (file)
@@ -77,6 +77,8 @@ static const std::string PRETTY_FORMAT("pretty-format");
 static const std::string VERBOSE("verbose");
 static const std::string NO_ERROR("no-error");
 
+static const std::string LIMIT("limit");
+
 static const std::set<std::string> SWITCH_ARGUMENTS = {
   WHOLE_OBJECT, NO_PROGRESS, PRETTY_FORMAT, VERBOSE, NO_ERROR};
 
@@ -111,6 +113,7 @@ struct JournalObjectSize {};
 std::string get_name_prefix(ArgumentModifier modifier);
 std::string get_description_prefix(ArgumentModifier modifier);
 
+
 void add_pool_option(boost::program_options::options_description *opt,
                      ArgumentModifier modifier,
                      const std::string &desc_suffix = "");
@@ -159,6 +162,8 @@ void add_path_options(boost::program_options::options_description *pos,
                       boost::program_options::options_description *opt,
                       const std::string &description);
 
+void add_limit_option(boost::program_options::options_description *opt);
+
 void add_no_progress_option(boost::program_options::options_description *opt);
 
 void add_format_options(boost::program_options::options_description *opt);
index 8e31e45028da0a23c7dc3bb91bf7f6050c62277a..65635e9b2a25bdfb35553c1bc302959f3eb19c89 100644 (file)
@@ -69,7 +69,7 @@ static int do_show_info(const char *imgname, librbd::Image& image,
   librbd::image_info_t info;
   std::string parent_pool, parent_name, parent_snapname;
   uint8_t old_format;
-  uint64_t overlap, features, flags;
+  uint64_t overlap, features, flags, snap_limit;
   bool snap_protected = false;
   librbd::mirror_image_info_t mirror_image;
   int r;
@@ -108,6 +108,10 @@ static int do_show_info(const char *imgname, librbd::Image& image,
     }
   }
 
+  r = image.snap_get_limit(&snap_limit);
+  if (r < 0)
+    return r;
+
   char prefix[RBD_MAX_BLOCK_NAME_SIZE + 1];
   strncpy(prefix, info.block_name_prefix, RBD_MAX_BLOCK_NAME_SIZE);
   prefix[RBD_MAX_BLOCK_NAME_SIZE] = '\0';
@@ -150,6 +154,14 @@ static int do_show_info(const char *imgname, librbd::Image& image,
     }
   }
 
+  if (snap_limit < UINT64_MAX) {
+    if (f) {
+      f->dump_unsigned("snapshot_limit", snap_limit);
+    } else {
+      std::cout << "\tsnapshot_limit: " << snap_limit << std::endl;
+    }
+  }
+
   // parent info, if present
   if ((image.parent_info(&parent_pool, &parent_name, &parent_snapname) == 0) &&
       parent_name.length() > 0) {
index 345e747fb9ca11d28cd82da1bc84331a041db1e5..1557c8d574e96124632c79d6009202dfd143ccb7 100644 (file)
@@ -148,6 +148,16 @@ int do_unprotect_snap(librbd::Image& image, const char *snapname)
   return 0;
 }
 
+int do_set_limit(librbd::Image& image, uint64_t limit)
+{
+  return image.snap_set_limit(limit);
+}
+
+int do_clear_limit(librbd::Image& image)
+{
+  return image.snap_set_limit(UINT64_MAX);
+}
+
 void get_list_arguments(po::options_description *positional,
                         po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -409,6 +419,80 @@ int execute_unprotect(const po::variables_map &vm) {
   return 0;
 }
 
+void get_set_limit_arguments(po::options_description *pos,
+                            po::options_description *opt) {
+  at::add_image_spec_options(pos, opt, at::ARGUMENT_MODIFIER_NONE);
+  at::add_limit_option(opt);
+}
+
+int execute_set_limit(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  uint64_t limit;
+
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE);
+
+  if (vm.count(at::LIMIT)) {
+    limit = vm[at::LIMIT].as<uint64_t>();
+  } else {
+    return -ERANGE;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                &io_ctx, &image);
+  if (r < 0) {
+      return r;
+  }
+
+  r = do_set_limit(image, limit);
+  if (r < 0) {
+    std::cerr << "rbd: setting snapshot limit failed: " << cpp_strerror(r)
+             << std::endl;
+    return r;
+  }
+  return 0;
+}
+
+void get_clear_limit_arguments(po::options_description *pos,
+                              po::options_description *opt) {
+  at::add_image_spec_options(pos, opt, at::ARGUMENT_MODIFIER_NONE);
+}
+
+int execute_clear_limit(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE);
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                &io_ctx, &image);
+  if (r < 0) {
+      return r;
+  }
+
+  r = do_clear_limit(image);
+  if (r < 0) {
+    std::cerr << "rbd: clearing snapshot limit failed: " << cpp_strerror(r)
+             << std::endl;
+    return r;
+  }
+  return 0;
+}
+
 void get_rename_arguments(po::options_description *positional,
                           po::options_description *options) {
   at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_SOURCE);
@@ -488,6 +572,12 @@ Shell::Action action_protect(
 Shell::Action action_unprotect(
   {"snap", "unprotect"}, {}, "Allow a snapshot to be deleted.", "",
   &get_unprotect_arguments, &execute_unprotect);
+Shell::Action action_set_limit(
+  {"snap", "limit", "set"}, {}, "Limit the number of snapshots.", "",
+  &get_set_limit_arguments, &execute_set_limit);
+Shell::Action action_clear_limit(
+  {"snap", "limit", "clear"}, {}, "Remove snapshot limit.", "",
+  &get_clear_limit_arguments, &execute_clear_limit);
 Shell::Action action_rename(
   {"snap", "rename"}, {}, "Rename a snapshot.", "",
   &get_rename_arguments, &execute_rename);
index f91e4e3ee93b38841cee1959867476be7a84f701..c162deaf5dd678b9f6e6ae19c7ad566bcfcc304e 100644 (file)
@@ -1290,6 +1290,46 @@ TRACEPOINT_EVENT(librbd, snap_exists_exit,
     )
 )
 
+TRACEPOINT_EVENT(librbd, snap_get_limit_enter,
+    TP_ARGS(
+        void*, imagectx,
+        const char*, name),
+    TP_FIELDS(
+        ctf_integer_hex(void*, imagectx, imagectx)
+        ctf_string(name, name)
+    )
+)
+
+TRACEPOINT_EVENT(librbd, snap_get_limit_exit,
+    TP_ARGS(
+        int, retval,
+        uint64_t, limit),
+    TP_FIELDS(
+        ctf_integer(int, retval, retval)
+        ctf_integer(uint64_t, limit, limit)
+    )
+)
+
+TRACEPOINT_EVENT(librbd, snap_set_limit_enter,
+    TP_ARGS(
+        void*,imagectx,
+       const char*, name,
+        const int, limit),
+    TP_FIELDS(
+        ctf_integer_hex(void*, imagectx, imagectx)
+       ctf_string(name, name)
+        ctf_integer(int, limit, limit)
+    )
+)
+
+TRACEPOINT_EVENT(librbd, snap_set_limit_exit,
+    TP_ARGS(
+        int, retval),
+    TP_FIELDS(
+        ctf_integer(int, retval, retval)
+    )
+)
+
 TRACEPOINT_EVENT(librbd, snap_set_enter,
     TP_ARGS(
         void*, imagectx,