Live block commit
v3 cover Live block commit
This series adds the basic case, of a live commit between two images below the active layer, e.g.:
[base] <--- [snp-1] <--- [snp-2] <--- [snp-3] <--- [active]
can be collapsed down via commit, into:
[base] <--- [active]
or,
[base] <--- [snp-1] <--- [active],
[base] <--- [snp-3] <--- [active],
etc..
TODO: 'stage-2' of live commit functionality, to be able to push down the
active layer.
These changes are all reflected in my github repo:
git://github.com/codyprime/qemu-kvm-jtc.git branch: jtc-live-commit-1.3-v9
Changes from v2 -> v3:
===================================
Patch 1/6: Comment cleanup, code cleanup Patch 2/6: Added additional sanity checks for base & top:
- check if top == active
- check if top == base
- check if top & base args are reversed
Patch 5/6: Made top = active as the default, however 'top' argument
is now non-optional.
Patch 6/6: Added 6 more tests to the qemu-io test:
- top == base
- top is a non-existent file
- base is a non-existent file
- top == active
- top and base are reversed
- top is omitted
Patch 7/7 (old): dropped, already applied
Changes from v1 -> v2:
===================================
Patch 1/7: [Eric] - Commit message cleanup
- Make comments clearer
- fixed whitespace removal
Dropped old Patch 4/8 (new qerror message) - Eric
Patch 2/7: - Changed error_set() to error_setg()
Patch 5/7: [Eric] - Changed error_set() to error_setg()
- Updated comments/docs to be clearer
- fixed whitespace removal
Changes from RFC to v1:
===================================
* Feedback incorporated (see below)
* qemu-iotests added
* live snapshots have active->backing_hd reopened read-only.
Patch 1/8: [Kevin] - Rename bdrv_delete_intermediate() to
bdrv_drop_intermediate()
- Change terminology from 'child' to 'overlay'
- Do not allow top == active, and return NULL for error, simplified some logic.
Patch 2/8: [Kevin] - Preserve errors in commit_populate()
[Kevin, Eric] - Check that there is enough room in base, and resize
with bdrv_truncate() if not.
[Jeff] - Switch from 'child' to 'overlay' terminology
- Fixed up more error checking
- Minor cleanup
[Kevin] - Perform the bdrv_reopen() inside commit_start(),
instead of in blockdev.c
Patch 3/8: [Paolo] - Rearranged patch, so the forward declaration is gone
Patch 4/8: None
Patch 5/8: None
Patch 6/8: [Eric] - Updated QMP/qmp-events.txt with the new 'type' field
[Kevin] - If base cannot be found, return attempted base name
- Moved reopen() logic to commit_start
- Fixed comments in JSON command header
[Jeff] - If 'top' is not specified, default to active->backing_hd
Patch 7/8: New - simple qemu-iotest for commit.
Patch 8/8: New - after live snapshot, use bdrv_reopen() to reopen
active->backing_hd read-only.
Changes from the RFC v1 -> RFC v2:
===================================
* This patch series is not on top of Paolo's blk mirror series yet, to make it easier to apply independently if desired. This means some of what was in the previous RFC series is not in this one (BlockdevOnError, for instance), but that can be easily added in once Paolo's series are in.
* This patches series is dependent on the reopen() series with transactional reopen.
* The target release for this series is 1.3
* Found some mistakes in the reopen calls
* Dropped the BlockdevOnError argument (for now), will add in if rebasing on top of Paolo's series.
* Used the new qerror system
Jeff Cody (6):
block: add support functions for live commit, to find and delete
images.
block: add live block commit functionality
blockdev: rename block_stream_cb to a generic block_job_cb
block: helper function, to find the base image of a chain
QAPI: add command for live block commit, 'block-commit'
qemu-iotests: add initial tests for live block commit
QMP/qmp-events.txt | 6 +- block.c | 159 +++++++++++++++++++++++++++ block.h | 5 + block/Makefile.objs | 1 + block/commit.c | 267 +++++++++++++++++++++++++++++++++++++++++++++ block_int.h | 16 +++ blockdev.c | 64 ++++++++++- qapi-schema.json | 34 ++++++ qmp-commands.hx | 6 + tests/qemu-iotests/040 | 178 ++++++++++++++++++++++++++++++ tests/qemu-iotests/040.out | 5 + tests/qemu-iotests/group | 1 + trace-events | 4 +- 13 files changed, 740 insertions(+), 6 deletions(-) create mode 100644 block/commit.c create mode 100755 tests/qemu-iotests/040 create mode 100644 tests/qemu-iotests/040.out
Signature · 1 line
1.7.11.4
PATCH v3 1/6 block: add support functions for live commit, to find and delete images.
Add bdrv_find_overlay(), and bdrv_drop_intermediate().
bdrv_find_overlay(): given 'bs' and the active (topmost) BDS of an image chain,
find the image that is the immediate top of 'bs'
bdrv_drop_intermediate():
Given 3 BDS (active, top, base), drop images above base up to and including top, and set base to be the backing file of top's overlay node.
E.g., this converts:
bottom <- base <- intermediate <- top <- active
to
bottom <- base <- active
--- block.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ block.h | 4 ++ 2 files changed, 147 insertions(+)
block.c +143 -0
@@ -1724,6 +1724,149 @@ int bdrv_change_backing_file(BlockDriverState *bs,return ret; } +/* +* Finds the image layer in the chain that has 'bs' as its backing file. +* +* active is the current topmost image. +* +* Returns NULL if bs is not found in active's image chain, +* or if active == bs. +*/ +BlockDriverState *bdrv_find_overlay (BlockDriverState *active, +BlockDriverState *bs) +{ +BlockDriverState *overlay =NULL ; +BlockDriverState *intermediate; + +assert (active !=NULL ); +assert (bs !=NULL ); + +/* if bs is the same as active, then by definition it has no overlay +*/ +if (active == bs) { +return NULL ; + } + + intermediate = active; +while (intermediate->backing_hd) { +if (intermediate->backing_hd == bs) { + overlay = intermediate; +break ; + } + intermediate = intermediate->backing_hd; + } + +return overlay; +} + +typedef struct BlkIntermediateStates { +BlockDriverState *bs; +QSIMPLEQ_ENTRY (BlkIntermediateStates) entry; +}BlkIntermediateStates ; + + +/* +* Drops images above 'base' up to and including 'top', and sets the image +* above 'top' to have base as its backing file. +* +* Requires that the overlay to 'top' is opened r/w, so that the backing file +* information in 'bs' can be properly updated. +* +* E.g., this will convert the following chain: +* bottom <- base <- intermediate <- top <- active +* +* to +* +* bottom <- base <- active +* +* It is allowed for bottom==base, in which case it converts: +* +* base <- intermediate <- top <- active +* +* to +* +* base <- active +* +* Error conditions: +* if active == top, that is considered an error +* +*/ +int bdrv_drop_intermediate (BlockDriverState *active,BlockDriverState *top, +BlockDriverState *base) +{ +BlockDriverState *intermediate; +BlockDriverState *base_bs =NULL ; +BlockDriverState *new_top_bs =NULL ; +BlkIntermediateStates *intermediate_state, *next; +int ret = -EIO ; + + QSIMPLEQ_HEAD(states_to_delete, BlkIntermediateStates) states_to_delete; +QSIMPLEQ_INIT (&states_to_delete); + +if (!top->drv || !base->drv) { + goto exit; + } + + new_top_bs =bdrv_find_overlay (active, top); + +if (new_top_bs ==NULL ) { +/* we could not find the image above 'top', this is an error */ + goto exit; + } + +/* special case of new_top_bs->backing_hd already pointing to base - nothing +* to do, no intermediate images */ +if (new_top_bs->backing_hd == base) { + ret =0 ; + goto exit; + } + + intermediate = top; + +/* now we will go down through the list, and add each BDS we find +* into our deletion queue, until we hit the 'base' +*/ +while (intermediate) { + intermediate_state =g_malloc0 (sizeof (BlkIntermediateStates)); + intermediate_state->bs = intermediate; +QSIMPLEQ_INSERT_TAIL (&states_to_delete, intermediate_state, entry); + +if (intermediate->backing_hd == base) { + base_bs = intermediate->backing_hd; +break ; + } + intermediate = intermediate->backing_hd; + } +if (base_bs ==NULL ) { +/* something went wrong, we did not end at the base. safely +* unravel everything, and exit with error */ + goto exit; + } + +/* success - we can delete the intermediate states, and link top->base */ + ret =bdrv_change_backing_file (new_top_bs, base_bs->filename, + base_bs->drv ? base_bs->drv->format_name :"" ); +if (ret) { + goto exit; + } + new_top_bs->backing_hd = base_bs; + + + QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) { +/* so that bdrv_close() does not recursively close the chain */ + intermediate_state->bs->backing_hd =NULL ; +bdrv_delete (intermediate_state->bs); + } + ret =0 ; + +exit: + QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) { +g_free (intermediate_state); + } +return ret; +} + +static int bdrv_check_byte_request (BlockDriverState *bs,int64_t offset,size_t size) {
block.h +4 -0
@@ -203,6 +203,10 @@ int bdrv_commit_all(void);int bdrv_change_backing_file (BlockDriverState *bs,const char *backing_file,const char *backing_fmt);void bdrv_register (BlockDriver *bdrv); +int bdrv_drop_intermediate (BlockDriverState *active,BlockDriverState *top, +BlockDriverState *base); +BlockDriverState *bdrv_find_overlay (BlockDriverState *active, +BlockDriverState *bs); typedef struct BdrvCheckResult {
Signature · 1 line
1.7.11.4
PATCH v3 2/6 block: add live block commit functionality
In reply to Jeff CodyThis adds the live commit coroutine. This iteration focuses on the commit only below the active layer, and not the active layer itself.
The behaviour is similar to block streaming; the sectors are walked through, and anything that exists above 'base' is committed back down into base. At the end, intermediate images are deleted, and the chain stitched together. Images are restored to their original open flags upon completion.
--- block/Makefile.objs | 1 + block/commit.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++++ block_int.h | 16 ++++ trace-events | 2 + 4 files changed, 286 insertions(+) create mode 100644 block/commit.c
block/Makefile.objs +1 -0
@@ -4,6 +4,7 @@ block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o block-obj-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o block-obj-y += stream.o +block-obj-y += commit.o block-obj-$(CONFIG_WIN32) += raw-win32.o block-obj-$(CONFIG_POSIX) += raw-posix.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o
block/commit.c new file +267 -0
267 lines not on this page · raw message
block_int.h +16 -0
@@ -463,4 +463,20 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base, BlockDriverCompletionFunc *cb, void *opaque, Error **errp); +/** +* commit_start: +* @bs: Top Block device +* @base: Block device that will be written into, and become the new top +* @speed: The maximum speed, in bytes per second, or 0 for unlimited. +* @on_error: The action to take upon error. +* @cb: Completion function for the job. +* @opaque: Opaque pointer value passed to @cb. +* @errp: Error object. +* +*/ +void commit_start (BlockDriverState *bs,BlockDriverState *base, +BlockDriverState *top,int64_t speed, +BlockErrorAction on_error,BlockDriverCompletionFunc *cb, +void *opaque,Error **errp); +#endif /* BLOCK_INT_H */
trace-events +2 -0
@@ -74,6 +74,8 @@ bdrv_co_do_copy_on_readv(void *bs, int64_t sector_num, int nb_sectors, int64_t c # block/stream.c stream_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d" stream_start(void *bs, void *base, void *s, void *co, void *opaque) "bs %p base %p s %p co %p opaque %p" +commit_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d" +commit_start(void *bs, void *base, void *top, void *s, void *co, void *opaque) "bs %p base %p top %p s %p co %p opaque %p" # blockdev.c qmp_block_job_cancel(void *job) "job %p"
Signature · 1 line
1.7.11.4
PATCH v3 3/6 blockdev: rename block_stream_cb to a generic block_job_cb
In reply to Jeff Cody--- blockdev.c | 6 +++--- trace-events | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-)
blockdev.c +3 -3
@@ -1063,12 +1063,12 @@ static QObject *qobject_from_block_job(BlockJob *job) job->speed); } -static void block_stream_cb (void *opaque,int ret) +static void block_job_cb (void *opaque,int ret) {BlockDriverState *bs = opaque;QObject *obj; -trace_block_stream_cb (bs, bs->job, ret); +trace_block_job_cb (bs, bs->job, ret);assert (bs->job); obj =qobject_from_block_job (bs->job);
@@ -1110,7 +1110,7 @@ void qmp_block_stream(const char *device, bool has_base, }stream_start (bs, base_bs, base, has_speed ? speed :0 , - block_stream_cb, bs, &local_err); + block_job_cb, bs, &local_err);if (error_is_set (&local_err)) {error_propagate (errp, local_err);return ;
trace-events +1 -1
@@ -79,7 +79,7 @@ commit_start(void *bs, void *base, void *top, void *s, void *co, void *opaque) " # blockdev.c qmp_block_job_cancel(void *job) "job %p" -block_stream_cb(void *bs, void *job, int ret) "bs %p job %p ret %d" +block_job_cb(void *bs, void *job, int ret) "bs %p job %p ret %d" qmp_block_stream(void *bs, void *job) "bs %p job %p" # hw/virtio-blk.c
Signature · 1 line
1.7.11.4
PATCH v3 4/6 block: helper function, to find the base image of a chain
In reply to Jeff CodyThis is a simple helper function, that will return the base image of a given image chain.
--- block.c | 16 ++++++++++++++++ block.h | 1 + 2 files changed, 17 insertions(+)
block.c +16 -0
@@ -3117,6 +3117,22 @@ int bdrv_get_backing_file_depth(BlockDriverState *bs)return 1 +bdrv_get_backing_file_depth (bs->backing_hd); } +BlockDriverState *bdrv_find_base (BlockDriverState *bs) +{ +BlockDriverState *curr_bs =NULL ; + +if (!bs) { +return NULL ; + } + + curr_bs = bs; + +while (curr_bs->backing_hd) { + curr_bs = curr_bs->backing_hd; + } +return curr_bs; +} +#define NB_SUFFIXES 4char *get_human_readable_size (char *buf,int buf_size,int64_t size)
block.h +1 -0
@@ -207,6 +207,7 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, BlockDriverState *base);BlockDriverState *bdrv_find_overlay (BlockDriverState *active,BlockDriverState *bs); +BlockDriverState *bdrv_find_base (BlockDriverState *bs); typedef struct BdrvCheckResult {
Signature · 1 line
1.7.11.4
PATCH v3 5/6 QAPI: add command for live block commit, 'block-commit'
In reply to Jeff CodyThe command for live block commit is added, which has the following arguments:
device: the block device to perform the commit on (mandatory) base: the base image to commit into; optional (if not specified,
it is the underlying original image)
top: the top image of the commit - all data from inside top down
to base will be committed into base (mandatory for now; see note, below)
speed: maximum speed, in bytes/sec
Note: Eventually this command will support merging down the active layer,
but that code is not yet complete. If the active layer is passed in as top, then an error will be returned. Once merging down the active layer is supported, the 'top' argument may become optional, and default to the active layer.
The is done as a block job, so upon completion a BLOCK_JOB_COMPLETED will be emitted.
--- QMP/qmp-events.txt | 6 ++++-- blockdev.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ qapi-schema.json | 34 ++++++++++++++++++++++++++++++++ qmp-commands.hx | 6 ++++++ 4 files changed, 102 insertions(+), 2 deletions(-)
QMP/qmp-events.txt +4 -2
@@ -50,7 +50,8 @@ Emitted when a block job has been cancelled. Data: -- "type": Job type ("stream" for image streaming, json-string) +- "type": Job type (json-string; "stream" for image streaming + "commit" for block commit) - "device": Device name (json-string) - "len": Maximum progress value (json-int) - "offset": Current progress value (json-int)
@@ -73,7 +74,8 @@ Emitted when a block job has completed. Data: -- "type": Job type ("stream" for image streaming, json-string) +- "type": Job type (json-string; "stream" for image streaming + "commit" for block commit) - "device": Device name (json-string) - "len": Maximum progress value (json-int) - "offset": Current progress value (json-int)
blockdev.c +58 -0
64 lines not on this page · raw message
qapi-schema.json +34 -0
@@ -1468,6 +1468,40 @@ 'returns': 'str' } ## +# @block-commit +# +# Live commit of data from overlay image nodes into backing nodes - i.e., +# writes data between 'top' and 'base' into 'base'. +# +# @device: the name of the device +# +# @base: #optional The file name of the backing image to write data into. +# If not specified, this is the deepest backing image +# +# @top: The file name of the backing image within the image chain, +# which contains the topmost data to be committed down. +# Note, the active layer as 'top' is currently unsupported. +# +# If top == base, that is an error. +# +# +# @speed: #optional the maximum speed, in bytes per second +# +# Returns: Nothing on success +# If commit or stream is already active on this device, DeviceInUse +# If @device does not exist, DeviceNotFound +# If image commit is not supported by this device, NotSupported +# If @base or @top is invalid, a generic error is returned +# If @top is the active layer, or omitted, a generic error is returned +# If @speed is invalid, InvalidParameter +# +# Since: 1.3 +# +## +{ 'command': 'block-commit', + 'data': { 'device': 'str', '*base': 'str', 'top': 'str', + '*speed': 'int' } } + # @migrate_cancel # # Cancel the current executing migration process.
qmp-commands.hx +6 -0
@@ -792,6 +792,12 @@ EQMP }, { + .name = "block-commit", + .args_type = "device:B,base:s?,top:s?,speed:o?", + .mhandler.cmd_new = qmp_marshal_input_block_commit, + }, + + { .name = "block-job-set-speed", .args_type = "device:B,speed:o", .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed,
Signature · 1 line
1.7.11.4
PATCH v3 6/6 qemu-iotests: add initial tests for live block commit
In reply to Jeff CodyDerived from the streaming test cases (030), this adds the following 9 tests:
1. For the following image chain, commit [mid] into [backing], and use qemu-io to verify [backing] has its original data, as well as the data from [mid]
[backing] <-- [mid] <-- [test]
2. Verifies that 'block-commit' with the 'speed' parameter sets the speed parameter, as reported by 'query-block-jobs'
3. Verifies that a bogus 'device' parameter to 'block-commit' results in error
4-9: Appropriate error values returned for the following argument errors:
* top == base
* top is nonexistent
* base is nonexistent
* top == active layer (this is currently not supported)
* top and base arguments are reversed
* top argument is omitted
--- tests/qemu-iotests/040 | 178 +++++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/040.out | 5 ++ tests/qemu-iotests/group | 1 + 3 files changed, 184 insertions(+) create mode 100755 tests/qemu-iotests/040 create mode 100644 tests/qemu-iotests/040.out
tests/qemu-iotests/040 new file +178 -0
@@ -0,0 +1,178 @@ +#!/usr/bin/env python +# +# Tests for image block commit. +# +# Copyright (C) 2012 IBM, Corp. +# Copyright (C) 2012 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Test for live block commit +# Derived from Image Streaming Test 030 + +import time +import os +import iotests +from iotests import qemu_img, qemu_io +import struct + +backing_img = os.path.join(iotests.test_dir, 'backing.img') +mid_img = os.path.join(iotests.test_dir, 'mid.img') +test_img = os.path.join(iotests.test_dir, 'test.img') + +class ImageCommitTestCase(iotests.QMPTestCase): + '''Abstract base class for image commit test cases''' + + def assert_no_active_commit(self): + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return', []) + + def cancel_and_wait(self, drive='drive0'): + '''Cancel a block job and wait for it to finish''' + result = self.vm.qmp('block-job-cancel', device=drive) + self.assert_qmp(result, 'return', {}) + + cancelled = False + while not cancelled: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_CANCELLED': + self.assert_qmp(event, 'data/type', 'commit') + self.assert_qmp(event, 'data/device', drive) + cancelled = True + + self.assert_no_active_commit() + + def create_image(self, name, size): + file = open(name, 'w') + i = 0 + while i < size: + sector = struct.pack('>l504xl', i / 512, i / 512) + file.write(sector) + i = i + 512 + file.close() + + +class TestSingleDrive(ImageCommitTestCase): + image_len = 1 * 1024 * 1024 + test_len = 1 * 1024 * 256 + + def setUp(self): + self.create_image(backing_img, TestSingleDrive.image_len) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) + qemu_io('-c', 'write -P 0xab 0 524288', backing_img) + qemu_io('-c', 'write -P 0xef 524288 524288', mid_img) + self.vm = iotests.VM().add_drive(test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + os.remove(mid_img) + os.remove(backing_img) + + def test_commit(self): + self.assert_no_active_commit() + result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img) + self.assert_qmp(result, 'return', {}) + + completed = False + while not completed: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_COMPLETED': + self.assert_qmp(event, 'data/type', 'commit') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/offset', self.image_len) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True + + self.assert_no_active_commit() + self.vm.shutdown() + + 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_device_not_found(self): + result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) + self.assert_qmp(result, 'error/class', 'DeviceNotFound') + + def test_top_same_base(self): + self.assert_no_active_commit() + result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % backing_img) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', 'Invalid files for merge: top and base are the same') + + def test_top_invalid(self): + self.assert_no_active_commit() + result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % backing_img) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') + + def test_base_invalid(self): + self.assert_no_active_commit() + result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img, base='badfile') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found') + + def test_top_is_active(self): + self.assert_no_active_commit() + result = self.vm.qmp('block-commit', device='drive0', top='%s' % test_img, base='%s' % backing_img) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', 'Top image as the active layer is currently unsupported') + + def test_top_and_base_reversed(self): + self.assert_no_active_commit() + 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/desc', 'Base (%(1)s) is not reachable from top (%(2)s)' % {"1" : mid_img, "2" : backing_img}) + + def test_top_omitted(self): + self.assert_no_active_commit() + result = self.vm.qmp('block-commit', device='drive0') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', 'Invalid parameter type for \'top\', expected: string') + + +class TestSetSpeed(ImageCommitTestCase): + image_len = 80 * 1024 * 1024 # MB + + def setUp(self): + qemu_img('create', backing_img, str(TestSetSpeed.image_len)) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) + self.vm = iotests.VM().add_drive(test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + os.remove(mid_img) + os.remove(backing_img) + + def test_set_speed(self): + self.assert_no_active_commit() + + result = self.vm.qmp('block-commit', device='drive0', top=mid_img, speed=1024 * 1024) + self.assert_qmp(result, 'return', {}) + + # Ensure the speed we set was accepted + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/device', 'drive0') + self.assert_qmp(result, 'return[0]/speed', 1024 * 1024) + + self.cancel_and_wait() + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2', 'qed'])
tests/qemu-iotests/040.out new file +5 -0
@@ -0,0 +1,5 @@ +......... +---------------------------------------------------------------------- +Ran 9 tests + +OK
tests/qemu-iotests/group +1 -0
@@ -46,3 +46,4 @@ 037 rw auto backing 038 rw auto backing 039 rw auto +040 rw auto
Signature · 1 line
1.7.11.4
Re: [Qemu-devel] [PATCH v3 5/6] QAPI: add command for live block commit, 'block-commit'
In reply to Jeff CodyQuoted 13 lines · Jeff Cody
The command for live block commit is added, which has the following arguments:
device: the block device to perform the commit on (mandatory) base: the base image to commit into; optional (if not specified,it is the underlying original image)
top: the top image of the commit - all data from inside top down
to base will be committed into base (mandatory for now; see note, below)+++ b/qmp-commands.hx
qmp-commands.hx EQMP · line 796
Quoted 5 lines
qmp-commands.hx +2
-0@@ -792,6 +792,12 @@ EQMP }, { + .name = "block-commit", + .args_type = "device:B,base:s?,top:s?,speed:o?",
s/top:s?/top:s/ now that top is mandatory.
Signature · 2 lines
Eric Blake [email protected] +1-919-301-3266 Libvirt virtualization library http://libvirt.org
Quoted 10 lines · Jeff Cody
This series adds the basic case, of a live commit between two images below the active layer, e.g.:
[base] <--- [snp-1] <--- [snp-2] <--- [snp-3] <--- [active]
can be collapsed down via commit, into:
[base] <--- [active]
Quoted 8 lines
Jeff Cody (6):
block: add support functions for live commit, to find and delete
images.
block: add live block commit functionality
blockdev: renameblock_stream_cbto a genericblock_job_cb
block: helper function, to find the base image of a chain
QAPI: add command for live block commit, 'block-commit'
qemu-iotests: add initial tests for live block commit
Series:
I found one minor problem in 5/6, and wonder if that can be fixed by whoever sends the pull request without needing a full spin of v4.
Signature · 2 lines
Eric Blake [email protected] +1-919-301-3266 Libvirt virtualization library http://libvirt.org
Re: [Qemu-devel] [PATCH v3 5/6] QAPI: add command for live block commit, 'block-commit'
In reply to Eric BlakeQuoted 16 lines · Eric Blake
Quoted 15 lines · Jeff Cody
The command for live block commit is added, which has the following arguments:
device: the block device to perform the commit on (mandatory) base: the base image to commit into; optional (if not specified,it is the underlying original image)
top: the top image of the commit - all data from inside top down
to base will be committed into base (mandatory for now; see note, below)+++ b/qmp-commands.hx
(quoted diff) +0
-0@@ -792,6 +792,12 @@ EQMP },
Quoted 6 lines
qmp-commands.hx EQMP · line 796
Quoted 3 lines
qmp-commands.hx +2
-0{ + .name = "block-commit", + .args_type = "device:B,base:s?,top:s?,speed:o?",s/top:s?/top:s/ now that top is mandatory.
Right, forgot the hx file. Although, QAPI catches it just fine regardless of the hx file... but I will spin that really quick (hopefully that is the only change required), unless Kevin wants to do it when he pulls it into his branch.
Kevin Wolf
AppliedAm 27.09.2012 19:29, schrieb Jeff Cody:
Quoted 15 lines
This series adds the basic case, of a live commit between two images below the active layer, e.g.:
[base] <--- [snp-1] <--- [snp-2] <--- [snp-3] <--- [active]
can be collapsed down via commit, into:
[base] <--- [active]
or,
[base] <--- [snp-1] <--- [active],
[base] <--- [snp-3] <--- [active],
Quoted 8 lines
etc..
TODO: 'stage-2' of live commit functionality, to be able to push down the
active layer.These changes are all reflected in my github repo:
git://github.com/codyprime/qemu-kvm-jtc.gitbranch: jtc-live-commit-1.3-v9
Thanks, applied all to the block branch.
Kevin