mirror of
https://github.com/ceph/ceph
synced 2025-01-04 02:02:36 +00:00
Merge branch 'wip-clsrbd'
Reviewed-by: Greg Farnum <greg@inktank.com> Reviewed-by: Josh Durgin <josh.durgin@inktank.com>
This commit is contained in:
commit
6948af0284
@ -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\
|
||||
|
308
src/cls_rbd.cc
308
src/cls_rbd.cc
@ -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",
|
||||
|
@ -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
122
src/librbd/cls_rbd.h
Normal 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
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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 ---
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user