[libvirt] [PATCHv4 0/3] Time setting and getting in qemu guests

Hopefully the last round. diff to v3: -rebase & resend Michal Privoznik (3): Introduce virDomain{Get,Set}Time APIs virsh: Expose virDomain{Get,Set}Time qemu: Implement virDomain{Get,Set}Time daemon/remote.c | 37 ++++++++++++ include/libvirt/libvirt.h.in | 14 +++++ src/access/viraccessperm.c | 2 +- src/access/viraccessperm.h | 7 ++- src/driver.h | 14 +++++ src/libvirt.c | 94 ++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + src/qemu/qemu_agent.c | 99 ++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 8 +++ src/qemu/qemu_driver.c | 109 +++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 35 ++++++++++++ src/remote/remote_protocol.x | 31 +++++++++- src/remote_protocol-structs | 16 ++++++ tools/virsh-domain-monitor.c | 132 +++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 16 ++++++ 15 files changed, 613 insertions(+), 3 deletions(-) -- 1.9.0

These APIs allow users to get or set time in a domain, which may come handy if the domain has been resumed just recently and NTP is not configured or hasn't kicked in yet and the guest is running something time critical. In addition, NTP may refuse to re-set the clock if the skew is too big. In addition, new ACL attribute is introduced 'set_time'. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- daemon/remote.c | 37 +++++++++++++++++ include/libvirt/libvirt.h.in | 14 +++++++ src/access/viraccessperm.c | 2 +- src/access/viraccessperm.h | 7 +++- src/driver.h | 14 +++++++ src/libvirt.c | 94 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + src/remote/remote_driver.c | 35 +++++++++++++++++ src/remote/remote_protocol.x | 31 ++++++++++++++- src/remote_protocol-structs | 16 ++++++++ 10 files changed, 249 insertions(+), 3 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 8476961..34c96c9 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6077,6 +6077,43 @@ qemuDispatchConnectDomainMonitorEventDeregister(virNetServerPtr server ATTRIBUTE return rv; } +static int +remoteDispatchDomainGetTime(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_time_args *args, + remote_domain_get_time_ret *ret) +{ + int rv = -1; + virDomainPtr dom = NULL; + long long seconds; + unsigned int nseconds; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if (virDomainGetTime(dom, &seconds, &nseconds, args->flags) < 0) + goto cleanup; + + ret->seconds = seconds; + ret->nseconds = nseconds; + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + return rv; +} /*----- Helpers. -----*/ diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 337ad71..260c971 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -5289,6 +5289,20 @@ int virDomainFSThaw(virDomainPtr dom, unsigned int nmountpoints, unsigned int flags); +int virDomainGetTime(virDomainPtr dom, + long long *seconds, + unsigned int *nseconds, + unsigned int flags); + +typedef enum { + VIR_DOMAIN_TIME_SYNC = (1 << 0), /* Re-sync domain time from domain's RTC */ +} virDomainSetTimeFlags; + +int virDomainSetTime(virDomainPtr dom, + long long seconds, + unsigned int nseconds, + unsigned int flags); + /** * virSchedParameterType: * diff --git a/src/access/viraccessperm.c b/src/access/viraccessperm.c index 8ca1edf..9007186 100644 --- a/src/access/viraccessperm.c +++ b/src/access/viraccessperm.c @@ -43,7 +43,7 @@ VIR_ENUM_IMPL(virAccessPermDomain, "fs_trim", "fs_freeze", "block_read", "block_write", "mem_read", "open_graphics", "open_device", "screenshot", - "open_namespace"); + "open_namespace", "set_time"); VIR_ENUM_IMPL(virAccessPermInterface, VIR_ACCESS_PERM_INTERFACE_LAST, diff --git a/src/access/viraccessperm.h b/src/access/viraccessperm.h index 50cea1e..8ccbbad 100644 --- a/src/access/viraccessperm.h +++ b/src/access/viraccessperm.h @@ -288,13 +288,18 @@ typedef enum { */ VIR_ACCESS_PERM_DOMAIN_SCREENSHOT, /* Trigger a screen shot */ - /** * @desc: Open domain namespace * @message: Opening domain namespaces requires authorization */ VIR_ACCESS_PERM_DOMAIN_OPEN_NAMESPACE, + /** + * @desc: Write domain time + * @message: Setting the domain time requires authorization + */ + VIR_ACCESS_PERM_DOMAIN_SET_TIME, + VIR_ACCESS_PERM_DOMAIN_LAST, } virAccessPermDomain; diff --git a/src/driver.h b/src/driver.h index 502f30e..5ac89d6 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1084,6 +1084,18 @@ typedef int unsigned int flags); typedef int +(*virDrvDomainGetTime)(virDomainPtr dom, + long long *seconds, + unsigned int *nseconds, + unsigned int flags); + +typedef int +(*virDrvDomainSetTime)(virDomainPtr dom, + long long seconds, + unsigned int nseconds, + unsigned int flags); + +typedef int (*virDrvDomainLxcOpenNamespace)(virDomainPtr dom, int **fdlist, unsigned int flags); @@ -1377,6 +1389,8 @@ struct _virDriver { virDrvConnectGetCPUModelNames connectGetCPUModelNames; virDrvDomainFSFreeze domainFSFreeze; virDrvDomainFSThaw domainFSThaw; + virDrvDomainGetTime domainGetTime; + virDrvDomainSetTime domainSetTime; }; diff --git a/src/libvirt.c b/src/libvirt.c index 2cd793c..ccb7113 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -20791,3 +20791,97 @@ virDomainFSThaw(virDomainPtr dom, virDispatchError(dom->conn); return -1; } + +/** + * virDomainGetTime: + * @dom: a domain object + * @seconds: domain's time in seconds + * @nseconds: the nanoscond part of @seconds + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Extract information about guest time and store it into + * @seconds and @nseconds. The @seconds represents the number of + * seconds since the UNIX Epoch of 1970-01-01 00:00:00 in UTC. + * + * Please note that some hypervisors may require guest agent to + * be configured and running in order to run this API. + * + * Returns 0 on success, -1 otherwise. + */ +int +virDomainGetTime(virDomainPtr dom, + long long *seconds, + unsigned int *nseconds, + unsigned int flags) +{ + VIR_DOMAIN_DEBUG(dom, "seconds=%p, nseconds=%p, flags=%x", + seconds, nseconds, flags); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + + if (dom->conn->driver->domainGetTime) { + int ret = dom->conn->driver->domainGetTime(dom, seconds, + nseconds, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} + +/** + * virDomainSetTime: + * @dom: a domain object + * @seconds: time to set + * @nseconds: the nanosecond part of @seconds + * @flags: bitwise-OR of virDomainSetTimeFlags + * + * When a domain is suspended or restored from a file the + * domain's OS has no idea that there was a big gap in the time. + * Depending on how long the gap was, NTP might not be able to + * resynchronize the guest. + * + * This API tries to set guest time to the given value. The time + * to set (@seconds and @nseconds) should be in seconds relative + * to the Epoch of 1970-01-01 00:00:00 in UTC. + * + * Please note that some hypervisors may require guest agent to + * be configured and running in order to be able to run this API. + * + * Returns 0 on success, -1 otherwise. + */ +int +virDomainSetTime(virDomainPtr dom, + long long seconds, + unsigned int nseconds, + unsigned int flags) +{ + VIR_DOMAIN_DEBUG(dom, "seconds=%lld, nseconds=%u, flags=%x", + seconds, nseconds, flags); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + virCheckReadOnlyGoto(dom->conn->flags, error); + + if (dom->conn->driver->domainSetTime) { + int ret = dom->conn->driver->domainSetTime(dom, seconds, + nseconds, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index f8113f4..cce6bdf 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -654,6 +654,8 @@ LIBVIRT_1.2.5 { global: virDomainFSFreeze; virDomainFSThaw; + virDomainGetTime; + virDomainSetTime; } LIBVIRT_1.2.3; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 4c3b38a..85fe597 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7435,6 +7435,39 @@ remoteDomainCreateWithFiles(virDomainPtr dom, return rv; } +static int +remoteDomainGetTime(virDomainPtr dom, + long long *seconds, + unsigned int *nseconds, + unsigned int flags) +{ + int rv = -1; + struct private_data *priv = dom->conn->privateData; + remote_domain_get_time_args args; + remote_domain_get_time_ret ret; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, dom); + args.flags = flags; + + *seconds = *nseconds = 0; + + if (call(dom->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_TIME, + (xdrproc_t) xdr_remote_domain_get_time_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_get_time_ret, (char *) &ret) == -1) + goto cleanup; + + *seconds = ret.seconds; + *nseconds = ret.nseconds; + xdr_free((xdrproc_t) &xdr_remote_domain_get_time_ret, (char *) &ret); + rv = 0; + + cleanup: + remoteDriverUnlock(priv); + return rv; +} + /* get_nonnull_domain and get_nonnull_network turn an on-wire * (name, uuid) pair into virDomainPtr or virNetworkPtr object. @@ -7770,6 +7803,8 @@ static virDriver remote_driver = { .connectGetCPUModelNames = remoteConnectGetCPUModelNames, /* 1.1.3 */ .domainFSFreeze = remoteDomainFSFreeze, /* 1.2.5 */ .domainFSThaw = remoteDomainFSThaw, /* 1.2.5 */ + .domainGetTime = remoteDomainGetTime, /* 1.2.5 */ + .domainSetTime = remoteDomainSetTime, /* 1.2.5 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 608490c..1f9d583 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2855,6 +2855,23 @@ struct remote_domain_fstrim_args { unsigned int flags; }; +struct remote_domain_get_time_args { + remote_nonnull_domain dom; + unsigned int flags; +}; + +struct remote_domain_get_time_ret { + hyper seconds; + unsigned int nseconds; +}; + +struct remote_domain_set_time_args { + remote_nonnull_domain dom; + hyper seconds; + unsigned int nseconds; + unsigned int flags; +}; + struct remote_domain_migrate_begin3_params_args { remote_nonnull_domain dom; remote_typed_param params<REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX>; @@ -5309,5 +5326,17 @@ enum remote_procedure { * @generate: both * @acl: domain:fs_freeze */ - REMOTE_PROC_DOMAIN_FSTHAW = 336 + REMOTE_PROC_DOMAIN_FSTHAW = 336, + + /** + * @generate: none + * @acl: domain:read + */ + REMOTE_PROC_DOMAIN_GET_TIME = 337, + + /** + * @generate: both + * @acl: domain:set_time + */ + REMOTE_PROC_DOMAIN_SET_TIME = 338 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index b62871f..5b22049 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2290,6 +2290,20 @@ struct remote_domain_fstrim_args { uint64_t minimum; u_int flags; }; +struct remote_domain_get_time_args { + remote_nonnull_domain dom; + u_int flags; +}; +struct remote_domain_get_time_ret { + int64_t seconds; + u_int nseconds; +}; +struct remote_domain_set_time_args { + remote_nonnull_domain dom; + int64_t seconds; + u_int nseconds; + u_int flags; +}; struct remote_domain_migrate_begin3_params_args { remote_nonnull_domain dom; struct { @@ -2786,4 +2800,6 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_CORE_DUMP_WITH_FORMAT = 334, REMOTE_PROC_DOMAIN_FSFREEZE = 335, REMOTE_PROC_DOMAIN_FSTHAW = 336, + REMOTE_PROC_DOMAIN_GET_TIME = 337, + REMOTE_PROC_DOMAIN_SET_TIME = 338, }; -- 1.9.0

On 05/07/2014 10:19 AM, Michal Privoznik wrote:
These APIs allow users to get or set time in a domain, which may come handy if the domain has been resumed just recently and NTP is not configured or hasn't kicked in yet and the guest is running something time critical. In addition, NTP may refuse to re-set the clock if the skew is too big.
In addition, new ACL attribute is introduced 'set_time'.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- daemon/remote.c | 37 +++++++++++++++++ include/libvirt/libvirt.h.in | 14 +++++++ src/access/viraccessperm.c | 2 +- src/access/viraccessperm.h | 7 +++- src/driver.h | 14 +++++++ src/libvirt.c | 94 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + src/remote/remote_driver.c | 35 +++++++++++++++++ src/remote/remote_protocol.x | 31 ++++++++++++++- src/remote_protocol-structs | 16 ++++++++ 10 files changed, 249 insertions(+), 3 deletions(-)
ACK Jan

These APIs are exposed under new virsh command 'domtime' which both gets and sets (not at the same time of course :)). Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tools/virsh-domain-monitor.c | 132 +++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 16 ++++++ 2 files changed, 148 insertions(+) diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c index 18d551a..31fa8de 100644 --- a/tools/virsh-domain-monitor.c +++ b/tools/virsh-domain-monitor.c @@ -1356,6 +1356,132 @@ cmdDomstate(vshControl *ctl, const vshCmd *cmd) } /* + * "domtime" command + */ +static const vshCmdInfo info_domtime[] = { + {.name = "help", + .data = N_("domain time") + }, + {.name = "desc", + .data = N_("Gets or sets a domain time") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domtime[] = { + {.name = "domain", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("domain name, id or uuid") + }, + {.name = "now", + .type = VSH_OT_BOOL, + .help = N_("set current host time") + }, + {.name = "pretty", + .type = VSH_OT_BOOL, + .help = N_("print domain's time in human readable form") + }, + {.name = "sync", + .type = VSH_OT_BOOL, + .help = N_("instead of setting given time, synchronize from domain's RTC"), + }, + {.name = "time", + .type = VSH_OT_INT, + .help = N_("time to set") + }, + {.name = NULL} +}; + +static bool +cmdDomTime(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + bool ret = false; + bool now = vshCommandOptBool(cmd, "now"); + bool pretty = vshCommandOptBool(cmd, "pretty"); + bool sync = vshCommandOptBool(cmd, "sync"); + long long seconds = 0; + unsigned int nseconds = 0; + unsigned int flags = 0; + bool doSet = false; + int rv; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + rv = vshCommandOptLongLong(cmd, "time", &seconds); + + if (rv < 0) { + /* invalid integer format */ + vshError(ctl, "%s", + _("Unable to parse integer parameter to --time.")); + goto cleanup; + } else if (rv > 0) { + /* --time is used, so set time instead of get time. + * However, --time and --now are mutually exclusive. */ + if (now) { + vshError(ctl, _("--time and --now are mutually exclusive")); + goto cleanup; + } + + /* Neither is --time and --sync */ + if (sync) { + vshError(ctl, _("--time and --sync are mutually exclusive")); + goto cleanup; + + } + doSet = true; + } + + if (sync && now) { + vshError(ctl, _("--sync and --now are mutually exclusive")); + goto cleanup; + } + + /* --now or --sync means setting */ + doSet |= now | sync; + + if (doSet) { + if (now && ((seconds = time(NULL)) == (time_t) -1)) { + vshError(ctl, _("Unable to get current time")); + goto cleanup; + } + + if (sync) + flags |= VIR_DOMAIN_TIME_SYNC; + + if (virDomainSetTime(dom, seconds, nseconds, flags) < 0) + goto cleanup; + + } else { + if (virDomainGetTime(dom, &seconds, &nseconds, flags) < 0) + goto cleanup; + + if (pretty) { + char timestr[100]; + time_t cur_time = seconds; + struct tm time_info; + + if (!gmtime_r(&cur_time, &time_info)) { + vshError(ctl, _("Unable to format time")); + goto cleanup; + } + strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", &time_info); + + vshPrint(ctl, _("Time: %s"), timestr); + } else { + vshPrint(ctl, _("Time: %lld"), seconds); + } + } + + ret = true; + cleanup: + virDomainFree(dom); + return ret; +} + +/* * "list" command */ static const vshCmdInfo info_list[] = { @@ -1911,6 +2037,12 @@ const vshCmdDef domMonitoringCmds[] = { .info = info_domstate, .flags = 0 }, + {.name = "domtime", + .handler = cmdDomTime, + .opts = opts_domtime, + .info = info_domtime, + .flags = 0 + }, {.name = "list", .handler = cmdList, .opts = opts_list, diff --git a/tools/virsh.pod b/tools/virsh.pod index 9104804..623f94d 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -994,6 +994,22 @@ Returns state of an interface to VMM used to control a domain. For states other than "ok" or "error" the command also prints number of seconds elapsed since the control interface entered its current state. +=item B<domtime> I<domain> { [I<--now>] [I<--pretty>] [I<--sync>] +[I<--time> B<time>] } + +Gets or sets the domain's system time. When run without any arguments +(but I<domain>), the current domain's system time is printed out. The +I<--pretty> modifier can be used to print the time in more human +readable form. When I<--time> B<time> is specified, the domain's time is +not get but set instead. The I<--now> modifier acts like if it was an +alias for I<--time> B<$now>, which means it sets the time that is +currently on the host virsh is running at. In both cases (setting and +getting), time is in seconds relative to Epoch of 1970-01-01 in UTC. +The I<--sync> modifies the set behavior a bit: The time passed is +ignored, but the time to set is read from domain's RTC instead. Please +note, that some hypervisors may require a guest agent to be configured +in order to get or set the guest time. + =item B<domxml-from-native> I<format> I<config> Convert the file I<config> in the native guest configuration format -- 1.9.0

On 05/07/2014 10:19 AM, Michal Privoznik wrote:
These APIs are exposed under new virsh command 'domtime' which both gets and sets (not at the same time of course :)).
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tools/virsh-domain-monitor.c | 132 +++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 16 ++++++ 2 files changed, 148 insertions(+)
diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c index 18d551a..31fa8de 100644 --- a/tools/virsh-domain-monitor.c +++ b/tools/virsh-domain-monitor.c @@ -1356,6 +1356,132 @@ cmdDomstate(vshControl *ctl, const vshCmd *cmd) }
/* + * "domtime" command + */ +static const vshCmdInfo info_domtime[] = { + {.name = "help", + .data = N_("domain time") + }, + {.name = "desc", + .data = N_("Gets or sets a domain time")
s/a domain time/the domain's system time/ would be more accurate IMO or even 'the domain time'
+ }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domtime[] = { + {.name = "domain", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("domain name, id or uuid") + }, + {.name = "now", + .type = VSH_OT_BOOL, + .help = N_("set current host time")
We are setting the guest's time, not the host's time. How about 'set to the time of the host running virsh'? :)
+ }, + {.name = "pretty", + .type = VSH_OT_BOOL, + .help = N_("print domain's time in human readable form") + }, + {.name = "sync", + .type = VSH_OT_BOOL, + .help = N_("instead of setting given time, synchronize from domain's RTC"), + }, + {.name = "time", + .type = VSH_OT_INT, + .help = N_("time to set") + }, + {.name = NULL} +}; + +static bool +cmdDomTime(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + bool ret = false; + bool now = vshCommandOptBool(cmd, "now"); + bool pretty = vshCommandOptBool(cmd, "pretty"); + bool sync = vshCommandOptBool(cmd, "sync"); + long long seconds = 0; + unsigned int nseconds = 0; + unsigned int flags = 0; + bool doSet = false; + int rv; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + rv = vshCommandOptLongLong(cmd, "time", &seconds); + + if (rv < 0) { + /* invalid integer format */ + vshError(ctl, "%s", + _("Unable to parse integer parameter to --time.")); + goto cleanup; + } else if (rv > 0) {
+ /* --time is used, so set time instead of get time. + * However, --time and --now are mutually exclusive. */ + if (now) { + vshError(ctl, _("--time and --now are mutually exclusive")); + goto cleanup; + } + + /* Neither is --time and --sync */ + if (sync) { + vshError(ctl, _("--time and --sync are mutually exclusive")); + goto cleanup; + + }
You can use the VSH_EXCLUSIVE_OPTIONS macros for these checks.
+ doSet = true; + } + + if (sync && now) { + vshError(ctl, _("--sync and --now are mutually exclusive")); + goto cleanup; + } +
+ /* --now or --sync means setting */ + doSet |= now | sync;
how about using doSet || now || sync in the if condition?
+ + if (doSet) { + if (now && ((seconds = time(NULL)) == (time_t) -1)) { + vshError(ctl, _("Unable to get current time")); + goto cleanup; + } + + if (sync) + flags |= VIR_DOMAIN_TIME_SYNC; + + if (virDomainSetTime(dom, seconds, nseconds, flags) < 0) + goto cleanup; + + } else { + if (virDomainGetTime(dom, &seconds, &nseconds, flags) < 0) + goto cleanup; + + if (pretty) { + char timestr[100]; + time_t cur_time = seconds; + struct tm time_info; + + if (!gmtime_r(&cur_time, &time_info)) { + vshError(ctl, _("Unable to format time")); + goto cleanup; + } + strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", &time_info); + + vshPrint(ctl, _("Time: %s"), timestr); + } else { + vshPrint(ctl, _("Time: %lld"), seconds); + } + } + + ret = true; + cleanup: + virDomainFree(dom); + return ret; +} + +/* * "list" command */ static const vshCmdInfo info_list[] = { @@ -1911,6 +2037,12 @@ const vshCmdDef domMonitoringCmds[] = { .info = info_domstate, .flags = 0 }, + {.name = "domtime", + .handler = cmdDomTime, + .opts = opts_domtime, + .info = info_domtime, + .flags = 0 + }, {.name = "list", .handler = cmdList, .opts = opts_list, diff --git a/tools/virsh.pod b/tools/virsh.pod index 9104804..623f94d 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -994,6 +994,22 @@ Returns state of an interface to VMM used to control a domain. For states other than "ok" or "error" the command also prints number of seconds elapsed since the control interface entered its current state.
+=item B<domtime> I<domain> { [I<--now>] [I<--pretty>] [I<--sync>] +[I<--time> B<time>] } + +Gets or sets the domain's system time. When run without any arguments +(but I<domain>), the current domain's system time is printed out. The
s/current domain's/domain's current/
+I<--pretty> modifier can be used to print the time in more human +readable form.
A new paragraph would be nice here to separate the get/set options.
When I<--time> B<time> is specified, the domain's time is +not get but set instead. The I<--now> modifier acts like if it was an +alias for I<--time> B<$now>, which means it sets the time that is +currently on the host virsh is running at. In both cases (setting and +getting), time is in seconds relative to Epoch of 1970-01-01 in UTC. +The I<--sync> modifies the set behavior a bit: The time passed is +ignored, but the time to set is read from domain's RTC instead. Please +note, that some hypervisors may require a guest agent to be configured +in order to get or set the guest time. + =item B<domxml-from-native> I<format> I<config>
Convert the file I<config> in the native guest configuration format
Jan

One caveat though, qemu-ga is expecting time and returning time in nanoseconds. With all the buffering and propagation delay, the time is already wrong once it gets to the qemu-ga, but there's nothing we can do about it. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_agent.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 8 ++++ src/qemu/qemu_driver.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 4082331..80a820e 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1646,3 +1646,102 @@ qemuAgentUpdateCPUInfo(unsigned int nvcpus, return 0; } + + +int +qemuAgentGetTime(qemuAgentPtr mon, + long long *seconds, + unsigned int *nseconds) +{ + int ret = -1; + unsigned long long json_time; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = qemuAgentMakeCommand("guest-get-time", + NULL); + if (!cmd) + return ret; + + if (qemuAgentCommand(mon, cmd, &reply, true, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + goto cleanup; + + if (!reply || qemuAgentCheckError(cmd, reply) < 0) + goto cleanup; + + if (virJSONValueObjectGetNumberUlong(reply, "return", &json_time) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed return value")); + goto cleanup; + } + + /* guest agent returns time in nanoseconds, + * we need it in seconds here */ + *seconds = json_time / 1000000000LL; + *nseconds = json_time % 1000000000LL; + ret = 0; + + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +/** + * qemuAgentSetTime: + * @setTime: time to set + * @sync: let guest agent to read domain's RTC (@setTime is ignored) + */ +int +qemuAgentSetTime(qemuAgentPtr mon, + long long seconds, + unsigned int nseconds, + bool sync) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + if (sync) { + cmd = qemuAgentMakeCommand("guest-set-time", NULL); + } else { + /* guest agent expect time with nanosecond granularity. + * Impressing. */ + long long json_time; + + /* Check if we overflow. For some reason qemu doesn't handle unsigned + * long long on the monitor well as it silently truncates numbers to + * signed long long. Therefore we must check overflow against LLONG_MAX + * not ULLONG_MAX. */ + if (seconds > LLONG_MAX / 1000000000LL) { + virReportError(VIR_ERR_INVALID_ARG, + _("Time '%lld' is too big for guest agent"), + seconds); + return ret; + } + + json_time = seconds * 1000000000LL; + json_time += nseconds; + cmd = qemuAgentMakeCommand("guest-set-time", + "I:time", json_time, + NULL); + } + + if (!cmd) + return ret; + + if (qemuAgentCommand(mon, cmd, &reply, true, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + goto cleanup; + + if (!reply || qemuAgentCheckError(cmd, reply) < 0) + goto cleanup; + + ret = 0; + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h index 5fbacdb..399b586 100644 --- a/src/qemu/qemu_agent.h +++ b/src/qemu/qemu_agent.h @@ -97,4 +97,12 @@ int qemuAgentSetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr cpus, size_t ncpus); int qemuAgentUpdateCPUInfo(unsigned int nvcpus, qemuAgentCPUInfoPtr cpuinfo, int ncpuinfo); + +int qemuAgentGetTime(qemuAgentPtr mon, + long long *seconds, + unsigned int *nseconds); +int qemuAgentSetTime(qemuAgentPtr mon, + long long seconds, + unsigned int nseconds, + bool sync); #endif /* __QEMU_AGENT_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f0e82e9..52afeaa 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -16526,6 +16526,113 @@ qemuConnectGetCPUModelNames(virConnectPtr conn, return cpuGetModels(arch, models); } +static int +qemuDomainGetTime(virDomainPtr dom, + long long *seconds, + unsigned int *nseconds, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + qemuDomainObjPrivatePtr priv; + int ret = -1; + int rv; + + virCheckFlags(0, ret); + + if (!(vm = qemuDomObjFromDomain(dom))) + return ret; + + if (virDomainGetTimeEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + priv = vm->privateData; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + if (!qemuDomainAgentAvailable(priv, true)) + goto endjob; + + qemuDomainObjEnterAgent(vm); + rv = qemuAgentGetTime(priv->agent, seconds, nseconds); + qemuDomainObjExitAgent(vm); + + if (rv < 0) + goto endjob; + + ret = 0; + + endjob: + if (!qemuDomainObjEndJob(driver, vm)) + vm = NULL; + + cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static int +qemuDomainSetTime(virDomainPtr dom, + long long seconds, + unsigned int nseconds, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + qemuDomainObjPrivatePtr priv; + virDomainObjPtr vm; + bool sync = flags & VIR_DOMAIN_TIME_SYNC; + int ret = -1; + int rv; + + virCheckFlags(VIR_DOMAIN_TIME_SYNC, ret); + + if (!(vm = qemuDomObjFromDomain(dom))) + return ret; + + if (virDomainSetTimeEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + priv = vm->privateData; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + if (!qemuDomainAgentAvailable(priv, true)) + goto endjob; + + qemuDomainObjEnterAgent(vm); + rv = qemuAgentSetTime(priv->agent, seconds, nseconds, sync); + qemuDomainObjExitAgent(vm); + + if (rv < 0) + goto endjob; + + ret = 0; + + endjob: + if (!qemuDomainObjEndJob(driver, vm)) + vm = NULL; + + cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + static virDriver qemuDriver = { .no = VIR_DRV_QEMU, @@ -16717,6 +16824,8 @@ static virDriver qemuDriver = { .domainMigrateFinish3Params = qemuDomainMigrateFinish3Params, /* 1.1.0 */ .domainMigrateConfirm3Params = qemuDomainMigrateConfirm3Params, /* 1.1.0 */ .connectGetCPUModelNames = qemuConnectGetCPUModelNames, /* 1.1.3 */ + .domainGetTime = qemuDomainGetTime, /* 1.2.4 */ + .domainSetTime = qemuDomainSetTime, /* 1.2.4 */ }; -- 1.9.0

On 05/07/2014 10:19 AM, Michal Privoznik wrote:
One caveat though, qemu-ga is expecting time and returning time in nanoseconds. With all the buffering and propagation delay, the time is already wrong once it gets to the qemu-ga, but there's nothing we can do about it.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_agent.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 8 ++++ src/qemu/qemu_driver.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+)
Jan
diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 4082331..80a820e 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1646,3 +1646,102 @@ qemuAgentUpdateCPUInfo(unsigned int nvcpus,
return 0; } + + +int +qemuAgentGetTime(qemuAgentPtr mon, + long long *seconds, + unsigned int *nseconds) +{ + int ret = -1; + unsigned long long json_time; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = qemuAgentMakeCommand("guest-get-time", + NULL); + if (!cmd) + return ret; + + if (qemuAgentCommand(mon, cmd, &reply, true, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + goto cleanup; +
+ if (!reply || qemuAgentCheckError(cmd, reply) < 0) + goto cleanup; +
Here too.
+ if (virJSONValueObjectGetNumberUlong(reply, "return", &json_time) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed return value")); + goto cleanup; + } +
...
+ + +/** + * qemuAgentSetTime: + * @setTime: time to set + * @sync: let guest agent to read domain's RTC (@setTime is ignored) + */ +int +qemuAgentSetTime(qemuAgentPtr mon, + long long seconds, + unsigned int nseconds, + bool sync) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; +
...
+ + if (!cmd) + return ret; + + if (qemuAgentCommand(mon, cmd, &reply, true, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + goto cleanup; +
+ if (!reply || qemuAgentCheckError(cmd, reply) < 0) + goto cleanup;
This should be checked by qemuAgentCommand already.
+ + ret = 0; + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +}
@@ -16717,6 +16824,8 @@ static virDriver qemuDriver = { .domainMigrateFinish3Params = qemuDomainMigrateFinish3Params, /* 1.1.0 */ .domainMigrateConfirm3Params = qemuDomainMigrateConfirm3Params, /* 1.1.0 */ .connectGetCPUModelNames = qemuConnectGetCPUModelNames, /* 1.1.3 */ + .domainGetTime = qemuDomainGetTime, /* 1.2.4 */ + .domainSetTime = qemuDomainSetTime, /* 1.2.4 */
1.2.5
};

These APIs are exposed under new virsh command 'domtime' which both gets and sets (not at the same time of course :)). Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- diff to v4: - changed wording in some help messages - use VSH_EXCLUSIVE_OPTIONS instead explicit separate code tools/virsh-domain-monitor.c | 116 +++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 18 +++++++ 2 files changed, 134 insertions(+) diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c index 18d551a..a3b66ed 100644 --- a/tools/virsh-domain-monitor.c +++ b/tools/virsh-domain-monitor.c @@ -1356,6 +1356,116 @@ cmdDomstate(vshControl *ctl, const vshCmd *cmd) } /* + * "domtime" command + */ +static const vshCmdInfo info_domtime[] = { + {.name = "help", + .data = N_("domain time") + }, + {.name = "desc", + .data = N_("Gets or sets the domain's system time") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domtime[] = { + {.name = "domain", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("domain name, id or uuid") + }, + {.name = "now", + .type = VSH_OT_BOOL, + .help = N_("set to the time of the host running virsh") + }, + {.name = "pretty", + .type = VSH_OT_BOOL, + .help = N_("print domain's time in human readable form") + }, + {.name = "sync", + .type = VSH_OT_BOOL, + .help = N_("instead of setting given time, synchronize from domain's RTC"), + }, + {.name = "time", + .type = VSH_OT_INT, + .help = N_("time to set") + }, + {.name = NULL} +}; + +static bool +cmdDomTime(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + bool ret = false; + bool now = vshCommandOptBool(cmd, "now"); + bool pretty = vshCommandOptBool(cmd, "pretty"); + bool sync = vshCommandOptBool(cmd, "sync"); + long long seconds = 0; + unsigned int nseconds = 0; + unsigned int flags = 0; + bool doSet = false; + int rv; + + VSH_EXCLUSIVE_OPTIONS("time", "now"); + VSH_EXCLUSIVE_OPTIONS("time", "sync"); + VSH_EXCLUSIVE_OPTIONS("now", "sync"); + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + rv = vshCommandOptLongLong(cmd, "time", &seconds); + + if (rv < 0) { + /* invalid integer format */ + vshError(ctl, "%s", + _("Unable to parse integer parameter to --time.")); + goto cleanup; + } else if (rv > 0) { + /* valid integer to set */ + doSet = true; + } + + if (doSet || now || sync) { + if (now && ((seconds = time(NULL)) == (time_t) -1)) { + vshError(ctl, _("Unable to get current time")); + goto cleanup; + } + + if (sync) + flags |= VIR_DOMAIN_TIME_SYNC; + + if (virDomainSetTime(dom, seconds, nseconds, flags) < 0) + goto cleanup; + + } else { + if (virDomainGetTime(dom, &seconds, &nseconds, flags) < 0) + goto cleanup; + + if (pretty) { + char timestr[100]; + time_t cur_time = seconds; + struct tm time_info; + + if (!gmtime_r(&cur_time, &time_info)) { + vshError(ctl, _("Unable to format time")); + goto cleanup; + } + strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", &time_info); + + vshPrint(ctl, _("Time: %s"), timestr); + } else { + vshPrint(ctl, _("Time: %lld"), seconds); + } + } + + ret = true; + cleanup: + virDomainFree(dom); + return ret; +} + +/* * "list" command */ static const vshCmdInfo info_list[] = { @@ -1911,6 +2021,12 @@ const vshCmdDef domMonitoringCmds[] = { .info = info_domstate, .flags = 0 }, + {.name = "domtime", + .handler = cmdDomTime, + .opts = opts_domtime, + .info = info_domtime, + .flags = 0 + }, {.name = "list", .handler = cmdList, .opts = opts_list, diff --git a/tools/virsh.pod b/tools/virsh.pod index 22ca196..120715a 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1017,6 +1017,24 @@ Returns state of an interface to VMM used to control a domain. For states other than "ok" or "error" the command also prints number of seconds elapsed since the control interface entered its current state. +=item B<domtime> I<domain> { [I<--now>] [I<--pretty>] [I<--sync>] +[I<--time> B<time>] } + +Gets or sets the domain's system time. When run without any arguments +(but I<domain>), the current domain's system time is printed out. The +I<--pretty> modifier can be used to print the time in more human +readable form. + +When I<--time> B<time> is specified, the domain's time is +not get but set instead. The I<--now> modifier acts like if it was an +alias for I<--time> B<$now>, which means it sets the time that is +currently on the host virsh is running at. In both cases (setting and +getting), time is in seconds relative to Epoch of 1970-01-01 in UTC. +The I<--sync> modifies the set behavior a bit: The time passed is +ignored, but the time to set is read from domain's RTC instead. Please +note, that some hypervisors may require a guest agent to be configured +in order to get or set the guest time. + =item B<domxml-from-native> I<format> I<config> Convert the file I<config> in the native guest configuration format -- 1.9.3

One caveat though, qemu-ga is expecting time and returning time in nanoseconds. With all the buffering and propagation delay, the time is already wrong once it gets to the qemu-ga, but there's nothing we can do about it. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- diff to v4: - s/1.2.4/1.2.5/ - drop explicit agent error check since it's done in qemuAgentCommand now src/qemu/qemu_agent.c | 93 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 8 ++++ src/qemu/qemu_driver.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 57c7cc5..10e2b8d 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1685,3 +1685,96 @@ qemuAgentUpdateCPUInfo(unsigned int nvcpus, return 0; } + + +int +qemuAgentGetTime(qemuAgentPtr mon, + long long *seconds, + unsigned int *nseconds) +{ + int ret = -1; + unsigned long long json_time; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = qemuAgentMakeCommand("guest-get-time", + NULL); + if (!cmd) + return ret; + + if (qemuAgentCommand(mon, cmd, &reply, true, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + goto cleanup; + + if (virJSONValueObjectGetNumberUlong(reply, "return", &json_time) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed return value")); + goto cleanup; + } + + /* guest agent returns time in nanoseconds, + * we need it in seconds here */ + *seconds = json_time / 1000000000LL; + *nseconds = json_time % 1000000000LL; + ret = 0; + + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +/** + * qemuAgentSetTime: + * @setTime: time to set + * @sync: let guest agent to read domain's RTC (@setTime is ignored) + */ +int +qemuAgentSetTime(qemuAgentPtr mon, + long long seconds, + unsigned int nseconds, + bool sync) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + if (sync) { + cmd = qemuAgentMakeCommand("guest-set-time", NULL); + } else { + /* guest agent expect time with nanosecond granularity. + * Impressing. */ + long long json_time; + + /* Check if we overflow. For some reason qemu doesn't handle unsigned + * long long on the monitor well as it silently truncates numbers to + * signed long long. Therefore we must check overflow against LLONG_MAX + * not ULLONG_MAX. */ + if (seconds > LLONG_MAX / 1000000000LL) { + virReportError(VIR_ERR_INVALID_ARG, + _("Time '%lld' is too big for guest agent"), + seconds); + return ret; + } + + json_time = seconds * 1000000000LL; + json_time += nseconds; + cmd = qemuAgentMakeCommand("guest-set-time", + "I:time", json_time, + NULL); + } + + if (!cmd) + return ret; + + if (qemuAgentCommand(mon, cmd, &reply, true, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + goto cleanup; + + ret = 0; + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h index 58531d5..6cd6b49 100644 --- a/src/qemu/qemu_agent.h +++ b/src/qemu/qemu_agent.h @@ -98,4 +98,12 @@ int qemuAgentSetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr cpus, size_t ncpus); int qemuAgentUpdateCPUInfo(unsigned int nvcpus, qemuAgentCPUInfoPtr cpuinfo, int ncpuinfo); + +int qemuAgentGetTime(qemuAgentPtr mon, + long long *seconds, + unsigned int *nseconds); +int qemuAgentSetTime(qemuAgentPtr mon, + long long seconds, + unsigned int nseconds, + bool sync); #endif /* __QEMU_AGENT_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 52ca47c..cab653b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -16536,6 +16536,113 @@ qemuConnectGetCPUModelNames(virConnectPtr conn, return cpuGetModels(arch, models); } +static int +qemuDomainGetTime(virDomainPtr dom, + long long *seconds, + unsigned int *nseconds, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + qemuDomainObjPrivatePtr priv; + int ret = -1; + int rv; + + virCheckFlags(0, ret); + + if (!(vm = qemuDomObjFromDomain(dom))) + return ret; + + if (virDomainGetTimeEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + priv = vm->privateData; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + if (!qemuDomainAgentAvailable(priv, true)) + goto endjob; + + qemuDomainObjEnterAgent(vm); + rv = qemuAgentGetTime(priv->agent, seconds, nseconds); + qemuDomainObjExitAgent(vm); + + if (rv < 0) + goto endjob; + + ret = 0; + + endjob: + if (!qemuDomainObjEndJob(driver, vm)) + vm = NULL; + + cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static int +qemuDomainSetTime(virDomainPtr dom, + long long seconds, + unsigned int nseconds, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + qemuDomainObjPrivatePtr priv; + virDomainObjPtr vm; + bool sync = flags & VIR_DOMAIN_TIME_SYNC; + int ret = -1; + int rv; + + virCheckFlags(VIR_DOMAIN_TIME_SYNC, ret); + + if (!(vm = qemuDomObjFromDomain(dom))) + return ret; + + if (virDomainSetTimeEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + priv = vm->privateData; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + if (!qemuDomainAgentAvailable(priv, true)) + goto endjob; + + qemuDomainObjEnterAgent(vm); + rv = qemuAgentSetTime(priv->agent, seconds, nseconds, sync); + qemuDomainObjExitAgent(vm); + + if (rv < 0) + goto endjob; + + ret = 0; + + endjob: + if (!qemuDomainObjEndJob(driver, vm)) + vm = NULL; + + cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + static int qemuDomainFSFreeze(virDomainPtr dom, @@ -16819,6 +16926,8 @@ static virDriver qemuDriver = { .connectGetCPUModelNames = qemuConnectGetCPUModelNames, /* 1.1.3 */ .domainFSFreeze = qemuDomainFSFreeze, /* 1.2.5 */ .domainFSThaw = qemuDomainFSThaw, /* 1.2.5 */ + .domainGetTime = qemuDomainGetTime, /* 1.2.5 */ + .domainSetTime = qemuDomainSetTime, /* 1.2.5 */ }; -- 1.9.3

On 05/15/2014 04:13 PM, Michal Privoznik wrote:
One caveat though, qemu-ga is expecting time and returning time in nanoseconds. With all the buffering and propagation delay, the time is already wrong once it gets to the qemu-ga, but there's nothing we can do about it.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> ---
diff to v4: - s/1.2.4/1.2.5/ - drop explicit agent error check since it's done in qemuAgentCommand now
src/qemu/qemu_agent.c | 93 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 8 ++++ src/qemu/qemu_driver.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+)
ACK Jan

On 05/15/2014 04:13 PM, Michal Privoznik wrote:
These APIs are exposed under new virsh command 'domtime' which both gets and sets (not at the same time of course :)).
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> ---
diff to v4: - changed wording in some help messages - use VSH_EXCLUSIVE_OPTIONS instead explicit separate code
tools/virsh-domain-monitor.c | 116 +++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 18 +++++++ 2 files changed, 134 insertions(+)
+ +Gets or sets the domain's system time. When run without any arguments +(but I<domain>), the current domain's system time is printed out. The +I<--pretty> modifier can be used to print the time in more human +readable form. + +When I<--time> B<time> is specified, the domain's time is +not get but set instead. The I<--now> modifier acts like if it was an
s/get/gotten/ I think
+alias for I<--time> B<$now>, which means it sets the time that is +currently on the host virsh is running at. In both cases (setting and +getting), time is in seconds relative to Epoch of 1970-01-01 in UTC. +The I<--sync> modifies the set behavior a bit: The time passed is +ignored, but the time to set is read from domain's RTC instead. Please +note, that some hypervisors may require a guest agent to be configured +in order to get or set the guest time. + =item B<domxml-from-native> I<format> I<config>
Convert the file I<config> in the native guest configuration format
ACK Jan
participants (2)
-
Ján Tomko
-
Michal Privoznik