Commit 8599ea4c authored by Federico Simoncelli's avatar Federico Simoncelli Committed by Kevin Wolf

qemu-img: add json output option to the check command

This option --output=[human|json] makes qemu-img check output a human
or JSON representation at the choice of the user.
Signed-off-by: default avatarFederico Simoncelli <fsimonce@redhat.com>
Reviewed-by: default avatarEric Blake <eblake@redhat.com>
Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
parent c6bb9ad1
......@@ -244,6 +244,52 @@
'*backing-filename': 'str', '*full-backing-filename': 'str',
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'] } }
##
# @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' } }
##
# @StatusInfo:
#
......
......@@ -10,9 +10,9 @@ STEXI
ETEXI
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,
......
......@@ -42,6 +42,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"
......@@ -375,6 +385,96 @@ static int img_create(int argc, char **argv)
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_pretty(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:
*
......@@ -386,15 +486,26 @@ static int img_create(int argc, char **argv)
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;
}
......@@ -417,6 +528,9 @@ static int img_check(int argc, char **argv)
help();
}
break;
case OPTION_OUTPUT:
output = optarg;
break;
}
}
if (optind >= argc) {
......@@ -424,77 +538,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) {
error_report("This image format does not support checks");
bdrv_delete(bs);
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)
......@@ -1396,16 +1512,6 @@ err:
return NULL;
}
enum {
OPTION_OUTPUT = 256,
OPTION_BACKING_CHAIN = 257,
};
typedef enum OutputFormat {
OFORMAT_JSON,
OFORMAT_HUMAN,
} OutputFormat;
static int img_info(int argc, char **argv)
{
int c;
......
......@@ -84,9 +84,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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment