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

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
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -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:
#
+2 −2
Original line number Diff line number Diff line
@@ -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,
+169 −63
Original line number Diff line number Diff line
@@ -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) {
        if (output_format == OFORMAT_HUMAN) {
            error_report("This image format does not support checks");
        bdrv_delete(bs);
        return 1;
        }
        ret = 1;
        goto fail;
    }

    if (check->corruptions_fixed || check->leaks_fixed) {
        int corruptions_fixed, leaks_fixed;

    if (result.corruptions_fixed || result.leaks_fixed) {
        leaks_fixed         = check->leaks_fixed;
        corruptions_fixed   = check->corruptions_fixed;

        if (output_format == OFORMAT_HUMAN) {
            printf("The following inconsistencies were found and repaired:\n\n"
               "    %d leaked clusters\n"
               "    %d corruptions\n\n"
                   "    %" PRId64 " leaked clusters\n"
                   "    %" PRId64 " corruptions\n\n"
                   "Double checking the fixed image now...\n",
               result.leaks_fixed,
               result.corruptions_fixed);
        ret = bdrv_check(bs, &result, 0);
                   check->leaks_fixed,
                   check->corruptions_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);
        }
        ret = collect_image_check(bs, check, filename, fmt, 0);

        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);
        check->leaks_fixed          = leaks_fixed;
        check->corruptions_fixed    = corruptions_fixed;
    }

        if (result.check_errors) {
            printf("\n%d internal errors have occurred during the check.\n",
                result.check_errors);
        }
    switch (output_format) {
    case OFORMAT_HUMAN:
        dump_human_image_check(check);
        break;
    case OFORMAT_JSON:
        dump_json_image_check(check);
        break;
    }

    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);
    if (ret || check->check_errors) {
        ret = 1;
        goto fail;
    }

    if (result.image_end_offset > 0) {
        printf("Image end offset: %" PRId64 "\n", result.image_end_offset);
    if (check->corruptions) {
        ret = 2;
    } else if (check->leaks) {
        ret = 3;
    } else {
        ret = 0;
    }

fail:
    qapi_free_ImageCheck(check);
    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 (result.corruptions) {
        return 2;
    } else if (result.leaks) {
        return 3;
    } else {
        return 0;
    }
    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;
+3 −2
Original line number Diff line number Diff line
@@ -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