]> xenbits.xensource.com Git - libvirt.git/commitdiff
Add handling for reboots of LXC containers
authorDaniel P. Berrange <berrange@redhat.com>
Mon, 23 Jul 2012 11:25:37 +0000 (12:25 +0100)
committerDaniel P. Berrange <berrange@redhat.com>
Mon, 30 Jul 2012 12:09:56 +0000 (13:09 +0100)
The reboot() syscall is allowed by new kernels for LXC containers.
The LXC controller can detect whether a reboot was requested
(instead of a normal shutdown) by looking at the "init" process
exit status. If a reboot was triggered, the exit status will
record SIGHUP as the kill reason.

The LXC controller has cleared all its capabilities, and the
veth network devices will no longer exist at this time. Thus
it cannot restart the container init process itself. Instead
it emits an event which is picked up by the LXC driver in
libvirtd. This will then re-create the container, using the
same configuration as it was previously running with (ie it
will not activate 'newDef').

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
src/lxc/lxc_controller.c
src/lxc/lxc_domain.h
src/lxc/lxc_process.c
src/lxc/lxc_protocol.x

index e388302a8b0d85027e2fd2b1ff4ac62b809dae61..56ed7d31953467ee900666facc4dbda32e1b752d 100644 (file)
@@ -661,6 +661,7 @@ static int lxcControllerClearCapabilities(void)
 }
 
 static bool quit = false;
+static bool wantReboot = false;
 static virMutex lock;
 
 
@@ -670,11 +671,15 @@ static void virLXCControllerSignalChildIO(virNetServerPtr server ATTRIBUTE_UNUSE
 {
     virLXCControllerPtr ctrl = opaque;
     int ret;
+    int status;
 
-    ret = waitpid(-1, NULL, WNOHANG);
+    ret = waitpid(-1, &status, WNOHANG);
     if (ret == ctrl->initpid) {
         virMutexLock(&lock);
         quit = true;
+        if (WIFSIGNALED(status) &&
+            WTERMSIG(status) == SIGHUP)
+            wantReboot = true;
         virMutexUnlock(&lock);
     }
 }
@@ -998,7 +1003,7 @@ static int virLXCControllerMain(virLXCControllerPtr ctrl)
 
     err = virGetLastError();
     if (!err || err->code == VIR_ERR_OK)
-        rc = 0;
+        rc = wantReboot ? 1 : 0;
 
 cleanup:
     virMutexDestroy(&lock);
@@ -1319,6 +1324,9 @@ virLXCControllerEventSendExit(virLXCControllerPtr ctrl,
     case 0:
         msg.status = VIR_LXC_PROTOCOL_EXIT_STATUS_SHUTDOWN;
         break;
+    case 1:
+        msg.status = VIR_LXC_PROTOCOL_EXIT_STATUS_REBOOT;
+        break;
     default:
         msg.status = VIR_LXC_PROTOCOL_EXIT_STATUS_ERROR;
         break;
index f4a83841264a538ef1d44fe27821fb4aa0291644..06da1418c428c802130cce3212a230128c60abfc 100644 (file)
@@ -33,6 +33,7 @@ struct _virLXCDomainObjPrivate {
     virLXCMonitorPtr monitor;
     bool doneStopEvent;
     int stopReason;
+    bool wantReboot;
 };
 
 void virLXCDomainSetPrivateDataHooks(virCapsPtr caps);
index 5ffdff5c65dc093465c73aba6cdfef62e739c75c..b42f4a04de9866e2d47728cc5763691be292b766 100644 (file)
@@ -148,6 +148,58 @@ int virLXCProcessAutoDestroyRemove(virLXCDriverPtr driver,
     return 0;
 }
 
+static virConnectPtr
+virLXCProcessAutoDestroyGetConn(virLXCDriverPtr driver,
+                                virDomainObjPtr vm)
+{
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+    virUUIDFormat(vm->def->uuid, uuidstr);
+    VIR_DEBUG("vm=%s uuid=%s", vm->def->name, uuidstr);
+    return virHashLookup(driver->autodestroy, uuidstr);
+}
+
+
+static int
+virLXCProcessReboot(virLXCDriverPtr driver,
+                    virDomainObjPtr vm)
+{
+    virConnectPtr conn = virLXCProcessAutoDestroyGetConn(driver, vm);
+    int reason = vm->state.reason;
+    bool autodestroy = false;
+    int ret = -1;
+    virDomainDefPtr savedDef;
+
+    if (conn) {
+        virConnectRef(conn);
+        autodestroy = true;
+    } else {
+        conn = virConnectOpen("lxc:///");
+        /* Ignoring NULL conn which is mostly harmless here */
+    }
+
+    /* In a reboot scenario, we need to make sure we continue
+     * to use the current 'def', and not switch to 'newDef'.
+     * So temporarily hide the newDef and then reinstate it
+     */
+    savedDef = vm->newDef;
+    vm->newDef = NULL;
+    virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
+    vm->newDef = savedDef;
+    if (virLXCProcessStart(conn, driver, vm, autodestroy, reason) < 0) {
+        VIR_WARN("Unable to handle reboot of vm %s",
+                 vm->def->name);
+        goto cleanup;
+    }
+
+    if (conn)
+        virConnectClose(conn);
+
+    ret = 0;
+
+cleanup:
+    return ret;
+}
+
 
 /**
  * virLXCProcessCleanup:
@@ -509,17 +561,35 @@ static void virLXCProcessMonitorEOFNotify(virLXCMonitorPtr mon ATTRIBUTE_UNUSED,
 
     priv = vm->privateData;
     virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
-    if (!priv->doneStopEvent) {
-        event = virDomainEventNewFromObj(vm,
-                                         VIR_DOMAIN_EVENT_STOPPED,
-                                         priv->stopReason);
-        virDomainAuditStop(vm, "shutdown");
+    if (!priv->wantReboot) {
+        virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
+        if (!priv->doneStopEvent) {
+            event = virDomainEventNewFromObj(vm,
+                                             VIR_DOMAIN_EVENT_STOPPED,
+                                             priv->stopReason);
+            virDomainAuditStop(vm, "shutdown");
+        } else {
+            VIR_DEBUG("Stop event has already been sent");
+        }
+        if (!vm->persistent) {
+            virDomainRemoveInactive(&driver->domains, vm);
+            vm = NULL;
+        }
     } else {
-        VIR_DEBUG("Stop event has already been sent");
-    }
-    if (!vm->persistent) {
-        virDomainRemoveInactive(&driver->domains, vm);
-        vm = NULL;
+        int ret = virLXCProcessReboot(driver, vm);
+        virDomainAuditStop(vm, "reboot");
+        virDomainAuditStart(vm, "reboot", ret == 0);
+        if (ret == 0) {
+            event = virDomainEventRebootNewFromObj(vm);
+        } else {
+            event = virDomainEventNewFromObj(vm,
+                                             VIR_DOMAIN_EVENT_STOPPED,
+                                             priv->stopReason);
+            if (!vm->persistent) {
+                virDomainRemoveInactive(&driver->domains, vm);
+                vm = NULL;
+            }
+        }
     }
 
     if (vm)
@@ -544,6 +614,10 @@ static void virLXCProcessMonitorExitNotify(virLXCMonitorPtr mon ATTRIBUTE_UNUSED
     case VIR_LXC_PROTOCOL_EXIT_STATUS_ERROR:
         priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED;
         break;
+    case VIR_LXC_PROTOCOL_EXIT_STATUS_REBOOT:
+        priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN;
+        priv->wantReboot = true;
+        break;
     default:
         priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED;
         break;
@@ -1033,6 +1107,7 @@ int virLXCProcessStart(virConnectPtr conn,
     }
 
     priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED;
+    priv->wantReboot = false;
     vm->def->id = vm->pid;
     virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
     priv->doneStopEvent = false;
index 4fdbe34e7bc9eaaf88c58c9f38a6b5ac602edeea..e4372178eb310a4439c0b11e8c62696f0aaf2e28 100644 (file)
@@ -6,7 +6,8 @@
 
 enum virLXCProtocolExitStatus {
     VIR_LXC_PROTOCOL_EXIT_STATUS_ERROR,
-    VIR_LXC_PROTOCOL_EXIT_STATUS_SHUTDOWN
+    VIR_LXC_PROTOCOL_EXIT_STATUS_SHUTDOWN,
+    VIR_LXC_PROTOCOL_EXIT_STATUS_REBOOT
 };
 
 struct virLXCProtocolExitEventMsg {