diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index b4c43ae138428e00ba5f8a5df6b203e4cf2108c0..6e8413119c8caacadfe985972a6760d867b1b520 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -81,6 +81,12 @@ VIR_ENUM_IMPL(qemuMonitorMigrationStatus, QEMU_MONITOR_MIGRATION_STATUS_LAST, "inactive", "active", "completed", "failed", "cancelled") +VIR_ENUM_IMPL(qemuMonitorVMStatus, + QEMU_MONITOR_VM_STATUS_LAST, + "debug", "inmigrate", "internal-error", "io-error", "paused", + "postmigrate", "prelaunch", "finish-migrate", "restore-vm", + "running", "save-vm", "shutdown", "watchdog") + char *qemuMonitorEscapeArg(const char *in) { int len = 0; @@ -1059,10 +1065,12 @@ qemuMonitorStopCPUs(qemuMonitorPtr mon) int -qemuMonitorGetStatus(qemuMonitorPtr mon, bool *running) +qemuMonitorGetStatus(qemuMonitorPtr mon, + bool *running, + virDomainPausedReason *reason) { int ret; - VIR_DEBUG("mon=%p, running=%p", mon, running); + VIR_DEBUG("mon=%p, running=%p, reason=%p", mon, running, reason); if (!mon || !running) { qemuReportError(VIR_ERR_INVALID_ARG, "%s", @@ -1071,9 +1079,9 @@ qemuMonitorGetStatus(qemuMonitorPtr mon, bool *running) } if (mon->json) - ret = qemuMonitorJSONGetStatus(mon, running); + ret = qemuMonitorJSONGetStatus(mon, running, reason); else - ret = qemuMonitorTextGetStatus(mon, running); + ret = qemuMonitorTextGetStatus(mon, running, reason); return ret; } @@ -2553,3 +2561,53 @@ int qemuMonitorBlockJob(qemuMonitorPtr mon, ret = qemuMonitorTextBlockJob(mon, device, bandwidth, info, mode); return ret; } + +int qemuMonitorVMStatusToPausedReason(const char *status) +{ + int st; + + if (!status) + return VIR_DOMAIN_PAUSED_UNKNOWN; + + if ((st = qemuMonitorVMStatusTypeFromString(status)) < 0) { + VIR_WARN("Qemu reported unknown VM status: '%s'", status); + return VIR_DOMAIN_PAUSED_UNKNOWN; + } + + switch ((qemuMonitorVMStatus) st) { + case QEMU_MONITOR_VM_STATUS_DEBUG: + case QEMU_MONITOR_VM_STATUS_INTERNAL_ERROR: + case QEMU_MONITOR_VM_STATUS_RESTORE_VM: + return VIR_DOMAIN_PAUSED_UNKNOWN; + + case QEMU_MONITOR_VM_STATUS_INMIGRATE: + case QEMU_MONITOR_VM_STATUS_POSTMIGRATE: + case QEMU_MONITOR_VM_STATUS_FINISH_MIGRATE: + return VIR_DOMAIN_PAUSED_MIGRATION; + + case QEMU_MONITOR_VM_STATUS_IO_ERROR: + return VIR_DOMAIN_PAUSED_IOERROR; + + case QEMU_MONITOR_VM_STATUS_PAUSED: + case QEMU_MONITOR_VM_STATUS_PRELAUNCH: + return VIR_DOMAIN_PAUSED_USER; + + case QEMU_MONITOR_VM_STATUS_RUNNING: + VIR_WARN("Qemu reports the guest is paused but status is 'running'"); + return VIR_DOMAIN_PAUSED_UNKNOWN; + + case QEMU_MONITOR_VM_STATUS_SAVE_VM: + return VIR_DOMAIN_PAUSED_SAVE; + + case QEMU_MONITOR_VM_STATUS_SHUTDOWN: + return VIR_DOMAIN_PAUSED_SHUTTING_DOWN; + + case QEMU_MONITOR_VM_STATUS_WATCHDOG: + return VIR_DOMAIN_PAUSED_WATCHDOG; + + /* unreachable from this point on */ + case QEMU_MONITOR_VM_STATUS_LAST: + ; + } + return VIR_DOMAIN_PAUSED_UNKNOWN; +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 477767fad439a15cf9e7ee71a04665b4b73f410a..a64c2c39fe6c7c3dd6e5147e9431e0427bb4a7e6 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -198,7 +198,30 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); int qemuMonitorStopCPUs(qemuMonitorPtr mon); -int qemuMonitorGetStatus(qemuMonitorPtr mon, bool *running); + +typedef enum { + QEMU_MONITOR_VM_STATUS_DEBUG, + QEMU_MONITOR_VM_STATUS_INMIGRATE, + QEMU_MONITOR_VM_STATUS_INTERNAL_ERROR, + QEMU_MONITOR_VM_STATUS_IO_ERROR, + QEMU_MONITOR_VM_STATUS_PAUSED, + QEMU_MONITOR_VM_STATUS_POSTMIGRATE, + QEMU_MONITOR_VM_STATUS_PRELAUNCH, + QEMU_MONITOR_VM_STATUS_FINISH_MIGRATE, + QEMU_MONITOR_VM_STATUS_RESTORE_VM, + QEMU_MONITOR_VM_STATUS_RUNNING, + QEMU_MONITOR_VM_STATUS_SAVE_VM, + QEMU_MONITOR_VM_STATUS_SHUTDOWN, + QEMU_MONITOR_VM_STATUS_WATCHDOG, + + QEMU_MONITOR_VM_STATUS_LAST +} qemuMonitorVMStatus; +VIR_ENUM_DECL(qemuMonitorVMStatus) +int qemuMonitorVMStatusToPausedReason(const char *status); + +int qemuMonitorGetStatus(qemuMonitorPtr mon, + bool *running, + virDomainPausedReason *reason); int qemuMonitorSystemReset(qemuMonitorPtr mon); int qemuMonitorSystemPowerdown(qemuMonitorPtr mon); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index e38c2edb1dce14b80be5e1b9f91ca00a0ca38b87..b7206176d9361db9ca06b4bf06894bbeed669ece 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -896,13 +896,19 @@ qemuMonitorJSONStopCPUs(qemuMonitorPtr mon) int -qemuMonitorJSONGetStatus(qemuMonitorPtr mon, bool *running) +qemuMonitorJSONGetStatus(qemuMonitorPtr mon, + bool *running, + virDomainPausedReason *reason) { int ret; + const char *status; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; + if (reason) + *reason = VIR_DOMAIN_PAUSED_UNKNOWN; + if (!(cmd = qemuMonitorJSONMakeCommand("query-status", NULL))) return -1; @@ -928,6 +934,13 @@ qemuMonitorJSONGetStatus(qemuMonitorPtr mon, bool *running) goto cleanup; } + if ((status = virJSONValueObjectGetString(data, "status"))) { + if (!*running && reason) + *reason = qemuMonitorVMStatusToPausedReason(status); + } else if (!*running) { + VIR_DEBUG("query-status reply was missing status details"); + } + ret = 0; cleanup: diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 673444f2e114a8669411e9be7d03c9d5d6aabd98..1a4071625184e640ec0dd95ae6b706084db823d2 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -46,7 +46,9 @@ int qemuMonitorJSONCheckHMP(qemuMonitorPtr mon); int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); int qemuMonitorJSONStopCPUs(qemuMonitorPtr mon); -int qemuMonitorJSONGetStatus(qemuMonitorPtr mon, bool *running); +int qemuMonitorJSONGetStatus(qemuMonitorPtr mon, + bool *running, + virDomainPausedReason *reason); int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon); int qemuMonitorJSONSystemReset(qemuMonitorPtr mon); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index cedf58242c798e65c0f30a1e10e81153d72547b2..57e74a8e37d5e05f74fafe795b4532e04020746e 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -392,11 +392,16 @@ qemuMonitorTextStopCPUs(qemuMonitorPtr mon) { int -qemuMonitorTextGetStatus(qemuMonitorPtr mon, bool *running) +qemuMonitorTextGetStatus(qemuMonitorPtr mon, + bool *running, + virDomainPausedReason *reason) { char *reply; int ret = -1; + if (reason) + *reason = VIR_DOMAIN_PAUSED_UNKNOWN; + if (qemuMonitorHMPCommand(mon, "info status", &reply) < 0) { qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", _("cannot get status info")); @@ -406,6 +411,19 @@ qemuMonitorTextGetStatus(qemuMonitorPtr mon, bool *running) if (strstr(reply, "running")) { *running = true; } else if (strstr(reply, "paused")) { + char *status; + + if ((status = strchr(reply, '('))) { + char *end = strchr(status, ')'); + if (end) + *end = '\0'; + else + status = NULL; + } + if (!status) + VIR_DEBUG("info status was missing status details"); + else if (reason) + *reason = qemuMonitorVMStatusToPausedReason(status); *running = false; } else { qemuReportError(VIR_ERR_OPERATION_FAILED, diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 4651a1735e2caebdf0f035fbfb0882e47f4a2e68..207001d699de3631ff751b43a7589d1e45c376be 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -43,7 +43,9 @@ int qemuMonitorTextCommandWithFd(qemuMonitorPtr mon, int qemuMonitorTextStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); int qemuMonitorTextStopCPUs(qemuMonitorPtr mon); -int qemuMonitorTextGetStatus(qemuMonitorPtr mon, bool *running); +int qemuMonitorTextGetStatus(qemuMonitorPtr mon, + bool *running, + virDomainPausedReason *reason); int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon); int qemuMonitorTextSystemReset(qemuMonitorPtr mon); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index c8f22e2d8a5e5d5690775c9edfbfe2445efb7e07..21e6fbf0d49bdfd0a4c31077140c4df8f5229b61 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2321,11 +2321,12 @@ qemuProcessUpdateState(struct qemud_driver *driver, virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; virDomainState state; + virDomainPausedReason reason; bool running; int ret; qemuDomainObjEnterMonitorWithDriver(driver, vm); - ret = qemuMonitorGetStatus(priv->mon, &running); + ret = qemuMonitorGetStatus(priv->mon, &running, &reason); qemuDomainObjExitMonitorWithDriver(driver, vm); if (ret < 0 || !virDomainObjIsActive(vm)) @@ -2339,9 +2340,10 @@ qemuProcessUpdateState(struct qemud_driver *driver, virDomainObjPtr vm) virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED); } else if (state == VIR_DOMAIN_RUNNING && !running) { - VIR_DEBUG("Domain %s was paused while its monitor was disconnected;" - " changing state to paused", vm->def->name); - virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_UNKNOWN); + VIR_DEBUG("Domain %s was paused (%s) while its monitor was" + " disconnected; changing state to paused", + vm->def->name, virDomainPausedReasonTypeToString(reason)); + virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, reason); } else if (state == VIR_DOMAIN_SHUTOFF && running) { VIR_DEBUG("Domain %s finished booting; changing state to running", vm->def->name); @@ -3470,6 +3472,7 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, char *timestamp; qemuDomainObjPrivatePtr priv = vm->privateData; bool running = true; + virDomainPausedReason reason; virSecurityLabelPtr seclabel = NULL; VIR_DEBUG("Beginning VM attach process"); @@ -3599,7 +3602,7 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } - if (qemuMonitorGetStatus(priv->mon, &running) < 0) { + if (qemuMonitorGetStatus(priv->mon, &running, &reason) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } @@ -3616,7 +3619,7 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED); else - virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_UNKNOWN); + virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, reason); VIR_DEBUG("Writing domain status to disk"); if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)