The virDomainGetBlockInfo API allows query physical block
extent and allocated block extent. These are normally the
same value unless storing a special format like qcow2
inside a block device. In this scenario we can query QEMU
to get the actual allocated extent.
Since last time:
- Return fatal error in text monitor
- Only invoke monitor command for block devices
- Fix error handling JSON code
* src/qemu/qemu_driver.c: Fill in block aloction extent when VM
is running
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
API to query the highest block extent via info blockstats
---
src/qemu/qemu_driver.c | 33 ++++++++++++---
src/qemu/qemu_monitor.c | 16 +++++++
src/qemu/qemu_monitor.h | 4 ++
src/qemu/qemu_monitor_json.c | 94 ++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_monitor_json.h | 3 +
src/qemu/qemu_monitor_text.c | 10 ++++
src/qemu/qemu_monitor_text.h | 4 +-
7 files changed, 156 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 4ef2f57..356c4be 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -9651,6 +9651,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
int fd = -1;
off_t end;
virStorageFileMetadata meta;
+ virDomainDiskDefPtr disk = NULL;
struct stat sb;
int i;
@@ -9677,19 +9678,17 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
for (i = 0 ; i < vm->def->ndisks ; i++) {
if (vm->def->disks[i]->src != NULL &&
STREQ (vm->def->disks[i]->src, path)) {
- ret = 0;
+ disk = vm->def->disks[i];
break;
}
}
- if (ret != 0) {
+ if (!disk) {
qemuReportError(VIR_ERR_INVALID_ARG,
_("invalid path %s not assigned to domain"), path);
goto cleanup;
}
- ret = -1;
-
/* The path is correct, now try to open it and get its size. */
fd = open (path, O_RDONLY);
if (fd == -1) {
@@ -9740,11 +9739,31 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
if (meta.capacity)
info->capacity = meta.capacity;
- /* XXX allocation will need to be pulled from QEMU for
- * the qcow inside LVM case */
+ /* Set default value .. */
info->allocation = info->physical;
- ret = 0;
+ /* ..but if guest is running & not using raw
+ disk format and on a block device, then query
+ highest allocated extent from QEMU */
+ if (virDomainObjIsActive(vm) &&
+ disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK &&
+ meta.format != VIR_STORAGE_FILE_RAW &&
+ S_ISBLK(sb.st_mode)) {
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ if (qemuDomainObjBeginJob(vm) < 0)
+ goto cleanup;
+
+ qemuDomainObjEnterMonitor(vm);
+ ret = qemuMonitorGetBlockExtent(priv->mon,
+ disk->info.alias,
+ &info->allocation);
+ qemuDomainObjExitMonitor(vm);
+
+ if (qemuDomainObjEndJob(vm) == 0)
+ vm = NULL;
+ } else {
+ ret = 0;
+ }
cleanup:
if (fd != -1)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 582225e..efaf74a 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1009,6 +1009,22 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
return ret;
}
+int qemuMonitorGetBlockExtent(qemuMonitorPtr mon,
+ const char *devname,
+ unsigned long long *extent)
+{
+ int ret;
+ DEBUG("mon=%p, fd=%d, devname=%p",
+ mon, mon->fd, devname);
+
+ if (mon->json)
+ ret = qemuMonitorJSONGetBlockExtent(mon, devname, extent);
+ else
+ ret = qemuMonitorTextGetBlockExtent(mon, devname, extent);
+
+ return ret;
+}
+
int qemuMonitorSetVNCPassword(qemuMonitorPtr mon,
const char *password)
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 7b1589e..adfb3bc 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -185,6 +185,10 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
long long *wr_bytes,
long long *errs);
+int qemuMonitorGetBlockExtent(qemuMonitorPtr mon,
+ const char *devname,
+ unsigned long long *extent);
+
int qemuMonitorSetVNCPassword(qemuMonitorPtr mon,
const char *password);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 6d8f328..31a66f1 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1186,6 +1186,100 @@ cleanup:
}
+int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
+ const char *devname,
+ unsigned long long *extent)
+{
+ int ret = -1;
+ int i;
+ int found = 0;
+ virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
+ NULL);
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr devices;
+
+ *extent = 0;
+
+ if (!cmd)
+ return -1;
+
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ devices = virJSONValueObjectGet(reply, "return");
+ if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("blockstats reply was missing device list"));
+ goto cleanup;
+ }
+
+ for (i = 0 ; i < virJSONValueArraySize(devices) ; i++) {
+ virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
+ virJSONValuePtr stats;
+ virJSONValuePtr parent;
+ const char *thisdev;
+ if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("blockstats device entry was not in expected
format"));
+ goto cleanup;
+ }
+
+ if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("blockstats device entry was not in expected
format"));
+ goto cleanup;
+ }
+
+ /* New QEMU has separate names for host & guest side of the disk
+ * and libvirt gives the host side a 'drive-' prefix. The passed
+ * in devname is the guest side though
+ */
+ if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
+ thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
+
+ if (STRNEQ(thisdev, devname))
+ continue;
+
+ found = 1;
+ if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL ||
+ parent->type != VIR_JSON_TYPE_OBJECT) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("blockstats parent entry was not in expected
format"));
+ goto cleanup;
+ }
+
+ if ((stats = virJSONValueObjectGet(parent, "stats")) == NULL ||
+ stats->type != VIR_JSON_TYPE_OBJECT) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("blockstats stats entry was not in expected
format"));
+ goto cleanup;
+ }
+
+ if (virJSONValueObjectGetNumberUlong(stats, "wr_highest_offset",
extent) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot read %s statistic"),
+ "wr_highest_offset");
+ goto cleanup;
+ }
+ }
+
+ if (!found) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find statistics for device '%s'"),
devname);
+ goto cleanup;
+ }
+ ret = 0;
+
+cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
const char *password)
{
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 26fc865..14597f4 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -56,6 +56,9 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
long long *wr_req,
long long *wr_bytes,
long long *errs);
+int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
+ const char *devname,
+ unsigned long long *extent);
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index d725d6d..72e3fd5 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -711,6 +711,16 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
}
+int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ const char *devname ATTRIBUTE_UNUSED,
+ unsigned long long *extent ATTRIBUTE_UNUSED)
+{
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("unable to query block extent with this QEMU"));
+ return -1;
+}
+
+
static int
qemuMonitorSendVNCPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
qemuMonitorMessagePtr msg,
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 2a62c7e..6fb7d7a 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -55,7 +55,9 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
long long *wr_req,
long long *wr_bytes,
long long *errs);
-
+int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon,
+ const char *devname,
+ unsigned long long *extent);
int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon,
const char *password);
--
1.6.6.1