From 6e41f30efcac08e50b21d9c943d6d27e90555951 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Thu, 1 Apr 2010 17:57:32 +0200 Subject: [PATCH] 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 --- src/qemu/qemu_conf.h | 1 + src/qemu/qemu_driver.c | 250 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 234 insertions(+), 17 deletions(-) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 39518ca7ef..0b247d6a7f 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 94f7fef522..eea49bb124 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,183 @@ 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) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + char *name = NULL; + int ret = -1; + int compressed; + + if (flags != 0) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("unsupported flags (0x%x) passed to '%s'"), + flags, __FUNCTION__); + return -1; + } + + 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) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + char *name = NULL; + + if (flags != 0) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("unsupported flags (0x%x) passed to '%s'"), + flags, __FUNCTION__); + return -1; + } + + 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) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + char *name = NULL; + + if (flags != 0) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("unsupported flags (0x%x) passed to '%s'"), + flags, __FUNCTION__); + return -1; + } + + 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 +6164,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 +6186,35 @@ 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 + * one should check for 0 anyway + */ + 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 +6226,7 @@ endjob: vm = NULL; cleanup: + VIR_FREE(managed_save); if (vm) virDomainObjUnlock(vm); if (event) @@ -10312,9 +10528,9 @@ static virDriver qemuDriver = { qemuDomainMigrateSetMaxDowntime, /* domainMigrateSetMaxDowntime */ qemuDomainEventRegisterAny, /* domainEventRegisterAny */ qemuDomainEventDeregisterAny, /* domainEventDeregisterAny */ - NULL, /* domainManagedSave */ - NULL, /* domainHasManagedSaveImage */ - NULL, /* domainManagedSaveRemove */ + qemuDomainManagedSave, /* domainManagedSave */ + qemuDomainHasManagedSaveImage, /* domainHasManagedSaveImage */ + qemuDomainManagedSaveRemove, /* domainManagedSaveRemove */ };