提交 9b5a514b 编写于 作者: P Peter Krempa

snapshot: qemu: Add support for external inactive snapshots

This patch adds support for external disk snapshots of inactive domains.
The snapshot is created by calling using qemu-img by calling:

 qemu-img create -f format_of_snapshot -o
 backing_file=/path/to/src,backing_fmt=format_of_backing_image
 /path/to/snapshot

in case the backing image format is known or probing is allowed and
otherwise:

 qemu-img create -f format_of_snapshot -o  backing_file=/path/to/src
 /path/to/snapshot

on each of the disks selected for snapshotting. This patch also modifies
the snapshot preparing function to support creating external snapshots
and to sanitize arguments. For now the user isn't able to mix external
and internal snapshots but this restriction might be lifted in the
future.
上级 d49adae2
...@@ -10658,13 +10658,126 @@ qemuDomainSnapshotFSThaw(struct qemud_driver *driver, ...@@ -10658,13 +10658,126 @@ qemuDomainSnapshotFSThaw(struct qemud_driver *driver,
/* The domain is expected to be locked and inactive. */ /* The domain is expected to be locked and inactive. */
static int static int
qemuDomainSnapshotCreateInactive(struct qemud_driver *driver, qemuDomainSnapshotCreateInactiveInternal(struct qemud_driver *driver,
virDomainObjPtr vm, virDomainObjPtr vm,
virDomainSnapshotObjPtr snap) virDomainSnapshotObjPtr snap)
{ {
return qemuDomainSnapshotForEachQcow2(driver, vm, snap, "-c", false); return qemuDomainSnapshotForEachQcow2(driver, vm, snap, "-c", false);
} }
/* The domain is expected to be locked and inactive. */
static int
qemuDomainSnapshotCreateInactiveExternal(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainSnapshotObjPtr snap,
bool reuse)
{
int i;
virDomainSnapshotDiskDefPtr snapdisk;
virDomainDiskDefPtr defdisk;
virCommandPtr cmd = NULL;
const char *qemuImgPath;
virBitmapPtr created;
int ret = -1;
if (!(qemuImgPath = qemuFindQemuImgBinary(driver)))
return -1;
if (!(created = virBitmapNew(snap->def->ndisks))) {
virReportOOMError();
return -1;
}
/* If reuse is true, then qemuDomainSnapshotPrepare already
* ensured that the new files exist, and it was up to the user to
* create them correctly. */
for (i = 0; i < snap->def->ndisks && !reuse; i++) {
snapdisk = &(snap->def->disks[i]);
defdisk = snap->def->dom->disks[snapdisk->index];
if (snapdisk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
continue;
if (!snapdisk->format)
snapdisk->format = VIR_STORAGE_FILE_QCOW2;
/* creates cmd line args: qemu-img create -f qcow2 -o */
if (!(cmd = virCommandNewArgList(qemuImgPath,
"create",
"-f",
virStorageFileFormatTypeToString(snapdisk->format),
"-o",
NULL)))
goto cleanup;
if (defdisk->format > 0) {
/* adds cmd line arg: backing_file=/path/to/backing/file,backing_fmd=format */
virCommandAddArgFormat(cmd, "backing_file=%s,backing_fmt=%s",
defdisk->src,
virStorageFileFormatTypeToString(defdisk->format));
} else {
if (!driver->allowDiskFormatProbing) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unknown image format of '%s' and "
"format probing is disabled"),
defdisk->src);
goto cleanup;
}
/* adds cmd line arg: backing_file=/path/to/backing/file */
virCommandAddArgFormat(cmd, "backing_file=%s", defdisk->src);
}
/* adds cmd line args: /path/to/target/file */
virCommandAddArg(cmd, snapdisk->file);
/* If the target does not exist, we're going to create it possibly */
if (!virFileExists(snapdisk->file))
ignore_value(virBitmapSetBit(created, i));
if (virCommandRun(cmd, NULL) < 0)
goto cleanup;
virCommandFree(cmd);
cmd = NULL;
}
/* update disk definitions */
for (i = 0; i < snap->def->ndisks; i++) {
snapdisk = &(snap->def->disks[i]);
defdisk = vm->def->disks[snapdisk->index];
if (snapdisk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
VIR_FREE(defdisk->src);
if (!(defdisk->src = strdup(snapdisk->file))) {
/* we cannot rollback here in a sane way */
virReportOOMError();
goto cleanup;
}
defdisk->format = snapdisk->format;
}
}
ret = 0;
cleanup:
virCommandFree(cmd);
/* unlink images if creation has failed */
if (ret < 0) {
ssize_t bit = -1;
while ((bit = virBitmapNextSetBit(created, bit)) >= 0) {
snapdisk = &(snap->def->disks[bit]);
if (unlink(snapdisk->file) < 0)
VIR_WARN("Failed to remove snapshot image '%s'",
snapdisk->file);
}
}
virBitmapFree(created);
return ret;
}
/* The domain is expected to be locked and active. */ /* The domain is expected to be locked and active. */
static int static int
...@@ -10758,11 +10871,11 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, ...@@ -10758,11 +10871,11 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def,
{ {
int ret = -1; int ret = -1;
int i; int i;
bool found = false;
bool active = virDomainObjIsActive(vm); bool active = virDomainObjIsActive(vm);
struct stat st; struct stat st;
bool reuse = (*flags & VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT) != 0; bool reuse = (*flags & VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT) != 0;
bool atomic = (*flags & VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC) != 0; bool atomic = (*flags & VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC) != 0;
bool found_internal = false;
int external = 0; int external = 0;
qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjPrivatePtr priv = vm->privateData;
...@@ -10783,7 +10896,6 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, ...@@ -10783,7 +10896,6 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def,
dom_disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK && dom_disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK &&
(dom_disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG || (dom_disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG ||
dom_disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_RBD)) { dom_disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_RBD)) {
found = true;
break; break;
} }
if (vm->def->disks[i]->format > 0 && if (vm->def->disks[i]->format > 0 &&
...@@ -10803,7 +10915,7 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, ...@@ -10803,7 +10915,7 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def,
disk->name); disk->name);
goto cleanup; goto cleanup;
} }
found = true; found_internal = true;
break; break;
case VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL: case VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL:
...@@ -10837,7 +10949,6 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, ...@@ -10837,7 +10949,6 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def,
disk->name, disk->file); disk->name, disk->file);
goto cleanup; goto cleanup;
} }
found = true;
external++; external++;
break; break;
...@@ -10852,15 +10963,37 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, ...@@ -10852,15 +10963,37 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def,
} }
} }
/* external snapshot is possible without specifying a disk to snapshot */ /* internal snapshot requires a disk image to store the memory image to */
if (!found && if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL &&
def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) { !found_internal) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("internal and disk-only snapshots require at least " _("internal checkpoints require at least "
"one disk to be selected for snapshot")); "one disk to be selected for snapshot"));
goto cleanup; goto cleanup;
} }
/* disk snapshot requires at least one disk */
if (def->state == VIR_DOMAIN_DISK_SNAPSHOT && !external) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disk-only snapshots require at least "
"one disk to be selected for snapshot"));
goto cleanup;
}
/* For now, we don't allow mixing internal and external disks.
* XXX technically, we could mix internal and external disks for
* offline snapshots */
if (found_internal && external) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("mixing internal and external snapshots is not "
"supported yet"));
goto cleanup;
}
/* Alter flags to let later users know what we learned. */
if (external && !active)
*flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
if (def->state != VIR_DOMAIN_DISK_SNAPSHOT && active) { if (def->state != VIR_DOMAIN_DISK_SNAPSHOT && active) {
if (external == 1 || if (external == 1 ||
qemuCapsGet(priv->caps, QEMU_CAPS_TRANSACTION)) { qemuCapsGet(priv->caps, QEMU_CAPS_TRANSACTION)) {
...@@ -11360,6 +11493,25 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, ...@@ -11360,6 +11493,25 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
parse_flags))) parse_flags)))
goto cleanup; goto cleanup;
/* reject the VIR_DOMAIN_SNAPSHOT_CREATE_LIVE flag where not supported */
if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_LIVE &&
(!virDomainObjIsActive(vm) ||
def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL ||
flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE)) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("live snapshot creation is supported only "
"with external checkpoints"));
goto cleanup;
}
if ((def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL ||
def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL) &&
flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("disk-only snapshot creation is not compatible with "
"memory snapshot"));
goto cleanup;
}
if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) { if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) {
/* Prevent circular chains */ /* Prevent circular chains */
if (def->parent) { if (def->parent) {
...@@ -11472,15 +11624,12 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, ...@@ -11472,15 +11624,12 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
goto cleanup; goto cleanup;
if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) { if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) {
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disk snapshots of inactive domains not "
"implemented yet"));
goto cleanup;
}
align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL; align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
align_match = false; align_match = false;
def->state = VIR_DOMAIN_DISK_SNAPSHOT; if (virDomainObjIsActive(vm))
def->state = VIR_DOMAIN_DISK_SNAPSHOT;
else
def->state = VIR_DOMAIN_SHUTOFF;
def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE; def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE;
} else if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) { } else if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
def->state = virDomainObjGetState(vm, NULL); def->state = virDomainObjGetState(vm, NULL);
...@@ -11523,25 +11672,6 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, ...@@ -11523,25 +11672,6 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
} }
} }
/* reject the VIR_DOMAIN_SNAPSHOT_CREATE_LIVE flag where not supported */
if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_LIVE &&
(!virDomainObjIsActive(vm) ||
snap->def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL ||
flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE)) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("live snapshot creation is supported only "
"with external checkpoints"));
goto cleanup;
}
if ((snap->def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL ||
snap->def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL) &&
flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("disk-only snapshot creation is not compatible with "
"memory snapshot"));
goto cleanup;
}
/* actually do the snapshot */ /* actually do the snapshot */
if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) { if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) {
/* XXX Should we validate that the redefined snapshot even /* XXX Should we validate that the redefined snapshot even
...@@ -11561,9 +11691,19 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, ...@@ -11561,9 +11691,19 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
goto cleanup; goto cleanup;
} }
} else { } else {
/* inactive */ /* inactive; qemuDomainSnapshotPrepare guaranteed that we
if (qemuDomainSnapshotCreateInactive(driver, vm, snap) < 0) * aren't mixing internal and external, and altered flags to
goto cleanup; * contain DISK_ONLY if there is an external disk. */
if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) {
bool reuse = !!(flags & VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT);
if (qemuDomainSnapshotCreateInactiveExternal(driver, vm, snap,
reuse) < 0)
goto cleanup;
} else {
if (qemuDomainSnapshotCreateInactiveInternal(driver, vm, snap) < 0)
goto cleanup;
}
} }
/* If we fail after this point, there's not a whole lot we can /* If we fail after this point, there's not a whole lot we can
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册