On Fri, Sep 26, 2008 at 04:47:05PM +0100, Daniel P. Berrange wrote: > On Fri, Sep 26, 2008 at 05:41:02PM +0200, Guido G?nther wrote: > > On Fri, Sep 26, 2008 at 09:51:26AM -0400, Cole Robinson wrote: > > > It's easy to forget, but anytime you update libvirt you will > > > need to restart libvirtd. Sounds like that could be the problem. > > The Ubuntu packages are based on the Debian ones and I dropped the > > daemon restart from the postinst so we don't tear down all running > > machines - so this is likely the problem. > > Did anybody work on saving enough state to restart libvirtd while > > keeping the VMs running? If not I might give it a whirl once I find some > > time. > > We've got this problem sorted in the 'lxc' driver and need to apply > the same approach to the QEMU driver. The problem was always finding > the Pseduo-TTY/PID for the monitor after restart, and associating the > live config with it. We've "solved" that in LXC driver by savingt the > live XML & PID to /var/run/libvirt/lxc/. It basically needs the same > approach to be applied to the QEMU daemons. In addition the DNSmasq > deamon needs adapting for simiarl reasons. Attached is a very early patch. It keeps the qemu instances running and writes out their states upon shutdown. Reconnecting (and therefore sending monitor commands, etc.) works but since stdout/stderr don't get picked up again yet we don't handle virEventAddhandle. I'll grab them from /proc/<pid>/fd/{1,2} later. Network state is missing too, but since this is moving into a separate file it's becoming a different matter anyway. -- Guido
Subject: [PATCH] let qemu/kvm survive libvirtd restarts --- src/domain_conf.c | 1 + src/domain_conf.h | 1 + src/qemu_conf.h | 1 + src/qemu_driver.c | 264 +++++++++++++++++++++++++++++++++++++++++++++++++---- src/util.c | 4 +- src/util.h | 1 + 6 files changed, 254 insertions(+), 18 deletions(-) diff --git a/src/domain_conf.c b/src/domain_conf.c index 6a35064..aa102f6 100644 --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -420,6 +420,7 @@ void virDomainObjFree(virDomainObjPtr dom) virDomainDefFree(dom->def); virDomainDefFree(dom->newDef); + VIR_FREE(dom->monitorpath); VIR_FREE(dom->vcpupids); VIR_FREE(dom); diff --git a/src/domain_conf.h b/src/domain_conf.h index 632e5ad..1ac1561 100644 --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -448,6 +448,7 @@ struct _virDomainObj { int stdout_fd; int stderr_fd; int monitor; + char *monitorpath; int logfile; int pid; int state; diff --git a/src/qemu_conf.h b/src/qemu_conf.h index 88dfade..f47582e 100644 --- a/src/qemu_conf.h +++ b/src/qemu_conf.h @@ -62,6 +62,7 @@ struct qemud_driver { char *networkConfigDir; char *networkAutostartDir; char *logDir; + char *stateDir; unsigned int vncTLS : 1; unsigned int vncTLSx509verify : 1; char *vncTLSx509certdir; diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 806608d..87789a9 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -172,6 +172,181 @@ void qemudAutostartConfigs(struct qemud_driver *driver) { } #ifdef WITH_LIBVIRTD + +static int +qemudFileWriteMonitor(const char *dir, + const char *name, + const char *path) +{ + int rc; + int fd; + FILE *file = NULL; + char *monitorfile = NULL; + + if ((rc = virFileMakePath(dir))) + goto cleanup; + + if (asprintf(&monitorfile, "%s/%s.monitor", dir, name) < 0) { + rc = ENOMEM; + goto cleanup; + } + + if ((fd = open(monitorfile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR)) < 0) { + rc = errno; + goto cleanup; + } + + if (!(file = fdopen(fd, "w"))) { + rc = errno; + close(fd); + goto cleanup; + } + + if (fprintf(file, "%s", path) < 0) { + rc = errno; + goto cleanup; + } + + rc = 0; + +cleanup: + if (file && + fclose(file) < 0) { + rc = errno; + } + + VIR_FREE(monitorfile); + return rc; +} + +static int +qemudFileReadMonitor(const char *dir, + const char *name, + char **path) +{ + int rc; + FILE *file; + char *monitorfile = NULL; + + if (asprintf(&monitorfile, "%s/%s.monitor", dir, name) < 0) { + rc = ENOMEM; + goto cleanup; + } + + if (!(file = fopen(monitorfile, "r"))) { + rc = errno; + goto cleanup; + } + + if (fscanf(file, "%as", path) != 1) { + rc = EINVAL; + goto cleanup; + } + + if (fclose(file) < 0) { + rc = errno; + goto cleanup; + } + + rc = 0; + + cleanup: + VIR_FREE(monitorfile); + return rc; +} + +static int +qemudFileDeleteMonitor(const char *dir, + const char *name) +{ + int rc = 0; + char *pidfile = NULL; + + if (asprintf(&pidfile, "%s/%s.monitor", dir, name) < 0) { + rc = errno; + goto cleanup; + } + + if (unlink(pidfile) < 0 && errno != ENOENT) + rc = errno; + +cleanup: + VIR_FREE(pidfile); + return rc; +} + + +static int qemudOpenMonitor(virConnectPtr conn, + struct qemud_driver *driver, + virDomainObjPtr vm, + const char *monitor, + int reconnect); +/** + * qemudReconnectVMs + * + * Reconnect running vms to the daemon process + */ +static int +qemudReconnectVMs(struct qemud_driver *driver) +{ + virDomainObjPtr vm = driver->domains; + + while (vm) { + virDomainDefPtr tmp; + char *config = NULL; + char *monitor = NULL; + int rc; + + /* Read pid */ + if ((rc = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) == 0) { + virFileDeletePid(driver->stateDir, vm->def->name); + DEBUG("Found pid %d for '%s'", vm->pid, vm->def->name); + } else + goto next; + + if ((config = virDomainConfigFile(NULL, + driver->stateDir, + vm->def->name)) == NULL) { + qemudLog(QEMUD_ERR, _("Failed to read back domain config for %s\n"), + vm->def->name); + goto next; + } + + /* Try and load the config */ + tmp = virDomainDefParseFile(NULL, driver->caps, config); + unlink(config); + if (tmp) { + vm->newDef = vm->def; + vm->def = tmp; + } + + /* read back monitor path */ + if ((rc = qemudFileReadMonitor(driver->stateDir, vm->def->name, &monitor)) != 0) { + qemudLog(QEMUD_ERR, _("Failed to read back monitor path for %s: %s\n"), + vm->def->name, strerror(rc)); + goto next; + } + qemudFileDeleteMonitor(driver->stateDir, vm->def->name); + + /* FIXME: reconnect stdout/stderr via /proc/pid/fd/ */ + + if ((rc = qemudOpenMonitor(NULL, driver, vm, monitor, 1)) != 0) { + qemudLog(QEMUD_ERR, _("Failed to reconnect to monitor for %s: %d\n"), + vm->def->name, rc); + goto next; + } + vm->def->id = driver->nextvmid++; + vm->state = VIR_DOMAIN_RUNNING; +next: + VIR_FREE(config); + VIR_FREE(monitor); + vm = vm->next; + } + return 0; +} + /** * qemudStartup: * @@ -197,6 +372,10 @@ qemudStartup(void) { if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) goto out_of_memory; + + if (asprintf (&qemu_driver->stateDir, + "%s/run/libvirt/qemu/", LOCAL_STATE_DIR) == -1) + goto out_of_memory; } else { if (!(pw = getpwuid(uid))) { qemudLog(QEMUD_ERR, _("Failed to find user record for uid '%d': %s\n"), @@ -210,6 +389,10 @@ qemudStartup(void) { if (asprintf (&base, "%s/.libvirt", pw->pw_dir) == -1) goto out_of_memory; + + if (asprintf (&qemu_driver->stateDir, + "%s/run/libvirt/qemu/", base) == -1) + goto out_of_memory; } /* Configuration paths are either ~/.libvirt/qemu/... (session) or @@ -250,6 +433,8 @@ qemudStartup(void) { qemudShutdown(); return -1; } + qemudReconnectVMs(qemu_driver); + if (virNetworkLoadAllConfigs(NULL, &qemu_driver->networks, qemu_driver->networkConfigDir, @@ -329,6 +514,34 @@ qemudActive(void) { return 0; } +/** + * qemudSaveDomainState + * + * Save the full state of a running domain to statedir + * + * Returns 0 on success + */ +static int +qemudSaveDomainState(struct qemud_driver * driver, virDomainObjPtr vm) { + int ret; + + if ((ret = virFileWritePid(driver->stateDir, vm->def->name, vm->pid)) != 0) { + qemudLog(QEMUD_ERR, _("Unable to safe pidfile for %s: %s\n"), + vm->def->name, strerror(ret)); + return ret; + } + if ((ret = virDomainSaveConfig(NULL, driver->stateDir, vm->def))) { + qemudLog(QEMUD_ERR, _("Unable to save domain %s\n"), vm->def->name); + return ret; + } + if ((ret = qemudFileWriteMonitor(driver->stateDir, vm->def->name, vm->monitorpath)) != 0) { + qemudLog(QEMUD_ERR, _("Unable to monitor file for %s: %s\n"), + vm->def->name, strerror(ret)); + return ret; + } + return 0; +} + /** * qemudShutdown: * @@ -349,10 +562,14 @@ qemudShutdown(void) { while (vm) { virDomainObjPtr next = vm->next; if (virDomainIsActive(vm)) +#if 1 + qemudSaveDomainState(qemu_driver, vm); +#else qemudShutdownVMDaemon(NULL, qemu_driver, vm); if (!vm->persistent) virDomainRemoveInactive(&qemu_driver->domains, vm); +#endif vm = next; } @@ -389,6 +606,7 @@ qemudShutdown(void) { VIR_FREE(qemu_driver->networkConfigDir); VIR_FREE(qemu_driver->networkAutostartDir); VIR_FREE(qemu_driver->vncTLSx509certdir); + VIR_FREE(qemu_driver->stateDir); if (qemu_driver->brctl) brShutdown(qemu_driver->brctl); @@ -499,7 +717,7 @@ qemudCheckMonitorPrompt(virConnectPtr conn ATTRIBUTE_UNUSED, static int qemudOpenMonitor(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm, - const char *monitor) { + const char *monitor, int reconnect) { int monfd; char buf[1024]; int ret = -1; @@ -520,11 +738,26 @@ static int qemudOpenMonitor(virConnectPtr conn, goto error; } - ret = qemudReadMonitorOutput(conn, - driver, vm, monfd, - buf, sizeof(buf), - qemudCheckMonitorPrompt, - "monitor"); + if (!reconnect) { + ret = qemudReadMonitorOutput(conn, + driver, vm, monfd, + buf, sizeof(buf), + qemudCheckMonitorPrompt, + "monitor"); + + } else { + vm->monitor = monfd; + ret = 0; + } + + if (ret != 0) + goto error; + + if (!(vm->monitorpath = strdup(monitor))) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, + "%s", _("failed to allocate space for monitor path")); + goto error; + } /* Keep monitor open upon success */ if (ret == 0) @@ -623,7 +856,7 @@ qemudFindCharDevicePTYs(virConnectPtr conn, } /* Got them all, so now open the monitor console */ - ret = qemudOpenMonitor(conn, driver, vm, monitor); + ret = qemudOpenMonitor(conn, driver, vm, monitor, 0); cleanup: VIR_FREE(monitor); @@ -966,12 +1199,11 @@ static int qemudStartVMDaemon(virConnectPtr conn, ret = virExec(conn, argv, NULL, &keepfd, &vm->pid, vm->stdin_fd, &vm->stdout_fd, &vm->stderr_fd, - VIR_EXEC_NONBLOCK); + VIR_EXEC_NONBLOCK | VIR_EXEC_SETSID); if (ret == 0) { vm->def->id = driver->nextvmid++; vm->state = migrateFrom ? VIR_DOMAIN_PAUSED : VIR_DOMAIN_RUNNING; } - for (i = 0 ; argv[i] ; i++) VIR_FREE(argv[i]); VIR_FREE(argv); @@ -1037,7 +1269,9 @@ static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, qemudLog(QEMUD_INFO, _("Shutting down VM '%s'\n"), vm->def->name); - kill(vm->pid, SIGTERM); + if (kill(vm->pid, SIGTERM) < 0) + qemudLog(QEMUD_ERROR, _("Failed to send SIGTERM to %s (%d): %s\n"), + vm->def->name, vm->pid, strerror(errno)); qemudVMData(driver, vm, vm->stdout_fd); qemudVMData(driver, vm, vm->stderr_fd); @@ -1057,13 +1291,9 @@ static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, vm->stderr_fd = -1; vm->monitor = -1; - if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) { - kill(vm->pid, SIGKILL); - if (waitpid(vm->pid, NULL, 0) != vm->pid) { - qemudLog(QEMUD_WARN, - "%s", _("Got unexpected pid, damn\n")); - } - } + /* shut if off for sure */ + kill(vm->pid, SIGKILL); + virFileDeletePid(driver->stateDir, vm->def->name); vm->pid = -1; vm->def->id = -1; diff --git a/src/util.c b/src/util.c index ca14be1..442c810 100644 --- a/src/util.c +++ b/src/util.c @@ -299,14 +299,16 @@ virExec(virConnectPtr conn, !FD_ISSET(i, keepfd))) close(i); - if (flags & VIR_EXEC_DAEMON) { + if (flags & VIR_EXEC_DAEMON || flags & VIR_EXEC_SETSID) { if (setsid() < 0) { ReportError(conn, VIR_ERR_INTERNAL_ERROR, _("cannot become session leader: %s"), strerror(errno)); _exit(1); } + } + if (flags & VIR_EXEC_DAEMON) { if (chdir("/") < 0) { ReportError(conn, VIR_ERR_INTERNAL_ERROR, _("cannot change to root directory: %s"), diff --git a/src/util.h b/src/util.h index 093ef46..8fbe2cd 100644 --- a/src/util.h +++ b/src/util.h @@ -32,6 +32,7 @@ enum { VIR_EXEC_NONE = 0, VIR_EXEC_NONBLOCK = (1 << 0), VIR_EXEC_DAEMON = (1 << 1), + VIR_EXEC_SETSID = (1 << 2), }; int virExec(virConnectPtr conn, -- 1.5.6.5
_______________________________________________ et-mgmt-tools mailing list et-mgmt-tools@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/et-mgmt-tools