On Tue, Jul 24, 2012 at 14:22:53 +0100, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange(a)redhat.com>
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(a)redhat.com>
---
src/lxc/lxc_controller.c | 12 ++++-
src/lxc/lxc_domain.h | 1 +
src/lxc/lxc_process.c | 119 ++++++++++++++++++++++++++++++++++++++++++----
src/lxc/lxc_protocol.x | 3 +-
4 files changed, 122 insertions(+), 13 deletions(-)
diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index 7fcc953..a26eec2 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -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;
diff --git a/src/lxc/lxc_domain.h b/src/lxc/lxc_domain.h
index 9216c7a..4301075 100644
--- a/src/lxc/lxc_domain.h
+++ b/src/lxc/lxc_domain.h
@@ -32,6 +32,7 @@ typedef virLXCDomainObjPrivate *virLXCDomainObjPrivatePtr;
struct _virLXCDomainObjPrivate {
virLXCMonitorPtr monitor;
int shutoffReason;
+ bool wantReboot;
};
void virLXCDomainSetPrivateDataHooks(virCapsPtr caps);
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 0b0a810..8a1ee7d 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -145,6 +145,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 */
So why do we even try to connect 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:
@@ -395,14 +447,37 @@ static int virLXCProcessSetupInterfaces(virConnectPtr conn,
case VIR_DOMAIN_NET_TYPE_NETWORK: {
virNetworkPtr network;
char *brname = NULL;
+ bool fail = false;
+ int active;
+ virErrorPtr errobj;
if (!(network = virNetworkLookupByName(conn,
def->nets[i]->data.network.name)))
goto cleanup;
- brname = virNetworkGetBridgeName(network);
+ active = virNetworkIsActive(network);
+ if (active != 1) {
+ fail = true;
+ if (active == 0)
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Network '%s' is not active."),
+ def->nets[i]->data.network.name);
+ goto cleanup;
+ }
+
+ if (!fail) {
+ brname = virNetworkGetBridgeName(network);
+ if (brname == NULL)
+ fail = true;
+ }
+
+ /* Make sure any above failure is preserved */
+ errobj = virSaveLastError();
virNetworkFree(network);
- if (!brname)
+ virSetError(errobj);
+ virFreeError(errobj);
+
+ if (fail)
goto cleanup;
if (virLXCProcessSetupInterfaceBridged(conn,
@@ -496,19 +571,38 @@ static void virLXCProcessMonitorEOFNotify(virLXCMonitorPtr mon
ATTRIBUTE_UNUSED,
{
virLXCDriverPtr driver = lxc_driver;
virDomainEventPtr event = NULL;
+ virLXCDomainObjPrivatePtr priv;
lxcDriverLock(driver);
virDomainObjLock(vm);
lxcDriverUnlock(driver);
- virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
- event = virDomainEventNewFromObj(vm,
- VIR_DOMAIN_EVENT_STOPPED,
- VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
- virDomainAuditStop(vm, "shutdown");
- if (!vm->persistent) {
- virDomainRemoveInactive(&driver->domains, vm);
- vm = NULL;
+ priv = vm->privateData;
+ if (!priv->wantReboot) {
+ virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
+ event = virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
+ virDomainAuditStop(vm, "shutdown");
+ if (!vm->persistent) {
+ virDomainRemoveInactive(&driver->domains, vm);
+ vm = NULL;
+ }
+ } else {
+ int ret =virLXCProcessReboot(driver, vm);
s/=/= /
+ virDomainAuditStop(vm, "reboot");
Should we audit stopped domain before calling virLXCProcessReboot?
+ virDomainAuditStart(vm, "reboot", ret == 0);
+ if (ret == 0) {
+ event = virDomainEventRebootNewFromObj(vm);
+ } else {
+ event = virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
+ if (!vm->persistent) {
+ virDomainRemoveInactive(&driver->domains, vm);
+ vm = NULL;
+ }
+ }
}
if (vm)
@@ -533,6 +627,10 @@ static void virLXCProcessMonitorExitNotify(virLXCMonitorPtr mon
ATTRIBUTE_UNUSED
case VIR_LXC_PROTOCOL_EXIT_STATUS_ERROR:
priv->shutoffReason = VIR_DOMAIN_SHUTOFF_CRASHED;
break;
+ case VIR_LXC_PROTOCOL_EXIT_STATUS_REBOOT:
+ priv->shutoffReason = VIR_DOMAIN_SHUTOFF_SHUTDOWN;
+ priv->wantReboot = true;
+ break;
default:
priv->shutoffReason = VIR_DOMAIN_SHUTOFF_UNKNOWN;
break;
@@ -1015,6 +1113,7 @@ int virLXCProcessStart(virConnectPtr conn,
}
priv->shutoffReason = VIR_DOMAIN_SHUTOFF_UNKNOWN;
+ priv->wantReboot = false;
vm->def->id = vm->pid;
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
diff --git a/src/lxc/lxc_protocol.x b/src/lxc/lxc_protocol.x
index 4fdbe34..e437217 100644
--- a/src/lxc/lxc_protocol.x
+++ b/src/lxc/lxc_protocol.x
@@ -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 {
ACK with nits addressed.
Jirka