ed5979
From ea73e9de42b446ce1049805c23f7706e4f87ed1f Mon Sep 17 00:00:00 2001
ed5979
From: Hanna Reitz <hreitz@redhat.com>
ed5979
Date: Mon, 20 Jun 2022 18:27:03 +0200
ed5979
Subject: [PATCH 16/20] qemu-img: Let info print block graph
ed5979
ed5979
RH-Author: Hanna Czenczek <hreitz@redhat.com>
ed5979
RH-MergeRequest: 145: Show protocol-level information in qemu-img info
ed5979
RH-Bugzilla: 1860292
ed5979
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
ed5979
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
ed5979
RH-Acked-by: Stefano Garzarella <sgarzare@redhat.com>
ed5979
RH-Commit: [11/12] 2c1b8a03c918484449e876acf4c6663766848ad8 (hreitz/qemu-kvm-c-9-s)
ed5979
ed5979
For every node in the backing chain, collect its BlockGraphInfo struct
ed5979
using bdrv_query_block_graph_info().  Print all nodes' information,
ed5979
indenting child nodes and labelling them with a path constructed from
ed5979
the child names leading to the node from the root (e.g. /file/file).
ed5979
ed5979
Note that we open each image with BDRV_O_NO_BACKING, so its backing
ed5979
child is omitted from this graph, and thus presented in the previous
ed5979
manner: By simply concatenating all images' information, separated with
ed5979
blank lines.
ed5979
ed5979
This affects two iotests:
ed5979
- 065: Here we try to get the format node's format specific information.
ed5979
  The pre-patch code does so by taking all lines from "Format specific
ed5979
  information:" until an empty line.  This format specific information
ed5979
  is no longer followed by an empty line, though, but by child node
ed5979
  information, so limit the range by "Child node '/file':".
ed5979
- 302: Calls qemu_img() for qemu-img info directly, which does not
ed5979
  filter the output, so the child node information ends up in the
ed5979
  output.
ed5979
ed5979
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
ed5979
Message-Id: <20220620162704.80987-12-hreitz@redhat.com>
ed5979
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
ed5979
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
ed5979
(cherry picked from commit c04d0ab026201d21873a63f768cb69c4554dfec1)
ed5979
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
ed5979
---
ed5979
 qapi/block-core.json       |  4 +--
ed5979
 qemu-img.c                 | 69 ++++++++++++++++++++++++++------------
ed5979
 tests/qemu-iotests/065     |  2 +-
ed5979
 tests/qemu-iotests/302.out |  5 +++
ed5979
 4 files changed, 56 insertions(+), 24 deletions(-)
ed5979
ed5979
diff --git a/qapi/block-core.json b/qapi/block-core.json
ed5979
index d703e0fb16..7f331eb8ea 100644
ed5979
--- a/qapi/block-core.json
ed5979
+++ b/qapi/block-core.json
ed5979
@@ -5831,9 +5831,9 @@
ed5979
 ##
ed5979
 # @DummyBlockCoreForceArrays:
ed5979
 #
ed5979
-# Not used by QMP; hack to let us use BlockNodeInfoList internally
ed5979
+# Not used by QMP; hack to let us use BlockGraphInfoList internally
ed5979
 #
ed5979
 # Since: 8.0
ed5979
 ##
ed5979
 { 'struct': 'DummyBlockCoreForceArrays',
ed5979
-  'data': { 'unused-block-node-info': ['BlockNodeInfo'] } }
ed5979
+  'data': { 'unused-block-graph-info': ['BlockGraphInfo'] } }
ed5979
diff --git a/qemu-img.c b/qemu-img.c
ed5979
index 30b4ea58bb..e281011245 100644
ed5979
--- a/qemu-img.c
ed5979
+++ b/qemu-img.c
ed5979
@@ -2816,13 +2816,13 @@ static void dump_snapshots(BlockDriverState *bs)
ed5979
     g_free(sn_tab);
ed5979
 }
ed5979
 
ed5979
-static void dump_json_block_node_info_list(BlockNodeInfoList *list)
ed5979
+static void dump_json_block_graph_info_list(BlockGraphInfoList *list)
ed5979
 {
ed5979
     GString *str;
ed5979
     QObject *obj;
ed5979
     Visitor *v = qobject_output_visitor_new(&obj);
ed5979
 
ed5979
-    visit_type_BlockNodeInfoList(v, NULL, &list, &error_abort);
ed5979
+    visit_type_BlockGraphInfoList(v, NULL, &list, &error_abort);
ed5979
     visit_complete(v, &obj);
ed5979
     str = qobject_to_json_pretty(obj, true);
ed5979
     assert(str != NULL);
ed5979
@@ -2832,13 +2832,13 @@ static void dump_json_block_node_info_list(BlockNodeInfoList *list)
ed5979
     g_string_free(str, true);
ed5979
 }
ed5979
 
ed5979
-static void dump_json_block_node_info(BlockNodeInfo *info)
ed5979
+static void dump_json_block_graph_info(BlockGraphInfo *info)
ed5979
 {
ed5979
     GString *str;
ed5979
     QObject *obj;
ed5979
     Visitor *v = qobject_output_visitor_new(&obj);
ed5979
 
ed5979
-    visit_type_BlockNodeInfo(v, NULL, &info, &error_abort);
ed5979
+    visit_type_BlockGraphInfo(v, NULL, &info, &error_abort);
ed5979
     visit_complete(v, &obj);
ed5979
     str = qobject_to_json_pretty(obj, true);
ed5979
     assert(str != NULL);
ed5979
@@ -2848,9 +2848,29 @@ static void dump_json_block_node_info(BlockNodeInfo *info)
ed5979
     g_string_free(str, true);
ed5979
 }
ed5979
 
ed5979
-static void dump_human_image_info_list(BlockNodeInfoList *list)
ed5979
+static void dump_human_image_info(BlockGraphInfo *info, int indentation,
ed5979
+                                  const char *path)
ed5979
 {
ed5979
-    BlockNodeInfoList *elem;
ed5979
+    BlockChildInfoList *children_list;
ed5979
+
ed5979
+    bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation);
ed5979
+
ed5979
+    for (children_list = info->children; children_list;
ed5979
+         children_list = children_list->next)
ed5979
+    {
ed5979
+        BlockChildInfo *child = children_list->value;
ed5979
+        g_autofree char *child_path = NULL;
ed5979
+
ed5979
+        printf("%*sChild node '%s%s':\n",
ed5979
+               indentation * 4, "", path, child->name);
ed5979
+        child_path = g_strdup_printf("%s%s/", path, child->name);
ed5979
+        dump_human_image_info(child->info, indentation + 1, child_path);
ed5979
+    }
ed5979
+}
ed5979
+
ed5979
+static void dump_human_image_info_list(BlockGraphInfoList *list)
ed5979
+{
ed5979
+    BlockGraphInfoList *elem;
ed5979
     bool delim = false;
ed5979
 
ed5979
     for (elem = list; elem; elem = elem->next) {
ed5979
@@ -2859,7 +2879,7 @@ static void dump_human_image_info_list(BlockNodeInfoList *list)
ed5979
         }
ed5979
         delim = true;
ed5979
 
ed5979
-        bdrv_node_info_dump(elem->value, 0);
ed5979
+        dump_human_image_info(elem->value, 0, "/");
ed5979
     }
ed5979
 }
ed5979
 
ed5979
@@ -2869,7 +2889,7 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
ed5979
 }
ed5979
 
ed5979
 /**
ed5979
- * Open an image file chain and return an BlockNodeInfoList
ed5979
+ * Open an image file chain and return an BlockGraphInfoList
ed5979
  *
ed5979
  * @filename: topmost image filename
ed5979
  * @fmt: topmost image format (may be NULL to autodetect)
ed5979
@@ -2880,13 +2900,13 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
ed5979
  * opening an image file.  If there was an error a message will have been
ed5979
  * printed to stderr.
ed5979
  */
ed5979
-static BlockNodeInfoList *collect_image_info_list(bool image_opts,
ed5979
-                                                  const char *filename,
ed5979
-                                                  const char *fmt,
ed5979
-                                                  bool chain, bool force_share)
ed5979
+static BlockGraphInfoList *collect_image_info_list(bool image_opts,
ed5979
+                                                   const char *filename,
ed5979
+                                                   const char *fmt,
ed5979
+                                                   bool chain, bool force_share)
ed5979
 {
ed5979
-    BlockNodeInfoList *head = NULL;
ed5979
-    BlockNodeInfoList **tail = &head;
ed5979
+    BlockGraphInfoList *head = NULL;
ed5979
+    BlockGraphInfoList **tail = &head;
ed5979
     GHashTable *filenames;
ed5979
     Error *err = NULL;
ed5979
 
ed5979
@@ -2895,7 +2915,7 @@ static BlockNodeInfoList *collect_image_info_list(bool image_opts,
ed5979
     while (filename) {
ed5979
         BlockBackend *blk;
ed5979
         BlockDriverState *bs;
ed5979
-        BlockNodeInfo *info;
ed5979
+        BlockGraphInfo *info;
ed5979
 
ed5979
         if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) {
ed5979
             error_report("Backing file '%s' creates an infinite loop.",
ed5979
@@ -2912,7 +2932,14 @@ static BlockNodeInfoList *collect_image_info_list(bool image_opts,
ed5979
         }
ed5979
         bs = blk_bs(blk);
ed5979
 
ed5979
-        bdrv_query_block_node_info(bs, &info, &err;;
ed5979
+        /*
ed5979
+         * Note that the returned BlockGraphInfo object will not have
ed5979
+         * information about this image's backing node, because we have opened
ed5979
+         * it with BDRV_O_NO_BACKING.  Printing this object will therefore not
ed5979
+         * duplicate the backing chain information that we obtain by walking
ed5979
+         * the chain manually here.
ed5979
+         */
ed5979
+        bdrv_query_block_graph_info(bs, &info, &err;;
ed5979
         if (err) {
ed5979
             error_report_err(err);
ed5979
             blk_unref(blk);
ed5979
@@ -2945,7 +2972,7 @@ static BlockNodeInfoList *collect_image_info_list(bool image_opts,
ed5979
     return head;
ed5979
 
ed5979
 err:
ed5979
-    qapi_free_BlockNodeInfoList(head);
ed5979
+    qapi_free_BlockGraphInfoList(head);
ed5979
     g_hash_table_destroy(filenames);
ed5979
     return NULL;
ed5979
 }
ed5979
@@ -2956,7 +2983,7 @@ static int img_info(int argc, char **argv)
ed5979
     OutputFormat output_format = OFORMAT_HUMAN;
ed5979
     bool chain = false;
ed5979
     const char *filename, *fmt, *output;
ed5979
-    BlockNodeInfoList *list;
ed5979
+    BlockGraphInfoList *list;
ed5979
     bool image_opts = false;
ed5979
     bool force_share = false;
ed5979
 
ed5979
@@ -3035,14 +3062,14 @@ static int img_info(int argc, char **argv)
ed5979
         break;
ed5979
     case OFORMAT_JSON:
ed5979
         if (chain) {
ed5979
-            dump_json_block_node_info_list(list);
ed5979
+            dump_json_block_graph_info_list(list);
ed5979
         } else {
ed5979
-            dump_json_block_node_info(list->value);
ed5979
+            dump_json_block_graph_info(list->value);
ed5979
         }
ed5979
         break;
ed5979
     }
ed5979
 
ed5979
-    qapi_free_BlockNodeInfoList(list);
ed5979
+    qapi_free_BlockGraphInfoList(list);
ed5979
     return 0;
ed5979
 }
ed5979
 
ed5979
diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065
ed5979
index b724c89c7c..b76701c71e 100755
ed5979
--- a/tests/qemu-iotests/065
ed5979
+++ b/tests/qemu-iotests/065
ed5979
@@ -56,7 +56,7 @@ class TestQemuImgInfo(TestImageInfoSpecific):
ed5979
     def test_human(self):
ed5979
         data = qemu_img('info', '--output=human', test_img).stdout.split('\n')
ed5979
         data = data[(data.index('Format specific information:') + 1)
ed5979
-                    :data.index('')]
ed5979
+                    :data.index("Child node '/file':")]
ed5979
         for field in data:
ed5979
             self.assertTrue(re.match('^ {4}[^ ]', field) is not None)
ed5979
         data = [line.strip() for line in data]
ed5979
diff --git a/tests/qemu-iotests/302.out b/tests/qemu-iotests/302.out
ed5979
index 3e7c281b91..edfa1c4f05 100644
ed5979
--- a/tests/qemu-iotests/302.out
ed5979
+++ b/tests/qemu-iotests/302.out
ed5979
@@ -4,6 +4,11 @@ image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock
ed5979
 file format: raw
ed5979
 virtual size: 448 KiB (458752 bytes)
ed5979
 disk size: unavailable
ed5979
+Child node '/file':
ed5979
+    image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock
ed5979
+    file format: nbd
ed5979
+    virtual size: 448 KiB (458752 bytes)
ed5979
+    disk size: unavailable
ed5979
 
ed5979
 === Converted image info ===
ed5979
 image: TEST_IMG
ed5979
-- 
ed5979
2.31.1
ed5979