[libvirt] [PATCH v3 0/4] Allow to make disk optional on migration

This is a 3rd version of patches [1]. Diff to v2 includes Daniel's review and some other improvements. The aim of these patches is to mark CDROM and floppy optional, thus they might get dropped during migration, domain startup, restore, etc. The granularity is set on disk level, so user can decide which cd-roms are important and which not. There are 3 levels of optionality: - mandatory - fail if missing for any reason (the default) - requisite - fail if missing on boot up, drop if missing on migrate/restore/revert - optional - drop if missing at any start attempt. To assign a disk one of these, just append 'on_missing' attribute to its <source>. If a domain is then eventually started (migrated, restored, whatever), we check for source path being accessible (currently only qemu driver is supported) from hypervisor's POV. This is done by virFileAccessibleAs() function. Currently, only uid+gid are considered. Ideally, this should be extended to SElinux labeling and cgroups. 1: https://www.redhat.com/archives/libvir-list/2011-October/msg00032.html Michal Privoznik (4): conf: Introduce optional on_missing attribute for cdrom and floppy util: Create virFileAccessibleAs function qemu: implement on_missing on_missing: Emit event on disk source dropping daemon/remote.c | 37 ++++++++++ docs/formatdomain.html.in | 26 ++++++- docs/schemas/domaincommon.rng | 22 +++++- examples/domain-events/events-c/event-test.c | 27 +++++++- examples/domain-events/events-python/event-test.py | 4 + include/libvirt/libvirt.h.in | 28 +++++++ python/libvirt-override-virConnect.py | 9 +++ python/libvirt-override.c | 51 +++++++++++++ src/conf/domain_conf.c | 43 ++++++++++- src/conf/domain_conf.h | 11 +++ src/conf/domain_event.c | 50 +++++++++++++ src/conf/domain_event.h | 7 ++ src/libvirt_private.syms | 4 + src/qemu/qemu_domain.c | 73 +++++++++++++++++++ src/qemu/qemu_domain.h | 4 + src/qemu/qemu_process.c | 4 + src/remote/remote_driver.c | 34 +++++++++ src/remote/remote_protocol.x | 9 ++- src/remote_protocol-structs | 5 ++ src/util/util.c | 76 ++++++++++++++++++++ src/util/util.h | 3 + .../qemuxml2xmlout-disk-cdrom-empty.xml | 31 ++++++++ 22 files changed, 548 insertions(+), 10 deletions(-) create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-cdrom-empty.xml -- 1.7.3.4

This attribute says what to do with cdrom (or floppy) if the source is missing. It accepts: - mandatory - fail if missing for any reason (the default) - requisite - fail if missing on boot up, drop if missing on migrate/restore/revert - optional - drop if missing at any start attempt. However, this patch introduces only XML part of this new functionality. --- docs/formatdomain.html.in | 26 +++++++++++- docs/schemas/domaincommon.rng | 22 +++++++++- src/conf/domain_conf.c | 43 ++++++++++++++++++- src/conf/domain_conf.h | 11 +++++ src/libvirt_private.syms | 2 + .../qemuxml2xmlout-disk-cdrom-empty.xml | 31 ++++++++++++++ 6 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-cdrom-empty.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 12055c2..9102f22 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -891,7 +891,7 @@ <devices> <disk type='file' snapshot='external'> <driver name="tap" type="aio" cache="default"/> - <source file='/var/lib/xen/images/fv0'/> + <source file='/var/lib/xen/images/fv0'/ on_missing='optional'> <target dev='hda' bus='ide'/> <boot order='2'/> <encryption type='...'> @@ -962,7 +962,29 @@ "network", the <code>source</code> may have zero or more <code>host</code> sub-elements used to specify the hosts to connect. - <span class="since">Since 0.0.3</span></dd> + <span class="since">Since 0.0.3</span> + For "file" disk type which represents cdrom or floppy + (the <code>device</code> attribute) it is possible to define + policy what to do with disk if source is not accessible. + This is done by <code>on_missing</code> attribute accepting + these values: + <table class="top_table"> + <tr> + <td> mandatory </td> + <td> fail if missing for any reason (the default) </td> + </tr> + <tr> + <td> requisite </td> + <td> fail if missing on boot up, + drop if missing on migrate/restore/revert </td> + </tr> + <tr> + <td> optional </td> + <td> drop if missing at any start attempt </td> + </tr> + </table> + <span class="since">Since 0.9.7</span> + </dd> <dt><code>target</code></dt> <dd>The <code>target</code> element controls the bus / device under which the disk is exposed to the guest diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index cd1a067..ec92788 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -665,6 +665,17 @@ </interleave> </element> </define> + + <define name="on_missing"> + <attribute name="on_missing"> + <choice> + <value>mandatory</value> + <value>requisite</value> + <value>optional</value> + </choice> + </attribute> + </define> + <!-- A disk description can be either of type file or block The name of the attribute on the source element depends on the type @@ -692,9 +703,14 @@ <interleave> <optional> <element name="source"> - <attribute name="file"> - <ref name="absFilePath"/> - </attribute> + <optional> + <attribute name="file"> + <ref name="absFilePath"/> + </attribute> + </optional> + <optional> + <ref name="on_missing"/> + </optional> <empty/> </element> </optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 5959593..607ea58 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -578,6 +578,12 @@ VIR_ENUM_IMPL(virDomainNumatuneMemMode, VIR_DOMAIN_NUMATUNE_MEM_LAST, "preferred", "interleave"); +VIR_ENUM_IMPL(virDomainOnMissing, VIR_DOMAIN_ON_MISSING_LAST, + "default", + "mandatory", + "requisite", + "optional"); + #define virDomainReportError(code, ...) \ virReportErrorHelper(VIR_FROM_DOMAIN, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) @@ -2319,6 +2325,7 @@ virDomainDiskDefParseXML(virCapsPtr caps, char *devaddr = NULL; virStorageEncryptionPtr encryption = NULL; char *serial = NULL; + char *on_missing = NULL; if (VIR_ALLOC(def) < 0) { virReportOOMError(); @@ -2347,6 +2354,7 @@ virDomainDiskDefParseXML(virCapsPtr caps, switch (def->type) { case VIR_DOMAIN_DISK_TYPE_FILE: source = virXMLPropString(cur, "file"); + on_missing = virXMLPropString(cur, "on_missing"); break; case VIR_DOMAIN_DISK_TYPE_BLOCK: source = virXMLPropString(cur, "dev"); @@ -2646,6 +2654,27 @@ virDomainDiskDefParseXML(virCapsPtr caps, goto error; } + if (on_missing) { + int i; + + if ((i = virDomainOnMissingTypeFromString(on_missing)) < 0) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown on_missing value '%s'"), + on_missing); + goto error; + } + + if (def->device != VIR_DOMAIN_DISK_DEVICE_CDROM && + def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY) { + virDomainReportError(VIR_ERR_INVALID_ARG, + _("Setting disk %s is allowed only for " + "cdrom or floppy"), + on_missing); + goto error; + } + def->on_missing = i; + } + def->src = source; source = NULL; def->dst = target; @@ -2701,6 +2730,7 @@ cleanup: VIR_FREE(devaddr); VIR_FREE(serial); virStorageEncryptionFree(encryption); + VIR_FREE(on_missing); return def; @@ -9176,6 +9206,7 @@ virDomainDiskDefFormat(virBufferPtr buf, const char *iomode = virDomainDiskIoTypeToString(def->iomode); const char *ioeventfd = virDomainIoEventFdTypeToString(def->ioeventfd); const char *event_idx = virDomainVirtioEventIdxTypeToString(def->event_idx); + const char *on_missing = virDomainOnMissingTypeToString(def->on_missing); if (!type) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -9234,11 +9265,17 @@ virDomainDiskDefFormat(virBufferPtr buf, virBufferAsprintf(buf, "/>\n"); } - if (def->src || def->nhosts > 0) { + if (def->src || def->nhosts > 0 || + def->on_missing) { switch (def->type) { case VIR_DOMAIN_DISK_TYPE_FILE: - virBufferEscapeString(buf, " <source file='%s'/>\n", - def->src); + virBufferAsprintf(buf," <source"); + if (def->src) + virBufferEscapeString(buf, " file='%s'", def->src); + if (def->on_missing) + virBufferEscapeString(buf, " on_missing='%s'", + on_missing); + virBufferAsprintf(buf, "/>\n"); break; case VIR_DOMAIN_DISK_TYPE_BLOCK: virBufferEscapeString(buf, " <source dev='%s'/>\n", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 2119b5a..9df6df9 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -269,6 +269,15 @@ enum virDomainSnapshotState { VIR_DOMAIN_DISK_SNAPSHOT = VIR_DOMAIN_LAST, }; +enum virDomainOnMissing { + VIR_DOMAIN_ON_MISSING_DEFAULT = 0, + VIR_DOMAIN_ON_MISSING_MANDATORY, + VIR_DOMAIN_ON_MISSING_REQUISITE, + VIR_DOMAIN_ON_MISSING_OPTIONAL, + + VIR_DOMAIN_ON_MISSING_LAST +}; + /* Stores the virtual disk configuration */ typedef struct _virDomainDiskDef virDomainDiskDef; typedef virDomainDiskDef *virDomainDiskDefPtr; @@ -292,6 +301,7 @@ struct _virDomainDiskDef { int ioeventfd; int event_idx; int snapshot; /* enum virDomainDiskSnapshot */ + int on_missing; /* enum virDomainOnMissing */ unsigned int readonly : 1; unsigned int shared : 1; unsigned int transient : 1; @@ -1935,4 +1945,5 @@ VIR_ENUM_DECL(virDomainTimerTrack) VIR_ENUM_DECL(virDomainTimerTickpolicy) VIR_ENUM_DECL(virDomainTimerMode) +VIR_ENUM_DECL(virDomainOnMissing) #endif /* __DOMAIN_CONF_H */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index dedbd16..ae18a0d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -387,6 +387,8 @@ virDomainObjSetState; virDomainObjTaint; virDomainObjUnlock; virDomainObjUnref; +virDomainOnMissingTypeFromString; +virDomainOnMissingTypeToString; virDomainPausedReasonTypeFromString; virDomainPausedReasonTypeToString; virDomainPciRombarModeTypeFromString; diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-cdrom-empty.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-cdrom-empty.xml new file mode 100644 index 0000000..a4ec9e0 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-cdrom-empty.xml @@ -0,0 +1,31 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219100</memory> + <currentMemory>219100</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <disk type='file' device='cdrom'> + <target dev='hdc' bus='ide'/> + <source on_missing='optional'/> + <readonly/> + <address type='drive' controller='0' bus='1' unit='0'/> + </disk> + <controller type='ide' index='0'/> + <memballoon model='virtio'/> + </devices> +</domain> -- 1.7.3.4

On Wed, Oct 19, 2011 at 04:42:56PM +0200, Michal Privoznik wrote:
This attribute says what to do with cdrom (or floppy) if the source is missing. It accepts: - mandatory - fail if missing for any reason (the default) - requisite - fail if missing on boot up, drop if missing on migrate/restore/revert - optional - drop if missing at any start attempt.
I find 'on_missing' to be a bit of wierd attribute name given the attribute values. Seeing it doesn't immediately make it clear to me what it does. If we're going to call it on_missing, then the attribute values need some other kind of name on_missing='fail|continue|drop' Or to keep these attribute names, I'd go for something like startPolicy="mandatory|requisite|optional"; If we resolve the naming decision, the patch looks fine Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This function checks if a given path is accessible under given uid and gid. --- src/util/util.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util/util.h | 3 ++ 2 files changed, 79 insertions(+), 0 deletions(-) diff --git a/src/util/util.c b/src/util/util.c index dac616b..09d6281 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -671,6 +671,70 @@ virFileIsExecutable(const char *file) } #ifndef WIN32 +/* Check that a file is accessible under certain + * user & gid. + * @mode can be R_OK, W_OK, X_OK or F_OK. + * see 'man accesss' for more details. + * Returns 0 on success, -errno on fail, + * >0 on signaled child. + */ +int +virFileAccessibleAs(const char *path, int mode, + uid_t uid, gid_t gid) +{ + pid_t pid; + int waitret, status, ret = 0; + int forkRet; + + forkRet = virFork(&pid); + + if (pid < 0) { + return -errno; + } + + if (pid) { /* parent */ + do { + if ((waitret = waitpid(pid, &status, 0)) < 0) { + ret = -errno; + virKillProcess(pid, SIGKILL); + return ret; + } + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + + /* child actually returns positive value + * anded by 0xFF (see man waitpid @ WEXITSTATUS) + */ + + if (WIFSIGNALED(status)) + return WTERMSIG(status); + + return -WEXITSTATUS(status); + } + + /* child */ + + if (forkRet < 0) { + ret = errno; + goto childerror; + } + + if (virSetUIDGID(uid, gid) < 0) { + ret = errno; + goto childerror; + } + + if (access(path, mode) < 0) + ret = errno; + +childerror: + if ((ret & 0xFF) != ret) { + VIR_WARN("unable to pass desired return value %d", ret); + ret = 0xFF; + } + + _exit(ret); +} + /* return -errno on failure, or 0 on success */ static int virFileOpenAsNoFork(const char *path, int openflags, mode_t mode, @@ -993,6 +1057,18 @@ childerror: #else /* WIN32 */ +int +virFileAccessibleAs(const char *path ATTRIBUTE_UNUSED, + int mode ATTRIBUTE_UNUSED, + uid_t uid ATTRIBUTE_UNUSED, + git_t gid ATTRIBUTE_UNUSED) +{ + virUtilError(VIR_ERR_INTERNAL_ERROR, + "%s", _("virFileAccessibleAs is not implemented for WIN32")); + + return -ENOSYS; +} + /* return -errno on failure, or 0 on success */ int virFileOpenAs(const char *path ATTRIBUTE_UNUSED, int openflags ATTRIBUTE_UNUSED, diff --git a/src/util/util.h b/src/util/util.h index c55e852..e869f1b 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -93,6 +93,9 @@ enum { VIR_FILE_OPEN_AS_UID = (1 << 0), VIR_FILE_OPEN_FORCE_PERMS = (1 << 1), }; +int virFileAccessibleAs(const char *path, int mode, + uid_t uid, gid_t gid) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; int virFileOpenAs(const char *path, int openflags, mode_t mode, uid_t uid, gid_t gid, unsigned int flags) -- 1.7.3.4

On Wed, Oct 19, 2011 at 04:42:57PM +0200, Michal Privoznik wrote:
This function checks if a given path is accessible under given uid and gid. --- src/util/util.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util/util.h | 3 ++ 2 files changed, 79 insertions(+), 0 deletions(-)
diff --git a/src/util/util.c b/src/util/util.c index dac616b..09d6281 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -671,6 +671,70 @@ virFileIsExecutable(const char *file) }
#ifndef WIN32 +/* Check that a file is accessible under certain + * user & gid. + * @mode can be R_OK, W_OK, X_OK or F_OK. + * see 'man accesss' for more details. + * Returns 0 on success, -errno on fail, + * >0 on signaled child. + */ +int +virFileAccessibleAs(const char *path, int mode, + uid_t uid, gid_t gid) +{ + pid_t pid; + int waitret, status, ret = 0; + int forkRet; + + forkRet = virFork(&pid); + + if (pid < 0) { + return -errno; + }
virFork() already raises a proper libvirt error on failure, so returning 'errno' too is a little misleading / redundant. Just return '-1' on error here & throughout the function
+ + if (pid) { /* parent */ + do { + if ((waitret = waitpid(pid, &status, 0)) < 0) { + ret = -errno; + virKillProcess(pid, SIGKILL); + return ret; + } + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + + /* child actually returns positive value + * anded by 0xFF (see man waitpid @ WEXITSTATUS) + */ + + if (WIFSIGNALED(status)) + return WTERMSIG(status); + + return -WEXITSTATUS(status); + } + + /* child */ + + if (forkRet < 0) { + ret = errno; + goto childerror; + } + + if (virSetUIDGID(uid, gid) < 0) { + ret = errno; + goto childerror; + } + + if (access(path, mode) < 0) + ret = errno; + +childerror: + if ((ret & 0xFF) != ret) { + VIR_WARN("unable to pass desired return value %d", ret); + ret = 0xFF; + } + + _exit(ret); +} + /* return -errno on failure, or 0 on success */ static int virFileOpenAsNoFork(const char *path, int openflags, mode_t mode, @@ -993,6 +1057,18 @@ childerror:
#else /* WIN32 */
+int +virFileAccessibleAs(const char *path ATTRIBUTE_UNUSED, + int mode ATTRIBUTE_UNUSED, + uid_t uid ATTRIBUTE_UNUSED, + git_t gid ATTRIBUTE_UNUSED) +{ + virUtilError(VIR_ERR_INTERNAL_ERROR, + "%s", _("virFileAccessibleAs is not implemented for WIN32")); + + return -ENOSYS; +}
Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This patch implements on_missing feature in qemu driver. Upon qemu startup process an accessibility of CDROMs and floppy disks is checked. The source might get dropped if unavailable and on_missing is set accordingly. No event is emit thought. Look for follow up patch. --- src/qemu/qemu_domain.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 +++ src/qemu/qemu_process.c | 4 +++ 3 files changed, 74 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 5abc900..ffcee29 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1614,3 +1614,69 @@ qemuDomainSetFakeReboot(struct qemud_driver *driver, if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) VIR_WARN("Failed to save status on vm %s", vm->def->name); } + +int +qemuDomainCheckDiskPresence(struct qemud_driver *driver, + virDomainObjPtr vm, + bool being_migrated) +{ + int ret = -1; + int i; + int accessRet; + virDomainDiskDefPtr disk; + char uuid[VIR_UUID_STRING_BUFLEN] ATTRIBUTE_UNUSED; + + virUUIDFormat(vm->def->uuid, uuid); + + for (i = 0; i < vm->def->ndisks; i++) { + disk = vm->def->disks[i]; + + if (!disk->on_missing || !disk->src) + continue; + + if ((accessRet = virFileAccessibleAs(disk->src, F_OK, + driver->user, + driver->group)) >= 0) { + /* disk accessible or virFileAccessibleAs() + * terminated with signal*/ + continue; + } + + switch (disk->on_missing) { + case VIR_DOMAIN_ON_MISSING_OPTIONAL: + break; + + case VIR_DOMAIN_ON_MISSING_MANDATORY: + virReportSystemError(-accessRet, + _("cannot access file '%s'"), + disk->src); + goto cleanup; + break; + + case VIR_DOMAIN_ON_MISSING_REQUISITE: + if (!being_migrated) { + virReportSystemError(-accessRet, + _("cannot access file '%s'"), + disk->src); + goto cleanup; + } + break; + + default: + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("something is tragically wrong")); + goto cleanup; + } + + VIR_DEBUG("Droping disk '%s' on domain '%s' (UUID '%s') " + "due to not accessible source '%s'", + disk->dst, vm->def->name, uuid, disk->src); + + VIR_FREE(disk->src); + } + + ret = 0; + +cleanup: + return ret; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index d9f323c..a768128 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -280,4 +280,8 @@ void qemuDomainSetFakeReboot(struct qemud_driver *driver, bool qemuDomainJobAllowed(qemuDomainObjPrivatePtr priv, enum qemuDomainJob job); + +int qemuDomainCheckDiskPresence(struct qemud_driver *driver, + virDomainObjPtr vm, + bool being_migrated); #endif /* __QEMU_DOMAIN_H__ */ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index a7fe86c..2f1d94d 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2880,6 +2880,10 @@ int qemuProcessStart(virConnectPtr conn, NULL) < 0) goto cleanup; + VIR_DEBUG("Checking for CDROM and floppy presence"); + if (qemuDomainCheckDiskPresence(driver, vm, migrateFrom != NULL) < 0) + goto cleanup; + /* If you are using a SecurityDriver with dynamic labelling, then generate a security label for isolation */ VIR_DEBUG("Generating domain security label (if required)"); -- 1.7.3.4

On Wed, Oct 19, 2011 at 04:42:58PM +0200, Michal Privoznik wrote:
This patch implements on_missing feature in qemu driver. Upon qemu startup process an accessibility of CDROMs and floppy disks is checked. The source might get dropped if unavailable and on_missing is set accordingly. No event is emit thought. Look for follow up patch. --- src/qemu/qemu_domain.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 +++ src/qemu/qemu_process.c | 4 +++ 3 files changed, 74 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 5abc900..ffcee29 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1614,3 +1614,69 @@ qemuDomainSetFakeReboot(struct qemud_driver *driver, if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) VIR_WARN("Failed to save status on vm %s", vm->def->name); } + +int +qemuDomainCheckDiskPresence(struct qemud_driver *driver, + virDomainObjPtr vm, + bool being_migrated)
We do this check upon restore from save image/snapshot too, so 'being_migrated' is a little misleading. How about 'bool start_with_state' to indicate a start attempt that is using existing VM state.
+{ + int ret = -1; + int i; + int accessRet; + virDomainDiskDefPtr disk; + char uuid[VIR_UUID_STRING_BUFLEN] ATTRIBUTE_UNUSED; + + virUUIDFormat(vm->def->uuid, uuid); + + for (i = 0; i < vm->def->ndisks; i++) { + disk = vm->def->disks[i]; + + if (!disk->on_missing || !disk->src) + continue; + + if ((accessRet = virFileAccessibleAs(disk->src, F_OK, + driver->user, + driver->group)) >= 0) { + /* disk accessible or virFileAccessibleAs() + * terminated with signal*/ + continue; + }
You can't assume 'disk->src' is valid. This struct field is only valid for disks type=file|block. It will SEGV if the type=network (ie NBD/Ceph/Sheepdog.
+ + switch (disk->on_missing) { + case VIR_DOMAIN_ON_MISSING_OPTIONAL: + break; + + case VIR_DOMAIN_ON_MISSING_MANDATORY: + virReportSystemError(-accessRet, + _("cannot access file '%s'"), + disk->src); + goto cleanup; + break; + + case VIR_DOMAIN_ON_MISSING_REQUISITE: + if (!being_migrated) { + virReportSystemError(-accessRet, + _("cannot access file '%s'"), + disk->src); + goto cleanup; + } + break; + + default: + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("something is tragically wrong")); + goto cleanup; + } + + VIR_DEBUG("Droping disk '%s' on domain '%s' (UUID '%s') " + "due to not accessible source '%s'", + disk->dst, vm->def->name, uuid, disk->src); + + VIR_FREE(disk->src); + } + + ret = 0; + +cleanup: + return ret; +}
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index a7fe86c..2f1d94d 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2880,6 +2880,10 @@ int qemuProcessStart(virConnectPtr conn, NULL) < 0) goto cleanup;
+ VIR_DEBUG("Checking for CDROM and floppy presence"); + if (qemuDomainCheckDiskPresence(driver, vm, migrateFrom != NULL) < 0) + goto cleanup;
This should cope with restore from save file, and incoming migration. Does it also work with restore from snapshot though ?
+ /* If you are using a SecurityDriver with dynamic labelling, then generate a security label for isolation */ VIR_DEBUG("Generating domain security label (if required)");
Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

If a disk source gets dropped because it is not accessible, mgmt application might want to be informed about this. Therefore we need to emit an event. The event presented in this patch is however a bit superset of what written above. The reason is simple: an intention to be easily expanded, e.g. on 'user ejected disk in guest' events. Therefore, callback gets target string (which should be unique among a domain) and reason (an integer); It can't get alias, as some disk changes (esp. dropping source caused by on_missing feature) might happen on inactive domain where no disks aliases exists. --- daemon/remote.c | 37 ++++++++++++++ examples/domain-events/events-c/event-test.c | 27 ++++++++++- examples/domain-events/events-python/event-test.py | 4 ++ include/libvirt/libvirt.h.in | 28 +++++++++++ python/libvirt-override-virConnect.py | 9 ++++ python/libvirt-override.c | 51 ++++++++++++++++++++ src/conf/domain_event.c | 50 +++++++++++++++++++ src/conf/domain_event.h | 7 +++ src/libvirt_private.syms | 2 + src/qemu/qemu_domain.c | 7 +++ src/remote/remote_driver.c | 34 +++++++++++++ src/remote/remote_protocol.x | 9 +++- src/remote_protocol-structs | 5 ++ 13 files changed, 268 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 550bed4..4d63288 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -451,6 +451,42 @@ static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSE } +static int remoteRelayDomainEventDiskEject(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *target, + int reason, + void *opaque) +{ + virNetServerClientPtr client = opaque; + remote_domain_event_disk_eject_msg data; + + if (!client) + return -1; + + VIR_DEBUG("Relaying domain %s %d disk eject %s %d", + dom->name, dom->id, target, reason); + + /* build return data */ + memset(&data, 0, sizeof data); + data.target = strdup(target); + if (!data.target) + goto mem_error; + data.reason = reason; + + make_nonnull_domain(&data.dom, dom); + + remoteDispatchDomainEventSend(client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_DISK_EJECT, + (xdrproc_t)xdr_remote_domain_event_disk_eject_msg, &data); + + return 0; + +mem_error: + virReportOOMError(); + return -1; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -461,6 +497,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDiskEject), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index 6a3ed26..08e371a 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -285,6 +285,23 @@ static int myDomainEventControlErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED } +const char *diskEjectReasonStrings[] = { + "on_missing", /* 0 */ + /* add new reason here */ +}; +static int myDomainEventDiskEjectCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *target, + int reason, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) disk eject target: %s reason: %s\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), + target, diskEjectReasonStrings[reason]); + return 0; +} + + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -319,6 +336,7 @@ int main(int argc, char **argv) int callback6ret = -1; int callback7ret = -1; int callback8ret = -1; + int callback9ret = -1; struct sigaction action_stop; memset(&action_stop, 0, sizeof action_stop); @@ -382,6 +400,11 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_CONTROL_ERROR, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventControlErrorCallback), strdup("callback control error"), myFreeFunc); + callback9ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_DISK_EJECT, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventDiskEjectCallback), + strdup("disk eject"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && @@ -389,7 +412,8 @@ int main(int argc, char **argv) (callback4ret != -1) && (callback5ret != -1) && (callback6ret != -1) && - (callback7ret != -1)) { + (callback7ret != -1) && + (callback9ret != -1)) { while (run) { if (virEventRunDefaultImpl() < 0) { virErrorPtr err = virGetLastError(); @@ -406,6 +430,7 @@ int main(int argc, char **argv) virConnectDomainEventDeregisterAny(dconn, callback5ret); virConnectDomainEventDeregisterAny(dconn, callback6ret); virConnectDomainEventDeregisterAny(dconn, callback7ret); + virConnectDomainEventDeregisterAny(dconn, callback9ret); if (callback8ret != -1) virConnectDomainEventDeregisterAny(dconn, callback8ret); } diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py index 4df9b98..b0ad603 100644 --- a/examples/domain-events/events-python/event-test.py +++ b/examples/domain-events/events-python/event-test.py @@ -469,6 +469,9 @@ def myDomainEventIOErrorCallback(conn, dom, srcpath, devalias, action, opaque): def myDomainEventGraphicsCallback(conn, dom, phase, localAddr, remoteAddr, authScheme, subject, opaque): print "myDomainEventGraphicsCallback: Domain %s(%s) %d %s" % (dom.name(), dom.ID(), phase, authScheme) +def myDomainEventDiskEjectCallback(conn, dom, target, reason, opaque): + print "myDomainEventDiskEjectCallback: Domain %s(%s) disk eject target: %s reason: %s" % ( + dom.name(), dom.ID(), target, reason) def usage(out=sys.stderr): print >>out, "usage: "+os.path.basename(sys.argv[0])+" [-hdl] [uri]" print >>out, " uri will default to qemu:///system" @@ -526,6 +529,7 @@ def main(): vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR, myDomainEventIOErrorCallback, None) vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG, myDomainEventWatchdogCallback, None) vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS, myDomainEventGraphicsCallback, None) + vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_DISK_EJECT, myDomainEventDiskEjectCallback, None) # The rest of your app would go here normally, but for sake # of demo we'll just go to sleep. The other option is to diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 361881a..d6d928a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2996,6 +2996,33 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, void *opaque); /** + * virConnectDomainEventDiskEjectReason: + * + * The reason describing why this callback is called + */ +typedef enum { + VIR_DOMAIN_DISK_EJECT_ON_MISSING = 0, +} virConnectDomainEventDiskEjectReason; + +/** + * virConnectDomainEventDiskEjectCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @target: target which changed status + * @reason: reason why this callback was called; any of + * virConnectDomainEventDiskEjectReason + * @opaque: application specified data + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_IO_ERROR with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventDiskEjectCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *target, + int reason, + void *opaque); + +/** * VIR_DOMAIN_EVENT_CALLBACK: * * Used to cast the event specific callback into the generic one @@ -3014,6 +3041,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON = 6, /* virConnectDomainEventIOErrorReasonCallback */ VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB = 8, /* virConnectDomainEventBlockJobCallback */ + VIR_DOMAIN_EVENT_ID_DISK_EJECT = 9, /* virConnectDomainEventDiskEjectCallback */ /* * NB: this enum value will increase over time as new events are diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index 65b5342..e945361 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -125,6 +125,15 @@ except AttributeError: pass + def _dispatchDomainEventDiskEjectCallback(self, dom, target, reason, cbData): + """Dispatches event to python user domain diskEject event callbacks + """ + cb = cbData["cb"] + opaque = cbData["opaque"] + + cb(self, virDomain(self, _obj=dom), target, reason, opaque) + return 0; + def domainEventDeregisterAny(self, callbackID): """Removes a Domain Event Callback. De-registering for a domain callback will disable delivery of this event type """ diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 523c03b..46da472 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -4374,6 +4374,54 @@ libvirt_virConnectDomainEventBlockJobCallback(virConnectPtr conn ATTRIBUTE_UNUSE return ret; } +static int +libvirt_virConnectDomainEventDiskEjectCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *target, + int reason, + void *opaque) +{ + PyObject *pyobj_cbData = (PyObject*)opaque; + PyObject *pyobj_dom; + PyObject *pyobj_ret; + PyObject *pyobj_conn; + PyObject *dictKey; + int ret = -1; + + LIBVIRT_ENSURE_THREAD_STATE; + /* Create a python instance of this virDomainPtr */ + virDomainRef(dom); + + pyobj_dom = libvirt_virDomainPtrWrap(dom); + Py_INCREF(pyobj_cbData); + + dictKey = libvirt_constcharPtrWrap("conn"); + pyobj_conn = PyDict_GetItem(pyobj_cbData, dictKey); + Py_DECREF(dictKey); + + /* Call the Callback Dispatcher */ + pyobj_ret = PyObject_CallMethod(pyobj_conn, + (char*)"_dispatchDomainEventDiskEjectCallback", + (char*)"OsiO", + pyobj_dom, + target, reason, + pyobj_cbData); + + Py_DECREF(pyobj_cbData); + Py_DECREF(pyobj_dom); + + if(!pyobj_ret) { + DEBUG("%s - ret:%p\n", __FUNCTION__, pyobj_ret); + PyErr_Print(); + } else { + Py_DECREF(pyobj_ret); + ret = 0; + } + + LIBVIRT_RELEASE_THREAD_STATE; + return ret; +} + static PyObject * libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) @@ -4431,6 +4479,9 @@ libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, case VIR_DOMAIN_EVENT_ID_BLOCK_JOB: cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventBlockJobCallback); break; + case VIR_DOMAIN_EVENT_ID_DISK_EJECT: + cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventDiskEjectCallback); + break; } if (!cb) { diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index f712c34..da6206a 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -88,6 +88,10 @@ struct _virDomainEvent { int type; int status; } blockJob; + struct { + char *target; + int reason; + } diskEject; } data; }; @@ -509,6 +513,10 @@ void virDomainEventFree(virDomainEventPtr event) case VIR_DOMAIN_EVENT_ID_BLOCK_JOB: VIR_FREE(event->data.blockJob.path); break; + + case VIR_DOMAIN_EVENT_ID_DISK_EJECT: + VIR_FREE(event->data.diskEject.target); + break; } VIR_FREE(event->dom.name); @@ -961,6 +969,41 @@ virDomainEventPtr virDomainEventControlErrorNewFromObj(virDomainObjPtr obj) return ev; } +static virDomainEventPtr +virDomainEventDiskEjectNew(int id, const char *name, unsigned char *uuid, + const char *target, int reason) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_DISK_EJECT, + id, name, uuid); + + if (ev) { + if (!(ev->data.diskEject.target = strdup(target))) { + virReportOOMError(); + virDomainEventFree(ev); + return NULL; + } + ev->data.diskEject.reason = reason; + } + + return ev; +} + +virDomainEventPtr virDomainEventDiskEjectNewFromObj(virDomainObjPtr obj, + const char *target, + int reason) +{ + return virDomainEventDiskEjectNew(obj->def->id, obj->def->name, + obj->def->uuid, target, reason); +} + +virDomainEventPtr virDomainEventDiskEjectNewFromDom(virDomainPtr dom, + const char *target, + int reason) +{ + return virDomainEventDiskEjectNew(dom->id, dom->name, dom->uuid, + target, reason); +} /** * virDomainEventQueuePop: @@ -1104,6 +1147,13 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_DISK_EJECT: + ((virConnectDomainEventDiskEjectCallback)cb)(conn, dom, + event->data.diskEject.target, + event->data.diskEject.reason, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 08930ed..add91c2 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -179,6 +179,13 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom, int type, int status); +virDomainEventPtr virDomainEventDiskEjectNewFromObj(virDomainObjPtr obj, + const char *target, + int reason); +virDomainEventPtr virDomainEventDiskEjectNewFromDom(virDomainPtr dom, + const char *target, + int reason); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ae18a0d..026ef48 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -478,6 +478,8 @@ virDomainEventCallbackListRemoveConn; virDomainEventCallbackListRemoveID; virDomainEventControlErrorNewFromDom; virDomainEventControlErrorNewFromObj; +virDomainEventDiskEjectNewFromDom; +virDomainEventDiskEjectNewFromObj; virDomainEventDispatch; virDomainEventDispatchDefaultFunc; virDomainEventFree; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index ffcee29..5547188 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -35,6 +35,7 @@ #include "ignore-value.h" #include "uuid.h" #include "virfile.h" +#include "domain_event.h" #include <sys/time.h> #include <fcntl.h> @@ -1625,6 +1626,7 @@ qemuDomainCheckDiskPresence(struct qemud_driver *driver, int accessRet; virDomainDiskDefPtr disk; char uuid[VIR_UUID_STRING_BUFLEN] ATTRIBUTE_UNUSED; + virDomainEventPtr event = NULL; virUUIDFormat(vm->def->uuid, uuid); @@ -1672,6 +1674,11 @@ qemuDomainCheckDiskPresence(struct qemud_driver *driver, "due to not accessible source '%s'", disk->dst, vm->def->name, uuid, disk->src); + event = virDomainEventDiskEjectNewFromObj(vm, disk->dst, + VIR_DOMAIN_DISK_EJECT_ON_MISSING); + if (event) + qemuDomainEventQueue(driver, event); + VIR_FREE(disk->src); } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1dea327..a4db884 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -228,6 +228,11 @@ remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteDomainBuildEventDiskEject(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + static virNetClientProgramEvent remoteDomainEvents[] = { { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, remoteDomainBuildEventRTCChange, @@ -265,6 +270,10 @@ static virNetClientProgramEvent remoteDomainEvents[] = { remoteDomainBuildEventBlockJob, sizeof(remote_domain_event_block_job_msg), (xdrproc_t)xdr_remote_domain_event_block_job_msg }, + { REMOTE_PROC_DOMAIN_EVENT_DISK_EJECT, + remoteDomainBuildEventDiskEject, + sizeof(remote_domain_event_disk_eject_msg), + (xdrproc_t)xdr_remote_domain_event_disk_eject_msg }, }; enum virDrvOpenRemoteFlags { @@ -3327,6 +3336,31 @@ remoteDomainBuildEventControlError(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, } +static void +remoteDomainBuildEventDiskEject(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_disk_eject_msg *msg = evdata; + virDomainPtr dom; + virDomainEventPtr event = NULL; + + dom = get_nonnull_domain(conn, msg->dom); + if (!dom) + return; + + event = virDomainEventDiskEjectNewFromDom(dom, + msg->target, + msg->reason); + + virDomainFree(dom); + + remoteDomainEventQueue(priv, event); +} + + static virDrvOpenStatus ATTRIBUTE_NONNULL (1) remoteSecretOpen(virConnectPtr conn, virConnectAuthPtr auth, unsigned int flags) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f95253e..1fb31d6 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2010,6 +2010,12 @@ struct remote_domain_event_block_job_msg { int status; }; +struct remote_domain_event_disk_eject_msg { + remote_nonnull_domain dom; + remote_nonnull_string target; + int reason; +}; + struct remote_domain_managed_save_args { remote_nonnull_domain dom; unsigned int flags; @@ -2546,7 +2552,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_GET_PARENT = 244, /* autogen autogen priority:high */ REMOTE_PROC_DOMAIN_RESET = 245, /* autogen autogen */ REMOTE_PROC_DOMAIN_SNAPSHOT_NUM_CHILDREN = 246, /* autogen autogen priority:high */ - REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_CHILDREN_NAMES = 247 /* autogen autogen priority:high */ + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_CHILDREN_NAMES = 247, /* autogen autogen priority:high */ + REMOTE_PROC_DOMAIN_EVENT_DISK_EJECT = 248 /* skipgen skipgen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 7894441..6ac514f 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1509,6 +1509,11 @@ struct remote_domain_event_block_job_msg { int type; int status; }; +struct remote_domain_event_disk_eject_msg { + remote_nonnull_domain dom; + remote_nonnull_string target; + int reason; +}; struct remote_domain_managed_save_args { remote_nonnull_domain dom; u_int flags; -- 1.7.3.4

On Wed, Oct 19, 2011 at 04:42:59PM +0200, Michal Privoznik wrote:
If a disk source gets dropped because it is not accessible, mgmt application might want to be informed about this. Therefore we need to emit an event. The event presented in this patch is however a bit superset of what written above. The reason is simple: an intention to be easily expanded, e.g. on 'user ejected disk in guest' events. Therefore, callback gets target string (which should be unique among a domain) and reason (an integer); It can't get alias, as some disk changes (esp. dropping source caused by on_missing feature) might happen on inactive domain where no disks aliases exists. --- daemon/remote.c | 37 ++++++++++++++ examples/domain-events/events-c/event-test.c | 27 ++++++++++- examples/domain-events/events-python/event-test.py | 4 ++ include/libvirt/libvirt.h.in | 28 +++++++++++ python/libvirt-override-virConnect.py | 9 ++++ python/libvirt-override.c | 51 ++++++++++++++++++++ src/conf/domain_event.c | 50 +++++++++++++++++++ src/conf/domain_event.h | 7 +++ src/libvirt_private.syms | 2 + src/qemu/qemu_domain.c | 7 +++ src/remote/remote_driver.c | 34 +++++++++++++ src/remote/remote_protocol.x | 9 +++- src/remote_protocol-structs | 5 ++ 13 files changed, 268 insertions(+), 2 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 361881a..d6d928a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2996,6 +2996,33 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, void *opaque);
/** + * virConnectDomainEventDiskEjectReason: + * + * The reason describing why this callback is called + */ +typedef enum { + VIR_DOMAIN_DISK_EJECT_ON_MISSING = 0, +} virConnectDomainEventDiskEjectReason;
I'd name this: VIR_DOMAIN_DISK_EJECT_MISSING_ON_START
+ +/** + * virConnectDomainEventDiskEjectCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @target: target which changed status
In the I/O error event, we use 'srcPath' and 'devAlias' in the event name. I think we probably ought todo the same here. So the app gets details of which file was missing, and the unique alias of the device.
+ * @reason: reason why this callback was called; any of + * virConnectDomainEventDiskEjectReason + * @opaque: application specified data + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_IO_ERROR with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventDiskEjectCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *target, + int reason, + void *opaque); +
Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Wed, Oct 19, 2011 at 04:42:55PM +0200, Michal Privoznik wrote:
This is a 3rd version of patches [1]. Diff to v2 includes Daniel's review and some other improvements.
The aim of these patches is to mark CDROM and floppy optional, thus they might get dropped during migration, domain startup, restore, etc. The granularity is set on disk level, so user can decide which cd-roms are important and which not.
There are 3 levels of optionality: - mandatory - fail if missing for any reason (the default) - requisite - fail if missing on boot up, drop if missing on migrate/restore/revert - optional - drop if missing at any start attempt.
To assign a disk one of these, just append 'on_missing' attribute to its <source>.
If a domain is then eventually started (migrated, restored, whatever), we check for source path being accessible (currently only qemu driver is supported) from hypervisor's POV. This is done by virFileAccessibleAs() function. Currently, only uid+gid are considered. Ideally, this should be extended to SElinux labeling and cgroups.
1: https://www.redhat.com/archives/libvir-list/2011-October/msg00032.html
Michal Privoznik (4): conf: Introduce optional on_missing attribute for cdrom and floppy util: Create virFileAccessibleAs function qemu: implement on_missing on_missing: Emit event on disk source dropping
daemon/remote.c | 37 ++++++++++ docs/formatdomain.html.in | 26 ++++++- docs/schemas/domaincommon.rng | 22 +++++- examples/domain-events/events-c/event-test.c | 27 +++++++- examples/domain-events/events-python/event-test.py | 4 + include/libvirt/libvirt.h.in | 28 +++++++ python/libvirt-override-virConnect.py | 9 +++ python/libvirt-override.c | 51 +++++++++++++ src/conf/domain_conf.c | 43 ++++++++++- src/conf/domain_conf.h | 11 +++ src/conf/domain_event.c | 50 +++++++++++++ src/conf/domain_event.h | 7 ++ src/libvirt_private.syms | 4 + src/qemu/qemu_domain.c | 73 +++++++++++++++++++ src/qemu/qemu_domain.h | 4 + src/qemu/qemu_process.c | 4 + src/remote/remote_driver.c | 34 +++++++++ src/remote/remote_protocol.x | 9 ++- src/remote_protocol-structs | 5 ++ src/util/util.c | 76 ++++++++++++++++++++ src/util/util.h | 3 + .../qemuxml2xmlout-disk-cdrom-empty.xml | 31 ++++++++ 22 files changed, 548 insertions(+), 10 deletions(-) create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-cdrom-empty.xml
ACK to the patch set, error "something is tragically wrong" in patch 3 is rather uninformative, I agree it should not occur, but still could you merge in something a bit more contectual here before the push :-) ? Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Michal Privoznik