2007-11-14 11:58:36 +00:00
|
|
|
/*
|
|
|
|
* Linux block and network stats.
|
|
|
|
*
|
2009-01-29 12:10:32 +00:00
|
|
|
* Copyright (C) 2007-2009 Red Hat, Inc.
|
2007-11-14 11:58:36 +00:00
|
|
|
*
|
|
|
|
* See COPYING.LIB for the License of this software
|
|
|
|
*
|
|
|
|
* Richard W.M. Jones <rjones@redhat.com>
|
|
|
|
*/
|
|
|
|
|
2008-01-29 18:15:54 +00:00
|
|
|
#include <config.h>
|
2007-11-14 11:58:36 +00:00
|
|
|
|
|
|
|
/* This file only applies on Linux. */
|
|
|
|
#ifdef __linux__
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
Recently upstream Xen added support for having xvd devices > 16. For the most
part, this doesn't really concern libvirt, since for things like attach and
detach we just pass it through and let xend worry about whether it is supported
or not. The one place this breaks down is in the stats collecting code, where
we need to figure out the device number so we can go digging in /sys for the
statistics.
To remedy this, I've re-written xenLinuxDomainDeviceID() to use regular
expressions to figure out the device number from the name. The major advantage
is that now xenLinuxDomainDeviceID() looks fairly identical to
tools/python/xen/util/blkif.py (in the Xen sources), so that adding additional
devices in the future should be much easier. It also reduces the size of the
code, and, in my opinion, the code complexity.
With this patch in place, I was able to get block statistics both on older style
devices (/dev/xvda) and on the new, expanded devices (/dev/xvdaa).
Signed-off-by: Chris Lalancette <clalance@redhat.com>
2008-08-05 16:38:49 +00:00
|
|
|
#include <regex.h>
|
2007-11-14 11:58:36 +00:00
|
|
|
|
2008-11-04 22:30:33 +00:00
|
|
|
#include "virterror_internal.h"
|
2008-11-04 23:22:06 +00:00
|
|
|
#include "datatypes.h"
|
2008-02-08 09:15:16 +00:00
|
|
|
#include "util.h"
|
2007-11-14 11:58:36 +00:00
|
|
|
#include "stats_linux.h"
|
Recently upstream Xen added support for having xvd devices > 16. For the most
part, this doesn't really concern libvirt, since for things like attach and
detach we just pass it through and let xend worry about whether it is supported
or not. The one place this breaks down is in the stats collecting code, where
we need to figure out the device number so we can go digging in /sys for the
statistics.
To remedy this, I've re-written xenLinuxDomainDeviceID() to use regular
expressions to figure out the device number from the name. The major advantage
is that now xenLinuxDomainDeviceID() looks fairly identical to
tools/python/xen/util/blkif.py (in the Xen sources), so that adding additional
devices in the future should be much easier. It also reduces the size of the
code, and, in my opinion, the code complexity.
With this patch in place, I was able to get block statistics both on older style
devices (/dev/xvda) and on the new, expanded devices (/dev/xvdaa).
Signed-off-by: Chris Lalancette <clalance@redhat.com>
2008-08-05 16:38:49 +00:00
|
|
|
#include "memory.h"
|
2007-11-14 11:58:36 +00:00
|
|
|
|
2009-01-29 12:10:32 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_STATS_LINUX
|
|
|
|
|
2007-11-14 11:58:36 +00:00
|
|
|
/**
|
|
|
|
* statsErrorFunc:
|
|
|
|
* @conn: the connection
|
|
|
|
* @error: the error number
|
|
|
|
* @func: the function failing
|
|
|
|
* @info: extra information string
|
|
|
|
* @value: extra information number
|
|
|
|
*
|
|
|
|
* Handle a stats error.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
statsErrorFunc (virConnectPtr conn,
|
|
|
|
virErrorNumber error, const char *func, const char *info,
|
|
|
|
int value)
|
|
|
|
{
|
|
|
|
char fullinfo[1000];
|
|
|
|
const char *errmsg;
|
|
|
|
|
2008-11-04 22:30:33 +00:00
|
|
|
errmsg = virErrorMsg(error, info);
|
2007-11-14 11:58:36 +00:00
|
|
|
if (func != NULL) {
|
|
|
|
snprintf(fullinfo, sizeof (fullinfo) - 1, "%s: %s", func, info);
|
|
|
|
fullinfo[sizeof (fullinfo) - 1] = 0;
|
|
|
|
info = fullinfo;
|
|
|
|
}
|
2008-11-04 22:30:33 +00:00
|
|
|
virRaiseError(conn, NULL, NULL, VIR_FROM_STATS_LINUX, error,
|
2007-11-20 10:58:21 +00:00
|
|
|
VIR_ERR_ERROR,
|
2007-11-14 11:58:36 +00:00
|
|
|
errmsg, info, NULL, value, 0, errmsg, info,
|
|
|
|
value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------- interface stats --------------------*/
|
|
|
|
/* Just reads the named interface, so not Xen or QEMU-specific.
|
|
|
|
* NB. Caller must check that libvirt user is trying to query
|
|
|
|
* the interface of a domain they own. We do no such checking.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
linuxDomainInterfaceStats (virConnectPtr conn, const char *path,
|
|
|
|
struct _virDomainInterfaceStats *stats)
|
|
|
|
{
|
|
|
|
int path_len;
|
|
|
|
FILE *fp;
|
2007-11-15 17:45:44 +00:00
|
|
|
char line[256], *colon;
|
2007-11-14 11:58:36 +00:00
|
|
|
|
|
|
|
fp = fopen ("/proc/net/dev", "r");
|
|
|
|
if (!fp) {
|
2008-01-29 18:36:00 +00:00
|
|
|
statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__,
|
2007-11-14 11:58:36 +00:00
|
|
|
"/proc/net/dev", errno);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
path_len = strlen (path);
|
|
|
|
|
|
|
|
while (fgets (line, sizeof line, fp)) {
|
|
|
|
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;
|
|
|
|
|
2007-11-15 17:45:44 +00:00
|
|
|
/* The line looks like:
|
|
|
|
* " eth0:..."
|
|
|
|
* Split it at the colon.
|
|
|
|
*/
|
|
|
|
colon = strchr (line, ':');
|
|
|
|
if (!colon) continue;
|
|
|
|
*colon = '\0';
|
|
|
|
if (colon-path_len >= line &&
|
|
|
|
STREQ (colon-path_len, path)) {
|
2007-11-14 11:58:36 +00:00
|
|
|
/* IMPORTANT NOTE!
|
|
|
|
* /proc/net/dev vif<domid>.nn sees the network from the point
|
|
|
|
* of view of dom0 / hypervisor. So bytes TRANSMITTED by dom0
|
|
|
|
* are bytes RECEIVED by the domain. That's why the TX/RX fields
|
|
|
|
* appear to be swapped here.
|
|
|
|
*/
|
2007-11-15 17:45:44 +00:00
|
|
|
if (sscanf (colon+1,
|
2007-11-14 11:58:36 +00:00
|
|
|
"%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
|
|
|
|
&tx_bytes, &tx_packets, &tx_errs, &tx_drop,
|
|
|
|
&dummy, &dummy, &dummy, &dummy,
|
|
|
|
&rx_bytes, &rx_packets, &rx_errs, &rx_drop,
|
|
|
|
&dummy, &dummy, &dummy, &dummy) != 16)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2008-01-29 18:36:00 +00:00
|
|
|
statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__,
|
2007-11-14 11:58:36 +00:00
|
|
|
"/proc/net/dev: Interface not found", 0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __linux__ */
|