From e714bb67ca5231fcd6ce19db1201a36477e95dbb Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 15 Nov 2007 17:45:44 +0000
Subject: [PATCH] Thu Nov 15 17:43:00 UTC 2007 Richard W.M. Jones
 <rjones@redhat.com>

	* src/stats_linux.c: Fix parsing of short interface names
	  occurring in the /proc/net/dev file.
	* src/qemu_driver.c: Collect interface stats for QEMU & KVM
	  domains.
---
 ChangeLog         |  7 ++++++
 src/qemu_driver.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/stats_linux.c | 16 ++++++++----
 3 files changed, 79 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 04b37e9c76..e019150fac 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Thu Nov 15 17:43:00 UTC 2007 Richard W.M. Jones <rjones@redhat.com>
+
+	* src/stats_linux.c: Fix parsing of short interface names
+	  occurring in the /proc/net/dev file.
+	* src/qemu_driver.c: Collect interface stats for QEMU & KVM
+	  domains.
+
 Thu Nov 15 17:40:15 UTC 2007 Richard W.M. Jones <rjones@redhat.com>
 
 	* docs/hvsupport.html, docs/libvir.html: Document HV support
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 62a1f6ba98..da690fa766 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -55,6 +55,7 @@
 #include "qemu_driver.h"
 #include "qemu_conf.h"
 #include "nodeinfo.h"
+#include "stats_linux.h"
 
 static int qemudShutdown(void);
 
@@ -2479,6 +2480,65 @@ static int qemudDomainSetAutostart(virDomainPtr dom,
     return 0;
 }
 
+static int
+qemudDomainInterfaceStats (virDomainPtr dom,
+                           const char *path,
+                           struct _virDomainInterfaceStats *stats)
+{
+#ifdef __linux__
+    struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+    struct qemud_vm *vm = qemudFindVMByID (driver, dom->id);
+    struct qemud_vm_net_def *net;
+
+    if (!vm) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                          "no domain with matching id %d", dom->id);
+        return -1;
+    }
+
+    if (!qemudIsActiveVM(vm)) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "domain is not running");
+        return -1;
+    }
+
+    if (!path || path[0] == '\0') {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+                         "NULL or empty path");
+        return -1;
+    }
+
+    /* Check the path is one of the domain's network interfaces. */
+    for (net = vm->def->nets; net; net = net->next) {
+        switch (net->type) {
+        case QEMUD_NET_NETWORK:
+            if (STREQ (net->dst.network.ifname, path))
+                goto ok;
+            break;
+        case QEMUD_NET_ETHERNET:
+            if (STREQ (net->dst.ethernet.ifname, path))
+                goto ok;
+            break;
+        case QEMUD_NET_BRIDGE:
+            if (STREQ (net->dst.bridge.ifname, path))
+                goto ok;
+            break;
+        }
+    }
+
+    qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+                      "invalid path, '%s' is not a known interface", path);
+    return -1;
+ ok:
+
+    return linuxDomainInterfaceStats (dom->conn, path, stats);
+#else
+    qemudReportError (dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+                      "%s", __FUNCTION__);
+    return -1;
+#endif
+}
+
 static virNetworkPtr qemudNetworkLookupByUUID(virConnectPtr conn ATTRIBUTE_UNUSED,
                                      const unsigned char *uuid) {
     struct qemud_driver *driver = (struct qemud_driver *)conn->networkPrivateData;
@@ -2822,7 +2882,7 @@ static virDriver qemuDriver = {
     NULL, /* domainMigratePerform */
     NULL, /* domainMigrateFinish */
     NULL, /* domainBlockStats */
-    NULL, /* domainInterfaceStats */
+    qemudDomainInterfaceStats, /* domainInterfaceStats */
     NULL, /* nodeGetCellsFreeMemory */
     NULL, /* getFreeMemory */
 };
diff --git a/src/stats_linux.c b/src/stats_linux.c
index 125af45fcd..f35d5dd4e1 100644
--- a/src/stats_linux.c
+++ b/src/stats_linux.c
@@ -291,7 +291,7 @@ linuxDomainInterfaceStats (virConnectPtr conn, const char *path,
 {
     int path_len;
     FILE *fp;
-    char line[256];
+    char line[256], *colon;
 
     fp = fopen ("/proc/net/dev", "r");
     if (!fp) {
@@ -313,16 +313,22 @@ linuxDomainInterfaceStats (virConnectPtr conn, const char *path,
         long long tx_errs;
         long long tx_drop;
 
-        if (STREQLEN (line, path, path_len) &&
-            line[path_len] == ':' &&
-            line[path_len+1] == ' ') {
+        /* 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)) {
             /* 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.
              */
-            if (sscanf (&line[path_len+2],
+            if (sscanf (colon+1,
                         "%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,