cls_method_handle_t h_get_object_prefix;
cls_method_handle_t h_get_data_pool;
cls_method_handle_t h_get_snapshot_name;
+cls_method_handle_t h_get_snapshot_namespace;
cls_method_handle_t h_snapshot_add;
cls_method_handle_t h_snapshot_remove;
cls_method_handle_t h_snapshot_rename;
return 0;
}
+/**
+ * Retrieve namespace of a snapshot.
+ *
+ * Input:
+ * @param snap_id id of the snapshot (uint64_t)
+ *
+ * Output:
+ * @param SnapshotNamespace
+ * @returns 0 on success, negative error code on failure.
+ */
+int get_snapshot_namespace(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ uint64_t snap_id;
+
+ bufferlist::iterator iter = in->begin();
+ try {
+ ::decode(snap_id, iter);
+ } catch (const buffer::error &err) {
+ return -EINVAL;
+ }
+
+ CLS_LOG(20, "get_snapshot_namespace snap_id=%" PRIu64, snap_id);
+
+ if (snap_id == CEPH_NOSNAP) {
+ return -EINVAL;
+ }
+
+ cls_rbd_snap snap;
+ string snapshot_key;
+ key_from_snap_id(snap_id, &snapshot_key);
+ int r = read_key(hctx, snapshot_key, &snap);
+ if (r < 0) {
+ return r;
+ }
+
+ ::encode(snap.snapshot_namespace, *out);
+
+ return 0;
+}
+
/**
* Adds a snapshot to an rbd header. Ensures the id and name are unique.
*
* Input:
* @param snap_name name of the snapshot (string)
* @param snap_id id of the snapshot (uint64_t)
+ * @param snap_namespace namespace of the snapshot (cls::rbd::SnapshotNamespaceOnDisk)
*
* Output:
* @returns 0 on success, negative error code on failure.
bufferlist::iterator iter = in->begin();
::decode(snap_meta.name, iter);
::decode(snap_meta.id, iter);
+ if (!iter.end()) {
+ ::decode(snap_meta.snapshot_namespace, iter);
+ }
} catch (const buffer::error &err) {
return -EINVAL;
}
cls_register_cxx_method(h_class, "get_snapshot_name",
CLS_METHOD_RD,
get_snapshot_name, &h_get_snapshot_name);
+ cls_register_cxx_method(h_class, "get_snapshot_namespace",
+ CLS_METHOD_RD,
+ get_snapshot_namespace, &h_get_snapshot_namespace);
cls_register_cxx_method(h_class, "snapshot_add",
CLS_METHOD_RD | CLS_METHOD_WR,
snapshot_add, &h_snapshot_add);
#include "include/buffer_fwd.h"
#include "common/Formatter.h"
#include "librbd/parent_types.h"
+#include "cls/rbd/cls_rbd_types.h"
/// information about our parent image, if any
struct cls_rbd_parent {
uint8_t protection_status;
cls_rbd_parent parent;
uint64_t flags;
+ cls::rbd::SnapshotNamespaceOnDisk snapshot_namespace;
/// true if we have a parent
bool has_parent() const {
flags(0)
{}
void encode(bufferlist& bl) const {
- ENCODE_START(4, 1, bl);
+ ENCODE_START(5, 1, bl);
::encode(id, bl);
::encode(name, bl);
::encode(image_size, bl);
::encode(parent, bl);
::encode(protection_status, bl);
::encode(flags, bl);
+ ::encode(snapshot_namespace, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& p) {
- DECODE_START(4, p);
+ DECODE_START(5, p);
::decode(id, p);
::decode(name, p);
::decode(image_size, p);
if (struct_v >= 4) {
::decode(flags, p);
}
+ if (struct_v >= 5) {
+ ::decode(snapshot_namespace, p);
+ } else {
+ snapshot_namespace = cls::rbd::SnapshotNamespaceOnDisk(cls::rbd::UserSnapshotNamespace());
+ }
DECODE_FINISH(p);
}
void dump(Formatter *f) const {
}
void snapshot_add(librados::ObjectWriteOperation *op, snapid_t snap_id,
- const std::string &snap_name)
+ const std::string &snap_name, const cls::rbd::SnapshotNamespace &snap_namespace)
{
bufferlist bl;
::encode(snap_name, bl);
::encode(snap_id, bl);
+ ::encode(cls::rbd::SnapshotNamespaceOnDisk(snap_namespace), bl);
op->exec("rbd", "snapshot_add", bl);
}
std::vector<string> *names,
std::vector<uint64_t> *sizes,
std::vector<parent_info> *parents,
- std::vector<uint8_t> *protection_statuses) {
+ std::vector<uint8_t> *protection_statuses)
+ {
names->resize(ids.size());
sizes->resize(ids.size());
parents->resize(ids.size());
protection_statuses);
}
+ void snap_namespace_list_start(librados::ObjectReadOperation *op,
+ const std::vector<snapid_t> &ids)
+ {
+ for (vector<snapid_t>::const_iterator it = ids.begin();
+ it != ids.end(); ++it) {
+ snapid_t snap_id = it->val;
+ bufferlist bl;
+ ::encode(snap_id, bl);
+ op->exec("rbd", "get_snapshot_namespace", bl);
+ }
+ }
+
+ int snap_namespace_list_finish(bufferlist::iterator *it,
+ const std::vector<snapid_t> &ids,
+ std::vector<cls::rbd::SnapshotNamespace> *namespaces)
+ {
+ namespaces->resize(ids.size());
+ try {
+ for (size_t i = 0; i < namespaces->size(); ++i) {
+ cls::rbd::SnapshotNamespaceOnDisk e;
+ ::decode(e, *it);
+ (*namespaces)[i] = e.snapshot_namespace;
+ }
+ } catch (const buffer::error &err) {
+ return -EBADMSG;
+ }
+ return 0;
+ }
+
+ int snap_namespace_list(librados::IoCtx *ioctx, const std::string &oid,
+ const std::vector<snapid_t> &ids,
+ std::vector<cls::rbd::SnapshotNamespace> *namespaces)
+ {
+ librados::ObjectReadOperation op;
+ snap_namespace_list_start(&op, ids);
+
+ bufferlist out_bl;
+ int r = ioctx->operate(oid, &op, &out_bl);
+ if (r < 0) {
+ return r;
+ }
+
+ bufferlist::iterator it = out_bl.begin();
+ return snap_namespace_list_finish(&it, ids, namespaces);
+ }
+
void old_snapshot_add(librados::ObjectWriteOperation *op,
snapid_t snap_id, const std::string &snap_name)
{
int get_children(librados::IoCtx *ioctx, const std::string &oid,
parent_spec pspec, set<string>& children);
void snapshot_add(librados::ObjectWriteOperation *op, snapid_t snap_id,
- const std::string &snap_name);
+ const std::string &snap_name,
+ const cls::rbd::SnapshotNamespace &snap_namespace);
void snapshot_remove(librados::ObjectWriteOperation *op, snapid_t snap_id);
void snapshot_rename(librados::ObjectWriteOperation *op,
snapid_t src_snap_id,
std::vector<parent_info> *parents,
std::vector<uint8_t> *protection_statuses);
+ void snap_namespace_list_start(librados::ObjectReadOperation *op,
+ const std::vector<snapid_t> &ids);
+ int snap_namespace_list_finish(bufferlist::iterator *it,
+ const std::vector<snapid_t> &ids,
+ std::vector<cls::rbd::SnapshotNamespace> *namespaces);
+ int snap_namespace_list(librados::IoCtx *ioctx, const std::string &oid,
+ const std::vector<snapid_t> &ids,
+ std::vector<cls::rbd::SnapshotNamespace> *namespaces);
+
int copyup(librados::IoCtx *ioctx, const std::string &oid,
bufferlist data);
int get_protection_status(librados::IoCtx *ioctx, const std::string &oid,
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
+#include <boost/variant.hpp>
#include "cls/rbd/cls_rbd_types.h"
#include "common/Formatter.h"
return (!group_id.empty()) && (pool_id != -1);
}
+void GroupSnapshotNamespace::encode(bufferlist& bl) const {
+ ::encode(group_pool, bl);
+ ::encode(group_id, bl);
+ ::encode(snapshot_id, bl);
+}
+
+void GroupSnapshotNamespace::decode(bufferlist::iterator& it) {
+ ::decode(group_pool, it);
+ ::decode(group_id, it);
+ ::decode(snapshot_id, it);
+}
+
+void GroupSnapshotNamespace::dump(Formatter *f) const {
+ f->dump_int("group_pool", group_pool);
+ f->dump_string("group_id", group_id);
+ f->dump_int("snapshot_id", snapshot_id);
+}
+
+class EncodeSnapshotNamespaceVisitor : public boost::static_visitor<void> {
+public:
+ explicit EncodeSnapshotNamespaceVisitor(bufferlist &bl) : m_bl(bl) {
+ }
+
+ template <typename T>
+ inline void operator()(const T& t) const {
+ ::encode(static_cast<uint32_t>(T::SNAPSHOT_NAMESPACE_TYPE), m_bl);
+ t.encode(m_bl);
+ }
+
+private:
+ bufferlist &m_bl;
+};
+
+class DecodeSnapshotNamespaceVisitor : public boost::static_visitor<void> {
+public:
+ DecodeSnapshotNamespaceVisitor(bufferlist::iterator &iter)
+ : m_iter(iter) {
+ }
+
+ template <typename T>
+ inline void operator()(T& t) const {
+ t.decode(m_iter);
+ }
+private:
+ bufferlist::iterator &m_iter;
+};
+
+class DumpSnapshotNamespaceVisitor : public boost::static_visitor<void> {
+public:
+ explicit DumpSnapshotNamespaceVisitor(Formatter *formatter, const std::string &key)
+ : m_formatter(formatter), m_key(key) {}
+
+ template <typename T>
+ inline void operator()(const T& t) const {
+ auto type = T::SNAPSHOT_NAMESPACE_TYPE;
+ m_formatter->dump_string(m_key.c_str(), stringify(type));
+ t.dump(m_formatter);
+ }
+private:
+ ceph::Formatter *m_formatter;
+ std::string m_key;
+};
+
+class GetTypeVisitor : public boost::static_visitor<SnapshotNamespaceType> {
+public:
+ template <typename T>
+ inline SnapshotNamespaceType operator()(const T&) const {
+ return static_cast<SnapshotNamespaceType>(T::SNAPSHOT_NAMESPACE_TYPE);
+ }
+};
+
+
+SnapshotNamespaceType SnapshotNamespaceOnDisk::get_namespace_type() const {
+ return static_cast<SnapshotNamespaceType>(boost::apply_visitor(GetTypeVisitor(),
+ snapshot_namespace));
+}
+
+void SnapshotNamespaceOnDisk::encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ boost::apply_visitor(EncodeSnapshotNamespaceVisitor(bl), snapshot_namespace);
+ ENCODE_FINISH(bl);
+}
+
+void SnapshotNamespaceOnDisk::decode(bufferlist::iterator &p)
+{
+ DECODE_START(1, p);
+ uint32_t snap_type;
+ ::decode(snap_type, p);
+ switch (snap_type) {
+ case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER:
+ snapshot_namespace = UserSnapshotNamespace();
+ break;
+ case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_GROUP:
+ snapshot_namespace = GroupSnapshotNamespace();
+ break;
+ default:
+ snapshot_namespace = UnknownSnapshotNamespace();
+ break;
+ }
+ boost::apply_visitor(DecodeSnapshotNamespaceVisitor(p), snapshot_namespace);
+ DECODE_FINISH(p);
+}
+
+void SnapshotNamespaceOnDisk::dump(Formatter *f) const {
+ boost::apply_visitor(DumpSnapshotNamespaceVisitor(f, "snapshot_namespace_type"), snapshot_namespace);
+}
+
+void SnapshotNamespaceOnDisk::generate_test_instances(std::list<SnapshotNamespaceOnDisk *> &o) {
+ o.push_back(new SnapshotNamespaceOnDisk(UserSnapshotNamespace()));
+ o.push_back(new SnapshotNamespaceOnDisk(GroupSnapshotNamespace(0, "10152ae8944a", 1)));
+ o.push_back(new SnapshotNamespaceOnDisk(GroupSnapshotNamespace(5, "1018643c9869", 3)));
+}
+
+std::ostream& operator<<(std::ostream& os, const UserSnapshotNamespace& ns) {
+ os << "[user]";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const GroupSnapshotNamespace& ns) {
+ os << "[group"
+ << " group_pool=" << ns.group_pool
+ << " group_id=" << ns.group_id
+ << " snapshot_id=" << ns.snapshot_id << "]";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const UnknownSnapshotNamespace& ns) {
+ os << "[unknown]";
+ return os;
+}
+
} // namespace rbd
} // namespace cls
#ifndef CEPH_CLS_RBD_TYPES_H
#define CEPH_CLS_RBD_TYPES_H
+#include <boost/variant.hpp>
#include "include/int_types.h"
#include "include/buffer.h"
#include "include/encoding.h"
WRITE_CLASS_ENCODER(GroupSpec);
+enum SnapshotNamespaceType {
+ SNAPSHOT_NAMESPACE_TYPE_USER = 0,
+ SNAPSHOT_NAMESPACE_TYPE_GROUP = 1
+};
+
+struct UserSnapshotNamespace {
+ static const uint32_t SNAPSHOT_NAMESPACE_TYPE = SNAPSHOT_NAMESPACE_TYPE_USER;
+
+ UserSnapshotNamespace() {}
+
+ void encode(bufferlist& bl) const {}
+ void decode(bufferlist::iterator& it) {}
+
+ void dump(Formatter *f) const {}
+
+ inline bool operator==(const UserSnapshotNamespace& usn) const {
+ return true;
+ }
+
+};
+
+std::ostream& operator<<(std::ostream& os, const UserSnapshotNamespace& ns);
+
+struct GroupSnapshotNamespace {
+ static const uint32_t SNAPSHOT_NAMESPACE_TYPE = SNAPSHOT_NAMESPACE_TYPE_GROUP;
+
+ GroupSnapshotNamespace() {}
+
+ GroupSnapshotNamespace(int64_t _group_pool,
+ const string &_group_id,
+ const snapid_t &_snapshot_id) :group_pool(_group_pool),
+ group_id(_group_id),
+ snapshot_id(_snapshot_id) {}
+
+ int64_t group_pool;
+ string group_id;
+ snapid_t snapshot_id;
+
+ void encode(bufferlist& bl) const;
+ void decode(bufferlist::iterator& it);
+
+ void dump(Formatter *f) const;
+
+ inline bool operator==(const GroupSnapshotNamespace& gsn) const {
+ return group_pool == gsn.group_pool &&
+ group_id == gsn.group_id &&
+ snapshot_id == gsn.snapshot_id;
+ }
+
+};
+
+std::ostream& operator<<(std::ostream& os, const GroupSnapshotNamespace& ns);
+
+struct UnknownSnapshotNamespace {
+ static const uint32_t SNAPSHOT_NAMESPACE_TYPE = static_cast<uint32_t>(-1);
+
+ UnknownSnapshotNamespace() {}
+
+ void encode(bufferlist& bl) const {}
+ void decode(bufferlist::iterator& it) {}
+ void dump(Formatter *f) const {}
+ inline bool operator==(const UnknownSnapshotNamespace& gsn) const {
+ return true;
+ }
+};
+
+std::ostream& operator<<(std::ostream& os, const UnknownSnapshotNamespace& ns);
+
+typedef boost::variant<UserSnapshotNamespace, GroupSnapshotNamespace, UnknownSnapshotNamespace> SnapshotNamespace;
+
+
+struct SnapshotNamespaceOnDisk {
+
+ SnapshotNamespaceOnDisk() : snapshot_namespace(UnknownSnapshotNamespace()) {}
+ SnapshotNamespaceOnDisk(const SnapshotNamespace &sn) : snapshot_namespace(sn) {}
+
+ SnapshotNamespace snapshot_namespace;
+
+ SnapshotNamespaceType get_namespace_type() const;
+
+ void encode(bufferlist& bl) const;
+ void decode(bufferlist::iterator& it);
+ void dump(Formatter *f) const;
+
+ static void generate_test_instances(std::list<SnapshotNamespaceOnDisk *> &o);
+
+ inline bool operator==(const SnapshotNamespaceOnDisk& gsn) const {
+ return snapshot_namespace == gsn.snapshot_namespace;
+ }
+};
+WRITE_CLASS_ENCODER(SnapshotNamespaceOnDisk);
+
} // namespace rbd
} // namespace cls
static int snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id, const std::string &snap_name) {
librados::ObjectWriteOperation op;
- ::librbd::cls_client::snapshot_add(&op, snap_id, snap_name);
+ ::librbd::cls_client::snapshot_add(&op, snap_id, snap_name, cls::rbd::UserSnapshotNamespace());
return ioctx->operate(oid, &op);
}
TEST_F(TestClsRbd, snapshots)
{
+ cls::rbd::SnapshotNamespace userSnapNamespace = cls::rbd::UserSnapshotNamespace();
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
ASSERT_EQ(0, create_image(&ioctx, oid, 10, 22, 0, oid, -1));
vector<string> snap_names;
+ vector<cls::rbd::SnapshotNamespace> snap_namespaces;
vector<uint64_t> snap_sizes;
SnapContext snapc;
vector<parent_info> parents;
ASSERT_EQ(0u, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(0u, snap_names.size());
+ ASSERT_EQ(0u, snap_namespaces.size());
ASSERT_EQ(0u, snap_sizes.size());
ASSERT_EQ(0, snapshot_add(&ioctx, oid, 0, "snap1"));
ASSERT_EQ(0u, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(1u, snap_names.size());
ASSERT_EQ("snap1", snap_names[0]);
+ ASSERT_EQ(1u, snap_namespaces.size());
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[0]);
ASSERT_EQ(10u, snap_sizes[0]);
// snap with same id and name
ASSERT_EQ(0u, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(1u, snap_names.size());
ASSERT_EQ("snap1", snap_names[0]);
+ ASSERT_EQ(1u, snap_namespaces.size());
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[0]);
ASSERT_EQ(10u, snap_sizes[0]);
// snap with same id, different name
ASSERT_EQ(0u, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(1u, snap_names.size());
ASSERT_EQ("snap1", snap_names[0]);
+ ASSERT_EQ(1u, snap_namespaces.size());
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[0]);
ASSERT_EQ(10u, snap_sizes[0]);
// snap with different id, same name
ASSERT_EQ(0u, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(snap_names.size(), 1u);
ASSERT_EQ(snap_names[0], "snap1");
+ ASSERT_EQ(1u, snap_namespaces.size());
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[0]);
ASSERT_EQ(snap_sizes[0], 10u);
// snap with different id, different name
ASSERT_EQ(1u, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(2u, snap_names.size());
+ ASSERT_EQ(2u, snap_namespaces.size());
ASSERT_EQ("snap2", snap_names[0]);
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[0]);
ASSERT_EQ(10u, snap_sizes[0]);
ASSERT_EQ("snap1", snap_names[1]);
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[1]);
ASSERT_EQ(10u, snap_sizes[1]);
ASSERT_EQ(0, snapshot_rename(&ioctx, oid, 0, "snap1-rename"));
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(2u, snap_names.size());
+ ASSERT_EQ(2u, snap_namespaces.size());
ASSERT_EQ("snap2", snap_names[0]);
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[0]);
ASSERT_EQ(10u, snap_sizes[0]);
ASSERT_EQ("snap1-rename", snap_names[1]);
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[1]);
ASSERT_EQ(10u, snap_sizes[1]);
ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 0));
ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
ASSERT_EQ(1u, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(1u, snap_names.size());
+ ASSERT_EQ(1u, snap_namespaces.size());
ASSERT_EQ("snap2", snap_names[0]);
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[0]);
ASSERT_EQ(10u, snap_sizes[0]);
uint64_t size;
ASSERT_EQ(large_snap_id, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(2u, snap_names.size());
+ ASSERT_EQ(2u, snap_namespaces.size());
ASSERT_EQ("snap3", snap_names[0]);
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[0]);
ASSERT_EQ(0u, snap_sizes[0]);
ASSERT_EQ("snap2", snap_names[1]);
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[1]);
ASSERT_EQ(10u, snap_sizes[1]);
ASSERT_EQ(0, get_size(&ioctx, oid, large_snap_id, &size, &order));
ASSERT_EQ(large_snap_id, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(1u, snap_names.size());
+ ASSERT_EQ(1u, snap_namespaces.size());
ASSERT_EQ("snap2", snap_names[0]);
+ ASSERT_EQ(userSnapNamespace, snap_namespaces[0]);
ASSERT_EQ(10u, snap_sizes[0]);
ASSERT_EQ(-ENOENT, snapshot_remove(&ioctx, oid, large_snap_id));
ASSERT_EQ(large_snap_id, snapc.seq);
ASSERT_EQ(0, snapshot_list(&ioctx, oid, snapc.snaps, &snap_names,
&snap_sizes, &parents, &protection_status));
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
ASSERT_EQ(0u, snap_names.size());
+ ASSERT_EQ(0u, snap_namespaces.size());
ASSERT_EQ(0u, snap_sizes.size());
ioctx.close();
}
+TEST_F(TestClsRbd, snapshots_namespaces)
+{
+ cls::rbd::SnapshotNamespace groupSnapNamespace = cls::rbd::GroupSnapshotNamespace(5, "1018643c9869", 3);
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+
+ ASSERT_EQ(0, create_image(&ioctx, oid, 10, 22, 0, oid, -1));
+
+ vector<string> snap_names;
+ vector<cls::rbd::SnapshotNamespace> snap_namespaces;
+ SnapContext snapc;
+
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(0u, snapc.snaps.size());
+ ASSERT_EQ(0u, snapc.seq);
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
+ ASSERT_EQ(0u, snap_namespaces.size());
+
+ ASSERT_EQ(0, snapshot_add(&ioctx, oid, 0, "snap1"));
+
+ librados::ObjectWriteOperation op;
+ ::librbd::cls_client::snapshot_add(&op, 0, "snap1", groupSnapNamespace);
+ int r = ioctx.operate(oid, &op);
+ ASSERT_EQ(0, r);
+
+ ASSERT_EQ(0, get_snapcontext(&ioctx, oid, &snapc));
+ ASSERT_EQ(1u, snapc.snaps.size());
+ ASSERT_EQ(0u, snapc.snaps[0]);
+ ASSERT_EQ(0u, snapc.seq);
+ ASSERT_EQ(0, snap_namespace_list(&ioctx, oid, snapc.snaps, &snap_namespaces));
+ ASSERT_EQ(groupSnapNamespace, snap_namespaces[0]);
+}
+
TEST_F(TestClsRbd, snapid_race)
{
librados::IoCtx ioctx;