[libvirt] [PATCH] qemu: Check for ejected media during startup and migration

Michal Privoznik mprivozn at redhat.com
Tue Sep 13 16:14:23 UTC 2011


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.
---
NB, the 'info block' is currently not updated yet. The qemu patches
that extend the monitor command are in a queue (that will be merged
quickly):
    http://repo.or.cz/w/qemu/kevin.git
head for-anthony
The commit that introduce requested feature:
    http://repo.or.cz/w/qemu/kevin.git/commitdiff/e4def80b36231e161b91fa984cd0d73b45668f00

 src/qemu/qemu_conf.h         |    6 +++
 src/qemu/qemu_driver.c       |    7 +++
 src/qemu/qemu_hotplug.c      |   31 ++++++++++++++
 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, 257 insertions(+), 0 deletions(-)

diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index e8b92a4..ff5cf23 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 b94d1c4..b10a56f 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -8236,6 +8236,13 @@ qemuDomainMigrateBegin3(virDomainPtr domain,
         goto endjob;
     }
 
+    /* Check if there is and 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 6cfe392..91ac78a 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -150,6 +150,37 @@ 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;
+
+        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 65d1d30..aaaed88 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 fca8fa4..7005564 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1215,6 +1215,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 *devname,
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 390eeff..a05d548 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;
@@ -212,6 +213,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 *devname,
                                  long long *rd_req,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 3a81ec8..812a78e 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1337,6 +1337,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 *devname,
                                      long long *rd_req,
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index dfeba7e..24d6aec 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -60,6 +60,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 *devname,
                                      long long *rd_req,
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 0b50ba2..50b9836 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -750,6 +750,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 ? true : false;
+                } 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 *devname,
                                      long long *rd_req,
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 7cc3172..68e16b3 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -57,6 +57,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 *devname,
                                      long long *rd_req,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index f8a8475..7b5d10a 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -2612,6 +2612,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;
 
-- 
1.7.3.4




More information about the libvir-list mailing list