int rbd_create(rados_ioctx_t io, const char *name, uint64_t size, int *order);
int rbd_create2(rados_ioctx_t io, const char *name, uint64_t size,
uint64_t features, int *order);
+int rbd_clone(rados_ioctx_t p_ioctx, const char *p_name,
+ const char *p_snapname, rados_ioctx_t c_ioctx,
+ const char *c_name, uint64_t features, int *c_order);
int rbd_remove(rados_ioctx_t io, const char *name);
int rbd_remove_with_progress(rados_ioctx_t io, const char *name,
librbd_progress_fn_t cb, void *cbdata);
int create(IoCtx& io_ctx, const char *name, uint64_t size, int *order);
int create2(IoCtx& io_ctx, const char *name, uint64_t size,
uint64_t features, int *order);
+ int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snapname,
+ IoCtx& c_ioctx, const char *c_name, uint64_t features,
+ int *c_order);
int remove(IoCtx& io_ctx, const char *name);
int remove_with_progress(IoCtx& io_ctx, const char *name, ProgressContext& pctx);
int rename(IoCtx& src_io_ctx, const char *srcname, const char *destname);
return c;
}
+ProgressContext::~ProgressContext()
+{
+}
+
+class NoOpProgressContext : public librbd::ProgressContext
+{
+public:
+ NoOpProgressContext()
+ {
+ }
+ int update_progress(uint64_t offset, uint64_t src_size)
+ {
+ return 0;
+ }
+};
+
+class CProgressContext : public librbd::ProgressContext
+{
+public:
+ CProgressContext(librbd_progress_fn_t fn, void *data)
+ : m_fn(fn), m_data(data)
+ {
+ }
+ int update_progress(uint64_t offset, uint64_t src_size)
+ {
+ return m_fn(offset, src_size, m_data);
+ }
+private:
+ librbd_progress_fn_t m_fn;
+ void *m_data;
+};
+
void WatchCtx::invalidate()
{
Mutex::Locker l(lock);
return 0;
}
+/*
+ * Parent may be in different pool, hence different IoCtx
+ */
+int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snapname,
+ IoCtx& c_ioctx, const char *c_name,
+ uint64_t features, int *c_order)
+{
+ CephContext *cct = (CephContext *)p_ioctx.cct();
+ ldout(cct, 20) << "clone " << &p_ioctx << " name " << p_name << " snap "
+ << p_snapname << "to child " << &c_ioctx << " name "
+ << c_name << " features = " << features << " order = "
+ << *c_order << dendl;
+
+ if (features & ~RBD_FEATURES_ALL) {
+ lderr(cct) << "librbd does not support requested features" << dendl;
+ return -ENOSYS;
+ }
+
+ // make sure child doesn't already exist, in either format
+ int r = detect_format(c_ioctx, c_name, NULL, NULL);
+ if (r != -ENOENT) {
+ lderr(cct) << "rbd image " << c_name << " already exists" << dendl;
+ return -EEXIST;
+ }
+
+ if (p_snapname == NULL) {
+ lderr(cct) << "image to be cloned must be a snapshot" << dendl;
+ return -EINVAL;
+ }
+
+ // make sure parent snapshot exists
+ ImageCtx *p_imctx = new ImageCtx(p_name, p_snapname, p_ioctx);
+ r = open_image(p_imctx);
+ if (r < 0) {
+ lderr(cct) << "error opening parent image: "
+ << cpp_strerror(-r) << dendl;
+ return r;
+ }
+
+ if (p_imctx->old_format) {
+ lderr(cct) << "parent image must be in new format" << dendl;
+ return -EINVAL;
+ }
+
+ if ((p_imctx->features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
+ lderr(cct) << "parent image must support layering" << dendl;
+ return -EINVAL;
+ }
+
+ int order = *c_order;
+
+ if (!*c_order) {
+ order = p_imctx->order;
+ }
+
+ uint64_t size = p_imctx->get_image_size();
+ int remove_r;
+ librbd::NoOpProgressContext no_op;
+ ImageCtx *c_imctx = NULL;
+ r = create(c_ioctx, c_name, size, false, features, &order);
+ if (r < 0) {
+ lderr(cct) << "error creating child: " << cpp_strerror(r) << dendl;
+ goto err_close_parent;
+ }
+
+ uint64_t p_poolid;
+ p_poolid = p_ioctx.get_id();
+
+ c_imctx = new ImageCtx(c_name, NULL, c_ioctx);
+ r = open_image(c_imctx);
+ if (r < 0) {
+ lderr(cct) << "Error opening new image: " << cpp_strerror(r) << dendl;
+ goto err_remove;
+ }
+
+ r = cls_client::set_parent(&c_ioctx, c_imctx->header_oid, p_poolid,
+ p_imctx->id, p_imctx->snapid, size);
+ if (r < 0) {
+ lderr(cct) << "couldn't set parent: " << r << dendl;
+ goto err_close_child;
+ }
+ ldout(cct, 2) << "done." << dendl;
+ close_image(c_imctx);
+ close_image(p_imctx);
+ return 0;
+
+ err_close_child:
+ close_image(c_imctx);
+ err_remove:
+ remove_r = remove(c_ioctx, c_name, no_op);
+ if (remove_r < 0) {
+ lderr(cct) << "Error removing failed clone: "
+ << cpp_strerror(remove_r) << dendl;
+ }
+ err_close_parent:
+ close_image(p_imctx);
+ return r;
+}
+
int rename(IoCtx& io_ctx, const char *srcname, const char *dstname)
{
CephContext *cct = (CephContext *)io_ctx.cct();
return 0;
}
-ProgressContext::~ProgressContext()
-{
-}
-
-class NoOpProgressContext : public librbd::ProgressContext
-{
-public:
- NoOpProgressContext()
- {
- }
- int update_progress(uint64_t offset, uint64_t src_size)
- {
- return 0;
- }
-};
-
-class CProgressContext : public librbd::ProgressContext
-{
-public:
- CProgressContext(librbd_progress_fn_t fn, void *data)
- : m_fn(fn), m_data(data)
- {
- }
- int update_progress(uint64_t offset, uint64_t src_size)
- {
- return m_fn(offset, src_size, m_data);
- }
-private:
- librbd_progress_fn_t m_fn;
- void *m_data;
-};
-
int snap_rollback(ImageCtx *ictx, const char *snap_name, ProgressContext& prog_ctx)
{
CephContext *cct = ictx->cct;
return librbd::create(io_ctx, name, size, false, features, order);
}
+int RBD::clone(IoCtx& p_ioctx, const char *p_name, const char *p_snapname,
+ IoCtx& c_ioctx, const char *c_name, uint64_t features,
+ int *c_order)
+{
+ return librbd::clone(p_ioctx, p_name, p_snapname, c_ioctx, c_name,
+ features, c_order);
+}
+
int RBD::remove(IoCtx& io_ctx, const char *name)
{
librbd::NoOpProgressContext prog_ctx;
return librbd::create(io_ctx, name, size, false, features, order);
}
+extern "C" int rbd_clone(rados_ioctx_t p_ioctx, const char *p_name,
+ const char *p_snapname, rados_ioctx_t c_ioctx,
+ const char *c_name, uint64_t features, int *c_order)
+{
+ librados::IoCtx p_ioc, c_ioc;
+ librados::IoCtx::from_rados_ioctx_t(p_ioctx, p_ioc);
+ librados::IoCtx::from_rados_ioctx_t(c_ioctx, c_ioc);
+ return librbd::clone(p_ioc, p_name, p_snapname, c_ioc, c_name,
+ features, c_order);
+}
+
extern "C" int rbd_remove(rados_ioctx_t p, const char *name)
{
librados::IoCtx io_ctx;
ANONYMOUS_AUID = 0xffffffffffffffff
ADMIN_AUID = 0
+RBD_FEATURE_LAYERING = 1
+
class Error(Exception):
pass
class ImageHasSnapshots(Error):
pass
+class FunctionNotSupported(Error):
+ pass
+
+class ArgumentOutOfRange(Error):
+ pass
+
def make_ex(ret, msg):
"""
Translate a librbd return code into an exception.
errno.EROFS : ReadOnlyImage,
errno.EBUSY : ImageBusy,
errno.ENOTEMPTY : ImageHasSnapshots,
+ errno.ENOSYS : FunctionNotSupported,
+ errno.EDOM : ArgumentOutOfRange
}
ret = abs(ret)
if ret in errors:
if ret < 0:
raise make_ex(ret, 'error creating image')
+ def clone(self, p_ioctx, p_name, p_snapname, c_ioctx, c_name,
+ features=0, order=None):
+ """
+ Clone a parent rbd snapshot into a COW sparse child.
+
+ :param p_ioctx: the parent context that represents the parent snap
+ :type ioctx: :class:`rados.Ioctx`
+ :param p_name: the parent image name
+ :type name: str
+ :param p_snapname: the parent image snapshot name
+ :type name: str
+ :param c_ioctx: the child context that represents the new clone
+ :type ioctx: :class:`rados.Ioctx`
+ :param c_name: the clone (child) name
+ :type name: str
+ :param features: bitmask of features to enable; if set, must include layering
+ :type features: int
+ :param order: the image is split into (2**order) byte objects
+ :type order: int
+ :raises: :class:`TypeError`
+ :raises: :class:`InvalidArgument`
+ :raises: :class:`ImageExists`
+ :raises: :class:`FunctionNotSupported`
+ :raises: :class:`ArgumentOutOfRange`
+ """
+ if order is None:
+ order = 0
+ if not isinstance(p_snapname, str) or not isinstance(p_name, str):
+ raise TypeError('parent name and snapname must be strings')
+ if not isinstance(c_name, str):
+ raise TypeError('child name must be a string')
+
+ ret = self.librbd.rbd_clone(p_ioctx.io, c_char_p(p_name),
+ c_char_p(p_snapname),
+ c_ioctx.io, c_char_p(c_name),
+ c_uint64(features),
+ byref(c_int(order)))
+ if ret < 0:
+ raise make_ex(ret, 'error creating clone')
+
def list(self, ioctx):
"""
List image names.
void usage()
{
- cout << "usage: rbd [-n <auth user>] [OPTIONS] <cmd> ...\n"
- << "where 'pool' is a rados pool name (default is 'rbd') and 'cmd' is one of:\n"
- << " (ls | list) [pool-name] list rbd images\n"
- << " info [--snap <name>] <image-name> show information about image size,\n"
- << " striping, etc.\n"
- << " create [--order <bits>] --size <MB> <name> create an empty image\n"
- << " resize --size <MB> <image-name> resize (expand or contract) image\n"
- << " rm <image-name> delete an image\n"
- << " export [--snap <name>] <image-name> <path> export image to file\n"
- << " import <path> <dst-image> import image from file (dest defaults\n"
- << " as the filename part of file)\n"
- << " (cp | copy) [--snap <name>] <src> <dest> copy src image to dest\n"
- << " (mv | rename) <src> <dest> rename src image to dest\n"
- << " snap ls <image-name> dump list of image snapshots\n"
- << " snap create --snap <name> <image-name> create a snapshot\n"
- << " snap rollback --snap <name> <image-name> rollback image head to snapshot\n"
- << " snap rm --snap <name> <image-name> deletes a snapshot\n"
- << " snap purge <image-name> deletes all snapshots\n"
- << " watch <image-name> watch events on image\n"
- << " map <image-name> map the image to a block device\n"
- << " using the kernel\n"
- << " unmap <device> unmap a rbd device that was\n"
- << " mapped by the kernel\n"
- << " showmapped show the rbd images mapped\n"
- << " by the kernel\n"
- << "\n"
- << "Other input options:\n"
- << " -p, --pool <pool> source pool name\n"
- << " --image <image-name> image name\n"
- << " --dest <name> destination [pool and] image name\n"
- << " --snap <snapname> specify snapshot name\n"
- << " --dest-pool <name> destination pool name\n"
- << " --path <path-name> path name for import/export (if not specified)\n"
- << " --size <size in MB> size parameter for create and resize commands\n"
- << " --order <bits> the object size in bits, such that the objects\n"
- << " are (1 << order) bytes. Default is 22 (4 MB).\n"
- << "\n"
- << "For the map command:\n"
- << " --user <username> rados user to authenticate as\n"
- << " --secret <path> file containing secret key for use with cephx\n";
+ cout <<
+"usage: rbd [-n <auth user>] [OPTIONS] <cmd> ...\n"
+"where 'pool' is a rados pool name (default is 'rbd') and 'cmd' is one of:\n"
+" (ls | list) [pool-name] list rbd images\n"
+" info <image-name> show information about image size,\n"
+" striping, etc.\n"
+" create [--order <bits>] --size <MB> <name> create an empty image\n"
+" clone [--order <bits>] <parentsnap> <clonename>\n"
+" clone a snapshot into a COW\n"
+" child image\n"
+" resize --size <MB> <image-name> resize (expand or contract) image\n"
+" rm <image-name> delete an image\n"
+" export <image-name> <path> export image to file\n"
+" import <path> <image-name> import image from file\n"
+" (dest defaults)\n"
+" as the filename part of file)\n"
+" (cp | copy) <src> <dest> copy src image to dest\n"
+" (mv | rename) <src> <dest> rename src image to dest\n"
+" snap ls <image-name> dump list of image snapshots\n"
+" snap create <snap-name> create a snapshot\n"
+" snap rollback <snap-name> rollback image to snapshot\n"
+" snap rm <snap-name> deletes a snapshot\n"
+" snap purge <image-name> deletes all snapshots\n"
+" watch <image-name> watch events on image\n"
+" map <image-name> map image to a block device\n"
+" using the kernel\n"
+" unmap <device> unmap a rbd device that was\n"
+" mapped by the kernel\n"
+" showmapped show the rbd images mapped\n"
+" by the kernel\n"
+"\n"
+"<image-name>, <snap-name> are [pool/]name[@snap], or you may specify\n"
+"individual pieces of names with -p/--pool, --image, and/or --snap.\n"
+"\n"
+"Other input options:\n"
+" -p, --pool <pool> source pool name\n"
+" --image <image-name> image name\n"
+" --dest <image-name> destination [pool and] image name\n"
+" --snap <snap-name> snapshot name\n"
+" --dest-pool <name> destination pool name\n"
+" --path <path-name> path name for import/export\n"
+" --size <size in MB> size of image for create and resize\n"
+" --order <bits> the object size in bits; object size will be\n"
+" (1 << order) bytes. Default is 22 (4 MB).\n"
+"\n"
+"For the map command:\n"
+" --user <username> rados user to authenticate as\n"
+" --secret <path> file containing secret key for use with cephx\n";
}
void usage_exit()
bool old_format, uint64_t features)
{
int r;
+ if (features == 0)
+ features = RBD_FEATURES_ALL;
+
if (old_format)
r = rbd.create(io_ctx, imgname, size, order);
else
return 0;
}
+static int do_clone(librbd::RBD &rbd, librados::IoCtx &p_ioctx,
+ const char *p_name, const char *p_snapname,
+ librados::IoCtx &c_ioctx, const char *c_name,
+ uint64_t features, int *c_order)
+{
+ if (features == 0)
+ features = RBD_FEATURES_ALL;
+ else if ((features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING)
+ return -EINVAL;
+
+ return rbd.clone(p_ioctx, p_name, p_snapname, c_ioctx, c_name, features,
+ c_order);
+}
+
static int do_rename(librbd::RBD &rbd, librados::IoCtx& io_ctx,
const char *imgname, const char *destname)
{
OPT_LIST,
OPT_INFO,
OPT_CREATE,
+ OPT_CLONE,
OPT_RESIZE,
OPT_RM,
OPT_EXPORT,
return OPT_INFO;
if (strcmp(cmd, "create") == 0)
return OPT_CREATE;
+ if (strcmp(cmd, "clone") == 0)
+ return OPT_CLONE;
if (strcmp(cmd, "resize") == 0)
return OPT_RESIZE;
if (strcmp(cmd, "rm") == 0)
uint64_t size = 0; // in bytes
int order = 0;
bool old_format = true;
- const char *imgname = NULL, *snapname = NULL, *destname = NULL, *dest_poolname = NULL, *path = NULL, *secretfile = NULL, *user = NULL, *devpath = NULL;
+ const char *imgname = NULL, *snapname = NULL, *destname = NULL, *dest_poolname = NULL, *dest_snapname = NULL, *path = NULL, *secretfile = NULL, *user = NULL, *devpath = NULL;
std::string val;
std::ostringstream err;
secretfile = strdup(val.c_str());
} else if (ceph_argparse_witharg(args, i, &val, "--user", (char*)NULL)) {
user = strdup(val.c_str());
+ } else if (ceph_argparse_witharg(args, i, &val, "--parent", (char *)NULL)) {
+ imgname = strdup(val.c_str());
} else {
++i;
}
case OPT_RENAME:
set_conf_param(v, &imgname, &destname);
break;
+ case OPT_CLONE:
+ if (imgname == NULL) {
+ set_conf_param(v, &imgname, NULL);
+ } else {
+ set_conf_param(v, &destname, NULL);
+ }
+ break;
case OPT_SHOWMAPPED:
usage_exit();
break;
if (snapname && opt_cmd != OPT_SNAP_CREATE && opt_cmd != OPT_SNAP_ROLLBACK &&
opt_cmd != OPT_SNAP_REMOVE && opt_cmd != OPT_INFO &&
opt_cmd != OPT_EXPORT && opt_cmd != OPT_COPY &&
- opt_cmd != OPT_MAP) {
+ opt_cmd != OPT_MAP && opt_cmd != OPT_CLONE) {
cerr << "error: snapname specified for a command that doesn't use it" << std::endl;
usage_exit();
}
if ((opt_cmd == OPT_SNAP_CREATE || opt_cmd == OPT_SNAP_ROLLBACK ||
- opt_cmd == OPT_SNAP_REMOVE) && !snapname) {
+ opt_cmd == OPT_SNAP_REMOVE || opt_cmd == OPT_CLONE) && !snapname) {
cerr << "error: snap name was not specified" << std::endl;
usage_exit();
}
- set_pool_image_name(dest_poolname, destname, (char **)&dest_poolname, (char **)&destname, NULL);
+ set_pool_image_name(dest_poolname, destname, (char **)&dest_poolname, (char **)&destname, (char **)&dest_snapname);
if (!poolname)
poolname = "rbd";
if (opt_cmd == OPT_EXPORT && !path)
path = imgname;
- if (opt_cmd == OPT_COPY && !destname ) {
+ if ((opt_cmd == OPT_COPY || opt_cmd == OPT_CLONE) && !destname ) {
cerr << "error: destination image name was not specified" << std::endl;
usage_exit();
}
+ if ((opt_cmd == OPT_CLONE) && dest_snapname) {
+ cerr << "error: cannot clone to a snapshot" << std::endl;
+ usage_exit();
+ }
+
+ if ((opt_cmd == OPT_CLONE) && size) {
+ cerr << "error: clone must begin at size of parent" << std::endl;
+ usage_exit();
+ }
+
if ((opt_cmd == OPT_RENAME) && (strcmp(poolname, dest_poolname) != 0)) {
cerr << "error: mv/rename across pools not supported" << std::endl;
cerr << "source pool: " << poolname << " dest pool: " << dest_poolname
}
}
- if (opt_cmd == OPT_COPY || opt_cmd == OPT_IMPORT) {
+ if (opt_cmd == OPT_COPY || opt_cmd == OPT_IMPORT || opt_cmd == OPT_CLONE) {
r = rados.ioctx_create(dest_poolname, dest_io_ctx);
if (r < 0) {
cerr << "error opening pool " << dest_poolname << ": " << cpp_strerror(-r) << std::endl;
}
break;
+ case OPT_CLONE:
+ if (order && (order < 12 || order > 25)) {
+ cerr << "order must be between 12 (4 KB) and 25 (32 MB)" << std::endl;
+ usage();
+ exit(1);
+ }
+
+ r = do_clone(rbd, io_ctx, imgname, snapname, dest_io_ctx, destname,
+ RBD_FEATURE_LAYERING, &order);
+ if (r < 0) {
+ cerr << "clone error: " << cpp_strerror(-r) << std::endl;
+ exit(1);
+ }
+ break;
+
case OPT_RENAME:
r = do_rename(rbd, io_ctx, imgname, destname);
if (r < 0) {