Merge branch 'wip-clsrbd'

Reviewed-by: Greg Farnum <greg@inktank.com>
Reviewed-by: Josh Durgin <josh.durgin@inktank.com>
This commit is contained in:
Sage Weil 2012-06-19 15:19:21 -07:00
commit 6948af0284
8 changed files with 622 additions and 50 deletions

View File

@ -1369,6 +1369,7 @@ noinst_HEADERS = \
librados/IoCtxImpl.h\
librados/PoolAsyncCompletionImpl.h\
librados/RadosClient.h\
librbd/cls_rbd.h\
librbd/cls_rbd_client.h\
librbd/LibrbdWriteback.h\
logrotate.conf\

View File

@ -26,11 +26,6 @@
* in each one that they take an input and an output bufferlist.
*/
#include "include/types.h"
#include "objclass/objclass.h"
#include "include/rbd_types.h"
#include <algorithm>
#include <cstring>
#include <cstdlib>
@ -40,6 +35,13 @@
#include <sstream>
#include <vector>
#include "include/types.h"
#include "objclass/objclass.h"
#include "include/rbd_types.h"
#include "librbd/cls_rbd.h"
CLS_VER(2,0)
CLS_NAME(rbd)
@ -48,6 +50,9 @@ cls_method_handle_t h_create;
cls_method_handle_t h_get_features;
cls_method_handle_t h_get_size;
cls_method_handle_t h_set_size;
cls_method_handle_t h_get_parent;
cls_method_handle_t h_set_parent;
cls_method_handle_t h_remove_parent;
cls_method_handle_t h_get_snapcontext;
cls_method_handle_t h_get_object_prefix;
cls_method_handle_t h_get_snapshot_name;
@ -72,31 +77,6 @@ cls_method_handle_t h_assign_bid;
#define RBD_LOCK_EXCLUSIVE "exclusive"
#define RBD_LOCK_SHARED "shared"
struct cls_rbd_snap {
snapid_t id;
string name;
uint64_t image_size;
uint64_t features;
cls_rbd_snap() : id(CEPH_NOSNAP), image_size(0), features(0) {}
void encode(bufferlist& bl) const {
ENCODE_START(1, 1, bl);
::encode(id, bl);
::encode(name, bl);
::encode(image_size, bl);
::encode(features, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& p) {
DECODE_START(1, p);
::decode(id, p);
::decode(name, p);
::decode(image_size, p);
::decode(features, p);
DECODE_FINISH(p);
}
};
WRITE_CLASS_ENCODER(cls_rbd_snap)
static int snap_read_header(cls_method_context_t hctx, bufferlist& bl)
{
@ -300,6 +280,28 @@ int get_features(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
return 0;
}
/**
* check that given feature(s) are set
*
* @param hctx context
* @param need features needed
* @return 0 if features are set, negative error (like ENOEXEC) otherwise
*/
int require_feature(cls_method_context_t hctx, uint64_t need)
{
uint64_t features;
int r = read_key(hctx, "features", &features);
if (r == -ENOENT) // this implies it's an old-style image with no features
return -ENOEXEC;
if (r < 0)
return r;
if ((features & need) != need) {
CLS_LOG(10, "require_feature missing feature %llx, have %llx", need, features);
return -ENOEXEC;
}
return 0;
}
/**
* Input:
* @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t)
@ -370,8 +372,6 @@ int set_size(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
return -EINVAL;
}
CLS_LOG(20, "set_size size=%llu", size);
// check that size exists to make sure this is a header object
// that was created correctly
uint64_t orig_size;
@ -381,23 +381,45 @@ int set_size(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
return r;
}
CLS_LOG(20, "set_size size=%llu orig_size=%llu", size);
bufferlist sizebl;
::encode(size, sizebl);
r = cls_cxx_map_set_val(hctx, "size", &sizebl);
if (r < 0) {
CLS_ERR("error writing snapshot metadata: %d", r);
return r;
}
// if we are shrinking, and have a parent, shrink our overlap with
// the parent, too.
if (size < orig_size) {
cls_rbd_parent parent;
r = read_key(hctx, "parent", &parent);
if (r == -ENOENT)
r = 0;
if (r < 0)
return r;
if (parent.exists() && parent.overlap > size) {
bufferlist parentbl;
parent.overlap = size;
::encode(parent, parentbl);
r = cls_cxx_map_set_val(hctx, "parent", &parentbl);
if (r < 0) {
CLS_ERR("error writing parent: %d", r);
return r;
}
}
}
return 0;
}
// image locking
/**
* helper function to add a lock and update disk state.
* @param lock_type The type of lock, either RBD_LOCK_EXCLUSIVE
* or RBD_LOCK_SHARED
*
* Input:
* @param lock_type The type of lock, either RBD_LOCK_EXCLUSIVE or RBD_LOCK_SHARED
* @param cookie The cookie to set in the lock
*
* @return 0 on success, or -errno on failure
@ -406,6 +428,7 @@ int lock_image(cls_method_context_t hctx, string lock_type,
const string &cookie)
{
bool exclusive = lock_type == RBD_LOCK_EXCLUSIVE;
// see if there's already a locker
set<pair<string, string> > lockers;
string existing_lock_type;
@ -454,8 +477,10 @@ int lock_image(cls_method_context_t hctx, string lock_type,
}
return r;
}
/**
* Set an exclusive lock on an image for the activating client, if possible.
*
* Input:
* @param lock_cookie A string cookie, defined by the locker.
*
@ -479,6 +504,7 @@ int lock_image_exclusive(cls_method_context_t hctx,
/**
* Set an exclusive lock on an image, if possible.
*
* Input:
* @param lock_cookie A string cookie, defined by the locker.
*
@ -499,8 +525,10 @@ int lock_image_shared(cls_method_context_t hctx,
return lock_image(hctx, RBD_LOCK_SHARED, lock_cookie);
}
/**
* helper function to remove a lock from on disk and clean up state.
*
* @param inst The string representation of the locker's entity.
* @param cookie The user-defined cookie associated with the lock.
*
@ -534,8 +562,10 @@ int remove_lock(cls_method_context_t hctx, const string& inst,
return 0;
}
/**
* Unlock an image which the activating client currently has locked.
*
* Input:
* @param lock_cookie The user-defined cookie associated with the lock.
*
@ -562,8 +592,10 @@ int unlock_image(cls_method_context_t hctx,
inst_stringstream << inst;
return remove_lock(hctx, inst_stringstream.str(), lock_cookie);
}
/**
* Break the lock on an image held by any client.
*
* Input:
* @param locker The string representation of the locking client's entity.
* @param lock_cookie The user-defined cookie associated with the lock.
@ -571,7 +603,8 @@ int unlock_image(cls_method_context_t hctx,
* @return 0 on success, -EINVAL if it can't decode the locker and
* cookie, -ENOENT if there is no such lock (either entity or cookie
* is wrong), or -errno on other (unexpected) error.
*/int break_lock(cls_method_context_t hctx,
*/
int break_lock(cls_method_context_t hctx,
bufferlist *in, bufferlist *out)
{
CLS_LOG(20, "break_lock");
@ -591,7 +624,10 @@ int unlock_image(cls_method_context_t hctx,
/**
* Retrieve a list of clients locking this object (presumably an rbd header),
* as well as whether the lock is shared or exclusive.
*
* Input:
* @param in is ignored.
*
* Output:
* @param set<pair<string, string> > lockers The set of clients holding locks,
* as <client, cookie> pairs.
@ -628,6 +664,186 @@ int list_locks(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
return r;
}
/**
* verify that the header object exists
*
* @return 0 if the object exists, -ENOENT if it does not, or other error
*/
int check_exists(cls_method_context_t hctx)
{
uint64_t size;
time_t mtime;
return cls_cxx_stat(hctx, &size, &mtime);
}
/**
* get the current parent, if any
*
* Input:
* @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t)
*
* Output:
* @param pool parent pool id
* @param image parent image id
* @param snapid parent snapid
* @param size portion of parent mapped under the child
*
* @returns 0 on success, -ENOENT if no parent, other negative error code on failure
*/
int get_parent(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;
}
int r = check_exists(hctx);
if (r < 0)
return r;
CLS_LOG(20, "get_parent snap_id=%llu", snap_id);
r = require_feature(hctx, RBD_FEATURE_LAYERING);
if (r < 0)
return r;
cls_rbd_parent parent;
if (snap_id == CEPH_NOSNAP) {
r = read_key(hctx, "parent", &parent);
if (r < 0)
return r;
} else {
cls_rbd_snap snap;
string snapshot_key;
key_from_snap_id(snap_id, &snapshot_key);
r = read_key(hctx, snapshot_key, &snap);
if (r < 0)
return r;
parent = snap.parent;
}
if (!parent.exists())
return -ENOENT;
::encode(parent.pool, *out);
::encode(parent.id, *out);
::encode(parent.snapid, *out);
::encode(parent.overlap, *out);
return 0;
}
/**
* set the image parent
*
* Input:
* @param pool parent pool
* @param id parent image id
* @param snapid parent snapid
* @param size parent size
*
* @returns 0 on success, or negative error code
*/
int set_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
int64_t pool;
string id;
snapid_t snapid;
uint64_t size;
bufferlist::iterator iter = in->begin();
try {
::decode(pool, iter);
::decode(id, iter);
::decode(snapid, iter);
::decode(size, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
int r = check_exists(hctx);
if (r < 0)
return r;
r = require_feature(hctx, RBD_FEATURE_LAYERING);
if (r < 0)
return r;
CLS_LOG(20, "set_parent pool=%lld id=%s snapid=%llu size=%llu",
pool, id.c_str(), snapid.val, size);
if (pool < 0 || id.length() == 0 || snapid == CEPH_NOSNAP || size == 0) {
return -EINVAL;
}
// make sure there isn't already a parent
cls_rbd_parent parent;
r = read_key(hctx, "parent", &parent);
if (r == 0) {
CLS_LOG(20, "set_parent existing parent pool=%lld id=%s snapid=%llu overlap=%llu",
parent.pool, parent.id.c_str(), parent.snapid.val,
parent.overlap);
return -EEXIST;
}
// our overlap is the min of our size and the parent's size.
uint64_t our_size;
r = read_key(hctx, "size", &our_size);
if (r < 0)
return r;
bufferlist parentbl;
parent.pool = pool;
parent.id = id;
parent.snapid = snapid;
parent.overlap = MIN(our_size, size);
::encode(parent, parentbl);
r = cls_cxx_map_set_val(hctx, "parent", &parentbl);
if (r < 0) {
CLS_ERR("error writing parent: %d", r);
return r;
}
return 0;
}
/**
* remove the parent pointer
*
* This can only happen on the head, not on a snapshot. No arguments.
*
* @returns 0 on success, negative error code on failure.
*/
int remove_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
int r = check_exists(hctx);
if (r < 0)
return r;
r = require_feature(hctx, RBD_FEATURE_LAYERING);
if (r < 0)
return r;
cls_rbd_parent parent;
r = read_key(hctx, "parent", &parent);
if (r < 0)
return r;
r = cls_cxx_map_remove_key(hctx, "parent");
if (r < 0) {
CLS_ERR("error removing parent: %d", r);
return r;
}
return 0;
}
/**
* Get the information needed to create a rados snap context for doing
* I/O to the data objects. This must include all snapshots.
@ -812,6 +1028,15 @@ int snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
last_read = vals.rbegin()->first;
} while (r == RBD_MAX_KEYS_READ);
// snapshot inherits parent, if any
cls_rbd_parent parent;
r = read_key(hctx, "parent", &parent);
if (r < 0 && r != -ENOENT)
return r;
if (r == 0) {
snap_meta.parent = parent;
}
bufferlist snap_metabl, snap_seqbl;
::encode(snap_meta, snap_metabl);
::encode(snap_meta.id, snap_seqbl);
@ -1171,6 +1396,15 @@ void __cls_init()
cls_register_cxx_method(h_class, "list_locks",
CLS_METHOD_RD | CLS_METHOD_PUBLIC,
list_locks, &h_list_locks);
cls_register_cxx_method(h_class, "get_parent",
CLS_METHOD_RD | CLS_METHOD_PUBLIC,
get_parent, &h_get_parent);
cls_register_cxx_method(h_class, "set_parent",
CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PUBLIC,
set_parent, &h_set_parent);
cls_register_cxx_method(h_class, "remove_parent",
CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PUBLIC,
remove_parent, &h_remove_parent);
/* methods for the old format */
cls_register_cxx_method(h_class, "snap_list",

View File

@ -31,8 +31,10 @@
#define RBD_HEADER_PREFIX "rbd_header."
#define RBD_DATA_PREFIX "rbd_data."
#define RBD_FEATURES_INCOMPATIBLE 0
#define RBD_FEATURES_ALL 0
#define RBD_FEATURE_LAYERING 1
#define RBD_FEATURES_INCOMPATIBLE (RBD_FEATURE_LAYERING)
#define RBD_FEATURES_ALL (RBD_FEATURE_LAYERING)
/*
* old-style rbd image 'foo' consists of objects

122
src/librbd/cls_rbd.h Normal file
View File

@ -0,0 +1,122 @@
#ifndef __CEPH_CLS_RBD_H
#define __CEPH_CLS_RBD_H
#include "include/types.h"
#include "include/buffer.h"
#include "common/Formatter.h"
/// information about our parent image, if any
struct cls_rbd_parent {
int64_t pool; ///< parent pool id
string id; ///< parent image id
snapid_t snapid; ///< parent snapid we refer to
uint64_t overlap; ///< portion of this image mapped onto parent (bytes)
/// true if our parent pointer information is defined
bool exists() const {
return snapid != CEPH_NOSNAP && pool >= 0 && id.length() > 0 && overlap > 0;
}
cls_rbd_parent() : pool(-1), snapid(CEPH_NOSNAP), overlap(0) {}
void encode(bufferlist& bl) const {
ENCODE_START(1, 1, bl);
::encode(pool, bl);
::encode(id, bl);
::encode(snapid, bl);
::encode(overlap, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& bl) {
DECODE_START(1, bl);
::decode(pool, bl);
::decode(id, bl);
::decode(snapid, bl);
::decode(overlap, bl);
DECODE_FINISH(bl);
}
void dump(Formatter *f) const {
f->dump_int("pool", pool);
f->dump_string("id", id);
f->dump_unsigned("snapid", snapid);
f->dump_unsigned("overlap", overlap);
}
static void generate_test_instances(list<cls_rbd_parent*>& o) {
o.push_back(new cls_rbd_parent);
cls_rbd_parent *t = new cls_rbd_parent;
t->pool = 1;
t->id = "foo";
t->snapid = 3;
t->overlap = 500;
o.push_back(t);
}
};
WRITE_CLASS_ENCODER(cls_rbd_parent)
struct cls_rbd_snap {
snapid_t id;
string name;
uint64_t image_size;
uint64_t features;
cls_rbd_parent parent;
/// true if we have a parent
bool has_parent() const {
return parent.exists();
}
cls_rbd_snap() : id(CEPH_NOSNAP), image_size(0), features(0) {}
void encode(bufferlist& bl) const {
ENCODE_START(2, 1, bl);
::encode(id, bl);
::encode(name, bl);
::encode(image_size, bl);
::encode(features, bl);
::encode(parent, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& p) {
DECODE_START(2, p);
::decode(id, p);
::decode(name, p);
::decode(image_size, p);
::decode(features, p);
if (struct_v >= 2) {
::decode(parent, p);
}
DECODE_FINISH(p);
}
void dump(Formatter *f) const {
f->dump_unsigned("id", id);
f->dump_string("name", name);
f->dump_unsigned("image_size", image_size);
f->dump_unsigned("features", features);
if (has_parent()) {
f->open_object_section("parent");
parent.dump(f);
f->close_section();
}
}
static void generate_test_instances(list<cls_rbd_snap*>& o) {
o.push_back(new cls_rbd_snap);
cls_rbd_snap *t = new cls_rbd_snap;
t->id = 1;
t->name = "snap";
t->image_size = 123456;
t->features = 123;
o.push_back(t);
t = new cls_rbd_snap;
t->id = 2;
t->name = "snap2";
t->image_size = 12345678;
t->features = 1234;
t->parent.pool = 1;
t->parent.id = "parent";
t->parent.snapid = 456;
t->parent.overlap = 12345;
o.push_back(t);
}
};
WRITE_CLASS_ENCODER(cls_rbd_snap)
#endif

View File

@ -95,7 +95,7 @@ namespace librbd {
}
int get_features(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id, uint64_t *features)
snapid_t snap_id, uint64_t *features)
{
bufferlist inbl, outbl;
::encode(snap_id, inbl);
@ -133,7 +133,7 @@ namespace librbd {
}
int get_size(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id, uint64_t *size, uint8_t *order)
snapid_t snap_id, uint64_t *size, uint8_t *order)
{
bufferlist inbl, outbl;
::encode(snap_id, inbl);
@ -162,8 +162,52 @@ namespace librbd {
return ioctx->exec(oid, "rbd", "set_size", bl, bl2);
}
int get_parent(librados::IoCtx *ioctx, const std::string &oid,
snapid_t snap_id, int64_t *parent_pool,
string *parent_image, snapid_t *parent_snap_id,
uint64_t *parent_overlap)
{
bufferlist inbl, outbl;
::encode(snap_id, inbl);
int r = ioctx->exec(oid, "rbd", "get_parent", inbl, outbl);
if (r < 0)
return r;
try {
bufferlist::iterator iter = outbl.begin();
::decode(*parent_pool, iter);
::decode(*parent_image, iter);
::decode(*parent_snap_id, iter);
::decode(*parent_overlap, iter);
} catch (const buffer::error &err) {
return -EBADMSG;
}
return 0;
}
int set_parent(librados::IoCtx *ioctx, const std::string &oid,
int64_t parent_pool, const string& parent_image,
snapid_t parent_snap_id, uint64_t parent_overlap)
{
bufferlist inbl, outbl;
::encode(parent_pool, inbl);
::encode(parent_image, inbl);
::encode(parent_snap_id, inbl);
::encode(parent_overlap, inbl);
return ioctx->exec(oid, "rbd", "set_parent", inbl, outbl);
}
int remove_parent(librados::IoCtx *ioctx, const std::string &oid)
{
bufferlist inbl, outbl;
return ioctx->exec(oid, "rbd", "remove_parent", inbl, outbl);
}
int snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id, const std::string &snap_name)
snapid_t snap_id, const std::string &snap_name)
{
bufferlist bl, bl2;
::encode(snap_name, bl);
@ -173,7 +217,7 @@ namespace librbd {
}
int snapshot_remove(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id)
snapid_t snap_id)
{
bufferlist bl, bl2;
::encode(snap_id, bl);
@ -219,7 +263,7 @@ namespace librbd {
for (vector<snapid_t>::const_iterator it = ids.begin();
it != ids.end(); ++it) {
bufferlist bl1, bl2, bl3;
uint64_t snap_id = it->val;
snapid_t snap_id = it->val;
::encode(snap_id, bl1);
op.exec("rbd", "get_snapshot_name", bl1);
::encode(snap_id, bl2);
@ -270,7 +314,7 @@ namespace librbd {
}
int old_snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id, const std::string &snap_name)
snapid_t snap_id, const std::string &snap_name)
{
bufferlist bl, bl2;
::encode(snap_name, bl);

View File

@ -30,17 +30,27 @@ namespace librbd {
uint64_t size, uint8_t order, uint64_t features,
const std::string &object_prefix);
int get_features(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id, uint64_t *features);
snapid_t snap_id, uint64_t *features);
int get_object_prefix(librados::IoCtx *ioctx, const std::string &oid,
std::string *object_prefix);
int get_size(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id, uint64_t *size, uint8_t *order);
snapid_t snap_id, uint64_t *size, uint8_t *order);
int set_size(librados::IoCtx *ioctx, const std::string &oid,
uint64_t size);
int set_size(librados::IoCtx *ioctx, const std::string &oid,
uint64_t size);
int get_parent(librados::IoCtx *ioctx, const std::string &oid,
snapid_t snap_id, int64_t *parent_pool,
std::string *parent_image, snapid_t *parent_snap_id,
uint64_t *parent_overlap);
int set_parent(librados::IoCtx *ioctx, const std::string &oid,
int64_t parent_pool, const std::string& parent_image, snapid_t parent_snap_id,
uint64_t parent_overlap);
int remove_parent(librados::IoCtx *ioctx, const std::string &oid);
int snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id, const std::string &snap_name);
snapid_t snap_id, const std::string &snap_name);
int snapshot_remove(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id);
snapid_t snap_id);
int get_snapcontext(librados::IoCtx *ioctx, const std::string &oid,
::SnapContext *snapc);
int snapshot_list(librados::IoCtx *ioctx, const std::string &oid,
@ -66,7 +76,7 @@ namespace librbd {
// class operations on the old format, kept for
// backwards compatability
int old_snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
uint64_t snap_id, const std::string &snap_name);
snapid_t snap_id, const std::string &snap_name);
int old_snapshot_remove(librados::IoCtx *ioctx, const std::string &oid,
const std::string &snap_name);
int old_snapshot_list(librados::IoCtx *ioctx, const std::string &oid,

View File

@ -132,6 +132,10 @@ TYPE(rgw_obj)
TYPE(rgw_log_entry)
TYPE(rgw_intent_log_entry)
#include "librbd/cls_rbd.h"
TYPE(cls_rbd_parent)
TYPE(cls_rbd_snap)
#endif
// --- messages ---

View File

@ -21,6 +21,9 @@ using ::librbd::cls_client::get_features;
using ::librbd::cls_client::get_size;
using ::librbd::cls_client::get_object_prefix;
using ::librbd::cls_client::set_size;
using ::librbd::cls_client::get_parent;
using ::librbd::cls_client::set_parent;
using ::librbd::cls_client::remove_parent;
using ::librbd::cls_client::snapshot_add;
using ::librbd::cls_client::snapshot_remove;
using ::librbd::cls_client::get_snapcontext;
@ -267,6 +270,158 @@ TEST(cls_rbd, set_size)
ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
}
TEST(cls_rbd, parents)
{
librados::Rados rados;
librados::IoCtx ioctx;
string pool_name = get_temp_pool_name();
ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
int64_t pool;
string parent;
snapid_t snapid;
uint64_t size;
ASSERT_EQ(-ENOENT, get_parent(&ioctx, "doesnotexist", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
// old image should fail
ASSERT_EQ(0, create_image(&ioctx, "old", 33<<20, 22, 0, "old_blk."));
ASSERT_EQ(-ENOEXEC, get_parent(&ioctx, "old", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
ASSERT_EQ(-ENOEXEC, set_parent(&ioctx, "old", -1, "parent", 3, 10<<20));
ASSERT_EQ(-ENOEXEC, remove_parent(&ioctx, "old"));
// new image will work
ASSERT_EQ(0, create_image(&ioctx, "foo", 33<<20, 22, RBD_FEATURE_LAYERING, "foo."));
ASSERT_EQ(-ENOENT, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
ASSERT_EQ(-ENOENT, get_parent(&ioctx, "foo", 123, &pool, &parent, &snapid, &size));
ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", -1, "parent", 3, 10<<20));
ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", 1, "", 3, 10<<20));
ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", 1, "parent", CEPH_NOSNAP, 10<<20));
ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", 1, "parent", 3, 0));
ASSERT_EQ(0, set_parent(&ioctx, "foo", 1, "parent", 3, 10<<20));
ASSERT_EQ(-EEXIST, set_parent(&ioctx, "foo", 1, "parent", 3, 10<<20));
ASSERT_EQ(-EEXIST, set_parent(&ioctx, "foo", 2, "parent", 34, 10<<20));
ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(0, remove_parent(&ioctx, "foo"));
ASSERT_EQ(-ENOENT, remove_parent(&ioctx, "foo"));
ASSERT_EQ(-ENOENT, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
// snapshots
ASSERT_EQ(0, set_parent(&ioctx, "foo", 1, "parent", 3, 10<<20));
ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 10, "snap1"));
ASSERT_EQ(0, get_parent(&ioctx, "foo", 10, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 10ull<<20);
ASSERT_EQ(0, remove_parent(&ioctx, "foo"));
ASSERT_EQ(0, set_parent(&ioctx, "foo", 4, "parent2", 6, 5<<20));
ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 11, "snap2"));
ASSERT_EQ(0, get_parent(&ioctx, "foo", 10, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 10ull<<20);
ASSERT_EQ(0, get_parent(&ioctx, "foo", 11, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 4);
ASSERT_EQ(parent, "parent2");
ASSERT_EQ(snapid, snapid_t(6));
ASSERT_EQ(size, 5ull<<20);
ASSERT_EQ(0, remove_parent(&ioctx, "foo"));
ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 12, "snap3"));
ASSERT_EQ(0, get_parent(&ioctx, "foo", 10, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 10ull<<20);
ASSERT_EQ(0, get_parent(&ioctx, "foo", 11, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 4);
ASSERT_EQ(parent, "parent2");
ASSERT_EQ(snapid, snapid_t(6));
ASSERT_EQ(size, 5ull<<20);
ASSERT_EQ(-ENOENT, get_parent(&ioctx, "foo", 12, &pool, &parent, &snapid, &size));
// make sure set_parent takes min of our size and parent's size
ASSERT_EQ(0, set_parent(&ioctx, "foo", 1, "parent", 3, 1<<20));
ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 1ull<<20);
ASSERT_EQ(0, remove_parent(&ioctx, "foo"));
ASSERT_EQ(0, set_parent(&ioctx, "foo", 1, "parent", 3, 100<<20));
ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 33ull<<20);
ASSERT_EQ(0, remove_parent(&ioctx, "foo"));
// make sure resize adjust parent overlap
ASSERT_EQ(0, set_parent(&ioctx, "foo", 1, "parent", 3, 10<<20));
ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 14, "snap4"));
ASSERT_EQ(0, set_size(&ioctx, "foo", 3 << 20));
ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 3ull<<20);
ASSERT_EQ(0, get_parent(&ioctx, "foo", 14, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 10ull<<20);
ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 15, "snap5"));
ASSERT_EQ(0, set_size(&ioctx, "foo", 30 << 20));
ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 3ull<<20);
ASSERT_EQ(0, get_parent(&ioctx, "foo", 14, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 10ull<<20);
ASSERT_EQ(0, get_parent(&ioctx, "foo", 15, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 3ull<<20);
ASSERT_EQ(0, set_size(&ioctx, "foo", 2 << 20));
ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 2ull<<20);
ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 16, "snap6"));
ASSERT_EQ(0, get_parent(&ioctx, "foo", 16, &pool, &parent, &snapid, &size));
ASSERT_EQ(pool, 1);
ASSERT_EQ(parent, "parent");
ASSERT_EQ(snapid, snapid_t(3));
ASSERT_EQ(size, 2ull<<20);
ioctx.close();
ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
}
TEST(cls_rbd, snapshots)
{
librados::Rados rados;