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:
parent
c249ee6825
commit
c054b3fd78
3
Makefile
3
Makefile
@ -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
|
iohandler.o cutils.o iov.o async.o
|
||||||
tools-obj-$(CONFIG_POSIX) += compatfd.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-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)
|
qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y)
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@ STEXI
|
|||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
DEF("info", img_info,
|
DEF("info", img_info,
|
||||||
"info [-f fmt] filename")
|
"info [-f fmt] [--output=ofmt] filename")
|
||||||
STEXI
|
STEXI
|
||||||
@item info [-f @var{fmt}] @var{filename}
|
@item info [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
DEF("snapshot", img_snapshot,
|
DEF("snapshot", img_snapshot,
|
||||||
|
236
qemu-img.c
236
qemu-img.c
@ -21,12 +21,16 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#include "qapi-visit.h"
|
||||||
|
#include "qapi/qmp-output-visitor.h"
|
||||||
|
#include "qjson.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu-option.h"
|
#include "qemu-option.h"
|
||||||
#include "qemu-error.h"
|
#include "qemu-error.h"
|
||||||
#include "osdep.h"
|
#include "osdep.h"
|
||||||
#include "sysemu.h"
|
#include "sysemu.h"
|
||||||
#include "block_int.h"
|
#include "block_int.h"
|
||||||
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -84,6 +88,7 @@ static void help(void)
|
|||||||
" '-p' show progress of command (only certain commands)\n"
|
" '-p' show progress of command (only certain commands)\n"
|
||||||
" '-S' indicates the consecutive number of bytes that must contain only zeros\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"
|
" 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"
|
"\n"
|
||||||
"Parameters to check subcommand:\n"
|
"Parameters to check subcommand:\n"
|
||||||
" '-r' tries to repair any inconsistencies that are found during the check.\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);
|
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;
|
uint64_t total_sectors;
|
||||||
int64_t allocated_size;
|
|
||||||
char backing_filename[1024];
|
char backing_filename[1024];
|
||||||
char backing_filename2[1024];
|
char backing_filename2[1024];
|
||||||
BlockDriverInfo bdi;
|
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;
|
fmt = NULL;
|
||||||
|
output = NULL;
|
||||||
for(;;) {
|
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) {
|
if (c == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1128,6 +1286,9 @@ static int img_info(int argc, char **argv)
|
|||||||
case 'f':
|
case 'f':
|
||||||
fmt = optarg;
|
fmt = optarg;
|
||||||
break;
|
break;
|
||||||
|
case OPTION_OUTPUT:
|
||||||
|
output = optarg;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (optind >= argc) {
|
if (optind >= argc) {
|
||||||
@ -1135,48 +1296,35 @@ static int img_info(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
filename = argv[optind++];
|
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);
|
bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_NO_BACKING);
|
||||||
if (!bs) {
|
if (!bs) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
bdrv_get_geometry(bs, &total_sectors);
|
|
||||||
get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
|
info = g_new0(ImageInfo, 1);
|
||||||
allocated_size = bdrv_get_allocated_file_size(bs);
|
collect_image_info(bs, info, filename, fmt);
|
||||||
if (allocated_size < 0) {
|
|
||||||
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
|
switch (output_format) {
|
||||||
} else {
|
case OFORMAT_HUMAN:
|
||||||
get_human_readable_size(dsize_buf, sizeof(dsize_buf),
|
dump_human_image_info(info);
|
||||||
allocated_size);
|
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"
|
qapi_free_ImageInfo(info);
|
||||||
"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);
|
|
||||||
bdrv_delete(bs);
|
bdrv_delete(bs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
@var{backing_file} should have the same content as the input's base image,
|
||||||
however the path, image format, etc may differ.
|
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
|
Give information about the disk image @var{filename}. Use it in
|
||||||
particular to know the size reserved on disk which can be different
|
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,
|
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}
|
@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user