From d41de816f0dd8757e08033b22e2c0f2fae63410e Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Mon, 2 Sep 2019 08:48:30 +0100 Subject: [PATCH] librbd: new mirroring snapshot namespace Signed-off-by: Mykola Golub --- src/cls/rbd/cls_rbd_types.cc | 85 +++++++++++++++++++++++++ src/cls/rbd/cls_rbd_types.h | 88 +++++++++++++++++++++++++- src/include/rbd/librbd.h | 33 +++++++++- src/include/rbd/librbd.hpp | 18 ++++++ src/librbd/api/Snapshot.cc | 92 +++++++++++++++++++++++++++ src/librbd/api/Snapshot.h | 8 +++ src/librbd/librbd.cc | 110 ++++++++++++++++++++++++++++++++ src/pybind/rbd/rbd.pyx | 119 +++++++++++++++++++++++++++++++++++ src/tools/rbd/action/Snap.cc | 84 ++++++++++++++++++++----- 9 files changed, 617 insertions(+), 20 deletions(-) diff --git a/src/cls/rbd/cls_rbd_types.cc b/src/cls/rbd/cls_rbd_types.cc index 6a8c1c476a5..cfdde40dd72 100644 --- a/src/cls/rbd/cls_rbd_types.cc +++ b/src/cls/rbd/cls_rbd_types.cc @@ -684,6 +684,50 @@ void TrashSnapshotNamespace::dump(Formatter *f) const { << original_snapshot_namespace_type; } +void MirrorPrimarySnapshotNamespace::encode(bufferlist& bl) const { + using ceph::encode; + encode(demoted, bl); + encode(mirror_peers, bl); +} + +void MirrorPrimarySnapshotNamespace::decode(bufferlist::const_iterator& it) { + using ceph::decode; + decode(demoted, it); + decode(mirror_peers, it); +} + +void MirrorPrimarySnapshotNamespace::dump(Formatter *f) const { + f->dump_bool("demoted", demoted); + f->open_array_section("mirror_peers"); + for (auto &peer : mirror_peers) { + f->dump_string("mirror_peer", peer); + } + f->close_section(); +} + +void MirrorNonPrimarySnapshotNamespace::encode(bufferlist& bl) const { + using ceph::encode; + encode(primary_mirror_uuid, bl); + encode(primary_snap_id, bl); + encode(copied, bl); + encode(copy_progress, bl); +} + +void MirrorNonPrimarySnapshotNamespace::decode(bufferlist::const_iterator& it) { + using ceph::decode; + decode(primary_mirror_uuid, it); + decode(primary_snap_id, it); + decode(copied, it); + decode(copy_progress, it); +} + +void MirrorNonPrimarySnapshotNamespace::dump(Formatter *f) const { + f->dump_string("primary_mirror_uuid", primary_mirror_uuid); + f->dump_unsigned("primary_snap_id", primary_snap_id); + f->dump_bool("copied", copied); + f->dump_unsigned("copy_progress", copy_progress); +} + class EncodeSnapshotNamespaceVisitor : public boost::static_visitor { public: explicit EncodeSnapshotNamespaceVisitor(bufferlist &bl) : m_bl(bl) { @@ -787,6 +831,12 @@ void SnapshotInfo::generate_test_instances(std::list &o) { TrashSnapshotNamespace{ SNAPSHOT_NAMESPACE_TYPE_USER, "snap1"}, "12345", 123, {123456, 0}, 429)); + o.push_back(new SnapshotInfo(1ULL, + MirrorPrimarySnapshotNamespace{true, {"1", "2"}}, + "snap1", 123, {123456, 0}, 12)); + o.push_back(new SnapshotInfo(1ULL, + MirrorNonPrimarySnapshotNamespace{"uuid", 111}, + "snap1", 123, {123456, 0}, 12)); } void SnapshotNamespace::encode(bufferlist& bl) const { @@ -810,6 +860,12 @@ void SnapshotNamespace::decode(bufferlist::const_iterator &p) case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH: *this = TrashSnapshotNamespace(); break; + case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY: + *this = MirrorPrimarySnapshotNamespace(); + break; + case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY: + *this = MirrorNonPrimarySnapshotNamespace(); + break; default: *this = UnknownSnapshotNamespace(); break; @@ -830,6 +886,9 @@ void SnapshotNamespace::generate_test_instances(std::list &o o.push_back(new SnapshotNamespace(GroupSnapshotNamespace(5, "1018643c9869", "33352be8933c"))); o.push_back(new SnapshotNamespace(TrashSnapshotNamespace())); + o.push_back(new SnapshotNamespace(MirrorPrimarySnapshotNamespace(true, + {"uuid"}))); + o.push_back(new SnapshotNamespace(MirrorNonPrimarySnapshotNamespace("", 0))); } std::ostream& operator<<(std::ostream& os, const SnapshotNamespaceType& type) { @@ -843,6 +902,12 @@ std::ostream& operator<<(std::ostream& os, const SnapshotNamespaceType& type) { case SNAPSHOT_NAMESPACE_TYPE_TRASH: os << "trash"; break; + case SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY: + os << "mirror_primary"; + break; + case SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY: + os << "mirror_non_primary"; + break; default: os << "unknown"; break; @@ -871,6 +936,26 @@ std::ostream& operator<<(std::ostream& os, const TrashSnapshotNamespace& ns) { return os; } +std::ostream& operator<<(std::ostream& os, + const MirrorPrimarySnapshotNamespace& ns) { + os << "[" << SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY << " " + << "demoted=" << ns.demoted << ", " + << "mirror_peers=" << ns.mirror_peers + << "]"; + return os; +} + +std::ostream& operator<<(std::ostream& os, + const MirrorNonPrimarySnapshotNamespace& ns) { + os << "[" << SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY << " " + << "primary_mirror_uuid=" << ns.primary_mirror_uuid << ", " + << "primary_snap_id=" << ns.primary_snap_id << ", " + << "copied=" << ns.copied << ", " + << "copy_progress=" << ns.copy_progress + << "]"; + return os; +} + std::ostream& operator<<(std::ostream& os, const UnknownSnapshotNamespace& ns) { os << "[unknown]"; return os; diff --git a/src/cls/rbd/cls_rbd_types.h b/src/cls/rbd/cls_rbd_types.h index 3e5c400de35..d4da0bd13e7 100644 --- a/src/cls/rbd/cls_rbd_types.h +++ b/src/cls/rbd/cls_rbd_types.h @@ -405,9 +405,11 @@ struct GroupSpec { WRITE_CLASS_ENCODER(GroupSpec); enum SnapshotNamespaceType { - SNAPSHOT_NAMESPACE_TYPE_USER = 0, - SNAPSHOT_NAMESPACE_TYPE_GROUP = 1, - SNAPSHOT_NAMESPACE_TYPE_TRASH = 2 + SNAPSHOT_NAMESPACE_TYPE_USER = 0, + SNAPSHOT_NAMESPACE_TYPE_GROUP = 1, + SNAPSHOT_NAMESPACE_TYPE_TRASH = 2, + SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY = 3, + SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY = 4, }; struct UserSnapshotNamespace { @@ -495,6 +497,80 @@ struct TrashSnapshotNamespace { } }; +struct MirrorPrimarySnapshotNamespace { + static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = + SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY; + + bool demoted = false; + std::set mirror_peers; + + MirrorPrimarySnapshotNamespace() { + } + MirrorPrimarySnapshotNamespace(bool demoted, + const std::set &mirror_peers) + : demoted(demoted), mirror_peers(mirror_peers) { + } + + void encode(bufferlist& bl) const; + void decode(bufferlist::const_iterator& it); + + void dump(Formatter *f) const; + + inline bool operator==(const MirrorPrimarySnapshotNamespace& mpsn) const { + return demoted == mpsn.demoted && + mirror_peer_uuids == mpsn.mirror_peer_uuids; + } + + inline bool operator<(const MirrorPrimarySnapshotNamespace& mpsn) const { + if (demoted != mpsn.demoted) { + return demoted < mpsn.demoted; + } + return mirror_peer_uuids < mpsn.mirror_peer_uuids; + } +}; + +struct MirrorNonPrimarySnapshotNamespace { + static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = + SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY; + + std::string primary_mirror_uuid; + snapid_t primary_snap_id = CEPH_NOSNAP; + bool copied = false; + uint64_t copy_progress = 0; + + MirrorNonPrimarySnapshotNamespace() { + } + MirrorNonPrimarySnapshotNamespace(const std::string &primary_mirror_uuid, + snapid_t primary_snap_id) + : primary_mirror_uuid(primary_mirror_uuid), + primary_snap_id(primary_snap_id) { + } + + void encode(bufferlist& bl) const; + void decode(bufferlist::const_iterator& it); + + void dump(Formatter *f) const; + + inline bool operator==(const MirrorNonPrimarySnapshotNamespace& mnsn) const { + return primary_mirror_uuid == mnsn.primary_mirror_uuid && + primary_snap_id == mnsn.primary_snap_id && copied == mnsn.copied && + last_copied_object_number == mnsn.last_copied_object_number; + } + + inline bool operator<(const MirrorNonPrimarySnapshotNamespace& mnsn) const { + if (primary_mirror_uuid != mnsn.primary_mirror_uuid) { + return primary_mirror_uuid < mnsn.primary_mirror_uuid; + } + if (primary_snap_id != mnsn.primary_snap_id) { + return primary_snap_id < mnsn.primary_snap_id; + } + if (copied != mnsn.copied) { + return copied < mnsn.copied; + } + return last_copied_object_number < mnsn.last_copied_object_number; + } +}; + struct UnknownSnapshotNamespace { static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = static_cast(-1); @@ -518,11 +594,17 @@ std::ostream& operator<<(std::ostream& os, const SnapshotNamespaceType& type); std::ostream& operator<<(std::ostream& os, const UserSnapshotNamespace& ns); std::ostream& operator<<(std::ostream& os, const GroupSnapshotNamespace& ns); std::ostream& operator<<(std::ostream& os, const TrashSnapshotNamespace& ns); +std::ostream& operator<<(std::ostream& os, + const MirrorPrimarySnapshotNamespace& ns); +std::ostream& operator<<(std::ostream& os, + const MirrorNonPrimarySnapshotNamespace& ns); std::ostream& operator<<(std::ostream& os, const UnknownSnapshotNamespace& ns); typedef boost::variant SnapshotNamespaceVariant; struct SnapshotNamespace : public SnapshotNamespaceVariant { diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index 23ac772406b..bcb215acdee 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -75,9 +75,11 @@ typedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void *ptr); typedef void (*rbd_update_callback_t)(void *arg); typedef enum { - RBD_SNAP_NAMESPACE_TYPE_USER = 0, - RBD_SNAP_NAMESPACE_TYPE_GROUP = 1, - RBD_SNAP_NAMESPACE_TYPE_TRASH = 2 + RBD_SNAP_NAMESPACE_TYPE_USER = 0, + RBD_SNAP_NAMESPACE_TYPE_GROUP = 1, + RBD_SNAP_NAMESPACE_TYPE_TRASH = 2, + RBD_SNAP_NAMESPACE_TYPE_MIRROR_PRIMARY = 3, + RBD_SNAP_NAMESPACE_TYPE_MIRROR_NON_PRIMARY = 4, } rbd_snap_namespace_type_t; typedef struct { @@ -247,6 +249,19 @@ typedef struct { char *group_snap_name; } rbd_snap_group_namespace_t; +typedef struct { + bool demoted; + size_t mirror_peers_count; + char *mirror_peers; +} rbd_snap_mirror_primary_namespace_t; + +typedef struct { + char *primary_mirror_uuid; + uint64_t primary_snap_id; + bool copied; + uint64_t copy_progress; +} rbd_snap_mirror_non_primary_namespace_t; + typedef enum { RBD_LOCK_MODE_EXCLUSIVE = 0, RBD_LOCK_MODE_SHARED = 1, @@ -825,6 +840,18 @@ CEPH_RBD_API int rbd_snap_get_trash_namespace(rbd_image_t image, uint64_t snap_id, char* original_name, size_t max_length); +CEPH_RBD_API int rbd_snap_get_mirror_primary_namespace( + rbd_image_t image, uint64_t snap_id, + rbd_snap_mirror_primary_namespace_t *mirror_snap, size_t mirror_snap_size); +CEPH_RBD_API int rbd_snap_mirror_primary_namespace_cleanup( + rbd_snap_mirror_primary_namespace_t *mirror_snap, size_t mirror_snap_size); +CEPH_RBD_API int rbd_snap_get_mirror_non_primary_namespace( + rbd_image_t image, uint64_t snap_id, + rbd_snap_mirror_non_primary_namespace_t *mirror_snap, + size_t mirror_snap_size); +CEPH_RBD_API int rbd_snap_mirror_non_primary_namespace_cleanup( + rbd_snap_mirror_non_primary_namespace_t *mirror_snap, + size_t mirror_snap_size); CEPH_RBD_API int rbd_flatten(rbd_image_t image); diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index 825a94e5f25..5511bad38a9 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -73,6 +73,18 @@ namespace librbd { std::string group_snap_name; } snap_group_namespace_t; + typedef struct { + bool demoted; + std::set mirror_peers; + } snap_mirror_primary_namespace_t; + + typedef struct { + std::string primary_mirror_uuid; + uint64_t primary_snap_id; + bool copied; + uint64_t copy_progress; + } snap_mirror_non_primary_namespace_t; + typedef struct { std::string client; std::string cookie; @@ -602,6 +614,12 @@ public: snap_group_namespace_t *group_namespace, size_t snap_group_namespace_size); int snap_get_trash_namespace(uint64_t snap_id, std::string* original_name); + int snap_get_mirror_primary_namespace( + uint64_t snap_id, snap_mirror_primary_namespace_t *mirror_namespace, + size_t snap_mirror_namespace_size); + int snap_get_mirror_non_primary_namespace( + uint64_t snap_id, snap_mirror_non_primary_namespace_t *mirror_namespace, + size_t snap_mirror_namespace_size); /* I/O */ ssize_t read(uint64_t ofs, size_t len, ceph::bufferlist& bl); diff --git a/src/librbd/api/Snapshot.cc b/src/librbd/api/Snapshot.cc index 2219d8f1a66..75864bae593 100644 --- a/src/librbd/api/Snapshot.cc +++ b/src/librbd/api/Snapshot.cc @@ -95,6 +95,51 @@ public: } }; +class GetMirrorPrimaryVisitor : public boost::static_visitor { +public: + snap_mirror_primary_namespace_t *mirror_snap; + + explicit GetMirrorPrimaryVisitor(snap_mirror_primary_namespace_t *mirror_snap) + : mirror_snap(mirror_snap) { + } + + template + inline int operator()(const T&) const { + return -EINVAL; + } + + inline int operator()( + const cls::rbd::MirrorPrimarySnapshotNamespace& snap_namespace) { + mirror_snap->demoted = snap_namespace.demoted; + mirror_snap->mirror_peers = snap_namespace.mirror_peers; + return 0; + } +}; + +class GetMirrorNonPrimaryVisitor : public boost::static_visitor { +public: + snap_mirror_non_primary_namespace_t *mirror_snap; + + explicit GetMirrorNonPrimaryVisitor( + snap_mirror_non_primary_namespace_t *mirror_snap) + : mirror_snap(mirror_snap) { + } + + template + inline int operator()(const T&) const { + return -EINVAL; + } + + inline int operator()( + const cls::rbd::MirrorNonPrimarySnapshotNamespace& snap_namespace) { + mirror_snap->primary_mirror_uuid = snap_namespace.primary_mirror_uuid; + mirror_snap->primary_snap_id = snap_namespace.primary_snap_id; + mirror_snap->copied = snap_namespace.copied; + mirror_snap->copy_progress = snap_namespace.copy_progress; + return 0; + } +}; + } // anonymous namespace template @@ -143,6 +188,53 @@ int Snapshot::get_trash_namespace(I *ictx, uint64_t snap_id, return 0; } +template +int Snapshot::get_mirror_primary_namespace( + I *ictx, uint64_t snap_id, snap_mirror_primary_namespace_t *mirror_snap) { + int r = ictx->state->refresh_if_required(); + if (r < 0) { + return r; + } + + std::shared_lock image_locker{ictx->image_lock}; + auto snap_info = ictx->get_snap_info(snap_id); + if (snap_info == nullptr) { + return -ENOENT; + } + + auto gmv = GetMirrorPrimaryVisitor(mirror_snap); + r = boost::apply_visitor(gmv, snap_info->snap_namespace); + if (r < 0) { + return r; + } + + return 0; +} + +template +int Snapshot::get_mirror_non_primary_namespace( + I *ictx, uint64_t snap_id, + snap_mirror_non_primary_namespace_t *mirror_snap) { + int r = ictx->state->refresh_if_required(); + if (r < 0) { + return r; + } + + std::shared_lock image_locker{ictx->image_lock}; + auto snap_info = ictx->get_snap_info(snap_id); + if (snap_info == nullptr) { + return -ENOENT; + } + + auto gmv = GetMirrorNonPrimaryVisitor(mirror_snap); + r = boost::apply_visitor(gmv, snap_info->snap_namespace); + if (r < 0) { + return r; + } + + return 0; +} + template int Snapshot::get_namespace_type(I *ictx, uint64_t snap_id, snap_namespace_type_t *namespace_type) { diff --git a/src/librbd/api/Snapshot.h b/src/librbd/api/Snapshot.h index 8a6696578ac..c00aaf85cf4 100644 --- a/src/librbd/api/Snapshot.h +++ b/src/librbd/api/Snapshot.h @@ -22,6 +22,14 @@ struct Snapshot { static int get_trash_namespace(ImageCtxT *ictx, uint64_t snap_id, std::string *original_name); + static int get_mirror_primary_namespace( + ImageCtxT *ictx, uint64_t snap_id, + snap_mirror_primary_namespace_t *mirror_snap); + + static int get_mirror_non_primary_namespace( + ImageCtxT *ictx, uint64_t snap_id, + snap_mirror_non_primary_namespace_t *mirror_snap); + static int get_namespace_type(ImageCtxT *ictx, uint64_t snap_id, snap_namespace_type_t *namespace_type); diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 5e729893f83..75a854a768e 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -2333,6 +2333,34 @@ namespace librbd { original_name); } + int Image::snap_get_mirror_primary_namespace( + uint64_t snap_id, snap_mirror_primary_namespace_t *mirror_snap, + size_t mirror_snap_size) { + ImageCtx *ictx = (ImageCtx *)ctx; + + if (mirror_snap_size != sizeof(snap_mirror_primary_namespace_t)) { + return -ERANGE; + } + + int r = librbd::api::Snapshot<>::get_mirror_primary_namespace( + ictx, snap_id, mirror_snap); + return r; + } + + int Image::snap_get_mirror_non_primary_namespace( + uint64_t snap_id, snap_mirror_non_primary_namespace_t *mirror_snap, + size_t mirror_snap_size) { + ImageCtx *ictx = (ImageCtx *)ctx; + + if (mirror_snap_size != sizeof(snap_mirror_non_primary_namespace_t)) { + return -ERANGE; + } + + int r = librbd::api::Snapshot<>::get_mirror_non_primary_namespace( + ictx, snap_id, mirror_snap); + return r; + } + int Image::snap_set_limit(uint64_t limit) { ImageCtx *ictx = (ImageCtx *)ctx; @@ -6812,6 +6840,88 @@ extern "C" int rbd_snap_get_trash_namespace(rbd_image_t image, uint64_t snap_id, strcpy(original_name, cpp_original_name.c_str()); return 0; } + +extern "C" int rbd_snap_get_mirror_primary_namespace( + rbd_image_t image, uint64_t snap_id, + rbd_snap_mirror_primary_namespace_t *mirror_snap, + size_t mirror_snap_size) { + librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; + + if (mirror_snap_size != sizeof(rbd_snap_mirror_primary_namespace_t)) { + return -ERANGE; + } + + librbd::snap_mirror_primary_namespace_t mirror_namespace; + int r = librbd::api::Snapshot<>::get_mirror_primary_namespace( + ictx, snap_id, &mirror_namespace); + if (r < 0) { + return r; + } + + mirror_snap->demoted = mirror_namespace.demoted; + mirror_snap->mirror_peers_count = mirror_namespace.mirror_peers.size(); + size_t len = 0; + for (auto &peer : mirror_namespace.mirror_peers) { + len += peer.size() + 1; + } + mirror_snap->mirror_peers = (char *)malloc(len); + char *p = mirror_snap->mirror_peers; + for (auto &peer : mirror_namespace.mirror_peers) { + strncpy(p, peer.c_str(), peer.size() + 1); + p += peer.size() + 1; + } + + return 0; +} + +extern "C" int rbd_snap_mirror_primary_namespace_cleanup( + rbd_snap_mirror_primary_namespace_t *mirror_snap, + size_t mirror_snap_size) { + if (mirror_snap_size != sizeof(rbd_snap_mirror_primary_namespace_t)) { + return -ERANGE; + } + + free(mirror_snap->mirror_peers); + return 0; +} + +extern "C" int rbd_snap_get_mirror_non_primary_namespace( + rbd_image_t image, uint64_t snap_id, + rbd_snap_mirror_non_primary_namespace_t *mirror_snap, + size_t mirror_snap_size) { + librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; + + if (mirror_snap_size != sizeof(rbd_snap_mirror_non_primary_namespace_t)) { + return -ERANGE; + } + + librbd::snap_mirror_non_primary_namespace_t mirror_namespace; + int r = librbd::api::Snapshot<>::get_mirror_non_primary_namespace( + ictx, snap_id, &mirror_namespace); + if (r < 0) { + return r; + } + + mirror_snap->primary_mirror_uuid = + strdup(mirror_namespace.primary_mirror_uuid.c_str()); + mirror_snap->primary_snap_id = mirror_namespace.primary_snap_id; + mirror_snap->copied = mirror_namespace.copied; + mirror_snap->copy_progress = mirror_namespace.copy_progress; + + return 0; +} + +extern "C" int rbd_snap_mirror_non_primary_namespace_cleanup( + rbd_snap_mirror_non_primary_namespace_t *mirror_snap, + size_t mirror_snap_size) { + if (mirror_snap_size != sizeof(rbd_snap_mirror_non_primary_namespace_t)) { + return -ERANGE; + } + + free(mirror_snap->primary_mirror_uuid); + return 0; +} + extern "C" int rbd_watchers_list(rbd_image_t image, rbd_image_watcher_t *watchers, size_t *max_watchers) { diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index a3355b8dcf6..7d835336f53 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -127,6 +127,17 @@ cdef extern from "rbd/librbd.h" nogil: char *group_name char *group_snap_name + ctypedef struct rbd_snap_mirror_primary_namespace_t: + bint demoted + size_t mirror_peers_count + char *mirror_peers + + ctypedef struct rbd_snap_mirror_non_primary_namespace_t: + char *primary_mirror_uuid + uint64_t primary_snap_id + bint copied + uint64_t copy_progress + ctypedef struct rbd_group_info_t: char *name int64_t pool @@ -147,6 +158,8 @@ cdef extern from "rbd/librbd.h" nogil: _RBD_SNAP_NAMESPACE_TYPE_USER "RBD_SNAP_NAMESPACE_TYPE_USER" _RBD_SNAP_NAMESPACE_TYPE_GROUP "RBD_SNAP_NAMESPACE_TYPE_GROUP" _RBD_SNAP_NAMESPACE_TYPE_TRASH "RBD_SNAP_NAMESPACE_TYPE_TRASH" + _RBD_SNAP_NAMESPACE_TYPE_MIRROR_PRIMARY "RBD_SNAP_NAMESPACE_TYPE_MIRROR_PRIMARY" + _RBD_SNAP_NAMESPACE_TYPE_MIRROR_NON_PRIMARY "RBD_SNAP_NAMESPACE_TYPE_MIRROR_NON_PRIMARY" ctypedef struct rbd_snap_spec_t: uint64_t id @@ -504,6 +517,20 @@ cdef extern from "rbd/librbd.h" nogil: size_t snap_group_namespace_size) int rbd_snap_get_trash_namespace(rbd_image_t image, uint64_t snap_id, char *original_name, size_t max_length) + int rbd_snap_get_mirror_primary_namespace( + rbd_image_t image, uint64_t snap_id, + rbd_snap_mirror_primary_namespace_t *mirror_ns, + size_t snap_mirror_primary_namespace_size) + void rbd_snap_mirror_primary_namespace_cleanup( + rbd_snap_mirror_primary_namespace_t *mirror_ns, + size_t snap_mirror_primary_namespace_size) + int rbd_snap_get_mirror_non_primary_namespace( + rbd_image_t image, uint64_t snap_id, + rbd_snap_mirror_non_primary_namespace_t *mirror_ns, + size_t snap_mirror_non_primary_namespace_size) + void rbd_snap_mirror_non_primary_namespace_cleanup( + rbd_snap_mirror_non_primary_namespace_t *mirror_ns, + size_t snap_mirror_non_primary_namespace_size) int rbd_flatten_with_progress(rbd_image_t image, librbd_progress_fn_t cb, void *cbdata) @@ -715,6 +742,8 @@ RBD_IMAGE_OPTION_DATA_POOL = _RBD_IMAGE_OPTION_DATA_POOL RBD_SNAP_NAMESPACE_TYPE_USER = _RBD_SNAP_NAMESPACE_TYPE_USER RBD_SNAP_NAMESPACE_TYPE_GROUP = _RBD_SNAP_NAMESPACE_TYPE_GROUP RBD_SNAP_NAMESPACE_TYPE_TRASH = _RBD_SNAP_NAMESPACE_TYPE_TRASH +RBD_SNAP_NAMESPACE_TYPE_MIRROR_PRIMARY = _RBD_SNAP_NAMESPACE_TYPE_MIRROR_PRIMARY +RBD_SNAP_NAMESPACE_TYPE_MIRROR_NON_PRIMARY = _RBD_SNAP_NAMESPACE_TYPE_MIRROR_NON_PRIMARY RBD_GROUP_IMAGE_STATE_ATTACHED = _RBD_GROUP_IMAGE_STATE_ATTACHED RBD_GROUP_IMAGE_STATE_INCOMPLETE = _RBD_GROUP_IMAGE_STATE_INCOMPLETE @@ -5041,6 +5070,78 @@ written." % (self.name, ret, length)) finally: free(_name) + def snap_get_mirror_primary_namespace(self, snap_id): + """ + get the mirror primary namespace details. + :param snap_id: the snapshot id of the mirror snapshot + :type key: int + :returns: dict - contains the following keys: + + * ``demoted`` (bool) - True if snapshot is in demoted state + + * ``mirror_peers`` (list) - mirror peer uuids + """ + cdef: + rbd_snap_mirror_primary_namespace_t sn + uint64_t _snap_id = snap_id + with nogil: + ret = rbd_snap_get_mirror_primary_namespace( + self.image, _snap_id, &sn, + sizeof(rbd_snap_mirror_primary_namespace_t)) + if ret != 0: + raise make_ex(ret, 'error getting snapshot mirror primary ' + 'namespace for image: %s, snap_id: %d' % + (self.name, snap_id)) + peers = [] + p = sn.mirror_peers + for i in range(sn.mirror_peers_count): + peer = decode_cstr(p) + peers.append(peer) + p += len(peer) + 1 + info = { + 'demoted' : sn.demoted, + 'mirror_peers' : peers, + } + rbd_snap_mirror_primary_namespace_cleanup( + &sn, sizeof(rbd_snap_mirror_primary_namespace_t)) + return info + + def snap_get_mirror_non_primary_namespace(self, snap_id): + """ + get the mirror non-primary namespace details. + :param snap_id: the snapshot id of the mirror snapshot + :type key: int + :returns: dict - contains the following keys: + + * ``primary_mirror_uuid`` (str) - primary mirror uuid + + * ``primary_snap_id`` (int) - primary snapshot Id + + * ``copied`` (bool) - True if snapsho is copied + + * ``copy_progress`` (int) - copy progress + """ + cdef: + rbd_snap_mirror_non_primary_namespace_t sn + uint64_t _snap_id = snap_id + with nogil: + ret = rbd_snap_get_mirror_non_primary_namespace( + self.image, _snap_id, &sn, + sizeof(rbd_snap_mirror_non_primary_namespace_t)) + if ret != 0: + raise make_ex(ret, 'error getting snapshot mirror non-primary ' + 'namespace for image: %s, snap_id: %d' % + (self.name, snap_id)) + info = { + 'primary_mirror_uuid' : decode_cstr(sn.primary_mirror_uuid), + 'primary_snap_id' : sn.primary_snap_id, + 'copied' : sn.copied, + 'copy_progress' : sn.copy_progress, + } + rbd_snap_mirror_non_primary_namespace_cleanup( + &sn, sizeof(rbd_snap_mirror_non_primary_namespace_t)) + return info + cdef class ImageIterator(object): """ @@ -5222,6 +5323,10 @@ cdef class SnapIterator(object): * ``group`` (dict) - optional for group namespace snapshots * ``trash`` (dict) - optional for trash namespace snapshots + + * ``mirror_primary`` (dict) - optional for mirror primary namespace snapshots + + * ``mirror_non_primary`` (dict) - optional for mirror non-primary namespace snapshots """ cdef rbd_snap_info_t *snaps @@ -5264,6 +5369,20 @@ cdef class SnapIterator(object): except: trash = None s['trash'] = trash + elif s['namespace'] == RBD_SNAP_NAMESPACE_TYPE_MIRROR_PRIMARY: + try: + mirror = self.image.snap_get_mirror_primary_namespace( + self.snaps[i].id) + except: + mirror = None + s['mirror_primary'] = mirror + elif s['namespace'] == RBD_SNAP_NAMESPACE_TYPE_MIRROR_NON_PRIMARY: + try: + mirror = self.image.snap_get_mirror_non_primary_namespace( + self.snaps[i].id) + except: + mirror = None + s['mirror_non_primary'] = mirror yield s def __dealloc__(self): diff --git a/src/tools/rbd/action/Snap.cc b/src/tools/rbd/action/Snap.cc index c652ce1c2fe..b2e6b665c6e 100644 --- a/src/tools/rbd/action/Snap.cc +++ b/src/tools/rbd/action/Snap.cc @@ -34,11 +34,18 @@ int do_list_snaps(librbd::Image& image, Formatter *f, bool all_snaps, librados:: return r; } + librbd::image_info_t info; if (!all_snaps) { snaps.erase(remove_if(snaps.begin(), snaps.end(), boost::bind(utils::is_not_user_snap_namespace, &image, _1)), snaps.end()); + } else if (!f) { + r = image.stat(info, sizeof(info)); + if (r < 0) { + std::cerr << "rbd: unable to get image info" << std::endl; + return r; + } } if (f) { @@ -88,18 +95,34 @@ int do_list_snaps(librbd::Image& image, Formatter *f, bool all_snaps, librados:: case RBD_SNAP_NAMESPACE_TYPE_TRASH: snap_namespace_name = "trash"; break; + case RBD_SNAP_NAMESPACE_TYPE_MIRROR_PRIMARY: + snap_namespace_name = "mirror_primary"; + break; + case RBD_SNAP_NAMESPACE_TYPE_MIRROR_NON_PRIMARY: + snap_namespace_name = "mirror_non_primary"; + break; } int get_trash_res = -ENOENT; std::string trash_original_name; int get_group_res = -ENOENT; librbd::snap_group_namespace_t group_snap; + int get_mirror_primary_res = -ENOENT; + librbd::snap_mirror_primary_namespace_t mirror_primary_snap; + int get_mirror_non_primary_res = -ENOENT; + librbd::snap_mirror_non_primary_namespace_t mirror_non_primary_snap; if (snap_namespace == RBD_SNAP_NAMESPACE_TYPE_GROUP) { get_group_res = image.snap_get_group_namespace(s->id, &group_snap, sizeof(group_snap)); } else if (snap_namespace == RBD_SNAP_NAMESPACE_TYPE_TRASH) { get_trash_res = image.snap_get_trash_namespace( s->id, &trash_original_name); + } else if (snap_namespace == RBD_SNAP_NAMESPACE_TYPE_MIRROR_PRIMARY) { + get_mirror_primary_res = image.snap_get_mirror_primary_namespace( + s->id, &mirror_primary_snap, sizeof(mirror_primary_snap)); + } else if (snap_namespace == RBD_SNAP_NAMESPACE_TYPE_MIRROR_NON_PRIMARY) { + get_mirror_non_primary_res = image.snap_get_mirror_non_primary_namespace( + s->id, &mirror_non_primary_snap, sizeof(mirror_non_primary_snap)); } std::string protected_str = ""; @@ -120,17 +143,32 @@ int do_list_snaps(librbd::Image& image, Formatter *f, bool all_snaps, librados:: f->dump_string("protected", protected_str); f->dump_string("timestamp", tt_str); if (all_snaps) { - f->open_object_section("namespace"); + f->open_object_section("namespace"); f->dump_string("type", snap_namespace_name); - if (get_group_res == 0) { - std::string pool_name = pool_map[group_snap.group_pool]; - f->dump_string("pool", pool_name); - f->dump_string("group", group_snap.group_name); - f->dump_string("group snap", group_snap.group_snap_name); - } else if (get_trash_res == 0) { + if (get_group_res == 0) { + std::string pool_name = pool_map[group_snap.group_pool]; + f->dump_string("pool", pool_name); + f->dump_string("group", group_snap.group_name); + f->dump_string("group snap", group_snap.group_snap_name); + } else if (get_trash_res == 0) { f->dump_string("original_name", trash_original_name); + } else if (get_mirror_primary_res == 0) { + f->dump_bool("demoted", mirror_primary_snap.demoted); + f->open_array_section("mirror_peer_uuids"); + for (auto &uuid : mirror_primary_snap.mirror_peer_uuids) { + f->dump_string("peer_uuid", uuid); + } + f->close_section(); + } else if (get_mirror_non_primary_res == 0) { + f->dump_string("primary_mirror_uuid", + mirror_non_primary_snap.primary_mirror_uuid); + f->dump_unsigned("primary_snap_id", + mirror_non_primary_snap.primary_snap_id); + f->dump_bool("copied", mirror_non_primary_snap.copied); + f->dump_unsigned("last_copied_object_number", + mirror_non_primary_snap.last_copied_object_number); } - f->close_section(); + f->close_section(); } f->close_section(); } else { @@ -138,19 +176,37 @@ int do_list_snaps(librbd::Image& image, Formatter *f, bool all_snaps, librados:: t << s->id << s->name << stringify(byte_u_t(s->size)) << protected_str << tt_str; if (all_snaps) { - ostringstream oss; + ostringstream oss; oss << snap_namespace_name; if (get_group_res == 0) { - std::string pool_name = pool_map[group_snap.group_pool]; - oss << " (" << pool_name << "/" - << group_snap.group_name << "@" - << group_snap.group_snap_name << ")"; + std::string pool_name = pool_map[group_snap.group_pool]; + oss << " (" << pool_name << "/" + << group_snap.group_name << "@" + << group_snap.group_snap_name << ")"; } else if (get_trash_res == 0) { oss << " (" << trash_original_name << ")"; + } else if (get_mirror_primary_res == 0) { + oss << " (" << (mirror_primary_snap.demoted ? "demoted " : "") + << "peer_uuids:[" << mirror_primary_snap.mirror_peer_uuids + << "])"; + } else if (get_mirror_non_primary_res == 0) { + oss << " (" << mirror_non_primary_snap.primary_mirror_uuid << ":" + << mirror_non_primary_snap.primary_snap_id << " "; + if (!mirror_non_primary_snap.copied) { + if (info.num_objs > 0) { + auto progress = std::min( + 100, 100 * mirror_non_primary_snap.last_copied_object_number / + info.num_objs); + oss << progress << "%"; + } else { + oss << "not "; + } + } + oss << " copied)"; } - t << oss.str(); + t << oss.str(); } t << TextTable::endrow; } -- 2.39.5