[libvirt] [PATCH 0/6] Add more options for clock sync

This series adds two new options for clock synchronization The two current options are: <clock offset='utc'/> <clock offset='localtime'/> This introduces a way to set a timezone for localtime <clock offset='localtime' timezone='Europe/Paris'/> And a way to set a completely arbitrary time, by giving the number of seconds offset from UTC. <clock offset='variable' adjustment='123456'/> It is open to debate whether we need to distinguish this last one via the extra string 'variable', or just add 'adjustment' as an optional attribute for the existing term 'utc'. Opinions welcome

The XML will soon be extended to allow more than just a simple localtime/utc boolean flag. This change replaces the plain 'int localtime' with a separate struct to prepare for future extension * src/conf/domain_conf.c, src/conf/domain_conf.h: Add a new virDomainClockDef structure * src/libvirt_private.syms: Export virDomainClockOffsetTypeToString and virDomainClockOffsetTypeFromString * src/qemu/qemu_conf.c, src/vbox/vbox_tmpl.c, src/xen/xend_internal.c, src/xen/xm_internal.c: Updated to use new structure for localtime --- src/conf/domain_conf.c | 20 ++++++++++++++++---- src/conf/domain_conf.h | 16 +++++++++++++++- src/libvirt_private.syms | 2 ++ src/qemu/qemu_conf.c | 11 +++++++++-- src/vbox/vbox_tmpl.c | 2 +- src/xen/xend_internal.c | 16 +++++++++++----- src/xen/xm_internal.c | 21 ++++++++++++++++++--- 7 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 766993c..1daf6f4 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -222,6 +222,11 @@ VIR_ENUM_IMPL(virDomainSeclabel, VIR_DOMAIN_SECLABEL_LAST, "dynamic", "static") +VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST, + "utc", + "localtime"); + + #define virDomainReportError(conn, code, fmt...) \ virReportErrorHelper(conn, VIR_FROM_DOMAIN, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) @@ -3464,9 +3469,16 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, tmp = virXPathString(conn, "string(./clock/@offset)", ctxt); - if (tmp && STREQ(tmp, "localtime")) - def->localtime = 1; - VIR_FREE(tmp); + if (tmp) { + if ((def->clock.offset = virDomainClockOffsetTypeFromString(tmp)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown clock offset '%s'"), tmp); + goto error; + } + VIR_FREE(tmp); + } else { + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC; + } def->os.bootloader = virXPathString(conn, "string(./bootloader)", ctxt); def->os.bootloaderArgs = virXPathString(conn, "string(./bootloader_args)", ctxt); @@ -5408,7 +5420,7 @@ char *virDomainDefFormat(virConnectPtr conn, goto cleanup; virBufferVSprintf(&buf, " <clock offset='%s'/>\n", - def->localtime ? "localtime" : "utc"); + virDomainClockOffsetTypeToString(def->clock.offset)); if (virDomainLifecycleDefFormat(conn, &buf, def->onPoweroff, "on_poweroff") < 0) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 0b79e88..5653b18 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -594,6 +594,19 @@ struct _virSecurityLabelDef { int type; }; +enum virDomainClockOffsetType { + VIR_DOMAIN_CLOCK_OFFSET_UTC = 0, + VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME = 1, + + VIR_DOMAIN_CLOCK_OFFSET_LAST, +}; + +typedef struct _virDomainClockDef virDomainClockDef; +typedef virDomainClockDef *virDomainClockDefPtr; +struct _virDomainClockDef { + int offset; +}; + #define VIR_DOMAIN_CPUMASK_LEN 1024 /* Guest VM main configuration */ @@ -622,7 +635,7 @@ struct _virDomainDef { char *emulator; int features; - int localtime; + virDomainClockDef clock; int ngraphics; virDomainGraphicsDefPtr *graphics; @@ -914,5 +927,6 @@ VIR_ENUM_DECL(virDomainGraphics) /* from libvirt.h */ VIR_ENUM_DECL(virDomainState) VIR_ENUM_DECL(virDomainSeclabel) +VIR_ENUM_DECL(virDomainClockOffset) #endif /* __DOMAIN_CONF_H */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e5e8860..e882ae4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -188,6 +188,8 @@ virDomainDefAddDiskControllers; virDomainDefClearPCIAddresses; virDomainDefClearDeviceAliases; virDomainDeviceInfoIterate; +virDomainClockOffsetTypeToString; +virDomainClockOffsetTypeFromString; # domain_event.h diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 389db7b..8109820 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -3312,8 +3312,14 @@ int qemudBuildCommandLine(virConnectPtr conn, } } - if (def->localtime) + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) ADD_ARG_LIT("-localtime"); + else if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_UTC) { + qemudReportError(conn, NULL, NULL, VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock offset '%s'"), + virDomainClockOffsetTypeToString(def->clock.offset)); + goto error; + } if ((qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT) && def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART) @@ -5127,6 +5133,7 @@ virDomainDefPtr qemuParseCommandLine(virConnectPtr conn, def->id = -1; def->memory = def->maxmem = 64 * 1024; def->vcpus = 1; + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC; def->features = (1 << VIR_DOMAIN_FEATURE_ACPI) /*| (1 << VIR_DOMAIN_FEATURE_APIC)*/; def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART; @@ -5306,7 +5313,7 @@ virDomainDefPtr qemuParseCommandLine(virConnectPtr conn, } else if (STREQ(arg, "-full-screen")) { fullscreen = 1; } else if (STREQ(arg, "-localtime")) { - def->localtime = 1; + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME; } else if (STREQ(arg, "-kernel")) { WANT_VALUE(); if (!(def->os.kernel = strdup(val))) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index e40c848..cffdca2 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -2051,7 +2051,7 @@ static char *vboxDomainDumpXML(virDomainPtr dom, int flags) { /* Currently VirtualBox always uses locatime * so locatime is always true here */ - def->localtime = 1; + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME; /* dump video options vram/2d/3d/directx/etc. */ { diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c index 9c5127f..98f6103 100644 --- a/src/xen/xend_internal.c +++ b/src/xen/xend_internal.c @@ -2452,7 +2452,7 @@ xenDaemonParseSxpr(virConnectPtr conn, } else def->onCrash = VIR_DOMAIN_LIFECYCLE_DESTROY; - + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC; if (hvm) { if (sexpr_int(root, "domain/image/hvm/acpi")) def->features |= (1 << VIR_DOMAIN_FEATURE_ACPI); @@ -2463,12 +2463,12 @@ xenDaemonParseSxpr(virConnectPtr conn, /* Old XenD only allows localtime here for HVM */ if (sexpr_int(root, "domain/image/hvm/localtime")) - def->localtime = 1; + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME; } /* Current XenD allows localtime here, for PV and HVM */ if (sexpr_int(root, "domain/localtime")) - def->localtime = 1; + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME; if (sexpr_node_copy(root, hvm ? "domain/image/hvm/device_model" : @@ -5788,8 +5788,14 @@ xenDaemonFormatSxpr(virConnectPtr conn, virBufferVSprintf(&buf, "(on_crash '%s')", tmp); /* Set localtime here for current XenD (both PV & HVM) */ - if (def->localtime) + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) virBufferAddLit(&buf, "(localtime 1)"); + else if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_UTC) { + virXendError(conn, VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock offset '%s'"), + virDomainClockOffsetTypeToString(def->clock.offset)); + goto error; + } if (!def->os.bootloader) { if (STREQ(def->os.type, "hvm")) @@ -5907,7 +5913,7 @@ xenDaemonFormatSxpr(virConnectPtr conn, } /* Set localtime here to keep old XenD happy for HVM */ - if (def->localtime) + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) virBufferAddLit(&buf, "(localtime 1)"); if (def->sounds) { diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c index 944d5d5..4e7f844 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -681,6 +681,7 @@ xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { virDomainHostdevDefPtr hostdev = NULL; int i; const char *defaultArch, *defaultMachine; + int vmlocaltime = 0; if (VIR_ALLOC(def) < 0) { virReportOOMError(conn); @@ -828,9 +829,13 @@ xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { else if (val) def->features |= (1 << VIR_DOMAIN_FEATURE_APIC); } - if (xenXMConfigGetBool(conn, conf, "localtime", &def->localtime, 0) < 0) + if (xenXMConfigGetBool(conn, conf, "localtime", &vmlocaltime, 0) < 0) goto cleanup; + def->clock.offset = vmlocaltime ? + VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME : + VIR_DOMAIN_CLOCK_OFFSET_UTC; + if (xenXMConfigCopyStringOpt(conn, conf, "device_model", &def->emulator) < 0) goto cleanup; @@ -2323,8 +2328,18 @@ virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, goto no_memory; - if (xenXMConfigSetInt(conf, "localtime", def->localtime ? 1 : 0) < 0) - goto no_memory; + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME || + def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_UTC) { + if (xenXMConfigSetInt(conf, "localtime", + def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME ? + 1 : 0) < 0) + goto no_memory; + } else { + xenXMError(conn, VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock offset '%s'"), + virDomainClockOffsetTypeToString(def->clock.offset)); + goto cleanup; + } if (priv->xendConfigVersion == 1) { for (i = 0 ; i < def->ndisks ; i++) { -- 1.6.6

This introduces a third option for clock offset synchronization, that allows an arbitrary / variable adjustment to be set. In essence the XML contains the time delta in seconds, relative to UTC. <clock offset='variable' adjustment='123465'/> The difference from 'utc' mode, is that management apps should track adjustments and preserve them at next reboot. * docs/schemas/domain.rng: Schema for new clock mode * src/conf/domain_conf.c, src/conf/domain_conf.h: Parse new clock time delta * src/libvirt_private.syms, src/util/xml.c, src/util/xml.h: Add virXPathLongLong() method --- docs/schemas/domain.rng | 25 +++++++++++++++++--- src/conf/domain_conf.c | 14 ++++++++++- src/conf/domain_conf.h | 5 ++++ src/libvirt_private.syms | 1 + src/util/xml.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/xml.h | 4 +++ 6 files changed, 98 insertions(+), 6 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index bb6d00d..5c48a8b 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -297,12 +297,24 @@ <define name="clock"> <optional> <element name="clock"> - <attribute name="offset"> - <choice> + <choice> + <attribute name="offset"> <value>localtime</value> + </attribute> + <attribute name="offset"> <value>utc</value> - </choice> - </attribute> + </attribute> + <group> + <attribute name="offset"> + <value>variable</value> + </attribute> + <optional> + <attribute name="adjustment"> + <ref name="timeDelta"/> + </attribute> + </optional> + </group> + </choice> <empty/> </element> </optional> @@ -1526,4 +1538,9 @@ <param name='pattern'>[a-zA-Z0-9\-_]+</param> </data> </define> + <define name="timeDelta"> + <data type="string"> + <param name="pattern">(-|\+)?[0-9]+</param> + </data> + </define> </grammar> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1daf6f4..0c502b9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -224,7 +224,8 @@ VIR_ENUM_IMPL(virDomainSeclabel, VIR_DOMAIN_SECLABEL_LAST, VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST, "utc", - "localtime"); + "localtime", + "variable"); #define virDomainReportError(conn, code, fmt...) \ @@ -3479,6 +3480,11 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, } else { def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC; } + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE) { + if (virXPathLongLong(conn, "./clock/@adjustment", ctxt, + &def->clock.adjustment) < 0) + def->clock.adjustment = 0; + } def->os.bootloader = virXPathString(conn, "string(./bootloader)", ctxt); def->os.bootloaderArgs = virXPathString(conn, "string(./bootloader_args)", ctxt); @@ -5419,8 +5425,12 @@ char *virDomainDefFormat(virConnectPtr conn, if (virCPUDefFormatBuf(conn, &buf, def->cpu, " ", 0) < 0) goto cleanup; - virBufferVSprintf(&buf, " <clock offset='%s'/>\n", + virBufferVSprintf(&buf, " <clock offset='%s'", virDomainClockOffsetTypeToString(def->clock.offset)); + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE) { + virBufferVSprintf(&buf, " adjustment='%lld'", def->clock.adjustment); + } + virBufferAddLit(&buf, "/>\n"); if (virDomainLifecycleDefFormat(conn, &buf, def->onPoweroff, "on_poweroff") < 0) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 5653b18..4df28e5 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -597,6 +597,7 @@ struct _virSecurityLabelDef { enum virDomainClockOffsetType { VIR_DOMAIN_CLOCK_OFFSET_UTC = 0, VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME = 1, + VIR_DOMAIN_CLOCK_OFFSET_VARIABLE = 2, VIR_DOMAIN_CLOCK_OFFSET_LAST, }; @@ -605,6 +606,10 @@ typedef struct _virDomainClockDef virDomainClockDef; typedef virDomainClockDef *virDomainClockDefPtr; struct _virDomainClockDef { int offset; + + /* Adjustment in seconds, relative to UTC, when + * offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE */ + long long adjustment; }; #define VIR_DOMAIN_CPUMASK_LEN 1024 diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e882ae4..39b7c45 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -625,6 +625,7 @@ virXPathStringLimit; virXPathBoolean; virXPathNumber; virXPathULong; +virXPathLongLong; virXPathULongLong; virXPathLongHex; virXPathULongHex; diff --git a/src/util/xml.c b/src/util/xml.c index 4fa443d..52786d0 100644 --- a/src/util/xml.c +++ b/src/util/xml.c @@ -374,6 +374,61 @@ virXPathULongLong(virConnectPtr conn, return (ret); } +/** + * virXPathULongLong: + * @xpath: the XPath string to evaluate + * @ctxt: an XPath context + * @value: the returned long long value + * + * Convenience function to evaluate an XPath number + * + * Returns 0 in case of success in which case @value is set, + * or -1 if the XPath evaluation failed or -2 if the + * value doesn't have a long format. + */ +int +virXPathLongLong(virConnectPtr conn, + const char *xpath, + xmlXPathContextPtr ctxt, + long long *value) +{ + xmlXPathObjectPtr obj; + xmlNodePtr relnode; + int ret = 0; + + if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) { + virXMLError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid parameter to virXPathLongLong()")); + return (-1); + } + relnode = ctxt->node; + obj = xmlXPathEval(BAD_CAST xpath, ctxt); + ctxt->node = relnode; + if ((obj != NULL) && (obj->type == XPATH_STRING) && + (obj->stringval != NULL) && (obj->stringval[0] != 0)) { + char *conv = NULL; + unsigned long long val; + + val = strtoll((const char *) obj->stringval, &conv, 10); + if (conv == (const char *) obj->stringval) { + ret = -2; + } else { + *value = val; + } + } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) && + (!(isnan(obj->floatval)))) { + *value = (long long) obj->floatval; + if (*value != obj->floatval) { + ret = -2; + } + } else { + ret = -1; + } + + xmlXPathFreeObject(obj); + return (ret); +} + char * virXMLPropString(xmlNodePtr node, const char *name) diff --git a/src/util/xml.h b/src/util/xml.h index cddd42b..80d6da1 100644 --- a/src/util/xml.h +++ b/src/util/xml.h @@ -37,6 +37,10 @@ int virXPathULongLong(virConnectPtr conn, const char *xpath, xmlXPathContextPtr ctxt, unsigned long long *value); +int virXPathLongLong(virConnectPtr conn, + const char *xpath, + xmlXPathContextPtr ctxt, + long long *value); int virXPathLongHex (virConnectPtr conn, const char *xpath, xmlXPathContextPtr ctxt, -- 1.6.6

This allows QEMU guests to be started with an arbitrary clock offset The test case can't actually be enabled, since QEMU argv expects an absolute timestring, and this will obviously change every time the test runs :-( Hopefully QEMU will allow a relative time offset in the future. * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Use the -rtc arg if available to support variable clock offset mode * tests/qemuhelptest.c: Add QEMUD_CMD_FLAG_RTC for qemu 0.12.1 * qemuxml2argvdata/qemuxml2argv-clock-variable.args, qemuxml2argvdata/qemuxml2argv-clock-variable.xml, qemuxml2argvtest.c: Test case, except we can't actually enable it yet. --- src/qemu/qemu_conf.c | 75 ++++++++++++++++++-- src/qemu/qemu_conf.h | 1 + tests/qemuhelptest.c | 3 +- .../qemuxml2argv-clock-variable.args | 1 + .../qemuxml2argv-clock-variable.xml | 24 ++++++ tests/qemuxml2argvtest.c | 4 + 6 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-clock-variable.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-clock-variable.xml diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 8109820..bf77f3c 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1154,6 +1154,9 @@ static unsigned int qemudComputeCmdFlags(const char *help, flags |= QEMUD_CMD_FLAG_BALLOON; if (strstr(help, "-device")) flags |= QEMUD_CMD_FLAG_DEVICE; + /* The trailing ' ' is important to avoid a bogus match */ + if (strstr(help, "-rtc ")) + flags |= QEMUD_CMD_FLAG_RTC; /* Keep disabled till we're actually ready to turn on netdev mode * The plan is todo it in 0.13.0 QEMU, but lets wait & see... */ #if 0 @@ -2882,6 +2885,56 @@ error: } +static char * +qemuBuildClockArgStr(virDomainClockDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + switch (def->offset) { + case VIR_DOMAIN_CLOCK_OFFSET_UTC: + virBufferAddLit(&buf, "base=utc"); + break; + + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + virBufferAddLit(&buf, "base=localtime"); + break; + + case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: { + time_t now = time(NULL); + struct tm nowbits; + + now += def->adjustment; + gmtime_r(&now, &nowbits); + + virBufferVSprintf(&buf, "base=%d-%d-%dT%d:%d:%d", + nowbits.tm_year, + nowbits.tm_mon, + nowbits.tm_mday, + nowbits.tm_hour, + nowbits.tm_min, + nowbits.tm_sec); + } break; + + default: + qemudReportError(NULL, NULL, NULL, VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock offset '%s'"), + virDomainClockOffsetTypeToString(def->offset)); + goto error; + } + + if (virBufferError(&buf)) { + virReportOOMError(NULL); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + static int qemuBuildCpuArgStr(virConnectPtr conn, const struct qemud_driver *driver, @@ -3312,13 +3365,21 @@ int qemudBuildCommandLine(virConnectPtr conn, } } - if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) - ADD_ARG_LIT("-localtime"); - else if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_UTC) { - qemudReportError(conn, NULL, NULL, VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported clock offset '%s'"), - virDomainClockOffsetTypeToString(def->clock.offset)); - goto error; + if (qemuCmdFlags & QEMUD_CMD_FLAG_RTC) { + const char *rtcopt; + ADD_ARG_LIT("-rtc"); + if (!(rtcopt = qemuBuildClockArgStr(&def->clock))) + goto error; + ADD_ARG(rtcopt); + } else { + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) + ADD_ARG_LIT("-localtime"); + else if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_UTC) { + qemudReportError(conn, NULL, NULL, VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock offset '%s'"), + virDomainClockOffsetTypeToString(def->clock.offset)); + goto error; + } } if ((qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT) && diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 101f187..f67284b 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -82,6 +82,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_SDL = (1 << 27), /* Is the new -sdl arg available */ QEMUD_CMD_FLAG_SMP_TOPOLOGY = (1 << 28), /* Is sockets=s,cores=c,threads=t available for -smp? */ QEMUD_CMD_FLAG_NETDEV = (1 << 29), /* The -netdev flag & netdev_add/remove monitor commands */ + QEMUD_CMD_FLAG_RTC = (1 << 30), /* The -rtc flag for clock options */ }; /* Main driver state */ diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index 34a6048..35e43cb 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -224,7 +224,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_CHARDEV | QEMUD_CMD_FLAG_BALLOON | QEMUD_CMD_FLAG_DEVICE | - QEMUD_CMD_FLAG_SMP_TOPOLOGY, + QEMUD_CMD_FLAG_SMP_TOPOLOGY | + QEMUD_CMD_FLAG_RTC, 12001, 0, 0); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.args b/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.args new file mode 100644 index 0000000..09a9197 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -monitor unix:/tmp/test-monitor,server,nowait -rtc base=2010-2-2T18:22:10 -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -usb diff --git a/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.xml b/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.xml new file mode 100644 index 0000000..fa20b27 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-clock-variable.xml @@ -0,0 +1,24 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='variable' adjustment='123456'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <controller type='ide' index='0'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 67dc47e..dff3e27 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -218,6 +218,10 @@ mymain(int argc, char **argv) DO_TEST("bootloader", QEMUD_CMD_FLAG_DOMID); DO_TEST("clock-utc", 0); DO_TEST("clock-localtime", 0); + /* + * Can't be enabled since the absolute timestamp changes every time + DO_TEST("clock-variable", QEMUD_CMD_FLAG_RTC); + */ DO_TEST("hugepages", QEMUD_CMD_FLAG_MEM_PATH); DO_TEST("disk-cdrom", 0); DO_TEST("disk-cdrom-empty", QEMUD_CMD_FLAG_DRIVE); -- 1.6.6

This extends the XML to allow for <clock offset='localtime' timezone='Europe/Paris'/> This is useful if the admin has not configured any timezone on the host OS, but still wants to synchronize a guest to a specific one. * src/conf/domain_conf.h, src/conf/domain_conf.c: Support extra 'timezone' attribute on clock configuration * docs/schemas/domain.rng: Add 'timezone' attribute * src/xen/xend_internal.c, src/xen/xm_internal.c: Reject configs with a configurable timezone --- docs/schemas/domain.rng | 18 +++++++++++++++--- src/conf/domain_conf.c | 25 ++++++++++++++++++++----- src/conf/domain_conf.h | 12 +++++++++--- src/xen/xend_internal.c | 10 ++++++++-- src/xen/xm_internal.c | 16 +++++++++++----- 5 files changed, 63 insertions(+), 18 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 5c48a8b..95c7d8e 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -298,9 +298,16 @@ <optional> <element name="clock"> <choice> - <attribute name="offset"> - <value>localtime</value> - </attribute> + <group> + <attribute name="offset"> + <value>localtime</value> + </attribute> + <optional> + <attribute name="timezone"> + <ref name="timeZone"/> + </attribute> + </optional> + </group> <attribute name="offset"> <value>utc</value> </attribute> @@ -1543,4 +1550,9 @@ <param name="pattern">(-|\+)?[0-9]+</param> </data> </define> + <define name="timeZone"> + <data type="string"> + <param name="pattern">[a-zA-Z0-9_\.\+\-/]+</param> + </data> + </define> </grammar> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0c502b9..c5eb086 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -640,6 +640,9 @@ void virDomainDefFree(virDomainDefPtr def) VIR_FREE(def->os.bootloader); VIR_FREE(def->os.bootloaderArgs); + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) + VIR_FREE(def->clock.data.timezone); + VIR_FREE(def->name); VIR_FREE(def->cpumask); VIR_FREE(def->emulator); @@ -3480,10 +3483,16 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, } else { def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC; } - if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE) { + switch (def->clock.offset) { + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + def->clock.data.timezone = virXPathString(conn, "string(./clock/@timezone)", ctxt); + break; + + case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: if (virXPathLongLong(conn, "./clock/@adjustment", ctxt, - &def->clock.adjustment) < 0) - def->clock.adjustment = 0; + &def->clock.data.adjustment) < 0) + def->clock.data.adjustment = 0; + break; } def->os.bootloader = virXPathString(conn, "string(./bootloader)", ctxt); @@ -5427,8 +5436,14 @@ char *virDomainDefFormat(virConnectPtr conn, virBufferVSprintf(&buf, " <clock offset='%s'", virDomainClockOffsetTypeToString(def->clock.offset)); - if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE) { - virBufferVSprintf(&buf, " adjustment='%lld'", def->clock.adjustment); + switch (def->clock.offset) { + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + if (def->clock.data.timezone) + virBufferEscapeString(&buf, " timezone='%s'", def->clock.data.timezone); + break; + case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: + virBufferVSprintf(&buf, " adjustment='%lld'", def->clock.data.adjustment); + break; } virBufferAddLit(&buf, "/>\n"); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4df28e5..0261979 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -607,9 +607,15 @@ typedef virDomainClockDef *virDomainClockDefPtr; struct _virDomainClockDef { int offset; - /* Adjustment in seconds, relative to UTC, when - * offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE */ - long long adjustment; + union { + /* Adjustment in seconds, relative to UTC, when + * offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE */ + long long adjustment; + + /* Timezone name, when + * offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME */ + char *timezone; + } data; }; #define VIR_DOMAIN_CPUMASK_LEN 1024 diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c index 98f6103..c6b5b6b 100644 --- a/src/xen/xend_internal.c +++ b/src/xen/xend_internal.c @@ -5788,9 +5788,15 @@ xenDaemonFormatSxpr(virConnectPtr conn, virBufferVSprintf(&buf, "(on_crash '%s')", tmp); /* Set localtime here for current XenD (both PV & HVM) */ - if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) { + if (def->clock.data.timezone) { + virXendError(conn, VIR_ERR_CONFIG_UNSUPPORTED, + _("configurable timezones are not supported")); + goto error; + } + virBufferAddLit(&buf, "(localtime 1)"); - else if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_UTC) { + } else if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_UTC) { virXendError(conn, VIR_ERR_CONFIG_UNSUPPORTED, _("unsupported clock offset '%s'"), virDomainClockOffsetTypeToString(def->clock.offset)); diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c index 4e7f844..88f472a 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -2328,11 +2328,17 @@ virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, goto no_memory; - if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME || - def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_UTC) { - if (xenXMConfigSetInt(conf, "localtime", - def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME ? - 1 : 0) < 0) + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) { + if (def->clock.data.timezone) { + virXendError(conn, VIR_ERR_CONFIG_UNSUPPORTED, + _("configurable timezones are not supported")); + goto error; + } + + if (xenXMConfigSetInt(conf, "localtime", 1) < 0) + goto no_memory; + } else if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_UTC) { + if (xenXMConfigSetInt(conf, "localtime", 0) < 0) goto no_memory; } else { xenXMError(conn, VIR_ERR_CONFIG_UNSUPPORTED, -- 1.6.6

Allow an arbitrary timezone with QEMU by setting the $TZ environment variable when launching QEMU * src/qemu/qemu_conf.c: Set TZ environment variable if a timezone is requested * tests/qemuxml2argvtest.c: Add test case for timezones * tests/qemuxml2argvdata/qemuxml2argv-clock-france.xml, tests/qemuxml2argvdata/qemuxml2argv-clock-france.args: Data for timezone tests --- src/qemu/qemu_conf.c | 6 ++++- .../qemuxml2argv-clock-france.args | 1 + .../qemuxml2argvdata/qemuxml2argv-clock-france.xml | 24 ++++++++++++++++++++ tests/qemuxml2argvtest.c | 2 + 4 files changed, 32 insertions(+), 1 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-clock-france.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-clock-france.xml diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index bf77f3c..d9bea33 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -2903,7 +2903,7 @@ qemuBuildClockArgStr(virDomainClockDefPtr def) time_t now = time(NULL); struct tm nowbits; - now += def->adjustment; + now += def->data.adjustment; gmtime_r(&now, &nowbits); virBufferVSprintf(&buf, "base=%d-%d-%dT%d:%d:%d", @@ -3381,6 +3381,10 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } } + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME && + def->clock.data.timezone) { + ADD_ENV_PAIR("TZ", def->clock.data.timezone); + } if ((qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT) && def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-clock-france.args b/tests/qemuxml2argvdata/qemuxml2argv-clock-france.args new file mode 100644 index 0000000..3c57397 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-clock-france.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test TZ=Europe/Paris /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -monitor unix:/tmp/test-monitor,server,nowait -rtc base=localtime -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -usb diff --git a/tests/qemuxml2argvdata/qemuxml2argv-clock-france.xml b/tests/qemuxml2argvdata/qemuxml2argv-clock-france.xml new file mode 100644 index 0000000..88477b6 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-clock-france.xml @@ -0,0 +1,24 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='localtime' timezone='Europe/Paris'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <controller type='ide' index='0'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index dff3e27..23ee5d5 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -222,6 +222,8 @@ mymain(int argc, char **argv) * Can't be enabled since the absolute timestamp changes every time DO_TEST("clock-variable", QEMUD_CMD_FLAG_RTC); */ + DO_TEST("clock-france", QEMUD_CMD_FLAG_RTC); + DO_TEST("hugepages", QEMUD_CMD_FLAG_MEM_PATH); DO_TEST("disk-cdrom", 0); DO_TEST("disk-cdrom-empty", QEMUD_CMD_FLAG_DRIVE); -- 1.6.6

* formatdomain.html.in: Document new clock options --- docs/formatdomain.html.in | 34 +++++++++++++++++++++++++++++++--- 1 files changed, 31 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index ce49f7d..e47a6b7 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -393,9 +393,37 @@ <dl> <dt><code>clock</code></dt> - <dd>The <code>offset</code> attribute takes either "utc" or - "localtime" to specify how the guest clock is initialized - in relation to the host OS. + <dd> + <p>The <code>offset</code> attribute takes three possible + values, allowing fine grained control over how the guest + clock is synchronized to the host. NB, not all hypervisors + support all modes.</p> + <dl> + <dt><code>utc</code></dt> + <dd> + The guest clock will always be synchronized to UTC when + booted</dd> + <dt><code>localtime</code></dt> + <dd> + The guest clock will be synchronized to the host's configured + timezone when booted. It is possible to override the timezone + by using the <code>timezone</code> attribute. + </dd> + <dt><code>variable</code></dt> + <dd> + The guest clock will have an arbitrary offset applied + relative to UTC. The delta relative to UTC is specified + in seconds, using the <code>adjustment</code> attribute. + The guest is free to adjust the RTC over time an expect + that it will be honoured at next reboot. This is in + contrast to 'utc' mode, where the RTC adjustments are + lost at each reboot. + </dd> + </dl> + <p> + NB, at time of writing, only QEMU supports the variable + clock mode, or custom timezones. + </p> </dd> </dl> -- 1.6.6
participants (1)
-
Daniel P. Berrange