mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-11 23:37:42 +00:00
Query block allocation extent from QEMU monitor
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
This commit is contained in:
parent
dd1058fa8f
commit
ebb0c19c48
@ -9453,6 +9453,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
|
|||||||
int fd = -1;
|
int fd = -1;
|
||||||
off_t end;
|
off_t end;
|
||||||
virStorageFileMetadata meta;
|
virStorageFileMetadata meta;
|
||||||
|
virDomainDiskDefPtr disk = NULL;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -9479,19 +9480,17 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
|
|||||||
for (i = 0 ; i < vm->def->ndisks ; i++) {
|
for (i = 0 ; i < vm->def->ndisks ; i++) {
|
||||||
if (vm->def->disks[i]->src != NULL &&
|
if (vm->def->disks[i]->src != NULL &&
|
||||||
STREQ (vm->def->disks[i]->src, path)) {
|
STREQ (vm->def->disks[i]->src, path)) {
|
||||||
ret = 0;
|
disk = vm->def->disks[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret != 0) {
|
if (!disk) {
|
||||||
qemuReportError(VIR_ERR_INVALID_ARG,
|
qemuReportError(VIR_ERR_INVALID_ARG,
|
||||||
_("invalid path %s not assigned to domain"), path);
|
_("invalid path %s not assigned to domain"), path);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = -1;
|
|
||||||
|
|
||||||
/* The path is correct, now try to open it and get its size. */
|
/* The path is correct, now try to open it and get its size. */
|
||||||
fd = open (path, O_RDONLY);
|
fd = open (path, O_RDONLY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
@ -9541,11 +9540,31 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
|
|||||||
if (meta.capacity)
|
if (meta.capacity)
|
||||||
info->capacity = meta.capacity;
|
info->capacity = meta.capacity;
|
||||||
|
|
||||||
/* XXX allocation will need to be pulled from QEMU for
|
/* Set default value .. */
|
||||||
* the qcow inside LVM case */
|
|
||||||
info->allocation = info->physical;
|
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:
|
cleanup:
|
||||||
if (fd != -1)
|
if (fd != -1)
|
||||||
|
@ -1060,6 +1060,22 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
return ret;
|
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,
|
int qemuMonitorSetVNCPassword(qemuMonitorPtr mon,
|
||||||
const char *password)
|
const char *password)
|
||||||
|
@ -185,6 +185,10 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
long long *wr_bytes,
|
long long *wr_bytes,
|
||||||
long long *errs);
|
long long *errs);
|
||||||
|
|
||||||
|
int qemuMonitorGetBlockExtent(qemuMonitorPtr mon,
|
||||||
|
const char *devname,
|
||||||
|
unsigned long long *extent);
|
||||||
|
|
||||||
|
|
||||||
int qemuMonitorSetVNCPassword(qemuMonitorPtr mon,
|
int qemuMonitorSetVNCPassword(qemuMonitorPtr mon,
|
||||||
const char *password);
|
const char *password);
|
||||||
|
@ -1143,6 +1143,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,
|
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
|
||||||
const char *password)
|
const char *password)
|
||||||
{
|
{
|
||||||
|
@ -56,6 +56,9 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
long long *wr_req,
|
long long *wr_req,
|
||||||
long long *wr_bytes,
|
long long *wr_bytes,
|
||||||
long long *errs);
|
long long *errs);
|
||||||
|
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
|
||||||
|
const char *devname,
|
||||||
|
unsigned long long *extent);
|
||||||
|
|
||||||
|
|
||||||
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
|
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
|
||||||
|
@ -712,6 +712,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
|
static int
|
||||||
qemuMonitorSendVNCPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
qemuMonitorSendVNCPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
||||||
qemuMonitorMessagePtr msg,
|
qemuMonitorMessagePtr msg,
|
||||||
|
@ -55,7 +55,9 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
|
|||||||
long long *wr_req,
|
long long *wr_req,
|
||||||
long long *wr_bytes,
|
long long *wr_bytes,
|
||||||
long long *errs);
|
long long *errs);
|
||||||
|
int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon,
|
||||||
|
const char *devname,
|
||||||
|
unsigned long long *extent);
|
||||||
|
|
||||||
int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon,
|
int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon,
|
||||||
const char *password);
|
const char *password);
|
||||||
|
Loading…
Reference in New Issue
Block a user