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.
This commit is contained in:
Michal Privoznik 2011-09-13 15:49:50 +02:00
parent b6dd366ad2
commit b1b5b51ae8
11 changed files with 260 additions and 0 deletions

View File

@ -165,4 +165,10 @@ void qemuDriverUnlock(struct qemud_driver *driver);
int qemudLoadDriverConfig(struct qemud_driver *driver, int qemudLoadDriverConfig(struct qemud_driver *driver,
const char *filename); const char *filename);
struct qemuDomainDiskInfo {
bool removable;
bool locked;
bool tray_open;
};
#endif /* __QEMUD_CONF_H */ #endif /* __QEMUD_CONF_H */

View File

@ -7985,6 +7985,13 @@ qemuDomainMigrateBegin3(virDomainPtr domain,
goto endjob; 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, if (!(xml = qemuMigrationBegin(driver, vm, xmlin,
cookieout, cookieoutlen))) cookieout, cookieoutlen)))
goto endjob; goto endjob;

View File

@ -150,6 +150,40 @@ error:
return -1; 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, int qemuDomainAttachPciDiskDevice(struct qemud_driver *driver,
virDomainObjPtr vm, virDomainObjPtr vm,

View File

@ -31,6 +31,8 @@ int qemuDomainChangeEjectableMedia(struct qemud_driver *driver,
virDomainObjPtr vm, virDomainObjPtr vm,
virDomainDiskDefPtr disk, virDomainDiskDefPtr disk,
bool force); bool force);
int qemuDomainCheckEjectableMedia(struct qemud_driver *driver,
virDomainObjPtr vm);
int qemuDomainAttachPciDiskDevice(struct qemud_driver *driver, int qemuDomainAttachPciDiskDevice(struct qemud_driver *driver,
virDomainObjPtr vm, virDomainObjPtr vm,
virDomainDiskDefPtr disk); virDomainDiskDefPtr disk);

View File

@ -1223,6 +1223,25 @@ int qemuMonitorGetMemoryStats(qemuMonitorPtr mon,
return ret; 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, int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
const char *dev_name, const char *dev_name,

View File

@ -28,6 +28,7 @@
# include "internal.h" # include "internal.h"
# include "domain_conf.h" # include "domain_conf.h"
# include "qemu_conf.h"
# include "hash.h" # include "hash.h"
typedef struct _qemuMonitor qemuMonitor; typedef struct _qemuMonitor qemuMonitor;
@ -235,6 +236,9 @@ int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon,
int qemuMonitorGetMemoryStats(qemuMonitorPtr mon, int qemuMonitorGetMemoryStats(qemuMonitorPtr mon,
virDomainMemoryStatPtr stats, virDomainMemoryStatPtr stats,
unsigned int nr_stats); unsigned int nr_stats);
int qemuMonitorGetBlockInfo(qemuMonitorPtr mon,
const char *devname,
struct qemuDomainDiskInfo *info);
int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
const char *dev_name, const char *dev_name,
long long *rd_req, long long *rd_req,

View File

@ -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, int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
const char *dev_name, const char *dev_name,
long long *rd_req, long long *rd_req,

View File

@ -62,6 +62,9 @@ int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon,
int qemuMonitorJSONGetMemoryStats(qemuMonitorPtr mon, int qemuMonitorJSONGetMemoryStats(qemuMonitorPtr mon,
virDomainMemoryStatPtr stats, virDomainMemoryStatPtr stats,
unsigned int nr_stats); unsigned int nr_stats);
int qemuMonitorJSONGetBlockInfo(qemuMonitorPtr mon,
const char *devname,
struct qemuDomainDiskInfo *info);
int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon, int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
const char *dev_name, const char *dev_name,
long long *rd_req, long long *rd_req,

View File

@ -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=<path> ro=0 drv=raw encrypted=0
* drive-ide0-1-0: removable=1 locked=0 file=<path> 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, int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
const char *dev_name, const char *dev_name,
long long *rd_req, long long *rd_req,

View File

@ -59,6 +59,9 @@ int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon,
int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon,
virDomainMemoryStatPtr stats, virDomainMemoryStatPtr stats,
unsigned int nr_stats); unsigned int nr_stats);
int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon,
const char *devname,
struct qemuDomainDiskInfo *info);
int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
const char *dev_name, const char *dev_name,
long long *rd_req, long long *rd_req,

View File

@ -2654,6 +2654,9 @@ qemuProcessReconnect(void *opaque)
if (qemuProcessFiltersInstantiate(conn, obj->def)) if (qemuProcessFiltersInstantiate(conn, obj->def))
goto error; goto error;
if (qemuDomainCheckEjectableMedia(driver, obj) < 0)
goto error;
if (qemuProcessRecoverJob(driver, obj, conn, &oldjob) < 0) if (qemuProcessRecoverJob(driver, obj, conn, &oldjob) < 0)
goto error; goto error;