From 1bebea37b43223a45c39dcbc158f3cec6164fdbf Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 31 Jan 2018 16:27:38 +0100 Subject: [PATCH] rbd: Support .bdrv_co_create This adds the .bdrv_co_create driver callback to rbd, which enables image creation over QMP. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/rbd.c | 194 +++++++++++++++++++++++++++---------------- qapi/block-core.json | 19 ++++- 2 files changed, 140 insertions(+), 73 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index 999fea105f..1cd526bcea 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -332,40 +332,127 @@ static QemuOptsList runtime_opts = { }, }; +/* FIXME Deprecate and remove keypairs or make it available in QMP. + * password_secret should eventually be configurable in opts->location. Support + * for it in .bdrv_open will make it work here as well. */ +static int qemu_rbd_do_create(BlockdevCreateOptions *options, + const char *keypairs, const char *password_secret, + Error **errp) +{ + BlockdevCreateOptionsRbd *opts = &options->u.rbd; + rados_t cluster; + rados_ioctx_t io_ctx; + int obj_order = 0; + int ret; + + assert(options->driver == BLOCKDEV_DRIVER_RBD); + if (opts->location->has_snapshot) { + error_setg(errp, "Can't use snapshot name for image creation"); + return -EINVAL; + } + + /* TODO Remove the limitation */ + if (opts->location->has_server) { + error_setg(errp, "Can't specify server for image creation"); + return -EINVAL; + } + + if (opts->has_cluster_size) { + int64_t objsize = opts->cluster_size; + if ((objsize - 1) & objsize) { /* not a power of 2? */ + error_setg(errp, "obj size needs to be power of 2"); + return -EINVAL; + } + if (objsize < 4096) { + error_setg(errp, "obj size too small"); + return -EINVAL; + } + obj_order = ctz32(objsize); + } + + ret = rados_create(&cluster, opts->location->user); + if (ret < 0) { + error_setg_errno(errp, -ret, "error initializing"); + return ret; + } + + /* try default location when conf=NULL, but ignore failure */ + ret = rados_conf_read_file(cluster, opts->location->conf); + if (opts->location->conf && ret < 0) { + error_setg_errno(errp, -ret, "error reading conf file %s", + opts->location->conf); + ret = -EIO; + goto shutdown; + } + + ret = qemu_rbd_set_keypairs(cluster, keypairs, errp); + if (ret < 0) { + ret = -EIO; + goto shutdown; + } + + if (qemu_rbd_set_auth(cluster, password_secret, errp) < 0) { + ret = -EIO; + goto shutdown; + } + + ret = rados_connect(cluster); + if (ret < 0) { + error_setg_errno(errp, -ret, "error connecting"); + goto shutdown; + } + + ret = rados_ioctx_create(cluster, opts->location->pool, &io_ctx); + if (ret < 0) { + error_setg_errno(errp, -ret, "error opening pool %s", + opts->location->pool); + goto shutdown; + } + + ret = rbd_create(io_ctx, opts->location->image, opts->size, &obj_order); + if (ret < 0) { + error_setg_errno(errp, -ret, "error rbd create"); + } + + rados_ioctx_destroy(io_ctx); + + ret = 0; +shutdown: + rados_shutdown(cluster); + return ret; +} + +static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp) +{ + return qemu_rbd_do_create(options, NULL, NULL, errp); +} + static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, QemuOpts *opts, Error **errp) { + BlockdevCreateOptions *create_options; + BlockdevCreateOptionsRbd *rbd_opts; + BlockdevOptionsRbd *loc; Error *local_err = NULL; - int64_t bytes = 0; - int64_t objsize; - int obj_order = 0; - const char *pool, *image_name, *conf, *user, *keypairs; - const char *secretid; - rados_t cluster; - rados_ioctx_t io_ctx; + const char *keypairs, *password_secret; QDict *options = NULL; int ret = 0; - secretid = qemu_opt_get(opts, "password-secret"); + create_options = g_new0(BlockdevCreateOptions, 1); + create_options->driver = BLOCKDEV_DRIVER_RBD; + rbd_opts = &create_options->u.rbd; + + rbd_opts->location = g_new0(BlockdevOptionsRbd, 1); + + password_secret = qemu_opt_get(opts, "password-secret"); /* Read out options */ - bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - objsize = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, 0); - if (objsize) { - if ((objsize - 1) & objsize) { /* not a power of 2? */ - error_setg(errp, "obj size needs to be power of 2"); - ret = -EINVAL; - goto exit; - } - if (objsize < 4096) { - error_setg(errp, "obj size too small"); - ret = -EINVAL; - goto exit; - } - obj_order = ctz32(objsize); - } + rbd_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + rbd_opts->cluster_size = qemu_opt_get_size_del(opts, + BLOCK_OPT_CLUSTER_SIZE, 0); + rbd_opts->has_cluster_size = (rbd_opts->cluster_size != 0); options = qdict_new(); qemu_rbd_parse_filename(filename, options, &local_err); @@ -381,61 +468,23 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, * or blockdev_add, its members are typed according to the QAPI * schema, but when they come from -drive, they're all QString. */ - pool = qdict_get_try_str(options, "pool"); - conf = qdict_get_try_str(options, "conf"); - user = qdict_get_try_str(options, "user"); - image_name = qdict_get_try_str(options, "image"); - keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); + loc = rbd_opts->location; + loc->pool = g_strdup(qdict_get_try_str(options, "pool")); + loc->conf = g_strdup(qdict_get_try_str(options, "conf")); + loc->has_conf = !!loc->conf; + loc->user = g_strdup(qdict_get_try_str(options, "user")); + loc->has_user = !!loc->user; + loc->image = g_strdup(qdict_get_try_str(options, "image")); + keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); - ret = rados_create(&cluster, user); + ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp); if (ret < 0) { - error_setg_errno(errp, -ret, "error initializing"); goto exit; } - /* try default location when conf=NULL, but ignore failure */ - ret = rados_conf_read_file(cluster, conf); - if (conf && ret < 0) { - error_setg_errno(errp, -ret, "error reading conf file %s", conf); - ret = -EIO; - goto shutdown; - } - - ret = qemu_rbd_set_keypairs(cluster, keypairs, errp); - if (ret < 0) { - ret = -EIO; - goto shutdown; - } - - if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) { - ret = -EIO; - goto shutdown; - } - - ret = rados_connect(cluster); - if (ret < 0) { - error_setg_errno(errp, -ret, "error connecting"); - goto shutdown; - } - - ret = rados_ioctx_create(cluster, pool, &io_ctx); - if (ret < 0) { - error_setg_errno(errp, -ret, "error opening pool %s", pool); - goto shutdown; - } - - ret = rbd_create(io_ctx, image_name, bytes, &obj_order); - if (ret < 0) { - error_setg_errno(errp, -ret, "error rbd create"); - } - - rados_ioctx_destroy(io_ctx); - -shutdown: - rados_shutdown(cluster); - exit: QDECREF(options); + qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -1143,6 +1192,7 @@ static BlockDriver bdrv_rbd = { .bdrv_file_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, .bdrv_reopen_prepare = qemu_rbd_reopen_prepare, + .bdrv_co_create = qemu_rbd_co_create, .bdrv_co_create_opts = qemu_rbd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_get_info = qemu_rbd_getinfo, diff --git a/qapi/block-core.json b/qapi/block-core.json index 9170fbf6e6..d4351877fc 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3435,6 +3435,23 @@ '*lazy-refcounts': 'bool', '*refcount-bits': 'int' } } +## +# @BlockdevCreateOptionsRbd: +# +# Driver specific image creation options for rbd/Ceph. +# +# @location Where to store the new image file. This location cannot +# point to a snapshot. +# @size Size of the virtual disk in bytes +# @cluster-size RBD object size +# +# Since: 2.12 +## +{ 'struct': 'BlockdevCreateOptionsRbd', + 'data': { 'location': 'BlockdevOptionsRbd', + 'size': 'size', + '*cluster-size' : 'size' } } + ## # @BlockdevCreateNotSupported: # @@ -3484,7 +3501,7 @@ 'qed': 'BlockdevCreateNotSupported', 'quorum': 'BlockdevCreateNotSupported', 'raw': 'BlockdevCreateNotSupported', - 'rbd': 'BlockdevCreateNotSupported', + 'rbd': 'BlockdevCreateOptionsRbd', 'replication': 'BlockdevCreateNotSupported', 'sheepdog': 'BlockdevCreateNotSupported', 'ssh': 'BlockdevCreateNotSupported',