]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: Check for ejected media during startup and migration
authorMichal Privoznik <mprivozn@redhat.com>
Tue, 13 Sep 2011 13:49:50 +0000 (15:49 +0200)
committerMichal Privoznik <mprivozn@redhat.com>
Wed, 28 Sep 2011 17:49:11 +0000 (19:49 +0200)
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
src/qemu/qemu_driver.c
src/qemu/qemu_hotplug.c
src/qemu/qemu_hotplug.h
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
src/qemu/qemu_process.c

index e8b92a49820337bc9b8bdf85834ab9b25da3cb71..ff5cf23a52588ff42adf8c28c12f793c214c2c95 100644 (file)
@@ -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 */
index fdd04e81ebb059d8f2dff3208dea13e3d3f40a15..ffa8b4c0fb369c138b5a9c65db9a2c882df3f758 100644 (file)
@@ -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;
index 6cfe392f2e40004b0cfeb0f25218e898e02cfc2a..3adec4896fe82e7894b38b56eda1051dbfd09568 100644 (file)
@@ -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,
index 65d1d30536dae5b15c1590fbb9653094da3fe019..aaaed88adb7ef0d200541db0c9fc23e1118e3558 100644 (file)
@@ -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);
index 6e8413119c8caacadfe985972a6760d867b1b520..c9dd69e061bf48d43a63951b0fba2b88735d3d4c 100644 (file)
@@ -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,
index a64c2c39fe6c7c3dd6e5147e9431e0427bb4a7e6..3ec78ad66d73779486b4f150e55aafede0118a15 100644 (file)
@@ -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,
index b7206176d9361db9ca06b4bf06894bbeed669ece..c4f8360e9d914f1e2774ad03145cd1ced3df43ea 100644 (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,
                                      const char *dev_name,
                                      long long *rd_req,
index 1a4071625184e640ec0dd95ae6b706084db823d2..a638b41ca89df13aa139795afec505173b7cadb4 100644 (file)
@@ -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,
index 57e74a8e37d5e05f74fafe795b4532e04020746e..51e8c5c1776c6f22ee6fa7efc128482d50169fbd 100644 (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,
                                      const char *dev_name,
                                      long long *rd_req,
index 207001d699de3631ff751b43a7589d1e45c376be..cc2a252051a91200536c6a372f4fa8c5bb6c381f 100644 (file)
@@ -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,
index 4c6b4a4f94bea2269b87016e91f2dceaa3403dd4..a7fe86c82ffc73f7a2ff415868f2c8da782fc1b5 100644 (file)
@@ -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;