From 762eb169dd4e1755f3ca543a260592f975c1bbab Mon Sep 17 00:00:00 2001 Message-Id: <762eb169dd4e1755f3ca543a260592f975c1bbab.1366117835.git.minovotn@redhat.com> In-Reply-To: <8a8dc925d6cdb62aba736eb1551195551e09271b.1366117835.git.minovotn@redhat.com> References: <8a8dc925d6cdb62aba736eb1551195551e09271b.1366117835.git.minovotn@redhat.com> From: Kevin Wolf Date: Thu, 7 Mar 2013 15:29:24 +0100 Subject: [PATCH 16/19] qemu-img: add json output option to the check command RH-Author: Kevin Wolf Message-id: <1362670164-15796-13-git-send-email-kwolf@redhat.com> Patchwork-id: 49314 O-Subject: [RHEL-6.5 qemu-kvm PATCH 12/12] qemu-img: add json output option to the check command Bugzilla: 888008 RH-Acked-by: Eric Blake RH-Acked-by: Miroslav Rezanina RH-Acked-by: Stefan Hajnoczi From: Federico Simoncelli This option --output=[human|json] makes qemu-img check output a human or JSON representation at the choice of the user. Signed-off-by: Federico Simoncelli Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf (cherry picked from commit 8599ea4c42c098d2657ed632ad569f7a665706a4) Conflicts: qapi-schema.json qemu-img.c Signed-off-by: Kevin Wolf --- Makefile | 26 ++++++- qapi-schema.json | 47 ++++++++++++ qemu-img-cmds.hx | 4 +- qemu-img.c | 226 ++++++++++++++++++++++++++++++++++++++++++------------- qemu-img.texi | 5 +- 5 files changed, 249 insertions(+), 59 deletions(-) Signed-off-by: Michal Novotny --- Makefile | 26 ++++++- qapi-schema.json | 47 ++++++++++++ qemu-img-cmds.hx | 4 +- qemu-img.c | 226 ++++++++++++++++++++++++++++++++++++++++++------------- qemu-img.texi | 5 +- 5 files changed, 249 insertions(+), 59 deletions(-) diff --git a/Makefile b/Makefile index 9044017..ca1839e 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ GENERATED_HEADERS = config-host.h trace.h config-all-devices.h ifeq ($(TRACE_BACKEND),dtrace) GENERATED_HEADERS += trace-dtrace.h endif -GENERATED_HEADERS += qmp-commands.h +GENERATED_HEADERS += qmp-commands.h qapi-visit.h ifneq ($(wildcard config-host.mak),) # Put the all: rule here so that config-host.mak can contain dependencies. @@ -29,10 +29,16 @@ endif ifeq ($(CONFIG_LIVE_SNAPSHOTS),y) rhel_rhev_qmp_commands.h = rhev-qmp-commands.h +rhel_rhev_qapi_visit.h = rhev-qapi-visit.h +rhel_rhev_qapi_visit.o = rhev-qapi-visit.o +rhel_rhev_qapi_types.o = rhev-qapi-types.o GENERATED_HEADERS += rhev-qmp-commands.h rhev-qapi-types.h rhev-qapi-visit.h GENERATED_SOURCES += rhev-qmp-marshal.c rhev-qapi-types.c rhev-qapi-visit.c else rhel_rhev_qmp_commands.h = rhel-qmp-commands.h +rhel_rhev_qapi_visit.h = rhel-qapi-visit.h +rhel_rhev_qapi_visit.o = rhel-qapi-visit.o +rhel_rhev_qapi_types.o = rhel-qapi-types.o GENERATED_HEADERS += rhel-qmp-commands.h rhel-qapi-types.h rhel-qapi-visit.h GENERATED_SOURCES += rhel-qmp-marshal.c rhel-qapi-types.c rhel-qapi-visit.c endif @@ -189,7 +195,8 @@ qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o: $(GENERATED_HEADERS) TOOLS_OBJ=qemu-tool.o qerror.o $(shared-obj-y) $(trace-obj-y) -qemu-img$(EXESUF): qemu-img.o $(TOOLS_OBJ) +qemu-img$(EXESUF): qemu-img.o $(TOOLS_OBJ) $(qapi-obj-y) \ + $(rhel_rhev_qapi_visit.o) $(rhel_rhev_qapi_types.o) qemu-nbd$(EXESUF): qemu-nbd.o $(TOOLS_OBJ) @@ -287,6 +294,21 @@ export QMP_COMMANDS_H qmp-commands.h: $(rhel_rhev_qmp_commands.h) $(call quiet-command, echo "$$QMP_COMMANDS_H" > $@, " GEN $@") +define QAPI_VISIT_H +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +#ifndef QAPI_VISIT_H +#define QAPI_VISIT_H + +#include "$(rhel_rhev_qapi_visit.h)" + + +#endif +endef + +export QAPI_VISIT_H +qapi-visit.h: $(rhel_rhev_qapi_visit.h) + $(call quiet-command, echo "$$QAPI_VISIT_H" > $@, " GEN $@") test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o diff --git a/qapi-schema.json b/qapi-schema.json index d6cbf05..bfcea79 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -224,6 +224,53 @@ #endif ## +# @ImageCheck: +# +# Information about a QEMU image file check +# +# @filename: name of the image file checked +# +# @format: format of the image file checked +# +# @check-errors: number of unexpected errors occurred during check +# +# @image-end-offset: #optional offset (in bytes) where the image ends, this +# field is present if the driver for the image format +# supports it +# +# @corruptions: #optional number of corruptions found during the check if any +# +# @leaks: #optional number of leaks found during the check if any +# +# @corruptions-fixed: #optional number of corruptions fixed during the check +# if any +# +# @leaks-fixed: #optional number of leaks fixed during the check if any +# +# @total-clusters: #optional total number of clusters, this field is present +# if the driver for the image format supports it +# +# @allocated-clusters: #optional total number of allocated clusters, this +# field is present if the driver for the image format +# supports it +# +# @fragmented-clusters: #optional total number of fragmented clusters, this +# field is present if the driver for the image format +# supports it +# +# Since: 1.4 +# +## + +{ 'type': 'ImageCheck', + 'data': {'filename': 'str', 'format': 'str', 'check-errors': 'int', + '*image-end-offset': 'int', '*corruptions': 'int', '*leaks': 'int', + '*corruptions-fixed': 'int', '*leaks-fixed': 'int', + '*total-clusters': 'int', '*allocated-clusters': 'int', + '*fragmented-clusters': 'int' } } + + +## # @EventInfo: # # Information about a QMP event diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 304d081..084108f 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -10,9 +10,9 @@ STEXI STEXI DEF("check", img_check, - "check [-f fmt] [-r [leaks | all]] filename") + "check [-f fmt] [--output=ofmt] [-r [leaks | all]] filename") STEXI -@item check [-f @var{fmt}] [-r [leaks | all]] @var{filename} +@item check [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] @var{filename} ETEXI DEF("create", img_create, diff --git a/qemu-img.c b/qemu-img.c index e60b79c..a83c9f8 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -21,11 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qapi-visit.h" +#include "qapi/qmp-output-visitor.h" +#include "qjson.h" #include "qemu-common.h" #include "qemu-option.h" #include "qemu-error.h" #include "osdep.h" #include "block_int.h" +#include #include #ifdef _WIN32 @@ -37,6 +41,16 @@ typedef struct img_cmd_t { int (*handler)(int argc, char **argv); } img_cmd_t; +enum { + OPTION_OUTPUT = 256, + OPTION_BACKING_CHAIN = 257, +}; + +typedef enum OutputFormat { + OFORMAT_JSON, + OFORMAT_HUMAN, +} OutputFormat; + /* Default to cache=writeback as data integrity is not important for qemu-tcg. */ #define BDRV_O_FLAGS BDRV_O_CACHE_WB #define BDRV_DEFAULT_CACHE "writeback" @@ -382,6 +396,96 @@ out: return 0; } +static void dump_json_image_check(ImageCheck *check) +{ + Error *errp = NULL; + QString *str; + QmpOutputVisitor *ov = qmp_output_visitor_new(); + QObject *obj; + visit_type_ImageCheck(qmp_output_get_visitor(ov), + &check, NULL, &errp); + obj = qmp_output_get_qobject(ov); + str = qobject_to_json(obj); + assert(str != NULL); + printf("%s\n", qstring_get_str(str)); + qobject_decref(obj); + qmp_output_visitor_cleanup(ov); + QDECREF(str); +} + +static void dump_human_image_check(ImageCheck *check) +{ + if (!(check->corruptions || check->leaks || check->check_errors)) { + printf("No errors were found on the image.\n"); + } else { + if (check->corruptions) { + printf("\n%" PRId64 " errors were found on the image.\n" + "Data may be corrupted, or further writes to the image " + "may corrupt it.\n", + check->corruptions); + } + + if (check->leaks) { + printf("\n%" PRId64 " leaked clusters were found on the image.\n" + "This means waste of disk space, but no harm to data.\n", + check->leaks); + } + + if (check->check_errors) { + printf("\n%" PRId64 " internal errors have occurred during the check.\n", + check->check_errors); + } + } + + if (check->total_clusters != 0 && check->allocated_clusters != 0) { + printf("%" PRId64 "/%" PRId64 "= %0.2f%% allocated, %0.2f%% fragmented\n", + check->allocated_clusters, check->total_clusters, + check->allocated_clusters * 100.0 / check->total_clusters, + check->fragmented_clusters * 100.0 / check->allocated_clusters); + } + + if (check->image_end_offset) { + printf("Image end offset: %" PRId64 "\n", check->image_end_offset); + } +} + +static int collect_image_check(BlockDriverState *bs, + ImageCheck *check, + const char *filename, + const char *fmt, + int fix) +{ + int ret; + BdrvCheckResult result; + + ret = bdrv_check(bs, &result, fix); + if (ret < 0) { + return ret; + } + + check->filename = g_strdup(filename); + check->format = g_strdup(bdrv_get_format_name(bs)); + check->check_errors = result.check_errors; + check->corruptions = result.corruptions; + check->has_corruptions = result.corruptions != 0; + check->leaks = result.leaks; + check->has_leaks = result.leaks != 0; + check->corruptions_fixed = result.corruptions_fixed; + check->has_corruptions_fixed = result.corruptions != 0; + check->leaks_fixed = result.leaks_fixed; + check->has_leaks_fixed = result.leaks != 0; + check->image_end_offset = result.image_end_offset; + check->has_image_end_offset = result.image_end_offset != 0; + check->total_clusters = result.bfi.total_clusters; + check->has_total_clusters = result.bfi.total_clusters != 0; + check->allocated_clusters = result.bfi.allocated_clusters; + check->has_allocated_clusters = result.bfi.allocated_clusters != 0; + check->fragmented_clusters = result.bfi.fragmented_clusters; + check->has_fragmented_clusters = result.bfi.fragmented_clusters != 0; + + return 0; +} + /* * Checks an image for consistency. Exit codes: * @@ -393,15 +497,26 @@ out: static int img_check(int argc, char **argv) { int c, ret; - const char *filename, *fmt; + OutputFormat output_format = OFORMAT_HUMAN; + const char *filename, *fmt, *output; BlockDriverState *bs; - BdrvCheckResult result; int fix = 0; int flags = BDRV_O_FLAGS | BDRV_O_CHECK; + ImageCheck *check; fmt = NULL; + output = NULL; for(;;) { - c = getopt(argc, argv, "f:hr:"); + int option_index = 0; + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"format", required_argument, 0, 'f'}, + {"repair", no_argument, 0, 'r'}, + {"output", required_argument, 0, OPTION_OUTPUT}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "f:hr:", + long_options, &option_index); if (c == -1) { break; } @@ -424,6 +539,9 @@ static int img_check(int argc, char **argv) help(); } break; + case OPTION_OUTPUT: + output = optarg; + break; } } if (optind >= argc) { @@ -431,77 +549,79 @@ static int img_check(int argc, char **argv) } filename = argv[optind++]; + if (output && !strcmp(output, "json")) { + output_format = OFORMAT_JSON; + } else if (output && !strcmp(output, "human")) { + output_format = OFORMAT_HUMAN; + } else if (output) { + error_report("--output must be used with human or json as argument."); + return 1; + } + bs = bdrv_new_open(filename, fmt, flags, true); if (!bs) { return 1; } - ret = bdrv_check(bs, &result, fix); + + check = g_new0(ImageCheck, 1); + ret = collect_image_check(bs, check, filename, fmt, fix); if (ret == -ENOTSUP) { - bdrv_delete(bs); - error_report("This image format does not support checks"); - return 1; + if (output_format == OFORMAT_HUMAN) { + error_report("This image format does not support checks"); + } + ret = 1; + goto fail; } - if (result.corruptions_fixed || result.leaks_fixed) { - printf("The following inconsistencies were found and repaired:\n\n" - " %d leaked clusters\n" - " %d corruptions\n\n" - "Double checking the fixed image now...\n", - result.leaks_fixed, - result.corruptions_fixed); - ret = bdrv_check(bs, &result, 0); - } + if (check->corruptions_fixed || check->leaks_fixed) { + int corruptions_fixed, leaks_fixed; - if (!(result.corruptions || result.leaks || result.check_errors)) { - printf("No errors were found on the image.\n"); - } else { - if (result.corruptions) { - printf("\n%d errors were found on the image.\n" - "Data may be corrupted, or further writes to the image " - "may corrupt it.\n", - result.corruptions); - } + leaks_fixed = check->leaks_fixed; + corruptions_fixed = check->corruptions_fixed; - if (result.leaks) { - printf("\n%d leaked clusters were found on the image.\n" - "This means waste of disk space, but no harm to data.\n", - result.leaks); + if (output_format == OFORMAT_HUMAN) { + printf("The following inconsistencies were found and repaired:\n\n" + " %" PRId64 " leaked clusters\n" + " %" PRId64 " corruptions\n\n" + "Double checking the fixed image now...\n", + check->leaks_fixed, + check->corruptions_fixed); } - if (result.check_errors) { - printf("\n%d internal errors have occurred during the check.\n", - result.check_errors); - } - } + ret = collect_image_check(bs, check, filename, fmt, 0); - if (result.bfi.total_clusters != 0 && result.bfi.allocated_clusters != 0) { - printf("%" PRId64 "/%" PRId64 "= %0.2f%% allocated, %0.2f%% fragmented\n", - result.bfi.allocated_clusters, result.bfi.total_clusters, - result.bfi.allocated_clusters * 100.0 / result.bfi.total_clusters, - result.bfi.fragmented_clusters * 100.0 / result.bfi.allocated_clusters); + check->leaks_fixed = leaks_fixed; + check->corruptions_fixed = corruptions_fixed; } - if (result.image_end_offset > 0) { - printf("Image end offset: %" PRId64 "\n", result.image_end_offset); + switch (output_format) { + case OFORMAT_HUMAN: + dump_human_image_check(check); + break; + case OFORMAT_JSON: + dump_json_image_check(check); + break; } - bdrv_delete(bs); - - if (ret < 0 || result.check_errors) { - printf("\nAn error has occurred during the check: %s\n" - "The check is not complete and may have missed error.\n", - strerror(-ret)); - return 1; + if (ret || check->check_errors) { + ret = 1; + goto fail; } - if (result.corruptions) { - return 2; - } else if (result.leaks) { - return 3; + if (check->corruptions) { + ret = 2; + } else if (check->leaks) { + ret = 3; } else { - return 0; + ret = 0; } + +fail: + qapi_free_ImageCheck(check); + bdrv_delete(bs); + + return ret; } static int img_commit(int argc, char **argv) diff --git a/qemu-img.texi b/qemu-img.texi index 20ac0eb..d1340f1 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -70,9 +70,10 @@ lists all snapshots in the given image Command description: @table @option -@item check [-f @var{fmt}] [-r [leaks | all]] @var{filename} +@item check [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] @var{filename} -Perform a consistency check on the disk image @var{filename}. +Perform a consistency check on the disk image @var{filename}. The command can +output in the format @var{ofmt} which is either @code{human} or @code{json}. If @code{-r} is specified, qemu-img tries to repair any inconsistencies found during the check. @code{-r leaks} repairs only cluster leaks, whereas -- 1.7.11.7