Copy snapshots out of QCOW2 disk
In order to backup snapshots, created from QCOW2 iamge, we want to copy snapshots out of QCOW2 disk to a seperate storage. The following patch adds a new option in "qemu-img": qemu-img convert -f qcow2 -O qcow2 -s snapshot_name src_img bck_img. Right now, it only supports to copy the full snapshot, delta snapshot is on the way. Changes from V1: all the comments from Kevin are addressed: Add read-only checking Fix coding style Change the name from bdrv_snapshot_load to bdrv_snapshot_load_tmp Signed-off-by: Disheng Su <edison@cloud.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
a58b8d5401
commit
51ef67270b
16
block.c
16
block.c
@ -1899,6 +1899,22 @@ int bdrv_snapshot_list(BlockDriverState *bs,
|
|||||||
return -ENOTSUP;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
#define NB_SUFFIXES 4
|
#define NB_SUFFIXES 4
|
||||||
|
|
||||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size)
|
char *get_human_readable_size(char *buf, int buf_size, int64_t size)
|
||||||
|
2
block.h
2
block.h
@ -211,6 +211,8 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
|||||||
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
||||||
int bdrv_snapshot_list(BlockDriverState *bs,
|
int bdrv_snapshot_list(BlockDriverState *bs,
|
||||||
QEMUSnapshotInfo **psn_info);
|
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 *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn);
|
||||||
|
|
||||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size);
|
char *get_human_readable_size(char *buf, int buf_size, int64_t size);
|
||||||
|
@ -418,3 +418,34 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
|||||||
return s->nb_snapshots;
|
return s->nb_snapshots;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
|
||||||
|
{
|
||||||
|
int i, snapshot_index, l1_size2;
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
QCowSnapshot *sn;
|
||||||
|
|
||||||
|
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name);
|
||||||
|
if (snapshot_index < 0) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
sn = &s->snapshots[snapshot_index];
|
||||||
|
s->l1_size = sn->l1_size;
|
||||||
|
l1_size2 = s->l1_size * sizeof(uint64_t);
|
||||||
|
if (s->l1_table != NULL) {
|
||||||
|
qemu_free(s->l1_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->l1_table_offset = sn->l1_table_offset;
|
||||||
|
s->l1_table = qemu_mallocz(align_offset(l1_size2, 512));
|
||||||
|
|
||||||
|
if (bdrv_pread(bs->file, sn->l1_table_offset,
|
||||||
|
s->l1_table, l1_size2) != l1_size2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0;i < s->l1_size; i++) {
|
||||||
|
be64_to_cpus(&s->l1_table[i]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -1286,6 +1286,7 @@ static BlockDriver bdrv_qcow2 = {
|
|||||||
.bdrv_snapshot_goto = qcow2_snapshot_goto,
|
.bdrv_snapshot_goto = qcow2_snapshot_goto,
|
||||||
.bdrv_snapshot_delete = qcow2_snapshot_delete,
|
.bdrv_snapshot_delete = qcow2_snapshot_delete,
|
||||||
.bdrv_snapshot_list = qcow2_snapshot_list,
|
.bdrv_snapshot_list = qcow2_snapshot_list,
|
||||||
|
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
|
||||||
.bdrv_get_info = qcow_get_info,
|
.bdrv_get_info = qcow_get_info,
|
||||||
|
|
||||||
.bdrv_save_vmstate = qcow_save_vmstate,
|
.bdrv_save_vmstate = qcow_save_vmstate,
|
||||||
|
@ -211,6 +211,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
|
|||||||
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
|
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
|
||||||
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
||||||
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
|
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
|
||||||
|
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
|
||||||
|
|
||||||
void qcow2_free_snapshots(BlockDriverState *bs);
|
void qcow2_free_snapshots(BlockDriverState *bs);
|
||||||
int qcow2_read_snapshots(BlockDriverState *bs);
|
int qcow2_read_snapshots(BlockDriverState *bs);
|
||||||
|
@ -93,6 +93,8 @@ struct BlockDriver {
|
|||||||
int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
|
int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
|
||||||
int (*bdrv_snapshot_list)(BlockDriverState *bs,
|
int (*bdrv_snapshot_list)(BlockDriverState *bs,
|
||||||
QEMUSnapshotInfo **psn_info);
|
QEMUSnapshotInfo **psn_info);
|
||||||
|
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
|
||||||
|
const char *snapshot_name);
|
||||||
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
|
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||||
|
|
||||||
int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,
|
int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,
|
||||||
|
@ -28,9 +28,9 @@ STEXI
|
|||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
DEF("convert", img_convert,
|
DEF("convert", img_convert,
|
||||||
"convert [-c] [-f fmt] [-O output_fmt] [-o options] filename [filename2 [...]] output_filename")
|
"convert [-c] [-f fmt] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename")
|
||||||
STEXI
|
STEXI
|
||||||
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
DEF("info", img_info,
|
DEF("info", img_info,
|
||||||
|
19
qemu-img.c
19
qemu-img.c
@ -646,13 +646,14 @@ static int img_convert(int argc, char **argv)
|
|||||||
BlockDriverInfo bdi;
|
BlockDriverInfo bdi;
|
||||||
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
||||||
char *options = NULL;
|
char *options = NULL;
|
||||||
|
const char *snapshot_name = NULL;
|
||||||
|
|
||||||
fmt = NULL;
|
fmt = NULL;
|
||||||
out_fmt = "raw";
|
out_fmt = "raw";
|
||||||
out_baseimg = NULL;
|
out_baseimg = NULL;
|
||||||
flags = 0;
|
flags = 0;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
c = getopt(argc, argv, "f:O:B:hce6o:");
|
c = getopt(argc, argv, "f:O:B:s:hce6o:");
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
switch(c) {
|
switch(c) {
|
||||||
@ -680,6 +681,9 @@ static int img_convert(int argc, char **argv)
|
|||||||
case 'o':
|
case 'o':
|
||||||
options = optarg;
|
options = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
snapshot_name = optarg;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,6 +715,19 @@ static int img_convert(int argc, char **argv)
|
|||||||
total_sectors += bs_sectors;
|
total_sectors += bs_sectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (snapshot_name != NULL) {
|
||||||
|
if (bs_n > 1) {
|
||||||
|
error("No support for concatenating multiple snapshot\n");
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (bdrv_snapshot_load_tmp(bs[0], snapshot_name) < 0) {
|
||||||
|
error("Failed to load snapshot\n");
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Find driver and parse its options */
|
/* Find driver and parse its options */
|
||||||
drv = bdrv_find_format(out_fmt);
|
drv = bdrv_find_format(out_fmt);
|
||||||
if (!drv) {
|
if (!drv) {
|
||||||
|
@ -77,9 +77,9 @@ it doesn't need to be specified separately in this case.
|
|||||||
|
|
||||||
Commit the changes recorded in @var{filename} in its base image.
|
Commit the changes recorded in @var{filename} in its base image.
|
||||||
|
|
||||||
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||||
|
|
||||||
Convert the disk image @var{filename} to disk image @var{output_filename}
|
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
|
||||||
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
|
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
|
||||||
option) or use any format specific options like encryption (@code{-o} option).
|
option) or use any format specific options like encryption (@code{-o} option).
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user