From b1b5b51ae83c049cfbaa98651721416a5d8fc5c6 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Tue, 13 Sep 2011 15:49:50 +0200 Subject: [PATCH] qemu: Check for ejected media during startup and migration If the daemon is restarted so we reconnect to monitor, cdrom media can be ejected. In that case we don't want to show it in domain xml, or require it on migration destination. To check for disk status use 'info block' monitor command. --- src/qemu/qemu_conf.h | 6 +++ src/qemu/qemu_driver.c | 7 +++ src/qemu/qemu_hotplug.c | 34 ++++++++++++++ src/qemu/qemu_hotplug.h | 2 + src/qemu/qemu_monitor.c | 19 ++++++++ src/qemu/qemu_monitor.h | 4 ++ src/qemu/qemu_monitor_json.c | 89 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 ++ src/qemu/qemu_monitor_text.c | 90 ++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 3 ++ src/qemu/qemu_process.c | 3 ++ 11 files changed, 260 insertions(+) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e8b92a4982..ff5cf23a52 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -165,4 +165,10 @@ void qemuDriverUnlock(struct qemud_driver *driver); int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename); +struct qemuDomainDiskInfo { + bool removable; + bool locked; + bool tray_open; +}; + #endif /* __QEMUD_CONF_H */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fdd04e81eb..ffa8b4c0fb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7985,6 +7985,13 @@ qemuDomainMigrateBegin3(virDomainPtr domain, goto endjob; } + /* Check if there is any ejected media. + * We don't want to require them on the destination. + */ + + if (qemuDomainCheckEjectableMedia(driver, vm) < 0) + goto endjob; + if (!(xml = qemuMigrationBegin(driver, vm, xmlin, cookieout, cookieoutlen))) goto endjob; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 6cfe392f2e..3adec4896f 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -150,6 +150,40 @@ error: return -1; } +int +qemuDomainCheckEjectableMedia(struct qemud_driver *driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + int ret = -1; + int i; + + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDefPtr disk = vm->def->disks[i]; + struct qemuDomainDiskInfo info; + + if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) + continue; + + memset(&info, 0, sizeof(info)); + + qemuDomainObjEnterMonitor(driver, vm); + if (qemuMonitorGetBlockInfo(priv->mon, disk->info.alias, &info) < 0) { + qemuDomainObjExitMonitor(driver, vm); + goto cleanup; + } + qemuDomainObjExitMonitor(driver, vm); + + if (info.tray_open && disk->src) + VIR_FREE(disk->src); + } + + ret = 0; + +cleanup: + return ret; +} + int qemuDomainAttachPciDiskDevice(struct qemud_driver *driver, virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 65d1d30536..aaaed88adb 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -31,6 +31,8 @@ int qemuDomainChangeEjectableMedia(struct qemud_driver *driver, virDomainObjPtr vm, virDomainDiskDefPtr disk, bool force); +int qemuDomainCheckEjectableMedia(struct qemud_driver *driver, + virDomainObjPtr vm); int qemuDomainAttachPciDiskDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainDiskDefPtr disk); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 6e8413119c..c9dd69e061 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1223,6 +1223,25 @@ int qemuMonitorGetMemoryStats(qemuMonitorPtr mon, return ret; } +int qemuMonitorGetBlockInfo(qemuMonitorPtr mon, + const char *devname, + struct qemuDomainDiskInfo *info) +{ + int ret; + + VIR_DEBUG("mon=%p dev=%p info=%p", mon, devname, info); + if (!mon) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (mon->json) + ret = qemuMonitorJSONGetBlockInfo(mon, devname, info); + else + ret = qemuMonitorTextGetBlockInfo(mon, devname, info); + return ret; +} int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, const char *dev_name, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index a64c2c39fe..3ec78ad66d 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -28,6 +28,7 @@ # include "internal.h" # include "domain_conf.h" +# include "qemu_conf.h" # include "hash.h" typedef struct _qemuMonitor qemuMonitor; @@ -235,6 +236,9 @@ int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon, int qemuMonitorGetMemoryStats(qemuMonitorPtr mon, virDomainMemoryStatPtr stats, unsigned int nr_stats); +int qemuMonitorGetBlockInfo(qemuMonitorPtr mon, + const char *devname, + struct qemuDomainDiskInfo *info); int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, const char *dev_name, long long *rd_req, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index b7206176d9..c4f8360e9d 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -1350,6 +1350,95 @@ cleanup: } +int qemuMonitorJSONGetBlockInfo(qemuMonitorPtr mon, + const char *devname, + struct qemuDomainDiskInfo *info) +{ + int ret = 0; + bool found = false; + int i; + + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-block", + NULL); + virJSONValuePtr reply = NULL; + virJSONValuePtr devices; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + if (ret < 0) + goto cleanup; + + ret = -1; + + devices = virJSONValueObjectGet(reply, "return"); + if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("block info reply was missing device list")); + goto cleanup; + } + + for (i = 0; i < virJSONValueArraySize(devices); i++) { + virJSONValuePtr dev = virJSONValueArrayGet(devices, i); + const char *thisdev; + + if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("block info device entry was not in expected format")); + goto cleanup; + } + + if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("block info device entry was not in expected format")); + goto cleanup; + } + + if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX)) + thisdev += strlen(QEMU_DRIVE_HOST_PREFIX); + + if (STRNEQ(thisdev, devname)) + continue; + + found = true; + if (virJSONValueObjectGetBoolean(dev, "removable", &info->removable) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot read %s value"), + "removable"); + goto cleanup; + } + + if (virJSONValueObjectGetBoolean(dev, "locked", &info->locked) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot read %s value"), + "locked"); + goto cleanup; + } + + /* Don't check for success here, because 'tray-open' is presented iff + * medium is ejected. + */ + virJSONValueObjectGetBoolean(dev, "tray-open", &info->tray_open); + + break; + } + + if (!found) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find info for device '%s'"), + devname); + goto cleanup; + } + + ret = 0; + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon, const char *dev_name, long long *rd_req, diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 1a40716251..a638b41ca8 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -62,6 +62,9 @@ int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon, int qemuMonitorJSONGetMemoryStats(qemuMonitorPtr mon, virDomainMemoryStatPtr stats, unsigned int nr_stats); +int qemuMonitorJSONGetBlockInfo(qemuMonitorPtr mon, + const char *devname, + struct qemuDomainDiskInfo *info); int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon, const char *dev_name, long long *rd_req, diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 57e74a8e37..51e8c5c177 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -768,6 +768,96 @@ int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, } +int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon, + const char *devname, + struct qemuDomainDiskInfo *info) +{ + char *reply = NULL; + int ret = -1; + char *dummy; + const char *p, *eol; + int devnamelen = strlen(devname); + int tmp; + + if (qemuMonitorHMPCommand(mon, "info block", &reply) < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("info block command failed")); + goto cleanup; + } + + if (strstr(reply, "\ninfo ")) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", + _("info block not supported by this qemu")); + goto cleanup; + } + + /* The output looks like this: + * drive-ide0-0-0: removable=0 file= ro=0 drv=raw encrypted=0 + * drive-ide0-1-0: removable=1 locked=0 file= ro=1 drv=raw encrypted=0 + */ + p = reply; + + while (*p) { + if (STRPREFIX(p, QEMU_DRIVE_HOST_PREFIX)) + p += strlen(QEMU_DRIVE_HOST_PREFIX); + + if (STREQLEN(p, devname, devnamelen) && + p[devnamelen] == ':' && p[devnamelen+1] == ' ') { + + eol = strchr(p, '\n'); + if (!eol) + eol = p + strlen(p); + + p += devnamelen + 2; /*Skip to first label. */ + + while (*p) { + if (STRPREFIX(p, "removable=")) { + p += strlen("removable="); + if (virStrToLong_i(p, &dummy, 10, &tmp) == -1) + VIR_DEBUG("error reading removable: %s", p); + else + info->removable = p != NULL; + } else if (STRPREFIX(p, "locked=")) { + p += strlen("locked="); + if (virStrToLong_i(p, &dummy, 10, &tmp) == -1) + VIR_DEBUG("error reading locked: %s", p); + else + info->locked = p ? true : false; + } else if (STRPREFIX(p, "tray_open=")) { + p += strlen("tray_open="); + if (virStrToLong_i(p, &dummy, 10, &tmp) == -1) + VIR_DEBUG("error reading tray_open: %s", p); + else + info->tray_open = p ? true : false; + } else { + /* ignore because we don't parse all options */ + } + + /* skip to next label */ + p = strchr(p, ' '); + if (!p || p >= eol) break; + p++; + } + + ret = 0; + goto cleanup; + } + + /* skip to next line */ + p = strchr(p, '\n'); + if (!p) break; + p++; + } + + qemuReportError(VIR_ERR_INVALID_ARG, + _("no info for device '%s'"), devname); + +cleanup: + VIR_FREE(reply); + return ret; +} + int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, const char *dev_name, long long *rd_req, diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 207001d699..cc2a252051 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -59,6 +59,9 @@ int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon, int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, virDomainMemoryStatPtr stats, unsigned int nr_stats); +int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon, + const char *devname, + struct qemuDomainDiskInfo *info); int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, const char *dev_name, long long *rd_req, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 4c6b4a4f94..a7fe86c82f 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2654,6 +2654,9 @@ qemuProcessReconnect(void *opaque) if (qemuProcessFiltersInstantiate(conn, obj->def)) goto error; + if (qemuDomainCheckEjectableMedia(driver, obj) < 0) + goto error; + if (qemuProcessRecoverJob(driver, obj, conn, &oldjob) < 0) goto error;