mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 21:55:25 +00:00
qemu: monitor: Implement HMP version for listing all block device stats
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.
This commit is contained in:
parent
fc4713454d
commit
f6563bc361
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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 \
|
||||
|
@ -12,6 +12,10 @@
|
||||
# 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user