[libvirt] [PATCH v4 0/7] support guest agent general command

Hi. All. I rewrote the patched for git head tree, adding some code. And @timeout must be following VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK(-2) : does not block and wait forever. VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT(-1) : use default time for waiting VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWIAT(0) : does not wait and out soon positive value : wait for pointed seconds. And I also add VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN for not writing static value. In addition, I added the some options for virsh command qemu-agent-command --block : it means taht @timeout is VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK. --async : it means taht @timeout is VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWIAT. --timeout @seconds : @seconds must be positive value and it means that @timeout is positive value case. These upper options are exclusive. If no options are given, it means taht @timeout is IR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT. virsh # help qemu-agent-command NAME qemu-agent-command - QEMU Guest Agent Command SYNOPSIS qemu-agent-command <domain> [--timeout <number>] [--async] [--block] {[--cmd] <string>}... DESCRIPTION Run an arbitrary qemu guest agent command; use at your own risk OPTIONS [--domain] <string> domain name, id or uuid --timeout <number> timeout seconds. must be positive. --async execute command without waiting for timeout --block execute command without timeout [--cmd] <string> command virsh # qemu-agent-command RHEL58_64 '{"execute":"guest-info"}' {"return":{"version":"1.1.50","supported_commands":[{"enabled":true,"name":"guest-network-get-interfaces"},{"enabled":true,"name":"guest-suspend-hybrid"},{"enabled":true,"name":"guest-suspend-ram"},{"enabled":true,"name":"guest-suspend-disk"},{"enabled":true,"name":"guest-fsfreeze-thaw"},{"enabled":true,"name":"guest-fsfreeze-freeze"},{"enabled":true,"name":"guest-fsfreeze-status"},{"enabled":true,"name":"guest-file-flush"},{"enabled":true,"name":"guest-file-seek"},{"enabled":true,"name":"guest-file-write"},{"enabled":true,"name":"guest-file-read"},{"enabled":true,"name":"guest-file-close"},{"enabled":true,"name":"guest-file-open"},{"enabled":true,"name":"guest-shutdown"},{"enabled":true,"name":"guest-info"},{"enabled":true,"name":"guest-ping"},{"enabled":true,"name":"guest-sync"},{"enabled":true,"name":"guest-sync-delimited"}]}} virsh # qemu-agent-command --block RHEL58_64 '{"execute":"guest-sync","arguments":{"id":123}}' {"return":123}

Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- include/libvirt/libvirt-qemu.h | 7 +++++++ src/qemu/qemu_agent.c | 32 ++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index a37f897..93386ca 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -44,6 +44,13 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int pid_value, unsigned int flags); +typedef enum { + VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN = -2, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK = -2, + VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT = -1, + VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT = 0, +} virDomainQemuAgentCommandTimeoutValues; + # ifdef __cplusplus } # endif diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 513f1d5..61f070c 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -837,6 +837,8 @@ void qemuAgentClose(qemuAgentPtr mon) * @mon: Monitor * @msg: Message * @timeout: use timeout? + * @seconds: timeout seconds. if VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT and + * @timeout is true, use default value. * * Send @msg to agent @mon. * Wait max QEMU_AGENT_WAIT_TIME for agent @@ -848,7 +850,8 @@ void qemuAgentClose(qemuAgentPtr mon) */ static int qemuAgentSend(qemuAgentPtr mon, qemuAgentMessagePtr msg, - bool timeout) + bool timeout, + int seconds) { int ret = -1; unsigned long long now, then = 0; @@ -864,7 +867,10 @@ static int qemuAgentSend(qemuAgentPtr mon, if (timeout) { if (virTimeMillisNow(&now) < 0) return -1; - then = now + QEMU_AGENT_WAIT_TIME; + if (!(seconds >= 0 || seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT)) + return -1; + then = now + (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT ? + QEMU_AGENT_WAIT_TIME : seconds * 1000ull); } mon->msg = msg; @@ -937,7 +943,8 @@ qemuAgentGuestSync(qemuAgentPtr mon) VIR_DEBUG("Sending guest-sync command with ID: %llu", id); - send_ret = qemuAgentSend(mon, &sync_msg, true); + send_ret = qemuAgentSend(mon, &sync_msg, true, + VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT); VIR_DEBUG("qemuAgentSend returned: %d", send_ret); @@ -977,7 +984,8 @@ cleanup: static int qemuAgentCommand(qemuAgentPtr mon, virJSONValuePtr cmd, - virJSONValuePtr *reply) + virJSONValuePtr *reply, + int seconds) { int ret = -1; qemuAgentMessage msg; @@ -1003,9 +1011,9 @@ qemuAgentCommand(qemuAgentPtr mon, } msg.txLength = strlen(msg.txBuffer); - VIR_DEBUG("Send command '%s' for write", cmdstr); + VIR_DEBUG("Send command '%s' for write, seconds = %d", cmdstr, seconds); - ret = qemuAgentSend(mon, &msg, false); + ret = qemuAgentSend(mon, &msg, seconds < -1 ? false : true, seconds); VIR_DEBUG("Receive command reply ret=%d rxObject=%p", ret, msg.rxObject); @@ -1283,7 +1291,8 @@ int qemuAgentShutdown(qemuAgentPtr mon, return -1; mon->await_event = QEMU_AGENT_EVENT_SHUTDOWN; - ret = qemuAgentCommand(mon, cmd, &reply); + ret = qemuAgentCommand(mon, cmd, &reply, + VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT); if (reply && ret == 0) ret = qemuAgentCheckError(cmd, reply); @@ -1315,7 +1324,8 @@ int qemuAgentFSFreeze(qemuAgentPtr mon) if (!cmd) return -1; - if (qemuAgentCommand(mon, cmd, &reply) < 0 || + if (qemuAgentCommand(mon, cmd, &reply, + VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT) < 0 || qemuAgentCheckError(cmd, reply) < 0) goto cleanup; @@ -1352,7 +1362,8 @@ int qemuAgentFSThaw(qemuAgentPtr mon) if (!cmd) return -1; - if (qemuAgentCommand(mon, cmd, &reply) < 0 || + if (qemuAgentCommand(mon, cmd, &reply, + VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT) < 0 || qemuAgentCheckError(cmd, reply) < 0) goto cleanup; @@ -1389,7 +1400,8 @@ qemuAgentSuspend(qemuAgentPtr mon, return -1; mon->await_event = QEMU_AGENT_EVENT_SUSPEND; - ret = qemuAgentCommand(mon, cmd, &reply); + ret = qemuAgentCommand(mon, cmd, &reply, + VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT); if (reply && ret == 0) ret = qemuAgentCheckError(cmd, reply); -- 1.7.1

Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- src/qemu/qemu_agent.c | 30 ++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 5 +++++ 2 files changed, 35 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 61f070c..c658bf8 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1410,3 +1410,33 @@ qemuAgentSuspend(qemuAgentPtr mon, virJSONValueFree(reply); return ret; } + +int +qemuAgentArbitraryCommand(qemuAgentPtr mon, + const char *cmd_str, + char **result, + int timeout) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + *result = NULL; + if (timeout < VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN) + return ret; + + cmd = virJSONValueFromString(cmd_str); + if (!cmd) + return ret; + + ret = qemuAgentCommand(mon, cmd, &reply, timeout); + + if (ret == 0) { + ret = qemuAgentCheckError(cmd, reply); + *result = virJSONValueToString(reply, false); + } + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h index 2fdebb2..528fee1 100644 --- a/src/qemu/qemu_agent.h +++ b/src/qemu/qemu_agent.h @@ -77,4 +77,9 @@ int qemuAgentFSThaw(qemuAgentPtr mon); int qemuAgentSuspend(qemuAgentPtr mon, unsigned int target); + +int qemuAgentArbitraryCommand(qemuAgentPtr mon, + const char *cmd, + char **result, + int timeout); #endif /* __QEMU_AGENT_H__ */ -- 1.7.1

Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- include/libvirt/libvirt-qemu.h | 3 ++ python/generator.py | 1 + src/driver.h | 4 +++ src/libvirt-qemu.c | 54 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_qemu.syms | 5 +++ 5 files changed, 67 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index 93386ca..d7f8703 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -51,6 +51,9 @@ typedef enum { VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT = 0, } virDomainQemuAgentCommandTimeoutValues; +char *virDomainQemuAgentCommand(virDomainPtr domain, const char *cmd, + int timeout, unsigned int flags); + # ifdef __cplusplus } # endif diff --git a/python/generator.py b/python/generator.py index 1f87195..7beb361 100755 --- a/python/generator.py +++ b/python/generator.py @@ -431,6 +431,7 @@ skip_impl = ( qemu_skip_impl = ( 'virDomainQemuMonitorCommand', + 'virDomainQemuAgentCommand', ) diff --git a/src/driver.h b/src/driver.h index 203497d..7ee103e 100644 --- a/src/driver.h +++ b/src/driver.h @@ -691,6 +691,9 @@ typedef int typedef int (*virDrvDomainQemuMonitorCommand)(virDomainPtr domain, const char *cmd, char **result, unsigned int flags); +typedef char * + (*virDrvDomainQemuAgentCommand)(virDomainPtr domain, const char *cmd, + int timeout, unsigned int flags); /* Choice of unsigned int rather than pid_t is intentional. */ typedef virDomainPtr @@ -1052,6 +1055,7 @@ struct _virDriver { virDrvDomainGetDiskErrors domainGetDiskErrors; virDrvDomainSetMetadata domainSetMetadata; virDrvDomainGetMetadata domainGetMetadata; + virDrvDomainQemuAgentCommand qemuDomainArbitraryAgentCommand; }; typedef int diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index 78480bb..7a45242 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -185,3 +185,57 @@ error: virDispatchError(conn); return NULL; } + +/** + * virDomainQemuAgentCommand: + * @domain: a domain object + * @cmd: the guest agent command string + * @timeout: timeout seconds + * @flags: execution flags + * + * Execute an arbitrary Guest Agent command. + * + * Issue @cmd to the guest agent running in @domain. + * @timeout must be -2, -1, 0 or positive. + * VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK(-2): meaning to block forever waiting for + * a result. + * VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT(-1): use default timeout value. + * VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT(0): does not wait. + * positive value: wait for @timeout seconds + * + * Returns strings if success, NULL in failure. + */ +char * +virDomainQemuAgentCommand(virDomainPtr domain, + const char *cmd, + int timeout, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("domain=%p, cmd=%s, timeout=%d, flags=%x", + domain, cmd, timeout, flags); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return NULL; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(NULL, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return NULL; + } + + conn = domain->conn; + + if (conn->driver->qemuDomainArbitraryAgentCommand) { + return conn->driver->qemuDomainArbitraryAgentCommand(domain, cmd, + timeout, flags); + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + + /* Copy to connection error object for back compatibility */ + virDispatchError(conn); + return NULL; +} diff --git a/src/libvirt_qemu.syms b/src/libvirt_qemu.syms index 8447730..6e11509 100644 --- a/src/libvirt_qemu.syms +++ b/src/libvirt_qemu.syms @@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 { global: virDomainQemuAttach; } LIBVIRT_QEMU_0.8.3; + +LIBVIRT_QEMU_0.10.1 { + global: + virDomainQemuAgentCommand; +} LIBVIRT_QEMU_0.9.4; -- 1.7.1

On 08/22/2012 09:29 PM, MATSUDA Daiki wrote:
Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- include/libvirt/libvirt-qemu.h | 3 ++ python/generator.py | 1 + src/driver.h | 4 +++ src/libvirt-qemu.c | 54 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_qemu.syms | 5 +++ 5 files changed, 67 insertions(+), 0 deletions(-)
+++ b/src/libvirt_qemu.syms @@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 { global: virDomainQemuAttach; } LIBVIRT_QEMU_0.8.3; + +LIBVIRT_QEMU_0.10.1 { + global: + virDomainQemuAgentCommand; +} LIBVIRT_QEMU_0.9.4;
Since this made it in before 0.10.0, this versioning is wrong. I'll push an obvious followup patch. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- src/qemu/qemu_driver.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 71 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 109d18d..a62d1bb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -13437,6 +13437,76 @@ qemuListAllDomains(virConnectPtr conn, return ret; } +static char * +qemuDrvDomainAgentCommand(virDomainPtr domain, + const char *cmd, + int timeout, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + char *result = NULL; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, NULL); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + if (priv->agentError) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("QEMU guest agent is not available due to an error")); + goto cleanup; + } + + if (!priv->agent) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("QEMU guest agent is not configured")); + goto cleanup; + } + + 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; + } + + qemuDomainObjEnterAgent(driver, vm); + ret = qemuAgentArbitraryCommand(priv->agent, cmd, &result, timeout); + qemuDomainObjExitAgent(driver, vm); + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) { + vm = NULL; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return result; +} + static virDriver qemuDriver = { .no = VIR_DRV_QEMU, .name = QEMU_DRIVER_NAME, @@ -13603,6 +13673,7 @@ static virDriver qemuDriver = { .domainPMSuspendForDuration = qemuDomainPMSuspendForDuration, /* 0.9.11 */ .domainPMWakeup = qemuDomainPMWakeup, /* 0.9.11 */ .domainGetCPUStats = qemuDomainGetCPUStats, /* 0.9.11 */ + .qemuDomainArbitraryAgentCommand = qemuDrvDomainAgentCommand, /* 0.10.1 */ }; -- 1.7.1

On Thu, Aug 23, 2012 at 12:29:24PM +0900, MATSUDA Daiki wrote:
Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- src/qemu/qemu_driver.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 71 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 109d18d..a62d1bb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -13437,6 +13437,76 @@ qemuListAllDomains(virConnectPtr conn, return ret; }
+static char * +qemuDrvDomainAgentCommand(virDomainPtr domain, + const char *cmd, + int timeout, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + char *result = NULL; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, NULL); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + if (priv->agentError) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("QEMU guest agent is not available due to an error")); + goto cleanup; + } + + if (!priv->agent) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("QEMU guest agent is not configured")); + goto cleanup; + } + + 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; + } + + qemuDomainObjEnterAgent(driver, vm); + ret = qemuAgentArbitraryCommand(priv->agent, cmd, &result, timeout); + qemuDomainObjExitAgent(driver, vm);
The problem here is that ret is not looked at. I think that part really need to be improved especially as the possibility to run any command is far more likely to fail, a better error report is needed
+endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) { + vm = NULL; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return result; +} + static virDriver qemuDriver = { .no = VIR_DRV_QEMU, .name = QEMU_DRIVER_NAME, @@ -13603,6 +13673,7 @@ static virDriver qemuDriver = { .domainPMSuspendForDuration = qemuDomainPMSuspendForDuration, /* 0.9.11 */ .domainPMWakeup = qemuDomainPMWakeup, /* 0.9.11 */ .domainGetCPUStats = qemuDomainGetCPUStats, /* 0.9.11 */ + .qemuDomainArbitraryAgentCommand = qemuDrvDomainAgentCommand, /* 0.10.1 */ };
I squashed that in the meantime but something better need to be done here, report the domain name and command and if possible an error message sounds a minimum. Daniel diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fc5fa52..3948bef 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -13857,6 +13857,11 @@ qemuDrvDomainAgentCommand(virDomainPtr domain, qemuDomainObjEnterAgent(driver, vm); ret = qemuAgentArbitraryCommand(priv->agent, cmd, &result, timeout); qemuDomainObjExitAgent(driver, vm); + if (ret < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to execute agent command")); + goto endjob; + } endjob: if (qemuDomainObjEndJob(driver, vm) == 0) { -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- src/qemu_protocol-structs | 10 ++++++++++ src/remote/qemu_protocol.x | 14 +++++++++++++- src/remote/remote_driver.c | 1 + 3 files changed, 24 insertions(+), 1 deletions(-) diff --git a/src/qemu_protocol-structs b/src/qemu_protocol-structs index 67968eb..e6cde60 100644 --- a/src/qemu_protocol-structs +++ b/src/qemu_protocol-structs @@ -19,7 +19,17 @@ struct qemu_domain_attach_args { struct qemu_domain_attach_ret { remote_nonnull_domain dom; }; +struct qemu_domain_agent_command_args { + remote_nonnull_domain dom; + remote_nonnull_string cmd; + int timeout; + u_int flags; +}; +struct qemu_domain_agent_command_ret { + remote_string result; +}; enum qemu_procedure { QEMU_PROC_MONITOR_COMMAND = 1, QEMU_PROC_DOMAIN_ATTACH = 2, + QEMU_PROC_DOMAIN_AGENT_COMMAND = 3, }; diff --git a/src/remote/qemu_protocol.x b/src/remote/qemu_protocol.x index c06339c..914caed 100644 --- a/src/remote/qemu_protocol.x +++ b/src/remote/qemu_protocol.x @@ -47,6 +47,17 @@ struct qemu_domain_attach_ret { remote_nonnull_domain dom; }; +struct qemu_domain_agent_command_args { + remote_nonnull_domain dom; + remote_nonnull_string cmd; + int timeout; + unsigned int flags; +}; + +struct qemu_domain_agent_command_ret { + remote_string result; +}; + /* Define the program number, protocol version and procedure numbers here. */ const QEMU_PROGRAM = 0x20008087; const QEMU_PROTOCOL_VERSION = 1; @@ -61,5 +72,6 @@ enum qemu_procedure { * are some exceptions to this rule, e.g. domainDestroy. Other APIs MAY * be marked as high priority. If in doubt, it's safe to choose low. */ QEMU_PROC_MONITOR_COMMAND = 1, /* skipgen skipgen priority:low */ - QEMU_PROC_DOMAIN_ATTACH = 2 /* autogen autogen priority:low */ + QEMU_PROC_DOMAIN_ATTACH = 2, /* autogen autogen priority:low */ + QEMU_PROC_DOMAIN_AGENT_COMMAND = 3 /* autogen autogen priority:low */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 977d139..aa234b3 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -5416,6 +5416,7 @@ static virDriver remote_driver = { .domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */ .domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */ .domainGetHostname = remoteDomainGetHostname, /* 0.10.0 */ + .qemuDomainArbitraryAgentCommand = qemuDomainAgentCommand, /* 0.10.1 */ }; static virNetworkDriver network_driver = { -- 1.7.1

Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- python/libvirt-qemu-override-api.xml | 8 ++++++++ python/libvirt-qemu-override.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 0 deletions(-) diff --git a/python/libvirt-qemu-override-api.xml b/python/libvirt-qemu-override-api.xml index d69acea..ca0dae9 100644 --- a/python/libvirt-qemu-override-api.xml +++ b/python/libvirt-qemu-override-api.xml @@ -8,5 +8,13 @@ <arg name='cmd' type='const char *' info='the command which will be passed to QEMU monitor'/> <arg name='flags' type='unsigned int' info='an OR'ed set of virDomainQemuMonitorCommandFlags'/> </function> + <function name='virDomainQemuAgentCommand' file='python-qemu'> + <info>Send a Guest Agent command to domain</info> + <return type='str *' info='the command output'/> + <arg name='domain' type='virDomainPtr' info='pointer to the domain'/> + <arg name='cmd' type='const char *' info='guest agent command on domain'/> + <arg name='timeout' type='int' info='timeout seconds'/> + <arg name='flags' type='unsigned int' info='execution flags'/> + </function> </symbols> </api> diff --git a/python/libvirt-qemu-override.c b/python/libvirt-qemu-override.c index e532416..243692a 100644 --- a/python/libvirt-qemu-override.c +++ b/python/libvirt-qemu-override.c @@ -82,6 +82,34 @@ libvirt_qemu_virDomainQemuMonitorCommand(PyObject *self ATTRIBUTE_UNUSED, return py_retval; } +static PyObject * +libvirt_qemu_virDomainQemuAgentCommand(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) +{ + PyObject *py_retval; + char *result = NULL; + virDomainPtr domain; + PyObject *pyobj_domain; + int timeout; + unsigned int flags; + char *cmd; + + if (!PyArg_ParseTuple(args, (char *)"Ozii:virDomainQemuAgentCommand", + &pyobj_domain, &cmd, &timeout, &flags)) + return NULL; + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (domain == NULL) + return VIR_PY_NONE; + LIBVIRT_BEGIN_ALLOW_THREADS; + result = virDomainQemuAgentCommand(domain, cmd, timeout, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (!result) + return VIR_PY_NONE; + + py_retval = PyString_FromString(result); + return py_retval; +} /************************************************************************ * * * The registration stuff * @@ -90,6 +118,7 @@ libvirt_qemu_virDomainQemuMonitorCommand(PyObject *self ATTRIBUTE_UNUSED, static PyMethodDef libvirtQemuMethods[] = { #include "libvirt-qemu-export.c" {(char *) "virDomainQemuMonitorCommand", libvirt_qemu_virDomainQemuMonitorCommand, METH_VARARGS, NULL}, + {(char *) "virDomainQemuAgentCommand", libvirt_qemu_virDomainQemuAgentCommand, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -- 1.7.1

On Thu, Aug 23, 2012 at 12:29:26PM +0900, MATSUDA Daiki wrote:
Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- python/libvirt-qemu-override-api.xml | 8 ++++++++ python/libvirt-qemu-override.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 0 deletions(-)
diff --git a/python/libvirt-qemu-override-api.xml b/python/libvirt-qemu-override-api.xml index d69acea..ca0dae9 100644 --- a/python/libvirt-qemu-override-api.xml +++ b/python/libvirt-qemu-override-api.xml @@ -8,5 +8,13 @@ <arg name='cmd' type='const char *' info='the command which will be passed to QEMU monitor'/> <arg name='flags' type='unsigned int' info='an OR'ed set of virDomainQemuMonitorCommandFlags'/> </function> + <function name='virDomainQemuAgentCommand' file='python-qemu'> + <info>Send a Guest Agent command to domain</info> + <return type='str *' info='the command output'/> + <arg name='domain' type='virDomainPtr' info='pointer to the domain'/> + <arg name='cmd' type='const char *' info='guest agent command on domain'/> + <arg name='timeout' type='int' info='timeout seconds'/> + <arg name='flags' type='unsigned int' info='execution flags'/> + </function> </symbols> </api> diff --git a/python/libvirt-qemu-override.c b/python/libvirt-qemu-override.c index e532416..243692a 100644 --- a/python/libvirt-qemu-override.c +++ b/python/libvirt-qemu-override.c @@ -82,6 +82,34 @@ libvirt_qemu_virDomainQemuMonitorCommand(PyObject *self ATTRIBUTE_UNUSED, return py_retval; }
+static PyObject * +libvirt_qemu_virDomainQemuAgentCommand(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) +{ + PyObject *py_retval; + char *result = NULL; + virDomainPtr domain; + PyObject *pyobj_domain; + int timeout; + unsigned int flags; + char *cmd; + + if (!PyArg_ParseTuple(args, (char *)"Ozii:virDomainQemuAgentCommand", + &pyobj_domain, &cmd, &timeout, &flags)) + return NULL; + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (domain == NULL) + return VIR_PY_NONE; + LIBVIRT_BEGIN_ALLOW_THREADS; + result = virDomainQemuAgentCommand(domain, cmd, timeout, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (!result) + return VIR_PY_NONE; + + py_retval = PyString_FromString(result); + return py_retval; +}
i agree to have a simple string returned back at the condition that we add error support by raising an exception from the C code (I don't remember how to do this) otherwise we have no way to report an error with the python API as-is,
* * * The registration stuff * @@ -90,6 +118,7 @@ libvirt_qemu_virDomainQemuMonitorCommand(PyObject *self ATTRIBUTE_UNUSED, static PyMethodDef libvirtQemuMethods[] = { #include "libvirt-qemu-export.c" {(char *) "virDomainQemuMonitorCommand", libvirt_qemu_virDomainQemuMonitorCommand, METH_VARARGS, NULL}, + {(char *) "virDomainQemuAgentCommand", libvirt_qemu_virDomainQemuAgentCommand, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} };
Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- tools/virsh-host.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 13 +++++++ 2 files changed, 102 insertions(+), 0 deletions(-) diff --git a/tools/virsh-host.c b/tools/virsh-host.c index b09d9f9..682d374 100644 --- a/tools/virsh-host.c +++ b/tools/virsh-host.c @@ -633,6 +633,93 @@ cleanup: } /* + * "qemu-agent-command" command + */ +static const vshCmdInfo info_qemu_agent_command[] = { + {"help", N_("QEMU Guest Agent Command")}, + {"desc", N_("Run an arbitrary qemu guest agent command; use at your own risk")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_qemu_agent_command[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"timeout", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("timeout seconds. must be positive.")}, + {"async", VSH_OT_BOOL, 0, N_("execute command without waiting for timeout")}, + {"block", VSH_OT_BOOL, 0, N_("execute command without timeout")}, + {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + bool ret = false; + char *guest_agent_cmd = NULL; + char *result = NULL; + int timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT; + int judge = 0; + unsigned int flags = 0; + const vshCmdOpt *opt = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + bool pad = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (pad) + virBufferAddChar(&buf, ' '); + pad = true; + virBufferAdd(&buf, opt->data, -1); + } + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to collect command")); + goto cleanup; + } + guest_agent_cmd = virBufferContentAndReset(&buf); + + judge = vshCommandOptInt(cmd, "timeout", &timeout); + if (judge < 0) { + vshError(ctl, "%s", _("timeout number has to be a number")); + } else if (judge > 0) { + judge = 1; + } + if (judge && timeout < 1) { + vshError(ctl, "%s", _("timeout must be positive")); + } + + if (vshCommandOptBool(cmd, "async")) { + timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT; + judge++; + } + if (vshCommandOptBool(cmd, "block")) { + timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK; + judge++; + } + + if (judge > 1) { + vshError(ctl, "%s", _("timeout, async and block options are exclusive")); + } + result = virDomainQemuAgentCommand(dom, guest_agent_cmd, timeout, flags); + + if (result) printf("%s\n", result); + + ret = true; + +cleanup: + VIR_FREE(result); + VIR_FREE(guest_agent_cmd); + if (dom) + virDomainFree(dom); + + return ret; +} +/* * "sysinfo" command */ static const vshCmdInfo info_sysinfo[] = { @@ -832,6 +919,8 @@ static const vshCmdDef hostAndHypervisorCmds[] = { {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach, 0}, {"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command, info_qemu_monitor_command, 0}, + {"qemu-agent-command", cmdQemuAgentCommand, opts_qemu_agent_command, + info_qemu_agent_command, 0}, {"sysinfo", cmdSysinfo, NULL, info_sysinfo, 0}, {"uri", cmdURI, NULL, info_uri, 0}, {"version", cmdVersion, opts_version, info_version, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 35613c4..f997cb4 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -164,6 +164,7 @@ group as an option. For example: hostname print the hypervisor hostname qemu-attach Attach to existing QEMU process qemu-monitor-command QEMU Monitor Command + qemu-agent-command QEMU Guest Agent Command sysinfo print the hypervisor sysinfo uri print the hypervisor canonical URI @@ -2780,6 +2781,18 @@ before passing the single command to the monitor. =back +=item B<qemu-agent-command> I<domain> [I<--timeout> I<seconds> | I<--async> | I<--block>] I<command>... + +Send an arbitrary guest agent command I<command> to domain I<domain> through +qemu agent. +I<--timeout>, I<--async> and I<--block> options are exclusive. +I<--timeout> requires timeout seconds I<seconds> and it must be positive. +When I<--aysnc> is given, the command waits for timeout whether success or +failed. And when I<--block> is given, the command waits forever with blocking +timeout. + +=back + =head1 ENVIRONMENT The following environment variables can be set to alter the behaviour -- 1.7.1

On Thu, Aug 23, 2012 at 12:19:24PM +0900, MATSUDA, Daiki wrote:
Hi. All.
I rewrote the patched for git head tree, adding some code.
And @timeout must be following VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK(-2) : does not block and wait forever. VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT(-1) : use default time for waiting VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWIAT(0) : does not wait and out soon positive value : wait for pointed seconds. And I also add VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN for not writing static value.
In addition, I added the some options for virsh command qemu-agent-command --block : it means taht @timeout is VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK. --async : it means taht @timeout is VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWIAT. --timeout @seconds : @seconds must be positive value and it means that @timeout is positive value case. These upper options are exclusive. If no options are given, it means taht @timeout is IR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT.
virsh # help qemu-agent-command NAME qemu-agent-command - QEMU Guest Agent Command
SYNOPSIS qemu-agent-command <domain> [--timeout <number>] [--async] [--block] {[--cmd] <string>}...
DESCRIPTION Run an arbitrary qemu guest agent command; use at your own risk
OPTIONS [--domain] <string> domain name, id or uuid --timeout <number> timeout seconds. must be positive. --async execute command without waiting for timeout --block execute command without timeout [--cmd] <string> command
Okay, I think that patch set is ripe, and since it it not touching the main libvirt API I think it's okay to push it now even if we entered freeze yesterday. My main concern is error handling, we must improve that part in subsequent patches preferably before the 0.10.0 release, an attempt to run an unsupported agent command or with an error syntax should be reported correctly at the C level and an exception should be raised in Python. I also had to reformat all the comment which got mangled as the mail title for some reason, Anyway it's in! thanks a lot, but let's fix the error handling :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (4)
-
Daniel Veillard
-
Eric Blake
-
MATSUDA Daiki
-
MATSUDA, Daiki