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:
		| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user