[libvirt] [PATCH 11/11] Add handling for reboots of LXC containers
Jiri Denemark
jdenemar at redhat.com
Fri Jul 27 08:18:48 UTC 2012
On Tue, Jul 24, 2012 at 14:22:53 +0100, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange at 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 at 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
More information about the libvir-list
mailing list