]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: monitor: Implement HMP version for listing all block device stats
authorPeter Krempa <pkrempa@redhat.com>
Mon, 9 Mar 2015 16:23:49 +0000 (17:23 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Wed, 11 Mar 2015 10:28:04 +0000 (11:28 +0100)
Add a different version of parser for "info blockstats" that basically
parses the same information as the existing copy of the function.

This will allow us to remove the single device version
qemuMonitorGetBlockStatsInfo in the future.

The new implementation uses few new helpers so it should be more
understandable and provides a test case to verify that it works.

src/qemu/qemu_monitor.c
src/qemu/qemu_monitor_text.c
src/qemu/qemu_monitor_text.h
tests/Makefile.am
tests/qemumonitortest.c

index 95a6989bf0969a0e08d49904b4d712ecefe4b10f..149e7431d9ec7633af8d3af54c37ca2a5b171d5a 100644 (file)
@@ -1867,8 +1867,20 @@ qemuMonitorGetAllBlockStatsInfo(qemuMonitorPtr mon,
     if (!(*ret_stats = virHashCreate(10, virHashValueFree)))
         goto error;
 
-    if (qemuMonitorJSONGetAllBlockStatsInfo(mon, *ret_stats, backingChain) < 0)
-        goto error;
+    if (mon->json) {
+        if (qemuMonitorJSONGetAllBlockStatsInfo(mon, *ret_stats, backingChain) < 0)
+            goto error;
+    } else {
+         if (backingChain) {
+             virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+                            _("text monitor doesn't support block stats for "
+                              "backing chain members"));
+             goto error;
+         }
+
+         if (qemuMonitorTextGetAllBlockStatsInfo(mon, *ret_stats) < 0)
+             goto error;
+    }
 
     return 0;
 
index 2de281ff355fbff119eb5ba154b35c5cf03e20c1..8b2ef90ee8edb2c85e5236c47230b208d6302c62 100644 (file)
@@ -838,6 +838,135 @@ int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon,
     return ret;
 }
 
+
+int
+qemuMonitorTextGetAllBlockStatsInfo(qemuMonitorPtr mon,
+                                    virHashTablePtr hash)
+{
+    qemuBlockStatsPtr stats = NULL;
+    char *info = NULL;
+    char *dev_name;
+    char **lines = NULL;
+    char **values = NULL;
+    char *line;
+    char *value;
+    char *key;
+    size_t i;
+    size_t j;
+    int ret = -1;
+
+    if (qemuMonitorHMPCommand(mon, "info blockstats", &info) < 0)
+        goto cleanup;
+
+    /* 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 (strstr(info, "\ninfo ")) {
+        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                       _("'info blockstats' not supported by this qemu"));
+        goto cleanup;
+    }
+
+    /* 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.
+     */
+    if (!(lines = virStringSplit(info, "\n", 0)))
+        goto cleanup;
+
+    for (i = 0; lines[i] && *lines[i]; i++) {
+        line = lines[i];
+
+        if (VIR_ALLOC(stats) < 0)
+            goto cleanup;
+
+        /* set the entries to -1, the JSON monitor enforces them, but it would
+         * be overly complex to achieve this here */
+        stats->rd_req = -1;
+        stats->rd_bytes = -1;
+        stats->wr_req = -1;
+        stats->wr_bytes = -1;
+        stats->rd_total_times = -1;
+        stats->wr_total_times = -1;
+        stats->flush_req = -1;
+        stats->flush_total_times = -1;
+
+        /* extract device name and make sure that it's followed by
+         * a colon and space */
+        dev_name = line;
+        if (!(line = strchr(line, ':')) && line[1] != ' ') {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("info blockstats reply was malformed"));
+            goto cleanup;
+        }
+
+        *line = '\0';
+        line += 2;
+
+        if (STRPREFIX(dev_name, QEMU_DRIVE_HOST_PREFIX))
+            dev_name += strlen(QEMU_DRIVE_HOST_PREFIX);
+
+        if (!(values = virStringSplit(line, " ", 0)))
+            goto cleanup;
+
+        for (j = 0; values[j] && *values[j]; j++) {
+            key = values[j];
+
+            if (!(value = strchr(key, '='))) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("info blockstats entry was malformed"));
+                goto cleanup;
+            }
+
+            *value = '\0';
+            value++;
+
+#define QEMU_MONITOR_TEXT_READ_BLOCK_STAT(NAME, VAR)                           \
+            if (STREQ(key, NAME)) {                                            \
+                if (virStrToLong_ll(value, NULL, 10, &VAR) < 0) {              \
+                    virReportError(VIR_ERR_INTERNAL_ERROR,                     \
+                                   _("'info blockstats' contains malformed "   \
+                                     "parameter '%s' value '%s'"), NAME, value);\
+                    goto cleanup;                                              \
+                }                                                              \
+                continue;                                                      \
+            }
+
+            QEMU_MONITOR_TEXT_READ_BLOCK_STAT("rd_bytes", stats->rd_bytes);
+            QEMU_MONITOR_TEXT_READ_BLOCK_STAT("wr_bytes", stats->wr_bytes);
+            QEMU_MONITOR_TEXT_READ_BLOCK_STAT("rd_operations", stats->rd_req);
+            QEMU_MONITOR_TEXT_READ_BLOCK_STAT("wr_operations", stats->wr_req);
+            QEMU_MONITOR_TEXT_READ_BLOCK_STAT("rd_total_time_ns", stats->rd_total_times);
+            QEMU_MONITOR_TEXT_READ_BLOCK_STAT("wr_total_time_ns", stats->wr_total_times);
+            QEMU_MONITOR_TEXT_READ_BLOCK_STAT("flush_operations", stats->flush_req);
+            QEMU_MONITOR_TEXT_READ_BLOCK_STAT("flush_total_time_ns", stats->flush_total_times);
+#undef QEMU_MONITOR_TEXT_READ_BLOCK_STAT
+
+            /* log if we get statistic element different from the above */
+            VIR_DEBUG("unknown block stat field '%s'", key);
+        }
+
+        if (virHashAddEntry(hash, dev_name, stats) < 0)
+            goto cleanup;
+        stats = NULL;
+
+        virStringFreeList(values);
+        values = NULL;
+    }
+
+    ret = 0;
+
+ cleanup:
+    virStringFreeList(lines);
+    virStringFreeList(values);
+    VIR_FREE(stats);
+    VIR_FREE(info);
+    return ret;
+}
+
+
 int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
                                      const char *dev_name,
                                      long long *rd_req,
index 695ac28a828b863c23688388ff7d8263e8d457ee..a1bc2b2d84b2851e776646cfa7392ccff0607802 100644 (file)
@@ -60,6 +60,9 @@ int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon,
                                   unsigned int nr_stats);
 int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon,
                                 virHashTablePtr table);
+
+int qemuMonitorTextGetAllBlockStatsInfo(qemuMonitorPtr mon,
+                                        virHashTablePtr hash);
 int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
                                      const char *dev_name,
                                      long long *rd_req,
index 938270ccbf95ff44a96bead94f106e310907c48a..9277c132a9eaff3a0690b217dad0c07685f9ab9d 100644 (file)
@@ -568,8 +568,12 @@ qemuargv2xmltest_LDADD = $(qemu_LDADDS) $(LDADDS)
 qemuhelptest_SOURCES = qemuhelptest.c testutils.c testutils.h
 qemuhelptest_LDADD = $(qemu_LDADDS) $(LDADDS)
 
-qemumonitortest_SOURCES = qemumonitortest.c testutils.c testutils.h
-qemumonitortest_LDADD = $(qemu_LDADDS) $(LDADDS)
+qemumonitortest_SOURCES = \
+       qemumonitortest.c \
+       testutils.c testutils.h \
+       testutilsqemu.c testutilsqemu.h
+qemumonitortest_LDADD = libqemumonitortestutils.la \
+       $(qemu_LDADDS) $(LDADDS)
 
 qemumonitorjsontest_SOURCES = \
        qemumonitorjsontest.c \
index 1c13a89f8be3fbfe478853f2144785e6f07263c4..d73bbf1c9d843b4dca574d4af435924dc8d0a83e 100644 (file)
 # include "internal.h"
 # include "viralloc.h"
 # include "qemu/qemu_monitor.h"
+# include "qemu/qemu_monitor_text.h"
+# include "qemumonitortestutils.h"
+
+# define VIR_FROM_THIS VIR_FROM_NONE
 
 struct testEscapeString
 {
@@ -86,21 +90,104 @@ static int testUnescapeArg(const void *data ATTRIBUTE_UNUSED)
     return 0;
 }
 
+struct blockInfoData {
+    const char *dev;
+    qemuBlockStats data;
+};
+
+static const struct blockInfoData testBlockInfoData[] =
+{
+/* NAME, rd_req, rd_bytes, wr_req, wr_bytes, rd_total_time, wr_total_time, flush_req, flush_total_time */
+    {"vda", {11, 12, 13, 14, 15, 16, 17, 18, 0, 0, 0}},
+    {"vdb", {21, 22, 23, 24, 25, 26, 27, 28, 0, 0, 0}},
+    {"vdc", {31, 32, 33, -1, 35, 36, 37, 38, 0, 0, 0}},
+    {"vdd", {-1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0}},
+    {"vde", {41, 42, 43, 44, 45, 46, 47, 48, 0, 0, 0}}
+};
+
+static const char testBlockInfoReply[] =
+"(qemu) info blockstats\r\n"
+"vda: rd_operations=11 rd_bytes=12 wr_operations=13 wr_bytes=14 rd_total_time_ns=15 wr_total_time_ns=16 flush_operations=17 flush_total_time_ns=18\n"
+"vdb: rd_total_time_ns=25 wr_total_time_ns=26 flush_operations=27 flush_total_time_ns=28 rd_operations=21 rd_bytes=22 wr_operations=23 wr_bytes=24 \n"
+"drive-vdc: rd_operations=31 rd_bytes=32 wr_operations=33 rd_total_time_ns=35 wr_total_time_ns=36 flush_operations=37 flush_total_time_ns=38\n"
+"vdd: \n"
+"vde: rd_operations=41 rd_bytes=42 wr_operations=43 wr_bytes=44 rd_total_time_ns=45 wr_total_time_ns=46 flush_operations=47 flush_total_time_ns=48\n"
+"(qemu) ";
+
+static int
+testMonitorTextBlockInfo(const void *opaque)
+{
+    virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr) opaque;
+    qemuMonitorTestPtr test = qemuMonitorTestNewSimple(false, xmlopt);
+    virHashTablePtr blockstats = NULL;
+    size_t i;
+    int ret = -1;
+
+    if (!test)
+        return -1;
+
+    if (!(blockstats = virHashCreate(10, virHashValueFree)))
+        goto cleanup;
+
+    if (qemuMonitorTestAddItem(test, "info", testBlockInfoReply) < 0)
+        goto cleanup;
+
+    if (qemuMonitorTextGetAllBlockStatsInfo(qemuMonitorTestGetMonitor(test),
+                                            blockstats) < 0)
+        goto cleanup;
+
+    for (i = 0; i < ARRAY_CARDINALITY(testBlockInfoData); i++) {
+        qemuBlockStatsPtr entry;
+
+        if (!(entry = virHashLookup(blockstats, testBlockInfoData[i].dev))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "device '%s' was not found in text block stats reply",
+                           testBlockInfoData[i].dev);
+            goto cleanup;
+        }
+
+        if (memcmp(entry, &testBlockInfoData[i].data, sizeof(qemuBlockStats)) != 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "block stats for device '%s' differ",
+                           testBlockInfoData[i].dev);
+            goto cleanup;
+        }
+    }
+
+    ret = 0;
+
+ cleanup:
+    qemuMonitorTestFree(test);
+    virHashFree(blockstats);
+    return ret;
+}
+
+
 static int
 mymain(void)
 {
+    virDomainXMLOptionPtr xmlopt;
     int result = 0;
 
+    if (virThreadInitialize() < 0 ||
+        !(xmlopt = virQEMUDriverCreateXMLConf(NULL)))
+        return EXIT_FAILURE;
+
+    virEventRegisterDefaultImpl();
+
 # define DO_TEST(_name)                                                 \
     do {                                                                \
         if (virtTestRun("qemu monitor "#_name, test##_name,             \
-                        NULL) < 0) {                                    \
+                        xmlopt) < 0) {                                  \
             result = -1;                                                \
         }                                                               \
     } while (0)
 
     DO_TEST(EscapeArg);
     DO_TEST(UnescapeArg);
+    DO_TEST(MonitorTextBlockInfo);
+
+    virObjectUnref(xmlopt);
 
     return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }