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 <eblake@redhat.com>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>

(virDomainObjCopyPersistentDef): make a copy of persistent vm definition
(qemuDomainAttach/Detach/UpdateDeviceConfig) : callbacks. now empty
(qemuDomainModifyDeviceFlags): add support for MODIFY_CONFIG and MODIFY_CURRENT
This commit is contained in:
KAMEZAWA Hiroyuki 2011-04-22 12:07:56 +09:00 committed by Eric Blake
parent 8a6e30f124
commit da1eba6bc8
4 changed files with 139 additions and 33 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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);