block: make 'top' argument to block-commit optional

Now that active layer block-commit is supported, the 'top' argument
no longer needs to be mandatory.

Change it to optional, with the default being the active layer in the
device chain.

[kwolf: Rebased and resolved conflict in tests/qemu-iotests/040]

Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Benoit Canet <benoit@irqsave.net>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Jeff Cody 2014-06-30 15:14:15 +02:00 committed by Stefan Hajnoczi
parent c891e3bbc5
commit 7676e2c597
4 changed files with 39 additions and 17 deletions

View File

@ -1913,7 +1913,8 @@ void qmp_block_stream(const char *device, bool has_base,
} }
void qmp_block_commit(const char *device, void qmp_block_commit(const char *device,
bool has_base, const char *base, const char *top, bool has_base, const char *base,
bool has_top, const char *top,
bool has_speed, int64_t speed, bool has_speed, int64_t speed,
Error **errp) Error **errp)
{ {
@ -1932,6 +1933,11 @@ void qmp_block_commit(const char *device,
/* drain all i/o before commits */ /* drain all i/o before commits */
bdrv_drain_all(); bdrv_drain_all();
/* Important Note:
* libvirt relies on the DeviceNotFound error class in order to probe for
* live commit feature versions; for this to work, we must make sure to
* perform the device lookup before any generic errors that may occur in a
* scenario in which all optional arguments are omitted. */
bs = bdrv_find(device); bs = bdrv_find(device);
if (!bs) { if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
@ -1945,7 +1951,7 @@ void qmp_block_commit(const char *device,
/* default top_bs is the active layer */ /* default top_bs is the active layer */
top_bs = bs; top_bs = bs;
if (top) { if (has_top && top) {
if (strcmp(bs->filename, top) != 0) { if (strcmp(bs->filename, top) != 0) {
top_bs = bdrv_find_backing_image(bs, top); top_bs = bdrv_find_backing_image(bs, top);
} }
@ -1967,6 +1973,12 @@ void qmp_block_commit(const char *device,
return; return;
} }
/* Do not allow attempts to commit an image into itself */
if (top_bs == base_bs) {
error_setg(errp, "cannot commit an image into itself");
return;
}
if (top_bs == bs) { if (top_bs == bs) {
commit_active_start(bs, base_bs, speed, on_error, block_job_cb, commit_active_start(bs, base_bs, speed, on_error, block_job_cb,
bs, &local_err); bs, &local_err);

View File

@ -690,8 +690,9 @@
# @base: #optional The file name of the backing image to write data into. # @base: #optional The file name of the backing image to write data into.
# If not specified, this is the deepest backing image # If not specified, this is the deepest backing image
# #
# @top: The file name of the backing image within the image chain, # @top: #optional The file name of the backing image within the image chain,
# which contains the topmost data to be committed down. # which contains the topmost data to be committed down. If
# not specified, this is the active layer.
# #
# If top == base, that is an error. # If top == base, that is an error.
# If top == active, the job will not be completed by itself, # If top == active, the job will not be completed by itself,
@ -719,7 +720,7 @@
# #
## ##
{ 'command': 'block-commit', { 'command': 'block-commit',
'data': { 'device': 'str', '*base': 'str', 'top': 'str', 'data': { 'device': 'str', '*base': 'str', '*top': 'str',
'*speed': 'int' } } '*speed': 'int' } }
## ##

View File

@ -985,7 +985,7 @@ EQMP
{ {
.name = "block-commit", .name = "block-commit",
.args_type = "device:B,base:s?,top:s,speed:o?", .args_type = "device:B,base:s?,top:s?,speed:o?",
.mhandler.cmd_new = qmp_marshal_input_block_commit, .mhandler.cmd_new = qmp_marshal_input_block_commit,
}, },
@ -1003,7 +1003,8 @@ Arguments:
If not specified, this is the deepest backing image If not specified, this is the deepest backing image
(json-string, optional) (json-string, optional)
- "top": The file name of the backing image within the image chain, - "top": The file name of the backing image within the image chain,
which contains the topmost data to be committed down. which contains the topmost data to be committed down. If
not specified, this is the active layer. (json-string, optional)
If top == base, that is an error. If top == base, that is an error.
If top == active, the job will not be completed by itself, If top == active, the job will not be completed by itself,

View File

@ -35,11 +35,7 @@ test_img = os.path.join(iotests.test_dir, 'test.img')
class ImageCommitTestCase(iotests.QMPTestCase): class ImageCommitTestCase(iotests.QMPTestCase):
'''Abstract base class for image commit test cases''' '''Abstract base class for image commit test cases'''
def run_commit_test(self, top, base, need_ready=False): def wait_for_complete(self, need_ready=False):
self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
self.assert_qmp(result, 'return', {})
completed = False completed = False
ready = False ready = False
while not completed: while not completed:
@ -62,6 +58,18 @@ class ImageCommitTestCase(iotests.QMPTestCase):
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
self.vm.shutdown() self.vm.shutdown()
def run_commit_test(self, top, base, need_ready=False):
self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
self.assert_qmp(result, 'return', {})
self.wait_for_complete(need_ready)
def run_default_commit_test(self):
self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0')
self.assert_qmp(result, 'return', {})
self.wait_for_complete()
class TestSingleDrive(ImageCommitTestCase): class TestSingleDrive(ImageCommitTestCase):
image_len = 1 * 1024 * 1024 image_len = 1 * 1024 * 1024
test_len = 1 * 1024 * 256 test_len = 1 * 1024 * 256
@ -113,17 +121,17 @@ class TestSingleDrive(ImageCommitTestCase):
self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
def test_top_is_default_active(self):
self.run_default_commit_test()
self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
def test_top_and_base_reversed(self): def test_top_and_base_reversed(self):
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img) result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img)
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img) self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img)
def test_top_omitted(self):
self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0')
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', "Parameter 'top' is missing")
class TestRelativePaths(ImageCommitTestCase): class TestRelativePaths(ImageCommitTestCase):
image_len = 1 * 1024 * 1024 image_len = 1 * 1024 * 1024