[libvirt] [PATCH v3 0/5] Guest filesystem information API

Hi, This is v3 of patchset to add virDomainGetFSInfo API. * changes in v2->v3: -[all] squashed fixup patches by John Ferlan (thanks!) -[all] added 'ndevAlias' filed instead of NULL-termination of devAlias -[all] renamed 'type' field to 'fstype' to avoid wireshark genxdrstub.pl error -[2/5] use @acl: domain:fs_freeze (v2: http://www.redhat.com/archives/libvir-list/2014-November/msg00559.html ) * changes in v1->v2: -[all] removed redundant NULL element at the last of returned info array -[3/5] make error messages in qemu_agent.c consistent with other commands -[4/5] added a test case for 2 items in info->devAliases -[5/5] added a pod document for virsh domfsinfo command (v1: http://www.redhat.com/archives/libvir-list/2014-October/msg00001.html ) * summary This series implements a new virDomainGetFSInfo API, that returns a list of mounted filesystems information in the guest, collected via the guest agent. The returned info contains mountpoints and disk device alias named in libvirt, so we can know which mountpoints should be frozen by virDomainFSFreeze to take snapshots of a part of disks. --- Tomoki Sekiyama (5): Implement public API for virDomainGetFSInfo remote: Implement the remote protocol for virDomainGetFSInfo qemu: Implement the qemu driver for virDomainGetFSInfo qemu: add test for qemuAgentGetFSInfo virsh: expose virDomainGetFSInfo daemon/remote.c | 113 +++++++++++++++++++ include/libvirt/libvirt-domain.h | 22 ++++ src/conf/domain_conf.c | 71 ++++++++++++ src/conf/domain_conf.h | 6 + src/driver-hypervisor.h | 6 + src/libvirt.c | 68 ++++++++++++ src/libvirt_private.syms | 1 src/libvirt_public.syms | 6 + src/qemu/qemu_agent.c | 176 ++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 2 src/qemu/qemu_driver.c | 48 ++++++++ src/remote/remote_driver.c | 93 ++++++++++++++++ src/remote/remote_protocol.x | 32 +++++ src/remote_protocol-structs | 21 ++++ src/rpc/gendispatch.pl | 1 tests/Makefile.am | 1 tests/qemuagentdata/qemuagent-fsinfo.xml | 39 +++++++ tests/qemuagenttest.c | 144 +++++++++++++++++++++++++ tools/virsh-domain.c | 70 ++++++++++++ tools/virsh.pod | 9 ++ 20 files changed, 928 insertions(+), 1 deletion(-) create mode 100644 tests/qemuagentdata/qemuagent-fsinfo.xml -- Tomoki Sekiyama

virDomainGetFSInfo returns a list of filesystems information mounted in the guest, which contains mountpoints, device names, filesystem types, and device aliases named by libvirt. This will be useful, for example, to specify mountpoints to fsfreeze when taking snapshot of a part of disks. Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com> --- include/libvirt/libvirt-domain.h | 22 ++++++++++++ src/driver-hypervisor.h | 6 +++ src/libvirt.c | 68 ++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 +++ 4 files changed, 102 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 1fac2a3..a273280 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3456,6 +3456,28 @@ int virDomainFSThaw(virDomainPtr dom, unsigned int nmountpoints, unsigned int flags); +/** + * virDomainFSInfo: + * + * The data structure containing mounted file systems within a guset + * + */ +typedef struct _virDomainFSInfo virDomainFSInfo; +typedef virDomainFSInfo *virDomainFSInfoPtr; +struct _virDomainFSInfo { + char *mountpoint; /* path to mount point */ + char *name; /* device name in the guest (e.g. "sda1") */ + char *fstype; /* filesystem type */ + size_t ndevAlias; /* number of elements in devAlias */ + char **devAlias; /* array of disk device aliases */ +}; + +void virDomainFSInfoFree(virDomainFSInfoPtr info); + +int virDomainGetFSInfo(virDomainPtr dom, + virDomainFSInfoPtr **info, + unsigned int flags); + int virDomainGetTime(virDomainPtr dom, long long *seconds, unsigned int *nseconds, diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index ad66629..9f26b13 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1139,6 +1139,11 @@ typedef int unsigned int flags); typedef int +(*virDrvDomainGetFSInfo)(virDomainPtr dom, + virDomainFSInfoPtr **info, + unsigned int flags); + +typedef int (*virDrvNodeGetFreePages)(virConnectPtr conn, unsigned int npages, unsigned int *pages, @@ -1390,6 +1395,7 @@ struct _virHypervisorDriver { virDrvConnectGetDomainCapabilities connectGetDomainCapabilities; virDrvConnectGetAllDomainStats connectGetAllDomainStats; virDrvNodeAllocPages nodeAllocPages; + virDrvDomainGetFSInfo domainGetFSInfo; }; diff --git a/src/libvirt.c b/src/libvirt.c index 3abedb4..86b0daa 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -1401,6 +1401,74 @@ virConnectOpenAuth(const char *name, /** + * virDomainGetFSInfo: + * @dom: a domain object + * @info: a pointer to a variable to store an array of mount points information + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Get a list of mapping information for each mounted file systems within the + * specified guest and the disks. + * + * Returns the number of returned mount points, or -1 in case of error. + * On success, the array of the information is stored into @info. The caller is + * responsible for calling virDomainFSInfoFree() on each array element, then + * calling free() on @info. On error, @info is set to NULL. + */ +int +virDomainGetFSInfo(virDomainPtr dom, + virDomainFSInfoPtr **info, + unsigned int flags) +{ + VIR_DOMAIN_DEBUG(dom, "info=%p, flags=%x", info, flags); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + virCheckReadOnlyGoto(dom->conn->flags, error); + virCheckNonNullArgGoto(info, error); + *info = NULL; + + if (dom->conn->driver->domainGetFSInfo) { + int ret = dom->conn->driver->domainGetFSInfo(dom, info, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} + + +/** + * virDomainFSInfoFree: + * @info: pointer to a FSInfo object + * + * Frees all the memory occupied by @info. + */ +void +virDomainFSInfoFree(virDomainFSInfoPtr info) +{ + size_t i; + + if (!info) + return; + + VIR_FREE(info->mountpoint); + VIR_FREE(info->name); + VIR_FREE(info->fstype); + + for (i = 0; i < info->ndevAlias; i++) + VIR_FREE(info->devAlias[i]); + VIR_FREE(info->devAlias); +} + + + +/** * virConnectClose: * @conn: pointer to the hypervisor connection * diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 5f95802..e4c2df1 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -684,4 +684,10 @@ LIBVIRT_1.2.9 { virNodeAllocPages; } LIBVIRT_1.2.8; +LIBVIRT_1.2.11 { + global: + virDomainFSInfoFree; + virDomainGetFSInfo; +} LIBVIRT_1.2.9; + # .... define new API here using predicted next version number ....

Add daemon and driver code to (de-)serialize virDomainFSInfo. Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com> --- daemon/remote.c | 113 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 93 +++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 32 ++++++++++++ src/remote_protocol-structs | 21 ++++++++ src/rpc/gendispatch.pl | 1 5 files changed, 259 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 1d7082e..e50fced 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6336,6 +6336,119 @@ remoteDispatchNodeAllocPages(virNetServerPtr server ATTRIBUTE_UNUSED, } +static int +remoteDispatchDomainGetFSInfo(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_fsinfo_args *args, + remote_domain_get_fsinfo_ret *ret) +{ + int rv = -1; + size_t i, j; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + virDomainFSInfoPtr *info = NULL; + virDomainPtr dom = NULL; + remote_domain_fsinfo *dst; + int ninfo = 0, ndisk; + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if ((ninfo = virDomainGetFSInfo(dom, &info, args->flags)) < 0) + goto cleanup; + + if (ninfo > REMOTE_DOMAIN_FSINFO_MAX) { + virReportError(VIR_ERR_RPC, + _("Too many mountpoints in fsinfo: %d for limit %d"), + ninfo, REMOTE_DOMAIN_FSINFO_MAX); + goto cleanup; + } + + if (ninfo) { + if (VIR_ALLOC_N(ret->info.info_val, ninfo) < 0) + goto cleanup; + + ret->info.info_len = ninfo; + + for (i = 0; i < ninfo; i++) { + dst = &ret->info.info_val[i]; + if (VIR_STRDUP(dst->mountpoint, info[i]->mountpoint) < 0) + goto cleanup; + + if (VIR_STRDUP(dst->name, info[i]->name) < 0) + goto cleanup; + + if (VIR_STRDUP(dst->fstype, info[i]->fstype) < 0) + goto cleanup; + + ndisk = info[i]->ndevAlias; + if (ndisk > REMOTE_DOMAIN_FSINFO_DISKS_MAX) { + virReportError(VIR_ERR_RPC, + _("Too many disks in fsinfo: %d for limit %d"), + ndisk, REMOTE_DOMAIN_FSINFO_DISKS_MAX); + goto cleanup; + } + + if (ndisk > 0) { + if (VIR_ALLOC_N(dst->dev_aliases.dev_aliases_val, ndisk) < 0) + goto cleanup; + + for (j = 0; j < ndisk; j++) { + if (VIR_STRDUP(dst->dev_aliases.dev_aliases_val[j], + info[i]->devAlias[j]) < 0) + goto cleanup; + } + + dst->dev_aliases.dev_aliases_len = ndisk; + } else { + dst->dev_aliases.dev_aliases_val = NULL; + dst->dev_aliases.dev_aliases_len = 0; + } + } + + } else { + ret->info.info_len = 0; + ret->info.info_val = NULL; + } + + ret->ret = ninfo; + + rv = 0; + + cleanup: + if (rv < 0) { + virNetMessageSaveError(rerr); + + if (ret->info.info_val && ninfo > 0) { + for (i = 0; i < ninfo; i++) { + dst = &ret->info.info_val[i]; + VIR_FREE(dst->mountpoint); + if (dst->dev_aliases.dev_aliases_val) { + for (j = 0; j < dst->dev_aliases.dev_aliases_len; j++) + VIR_FREE(dst->dev_aliases.dev_aliases_val[j]); + VIR_FREE(dst->dev_aliases.dev_aliases_val); + } + } + VIR_FREE(ret->info.info_val); + } + } + if (dom) + virDomainFree(dom); + if (ninfo >= 0) + for (i = 0; i < ninfo; i++) + virDomainFSInfoFree(info[i]); + VIR_FREE(info); + + return rv; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 04e5360..dc309cf 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7829,6 +7829,98 @@ remoteNodeAllocPages(virConnectPtr conn, } +static int +remoteDomainGetFSInfo(virDomainPtr dom, + virDomainFSInfoPtr **info, + unsigned int flags) +{ + int rv = -1; + size_t i, j, len; + struct private_data *priv = dom->conn->privateData; + remote_domain_get_fsinfo_args args; + remote_domain_get_fsinfo_ret ret; + remote_domain_fsinfo *src; + virDomainFSInfoPtr *info_ret = NULL; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, dom); + + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + + if (call(dom->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_FSINFO, + (xdrproc_t)xdr_remote_domain_get_fsinfo_args, (char *)&args, + (xdrproc_t)xdr_remote_domain_get_fsinfo_ret, (char *)&ret) == -1) + goto done; + + if (ret.info.info_len > REMOTE_DOMAIN_FSINFO_MAX) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Too many mountpoints in fsinfo: %d for limit %d"), + ret.info.info_len, REMOTE_DOMAIN_FSINFO_MAX); + goto cleanup; + } + + if (info) { + if (!ret.info.info_len) { + *info = NULL; + rv = ret.ret; + goto cleanup; + } + + if (VIR_ALLOC_N(info_ret, ret.info.info_len) < 0) + goto cleanup; + + for (i = 0; i < ret.info.info_len; i++) { + src = &ret.info.info_val[i]; + + if (VIR_ALLOC(info_ret[i]) < 0) + goto cleanup; + + if (VIR_STRDUP(info_ret[i]->mountpoint, src->mountpoint) < 0) + goto cleanup; + + if (VIR_STRDUP(info_ret[i]->name, src->name) < 0) + goto cleanup; + + if (VIR_STRDUP(info_ret[i]->fstype, src->fstype) < 0) + goto cleanup; + + len = src->dev_aliases.dev_aliases_len; + info_ret[i]->ndevAlias = len; + if (len && + VIR_ALLOC_N(info_ret[i]->devAlias, len) < 0) + goto cleanup; + + for (j = 0; j < len; j++) { + if (VIR_STRDUP(info_ret[i]->devAlias[j], + src->dev_aliases.dev_aliases_val[j]) < 0) + goto cleanup; + } + } + + *info = info_ret; + info_ret = NULL; + } + + rv = ret.ret; + + cleanup: + if (info_ret) { + for (i = 0; i < ret.info.info_len; i++) + virDomainFSInfoFree(info_ret[i]); + VIR_FREE(info_ret); + } + xdr_free((xdrproc_t)xdr_remote_domain_get_fsinfo_ret, + (char *) &ret); + + done: + remoteDriverUnlock(priv); + return rv; +} + + /* get_nonnull_domain and get_nonnull_network turn an on-wire * (name, uuid) pair into virDomainPtr or virNetworkPtr object. * These can return NULL if underlying memory allocations fail, @@ -8171,6 +8263,7 @@ static virHypervisorDriver hypervisor_driver = { .connectGetDomainCapabilities = remoteConnectGetDomainCapabilities, /* 1.2.7 */ .connectGetAllDomainStats = remoteConnectGetAllDomainStats, /* 1.2.8 */ .nodeAllocPages = remoteNodeAllocPages, /* 1.2.9 */ + .domainGetFSInfo = remoteDomainGetFSInfo, /* 1.2.11 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ebf4530..6b22377 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -250,6 +250,12 @@ const REMOTE_CONNECT_GET_ALL_DOMAIN_STATS_MAX = 4096; /* Upper limit of message size for tunable event. */ const REMOTE_DOMAIN_EVENT_TUNABLE_MAX = 2048; +/* Upper limit on number of mountpoints in fsinfo */ +const REMOTE_DOMAIN_FSINFO_MAX = 256; + +/* Upper limit on number of disks per mountpoint in fsinfo */ +const REMOTE_DOMAIN_FSINFO_DISKS_MAX = 256; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -3111,6 +3117,24 @@ struct remote_connect_get_all_domain_stats_args { struct remote_connect_get_all_domain_stats_ret { remote_domain_stats_record retStats<REMOTE_DOMAIN_LIST_MAX>; }; + +struct remote_domain_fsinfo { + remote_nonnull_string mountpoint; + remote_nonnull_string name; + remote_nonnull_string fstype; + remote_nonnull_string dev_aliases<REMOTE_DOMAIN_FSINFO_DISKS_MAX>; /* (const char **) */ +}; + +struct remote_domain_get_fsinfo_args { + remote_nonnull_domain dom; + unsigned int flags; +}; + +struct remote_domain_get_fsinfo_ret { + remote_domain_fsinfo info<REMOTE_DOMAIN_FSINFO_MAX>; + unsigned int ret; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -5506,5 +5530,11 @@ enum remote_procedure { * @generate: none * @acl: connect:write */ - REMOTE_PROC_NODE_ALLOC_PAGES = 347 + REMOTE_PROC_NODE_ALLOC_PAGES = 347, + + /** + * @generate: none + * @acl: domain:fs_freeze + */ + REMOTE_PROC_DOMAIN_GET_FSINFO = 348 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 362baf9..ec66a46 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2579,6 +2579,26 @@ struct remote_connect_get_all_domain_stats_ret { remote_domain_stats_record * retStats_val; } retStats; }; +struct remote_domain_fsinfo { + remote_nonnull_string mountpoint; + remote_nonnull_string name; + remote_nonnull_string fstype; + struct { + u_int dev_aliases_len; + remote_nonnull_string * dev_aliases_val; + } dev_aliases; +}; +struct remote_domain_get_fsinfo_args { + remote_nonnull_domain dom; + u_int flags; +}; +struct remote_domain_get_fsinfo_ret { + struct { + u_int info_len; + remote_domain_fsinfo * info_val; + } info; + u_int ret; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -2927,4 +2947,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_BLOCK_COPY = 345, REMOTE_PROC_DOMAIN_EVENT_CALLBACK_TUNABLE = 346, REMOTE_PROC_NODE_ALLOC_PAGES = 347, + REMOTE_PROC_DOMAIN_GET_FSINFO = 348, }; diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index b38d5bb..80f35b3 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -66,6 +66,7 @@ sub fixup_name { $name =~ s/Fstrim$/FSTrim/; $name =~ s/Fsfreeze$/FSFreeze/; $name =~ s/Fsthaw$/FSThaw/; + $name =~ s/Fsinfo$/FSInfo/; $name =~ s/Scsi/SCSI/; $name =~ s/Wwn$/WWN/; $name =~ s/Dhcp$/DHCP/;

Get mounted filesystems list, which contains hardware info of disks and its controllers, from QEMU guest agent 2.2+. Then, convert the hardware info to corresponding device aliases for the disks. Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com> --- src/conf/domain_conf.c | 71 +++++++++++++++++++ src/conf/domain_conf.h | 6 ++ src/libvirt_private.syms | 1 src/qemu/qemu_agent.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 2 + src/qemu/qemu_driver.c | 48 +++++++++++++ 6 files changed, 304 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f1c2f5f..68eef54 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11172,6 +11172,60 @@ virDomainHostdevFind(virDomainDefPtr def, return *found ? i : -1; } +static bool +virDomainDiskControllerMatch(int controller_type, int disk_bus) +{ + if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI && + disk_bus == VIR_DOMAIN_DISK_BUS_SCSI) + return true; + + if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_FDC && + disk_bus == VIR_DOMAIN_DISK_BUS_FDC) + return true; + + if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && + disk_bus == VIR_DOMAIN_DISK_BUS_IDE) + return true; + + if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_SATA && + disk_bus == VIR_DOMAIN_DISK_BUS_SATA) + return true; + + return false; +} + +int +virDomainDiskIndexByAddress(virDomainDefPtr def, + virDevicePCIAddressPtr pci_address, + unsigned int bus, unsigned int target, + unsigned int unit) +{ + virDomainDiskDefPtr vdisk; + virDomainControllerDefPtr controller = NULL; + size_t i; + int cidx; + + if ((cidx = virDomainControllerFindByPCIAddress(def, pci_address)) >= 0) + controller = def->controllers[cidx]; + + for (i = 0; i < def->ndisks; i++) { + vdisk = def->disks[i]; + if (vdisk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && + virDevicePCIAddressEqual(&vdisk->info.addr.pci, pci_address)) + return i; + if (vdisk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { + virDomainDeviceDriveAddressPtr drive = &vdisk->info.addr.drive; + if (controller && + virDomainDiskControllerMatch(controller->type, vdisk->bus) && + drive->controller == controller->idx && + drive->bus == bus && drive->target == target && + drive->unit == unit) + return i; + } + } + return -1; +} + int virDomainDiskIndexByName(virDomainDefPtr def, const char *name, bool allow_ambiguous) @@ -11461,6 +11515,23 @@ virDomainControllerFind(virDomainDefPtr def, return -1; } +int +virDomainControllerFindByPCIAddress(virDomainDefPtr def, + virDevicePCIAddressPtr addr) +{ + size_t i; + + for (i = 0; i < def->ncontrollers; i++) { + virDomainDeviceInfoPtr info = &def->controllers[i]->info; + + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && + virDevicePCIAddressEqual(&info->addr.pci, addr)) + return i; + } + + return -1; +} + virDomainControllerDefPtr virDomainControllerRemove(virDomainDefPtr def, size_t i) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 2dab1a4..0a609df 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2476,6 +2476,10 @@ int virDomainEmulatorPinDel(virDomainDefPtr def); void virDomainRNGDefFree(virDomainRNGDefPtr def); +int virDomainDiskIndexByAddress(virDomainDefPtr def, + virDevicePCIAddressPtr pci_controller, + unsigned int bus, unsigned int target, + unsigned int unit); int virDomainDiskIndexByName(virDomainDefPtr def, const char *name, bool allow_ambiguous); const char *virDomainDiskPathByName(virDomainDefPtr, const char *name); @@ -2545,6 +2549,8 @@ int virDomainControllerInsert(virDomainDefPtr def, void virDomainControllerInsertPreAlloced(virDomainDefPtr def, virDomainControllerDefPtr controller); int virDomainControllerFind(virDomainDefPtr def, int type, int idx); +int virDomainControllerFindByPCIAddress(virDomainDefPtr def, + virDevicePCIAddressPtr addr); virDomainControllerDefPtr virDomainControllerRemove(virDomainDefPtr def, size_t i); int virDomainLeaseIndex(virDomainDefPtr def, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index daf4dd7..9a0a9f7 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -234,6 +234,7 @@ virDomainDiskGetDriver; virDomainDiskGetFormat; virDomainDiskGetSource; virDomainDiskGetType; +virDomainDiskIndexByAddress; virDomainDiskIndexByName; virDomainDiskInsert; virDomainDiskInsertPreAlloced; diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 8df1330..5fcc40f 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1777,3 +1777,179 @@ qemuAgentSetTime(qemuAgentPtr mon, virJSONValueFree(reply); return ret; } + + +int +qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info, + virDomainDefPtr vmdef) +{ + size_t i, j, k; + int ret = -1; + int ndata = 0, ndisk; + char **alias; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + virJSONValuePtr data; + virDomainFSInfoPtr *info_ret = NULL; + virDevicePCIAddress pci_address; + + cmd = qemuAgentMakeCommand("guest-get-fsinfo", NULL); + if (!cmd) + return ret; + + if (qemuAgentCommand(mon, cmd, &reply, true, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + goto cleanup; + + if (!(data = virJSONValueObjectGet(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest-get-fsinfo reply was missing return data")); + goto cleanup; + } + + if (data->type != VIR_JSON_TYPE_ARRAY) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest-get-fsinfo return information was not " + "an array")); + goto cleanup; + } + + ndata = virJSONValueArraySize(data); + if (!ndata) { + ret = 0; + goto cleanup; + } + if (VIR_ALLOC_N(info_ret, ndata) < 0) + goto cleanup; + + for (i = 0; i < ndata; i++) { + /* Reverse the order to arrange in mount order */ + virJSONValuePtr entry = virJSONValueArrayGet(data, ndata - 1 - i); + + if (!entry) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("array element '%zd' of '%d' missing in " + "guest-get-fsinfo return data"), + i, ndata); + goto cleanup; + } + + if (VIR_ALLOC(info_ret[i]) < 0) + goto cleanup; + + if (VIR_STRDUP(info_ret[i]->mountpoint, + virJSONValueObjectGetString(entry, "mountpoint")) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'mountpoint' missing in reply of " + "guest-get-fsinfo")); + goto cleanup; + } + + if (VIR_STRDUP(info_ret[i]->name, + virJSONValueObjectGetString(entry, "name")) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'name' missing in reply of guest-get-fsinfo")); + goto cleanup; + } + + if (VIR_STRDUP(info_ret[i]->fstype, + virJSONValueObjectGetString(entry, "type")) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'type' missing in reply of guest-get-fsinfo")); + goto cleanup; + } + + if (!(entry = virJSONValueObjectGet(entry, "disk"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'disk' missing in reply of guest-get-fsinfo")); + goto cleanup; + } + + if (entry->type != VIR_JSON_TYPE_ARRAY) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest-get-fsinfo 'disk' data was not an array")); + goto cleanup; + } + + ndisk = virJSONValueArraySize(entry); + if (!ndisk) + continue; + if (VIR_ALLOC_N(info_ret[i]->devAlias, ndisk) < 0) + goto cleanup; + + alias = info_ret[i]->devAlias; + info_ret[i]->ndevAlias = 0; + for (j = 0; j < ndisk; j++) { + virJSONValuePtr disk = virJSONValueArrayGet(entry, j); + virJSONValuePtr pci; + int diskaddr[3], pciaddr[4], idx; + const char *diskaddr_comp[] = {"bus", "target", "unit"}; + const char *pciaddr_comp[] = {"domain", "bus", "slot", "function"}; + + if (!disk) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("array element '%zd' of '%d' missing in " + "guest-get-fsinfo 'disk' data"), + j, ndisk); + goto cleanup; + } + + if (!(pci = virJSONValueObjectGet(disk, "pci-controller"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'pci-controller' missing in guest-get-fsinfo " + "'disk' data")); + goto cleanup; + } + + for (k = 0; k < 3; k++) { + if (virJSONValueObjectGetNumberInt( + disk, diskaddr_comp[k], &diskaddr[k]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("'%s' missing in guest-get-fsinfo " + "'disk' data"), diskaddr_comp[k]); + goto cleanup; + } + } + for (k = 0; k < 4; k++) { + if (virJSONValueObjectGetNumberInt( + pci, pciaddr_comp[k], &pciaddr[k]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("'%s' missing in guest-get-fsinfo " + "'pci-address' data"), pciaddr_comp[k]); + goto cleanup; + } + } + + pci_address.domain = pciaddr[0]; + pci_address.bus = pciaddr[1]; + pci_address.slot = pciaddr[2]; + pci_address.function = pciaddr[3]; + if ((idx = virDomainDiskIndexByAddress( + vmdef, &pci_address, + diskaddr[0], diskaddr[1], diskaddr[2])) < 0) + continue; + + if (VIR_STRDUP(*alias, vmdef->disks[idx]->dst) < 0) + goto cleanup; + + if (*alias) { + alias++; + info_ret[i]->ndevAlias++; + } + } + } + + *info = info_ret; + info_ret = NULL; + ret = ndata; + + cleanup: + if (info_ret) { + for (i = 0; i < ndata; i++) + virDomainFSInfoFree(info_ret[i]); + VIR_FREE(info_ret); + } + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h index 6cd6b49..c983828 100644 --- a/src/qemu/qemu_agent.h +++ b/src/qemu/qemu_agent.h @@ -73,6 +73,8 @@ int qemuAgentShutdown(qemuAgentPtr mon, int qemuAgentFSFreeze(qemuAgentPtr mon, const char **mountpoints, unsigned int nmountpoints); int qemuAgentFSThaw(qemuAgentPtr mon); +int qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info, + virDomainDefPtr vmdef); int qemuAgentSuspend(qemuAgentPtr mon, unsigned int target); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5936d6f..145b426 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -18826,6 +18826,53 @@ qemuNodeAllocPages(virConnectPtr conn, } +static int +qemuDomainGetFSInfo(virDomainPtr dom, + virDomainFSInfoPtr **info, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + qemuDomainObjPrivatePtr priv; + virDomainObjPtr vm; + int ret = -1; + + virCheckFlags(0, ret); + + if (!(vm = qemuDomObjFromDomain(dom))) + return ret; + + if (virDomainGetFSInfoEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + priv = vm->privateData; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + if (!qemuDomainAgentAvailable(priv, true)) + goto endjob; + + qemuDomainObjEnterAgent(vm); + ret = qemuAgentGetFSInfo(priv->agent, info, vm->def); + qemuDomainObjExitAgent(vm); + + endjob: + if (!qemuDomainObjEndJob(driver, vm)) + vm = NULL; + + cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + + static virHypervisorDriver qemuDriver = { .no = VIR_DRV_QEMU, .name = QEMU_DRIVER_NAME, @@ -19026,6 +19073,7 @@ static virHypervisorDriver qemuDriver = { .connectGetDomainCapabilities = qemuConnectGetDomainCapabilities, /* 1.2.7 */ .connectGetAllDomainStats = qemuConnectGetAllDomainStats, /* 1.2.8 */ .nodeAllocPages = qemuNodeAllocPages, /* 1.2.9 */ + .domainGetFSInfo = qemuDomainGetFSInfo, /* 1.2.11 */ };

Add test cases for qemuAgentGetFSInfo, with a sample agent response for the qemu-get-fsinfo command and a configuration xml. Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com> --- tests/Makefile.am | 1 tests/qemuagentdata/qemuagent-fsinfo.xml | 39 ++++++++ tests/qemuagenttest.c | 144 ++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 tests/qemuagentdata/qemuagent-fsinfo.xml diff --git a/tests/Makefile.am b/tests/Makefile.am index a3e3ab3..e9418ea 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -103,6 +103,7 @@ EXTRA_DIST = \ nwfilterxml2xmlin \ nwfilterxml2xmlout \ oomtrace.pl \ + qemuagentdata \ qemucapabilitiesdata \ qemucaps2xmldata \ qemuhelpdata \ diff --git a/tests/qemuagentdata/qemuagent-fsinfo.xml b/tests/qemuagentdata/qemuagent-fsinfo.xml new file mode 100644 index 0000000..9638feb --- /dev/null +++ b/tests/qemuagentdata/qemuagent-fsinfo.xml @@ -0,0 +1,39 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='file' device='disk'> + <source file='/tmp/idedisk.img'/> + <target dev='hdc' bus='ide'/> + <address type='drive' controller='0' bus='1' target='0' unit='0'/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/tmp/virtio-blk1.qcow2'/> + <target dev='vda' bus='virtio'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/tmp/virtio-blk2.qcow2'/> + <target dev='vdb' bus='virtio'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </disk> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuagenttest.c b/tests/qemuagenttest.c index bc649b4..eaf764f 100644 --- a/tests/qemuagenttest.c +++ b/tests/qemuagenttest.c @@ -164,6 +164,149 @@ testQemuAgentFSTrim(const void *data) static int +testQemuAgentGetFSInfo(const void *data) +{ + virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data; + virCapsPtr caps = testQemuCapsInit(); + qemuMonitorTestPtr test = qemuMonitorTestNewAgent(xmlopt); + char *domain_filename = NULL; + char *domain_xml = NULL; + virDomainDefPtr def = NULL; + virDomainFSInfoPtr *info = NULL; + int ret = -1, ninfo = 0, i; + + if (!test) + return -1; + + if (virAsprintf(&domain_filename, "%s/qemuagentdata/qemuagent-fsinfo.xml", + abs_srcdir) < 0) + goto cleanup; + + if (virtTestLoadFile(domain_filename, &domain_xml) < 0) + goto cleanup; + + if (!(def = virDomainDefParseString(domain_xml, caps, xmlopt, + QEMU_EXPECTED_VIRT_TYPES, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if (qemuMonitorTestAddAgentSyncResponse(test) < 0) + goto cleanup; + + if (qemuMonitorTestAddItem(test, "guest-get-fsinfo", + "{\"return\": [" + " {\"name\": \"sda1\", \"mountpoint\": \"/\"," + " \"disk\": [" + " {\"bus-type\": \"ide\"," + " \"bus\": 1, \"unit\": 0," + " \"pci-controller\": {" + " \"bus\": 0, \"slot\": 1," + " \"domain\": 0, \"function\": 1}," + " \"target\": 0}]," + " \"type\": \"ext4\"}," + " {\"name\": \"dm-1\"," + " \"mountpoint\": \"/opt\"," + " \"disk\": [" + " {\"bus-type\": \"virtio\"," + " \"bus\": 0, \"unit\": 0," + " \"pci-controller\": {" + " \"bus\": 0, \"slot\": 6," + " \"domain\": 0, \"function\": 0}," + " \"target\": 0}," + " {\"bus-type\": \"virtio\"," + " \"bus\": 0, \"unit\": 0," + " \"pci-controller\": {" + " \"bus\": 0, \"slot\": 7," + " \"domain\": 0, \"function\": 0}," + " \"target\": 0}]," + " \"type\": \"vfat\"}," + " {\"name\": \"sdb1\"," + " \"mountpoint\": \"/mnt/disk\"," + " \"disk\": [], \"type\": \"xfs\"}]}") < 0) + goto cleanup; + + if ((ninfo = qemuAgentGetFSInfo(qemuMonitorTestGetAgent(test), + &info, def)) < 0) + goto cleanup; + + if (ninfo != 3) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "expected 3 filesystems information, got %d", ninfo); + ret = -1; + goto cleanup; + } + if (STRNEQ(info[2]->name, "sda1") || + STRNEQ(info[2]->mountpoint, "/") || + STRNEQ(info[2]->fstype, "ext4") || + info[2]->ndevAlias != 1 || + !info[2]->devAlias || !info[2]->devAlias[0] || + STRNEQ(info[2]->devAlias[0], "hdc")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "unexpected filesystems information returned for sda1 (%s,%s)", + info[2]->name, info[2]->devAlias ? info[2]->devAlias[0] : "null"); + ret = -1; + goto cleanup; + } + if (STRNEQ(info[1]->name, "dm-1") || + STRNEQ(info[1]->mountpoint, "/opt") || + STRNEQ(info[1]->fstype, "vfat") || + info[1]->ndevAlias != 2 || + !info[1]->devAlias || !info[1]->devAlias[0] || !info[1]->devAlias[1] || + STRNEQ(info[1]->devAlias[0], "vda") || + STRNEQ(info[1]->devAlias[1], "vdb")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "unexpected filesystems information returned for dm-1 (%s,%s)", + info[0]->name, info[0]->devAlias ? info[0]->devAlias[0] : "null"); + ret = -1; + goto cleanup; + } + if (STRNEQ(info[0]->name, "sdb1") || + STRNEQ(info[0]->mountpoint, "/mnt/disk") || + STRNEQ(info[0]->fstype, "xfs") || + info[0]->ndevAlias != 0 || info[0]->devAlias) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "unexpected filesystems information returned for sdb1 (%s,%s)", + info[0]->name, info[0]->devAlias ? info[0]->devAlias[0] : "null"); + ret = -1; + goto cleanup; + } + + if (qemuMonitorTestAddAgentSyncResponse(test) < 0) + goto cleanup; + + if (qemuMonitorTestAddItem(test, "guest-get-fsinfo", + "{\"error\":" + " {\"class\":\"CommandDisabled\"," + " \"desc\":\"The command guest-get-fsinfo " + "has been disabled for " + "this instance\"," + " \"data\":{\"name\":\"guest-get-fsinfo\"}" + " }" + "}") < 0) + goto cleanup; + + if (qemuAgentGetFSInfo(qemuMonitorTestGetAgent(test), &info, def) != -1) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "agent get-fsinfo command should have failed"); + goto cleanup; + } + + ret = 0; + + cleanup: + for (i = 0; i < ninfo; i++) + virDomainFSInfoFree(info[i]); + VIR_FREE(info); + VIR_FREE(domain_filename); + VIR_FREE(domain_xml); + virObjectUnref(caps); + virDomainDefFree(def); + qemuMonitorTestFree(test); + return ret; +} + + +static int testQemuAgentSuspend(const void *data) { virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data; @@ -605,6 +748,7 @@ mymain(void) DO_TEST(FSFreeze); DO_TEST(FSThaw); DO_TEST(FSTrim); + DO_TEST(GetFSInfo); DO_TEST(Suspend); DO_TEST(Shutdown); DO_TEST(CPU);

Add a "domfsinfo" command that shows a list of filesystems info mounted in the guest. For example: virsh # domfsinfo vm1 Mountpoint Name Type Target ------------------------------------------------------------------- / sda1 ext4 hdc /opt dm-2 vfat vda,vdb /mnt/test sdb1 xfs sda Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com> --- tools/virsh-domain.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 9 ++++++ 2 files changed, 79 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 0572275..5feba79 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -12186,6 +12186,70 @@ cmdDomFSThaw(vshControl *ctl, const vshCmd *cmd) return ret >= 0; } +static const vshCmdInfo info_domfsinfo[] = { + {.name = "help", + .data = N_("Get information of domain's mounted filesystems.") + }, + {.name = "desc", + .data = N_("Get information of domain's mounted filesystems.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domfsinfo[] = { + {.name = "domain", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("domain name, id or uuid") + }, + {.name = NULL} +}; + +static bool +cmdDomFSInfo(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int ret = -1; + size_t i, j; + virDomainFSInfoPtr *info; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + ret = virDomainGetFSInfo(dom, &info, 0); + if (ret < 0) { + vshError(ctl, _("Unable to get filesystem information")); + goto cleanup; + } + if (ret == 0) { + vshError(ctl, _("No filesystems are mounted in the domain")); + goto cleanup; + } + + if (info) { + vshPrintExtra(ctl, "%-36s %-8s %-8s %s\n", + _("Mountpoint"), _("Name"), _("Type"), _("Target")); + vshPrintExtra(ctl, "-------------------------------------------------------------------\n"); + for (i = 0; i < ret; i++) { + vshPrintExtra(ctl, "%-36s %-8s %-8s ", + info[i]->mountpoint, info[i]->name, info[i]->fstype); + for (j = 0; j < info[i]->ndevAlias; j++) { + vshPrintExtra(ctl, "%s", info[i]->devAlias[j]); + if (j != info[i]->ndevAlias - 1) + vshPrint(ctl, ","); + } + vshPrint(ctl, "\n"); + + virDomainFSInfoFree(info[i]); + } + VIR_FREE(info); + } + + cleanup: + virDomainFree(dom); + return ret >= 0; +} + const vshCmdDef domManagementCmds[] = { {.name = "attach-device", .handler = cmdAttachDevice, @@ -12345,6 +12409,12 @@ const vshCmdDef domManagementCmds[] = { .info = info_domfsthaw, .flags = 0 }, + {.name = "domfsinfo", + .handler = cmdDomFSInfo, + .opts = opts_domfsinfo, + .info = info_domfsinfo, + .flags = 0 + }, {.name = "domfstrim", .handler = cmdDomFSTrim, .opts = opts_domfstrim, diff --git a/tools/virsh.pod b/tools/virsh.pod index 5a577f6..da9c894 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1146,6 +1146,15 @@ be selected using the B<type> parameter (e.g. "vnc", "spice", "rdp"). If I<--include-password> is specified, the SPICE channel password will be included in the URI. +=item B<domfsinfo> I<domain> + +Show a list of mounted filesystems within the running domain. The list contains +mountpoints, names of a mounted device in the guest, filesystem types, and +unique target names used in the domain XML (<target dev='name'/>). + +Note that this command requires a guest agent configured and running in the +domain's guest OS. + =item B<domfsfreeze> I<domain> [[I<--mountpoint>] B<mountpoint>...] Freeze mounted filesystems within a running domain to prepare for consistent

On 11/21/2014 08:27 PM, Tomoki Sekiyama wrote:
Hi,
This is v3 of patchset to add virDomainGetFSInfo API.
* changes in v2->v3: -[all] squashed fixup patches by John Ferlan (thanks!) -[all] added 'ndevAlias' filed instead of NULL-termination of devAlias -[all] renamed 'type' field to 'fstype' to avoid wireshark genxdrstub.pl error -[2/5] use @acl: domain:fs_freeze (v2: http://www.redhat.com/archives/libvir-list/2014-November/msg00559.html )
* changes in v1->v2: -[all] removed redundant NULL element at the last of returned info array -[3/5] make error messages in qemu_agent.c consistent with other commands -[4/5] added a test case for 2 items in info->devAliases -[5/5] added a pod document for virsh domfsinfo command (v1: http://www.redhat.com/archives/libvir-list/2014-October/msg00001.html )
* summary This series implements a new virDomainGetFSInfo API, that returns a list of mounted filesystems information in the guest, collected via the guest agent.
The returned info contains mountpoints and disk device alias named in libvirt, so we can know which mountpoints should be frozen by virDomainFSFreeze to take snapshots of a part of disks.
ACK series w/ 2 minor changes (which I made) - the 'ndisk' in daemon/remote.c should be a 'size_t' not an 'int' since it's a copy of the (new) ndevAlias (which is a size_t). - Use QEMU_JOB_QUERY not MODIFY since that's what was agreed upon during v2 review Also fixed up the (now) merge conflicts w/ the agent lifecycle series that got pushed before this... And pushed John
--- Tomoki Sekiyama (5): Implement public API for virDomainGetFSInfo remote: Implement the remote protocol for virDomainGetFSInfo qemu: Implement the qemu driver for virDomainGetFSInfo qemu: add test for qemuAgentGetFSInfo virsh: expose virDomainGetFSInfo
daemon/remote.c | 113 +++++++++++++++++++ include/libvirt/libvirt-domain.h | 22 ++++ src/conf/domain_conf.c | 71 ++++++++++++ src/conf/domain_conf.h | 6 + src/driver-hypervisor.h | 6 + src/libvirt.c | 68 ++++++++++++ src/libvirt_private.syms | 1 src/libvirt_public.syms | 6 + src/qemu/qemu_agent.c | 176 ++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 2 src/qemu/qemu_driver.c | 48 ++++++++ src/remote/remote_driver.c | 93 ++++++++++++++++ src/remote/remote_protocol.x | 32 +++++ src/remote_protocol-structs | 21 ++++ src/rpc/gendispatch.pl | 1 tests/Makefile.am | 1 tests/qemuagentdata/qemuagent-fsinfo.xml | 39 +++++++ tests/qemuagenttest.c | 144 +++++++++++++++++++++++++ tools/virsh-domain.c | 70 ++++++++++++ tools/virsh.pod | 9 ++ 20 files changed, 928 insertions(+), 1 deletion(-) create mode 100644 tests/qemuagentdata/qemuagent-fsinfo.xml
--
Tomoki Sekiyama
participants (2)
-
John Ferlan
-
Tomoki Sekiyama