This allows guests to enter S4 ACPI state aka hibernation
by using guest agent. With no flag given, we keep consistent
and enter only S3 state (sleep).
---
src/qemu/qemu_driver.c | 57 +++++++++++++++++++++++++++++++++++++++++------
tools/virsh.c | 30 +++++++++++++++++++++---
2 files changed, 75 insertions(+), 12 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index ab69dca..fba9aeb 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -1388,7 +1388,10 @@ cleanup:
}
-static int qemudDomainSuspend(virDomainPtr dom) {
+static int
+qemudDomainSuspendFlags(virDomainPtr dom,
+ unsigned int flags)
+{
struct qemud_driver *driver = dom->conn->privateData;
virDomainObjPtr vm;
int ret = -1;
@@ -1397,6 +1400,15 @@ static int qemudDomainSuspend(virDomainPtr dom) {
virDomainPausedReason reason;
int eventDetail;
+ virCheckFlags(VIR_DOMAIN_SUSPEND_SLEEP |
+ VIR_DOMAIN_SUSPEND_HIBERNATE, -1);
+
+ if ((flags & VIR_DOMAIN_SUSPEND_SLEEP) &&
+ (flags & VIR_DOMAIN_SUSPEND_HIBERNATE)) {
+ qemuReportError(VIR_ERR_INVALID_ARG, "%s", _("seriosly?"));
+ return -1;
+ }
+
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -1431,16 +1443,39 @@ static int qemudDomainSuspend(virDomainPtr dom) {
"%s", _("domain is not running"));
goto endjob;
}
- if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_PAUSED) {
- if (qemuProcessStopCPUs(driver, vm, reason, QEMU_ASYNC_JOB_NONE) < 0) {
+
+ if (flags & VIR_DOMAIN_SUSPEND_SLEEP) {
+ if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_PAUSED) {
+ if (qemuProcessStopCPUs(driver, vm, reason, QEMU_ASYNC_JOB_NONE) < 0) {
+ goto endjob;
+ }
+ event = virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_SUSPENDED,
+ eventDetail);
+ }
+ if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+ goto endjob;
+ } else if (flags & VIR_DOMAIN_SUSPEND_HIBERNATE) {
+ if (priv->agentError) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("QEMU guest agent is not "
+ "available due to an error"));
goto endjob;
}
- event = virDomainEventNewFromObj(vm,
- VIR_DOMAIN_EVENT_SUSPENDED,
- eventDetail);
+ if (!priv->agent) {
+ qemuReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("QEMU guest agent is not configured"));
+ goto endjob;
+ }
+
+ qemuDomainObjEnterAgent(driver, vm);
+ ret = qemuAgentSuspend(priv->agent, QEMU_AGENT_SUSPEND_HIBERNATE);
+ qemuDomainObjExitAgent(driver, vm);
+
+ if (ret < 0)
+ goto endjob;
}
- if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
- goto endjob;
+
ret = 0;
endjob:
@@ -1457,6 +1492,11 @@ cleanup:
return ret;
}
+static int
+qemudDomainSuspend(virDomainPtr dom)
+{
+ return qemudDomainSuspendFlags(dom, 0);
+}
static int qemudDomainResume(virDomainPtr dom) {
struct qemud_driver *driver = dom->conn->privateData;
@@ -11760,6 +11800,7 @@ static virDriver qemuDriver = {
.domainLookupByUUID = qemudDomainLookupByUUID, /* 0.2.0 */
.domainLookupByName = qemudDomainLookupByName, /* 0.2.0 */
.domainSuspend = qemudDomainSuspend, /* 0.2.0 */
+ .domainSuspendFlags = qemudDomainSuspendFlags, /* 0.9.10 */
.domainResume = qemudDomainResume, /* 0.2.0 */
.domainShutdown = qemuDomainShutdown, /* 0.2.0 */
.domainShutdownFlags = qemuDomainShutdownFlags, /* 0.9.10 */
diff --git a/tools/virsh.c b/tools/virsh.c
index 74655c2..99b2ca9 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -2142,6 +2142,7 @@ static const vshCmdInfo info_suspend[] = {
static const vshCmdOptDef opts_suspend[] = {
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or
uuid")},
+ {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("state to enter:
sleep|hibernate")},
{NULL, 0, 0, NULL}
};
@@ -2150,7 +2151,9 @@ cmdSuspend(vshControl *ctl, const vshCmd *cmd)
{
virDomainPtr dom;
const char *name;
+ const char *mode;
bool ret = true;
+ unsigned int flags = 0;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
@@ -2158,12 +2161,31 @@ cmdSuspend(vshControl *ctl, const vshCmd *cmd)
if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
return false;
- if (virDomainSuspend(dom) == 0) {
+ if (vshCommandOptString(cmd, "mode", &mode) < 0) {
+ vshError(ctl, "%s", _("Invalid type"));
+ return false;
+ }
+
+ if (mode) {
+ if (STREQ(mode, "sleep")) {
+ flags |= VIR_DOMAIN_SUSPEND_SLEEP;
+ } else if (STREQ(mode, "hibernate")) {
+ flags |= VIR_DOMAIN_SUSPEND_HIBERNATE;
+ } else {
+ vshError(ctl, _("Unknown mode %s value, expecting 'sleep' or
'hibernate'"), mode);
+ return false;
+ }
+ }
+
+ if (flags)
+ ret = virDomainSuspendFlags(dom, flags) == 0;
+ else
+ ret = virDomainSuspend(dom) == 0;
+
+ if (ret)
vshPrint(ctl, _("Domain %s suspended\n"), name);
- } else {
+ else
vshError(ctl, _("Failed to suspend domain %s"), name);
- ret = false;
- }
virDomainFree(dom);
return ret;
--
1.7.3.4