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
This commit is contained in:
Daniel Veillard 2010-04-01 17:57:32 +02:00
parent 7a3da17a58
commit 6e41f30efc
2 changed files with 234 additions and 17 deletions

View File

@ -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;

View File

@ -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 */
};