From da1eba6bc8f58bfce34136710d1979a3a44adb17 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 22 Apr 2011 12:07:56 +0900 Subject: [PATCH] libvirt/qemu - support persistent modification of devices This patch adds functions for modify domain's persistent definition. To do error recovery in easy way, we use a copy of vmdef and update it. The whole sequence will be: make a copy of domain definition. if (flags & MODIFY_CONFIG) update copied domain definition if (flags & MODIF_LIVE) do hotplug. if (no error) save copied one to the file and update cached definition. else discard copied definition. This patch is mixuture of Eric Blake's work and mine. From: Eric Blake Signed-off-by: KAMEZAWA Hiroyuki (virDomainObjCopyPersistentDef): make a copy of persistent vm definition (qemuDomainAttach/Detach/UpdateDeviceConfig) : callbacks. now empty (qemuDomainModifyDeviceFlags): add support for MODIFY_CONFIG and MODIFY_CURRENT --- src/conf/domain_conf.c | 19 +++++ src/conf/domain_conf.h | 3 + src/libvirt_private.syms | 3 +- src/qemu/qemu_driver.c | 147 ++++++++++++++++++++++++++++++--------- 4 files changed, 139 insertions(+), 33 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0e7aeb587a..691e50ee8a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -9510,3 +9510,22 @@ cleanup: return ret; } + + +virDomainDefPtr +virDomainObjCopyPersistentDef(virCapsPtr caps, virDomainObjPtr dom) +{ + char *xml; + virDomainDefPtr cur, ret; + + cur = virDomainObjGetPersistentDef(caps, dom); + + xml = virDomainDefFormat(cur, VIR_DOMAIN_XML_WRITE_FLAGS); + if (!xml) + return NULL; + + ret = virDomainDefParseString(caps, xml, VIR_DOMAIN_XML_READ_FLAGS); + + VIR_FREE(xml); + return ret; +} diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6ea30b9c8f..ddf111a0a1 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1288,6 +1288,9 @@ int virDomainObjSetDefTransient(virCapsPtr caps, virDomainDefPtr virDomainObjGetPersistentDef(virCapsPtr caps, virDomainObjPtr domain); +virDomainDefPtr +virDomainObjCopyPersistentDef(virCapsPtr caps, virDomainObjPtr dom); + void virDomainRemoveInactive(virDomainObjListPtr doms, virDomainObjPtr dom); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 185809179a..579c62fafd 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -287,7 +287,7 @@ virDomainMemballoonModelTypeToString; virDomainNetDefFree; virDomainNetTypeToString; virDomainObjAssignDef; -virDomainObjSetDefTransient; +virDomainObjCopyPersistentDef; virDomainObjGetPersistentDef; virDomainObjIsDuplicate; virDomainObjListDeinit; @@ -297,6 +297,7 @@ virDomainObjListInit; virDomainObjListNumOfDomains; virDomainObjLock; virDomainObjRef; +virDomainObjSetDefTransient; virDomainObjUnlock; virDomainObjUnref; virDomainRemoveInactive; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2653708ec5..47ef4c72e5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4076,6 +4076,46 @@ qemuDomainUpdateDeviceLive(virDomainObjPtr vm, return ret; } +static int +qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev) +{ + switch (dev->type) { + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent attach of device is not supported")); + return -1; + } + return 0; +} + + +static int +qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev) +{ + switch (dev->type) { + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent detach of device is not supported")); + return -1; + } + return 0; +} + +static int +qemuDomainUpdateDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev) +{ + switch (dev->type) { + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent update of device is not supported")); + return -1; + } + return 0; +} + /* Actions for qemuDomainModifyDeviceFlags */ enum { QEMU_DEVICE_ATTACH, @@ -4091,6 +4131,7 @@ qemuDomainModifyDeviceFlags(virDomainPtr dom, const char *xml, struct qemud_driver *driver = dom->conn->privateData; virBitmapPtr qemuCaps = NULL; virDomainObjPtr vm = NULL; + virDomainDefPtr vmdef = NULL; virDomainDeviceDefPtr dev = NULL; bool force = (flags & VIR_DOMAIN_DEVICE_MODIFY_FORCE) != 0; int ret = -1; @@ -4100,12 +4141,6 @@ qemuDomainModifyDeviceFlags(virDomainPtr dom, const char *xml, (action == QEMU_DEVICE_UPDATE ? VIR_DOMAIN_DEVICE_MODIFY_FORCE : 0), -1); - if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { - qemuReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("cannot modify the persistent configuration of a domain")); - return -1; - } - qemuDriverLock(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { @@ -4119,12 +4154,27 @@ qemuDomainModifyDeviceFlags(virDomainPtr dom, const char *xml, if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) goto cleanup; - if (!virDomainObjIsActive(vm)) { - qemuReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("cannot attach device on inactive domain")); - goto endjob; + if (virDomainObjIsActive(vm)) { + if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT) + flags |= VIR_DOMAIN_DEVICE_MODIFY_LIVE; + } else { + if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT) + flags |= VIR_DOMAIN_DEVICE_MODIFY_CONFIG; + /* check consistency between flags and the vm state */ + if (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", + _("cannot do live update a device on " + "inactive domain")); + goto endjob; + } } + if ((flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) && !vm->persistent) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot modify device on transient domain")); + goto endjob; + } dev = virDomainDeviceDefParse(driver->caps, vm->def, xml, VIR_DOMAIN_XML_INACTIVE); if (dev == NULL) @@ -4135,35 +4185,68 @@ qemuDomainModifyDeviceFlags(virDomainPtr dom, const char *xml, &qemuCaps) < 0) goto endjob; - switch (action) { - case QEMU_DEVICE_ATTACH: - ret = qemuDomainAttachDeviceLive(vm, dev, dom, qemuCaps); - break; - case QEMU_DEVICE_DETACH: - ret = qemuDomainDetachDeviceLive(vm, dev, dom, qemuCaps); - break; - case QEMU_DEVICE_UPDATE: - ret = qemuDomainUpdateDeviceLive(vm, dev, dom, qemuCaps, force); - break; - default: - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown domain modify action %d"), action); - break; - } + if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { + /* Make a copy for updated domain. */ + vmdef = virDomainObjCopyPersistentDef(driver->caps, vm); + if (!vmdef) + goto endjob; + switch (action) { + case QEMU_DEVICE_ATTACH: + ret = qemuDomainAttachDeviceConfig(vmdef, dev); + break; + case QEMU_DEVICE_DETACH: + ret = qemuDomainDetachDeviceConfig(vmdef, dev); + break; + case QEMU_DEVICE_UPDATE: + ret = qemuDomainUpdateDeviceConfig(vmdef, dev); + break; + default: + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown domain modify action %d"), action); + break; + } + } else + ret = 0; - /* - * update domain status forcibly because the domain status may be changed - * even if we attach the device failed. For example, a new controller may - * be created. - */ - if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) - ret = -1; + if (!ret && (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE)) { + switch (action) { + case QEMU_DEVICE_ATTACH: + ret = qemuDomainAttachDeviceLive(vm, dev, dom, qemuCaps); + break; + case QEMU_DEVICE_DETACH: + ret = qemuDomainDetachDeviceLive(vm, dev, dom, qemuCaps); + break; + case QEMU_DEVICE_UPDATE: + ret = qemuDomainUpdateDeviceLive(vm, dev, dom, qemuCaps, force); + break; + default: + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown domain modify action %d"), action); + break; + } + /* + * update domain status forcibly because the domain status may be + * changed even if we attach the device failed. For example, a + * For example, a new controller may be created. + */ + if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) + ret = -1; + } + /* Finally, if no error until here, we can save config. */ + if (!ret && (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG)) { + ret = virDomainSaveConfig(driver->configDir, vmdef); + if (!ret) { + virDomainObjAssignDef(vm, vmdef, false); + vmdef = NULL; + } + } endjob: if (qemuDomainObjEndJob(vm) == 0) vm = NULL; cleanup: + virDomainDefFree(vmdef); virDomainDeviceDefFree(dev); if (vm) virDomainObjUnlock(vm);