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