This is the implementation (currently Xen, local only).It includes a couple of virsh commands if you want to play with looking at the stats.
Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903
Index: include/libvirt/libvirt.h.in =================================================================== RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h.in,v retrieving revision 1.31 diff -u -p -r1.31 libvirt.h.in --- include/libvirt/libvirt.h.in 18 Jul 2007 10:11:10 -0000 1.31 +++ include/libvirt/libvirt.h.in 10 Aug 2007 14:30:21 -0000 @@ -14,6 +14,9 @@ #ifndef __VIR_VIRLIB_H__ #define __VIR_VIRLIB_H__ +#include <sys/types.h> +#include <stdint.h> + #ifdef __cplusplus extern "C" { #endif @@ -197,6 +200,37 @@ int virDomainSetSchedulerParameters (vir virSchedParameterPtr params, int nparams); +/* Block device stats for virDomainBlockStats. + * + * Hypervisors may return a field set to (int64_t)-1 which indicates + * that the hypervisor does not support that statistic. + */ +struct _virDomainBlockStats { + int64_t rd_req; + int64_t rd_bytes; + int64_t wr_req; + int64_t wr_bytes; + int64_t errs; // In Xen this returns the mysterious 'oo_req'. +}; +typedef struct _virDomainBlockStats *virDomainBlockStatsPtr; + +/* Network interface stats for virDomainInterfaceStats. + * + * Hypervisors may return a field set to (int64_t)-1 which indicates + * that the hypervisor does not support that statistic. + */ +struct _virDomainInterfaceStats { + int64_t rx_bytes; + int64_t rx_packets; + int64_t rx_errs; + int64_t rx_drop; + int64_t tx_bytes; + int64_t tx_packets; + int64_t tx_errs; + int64_t tx_drop; +}; +typedef struct _virDomainInterfaceStats *virDomainInterfaceStatsPtr; + /** * VIR_NODEINFO_MAXCPUS: * @nodeinfo: virNodeInfo instance @@ -369,6 +403,16 @@ int virDomainGetMaxVcpus (virDomainPtr char * virDomainGetXMLDesc (virDomainPtr domain, int flags); +int virDomainBlockStats (virDomainPtr dom, + const char *path, + virDomainBlockStatsPtr stats, + size_t size); +int virDomainInterfaceStats (virDomainPtr dom, + const char *path, + virDomainInterfaceStatsPtr stats, + size_t size); + + /* * defined but not running domains */ Index: src/driver.h =================================================================== RCS file: /data/cvs/libvirt/src/driver.h,v retrieving revision 1.32 diff -u -p -r1.32 driver.h --- src/driver.h 27 Jul 2007 23:23:00 -0000 1.32 +++ src/driver.h 10 Aug 2007 14:30:21 -0000 @@ -181,6 +181,17 @@ typedef int virSchedParameterPtr params, int nparams); +typedef int + (*virDrvDomainBlockStats) + (virDomainPtr domain, + const char *path, + struct _virDomainBlockStats *stats); +typedef int + (*virDrvDomainInterfaceStats) + (virDomainPtr domain, + const char *path, + struct _virDomainInterfaceStats *stats); + typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr; @@ -245,6 +256,8 @@ struct _virDriver { virDrvDomainGetSchedulerType domainGetSchedulerType; virDrvDomainGetSchedulerParameters domainGetSchedulerParameters; virDrvDomainSetSchedulerParameters domainSetSchedulerParameters; + virDrvDomainBlockStats domainBlockStats; + virDrvDomainInterfaceStats domainInterfaceStats; }; typedef int Index: src/libvirt.c =================================================================== RCS file: /data/cvs/libvirt/src/libvirt.c,v retrieving revision 1.93 diff -u -p -r1.93 libvirt.c --- src/libvirt.c 9 Aug 2007 20:19:12 -0000 1.93 +++ src/libvirt.c 10 Aug 2007 14:30:23 -0000 @@ -1830,6 +1830,116 @@ virDomainSetSchedulerParameters(virDomai } +/** + * virDomainBlockStats + * @dom: pointer to the domain object + * @path: path to the block device + * @stats: block device stats (returned) + * @size: size of stats structure + * + * This function returns block device (disk) stats for block + * devices attached to the domain. + * + * The path parameter is the name of the block device. Get this + * by calling virDomainGetXMLDesc and finding the <target dev='...'> + * attribute within //domain/devices/disk. (For example, "xvda"). + * + * Domains may have more than one block device. To get stats for + * each you should make multiple calls to this function. + * + * Individual fields within the stats structure may be returned + * as -1, which indicates that the hypervisor does not support + * that particular statistic. + * + * Returns: 0 in case of success or -1 in case of failure. + */ +int +virDomainBlockStats (virDomainPtr dom, const char *path, + virDomainBlockStatsPtr stats, size_t size) +{ + virConnectPtr conn; + struct _virDomainBlockStats stats2 = { -1, -1, -1, -1, -1 }; + DEBUG("domain=%p, path=%s, stats=%p, size=%zi", dom, path, stats, size); + + if (!stats || size > sizeof stats2) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { + virLibDomainError (dom, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + conn = dom->conn; + + if (conn->driver->domainBlockStats) { + if (conn->driver->domainBlockStats (dom, path, &stats2) == -1) + return -1; + + memcpy (stats, &stats2, size); + return 0; + } + + virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +/** + * virDomainInterfaceStats + * @dom: pointer to the domain object + * @path: path to the interface + * @stats: network interface stats (returned) + * @size: size of stats structure + * + * This function returns network interface stats for interfaces + * attached to the domain. + * + * The path parameter is the name of the network interface. Get + * this by calling virDomainGetXMLDesc and finding the + * <source ...> attribute within //domain/devices/interface. + * (For example, "xenbr0" or "virbr0"). + * + * Domains may have more than network interface. To get stats for + * each you should make multiple calls to this function. + * + * Individual fields within the stats structure may be returned + * as -1, which indicates that the hypervisor does not support + * that particular statistic. + * + * Returns: 0 in case of success or -1 in case of failure. + */ +int +virDomainInterfaceStats (virDomainPtr dom, const char *path, + virDomainInterfaceStatsPtr stats, size_t size) +{ + virConnectPtr conn; + struct _virDomainInterfaceStats stats2 = { -1, -1, -1, -1, + -1, -1, -1, -1 }; + DEBUG("domain=%p, path=%s, stats=%p, size=%zi", dom, path, stats, size); + + if (!stats || size > sizeof stats2) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { + virLibDomainError (dom, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + conn = dom->conn; + + if (conn->driver->domainInterfaceStats) { + if (conn->driver->domainInterfaceStats (dom, path, &stats2) == -1) + return -1; + + memcpy (stats, &stats2, size); + return 0; + } + + virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + + /************************************************************************ * * Index: src/libvirt_sym.version =================================================================== RCS file: /data/cvs/libvirt/src/libvirt_sym.version,v retrieving revision 1.25 diff -u -p -r1.25 libvirt_sym.version --- src/libvirt_sym.version 26 Jun 2007 22:56:14 -0000 1.25 +++ src/libvirt_sym.version 10 Aug 2007 14:30:23 -0000 @@ -65,7 +65,8 @@ virDomainGetSchedulerType; virDomainGetSchedulerParameters; virDomainSetSchedulerParameters; - + virDomainBlockStats; + virDomainInterfaceStats; virDomainAttachDevice; virDomainDetachDevice; Index: src/qemu_driver.c =================================================================== RCS file: /data/cvs/libvirt/src/qemu_driver.c,v retrieving revision 1.14 diff -u -p -r1.14 qemu_driver.c --- src/qemu_driver.c 30 Jul 2007 09:59:06 -0000 1.14 +++ src/qemu_driver.c 10 Aug 2007 14:30:24 -0000 @@ -2386,6 +2386,8 @@ static virDriver qemuDriver = { NULL, /* domainGetSchedulerType */ NULL, /* domainGetSchedulerParameters */ NULL, /* domainSetSchedulerParameters */ + NULL, /* domainBlockStats */ + NULL, /* domainInterfaceStats */ }; static virNetworkDriver qemuNetworkDriver = { Index: src/test.c =================================================================== RCS file: /data/cvs/libvirt/src/test.c,v retrieving revision 1.44 diff -u -p -r1.44 test.c --- src/test.c 9 Aug 2007 20:19:12 -0000 1.44 +++ src/test.c 10 Aug 2007 14:30:26 -0000 @@ -1963,6 +1963,8 @@ static virDriver testDriver = { testDomainGetSchedulerType, /* domainGetSchedulerType */ testDomainGetSchedulerParams, /* domainGetSchedulerParameters */ testDomainSetSchedulerParams, /* domainSetSchedulerParameters */ + NULL, /* domainBlockStats */ + NULL, /* domainInterfaceStats */ }; static virNetworkDriver testNetworkDriver = { Index: src/virsh.c =================================================================== RCS file: /data/cvs/libvirt/src/virsh.c,v retrieving revision 1.94 diff -u -p -r1.94 virsh.c --- src/virsh.c 7 Aug 2007 15:26:51 -0000 1.94 +++ src/virsh.c 10 Aug 2007 14:30:28 -0000 @@ -31,6 +31,7 @@ #include <assert.h> #include <errno.h> #include <sys/stat.h> +#include <inttypes.h> #include <test.h> #include <libxml/parser.h> @@ -659,6 +660,129 @@ cmdDomstate(vshControl * ctl, vshCmd * c return ret; } +/* "domblkstat" command + */ +static vshCmdInfo info_domblkstat[] = { + {"syntax", "domblkstat <domain> <dev>"}, + {"help", gettext_noop("get device block stats for a domain")}, + {"desc", gettext_noop("Get device block stats for a running domain.")}, + {NULL,NULL} +}; + +static vshCmdOptDef opts_domblkstat[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {"device", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("block device")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdDomblkstat (vshControl *ctl, vshCmd *cmd) +{ + virDomainPtr dom; + char *name, *device; + struct _virDomainBlockStats stats; + + if (!vshConnectionUsability (ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain (ctl, cmd, "domain", &name))) + return FALSE; + + if (!(device = vshCommandOptString (cmd, "device", NULL))) + return FALSE; + + if (virDomainBlockStats (dom, device, &stats, sizeof stats) == -1) { + vshError (ctl, FALSE, _("Failed to get block stats %s %s"), + name, device); + virDomainFree(dom); + return FALSE; + } + + if (stats.rd_req >= 0) + vshPrint (ctl, "%s rd_req %" PRId64 "\n", device, stats.rd_req); + + if (stats.rd_bytes >= 0) + vshPrint (ctl, "%s rd_bytes %" PRId64 "\n", device, stats.rd_bytes); + + if (stats.wr_req >= 0) + vshPrint (ctl, "%s wr_req %" PRId64 "\n", device, stats.wr_req); + + if (stats.wr_bytes >= 0) + vshPrint (ctl, "%s wr_bytes %" PRId64 "\n", device, stats.wr_bytes); + + if (stats.errs >= 0) + vshPrint (ctl, "%s errs %" PRId64 "\n", device, stats.errs); + + virDomainFree(dom); + return TRUE; +} + +/* "domifstat" command + */ +static vshCmdInfo info_domifstat[] = { + {"syntax", "domifstat <domain> <dev>"}, + {"help", gettext_noop("get network interface stats for a domain")}, + {"desc", gettext_noop("Get network interface stats for a running domain.")}, + {NULL,NULL} +}; + +static vshCmdOptDef opts_domifstat[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("interface device")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdDomIfstat (vshControl *ctl, vshCmd *cmd) +{ + virDomainPtr dom; + char *name, *device; + struct _virDomainInterfaceStats stats; + + if (!vshConnectionUsability (ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain (ctl, cmd, "domain", &name))) + return FALSE; + + if (!(device = vshCommandOptString (cmd, "interface", NULL))) + return FALSE; + + if (virDomainInterfaceStats (dom, device, &stats, sizeof stats) == -1) { + vshError (ctl, FALSE, _("Failed to get interface stats %s %s"), + name, device); + virDomainFree(dom); + return FALSE; + } + + if (stats.rx_bytes >= 0) + vshPrint (ctl, "%s rx_bytes %" PRId64 "\n", device, stats.rx_bytes); + + if (stats.rx_packets >= 0) + vshPrint (ctl, "%s rx_packets %" PRId64 "\n", device, stats.rx_packets); + + if (stats.rx_errs >= 0) + vshPrint (ctl, "%s rx_errs %" PRId64 "\n", device, stats.rx_errs); + + if (stats.rx_drop >= 0) + vshPrint (ctl, "%s rx_drop %" PRId64 "\n", device, stats.rx_drop); + + if (stats.tx_bytes >= 0) + vshPrint (ctl, "%s tx_bytes %" PRId64 "\n", device, stats.tx_bytes); + + if (stats.tx_packets >= 0) + vshPrint (ctl, "%s tx_packets %" PRId64 "\n", device, stats.tx_packets); + + if (stats.tx_errs >= 0) + vshPrint (ctl, "%s tx_errs %" PRId64 "\n", device, stats.tx_errs); + + if (stats.tx_drop >= 0) + vshPrint (ctl, "%s tx_drop %" PRId64 "\n", device, stats.tx_drop); + + virDomainFree(dom); + return TRUE; +} + /* * "suspend" command */ @@ -3466,6 +3590,8 @@ static vshCmdDef commands[] = { {"dominfo", cmdDominfo, opts_dominfo, info_dominfo}, {"domname", cmdDomname, opts_domname, info_domname}, {"domstate", cmdDomstate, opts_domstate, info_domstate}, + {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat}, + {"domifstat", cmdDomIfstat, opts_domifstat, info_domifstat}, {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml}, {"hostname", cmdHostname, NULL, info_hostname}, {"list", cmdList, opts_list, info_list}, Index: src/xen_internal.c =================================================================== RCS file: /data/cvs/libvirt/src/xen_internal.c,v retrieving revision 1.90 diff -u -p -r1.90 xen_internal.c --- src/xen_internal.c 30 Jul 2007 10:15:58 -0000 1.90 +++ src/xen_internal.c 10 Aug 2007 14:30:30 -0000 @@ -1291,6 +1291,141 @@ xenHypervisorSetSchedulerParameters(virD return 0; } +static int64_t +read_stat (const char *path) +{ + char str[64]; + int64_t r; + int i; + FILE *fp; + + fp = fopen (path, "r"); + if (!fp) return -1; + /* stupid GCC warning */ i = fread (str, sizeof str, 1, fp); + r = strtoll (str, NULL, 10); + fclose (fp); + return r; +} + +static int64_t +read_bd_stat (int device, int domid, const char *str) +{ + char path[PATH_MAX]; + int64_t r; + + snprintf (path, sizeof path, + "/sys/devices/xen-backend/vbd-%d-%d/statistics/%s_req", + domid, device, str); + r = read_stat (path); + if (r >= 0) return r; + + snprintf (path, sizeof path, + "/sys/devices/xen-backend/tap-%d-%d/statistics/%s_req", + domid, device, str); + r = read_stat (path); + return r; +} + +/* Paths have the form "xvd[a-]" and map to paths /sys/devices/xen-backend/ + * (vbd|tap)-domid-major:minor/statistics/(rd|wr|oo)_req. The major:minor + * is in this case fixed as 202*256 + 16*minor where minor is 0 for xvda, + * 1 for xvdb and so on. + */ +int +xenHypervisorDomainBlockStats (virDomainPtr dom, + const char *path, + struct _virDomainBlockStats *stats) +{ + int minor, device; + + if (strlen (path) != 4 || + STRNEQLEN (path, "xvd", 3) || + (minor = path[3] - 'a') < 0 || + minor > 26) { + virXenErrorFunc (VIR_ERR_INVALID_ARG, __FUNCTION__, + "invalid path, should be xvda, xvdb, etc.", 0); + return -1; + } + device = 202 * 256 + minor; + + stats->rd_req = read_bd_stat (device, dom->id, "rd"); + stats->wr_req = read_bd_stat (device, dom->id, "wr"); + stats->errs = read_bd_stat (device, dom->id, "oo"); + + if (stats->rd_req == -1 && stats->wr_req == -1 && stats->errs == -1) { + virXenErrorFunc (VIR_ERR_NO_SUPPORT, __FUNCTION__, + "Failed to read any block statistics", dom->id); + return -1; + } + + return 0; +} + +/* Paths have the form "virbr\d+" or "xenbr\d+". On Linux we open + * /proc/net/dev and look for the device called vif<domid>.<n>. + */ +int +xenHypervisorDomainInterfaceStats (virDomainPtr dom, + const char *path, + struct _virDomainInterfaceStats *stats) +{ + int device; + FILE *fp; + char line[256]; + + if (sscanf (path, "virbr%d", &device) != 1 && + sscanf (path, "xenbr%d", &device) != 1) { + virXenErrorFunc (VIR_ERR_INVALID_ARG, __FUNCTION__, + "invalid path, should be virbrN or xenbrN.", 0); + return -1; + } + + fp = fopen ("/proc/net/dev", "r"); + if (!fp) { + virXenErrorFunc (VIR_ERR_NO_SUPPORT, __FUNCTION__, + "/proc/net/dev", errno); + return -1; + } + while (fgets (line, sizeof line, fp)) { + int domid, port; + long long dummy; + long long rx_bytes; + long long rx_packets; + long long rx_errs; + long long rx_drop; + long long tx_bytes; + long long tx_packets; + long long tx_errs; + long long tx_drop; + + if (sscanf (line, "vif%d.%d: %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld", + &domid, &port, + &rx_bytes, &rx_packets, &rx_errs, &rx_drop, + &dummy, &dummy, &dummy, &dummy, + &tx_bytes, &tx_packets, &tx_errs, &tx_drop, + &dummy, &dummy, &dummy, &dummy) != 18) + continue; + + if (domid == dom->id && port == device) { + stats->rx_bytes = rx_bytes; + stats->rx_packets = rx_packets; + stats->rx_errs = rx_errs; + stats->rx_drop = rx_drop; + stats->tx_bytes = tx_bytes; + stats->tx_packets = tx_packets; + stats->tx_errs = tx_errs; + stats->tx_drop = tx_drop; + fclose (fp); + return 0; + } + } + fclose (fp); + + virXenErrorFunc (VIR_ERR_NO_SUPPORT, __FUNCTION__, + "/proc/net/dev: Interface not found", 0); + return -1; +} + /** * virXen_pausedomain: * @handle: the hypervisor handle Index: src/xen_internal.h =================================================================== RCS file: /data/cvs/libvirt/src/xen_internal.h,v retrieving revision 1.21 diff -u -p -r1.21 xen_internal.h --- src/xen_internal.h 6 Jul 2007 15:11:22 -0000 1.21 +++ src/xen_internal.h 10 Aug 2007 14:30:30 -0000 @@ -77,6 +77,13 @@ int xenHypervisorSetSchedulerParameters virSchedParameterPtr params, int nparams); +int xenHypervisorDomainBlockStats (virDomainPtr domain, + const char *path, + struct _virDomainBlockStats *stats); +int xenHypervisorDomainInterfaceStats (virDomainPtr domain, + const char *path, + struct _virDomainInterfaceStats *stats); + #ifdef __cplusplus } #endif Index: src/xen_unified.c =================================================================== RCS file: /data/cvs/libvirt/src/xen_unified.c,v retrieving revision 1.17 diff -u -p -r1.17 xen_unified.c --- src/xen_unified.c 12 Jul 2007 08:34:51 -0000 1.17 +++ src/xen_unified.c 10 Aug 2007 14:30:31 -0000 @@ -948,6 +948,32 @@ xenUnifiedDomainSetSchedulerParameters ( return(-1); } +static int +xenUnifiedDomainBlockStats (virDomainPtr dom, const char *path, + struct _virDomainBlockStats *stats) +{ + GET_PRIVATE (dom->conn); + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) + return xenHypervisorDomainBlockStats (dom, path, stats); + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static int +xenUnifiedDomainInterfaceStats (virDomainPtr dom, const char *path, + struct _virDomainInterfaceStats *stats) +{ + GET_PRIVATE (dom->conn); + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) + return xenHypervisorDomainInterfaceStats (dom, path, stats); + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + /*----- Register with libvirt.c, and initialise Xen drivers. -----*/ #define VERSION ((DOM0_INTERFACE_VERSION >> 24) * 1000000 + \ @@ -1002,6 +1028,8 @@ static virDriver xenUnifiedDriver = { .domainGetSchedulerType = xenUnifiedDomainGetSchedulerType, .domainGetSchedulerParameters = xenUnifiedDomainGetSchedulerParameters, .domainSetSchedulerParameters = xenUnifiedDomainSetSchedulerParameters, + .domainBlockStats = xenUnifiedDomainBlockStats, + .domainInterfaceStats = xenUnifiedDomainInterfaceStats, }; /**
Attachment:
smime.p7s
Description: S/MIME Cryptographic Signature
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list