From nobody Wed Apr 16 04:33:53 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 14997923745571012.8063506255495; Tue, 11 Jul 2017 09:59:34 -0700 (PDT) Received: from localhost ([::1]:47608 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dUyVY-0002aI-58 for importer@patchew.org; Tue, 11 Jul 2017 12:59:32 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:46876) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dUxm9-0001wY-45 for qemu-devel@nongnu.org; Tue, 11 Jul 2017 12:12:38 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dUxm7-000782-0P for qemu-devel@nongnu.org; Tue, 11 Jul 2017 12:12:37 -0400 Received: from mx1.redhat.com ([209.132.183.28]:38968) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dUxlz-000727-E9; Tue, 11 Jul 2017 12:12:27 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D62808E254; Tue, 11 Jul 2017 16:12:21 +0000 (UTC) Received: from localhost (ovpn-204-123.brq.redhat.com [10.40.204.123]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2B0A119157; Tue, 11 Jul 2017 16:12:20 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com D62808E254 Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=mreitz@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com D62808E254 From: Max Reitz To: qemu-block@nongnu.org Date: Tue, 11 Jul 2017 18:07:56 +0200 Message-Id: <20170711160814.20941-68-mreitz@redhat.com> In-Reply-To: <20170711160814.20941-1-mreitz@redhat.com> References: <20170711160814.20941-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Tue, 11 Jul 2017 16:12:22 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PULL 67/85] qemu-img: add measure subcommand X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org, Max Reitz Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Stefan Hajnoczi The measure subcommand calculates the size required by a new image file. This can be used by users or management tools that need to allocate space on an LVM volume, SAN LUN, etc before creating or converting an image file. Suggested-by: Maor Lipchuk Signed-off-by: Stefan Hajnoczi Reviewed-by: Alberto Garcia Message-id: 20170705125738.8777-8-stefanha@redhat.com Signed-off-by: Max Reitz --- qemu-img.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++= ++++ qemu-img-cmds.hx | 6 ++ qemu-img.texi | 30 +++++++ 3 files changed, 270 insertions(+) diff --git a/qemu-img.c b/qemu-img.c index 7f1de74..930ae88 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -61,6 +61,7 @@ enum { OPTION_FLUSH_INTERVAL =3D 261, OPTION_NO_DRAIN =3D 262, OPTION_TARGET_IMAGE_OPTS =3D 263, + OPTION_SIZE =3D 264, }; =20 typedef enum OutputFormat { @@ -4419,6 +4420,239 @@ out: return 0; } =20 +static void dump_json_block_measure_info(BlockMeasureInfo *info) +{ + QString *str; + QObject *obj; + Visitor *v =3D qobject_output_visitor_new(&obj); + + visit_type_BlockMeasureInfo(v, NULL, &info, &error_abort); + visit_complete(v, &obj); + str =3D qobject_to_json_pretty(obj); + assert(str !=3D NULL); + printf("%s\n", qstring_get_str(str)); + qobject_decref(obj); + visit_free(v); + QDECREF(str); +} + +static int img_measure(int argc, char **argv) +{ + static const struct option long_options[] =3D { + {"help", no_argument, 0, 'h'}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"output", required_argument, 0, OPTION_OUTPUT}, + {"size", required_argument, 0, OPTION_SIZE}, + {"force-share", no_argument, 0, 'U'}, + {0, 0, 0, 0} + }; + OutputFormat output_format =3D OFORMAT_HUMAN; + BlockBackend *in_blk =3D NULL; + BlockDriver *drv; + const char *filename =3D NULL; + const char *fmt =3D NULL; + const char *out_fmt =3D "raw"; + char *options =3D NULL; + char *snapshot_name =3D NULL; + bool force_share =3D false; + QemuOpts *opts =3D NULL; + QemuOpts *object_opts =3D NULL; + QemuOpts *sn_opts =3D NULL; + QemuOptsList *create_opts =3D NULL; + bool image_opts =3D false; + uint64_t img_size =3D UINT64_MAX; + BlockMeasureInfo *info =3D NULL; + Error *local_err =3D NULL; + int ret =3D 1; + int c; + + while ((c =3D getopt_long(argc, argv, "hf:O:o:l:U", + long_options, NULL)) !=3D -1) { + switch (c) { + case '?': + case 'h': + help(); + break; + case 'f': + fmt =3D optarg; + break; + case 'O': + out_fmt =3D optarg; + break; + case 'o': + if (!is_valid_option_list(optarg)) { + error_report("Invalid option list: %s", optarg); + goto out; + } + if (!options) { + options =3D g_strdup(optarg); + } else { + char *old_options =3D options; + options =3D g_strdup_printf("%s,%s", options, optarg); + g_free(old_options); + } + break; + case 'l': + if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { + sn_opts =3D qemu_opts_parse_noisily(&internal_snapshot_opt= s, + optarg, false); + if (!sn_opts) { + error_report("Failed in parsing snapshot param '%s'", + optarg); + goto out; + } + } else { + snapshot_name =3D optarg; + } + break; + case 'U': + force_share =3D true; + break; + case OPTION_OBJECT: + object_opts =3D qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!object_opts) { + goto out; + } + break; + case OPTION_IMAGE_OPTS: + image_opts =3D true; + break; + case OPTION_OUTPUT: + if (!strcmp(optarg, "json")) { + output_format =3D OFORMAT_JSON; + } else if (!strcmp(optarg, "human")) { + output_format =3D OFORMAT_HUMAN; + } else { + error_report("--output must be used with human or json " + "as argument."); + goto out; + } + break; + case OPTION_SIZE: + { + int64_t sval; + + sval =3D cvtnum(optarg); + if (sval < 0) { + if (sval =3D=3D -ERANGE) { + error_report("Image size must be less than 8 EiB!"); + } else { + error_report("Invalid image size specified! You may us= e " + "k, M, G, T, P or E suffixes for "); + error_report("kilobytes, megabytes, gigabytes, terabyt= es, " + "petabytes and exabytes."); + } + goto out; + } + img_size =3D (uint64_t)sval; + } + break; + } + } + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + goto out; + } + + if (argc - optind > 1) { + error_report("At most one filename argument is allowed."); + goto out; + } else if (argc - optind =3D=3D 1) { + filename =3D argv[optind]; + } + + if (!filename && + (object_opts || image_opts || fmt || snapshot_name || sn_opts)) { + error_report("--object, --image-opts, -f, and -l " + "require a filename argument."); + goto out; + } + if (filename && img_size !=3D UINT64_MAX) { + error_report("--size N cannot be used together with a filename."); + goto out; + } + if (!filename && img_size =3D=3D UINT64_MAX) { + error_report("Either --size N or one filename must be specified."); + goto out; + } + + if (filename) { + in_blk =3D img_open(image_opts, filename, fmt, 0, + false, false, force_share); + if (!in_blk) { + goto out; + } + + if (sn_opts) { + bdrv_snapshot_load_tmp(blk_bs(in_blk), + qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID), + qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME), + &local_err); + } else if (snapshot_name !=3D NULL) { + bdrv_snapshot_load_tmp_by_id_or_name(blk_bs(in_blk), + snapshot_name, &local_err); + } + if (local_err) { + error_reportf_err(local_err, "Failed to load snapshot: "); + goto out; + } + } + + drv =3D bdrv_find_format(out_fmt); + if (!drv) { + error_report("Unknown file format '%s'", out_fmt); + goto out; + } + if (!drv->create_opts) { + error_report("Format driver '%s' does not support image creation", + drv->format_name); + goto out; + } + + create_opts =3D qemu_opts_append(create_opts, drv->create_opts); + create_opts =3D qemu_opts_append(create_opts, bdrv_file.create_opts); + opts =3D qemu_opts_create(create_opts, NULL, 0, &error_abort); + if (options) { + qemu_opts_do_parse(opts, options, NULL, &local_err); + if (local_err) { + error_report_err(local_err); + error_report("Invalid options for file format '%s'", out_fmt); + goto out; + } + } + if (img_size !=3D UINT64_MAX) { + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); + } + + info =3D bdrv_measure(drv, opts, in_blk ? blk_bs(in_blk) : NULL, &loca= l_err); + if (local_err) { + error_report_err(local_err); + goto out; + } + + if (output_format =3D=3D OFORMAT_HUMAN) { + printf("required size: %" PRIu64 "\n", info->required); + printf("fully allocated size: %" PRIu64 "\n", info->fully_allocate= d); + } else { + dump_json_block_measure_info(info); + } + + ret =3D 0; + +out: + qapi_free_BlockMeasureInfo(info); + qemu_opts_del(object_opts); + qemu_opts_del(opts); + qemu_opts_del(sn_opts); + qemu_opts_free(create_opts); + g_free(options); + blk_unref(in_blk); + return ret; +} =20 static const img_cmd_t img_cmds[] =3D { #define DEF(option, callback, arg_string) \ diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index a39fcdb..ac5946b 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -63,6 +63,12 @@ STEXI @item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--outp= ut=3D@var{ofmt}] [-U] @var{filename} ETEXI =20 +DEF("measure", img_measure, +"measure [--output=3Dofmt] [-O output_fmt] [-o options] [--size N | [--obj= ect objectdef] [--image-opts] [-f fmt] [-l snapshot_param] filename]") +STEXI +@item measure [--output=3D@var{ofmt}] [-O @var{output_fmt}] [-o @var{optio= ns}] [--size @var{N} | [--object @var{objectdef}] [--image-opts] [-f @var{f= mt}] [-l @var{snapshot_param}] @var{filename}] +ETEXI + DEF("snapshot", img_snapshot, "snapshot [--object objectdef] [--image-opts] [-U] [-q] [-l | -a snaps= hot | -c snapshot | -d snapshot] filename") STEXI diff --git a/qemu-img.texi b/qemu-img.texi index f335139..a2f4519 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -438,6 +438,36 @@ preallocated. For more information, consult @file{include/block/block.h} in QEMU's source code. =20 +@item measure [--output=3D@var{ofmt}] [-O @var{output_fmt}] [-o @var{optio= ns}] [--size @var{N} | [--object @var{objectdef}] [--image-opts] [-f @var{f= mt}] [-l @var{snapshot_param}] @var{filename}] + +Calculate the file size required for a new image. This information can be= used +to size logical volumes or SAN LUNs appropriately for the image that will = be +placed in them. The values reported are guaranteed to be large enough to = fit +the image. The command can output in the format @var{ofmt} which is either +@code{human} or @code{json}. + +If the size @var{N} is given then act as if creating a new empty image file +using @command{qemu-img create}. If @var{filename} is given then act as if +converting an existing image file using @command{qemu-img convert}. The f= ormat +of the new file is given by @var{output_fmt} while the format of an existi= ng +file is given by @var{fmt}. + +A snapshot in an existing image can be specified using @var{snapshot_param= }. + +The following fields are reported: +@example +required size: 524288 +fully allocated size: 1074069504 +@end example + +The @code{required size} is the file size of the new image. It may be sma= ller +than the virtual disk size if the image format supports compact representa= tion. + +The @code{fully allocated size} is the file size of the new image once dat= a has +been written to all sectors. This is the maximum size that the image file= can +occupy with the exception of internal snapshots, dirty bitmaps, vmstate da= ta, +and other advanced image format features. + @item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snaps= hot} ] @var{filename} =20 List, apply, create or delete snapshots in image @var{filename}. --=20 2.9.4