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
|
||||
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)
|
||||
|
||||
|
@ -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,
|
||||
|
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
|
||||
* 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);
|
||||
}
|
||||
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');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
qapi_free_ImageInfo(info);
|
||||
bdrv_delete(bs);
|
||||
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,
|
||||
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}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user