mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-25 23:25:24 +00:00
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:
parent
b6dd366ad2
commit
b1b5b51ae8
@ -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 */
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user