* src/domain_conf.h src/qemu_driver.c: use monitor fd for QEmu/KVM

domain shutdown and read saved vm status on libvirtd startup,
  last 2 patches from Guido Günther finishing up the surviving the
  libvirt daemon restart
daniel
This commit is contained in:
Daniel Veillard 2009-01-20 15:52:11 +00:00
parent 103ee5d4c2
commit 6d004d3f8a
3 changed files with 178 additions and 92 deletions

View File

@ -1,3 +1,10 @@
Tue Jan 20 16:48:00 CET 2009 Daniel Veillard <veillard@redhat.com>
* src/domain_conf.h src/qemu_driver.c: use monitor fd for QEmu/KVM
domain shutdown and read saved vm status on libvirtd startup,
last 2 patches from Guido Günther finishing up the surviving the
libvirt daemon restart
Tue Jan 20 16:35:24 CET 2009 Daniel Veillard <veillard@redhat.com> Tue Jan 20 16:35:24 CET 2009 Daniel Veillard <veillard@redhat.com>
* docs/apibuild.py: fix the parser with another Win32 keyword * docs/apibuild.py: fix the parser with another Win32 keyword

View File

@ -465,6 +465,7 @@ struct _virDomainObj {
int stderr_fd; int stderr_fd;
int stderr_watch; int stderr_watch;
int monitor; int monitor;
int monitor_watch;
char *monitorpath; char *monitorpath;
int monitorWatch; int monitorWatch;
int logfile; int logfile;

View File

@ -181,6 +181,45 @@ qemudLogFD(virConnectPtr conn, const char* logDir, const char* name)
} }
static int
qemudLogReadFD(virConnectPtr conn, const char* logDir, const char* name, off_t pos)
{
char logfile[PATH_MAX];
mode_t logmode = O_RDONLY;
int ret, fd = -1;
if ((ret = snprintf(logfile, sizeof(logfile), "%s/%s.log", logDir, name))
< 0 || ret >= sizeof(logfile)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("failed to build logfile name %s/%s.log"),
logDir, name);
return -1;
}
if ((fd = open(logfile, logmode)) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("failed to create logfile %s: %s"),
logfile, strerror(errno));
return -1;
}
if (qemudSetCloseExec(fd) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Unable to set VM logfile close-on-exec flag: %s"),
strerror(errno));
close(fd);
return -1;
}
if (lseek(fd, pos, SEEK_SET) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Unable to seek to %lld in %s: %s"),
pos, logfile, strerror(errno));
close(fd);
}
return fd;
}
static void static void
qemudAutostartConfigs(struct qemud_driver *driver) { qemudAutostartConfigs(struct qemud_driver *driver) {
unsigned int i; unsigned int i;
@ -258,6 +297,84 @@ cleanup:
} }
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)
{
int i;
for (i = 0 ; i < driver->domains.count ; i++) {
virDomainObjPtr vm = driver->domains.objs[i];
qemudDomainStatusPtr status = NULL;
char *config = NULL;
int rc;
virDomainObjLock(vm);
if ((rc = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) == 0)
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 domain status for %s\n"),
vm->def->name);
goto next_error;
}
status = qemudDomainStatusParseFile(NULL, driver->caps, config, 0);
if (status) {
vm->newDef = vm->def;
vm->def = status->def;
} else {
qemudLog(QEMUD_ERR, _("Failed to parse domain status for %s\n"),
vm->def->name);
goto next_error;
}
if ((rc = qemudOpenMonitor(NULL, driver, vm, status->monitorpath, 1)) != 0) {
qemudLog(QEMUD_ERR, _("Failed to reconnect monitor for %s: %d\n"),
vm->def->name, rc);
goto next_error;
} else
vm->monitorpath = status->monitorpath;
if((vm->logfile = qemudLogFD(NULL, driver->logDir, vm->def->name)) < 0)
return -1;
if (vm->def->id >= driver->nextvmid)
driver->nextvmid = vm->def->id + 1;
vm->state = status->state;
goto next;
next_error:
/* we failed to reconnect the vm so remove it's traces */
vm->def->id = -1;
qemudRemoveDomainStatus(NULL, driver, vm);
virDomainDefFree(vm->def);
vm->def = vm->newDef;
vm->newDef = NULL;
next:
virDomainObjUnlock(vm);
VIR_FREE(status);
VIR_FREE(config);
}
return 0;
}
/** /**
* qemudStartup: * qemudStartup:
* *
@ -357,6 +474,7 @@ qemudStartup(void) {
qemu_driver->autostartDir, qemu_driver->autostartDir,
NULL, NULL) < 0) NULL, NULL) < 0)
goto error; goto error;
qemudReconnectVMs(qemu_driver);
qemudAutostartConfigs(qemu_driver); qemudAutostartConfigs(qemu_driver);
qemuDriverUnlock(qemu_driver); qemuDriverUnlock(qemu_driver);
@ -449,7 +567,6 @@ qemudActive(void) {
*/ */
static int static int
qemudShutdown(void) { qemudShutdown(void) {
unsigned int i;
if (!qemu_driver) if (!qemu_driver)
return -1; return -1;
@ -457,15 +574,6 @@ qemudShutdown(void) {
qemuDriverLock(qemu_driver); qemuDriverLock(qemu_driver);
virCapabilitiesFree(qemu_driver->caps); virCapabilitiesFree(qemu_driver->caps);
/* shutdown active VMs */
for (i = 0 ; i < qemu_driver->domains.count ; i++) {
virDomainObjPtr dom = qemu_driver->domains.objs[i];
virDomainObjLock(dom);
if (virDomainIsActive(dom))
qemudShutdownVMDaemon(NULL, qemu_driver, dom);
virDomainObjUnlock(dom);
}
virDomainObjListFree(&qemu_driver->domains); virDomainObjListFree(&qemu_driver->domains);
VIR_FREE(qemu_driver->logDir); VIR_FREE(qemu_driver->logDir);
@ -516,11 +624,7 @@ qemudReadMonitorOutput(virConnectPtr conn,
int ret; int ret;
ret = read(fd, buf+got, buflen-got-1); ret = read(fd, buf+got, buflen-got-1);
if (ret == 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("QEMU quit during %s startup\n%s"), what, buf);
return -1;
}
if (ret < 0) { if (ret < 0) {
struct pollfd pfd = { .fd = fd, .events = POLLIN }; struct pollfd pfd = { .fd = fd, .events = POLLIN };
if (errno == EINTR) if (errno == EINTR)
@ -584,8 +688,10 @@ qemudCheckMonitorPrompt(virConnectPtr conn ATTRIBUTE_UNUSED,
} }
static int qemudOpenMonitor(virConnectPtr conn, static int qemudOpenMonitor(virConnectPtr conn,
struct qemud_driver* driver,
virDomainObjPtr vm, virDomainObjPtr vm,
const char *monitor) { const char *monitor,
int reconnect) {
int monfd; int monfd;
char buf[1024]; char buf[1024];
int ret = -1; int ret = -1;
@ -606,11 +712,19 @@ static int qemudOpenMonitor(virConnectPtr conn,
goto error; goto error;
} }
ret = qemudReadMonitorOutput(conn, if (!reconnect) {
vm, monfd, ret = qemudReadMonitorOutput(conn,
buf, sizeof(buf), vm, monfd,
qemudCheckMonitorPrompt, buf, sizeof(buf),
"monitor", 10000); qemudCheckMonitorPrompt,
"monitor", 10000);
} else {
vm->monitor = monfd;
ret = 0;
}
if (ret != 0)
goto error;
if (!(vm->monitorpath = strdup(monitor))) { if (!(vm->monitorpath = strdup(monitor))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
@ -618,6 +732,12 @@ static int qemudOpenMonitor(virConnectPtr conn,
goto error; goto error;
} }
if ((vm->monitor_watch = virEventAddHandle(vm->monitor, 0,
qemudDispatchVMEvent,
driver, NULL)) < 0)
goto error;
/* Keep monitor open upon success */ /* Keep monitor open upon success */
if (ret == 0) if (ret == 0)
return ret; return ret;
@ -677,6 +797,7 @@ qemudFindCharDevicePTYs(virConnectPtr conn,
const char *output, const char *output,
int fd ATTRIBUTE_UNUSED) int fd ATTRIBUTE_UNUSED)
{ {
struct qemud_driver* driver = conn->privateData;
char *monitor = NULL; char *monitor = NULL;
size_t offset = 0; size_t offset = 0;
int ret, i; int ret, i;
@ -711,7 +832,7 @@ qemudFindCharDevicePTYs(virConnectPtr conn,
} }
/* Got them all, so now open the monitor console */ /* Got them all, so now open the monitor console */
ret = qemudOpenMonitor(conn, vm, monitor); ret = qemudOpenMonitor(conn, driver, vm, monitor, 0);
cleanup: cleanup:
VIR_FREE(monitor); VIR_FREE(monitor);
@ -719,21 +840,23 @@ cleanup:
} }
static int qemudWaitForMonitor(virConnectPtr conn, static int qemudWaitForMonitor(virConnectPtr conn,
virDomainObjPtr vm) { struct qemud_driver* driver,
virDomainObjPtr vm, off_t pos)
{
char buf[1024]; /* Plenty of space to get startup greeting */ char buf[1024]; /* Plenty of space to get startup greeting */
int ret = qemudReadMonitorOutput(conn, int logfd;
vm, vm->stderr_fd, int ret;
buf, sizeof(buf),
qemudFindCharDevicePTYs,
"console", 3000);
buf[sizeof(buf)-1] = '\0'; if ((logfd = qemudLogReadFD(conn, driver->logDir, vm->def->name, pos))
< 0)
return logfd;
if (safewrite(vm->logfile, buf, strlen(buf)) < 0) { ret = qemudReadMonitorOutput(conn, vm, logfd, buf, sizeof(buf),
/* Log, but ignore failures to write logfile for VM */ qemudFindCharDevicePTYs,
qemudLog(QEMUD_WARN, _("Unable to log VM console data: %s\n"), "console", 3000);
if (close(logfd) < 0)
qemudLog(QEMUD_WARN, _("Unable to close logfile: %s\n"),
strerror(errno)); strerror(errno));
}
return ret; return ret;
} }
@ -942,6 +1065,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
fd_set keepfd; fd_set keepfd;
const char *emulator; const char *emulator;
pid_t child; pid_t child;
int pos = -1;
FD_ZERO(&keepfd); FD_ZERO(&keepfd);
@ -1034,14 +1158,15 @@ static int qemudStartVMDaemon(virConnectPtr conn,
qemudLog(QEMUD_WARN, _("Unable to write argv to logfile %d: %s\n"), qemudLog(QEMUD_WARN, _("Unable to write argv to logfile %d: %s\n"),
errno, strerror(errno)); errno, strerror(errno));
vm->stdout_fd = -1; if ((pos = lseek(vm->logfile, 0, SEEK_END)) < 0)
vm->stderr_fd = -1; qemudLog(QEMUD_WARN, _("Unable to seek to end of logfile %d: %s\n"),
errno, strerror(errno));
for (i = 0 ; i < ntapfds ; i++) for (i = 0 ; i < ntapfds ; i++)
FD_SET(tapfds[i], &keepfd); FD_SET(tapfds[i], &keepfd);
ret = virExec(conn, argv, progenv, &keepfd, &child, ret = virExec(conn, argv, progenv, &keepfd, &child,
vm->stdin_fd, &vm->stdout_fd, &vm->stderr_fd, vm->stdin_fd, &vm->logfile, &vm->logfile,
VIR_EXEC_NONBLOCK | VIR_EXEC_DAEMON); VIR_EXEC_NONBLOCK | VIR_EXEC_DAEMON);
/* wait for qemu process to to show up */ /* wait for qemu process to to show up */
@ -1078,19 +1203,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
} }
if (ret == 0) { if (ret == 0) {
if (((vm->stdout_watch = virEventAddHandle(vm->stdout_fd, if ((qemudWaitForMonitor(conn, driver, vm, pos) < 0) ||
VIR_EVENT_HANDLE_READABLE |
VIR_EVENT_HANDLE_ERROR |
VIR_EVENT_HANDLE_HANGUP,
qemudDispatchVMEvent,
driver, NULL)) < 0) ||
((vm->stderr_watch = virEventAddHandle(vm->stderr_fd,
VIR_EVENT_HANDLE_READABLE |
VIR_EVENT_HANDLE_ERROR |
VIR_EVENT_HANDLE_HANGUP,
qemudDispatchVMEvent,
driver, NULL)) < 0) ||
(qemudWaitForMonitor(conn, vm) < 0) ||
(qemudDetectVcpuPIDs(conn, vm) < 0) || (qemudDetectVcpuPIDs(conn, vm) < 0) ||
(qemudInitCpus(conn, vm, migrateFrom) < 0)) { (qemudInitCpus(conn, vm, migrateFrom) < 0)) {
qemudShutdownVMDaemon(conn, driver, vm); qemudShutdownVMDaemon(conn, driver, vm);
@ -1102,32 +1215,6 @@ static int qemudStartVMDaemon(virConnectPtr conn,
return ret; return ret;
} }
static int qemudVMData(struct qemud_driver *driver ATTRIBUTE_UNUSED,
virDomainObjPtr vm, int fd) {
char buf[4096];
if (vm->pid < 0)
return 0;
for (;;) {
int ret = read(fd, buf, sizeof(buf)-1);
if (ret < 0) {
if (errno == EAGAIN)
return 0;
return -1;
}
if (ret == 0) {
return 0;
}
buf[ret] = '\0';
if (safewrite(vm->logfile, buf, ret) < 0) {
/* Log, but ignore failures to write logfile for VM */
qemudLog(QEMUD_WARN, _("Unable to log VM console data: %s\n"),
strerror(errno));
}
}
}
static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
struct qemud_driver *driver, virDomainObjPtr vm) { struct qemud_driver *driver, virDomainObjPtr vm) {
@ -1141,22 +1228,14 @@ static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
qemudLog(QEMUD_ERROR, _("Failed to send SIGTERM to %s (%d): %s\n"), qemudLog(QEMUD_ERROR, _("Failed to send SIGTERM to %s (%d): %s\n"),
vm->def->name, vm->pid, strerror(errno)); vm->def->name, vm->pid, strerror(errno));
qemudVMData(driver, vm, vm->stdout_fd); virEventRemoveHandle(vm->monitor_watch);
qemudVMData(driver, vm, vm->stderr_fd);
virEventRemoveHandle(vm->stdout_watch);
virEventRemoveHandle(vm->stderr_watch);
if (close(vm->logfile) < 0) if (close(vm->logfile) < 0)
qemudLog(QEMUD_WARN, _("Unable to close logfile %d: %s\n"), qemudLog(QEMUD_WARN, _("Unable to close logfile %d: %s\n"),
errno, strerror(errno)); errno, strerror(errno));
close(vm->stdout_fd);
close(vm->stderr_fd);
if (vm->monitor != -1) if (vm->monitor != -1)
close(vm->monitor); close(vm->monitor);
vm->logfile = -1; vm->logfile = -1;
vm->stdout_fd = -1;
vm->stderr_fd = -1;
vm->monitor = -1; vm->monitor = -1;
/* shut it off for sure */ /* shut it off for sure */
@ -1191,8 +1270,7 @@ qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) {
virDomainObjPtr tmpvm = driver->domains.objs[i]; virDomainObjPtr tmpvm = driver->domains.objs[i];
virDomainObjLock(tmpvm); virDomainObjLock(tmpvm);
if (virDomainIsActive(tmpvm) && if (virDomainIsActive(tmpvm) &&
(tmpvm->stdout_watch == watch || tmpvm->monitor_watch == watch) {
tmpvm->stderr_watch == watch)) {
vm = tmpvm; vm = tmpvm;
break; break;
} }
@ -1202,15 +1280,15 @@ qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) {
if (!vm) if (!vm)
goto cleanup; goto cleanup;
if (vm->stdout_fd != fd && if (vm->monitor != fd) {
vm->stderr_fd != fd) {
failed = 1; failed = 1;
} else { } else {
if (events & VIR_EVENT_HANDLE_READABLE) { if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR))
if (qemudVMData(driver, vm, fd) < 0)
failed = 1;
} else {
quit = 1; quit = 1;
else {
qemudLog(QEMUD_ERROR, _("unhandled fd event %d for %s"),
events, vm->def->name);
failed = 1;
} }
} }