From b8bf79aad76040a4106903c3a8af42e7580f80de Mon Sep 17 00:00:00 2001 From: Philipp Hahn Date: Mon, 6 Feb 2012 14:59:16 +0100 Subject: [PATCH] Support clock=variable relative to localtime Since Xen 3.1 the clock=variable semantic is supported. In addition to qemu/kvm Xen also knows about a variant where the offset is relative to 'localtime' instead of 'utc'. Extends the libvirt structure with a flag 'basis' to specify, if the offset is relative to 'localtime' or 'utc'. Extends the libvirt structure with a flag 'reset' to force the reset behaviour of 'localtime' and 'utc'; this is needed for backward compatibility with previous versions of libvirt, since they report incorrect XML. Adapt the only user 'qemu' to the new name. Extend the RelaxNG schema accordingly. Document the new 'basis' attribute in the HTML documentation. Adapt test for the new attribute. Signed-off-by: Philipp Hahn --- docs/formatdomain.html.in | 24 ++++++-- docs/schemas/domaincommon.rng | 30 +++++++-- src/conf/domain_conf.c | 61 +++++++++++++++++-- src/conf/domain_conf.h | 17 +++++- src/libvirt_private.syms | 1 + src/qemu/qemu_command.c | 8 ++- src/qemu/qemu_process.c | 2 +- .../qemuxml2argv-clock-variable.xml | 2 +- 8 files changed, 126 insertions(+), 19 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 863377ca85..a382d30098 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -985,11 +985,23 @@
utc
The guest clock will always be synchronized to UTC when - booted
+ booted. + Since 0.9.11 'utc' mode can be converted + to 'variable' mode, which can be controlled by using the + adjustment attribute. If the value is 'reset', the + conversion is never done (not all hypervisors can + synchronize to UTC on each boot; use of 'reset' will cause + an error on those hypervisors). A numeric value + forces the conversion to 'variable' mode using the value as the + initial adjustment. The default adjustment is + hypervisor specific. +
localtime
The guest clock will be synchronized to the host's configured timezone when booted, if any. + Since 0.9.11, the adjustment + attribute behaves the same as in 'utc' mode.
timezone
@@ -1000,12 +1012,16 @@
variable
The guest clock will have an arbitrary offset applied - relative to UTC. The delta relative to UTC is specified + relative to UTC or localtime, depending on the basis + attribute. The delta relative to UTC (or localtime) is specified in seconds, using the adjustment attribute. The guest is free to adjust the RTC over time and expect - that it will be honoured at next reboot. This is in - contrast to 'utc' mode, where the RTC adjustments are + that it will be honored at next reboot. This is in + contrast to 'utc' and 'localtime' mode (with the optional + attribute adjustment='reset'), where the RTC adjustments are lost at each reboot. Since 0.7.7 + Since 0.9.11 the basis + attribute can be either 'utc' (default) or 'localtime'.

diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 5ed47aca63..0cc04af5da 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -582,12 +582,22 @@ - - localtime - - - utc - + + + + localtime + utc + + + + + + + reset + + + + timezone @@ -607,6 +617,14 @@ + + + + utc + localtime + + + diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c800160441..cca757d607 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -590,6 +590,10 @@ VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST, "variable", "timezone"); +VIR_ENUM_IMPL(virDomainClockBasis, VIR_DOMAIN_CLOCK_BASIS_LAST, + "utc", + "localtime"); + VIR_ENUM_IMPL(virDomainTimerName, VIR_DOMAIN_TIMER_NAME_LAST, "platform", "pit", @@ -8069,10 +8073,53 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC; } switch (def->clock.offset) { + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + case VIR_DOMAIN_CLOCK_OFFSET_UTC: + tmp = virXPathString("string(./clock/@adjustment)", ctxt); + if (tmp) { + if (STREQ(tmp, "reset")) { + def->clock.data.utc_reset = true; + } else { + char *conv = NULL; + unsigned long long val; + val = strtoll(tmp, &conv, 10); + if (conv == tmp || *conv != '\0') { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown clock adjustment '%s'"), tmp); + goto error; + } + switch (def->clock.offset) { + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + def->clock.data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_LOCALTIME; + break; + case VIR_DOMAIN_CLOCK_OFFSET_UTC: + def->clock.data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC; + break; + } + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_VARIABLE; + def->clock.data.variable.adjustment = val; + } + VIR_FREE(tmp); + } else { + def->clock.data.utc_reset = false; + } + break; + case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: if (virXPathLongLong("number(./clock/@adjustment)", ctxt, - &def->clock.data.adjustment) < 0) - def->clock.data.adjustment = 0; + &def->clock.data.variable.adjustment) < 0) + def->clock.data.variable.adjustment = 0; + tmp = virXPathString("string(./clock/@basis)", ctxt); + if (tmp) { + if ((def->clock.data.variable.basis = virDomainClockBasisTypeFromString(tmp)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown clock basis '%s'"), tmp); + goto error; + } + VIR_FREE(tmp); + } else { + def->clock.data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC; + } break; case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: @@ -12521,9 +12568,15 @@ virDomainDefFormatInternal(virDomainDefPtr def, virBufferAsprintf(buf, " clock.offset)); switch (def->clock.offset) { + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + case VIR_DOMAIN_CLOCK_OFFSET_UTC: + if (def->clock.data.utc_reset) + virBufferAddLit(buf, " adjustment='reset'"); + break; case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: - virBufferAsprintf(buf, " adjustment='%lld'", - def->clock.data.adjustment); + virBufferAsprintf(buf, " adjustment='%lld' basis='%s'", + def->clock.data.variable.adjustment, + virDomainClockBasisTypeToString(def->clock.data.variable.basis)); break; case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: virBufferEscapeString(buf, " timezone='%s'", def->clock.data.timezone); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3fcb02644c..0eed60e02a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1446,15 +1446,27 @@ enum virDomainClockOffsetType { VIR_DOMAIN_CLOCK_OFFSET_LAST, }; +enum virDomainClockBasis { + VIR_DOMAIN_CLOCK_BASIS_UTC = 0, + VIR_DOMAIN_CLOCK_BASIS_LOCALTIME = 1, + + VIR_DOMAIN_CLOCK_BASIS_LAST, +}; + typedef struct _virDomainClockDef virDomainClockDef; typedef virDomainClockDef *virDomainClockDefPtr; struct _virDomainClockDef { int offset; union { - /* Adjustment in seconds, relative to UTC, when + /* Bug-compatibility-mode for Xen utc|localtime */ + int utc_reset; + /* Adjustment in seconds, relative to UTC or LOCALTIME, when * offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE */ - long long adjustment; + struct { + long long adjustment; + int basis; + } variable; /* Timezone name, when * offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME */ @@ -2176,6 +2188,7 @@ int virDomainStateReasonFromString(virDomainState state, const char *reason); VIR_ENUM_DECL(virDomainSeclabel) VIR_ENUM_DECL(virDomainClockOffset) +VIR_ENUM_DECL(virDomainClockBasis) VIR_ENUM_DECL(virDomainTimerName) VIR_ENUM_DECL(virDomainTimerTrack) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 97fec2fcf4..a90f8a0fa2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -255,6 +255,7 @@ virDomainChrTcpProtocolTypeFromString; virDomainChrTcpProtocolTypeToString; virDomainChrTypeFromString; virDomainChrTypeToString; +virDomainClockBasisTypeToString; virDomainClockOffsetTypeFromString; virDomainClockOffsetTypeToString; virDomainConfigFile; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index cfd5d756cb..ea9431fa31 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3563,7 +3563,13 @@ qemuBuildClockArgStr(virDomainClockDefPtr def) time_t now = time(NULL); struct tm nowbits; - now += def->data.adjustment; + if (def->data.variable.basis != VIR_DOMAIN_CLOCK_BASIS_UTC) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock basis '%s'"), + virDomainClockBasisTypeToString(def->data.variable.basis)); + goto error; + } + now += def->data.variable.adjustment; gmtime_r(&now, &nowbits); virBufferAsprintf(&buf, "base=%d-%02d-%02dT%02d:%02d:%02d", diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 0667a03924..91f1c5b095 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -738,7 +738,7 @@ qemuProcessHandleRTCChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED, event = virDomainEventRTCChangeNewFromObj(vm, offset); if (vm->def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE) - vm->def->clock.data.adjustment = offset; + vm->def->clock.data.variable.adjustment = offset; if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) VIR_WARN("unable to save domain status with RTC change"); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.xml b/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.xml index 97c0aedb45..73103bbb3a 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.xml @@ -8,7 +8,7 @@ hvm - + destroy restart destroy