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