提交 b53169ea 编写于 作者: S Stefan Hajnoczi 提交者: Kevin Wolf

blockdev: add sync mode to drive-backup QMP command

The drive-backup command is similar to the drive-mirror command, except
no guest data written after the command executes gets copied.  Add a
sync mode argument which determines whether the entire disk is copied,
just allocated clusters, or only clusters being written to by the guest.

Currently only sync mode 'full' is supported - it copies the entire disk.
For read-only point-in-time snapshots we may only need sync mode 'none'
since the target can be a qcow2 file using the guest's disk as its
backing file (no need to copy the entire disk).  Finally, sync mode
'top' is useful if we wish to preserve the backing chain.

Note that this patch just adds the sync mode argument to drive-backup.
It does not implement sync modes 'top' or 'none'.  This patch is
necessary so we can add a drive-backup HMP command that behaves like the
existing drive-mirror HMP command and takes a sync mode.
Signed-off-by: NStefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: NEric Blake <eblake@redhat.com>
Signed-off-by: NKevin Wolf <kwolf@redhat.com>
上级 c3cb8e77
...@@ -936,6 +936,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) ...@@ -936,6 +936,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
qmp_drive_backup(backup->device, backup->target, qmp_drive_backup(backup->device, backup->target,
backup->has_format, backup->format, backup->has_format, backup->format,
backup->sync,
backup->has_mode, backup->mode, backup->has_mode, backup->mode,
backup->has_speed, backup->speed, backup->has_speed, backup->speed,
backup->has_on_source_error, backup->on_source_error, backup->has_on_source_error, backup->on_source_error,
...@@ -1421,6 +1422,7 @@ void qmp_block_commit(const char *device, ...@@ -1421,6 +1422,7 @@ void qmp_block_commit(const char *device,
void qmp_drive_backup(const char *device, const char *target, void qmp_drive_backup(const char *device, const char *target,
bool has_format, const char *format, bool has_format, const char *format,
enum MirrorSyncMode sync,
bool has_mode, enum NewImageMode mode, bool has_mode, enum NewImageMode mode,
bool has_speed, int64_t speed, bool has_speed, int64_t speed,
bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_source_error, BlockdevOnError on_source_error,
...@@ -1435,6 +1437,10 @@ void qmp_drive_backup(const char *device, const char *target, ...@@ -1435,6 +1437,10 @@ void qmp_drive_backup(const char *device, const char *target,
int64_t size; int64_t size;
int ret; int ret;
if (sync != MIRROR_SYNC_MODE_FULL) {
error_setg(errp, "only sync mode 'full' is currently supported");
return;
}
if (!has_speed) { if (!has_speed) {
speed = 0; speed = 0;
} }
......
...@@ -1634,6 +1634,10 @@ ...@@ -1634,6 +1634,10 @@
# @format: #optional the format of the new destination, default is to # @format: #optional the format of the new destination, default is to
# probe if @mode is 'existing', else the format of the source # probe if @mode is 'existing', else the format of the source
# #
# @sync: what parts of the disk image should be copied to the destination
# (all the disk, only the sectors allocated in the topmost image, or
# only new I/O).
#
# @mode: #optional whether and how QEMU should create a new image, default is # @mode: #optional whether and how QEMU should create a new image, default is
# 'absolute-paths'. # 'absolute-paths'.
# #
...@@ -1655,7 +1659,8 @@ ...@@ -1655,7 +1659,8 @@
## ##
{ 'type': 'DriveBackup', { 'type': 'DriveBackup',
'data': { 'device': 'str', 'target': 'str', '*format': 'str', 'data': { 'device': 'str', 'target': 'str', '*format': 'str',
'*mode': 'NewImageMode', '*speed': 'int', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
'*speed': 'int',
'*on-source-error': 'BlockdevOnError', '*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError' } } '*on-target-error': 'BlockdevOnError' } }
......
...@@ -913,7 +913,7 @@ EQMP ...@@ -913,7 +913,7 @@ EQMP
{ {
.name = "drive-backup", .name = "drive-backup",
.args_type = "device:B,target:s,speed:i?,mode:s?,format:s?," .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
"on-source-error:s?,on-target-error:s?", "on-source-error:s?,on-target-error:s?",
.mhandler.cmd_new = qmp_marshal_input_drive_backup, .mhandler.cmd_new = qmp_marshal_input_drive_backup,
}, },
...@@ -939,6 +939,10 @@ Arguments: ...@@ -939,6 +939,10 @@ Arguments:
- "format": the format of the new destination, default is to probe if 'mode' is - "format": the format of the new destination, default is to probe if 'mode' is
'existing', else the format of the source 'existing', else the format of the source
(json-string, optional) (json-string, optional)
- "sync": what parts of the disk image should be copied to the destination;
possibilities include "full" for all the disk, "top" for only the sectors
allocated in the topmost image, or "none" to only replicate new I/O
(MirrorSyncMode).
- "mode": whether and how QEMU should create a new image - "mode": whether and how QEMU should create a new image
(NewImageMode, optional, default 'absolute-paths') (NewImageMode, optional, default 'absolute-paths')
- "speed": the maximum speed, in bytes per second (json-int, optional) - "speed": the maximum speed, in bytes per second (json-int, optional)
......
...@@ -54,7 +54,7 @@ class TestSingleDrive(iotests.QMPTestCase): ...@@ -54,7 +54,7 @@ class TestSingleDrive(iotests.QMPTestCase):
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-backup', device='drive0', result = self.vm.qmp('drive-backup', device='drive0',
target=target_img) target=target_img, sync='full')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
event = self.cancel_and_wait() event = self.cancel_and_wait()
...@@ -64,7 +64,7 @@ class TestSingleDrive(iotests.QMPTestCase): ...@@ -64,7 +64,7 @@ class TestSingleDrive(iotests.QMPTestCase):
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-backup', device='drive0', result = self.vm.qmp('drive-backup', device='drive0',
target=target_img) target=target_img, sync='full')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
result = self.vm.qmp('block-job-pause', device='drive0') result = self.vm.qmp('block-job-pause', device='drive0')
...@@ -89,17 +89,17 @@ class TestSingleDrive(iotests.QMPTestCase): ...@@ -89,17 +89,17 @@ class TestSingleDrive(iotests.QMPTestCase):
def test_medium_not_found(self): def test_medium_not_found(self):
result = self.vm.qmp('drive-backup', device='ide1-cd0', result = self.vm.qmp('drive-backup', device='ide1-cd0',
target=target_img) target=target_img, sync='full')
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
def test_image_not_found(self): def test_image_not_found(self):
result = self.vm.qmp('drive-backup', device='drive0', result = self.vm.qmp('drive-backup', device='drive0',
mode='existing', target=target_img) target=target_img, sync='full', mode='existing')
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
def test_device_not_found(self): def test_device_not_found(self):
result = self.vm.qmp('drive-backup', device='nonexistent', result = self.vm.qmp('drive-backup', device='nonexistent',
target=target_img) target=target_img, sync='full')
self.assert_qmp(result, 'error/class', 'DeviceNotFound') self.assert_qmp(result, 'error/class', 'DeviceNotFound')
class TestSetSpeed(iotests.QMPTestCase): class TestSetSpeed(iotests.QMPTestCase):
...@@ -119,7 +119,7 @@ class TestSetSpeed(iotests.QMPTestCase): ...@@ -119,7 +119,7 @@ class TestSetSpeed(iotests.QMPTestCase):
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-backup', device='drive0', result = self.vm.qmp('drive-backup', device='drive0',
target=target_img) target=target_img, sync='full')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
# Default speed is 0 # Default speed is 0
...@@ -140,7 +140,7 @@ class TestSetSpeed(iotests.QMPTestCase): ...@@ -140,7 +140,7 @@ class TestSetSpeed(iotests.QMPTestCase):
# Check setting speed in drive-backup works # Check setting speed in drive-backup works
result = self.vm.qmp('drive-backup', device='drive0', result = self.vm.qmp('drive-backup', device='drive0',
target=target_img, speed=4*1024*1024) target=target_img, sync='full', speed=4*1024*1024)
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block-jobs') result = self.vm.qmp('query-block-jobs')
...@@ -154,13 +154,13 @@ class TestSetSpeed(iotests.QMPTestCase): ...@@ -154,13 +154,13 @@ class TestSetSpeed(iotests.QMPTestCase):
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-backup', device='drive0', result = self.vm.qmp('drive-backup', device='drive0',
target=target_img, speed=-1) target=target_img, sync='full', speed=-1)
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-backup', device='drive0', result = self.vm.qmp('drive-backup', device='drive0',
target=target_img) target=target_img, sync='full')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
...@@ -196,7 +196,8 @@ class TestSingleTransaction(iotests.QMPTestCase): ...@@ -196,7 +196,8 @@ class TestSingleTransaction(iotests.QMPTestCase):
result = self.vm.qmp('transaction', actions=[{ result = self.vm.qmp('transaction', actions=[{
'type': 'drive-backup', 'type': 'drive-backup',
'data': { 'device': 'drive0', 'data': { 'device': 'drive0',
'target': target_img }, 'target': target_img,
'sync': 'full' },
} }
]) ])
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
...@@ -210,7 +211,8 @@ class TestSingleTransaction(iotests.QMPTestCase): ...@@ -210,7 +211,8 @@ class TestSingleTransaction(iotests.QMPTestCase):
result = self.vm.qmp('transaction', actions=[{ result = self.vm.qmp('transaction', actions=[{
'type': 'drive-backup', 'type': 'drive-backup',
'data': { 'device': 'drive0', 'data': { 'device': 'drive0',
'target': target_img }, 'target': target_img,
'sync': 'full' },
} }
]) ])
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
...@@ -239,7 +241,8 @@ class TestSingleTransaction(iotests.QMPTestCase): ...@@ -239,7 +241,8 @@ class TestSingleTransaction(iotests.QMPTestCase):
result = self.vm.qmp('transaction', actions=[{ result = self.vm.qmp('transaction', actions=[{
'type': 'drive-backup', 'type': 'drive-backup',
'data': { 'device': 'ide1-cd0', 'data': { 'device': 'ide1-cd0',
'target': target_img }, 'target': target_img,
'sync': 'full' },
} }
]) ])
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
...@@ -249,7 +252,8 @@ class TestSingleTransaction(iotests.QMPTestCase): ...@@ -249,7 +252,8 @@ class TestSingleTransaction(iotests.QMPTestCase):
'type': 'drive-backup', 'type': 'drive-backup',
'data': { 'device': 'drive0', 'data': { 'device': 'drive0',
'mode': 'existing', 'mode': 'existing',
'target': target_img }, 'target': target_img,
'sync': 'full' },
} }
]) ])
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
...@@ -259,7 +263,8 @@ class TestSingleTransaction(iotests.QMPTestCase): ...@@ -259,7 +263,8 @@ class TestSingleTransaction(iotests.QMPTestCase):
'type': 'drive-backup', 'type': 'drive-backup',
'data': { 'device': 'nonexistent', 'data': { 'device': 'nonexistent',
'mode': 'existing', 'mode': 'existing',
'target': target_img }, 'target': target_img,
'sync': 'full' },
} }
]) ])
self.assert_qmp(result, 'error/class', 'DeviceNotFound') self.assert_qmp(result, 'error/class', 'DeviceNotFound')
...@@ -269,7 +274,8 @@ class TestSingleTransaction(iotests.QMPTestCase): ...@@ -269,7 +274,8 @@ class TestSingleTransaction(iotests.QMPTestCase):
'type': 'drive-backup', 'type': 'drive-backup',
'data': { 'device': 'nonexistent', 'data': { 'device': 'nonexistent',
'mode': 'existing', 'mode': 'existing',
'target': target_img }, 'target': target_img,
'sync': 'full' },
}, { }, {
'type': 'Abort', 'type': 'Abort',
'data': {}, 'data': {},
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册