qemu-img: Add json output option to the info command.

This option --output=[human|json] make qemu-img info output on
human or JSON representation at the choice of the user.

example:
{
    "snapshots": [
        {
            "vm-clock-nsec": 637102488,
            "name": "vm-20120821145509",
            "date-sec": 1345553709,
            "date-nsec": 220289000,
            "vm-clock-sec": 20,
            "id": "1",
            "vm-state-size": 96522745
        },
        {
            "vm-clock-nsec": 28210866,
            "name": "vm-20120821154059",
            "date-sec": 1345556459,
            "date-nsec": 171392000,
            "vm-clock-sec": 46,
            "id": "2",
            "vm-state-size": 101208714
        }
    ],
    "virtual-size": 1073741824,
    "filename": "snap.qcow2",
    "cluster-size": 65536,
    "format": "qcow2",
    "actual-size": 985587712,
    "dirty-flag": false
}

Signed-off-by: Benoit Canet <benoit@irqsave.net>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Benoît Canet 2012-09-05 13:09:02 +02:00 committed by Kevin Wolf
parent c249ee6825
commit c054b3fd78
4 changed files with 199 additions and 49 deletions

View file

@ -157,7 +157,8 @@ tools-obj-y = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \
iohandler.o cutils.o iov.o async.o
tools-obj-$(CONFIG_POSIX) += compatfd.o
qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y)
qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) $(qapi-obj-y) \
qapi-visit.o qapi-types.o
qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y)
qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y)

View file

@ -34,9 +34,9 @@ STEXI
ETEXI
DEF("info", img_info,
"info [-f fmt] filename")
"info [-f fmt] [--output=ofmt] filename")
STEXI
@item info [-f @var{fmt}] @var{filename}
@item info [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
ETEXI
DEF("snapshot", img_snapshot,

View file

@ -21,12 +21,16 @@
* 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 "sysemu.h"
#include "block_int.h"
#include <getopt.h>
#include <stdio.h>
#ifdef _WIN32
@ -84,6 +88,7 @@ static void help(void)
" '-p' show progress of command (only certain commands)\n"
" '-S' indicates the consecutive number of bytes that must contain only zeros\n"
" for qemu-img to create a sparse image during conversion\n"
" '--output' takes the format in which the output must be done (human or json)\n"
"\n"
"Parameters to check subcommand:\n"
" '-r' tries to repair any inconsistencies that are found during the check.\n"
@ -1102,21 +1107,174 @@ static void dump_snapshots(BlockDriverState *bs)
g_free(sn_tab);
}
static int img_info(int argc, char **argv)
static void collect_snapshots(BlockDriverState *bs , ImageInfo *info)
{
int i, sn_count;
QEMUSnapshotInfo *sn_tab = NULL;
SnapshotInfoList *info_list, *cur_item = NULL;
sn_count = bdrv_snapshot_list(bs, &sn_tab);
for (i = 0; i < sn_count; i++) {
info->has_snapshots = true;
info_list = g_new0(SnapshotInfoList, 1);
info_list->value = g_new0(SnapshotInfo, 1);
info_list->value->id = g_strdup(sn_tab[i].id_str);
info_list->value->name = g_strdup(sn_tab[i].name);
info_list->value->vm_state_size = sn_tab[i].vm_state_size;
info_list->value->date_sec = sn_tab[i].date_sec;
info_list->value->date_nsec = sn_tab[i].date_nsec;
info_list->value->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
info_list->value->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
/* XXX: waiting for the qapi to support qemu-queue.h types */
if (!cur_item) {
info->snapshots = cur_item = info_list;
} else {
cur_item->next = info_list;
cur_item = info_list;
}
}
g_free(sn_tab);
}
static void dump_json_image_info(ImageInfo *info)
{
Error *errp = NULL;
QString *str;
QmpOutputVisitor *ov = qmp_output_visitor_new();
QObject *obj;
visit_type_ImageInfo(qmp_output_get_visitor(ov),
&info, 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 collect_image_info(BlockDriverState *bs,
ImageInfo *info,
const char *filename,
const char *fmt)
{
int c;
const char *filename, *fmt;
BlockDriverState *bs;
char size_buf[128], dsize_buf[128];
uint64_t total_sectors;
int64_t allocated_size;
char backing_filename[1024];
char backing_filename2[1024];
BlockDriverInfo bdi;
bdrv_get_geometry(bs, &total_sectors);
info->filename = g_strdup(filename);
info->format = g_strdup(bdrv_get_format_name(bs));
info->virtual_size = total_sectors * 512;
info->actual_size = bdrv_get_allocated_file_size(bs);
info->has_actual_size = info->actual_size >= 0;
if (bdrv_is_encrypted(bs)) {
info->encrypted = true;
info->has_encrypted = true;
}
if (bdrv_get_info(bs, &bdi) >= 0) {
if (bdi.cluster_size != 0) {
info->cluster_size = bdi.cluster_size;
info->has_cluster_size = true;
}
info->dirty_flag = bdi.is_dirty;
info->has_dirty_flag = true;
}
bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
if (backing_filename[0] != '\0') {
info->backing_filename = g_strdup(backing_filename);
info->has_backing_filename = true;
bdrv_get_full_backing_filename(bs, backing_filename2,
sizeof(backing_filename2));
if (strcmp(backing_filename, backing_filename2) != 0) {
info->full_backing_filename =
g_strdup(backing_filename2);
info->has_full_backing_filename = true;
}
if (bs->backing_format[0]) {
info->backing_filename_format = g_strdup(bs->backing_format);
info->has_backing_filename_format = true;
}
}
}
static void dump_human_image_info(ImageInfo *info)
{
char size_buf[128], dsize_buf[128];
if (!info->has_actual_size) {
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
} else {
get_human_readable_size(dsize_buf, sizeof(dsize_buf),
info->actual_size);
}
get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size);
printf("image: %s\n"
"file format: %s\n"
"virtual size: %s (%" PRId64 " bytes)\n"
"disk size: %s\n",
info->filename, info->format, size_buf,
info->virtual_size,
dsize_buf);
if (info->has_encrypted && info->encrypted) {
printf("encrypted: yes\n");
}
if (info->has_cluster_size) {
printf("cluster_size: %" PRId64 "\n", info->cluster_size);
}
if (info->has_dirty_flag && info->dirty_flag) {
printf("cleanly shut down: no\n");
}
if (info->has_backing_filename) {
printf("backing file: %s", info->backing_filename);
if (info->has_full_backing_filename) {
printf(" (actual path: %s)", info->full_backing_filename);
}
putchar('\n');
if (info->has_backing_filename_format) {
printf("backing file format: %s\n", info->backing_filename_format);
}
}
}
enum {OPTION_OUTPUT = 256};
typedef enum OutputFormat {
OFORMAT_JSON,
OFORMAT_HUMAN,
} OutputFormat;
static int img_info(int argc, char **argv)
{
int c;
OutputFormat output_format = OFORMAT_HUMAN;
const char *filename, *fmt, *output;
BlockDriverState *bs;
ImageInfo *info;
fmt = NULL;
output = NULL;
for(;;) {
c = getopt(argc, argv, "f:h");
int option_index = 0;
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"format", required_argument, 0, 'f'},
{"output", required_argument, 0, OPTION_OUTPUT},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "f:h",
long_options, &option_index);
if (c == -1) {
break;
}
@ -1128,6 +1286,9 @@ static int img_info(int argc, char **argv)
case 'f':
fmt = optarg;
break;
case OPTION_OUTPUT:
output = optarg;
break;
}
}
if (optind >= argc) {
@ -1135,48 +1296,35 @@ static int img_info(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, BDRV_O_FLAGS | BDRV_O_NO_BACKING);
if (!bs) {
return 1;
}
bdrv_get_geometry(bs, &total_sectors);
get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
allocated_size = bdrv_get_allocated_file_size(bs);
if (allocated_size < 0) {
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
} else {
get_human_readable_size(dsize_buf, sizeof(dsize_buf),
allocated_size);
info = g_new0(ImageInfo, 1);
collect_image_info(bs, info, filename, fmt);
switch (output_format) {
case OFORMAT_HUMAN:
dump_human_image_info(info);
dump_snapshots(bs);
break;
case OFORMAT_JSON:
collect_snapshots(bs, info);
dump_json_image_info(info);
break;
}
printf("image: %s\n"
"file format: %s\n"
"virtual size: %s (%" PRId64 " bytes)\n"
"disk size: %s\n",
filename, bdrv_get_format_name(bs), size_buf,
(total_sectors * 512),
dsize_buf);
if (bdrv_is_encrypted(bs)) {
printf("encrypted: yes\n");
}
if (bdrv_get_info(bs, &bdi) >= 0) {
if (bdi.cluster_size != 0) {
printf("cluster_size: %d\n", bdi.cluster_size);
}
if (bdi.is_dirty) {
printf("cleanly shut down: no\n");
}
}
bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
if (backing_filename[0] != '\0') {
bdrv_get_full_backing_filename(bs, backing_filename2,
sizeof(backing_filename2));
printf("backing file: %s", backing_filename);
if (strcmp(backing_filename, backing_filename2) != 0) {
printf(" (actual path: %s)", backing_filename2);
}
putchar('\n');
}
dump_snapshots(bs);
qapi_free_ImageInfo(info);
bdrv_delete(bs);
return 0;
}

View file

@ -129,12 +129,13 @@ created as a copy on write image of the specified base image; the
@var{backing_file} should have the same content as the input's base image,
however the path, image format, etc may differ.
@item info [-f @var{fmt}] @var{filename}
@item info [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
Give information about the disk image @var{filename}. Use it in
particular to know the size reserved on disk which can be different
from the displayed size. If VM snapshots are stored in the disk image,
they are displayed too.
they are displayed too. The command can output in the format @var{ofmt}
which is either @code{human} or @code{json}.
@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename}