9ae3a8
From 2513477895b7fd9434a66be645dfeae5dc2eccb8 Mon Sep 17 00:00:00 2001
9ae3a8
From: Eric Blake <eblake@redhat.com>
9ae3a8
Date: Wed, 20 Aug 2014 16:40:12 +0200
9ae3a8
Subject: [PATCH 09/11] block: make 'top' argument to block-commit optional
9ae3a8
9ae3a8
Message-id: <1408552814-23031-6-git-send-email-eblake@redhat.com>
9ae3a8
Patchwork-id: 60647
9ae3a8
O-Subject: [qemu-kvm-rhev 7.0.z PATCH 5/7] block: make 'top' argument to block-commit optional
9ae3a8
Bugzilla: 1130603
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
RH-Acked-by: Fam Zheng <famz@redhat.com>
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
9ae3a8
From: Jeff Cody <jcody@redhat.com>
9ae3a8
9ae3a8
Now that active layer block-commit is supported, the 'top' argument
9ae3a8
no longer needs to be mandatory.
9ae3a8
9ae3a8
Change it to optional, with the default being the active layer in the
9ae3a8
device chain.
9ae3a8
9ae3a8
[kwolf: Rebased and resolved conflict in tests/qemu-iotests/040]
9ae3a8
9ae3a8
Reviewed-by: Eric Blake <eblake@redhat.com>
9ae3a8
Reviewed-by: Benoit Canet <benoit@irqsave.net>
9ae3a8
Signed-off-by: Jeff Cody <jcody@redhat.com>
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
(cherry picked from commit 7676e2c597000eff3a7233b40cca768b358f9bc9)
9ae3a8
9ae3a8
Conflicts:
9ae3a8
	qapi/block-core.json - downstream in qapi-schema.json still
9ae3a8
	tests/qemu-iotests/040 - context due to no common event handling
9ae3a8
9ae3a8
Signed-off-by: Eric Blake <eblake@redhat.com>
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 blockdev.c             |   16 ++++++++++++++--
9ae3a8
 qapi-schema.json       |    7 ++++---
9ae3a8
 qmp-commands.hx        |    5 +++--
9ae3a8
 tests/qemu-iotests/040 |   28 ++++++++++++++++++----------
9ae3a8
 4 files changed, 39 insertions(+), 17 deletions(-)
9ae3a8
9ae3a8
diff --git a/blockdev.c b/blockdev.c
9ae3a8
index aa5d0a6..107e27e 100644
9ae3a8
--- a/blockdev.c
9ae3a8
+++ b/blockdev.c
9ae3a8
@@ -1463,7 +1463,8 @@ void qmp_block_stream(const char *device, bool has_base,
9ae3a8
 }
9ae3a8
 
9ae3a8
 void qmp_block_commit(const char *device,
9ae3a8
-                      bool has_base, const char *base, const char *top,
9ae3a8
+                      bool has_base, const char *base,
9ae3a8
+                      bool has_top, const char *top,
9ae3a8
                       bool has_speed, int64_t speed,
9ae3a8
                       Error **errp)
9ae3a8
 {
9ae3a8
@@ -1478,6 +1479,11 @@ void qmp_block_commit(const char *device,
9ae3a8
     /* drain all i/o before commits */
9ae3a8
     bdrv_drain_all();
9ae3a8
 
9ae3a8
+    /* Important Note:
9ae3a8
+     *  libvirt relies on the DeviceNotFound error class in order to probe for
9ae3a8
+     *  live commit feature versions; for this to work, we must make sure to
9ae3a8
+     *  perform the device lookup before any generic errors that may occur in a
9ae3a8
+     *  scenario in which all optional arguments are omitted. */
9ae3a8
     bs = bdrv_find(device);
9ae3a8
     if (!bs) {
9ae3a8
         error_set(errp, QERR_DEVICE_NOT_FOUND, device);
9ae3a8
@@ -1487,7 +1493,7 @@ void qmp_block_commit(const char *device,
9ae3a8
     /* default top_bs is the active layer */
9ae3a8
     top_bs = bs;
9ae3a8
 
9ae3a8
-    if (top) {
9ae3a8
+    if (has_top && top) {
9ae3a8
         if (strcmp(bs->filename, top) != 0) {
9ae3a8
             top_bs = bdrv_find_backing_image(bs, top);
9ae3a8
         }
9ae3a8
@@ -1509,6 +1515,12 @@ void qmp_block_commit(const char *device,
9ae3a8
         return;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    /* Do not allow attempts to commit an image into itself */
9ae3a8
+    if (top_bs == base_bs) {
9ae3a8
+        error_setg(errp, "cannot commit an image into itself");
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     if (top_bs == bs) {
9ae3a8
         commit_active_start(bs, base_bs, speed, on_error, block_job_cb,
9ae3a8
                             bs, &local_err);
9ae3a8
diff --git a/qapi-schema.json b/qapi-schema.json
9ae3a8
index 604ec69..18ab949 100644
9ae3a8
--- a/qapi-schema.json
9ae3a8
+++ b/qapi-schema.json
9ae3a8
@@ -1844,8 +1844,9 @@
9ae3a8
 # @base:   #optional The file name of the backing image to write data into.
9ae3a8
 #                    If not specified, this is the deepest backing image
9ae3a8
 #
9ae3a8
-# @top:              The file name of the backing image within the image chain,
9ae3a8
-#                    which contains the topmost data to be committed down.
9ae3a8
+# @top:    #optional The file name of the backing image within the image chain,
9ae3a8
+#                    which contains the topmost data to be committed down. If
9ae3a8
+#                    not specified, this is the active layer.
9ae3a8
 #
9ae3a8
 #                    If top == base, that is an error.
9ae3a8
 #                    If top == active, the job will not be completed by itself,
9ae3a8
@@ -1873,7 +1874,7 @@
9ae3a8
 #
9ae3a8
 ##
9ae3a8
 { 'command': 'block-commit',
9ae3a8
-  'data': { 'device': 'str', '*base': 'str', 'top': 'str',
9ae3a8
+  'data': { 'device': 'str', '*base': 'str', '*top': 'str',
9ae3a8
             '*speed': 'int' } }
9ae3a8
 
9ae3a8
 ##
9ae3a8
diff --git a/qmp-commands.hx b/qmp-commands.hx
9ae3a8
index 405275c..f67121a 100644
9ae3a8
--- a/qmp-commands.hx
9ae3a8
+++ b/qmp-commands.hx
9ae3a8
@@ -1005,7 +1005,7 @@ EQMP
9ae3a8
 
9ae3a8
     {
9ae3a8
         .name       = "block-commit",
9ae3a8
-        .args_type  = "device:B,base:s?,top:s,speed:o?",
9ae3a8
+        .args_type  = "device:B,base:s?,top:s?,speed:o?",
9ae3a8
         .mhandler.cmd_new = qmp_marshal_input_block_commit,
9ae3a8
     },
9ae3a8
 
9ae3a8
@@ -1023,7 +1023,8 @@ Arguments:
9ae3a8
           If not specified, this is the deepest backing image
9ae3a8
           (json-string, optional)
9ae3a8
 - "top":  The file name of the backing image within the image chain,
9ae3a8
-          which contains the topmost data to be committed down.
9ae3a8
+          which contains the topmost data to be committed down. If
9ae3a8
+          not specified, this is the active layer. (json-string, optional)
9ae3a8
 
9ae3a8
           If top == base, that is an error.
9ae3a8
           If top == active, the job will not be completed by itself,
9ae3a8
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
9ae3a8
index cf80e0c..c5ab930 100755
9ae3a8
--- a/tests/qemu-iotests/040
9ae3a8
+++ b/tests/qemu-iotests/040
9ae3a8
@@ -63,11 +63,7 @@ class ImageCommitTestCase(iotests.QMPTestCase):
9ae3a8
             i = i + 512
9ae3a8
         file.close()
9ae3a8
 
9ae3a8
-    def run_commit_test(self, top, base, need_ready=False):
9ae3a8
-        self.assert_no_active_commit()
9ae3a8
-        result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
9ae3a8
-        self.assert_qmp(result, 'return', {})
9ae3a8
-
9ae3a8
+    def wait_for_complete(self, need_ready=False):
9ae3a8
         completed = False
9ae3a8
         ready = False
9ae3a8
         while not completed:
9ae3a8
@@ -90,6 +86,18 @@ class ImageCommitTestCase(iotests.QMPTestCase):
9ae3a8
         self.assert_no_active_commit()
9ae3a8
         self.vm.shutdown()
9ae3a8
 
9ae3a8
+    def run_commit_test(self, top, base, need_ready=False):
9ae3a8
+        self.assert_no_active_commit()
9ae3a8
+        result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
9ae3a8
+        self.assert_qmp(result, 'return', {})
9ae3a8
+        self.wait_for_complete(need_ready)
9ae3a8
+
9ae3a8
+    def run_default_commit_test(self):
9ae3a8
+        self.assert_no_active_commit()
9ae3a8
+        result = self.vm.qmp('block-commit', device='drive0')
9ae3a8
+        self.assert_qmp(result, 'return', {})
9ae3a8
+        self.wait_for_complete()
9ae3a8
+
9ae3a8
 class TestSingleDrive(ImageCommitTestCase):
9ae3a8
     image_len = 1 * 1024 * 1024
9ae3a8
     test_len = 1 * 1024 * 256
9ae3a8
@@ -141,17 +149,17 @@ class TestSingleDrive(ImageCommitTestCase):
9ae3a8
         self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
9ae3a8
         self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
9ae3a8
 
9ae3a8
+    def test_top_is_default_active(self):
9ae3a8
+        self.run_default_commit_test()
9ae3a8
+        self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
9ae3a8
+        self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
9ae3a8
+
9ae3a8
     def test_top_and_base_reversed(self):
9ae3a8
         self.assert_no_active_commit()
9ae3a8
         result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img)
9ae3a8
         self.assert_qmp(result, 'error/class', 'GenericError')
9ae3a8
         self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img)
9ae3a8
 
9ae3a8
-    def test_top_omitted(self):
9ae3a8
-        self.assert_no_active_commit()
9ae3a8
-        result = self.vm.qmp('block-commit', device='drive0')
9ae3a8
-        self.assert_qmp(result, 'error/class', 'GenericError')
9ae3a8
-        self.assert_qmp(result, 'error/desc', "Parameter 'top' is missing")
9ae3a8
 
9ae3a8
 class TestRelativePaths(ImageCommitTestCase):
9ae3a8
     image_len = 1 * 1024 * 1024
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8