mirror of https://github.com/xqemu/xqemu.git
Merge remote-tracking branch 'kwolf/for-anthony' into staging
# By Stefan Hajnoczi (6) and others # Via Kevin Wolf * kwolf/for-anthony: block: dump snapshot and image info to specified output block: move qmp and info dump related code to block/qapi.c block: move snapshot code in block.c to block/snapshot.c block: drop bs_snapshots global variable qemu-iotests: make create_image() common qemu-iotests: make compare_images() common qemu-iotests: make cancel_and_wait() common qemu-iotests: make assert_no_active_block_jobs() common block: add block driver read only whitelist qemu-iotests: fix 054 cluster size help output Message-id: 1370349940-4703-1-git-send-email-kwolf@redhat.com Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
a341619744
356
block.c
356
block.c
|
@ -99,9 +99,6 @@ static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
|
||||||
static QLIST_HEAD(, BlockDriver) bdrv_drivers =
|
static QLIST_HEAD(, BlockDriver) bdrv_drivers =
|
||||||
QLIST_HEAD_INITIALIZER(bdrv_drivers);
|
QLIST_HEAD_INITIALIZER(bdrv_drivers);
|
||||||
|
|
||||||
/* The device to use for VM snapshots */
|
|
||||||
static BlockDriverState *bs_snapshots;
|
|
||||||
|
|
||||||
/* If non-zero, use only whitelisted block drivers */
|
/* If non-zero, use only whitelisted block drivers */
|
||||||
static int use_bdrv_whitelist;
|
static int use_bdrv_whitelist;
|
||||||
|
|
||||||
|
@ -328,28 +325,40 @@ BlockDriver *bdrv_find_format(const char *format_name)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bdrv_is_whitelisted(BlockDriver *drv)
|
static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
|
||||||
{
|
{
|
||||||
static const char *whitelist[] = {
|
static const char *whitelist_rw[] = {
|
||||||
CONFIG_BDRV_WHITELIST
|
CONFIG_BDRV_RW_WHITELIST
|
||||||
|
};
|
||||||
|
static const char *whitelist_ro[] = {
|
||||||
|
CONFIG_BDRV_RO_WHITELIST
|
||||||
};
|
};
|
||||||
const char **p;
|
const char **p;
|
||||||
|
|
||||||
if (!whitelist[0])
|
if (!whitelist_rw[0] && !whitelist_ro[0]) {
|
||||||
return 1; /* no whitelist, anything goes */
|
return 1; /* no whitelist, anything goes */
|
||||||
|
}
|
||||||
|
|
||||||
for (p = whitelist; *p; p++) {
|
for (p = whitelist_rw; *p; p++) {
|
||||||
if (!strcmp(drv->format_name, *p)) {
|
if (!strcmp(drv->format_name, *p)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (read_only) {
|
||||||
|
for (p = whitelist_ro; *p; p++) {
|
||||||
|
if (!strcmp(drv->format_name, *p)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockDriver *bdrv_find_whitelisted_format(const char *format_name)
|
BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
|
||||||
|
bool read_only)
|
||||||
{
|
{
|
||||||
BlockDriver *drv = bdrv_find_format(format_name);
|
BlockDriver *drv = bdrv_find_format(format_name);
|
||||||
return drv && bdrv_is_whitelisted(drv) ? drv : NULL;
|
return drv && bdrv_is_whitelisted(drv, read_only) ? drv : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct CreateCo {
|
typedef struct CreateCo {
|
||||||
|
@ -684,10 +693,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||||
|
|
||||||
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
|
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
|
||||||
|
|
||||||
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* bdrv_open() with directly using a protocol as drv. This layer is already
|
/* bdrv_open() with directly using a protocol as drv. This layer is already
|
||||||
* opened, so assign it to bs (while file becomes a closed BlockDriverState)
|
* opened, so assign it to bs (while file becomes a closed BlockDriverState)
|
||||||
* and return immediately. */
|
* and return immediately. */
|
||||||
|
@ -698,9 +703,15 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||||
|
|
||||||
bs->open_flags = flags;
|
bs->open_flags = flags;
|
||||||
bs->buffer_alignment = 512;
|
bs->buffer_alignment = 512;
|
||||||
|
open_flags = bdrv_open_flags(bs, flags);
|
||||||
|
bs->read_only = !(open_flags & BDRV_O_RDWR);
|
||||||
|
|
||||||
|
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
|
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
|
||||||
if ((flags & BDRV_O_RDWR) && (flags & BDRV_O_COPY_ON_READ)) {
|
if (!bs->read_only && (flags & BDRV_O_COPY_ON_READ)) {
|
||||||
bdrv_enable_copy_on_read(bs);
|
bdrv_enable_copy_on_read(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,9 +725,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||||
bs->opaque = g_malloc0(drv->instance_size);
|
bs->opaque = g_malloc0(drv->instance_size);
|
||||||
|
|
||||||
bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
|
bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
|
||||||
open_flags = bdrv_open_flags(bs, flags);
|
|
||||||
|
|
||||||
bs->read_only = !(open_flags & BDRV_O_RDWR);
|
|
||||||
|
|
||||||
/* Open the image, either directly or using a protocol */
|
/* Open the image, either directly or using a protocol */
|
||||||
if (drv->bdrv_file_open) {
|
if (drv->bdrv_file_open) {
|
||||||
|
@ -801,7 +809,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
||||||
/* Find the right block driver */
|
/* Find the right block driver */
|
||||||
drvname = qdict_get_try_str(options, "driver");
|
drvname = qdict_get_try_str(options, "driver");
|
||||||
if (drvname) {
|
if (drvname) {
|
||||||
drv = bdrv_find_whitelisted_format(drvname);
|
drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR));
|
||||||
qdict_del(options, "driver");
|
qdict_del(options, "driver");
|
||||||
} else if (filename) {
|
} else if (filename) {
|
||||||
drv = bdrv_find_protocol(filename);
|
drv = bdrv_find_protocol(filename);
|
||||||
|
@ -1357,9 +1365,6 @@ void bdrv_close(BlockDriverState *bs)
|
||||||
notifier_list_notify(&bs->close_notifiers, bs);
|
notifier_list_notify(&bs->close_notifiers, bs);
|
||||||
|
|
||||||
if (bs->drv) {
|
if (bs->drv) {
|
||||||
if (bs == bs_snapshots) {
|
|
||||||
bs_snapshots = NULL;
|
|
||||||
}
|
|
||||||
if (bs->backing_hd) {
|
if (bs->backing_hd) {
|
||||||
bdrv_delete(bs->backing_hd);
|
bdrv_delete(bs->backing_hd);
|
||||||
bs->backing_hd = NULL;
|
bs->backing_hd = NULL;
|
||||||
|
@ -1591,7 +1596,6 @@ void bdrv_delete(BlockDriverState *bs)
|
||||||
|
|
||||||
bdrv_close(bs);
|
bdrv_close(bs);
|
||||||
|
|
||||||
assert(bs != bs_snapshots);
|
|
||||||
g_free(bs);
|
g_free(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1635,9 +1639,6 @@ void bdrv_set_dev_ops(BlockDriverState *bs, const BlockDevOps *ops,
|
||||||
{
|
{
|
||||||
bs->dev_ops = ops;
|
bs->dev_ops = ops;
|
||||||
bs->dev_opaque = opaque;
|
bs->dev_opaque = opaque;
|
||||||
if (bdrv_dev_has_removable_media(bs) && bs == bs_snapshots) {
|
|
||||||
bs_snapshots = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv,
|
void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv,
|
||||||
|
@ -3099,128 +3100,6 @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
||||||
return data.ret;
|
return data.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockInfo *bdrv_query_info(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
BlockInfo *info = g_malloc0(sizeof(*info));
|
|
||||||
info->device = g_strdup(bs->device_name);
|
|
||||||
info->type = g_strdup("unknown");
|
|
||||||
info->locked = bdrv_dev_is_medium_locked(bs);
|
|
||||||
info->removable = bdrv_dev_has_removable_media(bs);
|
|
||||||
|
|
||||||
if (bdrv_dev_has_removable_media(bs)) {
|
|
||||||
info->has_tray_open = true;
|
|
||||||
info->tray_open = bdrv_dev_is_tray_open(bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bdrv_iostatus_is_enabled(bs)) {
|
|
||||||
info->has_io_status = true;
|
|
||||||
info->io_status = bs->iostatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bs->dirty_bitmap) {
|
|
||||||
info->has_dirty = true;
|
|
||||||
info->dirty = g_malloc0(sizeof(*info->dirty));
|
|
||||||
info->dirty->count = bdrv_get_dirty_count(bs) * BDRV_SECTOR_SIZE;
|
|
||||||
info->dirty->granularity =
|
|
||||||
((int64_t) BDRV_SECTOR_SIZE << hbitmap_granularity(bs->dirty_bitmap));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bs->drv) {
|
|
||||||
info->has_inserted = true;
|
|
||||||
info->inserted = g_malloc0(sizeof(*info->inserted));
|
|
||||||
info->inserted->file = g_strdup(bs->filename);
|
|
||||||
info->inserted->ro = bs->read_only;
|
|
||||||
info->inserted->drv = g_strdup(bs->drv->format_name);
|
|
||||||
info->inserted->encrypted = bs->encrypted;
|
|
||||||
info->inserted->encryption_key_missing = bdrv_key_required(bs);
|
|
||||||
|
|
||||||
if (bs->backing_file[0]) {
|
|
||||||
info->inserted->has_backing_file = true;
|
|
||||||
info->inserted->backing_file = g_strdup(bs->backing_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
|
||||||
|
|
||||||
if (bs->io_limits_enabled) {
|
|
||||||
info->inserted->bps =
|
|
||||||
bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL];
|
|
||||||
info->inserted->bps_rd =
|
|
||||||
bs->io_limits.bps[BLOCK_IO_LIMIT_READ];
|
|
||||||
info->inserted->bps_wr =
|
|
||||||
bs->io_limits.bps[BLOCK_IO_LIMIT_WRITE];
|
|
||||||
info->inserted->iops =
|
|
||||||
bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL];
|
|
||||||
info->inserted->iops_rd =
|
|
||||||
bs->io_limits.iops[BLOCK_IO_LIMIT_READ];
|
|
||||||
info->inserted->iops_wr =
|
|
||||||
bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockInfoList *qmp_query_block(Error **errp)
|
|
||||||
{
|
|
||||||
BlockInfoList *head = NULL, **p_next = &head;
|
|
||||||
BlockDriverState *bs;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
|
||||||
BlockInfoList *info = g_malloc0(sizeof(*info));
|
|
||||||
info->value = bdrv_query_info(bs);
|
|
||||||
|
|
||||||
*p_next = info;
|
|
||||||
p_next = &info->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
BlockStats *s;
|
|
||||||
|
|
||||||
s = g_malloc0(sizeof(*s));
|
|
||||||
|
|
||||||
if (bs->device_name[0]) {
|
|
||||||
s->has_device = true;
|
|
||||||
s->device = g_strdup(bs->device_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
s->stats = g_malloc0(sizeof(*s->stats));
|
|
||||||
s->stats->rd_bytes = bs->nr_bytes[BDRV_ACCT_READ];
|
|
||||||
s->stats->wr_bytes = bs->nr_bytes[BDRV_ACCT_WRITE];
|
|
||||||
s->stats->rd_operations = bs->nr_ops[BDRV_ACCT_READ];
|
|
||||||
s->stats->wr_operations = bs->nr_ops[BDRV_ACCT_WRITE];
|
|
||||||
s->stats->wr_highest_offset = bs->wr_highest_sector * BDRV_SECTOR_SIZE;
|
|
||||||
s->stats->flush_operations = bs->nr_ops[BDRV_ACCT_FLUSH];
|
|
||||||
s->stats->wr_total_time_ns = bs->total_time_ns[BDRV_ACCT_WRITE];
|
|
||||||
s->stats->rd_total_time_ns = bs->total_time_ns[BDRV_ACCT_READ];
|
|
||||||
s->stats->flush_total_time_ns = bs->total_time_ns[BDRV_ACCT_FLUSH];
|
|
||||||
|
|
||||||
if (bs->file) {
|
|
||||||
s->has_parent = true;
|
|
||||||
s->parent = bdrv_query_stats(bs->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockStatsList *qmp_query_blockstats(Error **errp)
|
|
||||||
{
|
|
||||||
BlockStatsList *head = NULL, **p_next = &head;
|
|
||||||
BlockDriverState *bs;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
|
||||||
BlockStatsList *info = g_malloc0(sizeof(*info));
|
|
||||||
info->value = bdrv_query_stats(bs);
|
|
||||||
|
|
||||||
*p_next = info;
|
|
||||||
p_next = &info->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
|
const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
if (bs->backing_hd && bs->backing_hd->encrypted)
|
if (bs->backing_hd && bs->backing_hd->encrypted)
|
||||||
|
@ -3356,129 +3235,11 @@ bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************/
|
|
||||||
/* handling of snapshots */
|
|
||||||
|
|
||||||
int bdrv_can_snapshot(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
BlockDriver *drv = bs->drv;
|
|
||||||
if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!drv->bdrv_snapshot_create) {
|
|
||||||
if (bs->file != NULL) {
|
|
||||||
return bdrv_can_snapshot(bs->file);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bdrv_is_snapshot(BlockDriverState *bs)
|
int bdrv_is_snapshot(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return !!(bs->open_flags & BDRV_O_SNAPSHOT);
|
return !!(bs->open_flags & BDRV_O_SNAPSHOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockDriverState *bdrv_snapshots(void)
|
|
||||||
{
|
|
||||||
BlockDriverState *bs;
|
|
||||||
|
|
||||||
if (bs_snapshots) {
|
|
||||||
return bs_snapshots;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs = NULL;
|
|
||||||
while ((bs = bdrv_next(bs))) {
|
|
||||||
if (bdrv_can_snapshot(bs)) {
|
|
||||||
bs_snapshots = bs;
|
|
||||||
return bs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bdrv_snapshot_create(BlockDriverState *bs,
|
|
||||||
QEMUSnapshotInfo *sn_info)
|
|
||||||
{
|
|
||||||
BlockDriver *drv = bs->drv;
|
|
||||||
if (!drv)
|
|
||||||
return -ENOMEDIUM;
|
|
||||||
if (drv->bdrv_snapshot_create)
|
|
||||||
return drv->bdrv_snapshot_create(bs, sn_info);
|
|
||||||
if (bs->file)
|
|
||||||
return bdrv_snapshot_create(bs->file, sn_info);
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bdrv_snapshot_goto(BlockDriverState *bs,
|
|
||||||
const char *snapshot_id)
|
|
||||||
{
|
|
||||||
BlockDriver *drv = bs->drv;
|
|
||||||
int ret, open_ret;
|
|
||||||
|
|
||||||
if (!drv)
|
|
||||||
return -ENOMEDIUM;
|
|
||||||
if (drv->bdrv_snapshot_goto)
|
|
||||||
return drv->bdrv_snapshot_goto(bs, snapshot_id);
|
|
||||||
|
|
||||||
if (bs->file) {
|
|
||||||
drv->bdrv_close(bs);
|
|
||||||
ret = bdrv_snapshot_goto(bs->file, snapshot_id);
|
|
||||||
open_ret = drv->bdrv_open(bs, NULL, bs->open_flags);
|
|
||||||
if (open_ret < 0) {
|
|
||||||
bdrv_delete(bs->file);
|
|
||||||
bs->drv = NULL;
|
|
||||||
return open_ret;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
|
||||||
{
|
|
||||||
BlockDriver *drv = bs->drv;
|
|
||||||
if (!drv)
|
|
||||||
return -ENOMEDIUM;
|
|
||||||
if (drv->bdrv_snapshot_delete)
|
|
||||||
return drv->bdrv_snapshot_delete(bs, snapshot_id);
|
|
||||||
if (bs->file)
|
|
||||||
return bdrv_snapshot_delete(bs->file, snapshot_id);
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bdrv_snapshot_list(BlockDriverState *bs,
|
|
||||||
QEMUSnapshotInfo **psn_info)
|
|
||||||
{
|
|
||||||
BlockDriver *drv = bs->drv;
|
|
||||||
if (!drv)
|
|
||||||
return -ENOMEDIUM;
|
|
||||||
if (drv->bdrv_snapshot_list)
|
|
||||||
return drv->bdrv_snapshot_list(bs, psn_info);
|
|
||||||
if (bs->file)
|
|
||||||
return bdrv_snapshot_list(bs->file, psn_info);
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
|
||||||
const char *snapshot_name)
|
|
||||||
{
|
|
||||||
BlockDriver *drv = bs->drv;
|
|
||||||
if (!drv) {
|
|
||||||
return -ENOMEDIUM;
|
|
||||||
}
|
|
||||||
if (!bs->read_only) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
if (drv->bdrv_snapshot_load_tmp) {
|
|
||||||
return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
|
|
||||||
}
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* backing_file can either be relative, or absolute, or a protocol. If it is
|
/* backing_file can either be relative, or absolute, or a protocol. If it is
|
||||||
* relative, it must be relative to the chain. So, passing in bs->filename
|
* relative, it must be relative to the chain. So, passing in bs->filename
|
||||||
* from a BDS as backing_file should not be done, as that may be relative to
|
* from a BDS as backing_file should not be done, as that may be relative to
|
||||||
|
@ -3574,69 +3335,6 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
|
||||||
return curr_bs;
|
return curr_bs;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NB_SUFFIXES 4
|
|
||||||
|
|
||||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size)
|
|
||||||
{
|
|
||||||
static const char suffixes[NB_SUFFIXES] = "KMGT";
|
|
||||||
int64_t base;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (size <= 999) {
|
|
||||||
snprintf(buf, buf_size, "%" PRId64, size);
|
|
||||||
} else {
|
|
||||||
base = 1024;
|
|
||||||
for(i = 0; i < NB_SUFFIXES; i++) {
|
|
||||||
if (size < (10 * base)) {
|
|
||||||
snprintf(buf, buf_size, "%0.1f%c",
|
|
||||||
(double)size / base,
|
|
||||||
suffixes[i]);
|
|
||||||
break;
|
|
||||||
} else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) {
|
|
||||||
snprintf(buf, buf_size, "%" PRId64 "%c",
|
|
||||||
((size + (base >> 1)) / base),
|
|
||||||
suffixes[i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
base = base * 1024;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn)
|
|
||||||
{
|
|
||||||
char buf1[128], date_buf[128], clock_buf[128];
|
|
||||||
struct tm tm;
|
|
||||||
time_t ti;
|
|
||||||
int64_t secs;
|
|
||||||
|
|
||||||
if (!sn) {
|
|
||||||
snprintf(buf, buf_size,
|
|
||||||
"%-10s%-20s%7s%20s%15s",
|
|
||||||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
|
|
||||||
} else {
|
|
||||||
ti = sn->date_sec;
|
|
||||||
localtime_r(&ti, &tm);
|
|
||||||
strftime(date_buf, sizeof(date_buf),
|
|
||||||
"%Y-%m-%d %H:%M:%S", &tm);
|
|
||||||
secs = sn->vm_clock_nsec / 1000000000;
|
|
||||||
snprintf(clock_buf, sizeof(clock_buf),
|
|
||||||
"%02d:%02d:%02d.%03d",
|
|
||||||
(int)(secs / 3600),
|
|
||||||
(int)((secs / 60) % 60),
|
|
||||||
(int)(secs % 60),
|
|
||||||
(int)((sn->vm_clock_nsec / 1000000) % 1000));
|
|
||||||
snprintf(buf, buf_size,
|
|
||||||
"%-10s%-20s%7s%20s%15s",
|
|
||||||
sn->id_str, sn->name,
|
|
||||||
get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size),
|
|
||||||
date_buf,
|
|
||||||
clock_buf);
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************/
|
/**************************************************************/
|
||||||
/* async I/Os */
|
/* async I/Os */
|
||||||
|
|
||||||
|
|
|
@ -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 += qed-check.o
|
||||||
block-obj-y += vhdx.o
|
block-obj-y += vhdx.o
|
||||||
block-obj-y += parallels.o blkdebug.o blkverify.o
|
block-obj-y += parallels.o blkdebug.o blkverify.o
|
||||||
|
block-obj-y += snapshot.o qapi.o
|
||||||
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
||||||
block-obj-$(CONFIG_POSIX) += raw-posix.o
|
block-obj-$(CONFIG_POSIX) += raw-posix.o
|
||||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||||
|
|
|
@ -0,0 +1,366 @@
|
||||||
|
/*
|
||||||
|
* Block layer qmp and info dump related functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "block/qapi.h"
|
||||||
|
#include "block/block_int.h"
|
||||||
|
#include "qmp-commands.h"
|
||||||
|
|
||||||
|
void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info)
|
||||||
|
{
|
||||||
|
int i, sn_count;
|
||||||
|
QEMUSnapshotInfo *sn_tab = NULL;
|
||||||
|
SnapshotInfoList *info_list, *cur_item = NULL;
|
||||||
|
sn_count = bdrv_snapshot_list(bs, &sn_tab);
|
||||||
|
|
||||||
|
for (i = 0; i < sn_count; i++) {
|
||||||
|
info->has_snapshots = true;
|
||||||
|
info_list = g_new0(SnapshotInfoList, 1);
|
||||||
|
|
||||||
|
info_list->value = g_new0(SnapshotInfo, 1);
|
||||||
|
info_list->value->id = g_strdup(sn_tab[i].id_str);
|
||||||
|
info_list->value->name = g_strdup(sn_tab[i].name);
|
||||||
|
info_list->value->vm_state_size = sn_tab[i].vm_state_size;
|
||||||
|
info_list->value->date_sec = sn_tab[i].date_sec;
|
||||||
|
info_list->value->date_nsec = sn_tab[i].date_nsec;
|
||||||
|
info_list->value->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
|
||||||
|
info_list->value->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
|
||||||
|
|
||||||
|
/* XXX: waiting for the qapi to support qemu-queue.h types */
|
||||||
|
if (!cur_item) {
|
||||||
|
info->snapshots = cur_item = info_list;
|
||||||
|
} else {
|
||||||
|
cur_item->next = info_list;
|
||||||
|
cur_item = info_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(sn_tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_collect_image_info(BlockDriverState *bs,
|
||||||
|
ImageInfo *info,
|
||||||
|
const char *filename)
|
||||||
|
{
|
||||||
|
uint64_t total_sectors;
|
||||||
|
char backing_filename[1024];
|
||||||
|
char backing_filename2[1024];
|
||||||
|
BlockDriverInfo bdi;
|
||||||
|
|
||||||
|
bdrv_get_geometry(bs, &total_sectors);
|
||||||
|
|
||||||
|
info->filename = g_strdup(filename);
|
||||||
|
info->format = g_strdup(bdrv_get_format_name(bs));
|
||||||
|
info->virtual_size = total_sectors * 512;
|
||||||
|
info->actual_size = bdrv_get_allocated_file_size(bs);
|
||||||
|
info->has_actual_size = info->actual_size >= 0;
|
||||||
|
if (bdrv_is_encrypted(bs)) {
|
||||||
|
info->encrypted = true;
|
||||||
|
info->has_encrypted = true;
|
||||||
|
}
|
||||||
|
if (bdrv_get_info(bs, &bdi) >= 0) {
|
||||||
|
if (bdi.cluster_size != 0) {
|
||||||
|
info->cluster_size = bdi.cluster_size;
|
||||||
|
info->has_cluster_size = true;
|
||||||
|
}
|
||||||
|
info->dirty_flag = bdi.is_dirty;
|
||||||
|
info->has_dirty_flag = true;
|
||||||
|
}
|
||||||
|
bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
|
||||||
|
if (backing_filename[0] != '\0') {
|
||||||
|
info->backing_filename = g_strdup(backing_filename);
|
||||||
|
info->has_backing_filename = true;
|
||||||
|
bdrv_get_full_backing_filename(bs, backing_filename2,
|
||||||
|
sizeof(backing_filename2));
|
||||||
|
|
||||||
|
if (strcmp(backing_filename, backing_filename2) != 0) {
|
||||||
|
info->full_backing_filename =
|
||||||
|
g_strdup(backing_filename2);
|
||||||
|
info->has_full_backing_filename = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs->backing_format[0]) {
|
||||||
|
info->backing_filename_format = g_strdup(bs->backing_format);
|
||||||
|
info->has_backing_filename_format = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockInfo *bdrv_query_info(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BlockInfo *info = g_malloc0(sizeof(*info));
|
||||||
|
info->device = g_strdup(bs->device_name);
|
||||||
|
info->type = g_strdup("unknown");
|
||||||
|
info->locked = bdrv_dev_is_medium_locked(bs);
|
||||||
|
info->removable = bdrv_dev_has_removable_media(bs);
|
||||||
|
|
||||||
|
if (bdrv_dev_has_removable_media(bs)) {
|
||||||
|
info->has_tray_open = true;
|
||||||
|
info->tray_open = bdrv_dev_is_tray_open(bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bdrv_iostatus_is_enabled(bs)) {
|
||||||
|
info->has_io_status = true;
|
||||||
|
info->io_status = bs->iostatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs->dirty_bitmap) {
|
||||||
|
info->has_dirty = true;
|
||||||
|
info->dirty = g_malloc0(sizeof(*info->dirty));
|
||||||
|
info->dirty->count = bdrv_get_dirty_count(bs) * BDRV_SECTOR_SIZE;
|
||||||
|
info->dirty->granularity =
|
||||||
|
((int64_t) BDRV_SECTOR_SIZE << hbitmap_granularity(bs->dirty_bitmap));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs->drv) {
|
||||||
|
info->has_inserted = true;
|
||||||
|
info->inserted = g_malloc0(sizeof(*info->inserted));
|
||||||
|
info->inserted->file = g_strdup(bs->filename);
|
||||||
|
info->inserted->ro = bs->read_only;
|
||||||
|
info->inserted->drv = g_strdup(bs->drv->format_name);
|
||||||
|
info->inserted->encrypted = bs->encrypted;
|
||||||
|
info->inserted->encryption_key_missing = bdrv_key_required(bs);
|
||||||
|
|
||||||
|
if (bs->backing_file[0]) {
|
||||||
|
info->inserted->has_backing_file = true;
|
||||||
|
info->inserted->backing_file = g_strdup(bs->backing_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
||||||
|
|
||||||
|
if (bs->io_limits_enabled) {
|
||||||
|
info->inserted->bps =
|
||||||
|
bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL];
|
||||||
|
info->inserted->bps_rd =
|
||||||
|
bs->io_limits.bps[BLOCK_IO_LIMIT_READ];
|
||||||
|
info->inserted->bps_wr =
|
||||||
|
bs->io_limits.bps[BLOCK_IO_LIMIT_WRITE];
|
||||||
|
info->inserted->iops =
|
||||||
|
bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL];
|
||||||
|
info->inserted->iops_rd =
|
||||||
|
bs->io_limits.iops[BLOCK_IO_LIMIT_READ];
|
||||||
|
info->inserted->iops_wr =
|
||||||
|
bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BlockStats *s;
|
||||||
|
|
||||||
|
s = g_malloc0(sizeof(*s));
|
||||||
|
|
||||||
|
if (bs->device_name[0]) {
|
||||||
|
s->has_device = true;
|
||||||
|
s->device = g_strdup(bs->device_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->stats = g_malloc0(sizeof(*s->stats));
|
||||||
|
s->stats->rd_bytes = bs->nr_bytes[BDRV_ACCT_READ];
|
||||||
|
s->stats->wr_bytes = bs->nr_bytes[BDRV_ACCT_WRITE];
|
||||||
|
s->stats->rd_operations = bs->nr_ops[BDRV_ACCT_READ];
|
||||||
|
s->stats->wr_operations = bs->nr_ops[BDRV_ACCT_WRITE];
|
||||||
|
s->stats->wr_highest_offset = bs->wr_highest_sector * BDRV_SECTOR_SIZE;
|
||||||
|
s->stats->flush_operations = bs->nr_ops[BDRV_ACCT_FLUSH];
|
||||||
|
s->stats->wr_total_time_ns = bs->total_time_ns[BDRV_ACCT_WRITE];
|
||||||
|
s->stats->rd_total_time_ns = bs->total_time_ns[BDRV_ACCT_READ];
|
||||||
|
s->stats->flush_total_time_ns = bs->total_time_ns[BDRV_ACCT_FLUSH];
|
||||||
|
|
||||||
|
if (bs->file) {
|
||||||
|
s->has_parent = true;
|
||||||
|
s->parent = bdrv_query_stats(bs->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockInfoList *qmp_query_block(Error **errp)
|
||||||
|
{
|
||||||
|
BlockInfoList *head = NULL, **p_next = &head;
|
||||||
|
BlockDriverState *bs = NULL;
|
||||||
|
|
||||||
|
while ((bs = bdrv_next(bs))) {
|
||||||
|
BlockInfoList *info = g_malloc0(sizeof(*info));
|
||||||
|
info->value = bdrv_query_info(bs);
|
||||||
|
|
||||||
|
*p_next = info;
|
||||||
|
p_next = &info->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockStatsList *qmp_query_blockstats(Error **errp)
|
||||||
|
{
|
||||||
|
BlockStatsList *head = NULL, **p_next = &head;
|
||||||
|
BlockDriverState *bs = NULL;
|
||||||
|
|
||||||
|
while ((bs = bdrv_next(bs))) {
|
||||||
|
BlockStatsList *info = g_malloc0(sizeof(*info));
|
||||||
|
info->value = bdrv_query_stats(bs);
|
||||||
|
|
||||||
|
*p_next = info;
|
||||||
|
p_next = &info->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NB_SUFFIXES 4
|
||||||
|
|
||||||
|
static char *get_human_readable_size(char *buf, int buf_size, int64_t size)
|
||||||
|
{
|
||||||
|
static const char suffixes[NB_SUFFIXES] = "KMGT";
|
||||||
|
int64_t base;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (size <= 999) {
|
||||||
|
snprintf(buf, buf_size, "%" PRId64, size);
|
||||||
|
} else {
|
||||||
|
base = 1024;
|
||||||
|
for (i = 0; i < NB_SUFFIXES; i++) {
|
||||||
|
if (size < (10 * base)) {
|
||||||
|
snprintf(buf, buf_size, "%0.1f%c",
|
||||||
|
(double)size / base,
|
||||||
|
suffixes[i]);
|
||||||
|
break;
|
||||||
|
} else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) {
|
||||||
|
snprintf(buf, buf_size, "%" PRId64 "%c",
|
||||||
|
((size + (base >> 1)) / base),
|
||||||
|
suffixes[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
base = base * 1024;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
|
||||||
|
QEMUSnapshotInfo *sn)
|
||||||
|
{
|
||||||
|
char buf1[128], date_buf[128], clock_buf[128];
|
||||||
|
struct tm tm;
|
||||||
|
time_t ti;
|
||||||
|
int64_t secs;
|
||||||
|
|
||||||
|
if (!sn) {
|
||||||
|
func_fprintf(f,
|
||||||
|
"%-10s%-20s%7s%20s%15s",
|
||||||
|
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
|
||||||
|
} else {
|
||||||
|
ti = sn->date_sec;
|
||||||
|
localtime_r(&ti, &tm);
|
||||||
|
strftime(date_buf, sizeof(date_buf),
|
||||||
|
"%Y-%m-%d %H:%M:%S", &tm);
|
||||||
|
secs = sn->vm_clock_nsec / 1000000000;
|
||||||
|
snprintf(clock_buf, sizeof(clock_buf),
|
||||||
|
"%02d:%02d:%02d.%03d",
|
||||||
|
(int)(secs / 3600),
|
||||||
|
(int)((secs / 60) % 60),
|
||||||
|
(int)(secs % 60),
|
||||||
|
(int)((sn->vm_clock_nsec / 1000000) % 1000));
|
||||||
|
func_fprintf(f,
|
||||||
|
"%-10s%-20s%7s%20s%15s",
|
||||||
|
sn->id_str, sn->name,
|
||||||
|
get_human_readable_size(buf1, sizeof(buf1),
|
||||||
|
sn->vm_state_size),
|
||||||
|
date_buf,
|
||||||
|
clock_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
|
||||||
|
ImageInfo *info)
|
||||||
|
{
|
||||||
|
char size_buf[128], dsize_buf[128];
|
||||||
|
if (!info->has_actual_size) {
|
||||||
|
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
|
||||||
|
} else {
|
||||||
|
get_human_readable_size(dsize_buf, sizeof(dsize_buf),
|
||||||
|
info->actual_size);
|
||||||
|
}
|
||||||
|
get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size);
|
||||||
|
func_fprintf(f,
|
||||||
|
"image: %s\n"
|
||||||
|
"file format: %s\n"
|
||||||
|
"virtual size: %s (%" PRId64 " bytes)\n"
|
||||||
|
"disk size: %s\n",
|
||||||
|
info->filename, info->format, size_buf,
|
||||||
|
info->virtual_size,
|
||||||
|
dsize_buf);
|
||||||
|
|
||||||
|
if (info->has_encrypted && info->encrypted) {
|
||||||
|
func_fprintf(f, "encrypted: yes\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->has_cluster_size) {
|
||||||
|
func_fprintf(f, "cluster_size: %" PRId64 "\n",
|
||||||
|
info->cluster_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->has_dirty_flag && info->dirty_flag) {
|
||||||
|
func_fprintf(f, "cleanly shut down: no\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->has_backing_filename) {
|
||||||
|
func_fprintf(f, "backing file: %s", info->backing_filename);
|
||||||
|
if (info->has_full_backing_filename) {
|
||||||
|
func_fprintf(f, " (actual path: %s)", info->full_backing_filename);
|
||||||
|
}
|
||||||
|
func_fprintf(f, "\n");
|
||||||
|
if (info->has_backing_filename_format) {
|
||||||
|
func_fprintf(f, "backing file format: %s\n",
|
||||||
|
info->backing_filename_format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->has_snapshots) {
|
||||||
|
SnapshotInfoList *elem;
|
||||||
|
|
||||||
|
func_fprintf(f, "Snapshot list:\n");
|
||||||
|
bdrv_snapshot_dump(func_fprintf, f, NULL);
|
||||||
|
func_fprintf(f, "\n");
|
||||||
|
|
||||||
|
/* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but
|
||||||
|
* we convert to the block layer's native QEMUSnapshotInfo for now.
|
||||||
|
*/
|
||||||
|
for (elem = info->snapshots; elem; elem = elem->next) {
|
||||||
|
QEMUSnapshotInfo sn = {
|
||||||
|
.vm_state_size = elem->value->vm_state_size,
|
||||||
|
.date_sec = elem->value->date_sec,
|
||||||
|
.date_nsec = elem->value->date_nsec,
|
||||||
|
.vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL +
|
||||||
|
elem->value->vm_clock_nsec,
|
||||||
|
};
|
||||||
|
|
||||||
|
pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id);
|
||||||
|
pstrcpy(sn.name, sizeof(sn.name), elem->value->name);
|
||||||
|
bdrv_snapshot_dump(func_fprintf, f, &sn);
|
||||||
|
func_fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Block layer snapshot related functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "block/snapshot.h"
|
||||||
|
#include "block/block_int.h"
|
||||||
|
|
||||||
|
int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
QEMUSnapshotInfo *sn_tab, *sn;
|
||||||
|
int nb_sns, i, ret;
|
||||||
|
|
||||||
|
ret = -ENOENT;
|
||||||
|
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
||||||
|
if (nb_sns < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
for (i = 0; i < nb_sns; i++) {
|
||||||
|
sn = &sn_tab[i];
|
||||||
|
if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
|
||||||
|
*sn_info = *sn;
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free(sn_tab);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_can_snapshot(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BlockDriver *drv = bs->drv;
|
||||||
|
if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drv->bdrv_snapshot_create) {
|
||||||
|
if (bs->file != NULL) {
|
||||||
|
return bdrv_can_snapshot(bs->file);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_snapshot_create(BlockDriverState *bs,
|
||||||
|
QEMUSnapshotInfo *sn_info)
|
||||||
|
{
|
||||||
|
BlockDriver *drv = bs->drv;
|
||||||
|
if (!drv) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
if (drv->bdrv_snapshot_create) {
|
||||||
|
return drv->bdrv_snapshot_create(bs, sn_info);
|
||||||
|
}
|
||||||
|
if (bs->file) {
|
||||||
|
return bdrv_snapshot_create(bs->file, sn_info);
|
||||||
|
}
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||||
|
const char *snapshot_id)
|
||||||
|
{
|
||||||
|
BlockDriver *drv = bs->drv;
|
||||||
|
int ret, open_ret;
|
||||||
|
|
||||||
|
if (!drv) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
if (drv->bdrv_snapshot_goto) {
|
||||||
|
return drv->bdrv_snapshot_goto(bs, snapshot_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs->file) {
|
||||||
|
drv->bdrv_close(bs);
|
||||||
|
ret = bdrv_snapshot_goto(bs->file, snapshot_id);
|
||||||
|
open_ret = drv->bdrv_open(bs, NULL, bs->open_flags);
|
||||||
|
if (open_ret < 0) {
|
||||||
|
bdrv_delete(bs->file);
|
||||||
|
bs->drv = NULL;
|
||||||
|
return open_ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
||||||
|
{
|
||||||
|
BlockDriver *drv = bs->drv;
|
||||||
|
if (!drv) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
if (drv->bdrv_snapshot_delete) {
|
||||||
|
return drv->bdrv_snapshot_delete(bs, snapshot_id);
|
||||||
|
}
|
||||||
|
if (bs->file) {
|
||||||
|
return bdrv_snapshot_delete(bs->file, snapshot_id);
|
||||||
|
}
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_snapshot_list(BlockDriverState *bs,
|
||||||
|
QEMUSnapshotInfo **psn_info)
|
||||||
|
{
|
||||||
|
BlockDriver *drv = bs->drv;
|
||||||
|
if (!drv) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
if (drv->bdrv_snapshot_list) {
|
||||||
|
return drv->bdrv_snapshot_list(bs, psn_info);
|
||||||
|
}
|
||||||
|
if (bs->file) {
|
||||||
|
return bdrv_snapshot_list(bs->file, psn_info);
|
||||||
|
}
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
||||||
|
const char *snapshot_name)
|
||||||
|
{
|
||||||
|
BlockDriver *drv = bs->drv;
|
||||||
|
if (!drv) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
if (!bs->read_only) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (drv->bdrv_snapshot_load_tmp) {
|
||||||
|
return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
|
||||||
|
}
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
|
@ -477,7 +477,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||||
error_printf("\n");
|
error_printf("\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
drv = bdrv_find_whitelisted_format(buf);
|
drv = bdrv_find_whitelisted_format(buf, ro);
|
||||||
if (!drv) {
|
if (!drv) {
|
||||||
error_report("'%s' invalid format", buf);
|
error_report("'%s' invalid format", buf);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1096,7 +1096,7 @@ void qmp_change_blockdev(const char *device, const char *filename,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format) {
|
if (format) {
|
||||||
drv = bdrv_find_whitelisted_format(format);
|
drv = bdrv_find_whitelisted_format(format, bs->read_only);
|
||||||
if (!drv) {
|
if (!drv) {
|
||||||
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -123,7 +123,8 @@ interp_prefix="/usr/gnemul/qemu-%M"
|
||||||
static="no"
|
static="no"
|
||||||
cross_prefix=""
|
cross_prefix=""
|
||||||
audio_drv_list=""
|
audio_drv_list=""
|
||||||
block_drv_whitelist=""
|
block_drv_rw_whitelist=""
|
||||||
|
block_drv_ro_whitelist=""
|
||||||
host_cc="cc"
|
host_cc="cc"
|
||||||
libs_softmmu=""
|
libs_softmmu=""
|
||||||
libs_tools=""
|
libs_tools=""
|
||||||
|
@ -708,7 +709,9 @@ for opt do
|
||||||
;;
|
;;
|
||||||
--audio-drv-list=*) audio_drv_list="$optarg"
|
--audio-drv-list=*) audio_drv_list="$optarg"
|
||||||
;;
|
;;
|
||||||
--block-drv-whitelist=*) block_drv_whitelist=`echo "$optarg" | sed -e 's/,/ /g'`
|
--block-drv-rw-whitelist=*|--block-drv-whitelist=*) block_drv_rw_whitelist=`echo "$optarg" | sed -e 's/,/ /g'`
|
||||||
|
;;
|
||||||
|
--block-drv-ro-whitelist=*) block_drv_ro_whitelist=`echo "$optarg" | sed -e 's/,/ /g'`
|
||||||
;;
|
;;
|
||||||
--enable-debug-tcg) debug_tcg="yes"
|
--enable-debug-tcg) debug_tcg="yes"
|
||||||
;;
|
;;
|
||||||
|
@ -1049,7 +1052,12 @@ echo " --disable-cocoa disable Cocoa (Mac OS X only)"
|
||||||
echo " --enable-cocoa enable Cocoa (default on Mac OS X)"
|
echo " --enable-cocoa enable Cocoa (default on Mac OS X)"
|
||||||
echo " --audio-drv-list=LIST set audio drivers list:"
|
echo " --audio-drv-list=LIST set audio drivers list:"
|
||||||
echo " Available drivers: $audio_possible_drivers"
|
echo " Available drivers: $audio_possible_drivers"
|
||||||
echo " --block-drv-whitelist=L set block driver whitelist"
|
echo " --block-drv-whitelist=L Same as --block-drv-rw-whitelist=L"
|
||||||
|
echo " --block-drv-rw-whitelist=L"
|
||||||
|
echo " set block driver read-write whitelist"
|
||||||
|
echo " (affects only QEMU, not qemu-img)"
|
||||||
|
echo " --block-drv-ro-whitelist=L"
|
||||||
|
echo " set block driver read-only whitelist"
|
||||||
echo " (affects only QEMU, not qemu-img)"
|
echo " (affects only QEMU, not qemu-img)"
|
||||||
echo " --enable-mixemu enable mixer emulation"
|
echo " --enable-mixemu enable mixer emulation"
|
||||||
echo " --disable-xen disable xen backend driver support"
|
echo " --disable-xen disable xen backend driver support"
|
||||||
|
@ -3481,7 +3489,8 @@ echo "curses support $curses"
|
||||||
echo "curl support $curl"
|
echo "curl support $curl"
|
||||||
echo "mingw32 support $mingw32"
|
echo "mingw32 support $mingw32"
|
||||||
echo "Audio drivers $audio_drv_list"
|
echo "Audio drivers $audio_drv_list"
|
||||||
echo "Block whitelist $block_drv_whitelist"
|
echo "Block whitelist (rw) $block_drv_rw_whitelist"
|
||||||
|
echo "Block whitelist (ro) $block_drv_ro_whitelist"
|
||||||
echo "Mixer emulation $mixemu"
|
echo "Mixer emulation $mixemu"
|
||||||
echo "VirtFS support $virtfs"
|
echo "VirtFS support $virtfs"
|
||||||
echo "VNC support $vnc"
|
echo "VNC support $vnc"
|
||||||
|
@ -3662,7 +3671,8 @@ fi
|
||||||
if test "$audio_win_int" = "yes" ; then
|
if test "$audio_win_int" = "yes" ; then
|
||||||
echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak
|
echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
echo "CONFIG_BDRV_WHITELIST=$block_drv_whitelist" >> $config_host_mak
|
echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak
|
||||||
|
echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak
|
||||||
if test "$mixemu" = "yes" ; then
|
if test "$mixemu" = "yes" ; then
|
||||||
echo "CONFIG_MIXEMU=y" >> $config_host_mak
|
echo "CONFIG_MIXEMU=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -780,11 +780,13 @@ static int blk_connect(struct XenDevice *xendev)
|
||||||
{
|
{
|
||||||
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
|
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
|
||||||
int pers, index, qflags;
|
int pers, index, qflags;
|
||||||
|
bool readonly = true;
|
||||||
|
|
||||||
/* read-only ? */
|
/* read-only ? */
|
||||||
qflags = BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO;
|
qflags = BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO;
|
||||||
if (strcmp(blkdev->mode, "w") == 0) {
|
if (strcmp(blkdev->mode, "w") == 0) {
|
||||||
qflags |= BDRV_O_RDWR;
|
qflags |= BDRV_O_RDWR;
|
||||||
|
readonly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* init qemu block driver */
|
/* init qemu block driver */
|
||||||
|
@ -795,8 +797,10 @@ static int blk_connect(struct XenDevice *xendev)
|
||||||
xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
|
xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
|
||||||
blkdev->bs = bdrv_new(blkdev->dev);
|
blkdev->bs = bdrv_new(blkdev->dev);
|
||||||
if (blkdev->bs) {
|
if (blkdev->bs) {
|
||||||
if (bdrv_open(blkdev->bs, blkdev->filename, NULL, qflags,
|
BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto,
|
||||||
bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) {
|
readonly);
|
||||||
|
if (bdrv_open(blkdev->bs,
|
||||||
|
blkdev->filename, NULL, qflags, drv) != 0) {
|
||||||
bdrv_delete(blkdev->bs);
|
bdrv_delete(blkdev->bs);
|
||||||
blkdev->bs = NULL;
|
blkdev->bs = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,17 +27,6 @@ typedef struct BlockFragInfo {
|
||||||
uint64_t compressed_clusters;
|
uint64_t compressed_clusters;
|
||||||
} BlockFragInfo;
|
} BlockFragInfo;
|
||||||
|
|
||||||
typedef struct QEMUSnapshotInfo {
|
|
||||||
char id_str[128]; /* unique snapshot id */
|
|
||||||
/* the following fields are informative. They are not needed for
|
|
||||||
the consistency of the snapshot */
|
|
||||||
char name[256]; /* user chosen name */
|
|
||||||
uint64_t vm_state_size; /* VM state info size */
|
|
||||||
uint32_t date_sec; /* UTC date of the snapshot */
|
|
||||||
uint32_t date_nsec;
|
|
||||||
uint64_t vm_clock_nsec; /* VM clock relative to boot */
|
|
||||||
} QEMUSnapshotInfo;
|
|
||||||
|
|
||||||
/* Callbacks for block device models */
|
/* Callbacks for block device models */
|
||||||
typedef struct BlockDevOps {
|
typedef struct BlockDevOps {
|
||||||
/*
|
/*
|
||||||
|
@ -124,7 +113,8 @@ void bdrv_init(void);
|
||||||
void bdrv_init_with_whitelist(void);
|
void bdrv_init_with_whitelist(void);
|
||||||
BlockDriver *bdrv_find_protocol(const char *filename);
|
BlockDriver *bdrv_find_protocol(const char *filename);
|
||||||
BlockDriver *bdrv_find_format(const char *format_name);
|
BlockDriver *bdrv_find_format(const char *format_name);
|
||||||
BlockDriver *bdrv_find_whitelisted_format(const char *format_name);
|
BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
|
||||||
|
bool readonly);
|
||||||
int bdrv_create(BlockDriver *drv, const char* filename,
|
int bdrv_create(BlockDriver *drv, const char* filename,
|
||||||
QEMUOptionParameter *options);
|
QEMUOptionParameter *options);
|
||||||
int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
|
int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
|
||||||
|
@ -328,23 +318,8 @@ void bdrv_get_backing_filename(BlockDriverState *bs,
|
||||||
char *filename, int filename_size);
|
char *filename, int filename_size);
|
||||||
void bdrv_get_full_backing_filename(BlockDriverState *bs,
|
void bdrv_get_full_backing_filename(BlockDriverState *bs,
|
||||||
char *dest, size_t sz);
|
char *dest, size_t sz);
|
||||||
BlockInfo *bdrv_query_info(BlockDriverState *s);
|
|
||||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs);
|
|
||||||
int bdrv_can_snapshot(BlockDriverState *bs);
|
|
||||||
int bdrv_is_snapshot(BlockDriverState *bs);
|
int bdrv_is_snapshot(BlockDriverState *bs);
|
||||||
BlockDriverState *bdrv_snapshots(void);
|
|
||||||
int bdrv_snapshot_create(BlockDriverState *bs,
|
|
||||||
QEMUSnapshotInfo *sn_info);
|
|
||||||
int bdrv_snapshot_goto(BlockDriverState *bs,
|
|
||||||
const char *snapshot_id);
|
|
||||||
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
|
||||||
int bdrv_snapshot_list(BlockDriverState *bs,
|
|
||||||
QEMUSnapshotInfo **psn_info);
|
|
||||||
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
|
||||||
const char *snapshot_name);
|
|
||||||
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn);
|
|
||||||
|
|
||||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size);
|
|
||||||
int path_is_absolute(const char *path);
|
int path_is_absolute(const char *path);
|
||||||
void path_combine(char *dest, int dest_size,
|
void path_combine(char *dest, int dest_size,
|
||||||
const char *base_path,
|
const char *base_path,
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
#include "qemu/hbitmap.h"
|
#include "qemu/hbitmap.h"
|
||||||
|
#include "block/snapshot.h"
|
||||||
|
|
||||||
#define BLOCK_FLAG_ENCRYPT 1
|
#define BLOCK_FLAG_ENCRYPT 1
|
||||||
#define BLOCK_FLAG_COMPAT6 4
|
#define BLOCK_FLAG_COMPAT6 4
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Block layer qmp and info dump related functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLOCK_QAPI_H
|
||||||
|
#define BLOCK_QAPI_H
|
||||||
|
|
||||||
|
#include "qapi-types.h"
|
||||||
|
#include "block/block.h"
|
||||||
|
#include "block/snapshot.h"
|
||||||
|
|
||||||
|
void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info);
|
||||||
|
void bdrv_collect_image_info(BlockDriverState *bs,
|
||||||
|
ImageInfo *info,
|
||||||
|
const char *filename);
|
||||||
|
BlockInfo *bdrv_query_info(BlockDriverState *s);
|
||||||
|
BlockStats *bdrv_query_stats(const BlockDriverState *bs);
|
||||||
|
|
||||||
|
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
|
||||||
|
QEMUSnapshotInfo *sn);
|
||||||
|
void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
|
||||||
|
ImageInfo *info);
|
||||||
|
#endif
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Block layer snapshot related functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SNAPSHOT_H
|
||||||
|
#define SNAPSHOT_H
|
||||||
|
|
||||||
|
#include "qemu-common.h"
|
||||||
|
|
||||||
|
typedef struct QEMUSnapshotInfo {
|
||||||
|
char id_str[128]; /* unique snapshot id */
|
||||||
|
/* the following fields are informative. They are not needed for
|
||||||
|
the consistency of the snapshot */
|
||||||
|
char name[256]; /* user chosen name */
|
||||||
|
uint64_t vm_state_size; /* VM state info size */
|
||||||
|
uint32_t date_sec; /* UTC date of the snapshot */
|
||||||
|
uint32_t date_nsec;
|
||||||
|
uint64_t vm_clock_nsec; /* VM clock relative to boot */
|
||||||
|
} QEMUSnapshotInfo;
|
||||||
|
|
||||||
|
int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
|
||||||
|
const char *name);
|
||||||
|
int bdrv_can_snapshot(BlockDriverState *bs);
|
||||||
|
int bdrv_snapshot_create(BlockDriverState *bs,
|
||||||
|
QEMUSnapshotInfo *sn_info);
|
||||||
|
int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||||
|
const char *snapshot_id);
|
||||||
|
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
||||||
|
int bdrv_snapshot_list(BlockDriverState *bs,
|
||||||
|
QEMUSnapshotInfo **psn_info);
|
||||||
|
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
||||||
|
const char *snapshot_name);
|
||||||
|
#endif
|
163
qemu-img.c
163
qemu-img.c
|
@ -30,6 +30,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "block/qapi.h"
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
@ -1553,16 +1554,17 @@ static void dump_snapshots(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
QEMUSnapshotInfo *sn_tab, *sn;
|
QEMUSnapshotInfo *sn_tab, *sn;
|
||||||
int nb_sns, i;
|
int nb_sns, i;
|
||||||
char buf[256];
|
|
||||||
|
|
||||||
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
||||||
if (nb_sns <= 0)
|
if (nb_sns <= 0)
|
||||||
return;
|
return;
|
||||||
printf("Snapshot list:\n");
|
printf("Snapshot list:\n");
|
||||||
printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
|
bdrv_snapshot_dump(fprintf, stdout, NULL);
|
||||||
|
printf("\n");
|
||||||
for(i = 0; i < nb_sns; i++) {
|
for(i = 0; i < nb_sns; i++) {
|
||||||
sn = &sn_tab[i];
|
sn = &sn_tab[i];
|
||||||
printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
|
bdrv_snapshot_dump(fprintf, stdout, sn);
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
g_free(sn_tab);
|
g_free(sn_tab);
|
||||||
}
|
}
|
||||||
|
@ -1584,39 +1586,6 @@ static void dump_json_image_info_list(ImageInfoList *list)
|
||||||
QDECREF(str);
|
QDECREF(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void collect_snapshots(BlockDriverState *bs , ImageInfo *info)
|
|
||||||
{
|
|
||||||
int i, sn_count;
|
|
||||||
QEMUSnapshotInfo *sn_tab = NULL;
|
|
||||||
SnapshotInfoList *info_list, *cur_item = NULL;
|
|
||||||
sn_count = bdrv_snapshot_list(bs, &sn_tab);
|
|
||||||
|
|
||||||
for (i = 0; i < sn_count; i++) {
|
|
||||||
info->has_snapshots = true;
|
|
||||||
info_list = g_new0(SnapshotInfoList, 1);
|
|
||||||
|
|
||||||
info_list->value = g_new0(SnapshotInfo, 1);
|
|
||||||
info_list->value->id = g_strdup(sn_tab[i].id_str);
|
|
||||||
info_list->value->name = g_strdup(sn_tab[i].name);
|
|
||||||
info_list->value->vm_state_size = sn_tab[i].vm_state_size;
|
|
||||||
info_list->value->date_sec = sn_tab[i].date_sec;
|
|
||||||
info_list->value->date_nsec = sn_tab[i].date_nsec;
|
|
||||||
info_list->value->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
|
|
||||||
info_list->value->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
|
|
||||||
|
|
||||||
/* XXX: waiting for the qapi to support qemu-queue.h types */
|
|
||||||
if (!cur_item) {
|
|
||||||
info->snapshots = cur_item = info_list;
|
|
||||||
} else {
|
|
||||||
cur_item->next = info_list;
|
|
||||||
cur_item = info_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(sn_tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dump_json_image_info(ImageInfo *info)
|
static void dump_json_image_info(ImageInfo *info)
|
||||||
{
|
{
|
||||||
Error *errp = NULL;
|
Error *errp = NULL;
|
||||||
|
@ -1634,122 +1603,6 @@ static void dump_json_image_info(ImageInfo *info)
|
||||||
QDECREF(str);
|
QDECREF(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void collect_image_info(BlockDriverState *bs,
|
|
||||||
ImageInfo *info,
|
|
||||||
const char *filename,
|
|
||||||
const char *fmt)
|
|
||||||
{
|
|
||||||
uint64_t total_sectors;
|
|
||||||
char backing_filename[1024];
|
|
||||||
char backing_filename2[1024];
|
|
||||||
BlockDriverInfo bdi;
|
|
||||||
|
|
||||||
bdrv_get_geometry(bs, &total_sectors);
|
|
||||||
|
|
||||||
info->filename = g_strdup(filename);
|
|
||||||
info->format = g_strdup(bdrv_get_format_name(bs));
|
|
||||||
info->virtual_size = total_sectors * 512;
|
|
||||||
info->actual_size = bdrv_get_allocated_file_size(bs);
|
|
||||||
info->has_actual_size = info->actual_size >= 0;
|
|
||||||
if (bdrv_is_encrypted(bs)) {
|
|
||||||
info->encrypted = true;
|
|
||||||
info->has_encrypted = true;
|
|
||||||
}
|
|
||||||
if (bdrv_get_info(bs, &bdi) >= 0) {
|
|
||||||
if (bdi.cluster_size != 0) {
|
|
||||||
info->cluster_size = bdi.cluster_size;
|
|
||||||
info->has_cluster_size = true;
|
|
||||||
}
|
|
||||||
info->dirty_flag = bdi.is_dirty;
|
|
||||||
info->has_dirty_flag = true;
|
|
||||||
}
|
|
||||||
bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
|
|
||||||
if (backing_filename[0] != '\0') {
|
|
||||||
info->backing_filename = g_strdup(backing_filename);
|
|
||||||
info->has_backing_filename = true;
|
|
||||||
bdrv_get_full_backing_filename(bs, backing_filename2,
|
|
||||||
sizeof(backing_filename2));
|
|
||||||
|
|
||||||
if (strcmp(backing_filename, backing_filename2) != 0) {
|
|
||||||
info->full_backing_filename =
|
|
||||||
g_strdup(backing_filename2);
|
|
||||||
info->has_full_backing_filename = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bs->backing_format[0]) {
|
|
||||||
info->backing_filename_format = g_strdup(bs->backing_format);
|
|
||||||
info->has_backing_filename_format = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dump_human_image_info(ImageInfo *info)
|
|
||||||
{
|
|
||||||
char size_buf[128], dsize_buf[128];
|
|
||||||
if (!info->has_actual_size) {
|
|
||||||
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
|
|
||||||
} else {
|
|
||||||
get_human_readable_size(dsize_buf, sizeof(dsize_buf),
|
|
||||||
info->actual_size);
|
|
||||||
}
|
|
||||||
get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size);
|
|
||||||
printf("image: %s\n"
|
|
||||||
"file format: %s\n"
|
|
||||||
"virtual size: %s (%" PRId64 " bytes)\n"
|
|
||||||
"disk size: %s\n",
|
|
||||||
info->filename, info->format, size_buf,
|
|
||||||
info->virtual_size,
|
|
||||||
dsize_buf);
|
|
||||||
|
|
||||||
if (info->has_encrypted && info->encrypted) {
|
|
||||||
printf("encrypted: yes\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info->has_cluster_size) {
|
|
||||||
printf("cluster_size: %" PRId64 "\n", info->cluster_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info->has_dirty_flag && info->dirty_flag) {
|
|
||||||
printf("cleanly shut down: no\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info->has_backing_filename) {
|
|
||||||
printf("backing file: %s", info->backing_filename);
|
|
||||||
if (info->has_full_backing_filename) {
|
|
||||||
printf(" (actual path: %s)", info->full_backing_filename);
|
|
||||||
}
|
|
||||||
putchar('\n');
|
|
||||||
if (info->has_backing_filename_format) {
|
|
||||||
printf("backing file format: %s\n", info->backing_filename_format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info->has_snapshots) {
|
|
||||||
SnapshotInfoList *elem;
|
|
||||||
char buf[256];
|
|
||||||
|
|
||||||
printf("Snapshot list:\n");
|
|
||||||
printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
|
|
||||||
|
|
||||||
/* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but
|
|
||||||
* we convert to the block layer's native QEMUSnapshotInfo for now.
|
|
||||||
*/
|
|
||||||
for (elem = info->snapshots; elem; elem = elem->next) {
|
|
||||||
QEMUSnapshotInfo sn = {
|
|
||||||
.vm_state_size = elem->value->vm_state_size,
|
|
||||||
.date_sec = elem->value->date_sec,
|
|
||||||
.date_nsec = elem->value->date_nsec,
|
|
||||||
.vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL +
|
|
||||||
elem->value->vm_clock_nsec,
|
|
||||||
};
|
|
||||||
|
|
||||||
pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id);
|
|
||||||
pstrcpy(sn.name, sizeof(sn.name), elem->value->name);
|
|
||||||
printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), &sn));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dump_human_image_info_list(ImageInfoList *list)
|
static void dump_human_image_info_list(ImageInfoList *list)
|
||||||
{
|
{
|
||||||
ImageInfoList *elem;
|
ImageInfoList *elem;
|
||||||
|
@ -1761,7 +1614,7 @@ static void dump_human_image_info_list(ImageInfoList *list)
|
||||||
}
|
}
|
||||||
delim = true;
|
delim = true;
|
||||||
|
|
||||||
dump_human_image_info(elem->value);
|
bdrv_image_info_dump(fprintf, stdout, elem->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1811,8 +1664,8 @@ static ImageInfoList *collect_image_info_list(const char *filename,
|
||||||
}
|
}
|
||||||
|
|
||||||
info = g_new0(ImageInfo, 1);
|
info = g_new0(ImageInfo, 1);
|
||||||
collect_image_info(bs, info, filename, fmt);
|
bdrv_collect_image_info(bs, info, filename);
|
||||||
collect_snapshots(bs, info);
|
bdrv_collect_snapshots(bs, info);
|
||||||
|
|
||||||
elem = g_new0(ImageInfoList, 1);
|
elem = g_new0(ImageInfoList, 1);
|
||||||
elem->value = info;
|
elem->value = info;
|
||||||
|
|
40
savevm.c
40
savevm.c
|
@ -40,6 +40,8 @@
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
#include "qemu/iov.h"
|
#include "qemu/iov.h"
|
||||||
|
#include "block/snapshot.h"
|
||||||
|
#include "block/qapi.h"
|
||||||
|
|
||||||
#define SELF_ANNOUNCE_ROUNDS 5
|
#define SELF_ANNOUNCE_ROUNDS 5
|
||||||
|
|
||||||
|
@ -2262,26 +2264,15 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
|
static BlockDriverState *find_vmstate_bs(void)
|
||||||
const char *name)
|
|
||||||
{
|
{
|
||||||
QEMUSnapshotInfo *sn_tab, *sn;
|
BlockDriverState *bs = NULL;
|
||||||
int nb_sns, i, ret;
|
while ((bs = bdrv_next(bs))) {
|
||||||
|
if (bdrv_can_snapshot(bs)) {
|
||||||
ret = -ENOENT;
|
return bs;
|
||||||
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
|
||||||
if (nb_sns < 0)
|
|
||||||
return ret;
|
|
||||||
for(i = 0; i < nb_sns; i++) {
|
|
||||||
sn = &sn_tab[i];
|
|
||||||
if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
|
|
||||||
*sn_info = *sn;
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_free(sn_tab);
|
return NULL;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2338,7 +2329,7 @@ void do_savevm(Monitor *mon, const QDict *qdict)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bs = bdrv_snapshots();
|
bs = find_vmstate_bs();
|
||||||
if (!bs) {
|
if (!bs) {
|
||||||
monitor_printf(mon, "No block device can accept snapshots\n");
|
monitor_printf(mon, "No block device can accept snapshots\n");
|
||||||
return;
|
return;
|
||||||
|
@ -2440,7 +2431,7 @@ int load_vmstate(const char *name)
|
||||||
QEMUFile *f;
|
QEMUFile *f;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
bs_vm_state = bdrv_snapshots();
|
bs_vm_state = find_vmstate_bs();
|
||||||
if (!bs_vm_state) {
|
if (!bs_vm_state) {
|
||||||
error_report("No block device supports snapshots");
|
error_report("No block device supports snapshots");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
@ -2519,7 +2510,7 @@ void do_delvm(Monitor *mon, const QDict *qdict)
|
||||||
int ret;
|
int ret;
|
||||||
const char *name = qdict_get_str(qdict, "name");
|
const char *name = qdict_get_str(qdict, "name");
|
||||||
|
|
||||||
bs = bdrv_snapshots();
|
bs = find_vmstate_bs();
|
||||||
if (!bs) {
|
if (!bs) {
|
||||||
monitor_printf(mon, "No block device supports snapshots\n");
|
monitor_printf(mon, "No block device supports snapshots\n");
|
||||||
return;
|
return;
|
||||||
|
@ -2549,9 +2540,8 @@ void do_info_snapshots(Monitor *mon, const QDict *qdict)
|
||||||
int nb_sns, i, ret, available;
|
int nb_sns, i, ret, available;
|
||||||
int total;
|
int total;
|
||||||
int *available_snapshots;
|
int *available_snapshots;
|
||||||
char buf[256];
|
|
||||||
|
|
||||||
bs = bdrv_snapshots();
|
bs = find_vmstate_bs();
|
||||||
if (!bs) {
|
if (!bs) {
|
||||||
monitor_printf(mon, "No available block device supports snapshots\n");
|
monitor_printf(mon, "No available block device supports snapshots\n");
|
||||||
return;
|
return;
|
||||||
|
@ -2592,10 +2582,12 @@ void do_info_snapshots(Monitor *mon, const QDict *qdict)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
|
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, NULL);
|
||||||
|
monitor_printf(mon, "\n");
|
||||||
for (i = 0; i < total; i++) {
|
for (i = 0; i < total; i++) {
|
||||||
sn = &sn_tab[available_snapshots[i]];
|
sn = &sn_tab[available_snapshots[i]];
|
||||||
monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
|
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, sn);
|
||||||
|
monitor_printf(mon, "\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
monitor_printf(mon, "There is no suitable snapshot available\n");
|
monitor_printf(mon, "There is no suitable snapshot available\n");
|
||||||
|
|
|
@ -34,8 +34,15 @@ case $line in
|
||||||
done
|
done
|
||||||
echo ""
|
echo ""
|
||||||
;;
|
;;
|
||||||
CONFIG_BDRV_WHITELIST=*)
|
CONFIG_BDRV_RW_WHITELIST=*)
|
||||||
echo "#define CONFIG_BDRV_WHITELIST \\"
|
echo "#define CONFIG_BDRV_RW_WHITELIST\\"
|
||||||
|
for drv in ${line#*=}; do
|
||||||
|
echo " \"${drv}\",\\"
|
||||||
|
done
|
||||||
|
echo " NULL"
|
||||||
|
;;
|
||||||
|
CONFIG_BDRV_RO_WHITELIST=*)
|
||||||
|
echo "#define CONFIG_BDRV_RO_WHITELIST\\"
|
||||||
for drv in ${line#*=}; do
|
for drv in ${line#*=}; do
|
||||||
echo " \"${drv}\",\\"
|
echo " \"${drv}\",\\"
|
||||||
done
|
done
|
||||||
|
|
|
@ -22,49 +22,16 @@ import time
|
||||||
import os
|
import os
|
||||||
import iotests
|
import iotests
|
||||||
from iotests import qemu_img, qemu_io
|
from iotests import qemu_img, qemu_io
|
||||||
import struct
|
|
||||||
|
|
||||||
backing_img = os.path.join(iotests.test_dir, 'backing.img')
|
backing_img = os.path.join(iotests.test_dir, 'backing.img')
|
||||||
mid_img = os.path.join(iotests.test_dir, 'mid.img')
|
mid_img = os.path.join(iotests.test_dir, 'mid.img')
|
||||||
test_img = os.path.join(iotests.test_dir, 'test.img')
|
test_img = os.path.join(iotests.test_dir, 'test.img')
|
||||||
|
|
||||||
class ImageStreamingTestCase(iotests.QMPTestCase):
|
class TestSingleDrive(iotests.QMPTestCase):
|
||||||
'''Abstract base class for image streaming test cases'''
|
|
||||||
|
|
||||||
def assert_no_active_streams(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', 'stream')
|
|
||||||
self.assert_qmp(event, 'data/device', drive)
|
|
||||||
cancelled = True
|
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
|
||||||
|
|
||||||
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(ImageStreamingTestCase):
|
|
||||||
image_len = 1 * 1024 * 1024 # MB
|
image_len = 1 * 1024 * 1024 # MB
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.create_image(backing_img, TestSingleDrive.image_len)
|
iotests.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' % backing_img, mid_img)
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_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 = iotests.VM().add_drive(test_img)
|
||||||
|
@ -77,7 +44,7 @@ class TestSingleDrive(ImageStreamingTestCase):
|
||||||
os.remove(backing_img)
|
os.remove(backing_img)
|
||||||
|
|
||||||
def test_stream(self):
|
def test_stream(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0')
|
result = self.vm.qmp('block-stream', device='drive0')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -92,7 +59,7 @@ class TestSingleDrive(ImageStreamingTestCase):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
self.assertEqual(qemu_io('-c', 'map', backing_img),
|
self.assertEqual(qemu_io('-c', 'map', backing_img),
|
||||||
|
@ -100,7 +67,7 @@ class TestSingleDrive(ImageStreamingTestCase):
|
||||||
'image file map does not match backing file after streaming')
|
'image file map does not match backing file after streaming')
|
||||||
|
|
||||||
def test_stream_pause(self):
|
def test_stream_pause(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0')
|
result = self.vm.qmp('block-stream', device='drive0')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -129,7 +96,7 @@ class TestSingleDrive(ImageStreamingTestCase):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
self.assertEqual(qemu_io('-c', 'map', backing_img),
|
self.assertEqual(qemu_io('-c', 'map', backing_img),
|
||||||
|
@ -137,7 +104,7 @@ class TestSingleDrive(ImageStreamingTestCase):
|
||||||
'image file map does not match backing file after streaming')
|
'image file map does not match backing file after streaming')
|
||||||
|
|
||||||
def test_stream_partial(self):
|
def test_stream_partial(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0', base=mid_img)
|
result = self.vm.qmp('block-stream', device='drive0', base=mid_img)
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -152,7 +119,7 @@ class TestSingleDrive(ImageStreamingTestCase):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
self.assertEqual(qemu_io('-c', 'map', mid_img),
|
self.assertEqual(qemu_io('-c', 'map', mid_img),
|
||||||
|
@ -164,12 +131,12 @@ class TestSingleDrive(ImageStreamingTestCase):
|
||||||
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
|
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
|
||||||
|
|
||||||
|
|
||||||
class TestSmallerBackingFile(ImageStreamingTestCase):
|
class TestSmallerBackingFile(iotests.QMPTestCase):
|
||||||
backing_len = 1 * 1024 * 1024 # MB
|
backing_len = 1 * 1024 * 1024 # MB
|
||||||
image_len = 2 * backing_len
|
image_len = 2 * backing_len
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.create_image(backing_img, self.backing_len)
|
iotests.create_image(backing_img, self.backing_len)
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len))
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len))
|
||||||
self.vm = iotests.VM().add_drive(test_img)
|
self.vm = iotests.VM().add_drive(test_img)
|
||||||
self.vm.launch()
|
self.vm.launch()
|
||||||
|
@ -177,7 +144,7 @@ class TestSmallerBackingFile(ImageStreamingTestCase):
|
||||||
# If this hangs, then you are missing a fix to complete streaming when the
|
# If this hangs, then you are missing a fix to complete streaming when the
|
||||||
# end of the backing file is reached.
|
# end of the backing file is reached.
|
||||||
def test_stream(self):
|
def test_stream(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0')
|
result = self.vm.qmp('block-stream', device='drive0')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -192,10 +159,10 @@ class TestSmallerBackingFile(ImageStreamingTestCase):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
class TestErrors(ImageStreamingTestCase):
|
class TestErrors(iotests.QMPTestCase):
|
||||||
image_len = 2 * 1024 * 1024 # MB
|
image_len = 2 * 1024 * 1024 # MB
|
||||||
|
|
||||||
# this should match STREAM_BUFFER_SIZE/512 in block/stream.c
|
# this should match STREAM_BUFFER_SIZE/512 in block/stream.c
|
||||||
|
@ -227,7 +194,7 @@ new_state = "1"
|
||||||
class TestEIO(TestErrors):
|
class TestEIO(TestErrors):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.blkdebug_file = backing_img + ".blkdebug"
|
self.blkdebug_file = backing_img + ".blkdebug"
|
||||||
self.create_image(backing_img, TestErrors.image_len)
|
iotests.create_image(backing_img, TestErrors.image_len)
|
||||||
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
|
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
|
||||||
qemu_img('create', '-f', iotests.imgfmt,
|
qemu_img('create', '-f', iotests.imgfmt,
|
||||||
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
|
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
|
||||||
|
@ -243,7 +210,7 @@ class TestEIO(TestErrors):
|
||||||
os.remove(self.blkdebug_file)
|
os.remove(self.blkdebug_file)
|
||||||
|
|
||||||
def test_report(self):
|
def test_report(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0')
|
result = self.vm.qmp('block-stream', device='drive0')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -265,11 +232,11 @@ class TestEIO(TestErrors):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
def test_ignore(self):
|
def test_ignore(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0', on_error='ignore')
|
result = self.vm.qmp('block-stream', device='drive0', on_error='ignore')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -293,11 +260,11 @@ class TestEIO(TestErrors):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
def test_stop(self):
|
def test_stop(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0', on_error='stop')
|
result = self.vm.qmp('block-stream', device='drive0', on_error='stop')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -331,11 +298,11 @@ class TestEIO(TestErrors):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
def test_enospc(self):
|
def test_enospc(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
|
result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -357,13 +324,13 @@ class TestEIO(TestErrors):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
class TestENOSPC(TestErrors):
|
class TestENOSPC(TestErrors):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.blkdebug_file = backing_img + ".blkdebug"
|
self.blkdebug_file = backing_img + ".blkdebug"
|
||||||
self.create_image(backing_img, TestErrors.image_len)
|
iotests.create_image(backing_img, TestErrors.image_len)
|
||||||
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 28)
|
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 28)
|
||||||
qemu_img('create', '-f', iotests.imgfmt,
|
qemu_img('create', '-f', iotests.imgfmt,
|
||||||
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
|
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
|
||||||
|
@ -379,7 +346,7 @@ class TestENOSPC(TestErrors):
|
||||||
os.remove(self.blkdebug_file)
|
os.remove(self.blkdebug_file)
|
||||||
|
|
||||||
def test_enospc(self):
|
def test_enospc(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
|
result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -413,10 +380,10 @@ class TestENOSPC(TestErrors):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
class TestStreamStop(ImageStreamingTestCase):
|
class TestStreamStop(iotests.QMPTestCase):
|
||||||
image_len = 8 * 1024 * 1024 * 1024 # GB
|
image_len = 8 * 1024 * 1024 * 1024 # GB
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -431,7 +398,7 @@ class TestStreamStop(ImageStreamingTestCase):
|
||||||
os.remove(backing_img)
|
os.remove(backing_img)
|
||||||
|
|
||||||
def test_stream_stop(self):
|
def test_stream_stop(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0')
|
result = self.vm.qmp('block-stream', device='drive0')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -442,7 +409,7 @@ class TestStreamStop(ImageStreamingTestCase):
|
||||||
|
|
||||||
self.cancel_and_wait()
|
self.cancel_and_wait()
|
||||||
|
|
||||||
class TestSetSpeed(ImageStreamingTestCase):
|
class TestSetSpeed(iotests.QMPTestCase):
|
||||||
image_len = 80 * 1024 * 1024 # MB
|
image_len = 80 * 1024 * 1024 # MB
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -459,7 +426,7 @@ class TestSetSpeed(ImageStreamingTestCase):
|
||||||
# This is a short performance test which is not run by default.
|
# This is a short performance test which is not run by default.
|
||||||
# Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput"
|
# Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput"
|
||||||
def perf_test_throughput(self):
|
def perf_test_throughput(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0')
|
result = self.vm.qmp('block-stream', device='drive0')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -477,10 +444,10 @@ class TestSetSpeed(ImageStreamingTestCase):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
def test_set_speed(self):
|
def test_set_speed(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0')
|
result = self.vm.qmp('block-stream', device='drive0')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -511,12 +478,12 @@ class TestSetSpeed(ImageStreamingTestCase):
|
||||||
self.cancel_and_wait()
|
self.cancel_and_wait()
|
||||||
|
|
||||||
def test_set_speed_invalid(self):
|
def test_set_speed_invalid(self):
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0', speed=-1)
|
result = self.vm.qmp('block-stream', device='drive0', speed=-1)
|
||||||
self.assert_qmp(result, 'error/class', 'GenericError')
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
||||||
|
|
||||||
self.assert_no_active_streams()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('block-stream', device='drive0')
|
result = self.vm.qmp('block-stream', device='drive0')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
|
@ -22,7 +22,6 @@ import time
|
||||||
import os
|
import os
|
||||||
import iotests
|
import iotests
|
||||||
from iotests import qemu_img, qemu_io
|
from iotests import qemu_img, qemu_io
|
||||||
import struct
|
|
||||||
|
|
||||||
backing_img = os.path.join(iotests.test_dir, 'backing.img')
|
backing_img = os.path.join(iotests.test_dir, 'backing.img')
|
||||||
target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img')
|
target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img')
|
||||||
|
@ -32,50 +31,28 @@ target_img = os.path.join(iotests.test_dir, 'target.img')
|
||||||
class ImageMirroringTestCase(iotests.QMPTestCase):
|
class ImageMirroringTestCase(iotests.QMPTestCase):
|
||||||
'''Abstract base class for image mirroring test cases'''
|
'''Abstract base class for image mirroring test cases'''
|
||||||
|
|
||||||
def assert_no_active_mirrors(self):
|
def wait_ready(self, drive='drive0'):
|
||||||
result = self.vm.qmp('query-block-jobs')
|
'''Wait until a block job BLOCK_JOB_READY event'''
|
||||||
self.assert_qmp(result, 'return', [])
|
ready = False
|
||||||
|
while not ready:
|
||||||
def cancel_and_wait(self, drive='drive0', wait_ready=True):
|
|
||||||
'''Cancel a block job and wait for it to finish'''
|
|
||||||
if wait_ready:
|
|
||||||
ready = False
|
|
||||||
while not ready:
|
|
||||||
for event in self.vm.get_qmp_events(wait=True):
|
|
||||||
if event['event'] == 'BLOCK_JOB_READY':
|
|
||||||
self.assert_qmp(event, 'data/type', 'mirror')
|
|
||||||
self.assert_qmp(event, 'data/device', drive)
|
|
||||||
ready = True
|
|
||||||
|
|
||||||
result = self.vm.qmp('block-job-cancel', device=drive,
|
|
||||||
force=not wait_ready)
|
|
||||||
self.assert_qmp(result, 'return', {})
|
|
||||||
|
|
||||||
cancelled = False
|
|
||||||
while not cancelled:
|
|
||||||
for event in self.vm.get_qmp_events(wait=True):
|
for event in self.vm.get_qmp_events(wait=True):
|
||||||
if event['event'] == 'BLOCK_JOB_COMPLETED' or \
|
if event['event'] == 'BLOCK_JOB_READY':
|
||||||
event['event'] == 'BLOCK_JOB_CANCELLED':
|
|
||||||
self.assert_qmp(event, 'data/type', 'mirror')
|
self.assert_qmp(event, 'data/type', 'mirror')
|
||||||
self.assert_qmp(event, 'data/device', drive)
|
self.assert_qmp(event, 'data/device', drive)
|
||||||
if wait_ready:
|
ready = True
|
||||||
self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
|
|
||||||
self.assert_qmp(event, 'data/offset', self.image_len)
|
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
|
||||||
cancelled = True
|
|
||||||
|
|
||||||
self.assert_no_active_mirrors()
|
def wait_ready_and_cancel(self, drive='drive0'):
|
||||||
|
self.wait_ready(drive)
|
||||||
|
event = self.cancel_and_wait()
|
||||||
|
self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
|
||||||
|
self.assert_qmp(event, 'data/type', 'mirror')
|
||||||
|
self.assert_qmp(event, 'data/offset', self.image_len)
|
||||||
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
|
|
||||||
def complete_and_wait(self, drive='drive0', wait_ready=True):
|
def complete_and_wait(self, drive='drive0', wait_ready=True):
|
||||||
'''Complete a block job and wait for it to finish'''
|
'''Complete a block job and wait for it to finish'''
|
||||||
if wait_ready:
|
if wait_ready:
|
||||||
ready = False
|
self.wait_ready()
|
||||||
while not ready:
|
|
||||||
for event in self.vm.get_qmp_events(wait=True):
|
|
||||||
if event['event'] == 'BLOCK_JOB_READY':
|
|
||||||
self.assert_qmp(event, 'data/type', 'mirror')
|
|
||||||
self.assert_qmp(event, 'data/device', drive)
|
|
||||||
ready = True
|
|
||||||
|
|
||||||
result = self.vm.qmp('block-job-complete', device=drive)
|
result = self.vm.qmp('block-job-complete', device=drive)
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
@ -91,43 +68,13 @@ class ImageMirroringTestCase(iotests.QMPTestCase):
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
def compare_images(self, img1, img2):
|
|
||||||
try:
|
|
||||||
qemu_img('convert', '-f', iotests.imgfmt, '-O', 'raw', img1, img1 + '.raw')
|
|
||||||
qemu_img('convert', '-f', iotests.imgfmt, '-O', 'raw', img2, img2 + '.raw')
|
|
||||||
file1 = open(img1 + '.raw', 'r')
|
|
||||||
file2 = open(img2 + '.raw', 'r')
|
|
||||||
return file1.read() == file2.read()
|
|
||||||
finally:
|
|
||||||
if file1 is not None:
|
|
||||||
file1.close()
|
|
||||||
if file2 is not None:
|
|
||||||
file2.close()
|
|
||||||
try:
|
|
||||||
os.remove(img1 + '.raw')
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
os.remove(img2 + '.raw')
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
class TestSingleDrive(ImageMirroringTestCase):
|
class TestSingleDrive(ImageMirroringTestCase):
|
||||||
image_len = 1 * 1024 * 1024 # MB
|
image_len = 1 * 1024 * 1024 # MB
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.create_image(backing_img, TestSingleDrive.image_len)
|
iotests.create_image(backing_img, TestSingleDrive.image_len)
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
|
||||||
self.vm = iotests.VM().add_drive(test_img)
|
self.vm = iotests.VM().add_drive(test_img)
|
||||||
self.vm.launch()
|
self.vm.launch()
|
||||||
|
@ -142,7 +89,7 @@ class TestSingleDrive(ImageMirroringTestCase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_complete(self):
|
def test_complete(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img)
|
target=target_img)
|
||||||
|
@ -152,37 +99,37 @@ class TestSingleDrive(ImageMirroringTestCase):
|
||||||
result = self.vm.qmp('query-block')
|
result = self.vm.qmp('query-block')
|
||||||
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_cancel(self):
|
def test_cancel(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img)
|
target=target_img)
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
self.cancel_and_wait(wait_ready=False)
|
self.cancel_and_wait(force=True)
|
||||||
result = self.vm.qmp('query-block')
|
result = self.vm.qmp('query-block')
|
||||||
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
def test_cancel_after_ready(self):
|
def test_cancel_after_ready(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img)
|
target=target_img)
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
self.cancel_and_wait()
|
self.wait_ready_and_cancel()
|
||||||
result = self.vm.qmp('query-block')
|
result = self.vm.qmp('query-block')
|
||||||
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_pause(self):
|
def test_pause(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img)
|
target=target_img)
|
||||||
|
@ -204,11 +151,11 @@ class TestSingleDrive(ImageMirroringTestCase):
|
||||||
|
|
||||||
self.complete_and_wait()
|
self.complete_and_wait()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_small_buffer(self):
|
def test_small_buffer(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
# A small buffer is rounded up automatically
|
# A small buffer is rounded up automatically
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
|
@ -219,11 +166,11 @@ class TestSingleDrive(ImageMirroringTestCase):
|
||||||
result = self.vm.qmp('query-block')
|
result = self.vm.qmp('query-block')
|
||||||
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_small_buffer2(self):
|
def test_small_buffer2(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'
|
||||||
% (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img)
|
% (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img)
|
||||||
|
@ -235,11 +182,11 @@ class TestSingleDrive(ImageMirroringTestCase):
|
||||||
result = self.vm.qmp('query-block')
|
result = self.vm.qmp('query-block')
|
||||||
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_large_cluster(self):
|
def test_large_cluster(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
|
||||||
% (TestSingleDrive.image_len, backing_img), target_img)
|
% (TestSingleDrive.image_len, backing_img), target_img)
|
||||||
|
@ -251,7 +198,7 @@ class TestSingleDrive(ImageMirroringTestCase):
|
||||||
result = self.vm.qmp('query-block')
|
result = self.vm.qmp('query-block')
|
||||||
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_medium_not_found(self):
|
def test_medium_not_found(self):
|
||||||
|
@ -273,15 +220,15 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
||||||
image_len = 2 * 1024 * 1024 # MB
|
image_len = 2 * 1024 * 1024 # MB
|
||||||
|
|
||||||
def complete_and_wait(self, drive='drive0', wait_ready=True):
|
def complete_and_wait(self, drive='drive0', wait_ready=True):
|
||||||
self.create_image(target_backing_img, TestMirrorNoBacking.image_len)
|
iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len)
|
||||||
return ImageMirroringTestCase.complete_and_wait(self, drive, wait_ready)
|
return ImageMirroringTestCase.complete_and_wait(self, drive, wait_ready)
|
||||||
|
|
||||||
def compare_images(self, img1, img2):
|
def compare_images(self, img1, img2):
|
||||||
self.create_image(target_backing_img, TestMirrorNoBacking.image_len)
|
iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len)
|
||||||
return ImageMirroringTestCase.compare_images(self, img1, img2)
|
return iotests.compare_images(img1, img2)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.create_image(backing_img, TestMirrorNoBacking.image_len)
|
iotests.create_image(backing_img, TestMirrorNoBacking.image_len)
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
|
||||||
self.vm = iotests.VM().add_drive(test_img)
|
self.vm = iotests.VM().add_drive(test_img)
|
||||||
self.vm.launch()
|
self.vm.launch()
|
||||||
|
@ -294,7 +241,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
||||||
os.remove(target_img)
|
os.remove(target_img)
|
||||||
|
|
||||||
def test_complete(self):
|
def test_complete(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
|
@ -309,14 +256,14 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_cancel(self):
|
def test_cancel(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
mode='existing', target=target_img)
|
mode='existing', target=target_img)
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
self.cancel_and_wait()
|
self.wait_ready_and_cancel()
|
||||||
result = self.vm.qmp('query-block')
|
result = self.vm.qmp('query-block')
|
||||||
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
@ -324,7 +271,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_large_cluster(self):
|
def test_large_cluster(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
# qemu-img create fails if the image is not there
|
# qemu-img create fails if the image is not there
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d'
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d'
|
||||||
|
@ -349,7 +296,7 @@ class TestMirrorResized(ImageMirroringTestCase):
|
||||||
image_len = 2 * 1024 * 1024 # MB
|
image_len = 2 * 1024 * 1024 # MB
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.create_image(backing_img, TestMirrorResized.backing_len)
|
iotests.create_image(backing_img, TestMirrorResized.backing_len)
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
|
||||||
qemu_img('resize', test_img, '2M')
|
qemu_img('resize', test_img, '2M')
|
||||||
self.vm = iotests.VM().add_drive(test_img)
|
self.vm = iotests.VM().add_drive(test_img)
|
||||||
|
@ -365,7 +312,7 @@ class TestMirrorResized(ImageMirroringTestCase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_complete_top(self):
|
def test_complete_top(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
|
||||||
target=target_img)
|
target=target_img)
|
||||||
|
@ -375,11 +322,11 @@ class TestMirrorResized(ImageMirroringTestCase):
|
||||||
result = self.vm.qmp('query-block')
|
result = self.vm.qmp('query-block')
|
||||||
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_complete_full(self):
|
def test_complete_full(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img)
|
target=target_img)
|
||||||
|
@ -389,7 +336,7 @@ class TestMirrorResized(ImageMirroringTestCase):
|
||||||
result = self.vm.qmp('query-block')
|
result = self.vm.qmp('query-block')
|
||||||
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
class TestReadErrors(ImageMirroringTestCase):
|
class TestReadErrors(ImageMirroringTestCase):
|
||||||
|
@ -424,7 +371,7 @@ new_state = "1"
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.blkdebug_file = backing_img + ".blkdebug"
|
self.blkdebug_file = backing_img + ".blkdebug"
|
||||||
self.create_image(backing_img, TestReadErrors.image_len)
|
iotests.create_image(backing_img, TestReadErrors.image_len)
|
||||||
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
|
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
|
||||||
qemu_img('create', '-f', iotests.imgfmt,
|
qemu_img('create', '-f', iotests.imgfmt,
|
||||||
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
|
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
|
||||||
|
@ -443,7 +390,7 @@ new_state = "1"
|
||||||
os.remove(self.blkdebug_file)
|
os.remove(self.blkdebug_file)
|
||||||
|
|
||||||
def test_report_read(self):
|
def test_report_read(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img)
|
target=target_img)
|
||||||
|
@ -467,11 +414,11 @@ new_state = "1"
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
def test_ignore_read(self):
|
def test_ignore_read(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img, on_source_error='ignore')
|
target=target_img, on_source_error='ignore')
|
||||||
|
@ -487,7 +434,7 @@ new_state = "1"
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
def test_large_cluster(self):
|
def test_large_cluster(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
# Test COW into the target image. The first half of the
|
# Test COW into the target image. The first half of the
|
||||||
# cluster at MIRROR_GRANULARITY has to be copied from
|
# cluster at MIRROR_GRANULARITY has to be copied from
|
||||||
|
@ -509,11 +456,11 @@ new_state = "1"
|
||||||
|
|
||||||
# Detach blkdebug to compare images successfully
|
# Detach blkdebug to compare images successfully
|
||||||
qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img)
|
qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img)
|
||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
def test_stop_read(self):
|
def test_stop_read(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img, on_source_error='stop')
|
target=target_img, on_source_error='stop')
|
||||||
|
@ -544,7 +491,7 @@ new_state = "1"
|
||||||
self.assert_qmp(result, 'return[0]/io-status', 'ok')
|
self.assert_qmp(result, 'return[0]/io-status', 'ok')
|
||||||
|
|
||||||
self.complete_and_wait(wait_ready=False)
|
self.complete_and_wait(wait_ready=False)
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
class TestWriteErrors(ImageMirroringTestCase):
|
class TestWriteErrors(ImageMirroringTestCase):
|
||||||
|
@ -579,7 +526,7 @@ new_state = "1"
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.blkdebug_file = target_img + ".blkdebug"
|
self.blkdebug_file = target_img + ".blkdebug"
|
||||||
self.create_image(backing_img, TestWriteErrors.image_len)
|
iotests.create_image(backing_img, TestWriteErrors.image_len)
|
||||||
self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
|
self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img)
|
qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img)
|
||||||
self.vm = iotests.VM().add_drive(test_img)
|
self.vm = iotests.VM().add_drive(test_img)
|
||||||
|
@ -594,7 +541,7 @@ new_state = "1"
|
||||||
os.remove(self.blkdebug_file)
|
os.remove(self.blkdebug_file)
|
||||||
|
|
||||||
def test_report_write(self):
|
def test_report_write(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
mode='existing', target=self.target_img)
|
mode='existing', target=self.target_img)
|
||||||
|
@ -618,11 +565,11 @@ new_state = "1"
|
||||||
self.assert_qmp(event, 'data/len', self.image_len)
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
completed = True
|
completed = True
|
||||||
|
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
def test_ignore_write(self):
|
def test_ignore_write(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
mode='existing', target=self.target_img,
|
mode='existing', target=self.target_img,
|
||||||
|
@ -639,7 +586,7 @@ new_state = "1"
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
def test_stop_write(self):
|
def test_stop_write(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
mode='existing', target=self.target_img,
|
mode='existing', target=self.target_img,
|
||||||
|
@ -671,7 +618,7 @@ new_state = "1"
|
||||||
ready = True
|
ready = True
|
||||||
|
|
||||||
self.complete_and_wait(wait_ready=False)
|
self.complete_and_wait(wait_ready=False)
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
|
||||||
class TestSetSpeed(ImageMirroringTestCase):
|
class TestSetSpeed(ImageMirroringTestCase):
|
||||||
|
@ -690,7 +637,7 @@ class TestSetSpeed(ImageMirroringTestCase):
|
||||||
os.remove(target_img)
|
os.remove(target_img)
|
||||||
|
|
||||||
def test_set_speed(self):
|
def test_set_speed(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img)
|
target=target_img)
|
||||||
|
@ -709,7 +656,7 @@ class TestSetSpeed(ImageMirroringTestCase):
|
||||||
self.assert_qmp(result, 'return[0]/device', 'drive0')
|
self.assert_qmp(result, 'return[0]/device', 'drive0')
|
||||||
self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
|
self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
|
||||||
|
|
||||||
self.cancel_and_wait()
|
self.wait_ready_and_cancel()
|
||||||
|
|
||||||
# Check setting speed in drive-mirror works
|
# Check setting speed in drive-mirror works
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
|
@ -720,16 +667,16 @@ class TestSetSpeed(ImageMirroringTestCase):
|
||||||
self.assert_qmp(result, 'return[0]/device', 'drive0')
|
self.assert_qmp(result, 'return[0]/device', 'drive0')
|
||||||
self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
|
self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
|
||||||
|
|
||||||
self.cancel_and_wait()
|
self.wait_ready_and_cancel()
|
||||||
|
|
||||||
def test_set_speed_invalid(self):
|
def test_set_speed_invalid(self):
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img, speed=-1)
|
target=target_img, speed=-1)
|
||||||
self.assert_qmp(result, 'error/class', 'GenericError')
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
||||||
|
|
||||||
self.assert_no_active_mirrors()
|
self.assert_no_active_block_jobs()
|
||||||
|
|
||||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
target=target_img)
|
target=target_img)
|
||||||
|
@ -738,7 +685,7 @@ class TestSetSpeed(ImageMirroringTestCase):
|
||||||
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
|
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
|
||||||
self.assert_qmp(result, 'error/class', 'GenericError')
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
||||||
|
|
||||||
self.cancel_and_wait()
|
self.wait_ready_and_cancel()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
iotests.main(supported_fmts=['qcow2', 'qed'])
|
iotests.main(supported_fmts=['qcow2', 'qed'])
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
QA output created by 054
|
QA output created by 054
|
||||||
|
|
||||||
creating too large image (1 EB)
|
creating too large image (1 EB)
|
||||||
qemu-img: The image size is too large for file format 'qcow2'
|
qemu-img: The image size is too large for file format 'qcow2' (try using a larger cluster size)
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976
|
||||||
|
|
||||||
creating too large image (1 EB) using qcow2.py
|
creating too large image (1 EB) using qcow2.py
|
||||||
|
|
|
@ -23,6 +23,7 @@ import string
|
||||||
import unittest
|
import unittest
|
||||||
import sys; sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'QMP'))
|
import sys; sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'QMP'))
|
||||||
import qmp
|
import qmp
|
||||||
|
import struct
|
||||||
|
|
||||||
__all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io',
|
__all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io',
|
||||||
'VM', 'QMPTestCase', 'notrun', 'main']
|
'VM', 'QMPTestCase', 'notrun', 'main']
|
||||||
|
@ -51,6 +52,21 @@ def qemu_io(*args):
|
||||||
args = qemu_io_args + list(args)
|
args = qemu_io_args + list(args)
|
||||||
return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0]
|
return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0]
|
||||||
|
|
||||||
|
def compare_images(img1, img2):
|
||||||
|
'''Return True if two image files are identical'''
|
||||||
|
return qemu_img('compare', '-f', imgfmt,
|
||||||
|
'-F', imgfmt, img1, img2) == 0
|
||||||
|
|
||||||
|
def create_image(name, size):
|
||||||
|
'''Create a fully-allocated raw image with sector markers'''
|
||||||
|
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 VM(object):
|
class VM(object):
|
||||||
'''A QEMU VM'''
|
'''A QEMU VM'''
|
||||||
|
|
||||||
|
@ -170,6 +186,28 @@ class QMPTestCase(unittest.TestCase):
|
||||||
result = self.dictpath(d, path)
|
result = self.dictpath(d, path)
|
||||||
self.assertEqual(result, value, 'values not equal "%s" and "%s"' % (str(result), str(value)))
|
self.assertEqual(result, value, 'values not equal "%s" and "%s"' % (str(result), str(value)))
|
||||||
|
|
||||||
|
def assert_no_active_block_jobs(self):
|
||||||
|
result = self.vm.qmp('query-block-jobs')
|
||||||
|
self.assert_qmp(result, 'return', [])
|
||||||
|
|
||||||
|
def cancel_and_wait(self, drive='drive0', force=False):
|
||||||
|
'''Cancel a block job and wait for it to finish, returning the event'''
|
||||||
|
result = self.vm.qmp('block-job-cancel', device=drive, force=force)
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
cancelled = False
|
||||||
|
result = None
|
||||||
|
while not cancelled:
|
||||||
|
for event in self.vm.get_qmp_events(wait=True):
|
||||||
|
if event['event'] == 'BLOCK_JOB_COMPLETED' or \
|
||||||
|
event['event'] == 'BLOCK_JOB_CANCELLED':
|
||||||
|
self.assert_qmp(event, 'data/device', drive)
|
||||||
|
result = event
|
||||||
|
cancelled = True
|
||||||
|
|
||||||
|
self.assert_no_active_block_jobs()
|
||||||
|
return result
|
||||||
|
|
||||||
def notrun(reason):
|
def notrun(reason):
|
||||||
'''Skip this test suite'''
|
'''Skip this test suite'''
|
||||||
# Each test in qemu-iotests has a number ("seq")
|
# Each test in qemu-iotests has a number ("seq")
|
||||||
|
|
Loading…
Reference in New Issue