diff -uNrp libvirt-0.9.13.orig/daemon/remote.c libvirt-0.9.13/daemon/remote.c --- libvirt-0.9.13.orig/daemon/remote.c 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/daemon/remote.c 2012-06-26 11:34:02.058455043 +0900 @@ -3923,6 +3923,46 @@ cleanup: return rv; } +static int remoteDispatchDomainGuestAgentInfo(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_guest_agent_info_args *args, + remote_domain_guest_agent_info_ret *ret) +{ + virDomainPtr dom = NULL; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + ret->buffer.buffer_len = REMOTE_DOMAIN_GA_INFO_BUFFER_MAX; + if (VIR_ALLOC_N (ret->buffer.buffer_val, REMOTE_DOMAIN_GA_INFO_BUFFER_MAX) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virDomainGuestAgentInfo(dom, &(ret->buffer.buffer_val)) < 0) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("Guest Agent Error")); + goto cleanup; + } + + rv = 0; +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + return rv; +} + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff -uNrp libvirt-0.9.13.orig/daemon/remote_dispatch.h libvirt-0.9.13/daemon/remote_dispatch.h --- libvirt-0.9.13.orig/daemon/remote_dispatch.h 2012-06-25 19:48:08.000000000 +0900 +++ libvirt-0.9.13/daemon/remote_dispatch.h 2012-06-26 11:36:45.846454615 +0900 @@ -12889,6 +12889,28 @@ static int remoteDispatchSupportsFeature +static int remoteDispatchDomainGuestAgentInfo( + virNetServerPtr server, + virNetServerClientPtr client, + virNetMessagePtr msg, + virNetMessageErrorPtr rerr, + remote_domain_guest_agent_info_args *args, + remote_domain_guest_agent_info_ret *ret); +static int remoteDispatchDomainGuestAgentInfoHelper( + virNetServerPtr server, + virNetServerClientPtr client, + virNetMessagePtr msg, + virNetMessageErrorPtr rerr, + void *args, + void *ret) +{ + VIR_DEBUG("server=%p client=%p msg=%p rerr=%p args=%p ret=%p", server, client, msg, rerr, args, ret); + return remoteDispatchDomainGuestAgentInfo(server, client, msg, rerr, args, ret); +} +/* remoteDispatchDomainGuestAgentInfo body has to be implemented manually */ + + + virNetServerProgramProc remoteProcs[] = { { /* Unused 0 */ NULL, @@ -15374,5 +15396,14 @@ virNetServerProgramProc remoteProcs[] = true, 1 }, +{ /* Method DomainGuestAgentInfo => 276 */ + remoteDispatchDomainGuestAgentInfoHelper, + sizeof(remote_domain_guest_agent_info_args), + (xdrproc_t)xdr_remote_guest_agent_info_args, + sizeof(remote_domain_guest_agent_info_ret), + (xdrproc_t)remote_domain_guest_agent_info_ret, + true, + 0 +}, }; size_t remoteNProcs = ARRAY_CARDINALITY(remoteProcs); diff -uNrp libvirt-0.9.13.orig/include/libvirt/libvirt.h.in libvirt-0.9.13/include/libvirt/libvirt.h.in --- libvirt-0.9.13.orig/include/libvirt/libvirt.h.in 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/include/libvirt/libvirt.h.in 2012-06-26 11:38:08.694455103 +0900 @@ -4128,6 +4128,11 @@ typedef struct _virTypedParameter virMem */ typedef virMemoryParameter *virMemoryParameterPtr; +/** + * Guest Agent Code: + */ +int virDomainGuestAgentInfo(virDomainPtr domain, char **buffer); + #ifdef __cplusplus } #endif diff -uNrp libvirt-0.9.13.orig/python/generator.py libvirt-0.9.13/python/generator.py --- libvirt-0.9.13.orig/python/generator.py 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/python/generator.py 2012-06-26 11:38:41.150580134 +0900 @@ -425,6 +425,7 @@ skip_impl = ( 'virDomainGetInterfaceParameters', 'virDomainGetCPUStats', 'virDomainGetDiskErrors', + 'virDomainGuestAgentInfo', ) qemu_skip_impl = ( diff -uNrp libvirt-0.9.13.orig/python/libvirt-override-api.xml libvirt-0.9.13/python/libvirt-override-api.xml --- libvirt-0.9.13.orig/python/libvirt-override-api.xml 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/python/libvirt-override-api.xml 2012-06-26 11:39:18.182492585 +0900 @@ -487,5 +487,10 @@ + + guestagent-info + + + diff -uNrp libvirt-0.9.13.orig/python/libvirt-override.c libvirt-0.9.13/python/libvirt-override.c --- libvirt-0.9.13.orig/python/libvirt-override.c 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/python/libvirt-override.c 2012-06-26 11:40:17.312579756 +0900 @@ -5758,6 +5758,36 @@ cleanup: return py_retval; } +static PyObject * +libvirt_virDomainGuestAgentInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) +{ + PyObject *py_retval; + int c_retval; + char *info; + virDomainPtr domain; + PyObject *pyobj_domain; + + if (!PyArg_ParseTuple(args, (char *)"Oz:virDomainGuestAgentInfo", &pyobj_domain, &info)) { +#if DEBUG_ERROR + printf("%s failed to parse tuple\n", __FUNCTION__); +#endif + return(NULL); + } + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virDomainGuestAgentInfo(domain, &info); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + py_retval = PyList_New(1); + PyList_SetItem(py_retval, 0, libvirt_constcharPtrWrap(info)); + + if (info) free(info); + return(py_retval); +} + /************************************************************************ * * * The registration stuff * @@ -5865,6 +5895,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainBlockPeek", libvirt_virDomainBlockPeek, METH_VARARGS, NULL}, {(char *) "virDomainMemoryPeek", libvirt_virDomainMemoryPeek, METH_VARARGS, NULL}, {(char *) "virDomainGetDiskErrors", libvirt_virDomainGetDiskErrors, METH_VARARGS, NULL}, + {(char *) "virDomainGuestAgentInfo", libvirt_virDomainGuestAgentInfo, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; diff -uNrp libvirt-0.9.13.orig/src/driver.h libvirt-0.9.13/src/driver.h --- libvirt-0.9.13.orig/src/driver.h 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/driver.h 2012-06-26 11:42:25.618580083 +0900 @@ -860,6 +860,10 @@ typedef char * const char *uri, unsigned int flags); +typedef int + (*virDrvDomainGuestAgentInfo)(virDomainPtr dom, + char **buffer); + /** * _virDriver: * @@ -1041,6 +1045,7 @@ struct _virDriver { virDrvDomainGetDiskErrors domainGetDiskErrors; virDrvDomainSetMetadata domainSetMetadata; virDrvDomainGetMetadata domainGetMetadata; + virDrvDomainGuestAgentInfo domainGuestAgentInfo; }; typedef int diff -uNrp libvirt-0.9.13.orig/src/libvirt.c libvirt-0.9.13/src/libvirt.c --- libvirt-0.9.13.orig/src/libvirt.c 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/libvirt.c 2012-06-26 11:42:58.093454455 +0900 @@ -18973,3 +18973,38 @@ error: virDispatchError(dom->conn); return -1; } + +/** + * virDomainGuestAgentInfo: + * @domain: a domain object + * @buffer: returning Guest Agent information + * + * Provide a list of Guest Agent's support command. + * Returns 0 if succeeded, -1 in failing. + */ +int virDomainGuestAgentInfo(virDomainPtr domain, char **buffer) +{ + virConnectPtr conn; + int ret = -1; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return ret; + } + + conn = domain->conn; + + if (conn->driver->domainGuestAgentInfo) { + ret = conn->driver->domainGuestAgentInfo(domain, buffer); + if (ret < 0) + goto error; + return ret; + } + + virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virDispatchError(domain->conn); + return ret; +} diff -uNrp libvirt-0.9.13.orig/src/libvirt_public.syms libvirt-0.9.13/src/libvirt_public.syms --- libvirt-0.9.13.orig/src/libvirt_public.syms 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/libvirt_public.syms 2012-06-26 11:43:29.939455045 +0900 @@ -542,6 +542,7 @@ LIBVIRT_0.9.13 { virDomainSnapshotIsCurrent; virDomainSnapshotListAllChildren; virDomainSnapshotRef; + virDomainGuestAgentInfo; } LIBVIRT_0.9.11; # .... define new API here using predicted next version number .... diff -uNrp libvirt-0.9.13.orig/src/qemu/qemu_agent.c libvirt-0.9.13/src/qemu/qemu_agent.c --- libvirt-0.9.13.orig/src/qemu/qemu_agent.c 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/qemu/qemu_agent.c 2012-06-26 11:44:35.332580082 +0900 @@ -1410,3 +1410,74 @@ qemuAgentSuspend(qemuAgentPtr mon, virJSONValueFree(reply); return ret; } + +static int qemuMonitorJSONExtractInfo(virJSONValuePtr reply, char **buffer) +{ + virJSONValuePtr data, data2; + int ret = -1; + int i; + char *pin = NULL; + + pin = *buffer; + + if (!(data = virJSONValueObjectGet(reply, "return")) || + data->type != VIR_JSON_TYPE_OBJECT) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guestagent-info reply was missing return data")); + goto cleanup; + } + + if (!(data2 = virJSONValueObjectGet(data, "version")) || + data2->type != VIR_JSON_TYPE_STRING) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guestagent-info reply was not complete data(version)")); + goto cleanup; + } + sprintf(pin, "Version: %s\nCommands:", virJSONValueGetString(data2)); + pin = strchr(pin, '\0'); + + if (!(data2 = virJSONValueObjectGet(data, "supported_commands")) || + data2->type != VIR_JSON_TYPE_ARRAY) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guestagent-info reply was not complete data(commands)")); + goto cleanup; + } + + data = data2; + for (i = 0; i < virJSONValueArraySize(data); i++) { + bool value; + data2 = virJSONValueArrayGet(data, i); + if (!virJSONValueGetBoolean(virJSONValueObjectGet(data2, "enabled"), &value) && value) { + sprintf(pin, "\n\t%s", virJSONValueGetString(virJSONValueObjectGet(data2, "name"))); + pin = strchr(pin, '\0'); + } + } + + ret = 0; + +cleanup: + return ret; +} + +int qemuAgentInfo(qemuAgentPtr mon, char **buffer) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = qemuAgentMakeCommand("guest-info", NULL); + if (!cmd) + return ret; + + ret = qemuAgentCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuAgentCheckError(cmd, reply); + + if (ret == 0) + ret = qemuMonitorJSONExtractInfo(reply, buffer); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff -uNrp libvirt-0.9.13.orig/src/qemu/qemu_agent.h libvirt-0.9.13/src/qemu/qemu_agent.h --- libvirt-0.9.13.orig/src/qemu/qemu_agent.h 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/qemu/qemu_agent.h 2012-06-26 11:44:57.044580088 +0900 @@ -80,4 +80,6 @@ int qemuAgentFSThaw(qemuAgentPtr mon); int qemuAgentSuspend(qemuAgentPtr mon, unsigned int target); + +int qemuAgentInfo(qemuAgentPtr mon, char **buffer); #endif /* __QEMU_AGENT_H__ */ diff -uNrp libvirt-0.9.13.orig/src/qemu/qemu_driver.c libvirt-0.9.13/src/qemu/qemu_driver.c --- libvirt-0.9.13.orig/src/qemu/qemu_driver.c 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/qemu/qemu_driver.c 2012-06-26 11:46:19.600454553 +0900 @@ -13157,6 +13157,59 @@ qemuListAllDomains(virConnectPtr conn, return ret; } +static int qemuDomainGuestAgentInfo(virDomainPtr dom, char **buffer) +{ + int ret = -1; + + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + qemuDomainObjPrivatePtr priv; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + + if (priv->agentError) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("QEMU guest agent is not " + "available due to an error")); + goto cleanup; + } + + if (!priv->agent) { + qemuReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("QEMU guest agent is not configured")); + goto cleanup; + } + + qemuDomainObjEnterAgent(driver, vm); + ret = qemuAgentInfo(priv->agent, buffer); + qemuDomainObjExitAgent(driver, vm); + + VIR_DEBUG ("guestagent-info ret: '%d' domain: '%s' string: %s", ret, vm->def->name, *buffer); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + static virDriver qemuDriver = { .no = VIR_DRV_QEMU, .name = QEMU_DRIVER_NAME, @@ -13322,6 +13375,7 @@ static virDriver qemuDriver = { .domainPMSuspendForDuration = qemuDomainPMSuspendForDuration, /* 0.9.11 */ .domainPMWakeup = qemuDomainPMWakeup, /* 0.9.11 */ .domainGetCPUStats = qemuDomainGetCPUStats, /* 0.9.11 */ + .domainGuestAgentInfo = qemuDomainGuestAgentInfo, /* 0.9.13 */ }; diff -uNrp libvirt-0.9.13.orig/src/remote/remote_driver.c libvirt-0.9.13/src/remote/remote_driver.c --- libvirt-0.9.13.orig/src/remote/remote_driver.c 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/remote/remote_driver.c 2012-06-26 11:47:41.600579693 +0900 @@ -5127,6 +5127,34 @@ make_nonnull_domain_snapshot (remote_non make_nonnull_domain(&snapshot_dst->dom, snapshot_src->domain); } +static int +remoteDomainGuestAgentInfo(virDomainPtr domain, char **buffer) +{ + int rv = -1; + struct private_data *priv = domain->conn->privateData; + remote_domain_guest_agent_info_args args; + remote_domain_guest_agent_info_ret ret; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, domain); + + memset (&ret, 0, sizeof ret); + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GUEST_AGENT_INFO, + (xdrproc_t) xdr_remote_domain_guest_agent_info_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_guest_agent_info_ret, (char *) &ret) == -1) + goto done; + *buffer = strdup(ret.buffer.buffer_val); + rv = 0; + + VIR_FREE(ret.buffer.buffer_val); + +done: + remoteDriverUnlock(priv); + return rv; +} + /*----------------------------------------------------------------------*/ unsigned long remoteVersion(void) @@ -5303,6 +5331,7 @@ static virDriver remote_driver = { .domainGetDiskErrors = remoteDomainGetDiskErrors, /* 0.9.10 */ .domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */ .domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */ + .domainGuestAgentInfo = remoteDomainGuestAgentInfo, /* 0.9.13 */ }; static virNetworkDriver network_driver = { diff -uNrp libvirt-0.9.13.orig/src/remote/remote_protocol.x libvirt-0.9.13/src/remote/remote_protocol.x --- libvirt-0.9.13.orig/src/remote/remote_protocol.x 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/remote/remote_protocol.x 2012-06-26 11:50:00.761455124 +0900 @@ -2513,6 +2513,16 @@ struct remote_connect_list_all_domains_r unsigned int ret; }; +const REMOTE_DOMAIN_GA_INFO_BUFFER_MAX = 1024; + +struct remote_domain_guest_agent_info_args { + remote_nonnull_domain dom; +}; + +struct remote_domain_guest_agent_info_ret { + opaque buffer; +}; + /*----- Protocol. -----*/ @@ -2838,7 +2848,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_HAS_METADATA = 272, /* autogen autogen */ REMOTE_PROC_CONNECT_LIST_ALL_DOMAINS = 273, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_LIST_ALL_SNAPSHOTS = 274, /* skipgen skipgen priority:high */ - REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275 /* skipgen skipgen priority:high */ + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, /* skipgen skipgen priority:high */ + REMOTE_PROC_DOMAIN_GUEST_AGENT_INFO = 276 /* skipgen skipgen */ /* * Notice how the entries are grouped in sets of 10 ? diff -uNrp libvirt-0.9.13.orig/tools/virsh.c libvirt-0.9.13/tools/virsh.c --- libvirt-0.9.13.orig/tools/virsh.c 2012-06-25 19:07:41.000000000 +0900 +++ libvirt-0.9.13/tools/virsh.c 2012-06-26 11:52:42.751454461 +0900 @@ -160,6 +160,7 @@ typedef enum { #define VSH_CMD_GRP_SNAPSHOT "Snapshot" #define VSH_CMD_GRP_HOST_AND_HV "Host and Hypervisor" #define VSH_CMD_GRP_VIRSH "Virsh itself" +#define VSH_CMD_GRP_GUESTAGENT "QEMU Guest Agent" /* * Command Option Flags @@ -17947,6 +17948,44 @@ cleanup: return ret; } +/* + * "guest-agent-info" command + */ +static const vshCmdInfo info_guest_agent_info[] = { + {"help", N_("Guest Agent Info")}, + {"desc", N_("Guest Agent Info")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_guest_agent_info[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdGuestAgentInfo(vshControl *ctl, const vshCmd *cmd) +{ + int ret = -1; + virDomainPtr dom; + char *info = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL, VSH_BYNAME|VSH_BYUUID))) + return false; + + if ((ret = virDomainGuestAgentInfo(dom, &info)) < 0 || !info) { + vshError(ctl, "%s", _("Failed to set guest-agent-info")); + } else { + vshPrint(ctl, "%s\n", info); + } + + if (info) VIR_FREE(info); + virDomainFree(dom); + return ret < 0 ? false : true; +} + static const vshCmdDef domManagementCmds[] = { {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device, 0}, @@ -18261,6 +18300,11 @@ static const vshCmdDef hostAndHypervisor {NULL, NULL, NULL, NULL, 0} }; +static const vshCmdDef guestagentCmds[] = { + {"guest-agent-info", cmdGuestAgentInfo, opts_guest_agent_info, info_guest_agent_info, 0}, + {NULL, NULL, NULL, NULL, 0} +}; + static const vshCmdGrp cmdGroups[] = { {VSH_CMD_GRP_DOM_MANAGEMENT, "domain", domManagementCmds}, {VSH_CMD_GRP_DOM_MONITORING, "monitor", domMonitoringCmds}, @@ -18273,6 +18317,7 @@ static const vshCmdGrp cmdGroups[] = { {VSH_CMD_GRP_SNAPSHOT, "snapshot", snapshotCmds}, {VSH_CMD_GRP_STORAGE_POOL, "pool", storagePoolCmds}, {VSH_CMD_GRP_STORAGE_VOL, "volume", storageVolCmds}, + {VSH_CMD_GRP_GUESTAGENT, "guestagent", guestagentCmds}, {VSH_CMD_GRP_VIRSH, "virsh", virshCmds}, {NULL, NULL, NULL} };