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