mirror of https://github.com/xemu-project/xemu.git
qemu-img: Let info print block graph
For every node in the backing chain, collect its BlockGraphInfo struct using bdrv_query_block_graph_info(). Print all nodes' information, indenting child nodes and labelling them with a path constructed from the child names leading to the node from the root (e.g. /file/file). Note that we open each image with BDRV_O_NO_BACKING, so its backing child is omitted from this graph, and thus presented in the previous manner: By simply concatenating all images' information, separated with blank lines. This affects two iotests: - 065: Here we try to get the format node's format specific information. The pre-patch code does so by taking all lines from "Format specific information:" until an empty line. This format specific information is no longer followed by an empty line, though, but by child node information, so limit the range by "Child node '/file':". - 302: Calls qemu_img() for qemu-img info directly, which does not filter the output, so the child node information ends up in the output. Signed-off-by: Hanna Reitz <hreitz@redhat.com> Message-Id: <20220620162704.80987-12-hreitz@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
74163adda3
commit
c04d0ab026
|
@ -5831,9 +5831,9 @@
|
||||||
##
|
##
|
||||||
# @DummyBlockCoreForceArrays:
|
# @DummyBlockCoreForceArrays:
|
||||||
#
|
#
|
||||||
# Not used by QMP; hack to let us use BlockNodeInfoList internally
|
# Not used by QMP; hack to let us use BlockGraphInfoList internally
|
||||||
#
|
#
|
||||||
# Since: 8.0
|
# Since: 8.0
|
||||||
##
|
##
|
||||||
{ 'struct': 'DummyBlockCoreForceArrays',
|
{ 'struct': 'DummyBlockCoreForceArrays',
|
||||||
'data': { 'unused-block-node-info': ['BlockNodeInfo'] } }
|
'data': { 'unused-block-graph-info': ['BlockGraphInfo'] } }
|
||||||
|
|
63
qemu-img.c
63
qemu-img.c
|
@ -2817,13 +2817,13 @@ static void dump_snapshots(BlockDriverState *bs)
|
||||||
g_free(sn_tab);
|
g_free(sn_tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_json_block_node_info_list(BlockNodeInfoList *list)
|
static void dump_json_block_graph_info_list(BlockGraphInfoList *list)
|
||||||
{
|
{
|
||||||
GString *str;
|
GString *str;
|
||||||
QObject *obj;
|
QObject *obj;
|
||||||
Visitor *v = qobject_output_visitor_new(&obj);
|
Visitor *v = qobject_output_visitor_new(&obj);
|
||||||
|
|
||||||
visit_type_BlockNodeInfoList(v, NULL, &list, &error_abort);
|
visit_type_BlockGraphInfoList(v, NULL, &list, &error_abort);
|
||||||
visit_complete(v, &obj);
|
visit_complete(v, &obj);
|
||||||
str = qobject_to_json_pretty(obj, true);
|
str = qobject_to_json_pretty(obj, true);
|
||||||
assert(str != NULL);
|
assert(str != NULL);
|
||||||
|
@ -2833,13 +2833,13 @@ static void dump_json_block_node_info_list(BlockNodeInfoList *list)
|
||||||
g_string_free(str, true);
|
g_string_free(str, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_json_block_node_info(BlockNodeInfo *info)
|
static void dump_json_block_graph_info(BlockGraphInfo *info)
|
||||||
{
|
{
|
||||||
GString *str;
|
GString *str;
|
||||||
QObject *obj;
|
QObject *obj;
|
||||||
Visitor *v = qobject_output_visitor_new(&obj);
|
Visitor *v = qobject_output_visitor_new(&obj);
|
||||||
|
|
||||||
visit_type_BlockNodeInfo(v, NULL, &info, &error_abort);
|
visit_type_BlockGraphInfo(v, NULL, &info, &error_abort);
|
||||||
visit_complete(v, &obj);
|
visit_complete(v, &obj);
|
||||||
str = qobject_to_json_pretty(obj, true);
|
str = qobject_to_json_pretty(obj, true);
|
||||||
assert(str != NULL);
|
assert(str != NULL);
|
||||||
|
@ -2849,9 +2849,29 @@ static void dump_json_block_node_info(BlockNodeInfo *info)
|
||||||
g_string_free(str, true);
|
g_string_free(str, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_human_image_info_list(BlockNodeInfoList *list)
|
static void dump_human_image_info(BlockGraphInfo *info, int indentation,
|
||||||
|
const char *path)
|
||||||
{
|
{
|
||||||
BlockNodeInfoList *elem;
|
BlockChildInfoList *children_list;
|
||||||
|
|
||||||
|
bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation);
|
||||||
|
|
||||||
|
for (children_list = info->children; children_list;
|
||||||
|
children_list = children_list->next)
|
||||||
|
{
|
||||||
|
BlockChildInfo *child = children_list->value;
|
||||||
|
g_autofree char *child_path = NULL;
|
||||||
|
|
||||||
|
printf("%*sChild node '%s%s':\n",
|
||||||
|
indentation * 4, "", path, child->name);
|
||||||
|
child_path = g_strdup_printf("%s%s/", path, child->name);
|
||||||
|
dump_human_image_info(child->info, indentation + 1, child_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_human_image_info_list(BlockGraphInfoList *list)
|
||||||
|
{
|
||||||
|
BlockGraphInfoList *elem;
|
||||||
bool delim = false;
|
bool delim = false;
|
||||||
|
|
||||||
for (elem = list; elem; elem = elem->next) {
|
for (elem = list; elem; elem = elem->next) {
|
||||||
|
@ -2860,7 +2880,7 @@ static void dump_human_image_info_list(BlockNodeInfoList *list)
|
||||||
}
|
}
|
||||||
delim = true;
|
delim = true;
|
||||||
|
|
||||||
bdrv_node_info_dump(elem->value, 0);
|
dump_human_image_info(elem->value, 0, "/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2870,7 +2890,7 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open an image file chain and return an BlockNodeInfoList
|
* Open an image file chain and return an BlockGraphInfoList
|
||||||
*
|
*
|
||||||
* @filename: topmost image filename
|
* @filename: topmost image filename
|
||||||
* @fmt: topmost image format (may be NULL to autodetect)
|
* @fmt: topmost image format (may be NULL to autodetect)
|
||||||
|
@ -2881,13 +2901,13 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
|
||||||
* opening an image file. If there was an error a message will have been
|
* opening an image file. If there was an error a message will have been
|
||||||
* printed to stderr.
|
* printed to stderr.
|
||||||
*/
|
*/
|
||||||
static BlockNodeInfoList *collect_image_info_list(bool image_opts,
|
static BlockGraphInfoList *collect_image_info_list(bool image_opts,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
const char *fmt,
|
const char *fmt,
|
||||||
bool chain, bool force_share)
|
bool chain, bool force_share)
|
||||||
{
|
{
|
||||||
BlockNodeInfoList *head = NULL;
|
BlockGraphInfoList *head = NULL;
|
||||||
BlockNodeInfoList **tail = &head;
|
BlockGraphInfoList **tail = &head;
|
||||||
GHashTable *filenames;
|
GHashTable *filenames;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
|
@ -2896,7 +2916,7 @@ static BlockNodeInfoList *collect_image_info_list(bool image_opts,
|
||||||
while (filename) {
|
while (filename) {
|
||||||
BlockBackend *blk;
|
BlockBackend *blk;
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BlockNodeInfo *info;
|
BlockGraphInfo *info;
|
||||||
|
|
||||||
if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) {
|
if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) {
|
||||||
error_report("Backing file '%s' creates an infinite loop.",
|
error_report("Backing file '%s' creates an infinite loop.",
|
||||||
|
@ -2913,7 +2933,14 @@ static BlockNodeInfoList *collect_image_info_list(bool image_opts,
|
||||||
}
|
}
|
||||||
bs = blk_bs(blk);
|
bs = blk_bs(blk);
|
||||||
|
|
||||||
bdrv_query_block_node_info(bs, &info, &err);
|
/*
|
||||||
|
* Note that the returned BlockGraphInfo object will not have
|
||||||
|
* information about this image's backing node, because we have opened
|
||||||
|
* it with BDRV_O_NO_BACKING. Printing this object will therefore not
|
||||||
|
* duplicate the backing chain information that we obtain by walking
|
||||||
|
* the chain manually here.
|
||||||
|
*/
|
||||||
|
bdrv_query_block_graph_info(bs, &info, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
error_report_err(err);
|
error_report_err(err);
|
||||||
blk_unref(blk);
|
blk_unref(blk);
|
||||||
|
@ -2946,7 +2973,7 @@ static BlockNodeInfoList *collect_image_info_list(bool image_opts,
|
||||||
return head;
|
return head;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
qapi_free_BlockNodeInfoList(head);
|
qapi_free_BlockGraphInfoList(head);
|
||||||
g_hash_table_destroy(filenames);
|
g_hash_table_destroy(filenames);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -2957,7 +2984,7 @@ static int img_info(int argc, char **argv)
|
||||||
OutputFormat output_format = OFORMAT_HUMAN;
|
OutputFormat output_format = OFORMAT_HUMAN;
|
||||||
bool chain = false;
|
bool chain = false;
|
||||||
const char *filename, *fmt, *output;
|
const char *filename, *fmt, *output;
|
||||||
BlockNodeInfoList *list;
|
BlockGraphInfoList *list;
|
||||||
bool image_opts = false;
|
bool image_opts = false;
|
||||||
bool force_share = false;
|
bool force_share = false;
|
||||||
|
|
||||||
|
@ -3036,14 +3063,14 @@ static int img_info(int argc, char **argv)
|
||||||
break;
|
break;
|
||||||
case OFORMAT_JSON:
|
case OFORMAT_JSON:
|
||||||
if (chain) {
|
if (chain) {
|
||||||
dump_json_block_node_info_list(list);
|
dump_json_block_graph_info_list(list);
|
||||||
} else {
|
} else {
|
||||||
dump_json_block_node_info(list->value);
|
dump_json_block_graph_info(list->value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
qapi_free_BlockNodeInfoList(list);
|
qapi_free_BlockGraphInfoList(list);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ class TestQemuImgInfo(TestImageInfoSpecific):
|
||||||
def test_human(self):
|
def test_human(self):
|
||||||
data = qemu_img('info', '--output=human', test_img).stdout.split('\n')
|
data = qemu_img('info', '--output=human', test_img).stdout.split('\n')
|
||||||
data = data[(data.index('Format specific information:') + 1)
|
data = data[(data.index('Format specific information:') + 1)
|
||||||
:data.index('')]
|
:data.index("Child node '/file':")]
|
||||||
for field in data:
|
for field in data:
|
||||||
self.assertTrue(re.match('^ {4}[^ ]', field) is not None)
|
self.assertTrue(re.match('^ {4}[^ ]', field) is not None)
|
||||||
data = [line.strip() for line in data]
|
data = [line.strip() for line in data]
|
||||||
|
|
|
@ -4,6 +4,11 @@ image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock
|
||||||
file format: raw
|
file format: raw
|
||||||
virtual size: 448 KiB (458752 bytes)
|
virtual size: 448 KiB (458752 bytes)
|
||||||
disk size: unavailable
|
disk size: unavailable
|
||||||
|
Child node '/file':
|
||||||
|
image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock
|
||||||
|
file format: nbd
|
||||||
|
virtual size: 448 KiB (458752 bytes)
|
||||||
|
disk size: unavailable
|
||||||
|
|
||||||
=== Converted image info ===
|
=== Converted image info ===
|
||||||
image: TEST_IMG
|
image: TEST_IMG
|
||||||
|
|
Loading…
Reference in New Issue