[libvirt] [PATCH 1/6] domMemoryStats: Add domainMemoryStats method to struct _virDriver
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: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> Cc: libvirt list <libvir-list@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..820bf98 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 = 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: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> Cc: libvirt list <libvir-list@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: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> Cc: libvirt list <libvir-list@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: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> Cc: libvirt list <libvir-list@redhat.com> --- daemon/remote.c | 56 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 45 +++++++++++++++++++++++++++++++++- src/remote/remote_protocol.x | 21 +++++++++++++++- 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 7a43046..8658d6e 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.stats_val, 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.stats_val[i].tag = stats[i].tag; + ret->stats.stats_val[i].val = stats[i].val; + } + ret->stats.stats_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..bd78984 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -3366,6 +3366,49 @@ 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; + args.flags = 0; + 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.stats.stats_len; i++) { + stats[i].tag = ret.stats.stats_val[i].tag; + stats[i].val = ret.stats.stats_val[i].val; + } + rv = ret.stats.stats_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 +8857,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.x b/src/remote/remote_protocol.x index 92f7010..d9d0fbc 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -115,6 +115,9 @@ const REMOTE_AUTH_SASL_DATA_MAX = 65536; /* Maximum number of auth types */ const REMOTE_AUTH_TYPE_LIST_MAX = 20; +/* Upper limit on list of memory stats */ +const REMOTE_DOMAIN_MEMORY_STATS_MAX = 1024; + /* Maximum length of a block peek buffer message. * Note applications need to be aware of this limit and issue multiple * requests for large amounts of data. @@ -405,6 +408,21 @@ struct remote_domain_interface_stats_ret { hyper tx_drop; }; +struct remote_domain_memory_stats_args { + remote_nonnull_domain dom; + u_int maxStats; + u_int flags; +}; + +struct remote_domain_memory_stat { + int tag; + unsigned hyper val; +}; + +struct remote_domain_memory_stats_ret { + remote_domain_memory_stat stats<REMOTE_DOMAIN_MEMORY_STATS_MAX>; +}; + struct remote_domain_block_peek_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -1611,7 +1629,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: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> Cc: libvirt list <libvir-list@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: Daniel Veillard <veillard@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> Cc: libvirt list <libvir-list@redhat.com> --- python/generator.py | 3 +- python/libvirt-override-api.xml | 5 ++++ python/libvirt-override.c | 44 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 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..db4c0e1 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -120,6 +120,49 @@ 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_DOMAIN_MEMORY_STAT_NR]; + 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_DOMAIN_MEMORY_STAT_NR, 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_DOMAIN_MEMORY_STAT_SWAP_IN) + PyDict_SetItem(info, libvirt_constcharPtrWrap("swap_in"), + PyLong_FromUnsignedLongLong(stats[i].val)); + else if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_OUT) + PyDict_SetItem(info, libvirt_constcharPtrWrap("swap_out"), + PyLong_FromUnsignedLongLong(stats[i].val)); + else if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT) + PyDict_SetItem(info, libvirt_constcharPtrWrap("major_fault"), + PyLong_FromUnsignedLongLong(stats[i].val)); + else if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT) + PyDict_SetItem(info, libvirt_constcharPtrWrap("minor_fault"), + PyLong_FromUnsignedLongLong(stats[i].val)); + else if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_UNUSED) + PyDict_SetItem(info, libvirt_constcharPtrWrap("unused"), + PyLong_FromUnsignedLongLong(stats[i].val)); + else if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_AVAILABLE) + PyDict_SetItem(info, libvirt_constcharPtrWrap("available"), + PyLong_FromUnsignedLongLong(stats[i].val)); + } + return info; +} static PyObject * libvirt_virDomainGetSchedulerType(PyObject *self ATTRIBUTE_UNUSED, @@ -2635,6 +2678,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
Hi Adam. On Fri, 2009-12-18 at 13:17 -0500, Adam Litke wrote:
Define a new command 'dommemstats' to report domain memory statistics. The output format is inspired by 'domblkstat' and 'domifstat'
Minor point but as this is inspired by existing 'domblkstat' and 'domifstat', why 'dommemstats' instead of 'dommemstat'? The inconsistency here seems like it could confuse a user. Thanks, Paul
On Sun, Dec 20, 2009 at 06:05:26PM +0000, Paul Jenner wrote:
Hi Adam.
On Fri, 2009-12-18 at 13:17 -0500, Adam Litke wrote:
Define a new command 'dommemstats' to report domain memory statistics. The output format is inspired by 'domblkstat' and 'domifstat'
Minor point but as this is inspired by existing 'domblkstat' and 'domifstat', why 'dommemstats' instead of 'dommemstat'? The inconsistency here seems like it could confuse a user.
Agreed, lets keep this new one to be same convention 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 Mon, Dec 21, 2009 at 11:29:27AM +0000, Daniel P. Berrange wrote:
On Sun, Dec 20, 2009 at 06:05:26PM +0000, Paul Jenner wrote:
Hi Adam.
On Fri, 2009-12-18 at 13:17 -0500, Adam Litke wrote:
Define a new command 'dommemstats' to report domain memory statistics. The output format is inspired by 'domblkstat' and 'domifstat'
Minor point but as this is inspired by existing 'domblkstat' and 'domifstat', why 'dommemstats' instead of 'dommemstat'? The inconsistency here seems like it could confuse a user.
Agreed, lets keep this new one to be same convention
Yes, that makes sense, I just fixed this, thanks for raising the issue Paul ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (4)
-
Adam Litke -
Daniel P. Berrange -
Daniel Veillard -
Paul Jenner