Implement shutdown using a guest agent.
Implementation and behaviour is very similar to the one
found in the qemu driver.

The bhyveDomainShutdownFlags() function now supports
the VIR_DOMAIN_SHUTDOWN_SIGNAL and VIR_DOMAIN_SHUTDOWN_GUEST_AGENT.
If flags were not specified, try the agent first, and if it does
not work, proceed with the signal.

As we do not expect a response from the agent shutdown command,
we do not check qemuAgentShutdown()'s return value. Assume that the
command failed if the agent did not receive EOF, thus "priv->agent"
is not NULL.

Signed-off-by: Roman Bogorodskiy <[email protected]>
---
 src/bhyve/bhyve_driver.c | 69 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 67 insertions(+), 2 deletions(-)

diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c
index 35abf36877..4b28c698e7 100644
--- a/src/bhyve/bhyve_driver.c
+++ b/src/bhyve/bhyve_driver.c
@@ -1053,14 +1053,51 @@ bhyveDomainShutdownSignal(virDomainObj *vm,
     return virBhyveProcessShutdown(vm);
 }
 
+static int
+bhyveDomainShutdownFlagsAgent(virDomainObj *vm,
+                              bool isReboot,
+                              bool reportError)
+{
+    int ret = -1;
+    qemuAgent *agent;
+    int agentFlag = isReboot ? QEMU_AGENT_SHUTDOWN_REBOOT :
+        QEMU_AGENT_SHUTDOWN_POWERDOWN;
+
+    if (virDomainObjBeginAgentJob(vm, VIR_AGENT_JOB_MODIFY) < 0)
+        return -1;
+
+    if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
+        virReportError(VIR_ERR_OPERATION_INVALID,
+                       "%s", _("domain is not running"));
+        goto endjob;
+    }
+
+    if (bhyveDomainEnsureAgent(vm, reportError) < 0)
+        goto endjob;
+
+    agent = bhyveDomainObjEnterAgent(vm);
+    qemuAgentShutdown(agent, agentFlag);
+    bhyveDomainObjExitAgent(vm, agent);
+    ret = 0;
+
+ endjob:
+    virDomainObjEndAgentJob(vm);
+    return ret;
+}
+
 static int
 bhyveDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
 {
     virDomainObj *vm;
+    bhyveDomainObjPrivate *priv;
     int ret = -1;
     bool isReboot = false;
+    bool useAgent = false;
+    bool agentRequested, signalRequested;
+    bool agentForced;
 
-    virCheckFlags(0, -1);
+    virCheckFlags(VIR_DOMAIN_SHUTDOWN_SIGNAL |
+                  VIR_DOMAIN_SHUTDOWN_GUEST_AGENT, -1);
 
     if (!(vm = bhyveDomObjFromDomain(dom)))
         goto cleanup;
@@ -1071,13 +1108,41 @@ bhyveDomainShutdownFlags(virDomainPtr dom, unsigned int 
flags)
         VIR_INFO("Domain on_poweroff setting overridden, attempting reboot");
     }
 
+    priv = vm->privateData;
+    agentRequested = flags & VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
+    signalRequested = flags & VIR_DOMAIN_SHUTDOWN_SIGNAL;
+
+    /* Prefer agent unless we were requested to not to. */
+    if (agentRequested || !flags)
+        useAgent = true;
+
     if (virDomainShutdownFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
         goto cleanup;
 
     if (virDomainObjCheckActive(vm) < 0)
         goto cleanup;
 
-    ret = bhyveDomainShutdownSignal(vm, isReboot);
+    agentForced = agentRequested && !signalRequested;
+    if (useAgent) {
+        ret = bhyveDomainShutdownFlagsAgent(vm, isReboot, agentForced);
+        if (((ret < 0) || (priv->agent != NULL)) && agentForced)
+            goto cleanup;
+    }
+
+    /* If we are not enforced to use just an agent, try signal
+     * shutdown as well in case agent did not succeed.
+     */
+    if (!useAgent || (((ret < 0) ||
+        (priv->agent != NULL)) && (signalRequested || !flags))) {
+        /* Even if agent failed, we have to check if guest went away
+         * by itself while our locks were down.  */
+        if (useAgent && !virDomainObjIsActive(vm)) {
+            ret = 0;
+            goto cleanup;
+        }
+
+        ret = bhyveDomainShutdownSignal(vm, isReboot);
+    }
 
  cleanup:
     virDomainObjEndAPI(&vm);
-- 
2.52.0

Reply via email to