From 95145387706bf6276f69289221e82cb4eb677ffa Mon Sep 17 00:00:00 2001
From: Lee Yarwood <lyarwood@redhat.com>
Date: Mon, 10 Feb 2020 15:10:36 +0000
Subject: [PATCH] imageutils: Report format specific details when using JSON
 output format

The data held within this section is particularly useful when dealing
with encrypted LUKSv1, as shown below:

$ qemu-img info --output=json rbd:volumes/volume
{
    [..]
    "format-specific": {
        "type": "luks",
        "data": {
            "ivgen-alg": "plain64",
            "hash-alg": "sha256",
            "cipher-alg": "aes-256",
            "uuid": "de946b24-f318-4fa6-aacf-c5e97db04609",
            "cipher-mode": "xts",
            "slots": [
                {
                    "active": true,
                    "iters": 1966316,
                    "key-offset": 4096,
                    "stripes": 4000
                },
                {
                    "active": false,
                    "key-offset": 262144
                },
                {
                    "active": false,
                    "key-offset": 520192
                },
                {
                    "active": false,
                    "key-offset": 778240
                },
                {
                    "active": false,
                    "key-offset": 1036288
                },
                {
                    "active": false,
                    "key-offset": 1294336
                },
                {
                    "active": false,
                    "key-offset": 1552384
                },
                {
                    "active": false,
                    "key-offset": 1810432
                }
            ],
            "payload-offset": 2068480,
            "master-key-iters": 479636
        }
    },
}

Unfortunately the only documentation for this is in the QEMU codebase at
present, for example for LUKSv1:

https://github.com/qemu/qemu/blob/d8d5fefd8657d4f7b380b3a1533340434b5b9def/qapi/crypto.json#L272-L298

Related-Bug: #1861071
Change-Id: I133da07a5a9628b8a9338556939c858afae679f4
(cherry picked from commit 2180db82b605cf84902ee379fffc0b34e17e92c7)
---
 oslo_utils/imageutils.py            | 4 ++++
 oslo_utils/tests/test_imageutils.py | 5 ++++-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/oslo_utils/imageutils.py b/oslo_utils/imageutils.py
index a9bb3bb..a366dd8 100644
--- a/oslo_utils/imageutils.py
+++ b/oslo_utils/imageutils.py
@@ -58,6 +58,7 @@ class QemuImgInfo(object):
             self.disk_size = details.get('actual-size')
             self.snapshots = details.get('snapshots', [])
             self.encrypted = details.get('encrypted')
+            self.format_specific = details.get('format-specific')
         else:
             details = self._parse(cmd_output or '')
             self.image = details.get('image')
@@ -68,6 +69,7 @@ class QemuImgInfo(object):
             self.disk_size = details.get('disk_size')
             self.snapshots = details.get('snapshot_list', [])
             self.encrypted = details.get('encrypted')
+            self.format_specific = None
 
     def __str__(self):
         lines = [
@@ -82,6 +84,8 @@ class QemuImgInfo(object):
             lines.append("snapshots: %s" % self.snapshots)
         if self.encrypted:
             lines.append("encrypted: %s" % self.encrypted)
+        if self.format_specific:
+            lines.appened("format_specific: %s" % self.format_specific)
         return "\n".join(lines)
 
     def _canonicalize(self, field):
diff --git a/oslo_utils/tests/test_imageutils.py b/oslo_utils/tests/test_imageutils.py
index 532c455..073367b 100644
--- a/oslo_utils/tests/test_imageutils.py
+++ b/oslo_utils/tests/test_imageutils.py
@@ -208,7 +208,8 @@ class ImageUtilsJSONTestCase(test_base.BaseTestCase):
                        "filename": "fake_img",
                        "cluster-size": 65536,
                        "format": "qcow2",
-                       "actual-size": 13168640
+                       "actual-size": 13168640,
+                       "format-specific": {"data": {"foo": "bar"}}
                       }'''
         image_info = imageutils.QemuImgInfo(img_output, format='json')
         self.assertEqual(41126400, image_info.virtual_size)
@@ -216,6 +217,7 @@ class ImageUtilsJSONTestCase(test_base.BaseTestCase):
         self.assertEqual(65536, image_info.cluster_size)
         self.assertEqual('qcow2', image_info.file_format)
         self.assertEqual(13168640, image_info.disk_size)
+        self.assertEqual("bar", image_info.format_specific["data"]["foo"])
 
     def test_qemu_img_info_json_format_blank(self):
         img_output = '{}'
@@ -225,3 +227,4 @@ class ImageUtilsJSONTestCase(test_base.BaseTestCase):
         self.assertIsNone(image_info.cluster_size)
         self.assertIsNone(image_info.file_format)
         self.assertIsNone(image_info.disk_size)
+        self.assertIsNone(image_info.format_specific)
-- 
2.7.4

