[libvirt] New libvirt API for domain memory statistics reporting (V3)

Daniel, Daniel, and Matthias: Thanks for your review. This series addresses almost all of your most recent comments. I am still working on writing a decent implementation for the test driver but I wanted to get these updates out right away. Changes since V2: * Ported to GIT HEAD for easier merging :) * Amounts of memory are now reported in kilobytes * Added flags parameter to API (currently unused) * Moved to less awkward xdr wire format for remote driver * Stats 'Free' and 'Total' renamed to 'Unused' and 'Available' * Various small fixups: constant names, data types, etc Changes since V1: * New system for maintaining ABI compatibility and API extensibility: Rather than passing around a fixed-size stats structure, work with arrays of stats. An enum of known statistic tags (SWAP_IN, SWAP_OUT, TOTAL_MEM, etc) is defined. A stat is defined as a tag/value pair. When making a call to the API, the caller provides an array of stats and the size of the array. That array is filled with up to the requested number of stats (depending on hypervisor and guest support). The number of stats provided is returned. * Miscellaneous changes: Changed the API function from virDomainMemStats to virDomainMemoryStats Added documentation for each memory stat -- When using ballooning to manage overcommitted memory on a host, a system for guests to communicate their memory usage to the host can provide information that will minimize the impact of ballooning on the guests while maximizing efficient use of host memory. The design of such a communication channel was recently added to version 0.8.2 of the VirtIO Spec (See Appendix G): - http://ozlabs.org/~rusty/virtio-spec/virtio-spec-0.8.2.pdf Host-side and guest-side implementations have been accepted for inclusion in their respective projects: - Guest: http://lkml.org/lkml/2009/11/30/274 - Host: http://lists.gnu.org/archive/html/qemu-devel/2009-12/msg00380.html The following patch series implements a new libvirt interface to expose these memory statistics. Thank you for your review and comments. [PATCH 1/6] domMemoryStats: Add domainMemoryStats method to struct _virDriver [PATCH 2/6] domMemoryStats: Add public symbol to libvirt API [PATCH 3/6] qemu-driver: Enable domainMemStats in the qemu driver [PATCH 4/6] remote-driver: Add domainMemoryStats support [PATCH 5/6] virsh: Enable virDomainMemoryStats as a new command [PATCH 6/6] python: Add python bindings for virDomainMemoryStats

Set up the types for the domainMemoryStats function and insert it into the virDriver structure definition. Because of static initializers, update every driver and set the new field to NULL. Note: The changes in python/* are to fix compiler errors. The actual python binding is implemented in a later patch. Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- include/libvirt/libvirt.h.in | 53 ++++++++++++++++++++++++++++++++++++++++++ python/generator.py | 4 ++- src/driver.h | 7 +++++ src/esx/esx_driver.c | 1 + src/lxc/lxc_driver.c | 1 + src/opennebula/one_driver.c | 1 + src/openvz/openvz_driver.c | 1 + src/phyp/phyp_driver.c | 1 + src/qemu/qemu_driver.c | 1 + src/remote/remote_driver.c | 1 + src/test/test_driver.c | 1 + src/uml/uml_driver.c | 1 + src/vbox/vbox_tmpl.c | 1 + src/xen/xen_driver.c | 1 + 14 files changed, 74 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 6c3aded..17e3532 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -333,6 +333,55 @@ struct _virDomainInterfaceStats { */ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr; +/** + * Memory Statistics Tags: + */ +typedef enum { + /* The total amount of data read from swap space (in kB). */ + VIR_DOMAIN_MEMORY_STAT_SWAP_IN = 0, + /* The total amount of memory written out to swap space (in kB). */ + VIR_DOMAIN_MEMORY_STAT_SWAP_OUT = 1, + + /* + * Page faults occur when a process makes a valid access to virtual memory + * that is not available. When servicing the page fault, if disk IO is + * required, it is considered a major fault. If not, it is a minor fault. + * These are expressed as the number of faults that have occurred. + */ + VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT = 2, + VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT = 3, + + /* + * The amount of memory left completely unused by the system. Memory that + * is available but used for reclaimable caches should NOT be reported as + * free. This value is expressed in kB. + */ + VIR_DOMAIN_MEMORY_STAT_UNUSED = 4, + + /* + * The total amount of usable memory as seen by the domain. This value + * may be less than the amount of memory assigned to the domain if a + * balloon driver is in use or if the guest OS does not initialize all + * assigned pages. This value is expressed in kB. + */ + VIR_DOMAIN_MEMORY_STAT_AVAILABLE = 5, + + /* + * The number of statistics supported by this version of the interface. + * To add new statistics, add them to the enum and increase this value. + */ + VIR_DOMAIN_MEMORY_STAT_NR_STATS = 6, +} virDomainMemoryStatTags; + +typedef struct _virDomainMemoryStat virDomainMemoryStatStruct; + +struct _virDomainMemoryStat { + int tag; + unsigned long long val; +}; + +typedef virDomainMemoryStatStruct *virDomainMemoryStatPtr; + /* Domain core dump flags. */ typedef enum { @@ -644,6 +693,10 @@ int virDomainInterfaceStats (virDomainPtr dom, const char *path, virDomainInterfaceStatsPtr stats, size_t size); +int virDomainMemoryStats (virDomainPtr dom, + virDomainMemoryStatPtr stats, + unsigned int nr_stats, + unsigned int flags); int virDomainBlockPeek (virDomainPtr dom, const char *path, unsigned long long offset, diff --git a/python/generator.py b/python/generator.py index 3fd7f90..06f1ff4 100755 --- a/python/generator.py +++ b/python/generator.py @@ -160,7 +160,8 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ - "virConnectListDomains" + "virConnectListDomains", + "virDomainMemoryStats" ] skipped_modules = { @@ -170,6 +171,7 @@ skipped_types = { # 'int *': "usually a return type", 'virConnectDomainEventCallback': "No function types in python", 'virEventAddHandleFunc': "No function types in python", + 'virDomainMemoryStatPtr': "Not implemented yet", } ####################################################################### diff --git a/src/driver.h b/src/driver.h index 09ce8e2..6193e47 100644 --- a/src/driver.h +++ b/src/driver.h @@ -229,6 +229,12 @@ typedef int struct _virDomainInterfaceStats *stats); typedef int + (*virDrvDomainMemoryStats) + (virDomainPtr domain, + struct _virDomainMemoryStat *stats, + unsigned int nr_stats); + +typedef int (*virDrvDomainBlockPeek) (virDomainPtr domain, const char *path, @@ -419,6 +425,7 @@ struct _virDriver { virDrvDomainMigrateFinish domainMigrateFinish; virDrvDomainBlockStats domainBlockStats; virDrvDomainInterfaceStats domainInterfaceStats; + virDrvDomainMemoryStats domainMemoryStats; virDrvDomainBlockPeek domainBlockPeek; virDrvDomainMemoryPeek domainMemoryPeek; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index ea464a3..2626162 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -3412,6 +3412,7 @@ static virDriver esxDriver = { esxDomainMigrateFinish, /* domainMigrateFinish */ NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ NULL, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ NULL, /* nodeGetCellsFreeMemory */ diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index c8e2dca..ff998c7 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2438,6 +2438,7 @@ static virDriver lxcDriver = { NULL, /* domainMigrateFinish */ NULL, /* domainBlockStats */ lxcDomainInterfaceStats, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ NULL, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ nodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index a30c110..a4ad31e 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -765,6 +765,7 @@ static virDriver oneDriver = { NULL, /* domainMigrateFinish */ NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ NULL, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ NULL, /* nodeGetCellsFreeMemory */ diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 1c0fccc..7acce5b 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1517,6 +1517,7 @@ static virDriver openvzDriver = { NULL, /* domainMigrateFinish */ NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ NULL, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ NULL, /* nodeGetCellsFreeMemory */ diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index c4fcfed..07adccb 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -1633,6 +1633,7 @@ virDriver phypDriver = { NULL, /* domainMigrateFinish */ NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ NULL, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ NULL, /* nodeGetCellsFreeMemory */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9ef6c35..1ddc23e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7907,6 +7907,7 @@ static virDriver qemuDriver = { NULL, /* domainMigrateFinish */ qemudDomainBlockStats, /* domainBlockStats */ qemudDomainInterfaceStats, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ qemudDomainBlockPeek, /* domainBlockPeek */ qemudDomainMemoryPeek, /* domainMemoryPeek */ nodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 77962fe..5d32d3b 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8814,6 +8814,7 @@ static virDriver remote_driver = { remoteDomainMigrateFinish, /* domainMigrateFinish */ remoteDomainBlockStats, /* domainBlockStats */ remoteDomainInterfaceStats, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ remoteDomainBlockPeek, /* domainBlockPeek */ remoteDomainMemoryPeek, /* domainMemoryPeek */ remoteNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 7db9a4c..21ea491 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5220,6 +5220,7 @@ static virDriver testDriver = { NULL, /* domainMigrateFinish */ testDomainBlockStats, /* domainBlockStats */ testDomainInterfaceStats, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ NULL, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 48ef103..035ed0c 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1906,6 +1906,7 @@ static virDriver umlDriver = { NULL, /* domainMigrateFinish */ NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ umlDomainBlockPeek, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ nodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index d6b681c..e2e1ce6 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7028,6 +7028,7 @@ virDriver NAME(Driver) = { NULL, /* domainMigrateFinish */ NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ NULL, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ nodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index ed94bed..e952610 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -1844,6 +1844,7 @@ static virDriver xenUnifiedDriver = { xenUnifiedDomainMigrateFinish, /* domainMigrateFinish */ xenUnifiedDomainBlockStats, /* domainBlockStats */ xenUnifiedDomainInterfaceStats, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ xenUnifiedDomainBlockPeek, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ xenUnifiedNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ -- 1.6.5

Add a new function 'virDomainMemoryStats' to the libvirt API. Export it via libvirt_public.syms Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 2 files changed, 72 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 103b331..298610d 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -4109,6 +4109,77 @@ error: } /** + * virDomainMemoryStats: + * @dom: pointer to the domain object + * @stats: nr_stats-sized array of stat structures (returned) + * @nr_stats: number of memory statistics requested + * @flags: unused, always pass 0 + * + * This function provides memory statistics for the domain. + * + * Up to 'nr_stats' elements of 'stats' will be populated with memory statistics + * from the domain. Only statistics supported by the domain, the driver, and + * this version of libvirt will be returned. + * + * Memory Statistics: + * + * VIR_DOMAIN_MEMORY_STAT_SWAP_IN: + * The total amount of data read from swap space (in kb). + * VIR_DOMAIN_MEMORY_STAT_SWAP_OUT: + * The total amount of memory written out to swap space (in kb). + * VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT: + * The number of page faults that required disk IO to service. + * VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT: + * The number of page faults serviced without disk IO. + * VIR_DOMAIN_MEMORY_STAT_UNUSED: + * The amount of memory which is not being used for any purpose (in kb). + * VIR_DOMAIN_MEMORY_STAT_AVAILABLE: + * The total amount of memory available to the domain's OS (in kb). + * + * Returns: The number of stats provided or -1 in case of failure. + */ +int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats, + unsigned int nr_stats, unsigned int flags) +{ + virConnectPtr conn; + unsigned long nr_stats_ret = 0; + DEBUG("domain=%p, stats=%p, nr_stats=%u", dom, stats, nr_stats); + + if (flags != 0) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + _("flags must be zero")); + goto error; + } + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { + virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + if (!stats || nr_stats == 0) + return 0; + + if (nr_stats > VIR_DOMAIN_MEMORY_STAT_NR) + nr_stats = VIR_DOMAIN_MEMORY_STAT_NR; + + conn = dom->conn; + if (conn->driver->domainMemoryStats) { + nr_stats_ret = conn->driver->domainMemoryStats (dom, stats, nr_stats); + if (nr_stats_ret == -1) + goto error; + return nr_stats_ret; + } + + virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dom->conn); + return -1; +} + +/** * virDomainBlockPeek: * @dom: pointer to the domain object * @path: path to the block device diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b4f57e7..c0c3693 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -341,6 +341,7 @@ LIBVIRT_0.7.3 { virStoragePoolIsActive; virStoragePoolIsPersistent; virInterfaceIsActive; + virDomainMemoryStats; } LIBVIRT_0.7.2; # .... define new API here using predicted next version number .... -- 1.6.5

Support for memory statistics reporting is accepted for qemu inclusion. Statistics are reported via the monitor command 'info balloon' as a comma seprated list: (qemu) info balloon balloon: actual=1024,mem_swapped_in=0,mem_swapped_out=0,major_page_faults=88,minor_page_faults=105535,free_mem=1017065472,total_mem=1045229568 Libvirt, qemu, and the guest operating system may support a subset of the statistics defined by the virtio spec. Thus, only statistics recognized by components will be reported. Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- src/qemu/qemu_driver.c | 39 +++++++++++++++++++- src/qemu/qemu_monitor_text.c | 83 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 3 ++ 3 files changed, 124 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1ddc23e..562a20b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6235,6 +6235,43 @@ qemudDomainInterfaceStats (virDomainPtr dom, #endif static int +qemudDomainMemoryStats (virDomainPtr dom, + struct _virDomainMemoryStat *stats, + unsigned int nr_stats) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + unsigned int ret = -1; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorTextGetMemoryStats(priv->mon, stats, nr_stats); + qemuDomainObjExitMonitor(vm); + } else { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int qemudDomainBlockPeek (virDomainPtr dom, const char *path, unsigned long long offset, size_t size, @@ -7907,7 +7944,7 @@ static virDriver qemuDriver = { NULL, /* domainMigrateFinish */ qemudDomainBlockStats, /* domainBlockStats */ qemudDomainInterfaceStats, /* domainInterfaceStats */ - NULL, /* domainMemoryStats */ + qemudDomainMemoryStats, /* domainMemoryStats */ qemudDomainBlockPeek, /* domainBlockPeek */ qemudDomainMemoryPeek, /* domainMemoryPeek */ nodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 0cb9ea6..ab361c6 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -454,6 +454,65 @@ error: return 0; } +static int parseMemoryStat(char **text, unsigned int tag, + const char *search, virDomainMemoryStatPtr stat) +{ + char *dummy; + unsigned long long value; + + if (STRPREFIX (*text, search)) { + *text += strlen(search); + if (virStrToLong_ull (*text, &dummy, 10, &value)) { + DEBUG ("error reading %s: %s", search, *text); + return 0; + } + + /* Convert bytes to kilobytes for libvirt */ + switch (tag) { + case VIR_DOMAIN_MEMORY_STAT_SWAP_IN: + case VIR_DOMAIN_MEMORY_STAT_SWAP_OUT: + case VIR_DOMAIN_MEMORY_STAT_UNUSED: + case VIR_DOMAIN_MEMORY_STAT_AVAILABLE: + value = value >> 10; + } + stat->tag = tag; + stat->val = value; + return 1; + } + return 0; +} + +/* The reply from the 'info balloon' command may contain additional memory + * statistics in the form: '[,<tag>=<val>]*' + */ +static int qemuMonitorParseExtraBalloonInfo(char *text, + virDomainMemoryStatPtr stats, + unsigned int nr_stats) +{ + char *p = text; + unsigned int nr_stats_found = 0; + + while (*p && nr_stats_found < nr_stats) { + if (parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_SWAP_IN, + ",mem_swapped_in=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_SWAP_OUT, + ",mem_swapped_out=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT, + ",major_page_faults=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT, + ",minor_page_faults=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_UNUSED, + ",free_mem=", &stats[nr_stats_found]) || + parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_AVAILABLE, + ",total_mem=", &stats[nr_stats_found])) + nr_stats_found++; + + /* Skip to the next label */ + p = strchr (p, ','); + if (!p) break; + } + return nr_stats_found; +} /* The reply from QEMU contains 'ballon: actual=421' where value is in MB */ @@ -499,6 +558,30 @@ cleanup: return ret; } +int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, + virDomainMemoryStatPtr stats, + unsigned int nr_stats) +{ + char *reply = NULL; + int ret = 0; + char *offset; + + if (qemuMonitorCommand(mon, "info balloon", &reply) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("could not query memory balloon statistics")); + return -1; + } + + if ((offset = strstr(reply, BALLOON_PREFIX)) != NULL) { + if ((offset = strchr(reply, ',')) != NULL) { + ret = qemuMonitorParseExtraBalloonInfo(offset, stats, nr_stats); + } + } + + VIR_FREE(reply); + return ret; +} + int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, const char *devname, diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 519edf4..5897a03 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -45,6 +45,9 @@ int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon, int **pids); int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon, unsigned long *currmem); +int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, + virDomainMemoryStatPtr stats, + unsigned int nr_stats); int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, const char *devname, long long *rd_req, -- 1.6.5

Use a dynamically sized xdr_array to pass memory stats on the wire. This supports the addition of future memory stats and reduces the message size since only supported statistics are returned. Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 56 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 44 ++++++++++++++++++++++++++++++++- src/remote/remote_protocol.c | 35 ++++++++++++++++++++++++++ src/remote/remote_protocol.h | 25 ++++++++++++++++++ src/remote/remote_protocol.x | 18 ++++++++++++- 5 files changed, 176 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 7a43046..2066c49 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -708,6 +708,62 @@ remoteDispatchDomainInterfaceStats (struct qemud_server *server ATTRIBUTE_UNUSED } static int +remoteDispatchDomainMemoryStats (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_memory_stats_args *args, + remote_domain_memory_stats_ret *ret) +{ + virDomainPtr dom; + struct _virDomainMemoryStat *stats; + unsigned int nr_stats, i; + + if (args->maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX) { + remoteDispatchFormatError (rerr, "%s", + _("maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX")); + return -1; + } + + dom = get_nonnull_domain (conn, args->dom); + if (dom == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + /* Allocate stats array for making dispatch call */ + if (VIR_ALLOC_N(stats, args->maxStats) < 0) { + remoteDispatchOOMError(rerr); + return -1; + } + + nr_stats = virDomainMemoryStats (dom, stats, args->maxStats, 0); + virDomainFree (dom); + if (nr_stats == -1) { + VIR_FREE(stats); + remoteDispatchConnError(rerr, conn); + return -1; + } + + /* Allocate return buffer */ + if (VIR_ALLOC_N(ret->stats, args->maxStats) < 0) { + VIR_FREE(stats); + remoteDispatchOOMError(rerr); + return -1; + } + + /* Copy the stats into the xdr return structure */ + for (i = 0; i < nr_stats; i++) { + ret->stats[i].tag = stats[i].tag; + ret->stats[i].val = stats[i].val; + } + ret->len = nr_stats; + VIR_FREE(stats); + return 0; +} + +static int remoteDispatchDomainBlockPeek (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 5d32d3b..384779d 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -3366,6 +3366,48 @@ done: } static int +remoteDomainMemoryStats (virDomainPtr domain, + struct _virDomainMemoryStat *stats, + unsigned int nr_stats) +{ + int rv = -1; + remote_domain_memory_stats_args args; + remote_domain_memory_stats_ret ret; + struct private_data *priv = domain->conn->privateData; + unsigned int i; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, domain); + if (nr_stats > REMOTE_DOMAIN_MEMORY_STATS_MAX) { + errorf (domain->conn, VIR_ERR_RPC, + _("too many memory stats requested: %d > %d"), nr_stats, + REMOTE_DOMAIN_MEMORY_STATS_MAX); + goto done; + } + args.maxStats = nr_stats; + memset (&ret, 0, sizeof ret); + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_MEMORY_STATS, + (xdrproc_t) xdr_remote_domain_memory_stats_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_memory_stats_ret, + (char *) &ret) == -1) + goto done; + + for (i = 0; i < ret.len; i++) { + stats[i].tag = ret.stats[i].tag; + stats[i].val = ret.stats[i].val; + } + rv = ret.len; + xdr_free((xdrproc_t) xdr_remote_domain_memory_stats_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + +static int remoteDomainBlockPeek (virDomainPtr domain, const char *path, unsigned long long offset, @@ -8814,7 +8856,7 @@ static virDriver remote_driver = { remoteDomainMigrateFinish, /* domainMigrateFinish */ remoteDomainBlockStats, /* domainBlockStats */ remoteDomainInterfaceStats, /* domainInterfaceStats */ - NULL, /* domainMemoryStats */ + remoteDomainMemoryStats, /* domainMemoryStats */ remoteDomainBlockPeek, /* domainBlockPeek */ remoteDomainMemoryPeek, /* domainMemoryPeek */ remoteNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index a10558a..45510ae 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -597,6 +597,41 @@ xdr_remote_domain_interface_stats_ret (XDR *xdrs, remote_domain_interface_stats_ } bool_t +xdr_remote_domain_memory_stats_args (XDR *xdrs, remote_domain_memory_stats_args *objp) +{ + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->maxStats)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_memory_stat (XDR *xdrs, remote_domain_memory_stat *objp) +{ + if (!xdr_int (xdrs, &objp->tag)) + return FALSE; + if (!xdr_u_hyper (xdrs, &objp->val)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_memory_stats_ret (XDR *xdrs, remote_domain_memory_stats_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->stats; + + if (!xdr_u_int (xdrs, &objp->len)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->len, + REMOTE_DOMAIN_MEMORY_STATS_MAX, + sizeof (struct remote_domain_memory_stat), + (xdrproc_t) xdr_remote_memory_stat)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_domain_block_peek_args (XDR *xdrs, remote_domain_block_peek_args *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index 2ba9be4..e7832ff 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -40,6 +40,7 @@ typedef remote_nonnull_string *remote_string; #define REMOTE_NODE_MAX_CELLS 1024 #define REMOTE_AUTH_SASL_DATA_MAX 65536 #define REMOTE_AUTH_TYPE_LIST_MAX 20 +#define REMOTE_DOMAIN_MEMORY_STATS_MAX 1024 #define REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX 65536 #define REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX 65536 #define REMOTE_SECURITY_MODEL_MAX VIR_SECURITY_MODEL_BUFLEN @@ -307,6 +308,24 @@ struct remote_domain_interface_stats_ret { }; typedef struct remote_domain_interface_stats_ret remote_domain_interface_stats_ret; +struct remote_domain_memory_stats_args { + remote_nonnull_domain dom; + u_int maxStats; +}; +typedef struct remote_domain_memory_stats_args remote_domain_memory_stats_args; + +struct remote_domain_memory_stat { + int tag; + uint64_t val; +}; +typedef struct remote_domain_memory_stat remote_domain_memory_stat; + +struct remote_domain_memory_stats_ret { + u_int len; + struct remote_domain_memory_stat *stats; +}; +typedef struct remote_domain_memory_stats_ret remote_domain_memory_stats_ret; + struct remote_domain_block_peek_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -1777,6 +1796,7 @@ enum remote_procedure { REMOTE_PROC_STORAGE_POOL_IS_PERSISTENT = 155, REMOTE_PROC_INTERFACE_IS_ACTIVE = 156, REMOTE_PROC_GET_LIB_VERSION = 157, + REMOTE_PROC_DOMAIN_MEMORY_STATS = 158, }; typedef enum remote_procedure remote_procedure; @@ -1853,6 +1873,9 @@ extern bool_t xdr_remote_domain_block_stats_args (XDR *, remote_domain_block_st extern bool_t xdr_remote_domain_block_stats_ret (XDR *, remote_domain_block_stats_ret*); extern bool_t xdr_remote_domain_interface_stats_args (XDR *, remote_domain_interface_stats_args*); extern bool_t xdr_remote_domain_interface_stats_ret (XDR *, remote_domain_interface_stats_ret*); +extern bool_t xdr_remote_domain_memory_stats_args (XDR *, remote_domain_memory_stats_args*); +extern bool_t xdr_remote_domain_memory_stats_ret (XDR *, remote_domain_memory_stats_ret*); +extern bool_t xdr_remote_memory_stat (XDR *xdrs, remote_domain_memory_stat *objp); extern bool_t xdr_remote_domain_block_peek_args (XDR *, remote_domain_block_peek_args*); extern bool_t xdr_remote_domain_block_peek_ret (XDR *, remote_domain_block_peek_ret*); extern bool_t xdr_remote_domain_memory_peek_args (XDR *, remote_domain_memory_peek_args*); @@ -2124,6 +2147,8 @@ extern bool_t xdr_remote_domain_block_stats_args (); extern bool_t xdr_remote_domain_block_stats_ret (); extern bool_t xdr_remote_domain_interface_stats_args (); extern bool_t xdr_remote_domain_interface_stats_ret (); +extern bool_t xdr_remote_domain_memory_stats_args (); +extern bool_t xdr_remote_domain_memory_stats_ret (); extern bool_t xdr_remote_domain_block_peek_args (); extern bool_t xdr_remote_domain_block_peek_ret (); extern bool_t xdr_remote_domain_memory_peek_args (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 92f7010..36e7f73 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -405,6 +405,21 @@ struct remote_domain_interface_stats_ret { hyper tx_drop; }; +struct remote_domain_memory_stats_args { + remote_nonnull_domain dom; + u_int maxStats; +}; + +struct remote_domain_memory_stat { + int tag; + unsigned hyper val; +}; + +struct remote_domain_memory_stats_ret { + u_int len; + remote_domain_memory_stat<REMOTE_DOMAIN_MEMORY_STATS_MAX>; +}; + struct remote_domain_block_peek_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -1611,7 +1626,8 @@ enum remote_procedure { REMOTE_PROC_STORAGE_POOL_IS_ACTIVE = 154, REMOTE_PROC_STORAGE_POOL_IS_PERSISTENT = 155, REMOTE_PROC_INTERFACE_IS_ACTIVE = 156, - REMOTE_PROC_GET_LIB_VERSION = 157 + REMOTE_PROC_GET_LIB_VERSION = 157, + REMOTE_PROC_DOMAIN_MEMORY_STATS = 158 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.5

Define a new command 'dommemstats' to report domain memory statistics. The output format is inspired by 'domblkstat' and 'domifstat' and consists of tag/value pairs, one per line. The command can complete successfully and print no output if virDomainMemoryStats is supported by the driver, but not the guest operating system. Sample output: swap_in 0 swap_out 0 major_fault 54 minor_fault 58259 unused 487680 available 502472 All stats referring to a quantity of memory (eg. all above except major and minor faults) represent the quantity in kb. Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 55 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 8f96ca8..c2250ba 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -885,6 +885,60 @@ cmdDomIfstat (vshControl *ctl, const vshCmd *cmd) } /* + * "dommemstats" command + */ +static const vshCmdInfo info_dommemstats[] = { + {"help", gettext_noop("get memory statistics for a domain")}, + {"desc", gettext_noop("Get memory statistics for a runnng domain.")}, + {NULL,NULL} +}; + +static const vshCmdOptDef opts_dommemstats[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdDomMemStats(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + char *name; + struct _virDomainMemoryStat stats[VIR_DOMAIN_MEMORY_STAT_NR]; + unsigned int nr_stats, i; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, &name))) + return FALSE; + + nr_stats = virDomainMemoryStats (dom, stats, VIR_DOMAIN_MEMORY_STAT_NR, 0); + if (nr_stats == -1) { + vshError(ctl, _("Failed to get memory statistics for domain %s"), name); + virDomainFree(dom); + return FALSE; + } + + for (i = 0; i < nr_stats; i++) { + if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_IN) + vshPrint (ctl, "swap_in %llu\n", stats[i].val); + if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_OUT) + vshPrint (ctl, "swap_out %llu\n", stats[i].val); + if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT) + vshPrint (ctl, "major_fault %llu\n", stats[i].val); + if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT) + vshPrint (ctl, "minor_fault %llu\n", stats[i].val); + if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_UNUSED) + vshPrint (ctl, "unused %llu\n", stats[i].val); + if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_AVAILABLE) + vshPrint (ctl, "available %llu\n", stats[i].val); + } + + virDomainFree(dom); + return TRUE; +} + +/* * "suspend" command */ static const vshCmdInfo info_suspend[] = { @@ -7221,6 +7275,7 @@ static const vshCmdDef commands[] = { {"domstate", cmdDomstate, opts_domstate, info_domstate}, {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat}, {"domifstat", cmdDomIfstat, opts_domifstat, info_domifstat}, + {"dommemstats", cmdDomMemStats, opts_dommemstats, info_dommemstats}, {"domxml-from-native", cmdDomXMLFromNative, opts_domxmlfromnative, info_domxmlfromnative}, {"domxml-to-native", cmdDomXMLToNative, opts_domxmltonative, info_domxmltonative}, {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml}, -- 1.6.5

Enable virDomainMemoryStats in the python API. dom.memoryStats() will return a dictionary containing the supported statistics. A dictionary is required because the meaining of each quantity cannot be inferred from its index in a list. Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- python/generator.py | 3 +- python/libvirt-override-api.xml | 5 ++++ python/libvirt-override.c | 43 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/python/generator.py b/python/generator.py index 06f1ff4..56f8925 100755 --- a/python/generator.py +++ b/python/generator.py @@ -161,7 +161,6 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ "virConnectListDomains", - "virDomainMemoryStats" ] skipped_modules = { @@ -171,7 +170,6 @@ skipped_types = { # 'int *': "usually a return type", 'virConnectDomainEventCallback': "No function types in python", 'virEventAddHandleFunc': "No function types in python", - 'virDomainMemoryStatPtr': "Not implemented yet", } ####################################################################### @@ -283,6 +281,7 @@ skip_impl = ( 'virNetworkGetAutostart', 'virDomainBlockStats', 'virDomainInterfaceStats', + 'virDomainMemoryStats', 'virNodeGetCellsFreeMemory', 'virDomainGetSchedulerType', 'virDomainGetSchedulerParameters', diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 96958b5..6ae2742 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -105,6 +105,11 @@ <arg name='domain' type='virDomainPtr' info='a domain object'/> <arg name='path' type='char *' info='the path for the interface device'/> </function> + <function name='virDomainMemoryStats' file='python'> + <info>Extracts memory statistics for a domain</info> + <return type='virDomainMemoryStats' info='a dictionary of statistics'/> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + </function> <function name="virNodeGetCellsFreeMemory" file='python'> <info>Returns the available memory for a list of cells</info> <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 6c1e51b..8aa2dad 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -120,6 +120,48 @@ libvirt_virDomainInterfaceStats(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) return(info); } +static PyObject * +libvirt_virDomainMemoryStats(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain; + unsigned int nr_stats, i; + virDomainMemoryStatStruct stats[VIR_MEMSTAT_NR_TAGS]; + PyObject *info; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainMemoryStats", &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + nr_stats = virDomainMemoryStats(domain, stats, VIR_MEMSTAT_NR_TAGS, 0); + if (nr_stats == -1) + return VIR_PY_NONE; + + /* convert to a Python dictionary */ + if ((info = PyDict_New()) == NULL) + return VIR_PY_NONE; + + for (i = 0; i < nr_stats; i++) { + if (stats[i].tag == VIR_MEMSTAT_SWAP_IN) + PyDict_SetItem(info, libvirt_constcharPtrWrap("swap_in"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_SWAP_OUT) + PyDict_SetItem(info, libvirt_constcharPtrWrap("swap_out"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_MAJOR_FAULT) + PyDict_SetItem(info, libvirt_constcharPtrWrap("major_fault"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_MINOR_FAULT) + PyDict_SetItem(info, libvirt_constcharPtrWrap("minor_fault"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_MEM_FREE) + PyDict_SetItem(info, libvirt_constcharPtrWrap("free_memory"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_MEM_TOTAL) + PyDict_SetItem(info, libvirt_constcharPtrWrap("total_memory"), + PyLong_FromUnsignedLongLong(stats[i].val)); + } + return info; +} static PyObject * libvirt_virDomainGetSchedulerType(PyObject *self ATTRIBUTE_UNUSED, @@ -2635,6 +2677,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virNetworkGetAutostart", libvirt_virNetworkGetAutostart, METH_VARARGS, NULL}, {(char *) "virDomainBlockStats", libvirt_virDomainBlockStats, METH_VARARGS, NULL}, {(char *) "virDomainInterfaceStats", libvirt_virDomainInterfaceStats, METH_VARARGS, NULL}, + {(char *) "virDomainMemoryStats", libvirt_virDomainMemoryStats, METH_VARARGS, NULL}, {(char *) "virNodeGetCellsFreeMemory", libvirt_virNodeGetCellsFreeMemory, METH_VARARGS, NULL}, {(char *) "virDomainGetSchedulerType", libvirt_virDomainGetSchedulerType, METH_VARARGS, NULL}, {(char *) "virDomainGetSchedulerParameters", libvirt_virDomainGetSchedulerParameters, METH_VARARGS, NULL}, -- 1.6.5

2009/12/17 Adam Litke <agl@us.ibm.com>:
Enable virDomainMemoryStats in the python API. dom.memoryStats() will return a dictionary containing the supported statistics. A dictionary is required because the meaining of each quantity cannot be inferred from its index in a list.
Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- python/generator.py | 3 +- python/libvirt-override-api.xml | 5 ++++ python/libvirt-override.c | 43 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/python/generator.py b/python/generator.py index 06f1ff4..56f8925 100755 --- a/python/generator.py +++ b/python/generator.py @@ -161,7 +161,6 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ "virConnectListDomains", - "virDomainMemoryStats" ]
skipped_modules = { @@ -171,7 +170,6 @@ skipped_types = { # 'int *': "usually a return type", 'virConnectDomainEventCallback': "No function types in python", 'virEventAddHandleFunc': "No function types in python", - 'virDomainMemoryStatPtr': "Not implemented yet", }
####################################################################### @@ -283,6 +281,7 @@ skip_impl = ( 'virNetworkGetAutostart', 'virDomainBlockStats', 'virDomainInterfaceStats', + 'virDomainMemoryStats', 'virNodeGetCellsFreeMemory', 'virDomainGetSchedulerType', 'virDomainGetSchedulerParameters', diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 96958b5..6ae2742 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -105,6 +105,11 @@ <arg name='domain' type='virDomainPtr' info='a domain object'/> <arg name='path' type='char *' info='the path for the interface device'/> </function> + <function name='virDomainMemoryStats' file='python'> + <info>Extracts memory statistics for a domain</info> + <return type='virDomainMemoryStats' info='a dictionary of statistics'/> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + </function> <function name="virNodeGetCellsFreeMemory" file='python'> <info>Returns the available memory for a list of cells</info> <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 6c1e51b..8aa2dad 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -120,6 +120,48 @@ libvirt_virDomainInterfaceStats(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) return(info); }
+static PyObject * +libvirt_virDomainMemoryStats(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain; + unsigned int nr_stats, i; + virDomainMemoryStatStruct stats[VIR_MEMSTAT_NR_TAGS]; + PyObject *info; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainMemoryStats", &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + nr_stats = virDomainMemoryStats(domain, stats, VIR_MEMSTAT_NR_TAGS, 0); + if (nr_stats == -1) + return VIR_PY_NONE; + + /* convert to a Python dictionary */ + if ((info = PyDict_New()) == NULL) + return VIR_PY_NONE; + + for (i = 0; i < nr_stats; i++) { + if (stats[i].tag == VIR_MEMSTAT_SWAP_IN) + PyDict_SetItem(info, libvirt_constcharPtrWrap("swap_in"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_SWAP_OUT) + PyDict_SetItem(info, libvirt_constcharPtrWrap("swap_out"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_MAJOR_FAULT) + PyDict_SetItem(info, libvirt_constcharPtrWrap("major_fault"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_MINOR_FAULT) + PyDict_SetItem(info, libvirt_constcharPtrWrap("minor_fault"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_MEM_FREE) + PyDict_SetItem(info, libvirt_constcharPtrWrap("free_memory"), + PyLong_FromUnsignedLongLong(stats[i].val)); + if (stats[i].tag == VIR_MEMSTAT_MEM_TOTAL) + PyDict_SetItem(info, libvirt_constcharPtrWrap("total_memory"), + PyLong_FromUnsignedLongLong(stats[i].val)); + } + return info; +}
You're using the old tags (VIR_MEMSTAT_*) and names (free_memory, total_memory) here. You could convert the adjacent if blocks into and if/else if block or a switch block. You're also silently ignoring unknown tags here, but that should be handled at the libvirt C API level if it should be handled at all. Matthias

On Fri, 2009-12-18 at 00:41 +0100, Matthias Bolte wrote:
You're using the old tags (VIR_MEMSTAT_*) and names (free_memory, total_memory) here.
Argh. I forgot to commit my local changes to the python patch :( This is done and will appear in the next series.
You could convert the adjacent if blocks into and if/else if block or a switch block.
Sure. I will play around with it and see if I can spruce the look of this code up a bit.
You're also silently ignoring unknown tags here, but that should be handled at the libvirt C API level if it should be handled at all.
Ignoring unknown tags allows us to inter-operate with a newer remote libvirt version. So this semantic should be okay. -- Thanks, Adam

And just now I saw the comment about updating drivers so that flags can be transmitted over the wire. I am working on this now. -- Thanks, Adam

On Thu, Dec 17, 2009 at 05:41:45PM -0500, Adam Litke wrote:
Use a dynamically sized xdr_array to pass memory stats on the wire. This supports the addition of future memory stats and reduces the message size since only supported statistics are returned.
Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 56 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 44 ++++++++++++++++++++++++++++++++- src/remote/remote_protocol.c | 35 ++++++++++++++++++++++++++ src/remote/remote_protocol.h | 25 ++++++++++++++++++ src/remote/remote_protocol.x | 18 ++++++++++++- 5 files changed, 176 insertions(+), 2 deletions(-)
+struct remote_domain_memory_stats_ret { + u_int len; + struct remote_domain_memory_stat *stats; +}; +typedef struct remote_domain_memory_stats_ret remote_domain_memory_stats_ret;
+ +struct remote_domain_memory_stats_ret { + u_int len; + remote_domain_memory_stat<REMOTE_DOMAIN_MEMORY_STATS_MAX>; +};
Something odd happened in the RPC generated code here, because rpcgen should create you an explicit length field without you needing one. I think it is because I forgot the param name on the array in the example I gave you. Basically it should work just like the existing 'struct remote_domain_get_scheduler_parameters_ret' definition struct remote_domain_memory_stats_ret { remote_domain_memory_stat stats<REMOTE_DOMAIN_MEMORY_STATS_MAX>; }; Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Fri, 2009-12-18 at 10:30 +0000, Daniel P. Berrange wrote:
On Thu, Dec 17, 2009 at 05:41:45PM -0500, Adam Litke wrote:
Use a dynamically sized xdr_array to pass memory stats on the wire. This supports the addition of future memory stats and reduces the message size since only supported statistics are returned.
Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 56 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 44 ++++++++++++++++++++++++++++++++- src/remote/remote_protocol.c | 35 ++++++++++++++++++++++++++ src/remote/remote_protocol.h | 25 ++++++++++++++++++ src/remote/remote_protocol.x | 18 ++++++++++++- 5 files changed, 176 insertions(+), 2 deletions(-)
+struct remote_domain_memory_stats_ret { + u_int len; + struct remote_domain_memory_stat *stats; +}; +typedef struct remote_domain_memory_stats_ret remote_domain_memory_stats_ret;
+ +struct remote_domain_memory_stats_ret { + u_int len; + remote_domain_memory_stat<REMOTE_DOMAIN_MEMORY_STATS_MAX>; +};
Something odd happened in the RPC generated code here, because rpcgen should create you an explicit length field without you needing one. I think it is because I forgot the param name on the array in the example I gave you. Basically it should work just like the existing 'struct remote_domain_get_scheduler_parameters_ret' definition
I explicitly added the len parameter because I need to return the number of elements actually updated back to the remote caller (see remoteDomainMemoryStats). Is there another way to extract this data using xdr without my explicit variable?
struct remote_domain_memory_stats_ret { remote_domain_memory_stat stats<REMOTE_DOMAIN_MEMORY_STATS_MAX>; };
Daniel
-- Thanks, Adam

On Fri, Dec 18, 2009 at 09:36:11AM -0600, Adam Litke wrote:
On Fri, 2009-12-18 at 10:30 +0000, Daniel P. Berrange wrote:
On Thu, Dec 17, 2009 at 05:41:45PM -0500, Adam Litke wrote:
Use a dynamically sized xdr_array to pass memory stats on the wire. This supports the addition of future memory stats and reduces the message size since only supported statistics are returned.
Signed-off-by: Adam Litke <agl@us.ibm.com> To: libvirt list <libvir-list@redhat.com> Cc: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 56 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 44 ++++++++++++++++++++++++++++++++- src/remote/remote_protocol.c | 35 ++++++++++++++++++++++++++ src/remote/remote_protocol.h | 25 ++++++++++++++++++ src/remote/remote_protocol.x | 18 ++++++++++++- 5 files changed, 176 insertions(+), 2 deletions(-)
+struct remote_domain_memory_stats_ret { + u_int len; + struct remote_domain_memory_stat *stats; +}; +typedef struct remote_domain_memory_stats_ret remote_domain_memory_stats_ret;
+ +struct remote_domain_memory_stats_ret { + u_int len; + remote_domain_memory_stat<REMOTE_DOMAIN_MEMORY_STATS_MAX>; +};
Something odd happened in the RPC generated code here, because rpcgen should create you an explicit length field without you needing one. I think it is because I forgot the param name on the array in the example I gave you. Basically it should work just like the existing 'struct remote_domain_get_scheduler_parameters_ret' definition
I explicitly added the len parameter because I need to return the number of elements actually updated back to the remote caller (see remoteDomainMemoryStats). Is there another way to extract this data using xdr without my explicit variable?
struct remote_domain_memory_stats_ret { remote_domain_memory_stat stats<REMOTE_DOMAIN_MEMORY_STATS_MAX>; };
Take a look at the header file generated when you use this style. The 'stats' variable should end up with two fields 'stats_len' and 'stats_val' The end result is ultimately the same as yours, but this is the explicit XDR array syntax Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Hi Adam, thanks for your efforts, for me such an API would be very helpful. I tried to use it by applying this patch http://git.kernel.org/?p=linux/kernel/git/rusty/linux-2.6-for-linus.git;a=pa... against vanilla 2.6.32.2 and using latest libvirt from git, but when I do $ virsh dommemstat mydomain I just get an empty line of output, no error message, nothing. Ballooning works fine, but as I said, no statistics output. I'm also using virtio for net and blk - did I miss something (additional entry in the dom-config?) or is there something going wrong? I'm using qemu-kvm-0.11.0 currently, -0.12 is not working for me. Could that be the cause? Thanks for any directions, kr,tom

Hi Thomas, To use the memory statistics API with qemu, you will need support in 3 places: libvirt (you're good here), qemu, and linux. The qemu and linux parts have been accepted but are still staging. If you want to build your own qemu and linux-kernel, let me know and I can provide you some patches. On Wed, 2009-12-23 at 15:25 +0100, Thomas Treutner wrote:
Hi Adam,
thanks for your efforts, for me such an API would be very helpful.
I tried to use it by applying this patch
http://git.kernel.org/?p=linux/kernel/git/rusty/linux-2.6-for-linus.git;a=pa...
against vanilla 2.6.32.2 and using latest libvirt from git, but when I do
$ virsh dommemstat mydomain
I just get an empty line of output, no error message, nothing. Ballooning works fine, but as I said, no statistics output. I'm also using virtio for net and blk - did I miss something (additional entry in the dom-config?) or is there something going wrong?
I'm using qemu-kvm-0.11.0 currently, -0.12 is not working for me. Could that be the cause?
Thanks for any directions,
kr,tom
-- Thanks, Adam

Hi Adam, On Wednesday 23 December 2009 15:47:58 Adam Litke wrote:
To use the memory statistics API with qemu, you will need support in 3 places: libvirt (you're good here), qemu, and linux. The qemu and linux parts have been accepted but are still staging. If you want to build your own qemu and linux-kernel, let me know and I can provide you some patches.
yes, I'd highly appreciate that! I can use any Linux kernel that is stable enough for dev&testing, with qemu-kvm, I wasn't lucky with all releases newer than 0.11.0, as my VMs are stuck at boot (SeaBIOS...gPXE, then nothing happens). Thx&kr, tom
participants (4)
-
Adam Litke
-
Daniel P. Berrange
-
Matthias Bolte
-
Thomas Treutner