[ v2 with new saving path, bug fix and typo fix ]
Implement managed save operations for qemu driver
The images are saved in /var/lib/libvirt/qemu/save/
and named $domainname.save . The directory is created appropriately
at daemon startup. When a domain is started while a saved image is
available, libvirt will try to load this saved image, and start the
domain as usual in case of failure. In any case the saved image is
discarded once the domain is created.
* src/qemu/qemu_conf.h: adds an extra save path to the driver config
* src/qemu/qemu_driver.c: implement the 3 new operations and handling
of the image directory
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 39518ca..0b247d6 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -120,6 +120,7 @@ struct qemud_driver {
* the QEMU user/group */
char *libDir;
char *cacheDir;
+ char *saveDir;
unsigned int vncTLS : 1;
unsigned int vncTLSx509verify : 1;
unsigned int vncSASL : 1;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 94f7fef..5a678c9 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -1399,6 +1399,9 @@ qemudStartup(int privileged) {
if (virAsprintf(&qemu_driver->cacheDir,
"%s/cache/libvirt/qemu", LOCAL_STATE_DIR) == -1)
goto out_of_memory;
+ if (virAsprintf(&qemu_driver->saveDir,
+ "%s/lib/libvirt/qemu/save/", LOCAL_STATE_DIR) == -1)
+ goto out_of_memory;
} else {
uid_t uid = geteuid();
char *userdir = virGetUserDirectory(uid);
@@ -1423,6 +1426,8 @@ qemudStartup(int privileged) {
goto out_of_memory;
if (virAsprintf(&qemu_driver->cacheDir, "%s/qemu/cache", base)
== -1)
goto out_of_memory;
+ if (virAsprintf(&qemu_driver->saveDir, "%s/qemu/save", base) ==
-1)
+ goto out_of_memory;
}
if (virFileMakePath(qemu_driver->stateDir) != 0) {
@@ -1443,6 +1448,12 @@ qemudStartup(int privileged) {
qemu_driver->cacheDir, virStrerror(errno, ebuf, sizeof ebuf));
goto error;
}
+ if (virFileMakePath(qemu_driver->saveDir) != 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to create save dir '%s': %s"),
+ qemu_driver->saveDir, virStrerror(errno, ebuf, sizeof ebuf));
+ goto error;
+ }
/* Configuration paths are either ~/.libvirt/qemu/... (session) or
* /etc/libvirt/qemu/... (system).
@@ -1493,6 +1504,12 @@ qemudStartup(int privileged) {
qemu_driver->cacheDir, qemu_driver->user,
qemu_driver->group);
goto error;
}
+ if (chown(qemu_driver->saveDir, qemu_driver->user, qemu_driver->group)
< 0) {
+ virReportSystemError(errno,
+ _("unable to set ownership of '%s' to
%d:%d"),
+ qemu_driver->saveDir, qemu_driver->user,
qemu_driver->group);
+ goto error;
+ }
}
/* If hugetlbfs is present, then we need to create a sub-directory within
@@ -1645,6 +1662,7 @@ qemudShutdown(void) {
VIR_FREE(qemu_driver->stateDir);
VIR_FREE(qemu_driver->libDir);
VIR_FREE(qemu_driver->cacheDir);
+ VIR_FREE(qemu_driver->saveDir);
VIR_FREE(qemu_driver->vncTLSx509certdir);
VIR_FREE(qemu_driver->vncListen);
VIR_FREE(qemu_driver->vncPassword);
@@ -4570,8 +4588,8 @@ endjob:
}
-static int qemudDomainSave(virDomainPtr dom,
- const char *path)
+static int qemudDomainSaveFlag(virDomainPtr dom, const char *path,
+ int compressed)
{
struct qemud_driver *driver = dom->conn->privateData;
virDomainObjPtr vm = NULL;
@@ -4589,18 +4607,8 @@ static int qemudDomainSave(virDomainPtr dom,
header.version = QEMUD_SAVE_VERSION;
qemuDriverLock(driver);
- if (driver->saveImageFormat == NULL)
- header.compressed = QEMUD_SAVE_FORMAT_RAW;
- else {
- header.compressed =
- qemudSaveCompressionTypeFromString(driver->saveImageFormat);
- if (header.compressed < 0) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- "%s", _("Invalid save image format specified
"
- "in configuration file"));
- goto cleanup;
- }
- }
+
+ header.compressed = compressed;
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -4829,6 +4837,165 @@ cleanup:
return ret;
}
+static int qemudDomainSave(virDomainPtr dom, const char *path)
+{
+ struct qemud_driver *driver = dom->conn->privateData;
+ int compressed;
+
+ /* Hm, is this safe against qemudReload? */
+ if (driver->saveImageFormat == NULL)
+ compressed = QEMUD_SAVE_FORMAT_RAW;
+ else {
+ compressed = qemudSaveCompressionTypeFromString(driver->saveImageFormat);
+ if (compressed < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("Invalid save image format specified
"
+ "in configuration file"));
+ return -1;
+ }
+ }
+
+ return qemudDomainSaveFlag(dom, path, compressed);
+}
+
+static char *
+qemuDomainManagedSavePath(struct qemud_driver *driver, virDomainObjPtr vm) {
+ char *ret;
+
+ if (virAsprintf(&ret, "%s/%s.save", driver->saveDir,
vm->def->name) < 0) {
+ virReportOOMError();
+ return(NULL);
+ }
+
+ return(ret);
+}
+
+static int
+qemuDomainManagedSave(virDomainPtr dom,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ struct qemud_driver *driver = dom->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ char *name = NULL;
+ int ret = -1;
+ int compressed;
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ goto error;
+ }
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto error;
+ }
+
+ name = qemuDomainManagedSavePath(driver, vm);
+ if (name == NULL)
+ goto error;
+
+ VIR_DEBUG("Saving state to %s", name);
+
+ /* FIXME: we should take the flags parameter, and use bits out
+ * of there to control whether we are compressing or not
+ */
+ compressed = QEMUD_SAVE_FORMAT_RAW;
+
+ /* we have to drop our locks here because qemudDomainSaveFlag is
+ * going to pick them back up. Unfortunately it opens a race window
+ * between us dropping and qemudDomainSaveFlag picking it back up, but
+ * if we want to allow other operations to be able to happen while
+ * qemuDomainSaveFlag is running (possibly for a long time), I'm not
+ * sure if there is a better solution
+ */
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+
+ ret = qemudDomainSaveFlag(dom, name, compressed);
+
+cleanup:
+ VIR_FREE(name);
+
+ return ret;
+
+error:
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ goto cleanup;
+}
+
+static int
+qemuDomainHasManagedSaveImage(virDomainPtr dom,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ struct qemud_driver *driver = dom->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ int ret = -1;
+ char *name = NULL;
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ goto cleanup;
+ }
+
+ name = qemuDomainManagedSavePath(driver, vm);
+ if (name == NULL)
+ goto cleanup;
+
+ ret = virFileExists(name);
+
+cleanup:
+ VIR_FREE(name);
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ return ret;
+}
+
+static int
+qemuDomainManagedSaveRemove(virDomainPtr dom,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ struct qemud_driver *driver = dom->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ int ret = -1;
+ char *name = NULL;
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ goto cleanup;
+ }
+
+ name = qemuDomainManagedSavePath(driver, vm);
+ if (name == NULL)
+ goto cleanup;
+
+ ret = unlink(name);
+
+cleanup:
+ VIR_FREE(name);
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ return ret;
+}
static int qemudDomainCoreDump(virDomainPtr dom,
const char *path,
@@ -5979,6 +6146,7 @@ static int qemudDomainStart(virDomainPtr dom) {
virDomainObjPtr vm;
int ret = -1;
virDomainEventPtr event = NULL;
+ char *managed_save = NULL;
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -6000,6 +6168,32 @@ static int qemudDomainStart(virDomainPtr dom) {
goto endjob;
}
+ /*
+ * If there is a managed saved state restore it instead of starting
+ * from scratch. In any case the old state is removed.
+ */
+ managed_save = qemuDomainManagedSavePath(driver, vm);
+ if ((managed_save) && (virFileExists(managed_save))) {
+ /* We should still have a reference left to vm but */
+ if (qemuDomainObjEndJob(vm) == 0)
+ vm = NULL;
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ ret = qemudDomainRestore(dom->conn, managed_save);
+
+ if (unlink(managed_save) < 0) {
+ VIR_WARN("Failed to remove the managed state %s", managed_save);
+ }
+
+ if (ret == 0) {
+ /* qemudDomainRestore should have sent the Started/Restore event */
+ VIR_FREE(managed_save);
+ return(ret);
+ }
+ qemuDriverLock(driver);
+ virDomainObjLock(vm);
+ }
+
ret = qemudStartVMDaemon(dom->conn, driver, vm, NULL, -1);
if (ret != -1)
event = virDomainEventNewFromObj(vm,
@@ -6011,6 +6205,7 @@ endjob:
vm = NULL;
cleanup:
+ VIR_FREE(managed_save);
if (vm)
virDomainObjUnlock(vm);
if (event)
@@ -10312,9 +10507,9 @@ static virDriver qemuDriver = {
qemuDomainMigrateSetMaxDowntime, /* domainMigrateSetMaxDowntime */
qemuDomainEventRegisterAny, /* domainEventRegisterAny */
qemuDomainEventDeregisterAny, /* domainEventDeregisterAny */
- NULL, /* domainManagedSave */
- NULL, /* domainHasManagedSaveImage */
- NULL, /* domainManagedSaveRemove */
+ qemuDomainManagedSave, /* domainManagedSave */
+ qemuDomainHasManagedSaveImage, /* domainHasManagedSaveImage */
+ qemuDomainManagedSaveRemove, /* domainManagedSaveRemove */
};
--
Daniel Veillard | libxml Gnome XML XSLT toolkit
http://xmlsoft.org/
daniel(a)veillard.com | Rpmfind RPM search engine
http://rpmfind.net/
http://veillard.com/ | virtualization library
http://libvirt.org/