[Libvir] [PATCH] Implement virDomainBlockStats for QEMU/KVM
Richard W.M. Jones
rjones at redhat.com
Tue Feb 26 11:32:20 UTC 2008
This little patch just implements the virDomainBlockStats call for
qemu & kvm. It does this by using the new 'info blockstats' monitor
command which I added to qemu & KVM upstream some months back, and
(hopefully) it does the right thing if this command is not available.
Rich.
--
Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines. Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.55
diff -u -r1.55 qemu_driver.c
--- src/qemu_driver.c 26 Feb 2008 07:05:18 -0000 1.55
+++ src/qemu_driver.c 26 Feb 2008 11:31:53 -0000
@@ -59,6 +59,9 @@
static int qemudShutdown(void);
+/* qemudDebug statements should be changed to use this macro instead. */
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
#define qemudLog(level, msg...) fprintf(stderr, msg)
@@ -2527,6 +2530,144 @@
return 0;
}
+/* This uses the 'info blockstats' monitor command which was
+ * integrated into both qemu & kvm in late 2007. If the command is
+ * not supported we detect this and return the appropriate error.
+ */
+static int
+qemudDomainBlockStats (virDomainPtr dom,
+ const char *path,
+ struct _virDomainBlockStats *stats)
+{
+ struct qemud_driver *driver =
+ (struct qemud_driver *)dom->conn->privateData;
+ char *info, *p, *dummy, *eol;
+ char qemu_dev_name[32];
+ int len;
+ struct qemud_vm *vm = qemudFindVMByID(driver, dom->id);
+
+ 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;
+ }
+
+ /*
+ * QEMU internal block device names are different from the device
+ * names we use in libvirt, so we need to map between them:
+ *
+ * hd[a-] to ide0-hd[0-]
+ * cdrom to ide1-cd0
+ * fd[a-] to floppy[0-]
+ */
+ if (STREQLEN (path, "hd", 2) && path[2] >= 'a' && path[2] <= 'z')
+ snprintf (qemu_dev_name, sizeof (qemu_dev_name),
+ "ide0-hd%d", path[2] - 'a');
+ else if (STREQ (path, "cdrom"))
+ strcpy (qemu_dev_name, "ide1-cd0");
+ else if (STREQLEN (path, "fd", 2) && path[2] >= 'a' && path[2] <= 'z')
+ snprintf (qemu_dev_name, sizeof (qemu_dev_name),
+ "floppy%d", path[2] - 'a');
+ else {
+ qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+ _("invalid path: %s"), path);
+ return -1;
+ }
+
+ len = strlen (qemu_dev_name);
+
+ if (qemudMonitorCommand (driver, vm, "info blockstats", &info) < 0) {
+ qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+ _("'info blockstats' command failed"));
+ return -1;
+ }
+
+ DEBUG ("info blockstats reply: %s", info);
+
+ /* If the command isn't supported then qemu prints the supported
+ * info commands, so the output starts "info ". Since this is
+ * unlikely to be the name of a block device, we can use this
+ * to detect if qemu supports the command.
+ */
+ if (STREQLEN (info, "info ", 5)) {
+ free (info);
+ qemudReportError (dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+ _("'info blockstats' not supported by this qemu"));
+ return -1;
+ }
+
+ stats->rd_req = -1;
+ stats->rd_bytes = -1;
+ stats->wr_req = -1;
+ stats->wr_bytes = -1;
+ stats->errs = -1;
+
+ /* The output format for both qemu & KVM is:
+ * blockdevice: rd_bytes=% wr_bytes=% rd_operations=% wr_operations=%
+ * (repeated for each block device)
+ * where '%' is a 64 bit number.
+ */
+ p = info;
+
+ while (*p) {
+ if (STREQLEN (p, qemu_dev_name, len)
+ && p[len] == ':' && p[len+1] == ' ') {
+
+ eol = strchr (p, '\n'); if (!eol) eol = p + strlen (p);
+
+ p += len+2; /* Skip to first label. */
+
+ while (*p) {
+ if (STREQLEN (p, "rd_bytes=", 9)) {
+ p += 9;
+ if (virStrToLong_ll (p, &dummy, 10, &stats->rd_bytes) == -1)
+ DEBUG ("error reading rd_bytes: %s", p);
+ } else if (STREQLEN (p, "wr_bytes=", 9)) {
+ p += 9;
+ if (virStrToLong_ll (p, &dummy, 10, &stats->wr_bytes) == -1)
+ DEBUG ("error reading wr_bytes: %s", p);
+ } else if (STREQLEN (p, "rd_operations=", 14)) {
+ p += 14;
+ if (virStrToLong_ll (p, &dummy, 10, &stats->rd_req) == -1)
+ DEBUG ("error reading rd_req: %s", p);
+ } else if (STREQLEN (p, "wr_operations=", 14)) {
+ p += 14;
+ if (virStrToLong_ll (p, &dummy, 10, &stats->wr_req) == -1)
+ DEBUG ("error reading wr_req: %s", p);
+ } else
+ DEBUG ("unknown block stat near %s", p);
+
+ /* Skip to next label. */
+ p = strchr (p, ' ');
+ if (!p || p >= eol) break;
+ p++;
+ }
+
+ goto done;
+ }
+
+ /* Skip to next line. */
+ p = strchr (p, '\n');
+ if (!p) break;
+ p++;
+ }
+
+ /* If we reach here then the device was not found. */
+ free (info);
+ qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+ _("device not found: %s (%s)"), path, qemu_dev_name);
+ return -1;
+
+ done:
+ free (info);
+ return 0;
+}
+
static int
qemudDomainInterfaceStats (virDomainPtr dom,
const char *path,
@@ -2928,7 +3069,7 @@
NULL, /* domainMigratePrepare */
NULL, /* domainMigratePerform */
NULL, /* domainMigrateFinish */
- NULL, /* domainBlockStats */
+ qemudDomainBlockStats, /* domainBlockStats */
qemudDomainInterfaceStats, /* domainInterfaceStats */
NULL, /* nodeGetCellsFreeMemory */
NULL, /* getFreeMemory */
More information about the libvir-list
mailing list