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

Hi. All. I rewrote the patched. But I modified the feature for the situation @result pointer is NULL. Though before it does not forcely wait, I think it is not meanless because @result pointer is always NULL. 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. 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}

Add @seconds variable to qemuAgentSend(). When @tiemout is true, @seconds controls how long to wait for a response (if @seconds is VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, default to QEMU_AGENT_WAIT_TIME). If @timeout is false, @seconds is ignored. Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- include/libvirt/libvirt-qemu.h | 6 ++++++ src/qemu/qemu_agent.c | 30 ++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index a37f897..013ed5a 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -44,6 +44,12 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int pid_value, unsigned int flags); +typedef enum { + 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 15af758..26c2726 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,8 @@ static int qemuAgentSend(qemuAgentPtr mon, if (timeout) { if (virTimeMillisNow(&now) < 0) return -1; - then = now + QEMU_AGENT_WAIT_TIME; + then = now + (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT ? + QEMU_AGENT_WAIT_TIME : seconds * 1000ull); } mon->msg = msg; @@ -937,7 +941,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 +982,8 @@ cleanup: static int qemuAgentCommand(qemuAgentPtr mon, virJSONValuePtr cmd, - virJSONValuePtr *reply) + virJSONValuePtr *reply, + int seconds) { int ret = -1; qemuAgentMessage msg; @@ -1003,9 +1009,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 +1289,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 +1322,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 +1360,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 +1398,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

On 08/15/2012 03:36 AM, MATSUDA Daiki wrote:
Add @seconds variable to qemuAgentSend(). When @tiemout is true, @seconds controls how long to wait for a response (if @seconds is VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, default to QEMU_AGENT_WAIT_TIME). If @timeout is false, @seconds is ignored.
Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- include/libvirt/libvirt-qemu.h | 6 ++++++ src/qemu/qemu_agent.c | 30 ++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index a37f897..013ed5a 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -44,6 +44,12 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int pid_value, unsigned int flags);
+typedef enum { + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK = -2,
Correct me if I'm wrong, but isn't this the same as setting timeout to false?
+ 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 15af758..26c2726 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.
If my previous assumption is correct, than the timeout parameter can be completely thrown away.
* * 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,8 @@ static int qemuAgentSend(qemuAgentPtr mon, if (timeout) { if (virTimeMillisNow(&now) < 0) return -1; - then = now + QEMU_AGENT_WAIT_TIME; + then = now + (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT ? + QEMU_AGENT_WAIT_TIME : seconds * 1000ull); }
Also if seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK, then this causes 'then' to be smaller than now. I'm not sure what that would do with pthread_cond_timedwait, but that's definitely not wanted behavior. Martin

(2012/08/16 21:58), Martin Kletzander wrote:
On 08/15/2012 03:36 AM, MATSUDA Daiki wrote:
Add @seconds variable to qemuAgentSend(). When @tiemout is true, @seconds controls how long to wait for a response (if @seconds is VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, default to QEMU_AGENT_WAIT_TIME). If @timeout is false, @seconds is ignored.
Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- include/libvirt/libvirt-qemu.h | 6 ++++++ src/qemu/qemu_agent.c | 30 ++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index a37f897..013ed5a 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -44,6 +44,12 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int pid_value, unsigned int flags);
+typedef enum { + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK = -2,
Correct me if I'm wrong, but isn't this the same as setting timeout to false?
Yes, it is same that @timeout = false to qemuAgentSend() from qemuAgentCommand().
+ 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 15af758..26c2726 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.
If my previous assumption is correct, than the timeout parameter can be completely thrown away.
@seconds is effective uder the situation @timeout = true. Then @seconds must be > 0. But @seconds = 0, it is dangerous. I do not understand whether it is needed, but other people discussed. https://www.redhat.com/archives/libvir-list/2012-July/msg00603.html
* * 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,8 @@ static int qemuAgentSend(qemuAgentPtr mon, if (timeout) { if (virTimeMillisNow(&now) < 0) return -1; - then = now + QEMU_AGENT_WAIT_TIME; + then = now + (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT ? + QEMU_AGENT_WAIT_TIME : seconds * 1000ull); }
Also if seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK, then this causes 'then' to be smaller than now. I'm not sure what that would do with pthread_cond_timedwait, but that's definitely not wanted behavior.
Its situation is blocked on calling qemuAgentSend() from qemuAgentCommand(). But qemuAgentSend() may also have the block code.
Martin

On 08/20/2012 02:10 AM, MATSUDA, Daiki wrote:
(2012/08/16 21:58), Martin Kletzander wrote:
On 08/15/2012 03:36 AM, MATSUDA Daiki wrote:
Add @seconds variable to qemuAgentSend(). When @tiemout is true, @seconds controls how long to wait for a response (if @seconds is VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, default to QEMU_AGENT_WAIT_TIME). If @timeout is false, @seconds is ignored.
Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- include/libvirt/libvirt-qemu.h | 6 ++++++ src/qemu/qemu_agent.c | 30 ++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index a37f897..013ed5a 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -44,6 +44,12 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int pid_value, unsigned int flags);
+typedef enum { + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK = -2,
Correct me if I'm wrong, but isn't this the same as setting timeout to false?
Yes, it is same that @timeout = false to qemuAgentSend() from qemuAgentCommand().
+ 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 15af758..26c2726 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.
If my previous assumption is correct, than the timeout parameter can be completely thrown away.
@seconds is effective uder the situation @timeout = true. Then @seconds must be > 0. But @seconds = 0, it is dangerous.
I do not understand whether it is needed, but other people discussed. https://www.redhat.com/archives/libvir-list/2012-July/msg00603.html
I don't suggest to change the behavior, I just see that if the parameter 'timeout' is omitted, we can still get all the options to run the command with (nonblock/timeout/block), so it's just a simplification.
* * 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,8 @@ static int qemuAgentSend(qemuAgentPtr mon, if (timeout) { if (virTimeMillisNow(&now) < 0) return -1; - then = now + QEMU_AGENT_WAIT_TIME; + then = now + (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT ? + QEMU_AGENT_WAIT_TIME : seconds * 1000ull); }
Also if seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK, then this causes 'then' to be smaller than now. I'm not sure what that would do with pthread_cond_timedwait, but that's definitely not wanted behavior.
Its situation is blocked on calling qemuAgentSend() from qemuAgentCommand(). But qemuAgentSend() may also have the block code.
OK, you're right. It is blocked when the qemuAgentSend is called. It still looks unclean to me, though, because of the redundant option. It'd be great to have another opinion from someone else :) Martin

add qemuAgentArbitraryCommand() for general qemu agent command. Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- src/qemu/qemu_agent.c | 31 +++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 5 +++++ 2 files changed, 36 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 26c2726..c634c23 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1408,3 +1408,34 @@ 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; + + if (timeout < -2) + 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); + } else { + result = NULL; + } + + 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

On 08/15/2012 03:36 AM, MATSUDA Daiki wrote:>
add qemuAgentArbitraryCommand() for general qemu agent command.
Twice the same line in the message, could be less brief, maybe :)
Signed-off-by: MATSUDA Daiki <matsudadik@intellilink.co.jp> --- src/qemu/qemu_agent.c | 31 +++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 5 +++++ 2 files changed, 36 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 26c2726..c634c23 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1408,3 +1408,34 @@ 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; + + if (timeout < -2) + return ret;
I think you can leave the test to only one layer of the code and that would be 'qemuAgentSend'.
+ + cmd = virJSONValueFromString(cmd_str); + if (!cmd) + return ret; + + ret = qemuAgentCommand(mon, cmd, &reply, timeout); + + if (ret == 0) { + ret = qemuAgentCheckError(cmd, reply); + *result = virJSONValueToString(reply); + } else { + result = NULL;
First of all, you probably wanted to do '*result = NULL', because 'result = NULL' won't do anything here, however in case of any other failure (timeout == -3, fail in virJSONValuFromSting) you don't guarantee the *result to be NULL, so I don't think you need to do this here. Martin

Add virDrvAgentCommand prototype for drivers. Add virAgentCommand() for virDrvAgentCommand. 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 | 51 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_qemu.syms | 5 ++++ 5 files changed, 64 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index 013ed5a..60b83ef 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -50,6 +50,9 @@ typedef enum { VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT = 0, } virDomainQemuAgentCommandTimeoutValues; +char *virAgentCommand(virDomainPtr domain, const char *cmd, + int timeout, unsigned int flags); + # ifdef __cplusplus } # endif diff --git a/python/generator.py b/python/generator.py index 6559ece..68758ad 100755 --- a/python/generator.py +++ b/python/generator.py @@ -431,6 +431,7 @@ skip_impl = ( qemu_skip_impl = ( 'virDomainQemuMonitorCommand', + 'virAgentCommand', ) diff --git a/src/driver.h b/src/driver.h index aab9766..b780e45 100644 --- a/src/driver.h +++ b/src/driver.h @@ -688,6 +688,9 @@ typedef int typedef int (*virDrvDomainQemuMonitorCommand)(virDomainPtr domain, const char *cmd, char **result, unsigned int flags); +typedef char * + (*virDrvAgentCommand)(virDomainPtr domain, const char *cmd, + int timeout, unsigned int flags); /* Choice of unsigned int rather than pid_t is intentional. */ typedef virDomainPtr @@ -1048,6 +1051,7 @@ struct _virDriver { virDrvDomainGetDiskErrors domainGetDiskErrors; virDrvDomainSetMetadata domainSetMetadata; virDrvDomainGetMetadata domainGetMetadata; + virDrvAgentCommand qemuAgentCommand; }; typedef int diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index 78480bb..49dfc20 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -185,3 +185,54 @@ error: virDispatchError(conn); return NULL; } + +/** + * virAgentCommand: + * @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. + * If @result is NULL, then don't wait for a result (and @timeout + * must be 0). Otherwise, wait for @timeout seconds for a + * @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 * +virAgentCommand(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; + } + + conn = domain->conn; + + if (conn->driver->qemuAgentCommand) { + return conn->driver->qemuAgentCommand(domain, cmd, timeout, flags); + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + + /* Copy to connection error object for back compatability */ + virDispatchError(domain->conn); + return NULL; +} diff --git a/src/libvirt_qemu.syms b/src/libvirt_qemu.syms index 8447730..f1a4bb4 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.0 { + global: + virAgentCommand; +} LIBVIRT_QEMU_0.9.4; -- 1.7.1

On 08/15/2012 03:36 AM, MATSUDA Daiki wrote:
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index 013ed5a..60b83ef 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -50,6 +50,9 @@ typedef enum { VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT = 0, } virDomainQemuAgentCommandTimeoutValues;
+char *virAgentCommand(virDomainPtr domain, const char *cmd, + int timeout, unsigned int flags); +
I wondered why this is type 'char *', when we can use 'int' as usual and have '**result' (same as monitor command does).
diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index 78480bb..49dfc20 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -185,3 +185,54 @@ error: virDispatchError(conn); return NULL; } + +/** + * virAgentCommand: + * @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. + * If @result is NULL, then don't wait for a result (and @timeout
And then I noticed that there is no @result here, so maybe you wanted to make it that way and didn't change something?
+ * must be 0). Otherwise, wait for @timeout seconds for a + * @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 * +virAgentCommand(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; + } + + conn = domain->conn; + + if (conn->driver->qemuAgentCommand) { + return conn->driver->qemuAgentCommand(domain, cmd, timeout, flags);
Shouldn't this be allowed only for read-only connections?
+ } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + + /* Copy to connection error object for back compatability */
s/compatability/compatibility/
+ virDispatchError(domain->conn);
You have 'domain->conn' in 'conn' already. Martin

(2012/08/16 22:13), Martin Kletzander wrote:
On 08/15/2012 03:36 AM, MATSUDA Daiki wrote:
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index 013ed5a..60b83ef 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -50,6 +50,9 @@ typedef enum { VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT = 0, } virDomainQemuAgentCommandTimeoutValues;
+char *virAgentCommand(virDomainPtr domain, const char *cmd, + int timeout, unsigned int flags); +
I wondered why this is type 'char *', when we can use 'int' as usual and have '**result' (same as monitor command does).
It depends on generating the code automatically Eric suggested. It decides the returning type.
diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index 78480bb..49dfc20 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -185,3 +185,54 @@ error: virDispatchError(conn); return NULL; } + +/** + * virAgentCommand: + * @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. + * If @result is NULL, then don't wait for a result (and @timeout
And then I noticed that there is no @result here, so maybe you wanted to make it that way and didn't change something?
It is my mistake.
+ * must be 0). Otherwise, wait for @timeout seconds for a + * @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 * +virAgentCommand(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; + } + + conn = domain->conn; + + if (conn->driver->qemuAgentCommand) { + return conn->driver->qemuAgentCommand(domain, cmd, timeout, flags);
Shouldn't this be allowed only for read-only connections?
I do not uderstand what you say.
+ } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + + /* Copy to connection error object for back compatability */
s/compatability/compatibility/
+ virDispatchError(domain->conn);
You have 'domain->conn' in 'conn' already.
Martin

On 08/20/2012 02:16 AM, MATSUDA, Daiki wrote:
(2012/08/16 22:13), Martin Kletzander wrote:
On 08/15/2012 03:36 AM, MATSUDA Daiki wrote: [...]
+char * +virAgentCommand(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; + } + + conn = domain->conn; + + if (conn->driver->qemuAgentCommand) { + return conn->driver->qemuAgentCommand(domain, cmd, timeout, flags);
Shouldn't this be allowed only for read-only connections?
I do not uderstand what you say.
I meant a check similar to this: if (conn->flags & VIR_CONNECT_RO) virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__);

Add qemuDomainAgentCommand() for .qemuAgentCommand to qemu driver. 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 dee1268..36a698b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -13204,6 +13204,76 @@ qemuListAllDomains(virConnectPtr conn, return ret; } +static char * +qemuDomainAgentCommand(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, @@ -13369,6 +13439,7 @@ static virDriver qemuDriver = { .domainPMSuspendForDuration = qemuDomainPMSuspendForDuration, /* 0.9.11 */ .domainPMWakeup = qemuDomainPMWakeup, /* 0.9.11 */ .domainGetCPUStats = qemuDomainGetCPUStats, /* 0.9.11 */ + .qemuAgentCommand = qemuDomainAgentCommand, /* 0.10.0 */ }; -- 1.7.1

Add qemuAgentCommand() which is generated automatically, for .qemuAgentCommand to remote driver. 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..65fec26 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_agent_command_args { + remote_nonnull_domain dom; + remote_nonnull_string cmd; + int timeout; + u_int flags; +}; +struct qemu_agent_command_ret { + remote_string result; +}; enum qemu_procedure { QEMU_PROC_MONITOR_COMMAND = 1, QEMU_PROC_DOMAIN_ATTACH = 2, + QEMU_PROC_AGENT_COMMAND = 3, }; diff --git a/src/remote/qemu_protocol.x b/src/remote/qemu_protocol.x index c06339c..b593a1f 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_agent_command_args { + remote_nonnull_domain dom; + remote_nonnull_string cmd; + int timeout; + unsigned int flags; +}; + +struct qemu_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_AGENT_COMMAND = 3 /* autogen autogen priority:low */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 353a153..3c60709 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -5368,6 +5368,7 @@ static virDriver remote_driver = { .domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */ .domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */ .domainGetHostname = remoteDomainGetHostname, /* 0.10.0 */ + .qemuAgentCommand = qemuAgentCommand, /* 0.10.0 */ }; static virNetworkDriver network_driver = { -- 1.7.1

On 08/15/2012 03:36 AM, MATSUDA Daiki wrote:
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 353a153..3c60709 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -5368,6 +5368,7 @@ static virDriver remote_driver = { .domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */ .domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */ .domainGetHostname = remoteDomainGetHostname, /* 0.10.0 */ + .qemuAgentCommand = qemuAgentCommand, /* 0.10.0 */
You probably want to call some function like 'remoteQemuDomainAgentCommand' or something like that since qemuAgentCommand works locally and not remotely. I haven't had a look at the rest of the series. Please feel free to correct me in case I've misunderstood the code somewhere. Martin

(2012/08/16 22:25), Martin Kletzander wrote:
On 08/15/2012 03:36 AM, MATSUDA Daiki wrote:
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 353a153..3c60709 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -5368,6 +5368,7 @@ static virDriver remote_driver = { .domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */ .domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */ .domainGetHostname = remoteDomainGetHostname, /* 0.10.0 */ + .qemuAgentCommand = qemuAgentCommand, /* 0.10.0 */
You probably want to call some function like 'remoteQemuDomainAgentCommand' or something like that since qemuAgentCommand works locally and not remotely.
Yes. But the function name is generated automatically from QEMU_RPOC_AGNET_COMMAND. And I will change it on next version. So, do you have suggestion name? qemuDomainAgentCommand() from QEMU_PROC_DOMAIN_AGENT_COMMAND ?
I haven't had a look at the rest of the series. Please feel free to correct me in case I've misunderstood the code somewhere.
Martin

On 08/20/2012 02:20 AM, MATSUDA, Daiki wrote:
(2012/08/16 22:25), Martin Kletzander wrote:
On 08/15/2012 03:36 AM, MATSUDA Daiki wrote:
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 353a153..3c60709 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -5368,6 +5368,7 @@ static virDriver remote_driver = { .domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */ .domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */ .domainGetHostname = remoteDomainGetHostname, /* 0.10.0 */ + .qemuAgentCommand = qemuAgentCommand, /* 0.10.0 */
You probably want to call some function like 'remoteQemuDomainAgentCommand' or something like that since qemuAgentCommand works locally and not remotely.
Yes. But the function name is generated automatically from QEMU_RPOC_AGNET_COMMAND. And I will change it on next version. So, do you have suggestion name? qemuDomainAgentCommand() from QEMU_PROC_DOMAIN_AGENT_COMMAND ?
My concern wasn't about the function name. The problem is that there is no remote call. The function you are assigning here operates on local machine and not on the connected daemon side (there is no 'call()' in the stack). OTOH I haven't wrote an API myself, so I'm not in the right position to comment on this with a lot of self-confidence (I just took an inspiration out of other commands, mainly qemuMonitor*), so others opinions are welcomed. Martin

Add virAgentCommand() support function to python module. 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..7a31b0e 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='virAgentCommand' 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..738b2da 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_virAgentCommand(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:virAgentCommand", + &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 *) "virAgentCommand", libvirt_qemu_virAgentCommand, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -- 1.7.1

Add qemu-agent-command to virsh to support virAgentCommand(). 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 d9d09b4..d302e19 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 = virAgentCommand(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
participants (3)
-
Martin Kletzander
-
MATSUDA Daiki
-
MATSUDA, Daiki