nbd patches for 2017-08-23
- Fam Zheng: 0/4 block: Fix non-shared storage migration - Stefan Hajnoczi: qemu-iotests: add 194 non-shared storage migration test - Stefan Hajnoczi: nbd-client: avoid spurious qio_channel_yield() re-entry -----BEGIN PGP SIGNATURE----- Comment: Public key at http://people.redhat.com/eblake/eblake.gpg iQEcBAABCAAGBQJZnavdAAoJEKeha0olJ0NqtuEH/javGzZzPu7vsxwpifJkP8mD W4WMKmFL2a+RDA7zd31w/6yPXIAviHwa4ahsEvxysmn3TpxOSnLOR8qKAkkjJH6E duERzk+270rt/7lB5gtalyw2AROfps6NjbYZeo7SLg9vB9DRQX7PUG8ob0DbNWK4 vJrrai57VODprGUGRaiOBxShP9nWTaauQbyhu+S13vhUASYb7XqtTJEfpcM5XwqG iH5jLPAkXQNv3Ltux4P0MPEuP8zkrgcUs3p6L7+s0h76IJsT8YOmTwJQG//HxxRo LcWb/hGyr1NkfWk4Im8f/Dh7dxlEdotGRdd/zxrEEXtBm/WmoM7jjAZe/Con7jQ= =sJy0 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2017-08-23' into staging nbd patches for 2017-08-23 - Fam Zheng: 0/4 block: Fix non-shared storage migration - Stefan Hajnoczi: qemu-iotests: add 194 non-shared storage migration test - Stefan Hajnoczi: nbd-client: avoid spurious qio_channel_yield() re-entry # gpg: Signature made Wed 23 Aug 2017 17:22:53 BST # gpg: using RSA key 0xA7A16B4A2527436A # gpg: Good signature from "Eric Blake <eblake@redhat.com>" # gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" # gpg: aka "[jpeg image of size 6874]" # Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A * remotes/ericb/tags/pull-nbd-2017-08-23: nbd-client: avoid spurious qio_channel_yield() re-entry qemu-iotests: add 194 non-shared storage migration test block: Update open_flags after ->inactivate() callback mirror: Mark target BB as "force allow inactivate" block-backend: Allow more "can inactivate" cases block-backend: Refactor inactivate check Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1eed33994e
7
block.c
7
block.c
|
@ -4085,21 +4085,20 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setting_flag) {
|
if (setting_flag && !(bs->open_flags & BDRV_O_INACTIVE)) {
|
||||||
uint64_t perm, shared_perm;
|
uint64_t perm, shared_perm;
|
||||||
|
|
||||||
bs->open_flags |= BDRV_O_INACTIVE;
|
|
||||||
|
|
||||||
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
||||||
if (parent->role->inactivate) {
|
if (parent->role->inactivate) {
|
||||||
ret = parent->role->inactivate(parent);
|
ret = parent->role->inactivate(parent);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bs->open_flags &= ~BDRV_O_INACTIVE;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bs->open_flags |= BDRV_O_INACTIVE;
|
||||||
|
|
||||||
/* Update permissions, they may differ for inactive nodes */
|
/* Update permissions, they may differ for inactive nodes */
|
||||||
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||||
bdrv_check_perm(bs, perm, shared_perm, NULL, &error_abort);
|
bdrv_check_perm(bs, perm, shared_perm, NULL, &error_abort);
|
||||||
|
|
|
@ -70,6 +70,7 @@ struct BlockBackend {
|
||||||
|
|
||||||
int quiesce_counter;
|
int quiesce_counter;
|
||||||
VMChangeStateEntry *vmsh;
|
VMChangeStateEntry *vmsh;
|
||||||
|
bool force_allow_inactivate;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct BlockBackendAIOCB {
|
typedef struct BlockBackendAIOCB {
|
||||||
|
@ -192,6 +193,30 @@ static void blk_root_activate(BdrvChild *child, Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void blk_set_force_allow_inactivate(BlockBackend *blk)
|
||||||
|
{
|
||||||
|
blk->force_allow_inactivate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool blk_can_inactivate(BlockBackend *blk)
|
||||||
|
{
|
||||||
|
/* If it is a guest device, inactivate is ok. */
|
||||||
|
if (blk->dev || blk_name(blk)[0]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inactivating means no more writes to the image can be done,
|
||||||
|
* even if those writes would be changes invisible to the
|
||||||
|
* guest. For block job BBs that satisfy this, we can just allow
|
||||||
|
* it. This is the case for mirror job source, which is required
|
||||||
|
* by libvirt non-shared block migration. */
|
||||||
|
if (!(blk->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return blk->force_allow_inactivate;
|
||||||
|
}
|
||||||
|
|
||||||
static int blk_root_inactivate(BdrvChild *child)
|
static int blk_root_inactivate(BdrvChild *child)
|
||||||
{
|
{
|
||||||
BlockBackend *blk = child->opaque;
|
BlockBackend *blk = child->opaque;
|
||||||
|
@ -200,11 +225,7 @@ static int blk_root_inactivate(BdrvChild *child)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only inactivate BlockBackends for guest devices (which are inactive at
|
if (!blk_can_inactivate(blk)) {
|
||||||
* this point because the VM is stopped) and unattached monitor-owned
|
|
||||||
* BlockBackends. If there is still any other user like a block job, then
|
|
||||||
* we simply can't inactivate the image. */
|
|
||||||
if (!blk->dev && !blk_name(blk)[0]) {
|
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1134,6 +1134,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||||
const BlockJobDriver *driver,
|
const BlockJobDriver *driver,
|
||||||
bool is_none_mode, BlockDriverState *base,
|
bool is_none_mode, BlockDriverState *base,
|
||||||
bool auto_complete, const char *filter_node_name,
|
bool auto_complete, const char *filter_node_name,
|
||||||
|
bool is_mirror,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s;
|
MirrorBlockJob *s;
|
||||||
|
@ -1222,6 +1223,15 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
if (is_mirror) {
|
||||||
|
/* XXX: Mirror target could be a NBD server of target QEMU in the case
|
||||||
|
* of non-shared block migration. To allow migration completion, we
|
||||||
|
* have to allow "inactivate" of the target BB. When that happens, we
|
||||||
|
* know the job is drained, and the vcpus are stopped, so no write
|
||||||
|
* operation will be performed. Block layer already has assertions to
|
||||||
|
* ensure that. */
|
||||||
|
blk_set_force_allow_inactivate(s->target);
|
||||||
|
}
|
||||||
|
|
||||||
s->replaces = g_strdup(replaces);
|
s->replaces = g_strdup(replaces);
|
||||||
s->on_source_error = on_source_error;
|
s->on_source_error = on_source_error;
|
||||||
|
@ -1306,7 +1316,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||||
speed, granularity, buf_size, backing_mode,
|
speed, granularity, buf_size, backing_mode,
|
||||||
on_source_error, on_target_error, unmap, NULL, NULL,
|
on_source_error, on_target_error, unmap, NULL, NULL,
|
||||||
&mirror_job_driver, is_none_mode, base, false,
|
&mirror_job_driver, is_none_mode, base, false,
|
||||||
filter_node_name, errp);
|
filter_node_name, true, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void commit_active_start(const char *job_id, BlockDriverState *bs,
|
void commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||||
|
@ -1329,7 +1339,7 @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||||
MIRROR_LEAVE_BACKING_CHAIN,
|
MIRROR_LEAVE_BACKING_CHAIN,
|
||||||
on_error, on_error, true, cb, opaque,
|
on_error, on_error, true, cb, opaque,
|
||||||
&commit_active_job_driver, false, base, auto_complete,
|
&commit_active_job_driver, false, base, auto_complete,
|
||||||
filter_node_name, &local_err);
|
filter_node_name, false, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto error_restore_flags;
|
goto error_restore_flags;
|
||||||
|
|
|
@ -39,8 +39,10 @@ static void nbd_recv_coroutines_enter_all(NBDClientSession *s)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||||
if (s->recv_coroutine[i]) {
|
NBDClientRequest *req = &s->requests[i];
|
||||||
aio_co_wake(s->recv_coroutine[i]);
|
|
||||||
|
if (req->coroutine && req->receiving) {
|
||||||
|
aio_co_wake(req->coroutine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,28 +90,28 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
|
||||||
* one coroutine is called until the reply finishes.
|
* one coroutine is called until the reply finishes.
|
||||||
*/
|
*/
|
||||||
i = HANDLE_TO_INDEX(s, s->reply.handle);
|
i = HANDLE_TO_INDEX(s, s->reply.handle);
|
||||||
if (i >= MAX_NBD_REQUESTS || !s->recv_coroutine[i]) {
|
if (i >= MAX_NBD_REQUESTS ||
|
||||||
|
!s->requests[i].coroutine ||
|
||||||
|
!s->requests[i].receiving) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We're woken up by the recv_coroutine itself. Note that there
|
/* We're woken up again by the request itself. Note that there
|
||||||
* is no race between yielding and reentering read_reply_co. This
|
* is no race between yielding and reentering read_reply_co. This
|
||||||
* is because:
|
* is because:
|
||||||
*
|
*
|
||||||
* - if recv_coroutine[i] runs on the same AioContext, it is only
|
* - if the request runs on the same AioContext, it is only
|
||||||
* entered after we yield
|
* entered after we yield
|
||||||
*
|
*
|
||||||
* - if recv_coroutine[i] runs on a different AioContext, reentering
|
* - if the request runs on a different AioContext, reentering
|
||||||
* read_reply_co happens through a bottom half, which can only
|
* read_reply_co happens through a bottom half, which can only
|
||||||
* run after we yield.
|
* run after we yield.
|
||||||
*/
|
*/
|
||||||
aio_co_wake(s->recv_coroutine[i]);
|
aio_co_wake(s->requests[i].coroutine);
|
||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
s->quit = true;
|
||||||
s->quit = true;
|
|
||||||
}
|
|
||||||
nbd_recv_coroutines_enter_all(s);
|
nbd_recv_coroutines_enter_all(s);
|
||||||
s->read_reply_co = NULL;
|
s->read_reply_co = NULL;
|
||||||
}
|
}
|
||||||
|
@ -128,14 +130,17 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||||
s->in_flight++;
|
s->in_flight++;
|
||||||
|
|
||||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||||
if (s->recv_coroutine[i] == NULL) {
|
if (s->requests[i].coroutine == NULL) {
|
||||||
s->recv_coroutine[i] = qemu_coroutine_self();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_assert(qemu_in_coroutine());
|
g_assert(qemu_in_coroutine());
|
||||||
assert(i < MAX_NBD_REQUESTS);
|
assert(i < MAX_NBD_REQUESTS);
|
||||||
|
|
||||||
|
s->requests[i].coroutine = qemu_coroutine_self();
|
||||||
|
s->requests[i].receiving = false;
|
||||||
|
|
||||||
request->handle = INDEX_TO_HANDLE(s, i);
|
request->handle = INDEX_TO_HANDLE(s, i);
|
||||||
|
|
||||||
if (s->quit) {
|
if (s->quit) {
|
||||||
|
@ -173,10 +178,13 @@ static void nbd_co_receive_reply(NBDClientSession *s,
|
||||||
NBDReply *reply,
|
NBDReply *reply,
|
||||||
QEMUIOVector *qiov)
|
QEMUIOVector *qiov)
|
||||||
{
|
{
|
||||||
|
int i = HANDLE_TO_INDEX(s, request->handle);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Wait until we're woken up by nbd_read_reply_entry. */
|
/* Wait until we're woken up by nbd_read_reply_entry. */
|
||||||
|
s->requests[i].receiving = true;
|
||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
|
s->requests[i].receiving = false;
|
||||||
*reply = s->reply;
|
*reply = s->reply;
|
||||||
if (reply->handle != request->handle || !s->ioc || s->quit) {
|
if (reply->handle != request->handle || !s->ioc || s->quit) {
|
||||||
reply->error = EIO;
|
reply->error = EIO;
|
||||||
|
@ -186,6 +194,7 @@ static void nbd_co_receive_reply(NBDClientSession *s,
|
||||||
NULL);
|
NULL);
|
||||||
if (ret != request->len) {
|
if (ret != request->len) {
|
||||||
reply->error = EIO;
|
reply->error = EIO;
|
||||||
|
s->quit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +209,7 @@ static void nbd_coroutine_end(BlockDriverState *bs,
|
||||||
NBDClientSession *s = nbd_get_client_session(bs);
|
NBDClientSession *s = nbd_get_client_session(bs);
|
||||||
int i = HANDLE_TO_INDEX(s, request->handle);
|
int i = HANDLE_TO_INDEX(s, request->handle);
|
||||||
|
|
||||||
s->recv_coroutine[i] = NULL;
|
s->requests[i].coroutine = NULL;
|
||||||
|
|
||||||
/* Kick the read_reply_co to get the next reply. */
|
/* Kick the read_reply_co to get the next reply. */
|
||||||
if (s->read_reply_co) {
|
if (s->read_reply_co) {
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
|
|
||||||
#define MAX_NBD_REQUESTS 16
|
#define MAX_NBD_REQUESTS 16
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Coroutine *coroutine;
|
||||||
|
bool receiving; /* waiting for read_reply_co? */
|
||||||
|
} NBDClientRequest;
|
||||||
|
|
||||||
typedef struct NBDClientSession {
|
typedef struct NBDClientSession {
|
||||||
QIOChannelSocket *sioc; /* The master data channel */
|
QIOChannelSocket *sioc; /* The master data channel */
|
||||||
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
|
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
|
||||||
|
@ -27,7 +32,7 @@ typedef struct NBDClientSession {
|
||||||
Coroutine *read_reply_co;
|
Coroutine *read_reply_co;
|
||||||
int in_flight;
|
int in_flight;
|
||||||
|
|
||||||
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
|
NBDClientRequest requests[MAX_NBD_REQUESTS];
|
||||||
NBDReply reply;
|
NBDReply reply;
|
||||||
bool quit;
|
bool quit;
|
||||||
} NBDClientSession;
|
} NBDClientSession;
|
||||||
|
|
|
@ -241,5 +241,6 @@ void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg);
|
||||||
void blk_io_limits_disable(BlockBackend *blk);
|
void blk_io_limits_disable(BlockBackend *blk);
|
||||||
void blk_io_limits_enable(BlockBackend *blk, const char *group);
|
void blk_io_limits_enable(BlockBackend *blk, const char *group);
|
||||||
void blk_io_limits_update_group(BlockBackend *blk, const char *group);
|
void blk_io_limits_update_group(BlockBackend *blk, const char *group);
|
||||||
|
void blk_set_force_allow_inactivate(BlockBackend *blk);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 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/>.
|
||||||
|
#
|
||||||
|
# Creator/Owner: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
#
|
||||||
|
# Non-shared storage migration test using NBD server and drive-mirror
|
||||||
|
|
||||||
|
import os
|
||||||
|
import atexit
|
||||||
|
import iotests
|
||||||
|
|
||||||
|
iotests.verify_platform(['linux'])
|
||||||
|
|
||||||
|
img_size = '1G'
|
||||||
|
source_img_path = os.path.join(iotests.test_dir, 'source.img')
|
||||||
|
dest_img_path = os.path.join(iotests.test_dir, 'dest.img')
|
||||||
|
iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, source_img_path, img_size)
|
||||||
|
iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, dest_img_path, img_size)
|
||||||
|
|
||||||
|
iotests.log('Launching VMs...')
|
||||||
|
migration_sock_path = os.path.join(iotests.test_dir, 'migration.sock')
|
||||||
|
nbd_sock_path = os.path.join(iotests.test_dir, 'nbd.sock')
|
||||||
|
source_vm = iotests.VM('source').add_drive(source_img_path)
|
||||||
|
dest_vm = (iotests.VM('dest').add_drive(dest_img_path)
|
||||||
|
.add_incoming('unix:{0}'.format(migration_sock_path)))
|
||||||
|
source_vm.launch()
|
||||||
|
atexit.register(source_vm.shutdown)
|
||||||
|
dest_vm.launch()
|
||||||
|
atexit.register(dest_vm.shutdown)
|
||||||
|
|
||||||
|
iotests.log('Launching NBD server on destination...')
|
||||||
|
iotests.log(dest_vm.qmp('nbd-server-start', addr={'type': 'unix', 'data': {'path': nbd_sock_path}}))
|
||||||
|
iotests.log(dest_vm.qmp('nbd-server-add', device='drive0', writable=True))
|
||||||
|
|
||||||
|
iotests.log('Starting drive-mirror on source...')
|
||||||
|
iotests.log(source_vm.qmp(
|
||||||
|
'drive-mirror',
|
||||||
|
device='drive0',
|
||||||
|
target='nbd+unix:///drive0?socket={0}'.format(nbd_sock_path),
|
||||||
|
sync='full',
|
||||||
|
format='raw', # always raw, the server handles the format
|
||||||
|
mode='existing'))
|
||||||
|
|
||||||
|
iotests.log('Waiting for drive-mirror to complete...')
|
||||||
|
iotests.log(source_vm.event_wait('BLOCK_JOB_READY'),
|
||||||
|
filters=[iotests.filter_qmp_event])
|
||||||
|
|
||||||
|
iotests.log('Starting migration...')
|
||||||
|
source_vm.qmp('migrate-set-capabilities',
|
||||||
|
capabilities=[{'capability': 'events', 'state': True}])
|
||||||
|
dest_vm.qmp('migrate-set-capabilities',
|
||||||
|
capabilities=[{'capability': 'events', 'state': True}])
|
||||||
|
iotests.log(source_vm.qmp('migrate', uri='unix:{0}'.format(migration_sock_path)))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
event = source_vm.event_wait('MIGRATION')
|
||||||
|
iotests.log(event, filters=[iotests.filter_qmp_event])
|
||||||
|
if event['data']['status'] in ('completed', 'failed'):
|
||||||
|
break
|
|
@ -0,0 +1,13 @@
|
||||||
|
Launching VMs...
|
||||||
|
Launching NBD server on destination...
|
||||||
|
{u'return': {}}
|
||||||
|
{u'return': {}}
|
||||||
|
Starting drive-mirror on source...
|
||||||
|
{u'return': {}}
|
||||||
|
Waiting for drive-mirror to complete...
|
||||||
|
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'drive0', u'type': u'mirror', u'speed': 0, u'len': 1073741824, u'offset': 1073741824}, u'event': u'BLOCK_JOB_READY'}
|
||||||
|
Starting migration...
|
||||||
|
{u'return': {}}
|
||||||
|
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'}
|
||||||
|
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'}
|
||||||
|
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'}
|
|
@ -187,3 +187,4 @@
|
||||||
189 rw auto
|
189 rw auto
|
||||||
190 rw auto quick
|
190 rw auto quick
|
||||||
192 rw auto quick
|
192 rw auto quick
|
||||||
|
194 rw auto migration quick
|
||||||
|
|
|
@ -133,6 +133,14 @@ chown_re = re.compile(r"chown [0-9]+:[0-9]+")
|
||||||
def filter_chown(msg):
|
def filter_chown(msg):
|
||||||
return chown_re.sub("chown UID:GID", msg)
|
return chown_re.sub("chown UID:GID", msg)
|
||||||
|
|
||||||
|
def filter_qmp_event(event):
|
||||||
|
'''Filter a QMP event dict'''
|
||||||
|
event = dict(event)
|
||||||
|
if 'timestamp' in event:
|
||||||
|
event['timestamp']['seconds'] = 'SECS'
|
||||||
|
event['timestamp']['microseconds'] = 'USECS'
|
||||||
|
return event
|
||||||
|
|
||||||
def log(msg, filters=[]):
|
def log(msg, filters=[]):
|
||||||
for flt in filters:
|
for flt in filters:
|
||||||
msg = flt(msg)
|
msg = flt(msg)
|
||||||
|
@ -200,6 +208,11 @@ class VM(qtest.QEMUQtestMachine):
|
||||||
self._args.append(','.join(opts))
|
self._args.append(','.join(opts))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def add_incoming(self, addr):
|
||||||
|
self._args.append('-incoming')
|
||||||
|
self._args.append(addr)
|
||||||
|
return self
|
||||||
|
|
||||||
def pause_drive(self, drive, event=None):
|
def pause_drive(self, drive, event=None):
|
||||||
'''Pause drive r/w operations'''
|
'''Pause drive r/w operations'''
|
||||||
if not event:
|
if not event:
|
||||||
|
|
Loading…
Reference in New Issue