On Tue, 2021-08-17 at 14:13 +0200, Martin Kletzander wrote: > On Mon, Aug 16, 2021 at 07:19:45PM +0800, Luke Yue wrote: > > Introduce testDomainChgDevice for further development (just like > > what we > > did for IOThread). And introduce > > testDomainDetachDeviceLiveAndConfig for > > detaching devices. > > > > Signed-off-by: Luke Yue <lukedyue@xxxxxxxxx> > > --- > > src/test/test_driver.c | 270 > > +++++++++++++++++++++++++++++++++++++++++ > > 1 file changed, 270 insertions(+) > > > > diff --git a/src/test/test_driver.c b/src/test/test_driver.c > > index 00cc13511a..2ebdbaa604 100644 > > --- a/src/test/test_driver.c > > +++ b/src/test/test_driver.c > > @@ -9452,6 +9452,275 @@ testDomainGetMessages(virDomainPtr dom, > > return rv; > > } > > > > +static int > > +testDomainDetachDeviceLiveAndConfig(virDomainDef *vmdef, > > + virDomainDeviceDef *dev) > > +{ > > + virDomainDiskDef *disk; > > + virDomainDiskDef *det_disk; > > + virDomainNetDef *net; > > + virDomainSoundDef *sound; > > + virDomainHostdevDef *hostdev; > > + virDomainHostdevDef *det_hostdev; > > + virDomainLeaseDef *lease; > > + virDomainLeaseDef *det_lease; > > + virDomainControllerDef *cont; > > + virDomainControllerDef *det_cont; > > + virDomainFSDef *fs; > > + virDomainMemoryDef *mem; > > + int idx; > > + > > + switch (dev->type) { > > + case VIR_DOMAIN_DEVICE_DISK: > > + disk = dev->data.disk; > > + if (!(det_disk = virDomainDiskRemoveByName(vmdef, disk- > > >dst))) { > > + virReportError(VIR_ERR_DEVICE_MISSING, > > + _("no target device %s"), disk->dst); > > + return -1; > > + } > > + virDomainDiskDefFree(det_disk); > > + break; > > + > > + case VIR_DOMAIN_DEVICE_NET: > > + net = dev->data.net; > > + if ((idx = virDomainNetFindIdx(vmdef, net)) < 0) > > + return -1; > > + > > + /* this is guaranteed to succeed */ > > + virDomainNetDefFree(virDomainNetRemove(vmdef, idx)); > > + break; > > + > > + case VIR_DOMAIN_DEVICE_SOUND: > > + sound = dev->data.sound; > > + if ((idx = virDomainSoundDefFind(vmdef, sound)) < 0) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("device not present in domain > > configuration")); > > + return -1; > > + } > > + virDomainSoundDefFree(virDomainSoundDefRemove(vmdef, > > idx)); > > + break; > > + > > + case VIR_DOMAIN_DEVICE_HOSTDEV: { > > + hostdev = dev->data.hostdev; > > + if ((idx = virDomainHostdevFind(vmdef, hostdev, > > &det_hostdev)) < 0) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("device not present in domain > > configuration")); > > + return -1; > > + } > > + virDomainHostdevRemove(vmdef, idx); > > + virDomainHostdevDefFree(det_hostdev); > > + break; > > + } > > + > > + case VIR_DOMAIN_DEVICE_LEASE: > > + lease = dev->data.lease; > > + if (!(det_lease = virDomainLeaseRemove(vmdef, lease))) { > > + virReportError(VIR_ERR_DEVICE_MISSING, > > + _("Lease %s in lockspace %s does not > > exist"), > > + lease->key, NULLSTR(lease->lockspace)); > > + return -1; > > + } > > + virDomainLeaseDefFree(det_lease); > > + break; > > + > > + case VIR_DOMAIN_DEVICE_CONTROLLER: > > + cont = dev->data.controller; > > + if ((idx = virDomainControllerFind(vmdef, cont->type, > > + cont->idx)) < 0) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("device not present in domain > > configuration")); > > + return -1; > > + } > > + det_cont = virDomainControllerRemove(vmdef, idx); > > + virDomainControllerDefFree(det_cont); > > + > > + break; > > + > > + case VIR_DOMAIN_DEVICE_FS: > > + fs = dev->data.fs; > > + idx = virDomainFSIndexByName(vmdef, fs->dst); > > + if (idx < 0) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("no matching filesystem device was > > found")); > > + return -1; > > + } > > + > > + fs = virDomainFSRemove(vmdef, idx); > > + virDomainFSDefFree(fs); > > + break; > > + > > + case VIR_DOMAIN_DEVICE_RNG: > > + if ((idx = virDomainRNGFind(vmdef, dev->data.rng)) < 0) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("no matching RNG device was found")); > > + return -1; > > + } > > + > > + virDomainRNGDefFree(virDomainRNGRemove(vmdef, idx)); > > + break; > > + > > + case VIR_DOMAIN_DEVICE_MEMORY: > > + if ((idx = virDomainMemoryFindInactiveByDef(vmdef, > > + dev- > > >data.memory)) < 0) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("matching memory device was not > > found")); > > + return -1; > > + } > > + mem = virDomainMemoryRemove(vmdef, idx); > > + vmdef->mem.cur_balloon -= mem->size; > > + virDomainMemoryDefFree(mem); > > + break; > > + > > + case VIR_DOMAIN_DEVICE_REDIRDEV: > > + if ((idx = virDomainRedirdevDefFind(vmdef, > > + dev->data.redirdev)) < > > 0) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("no matching redirdev was not > > found")); > > + return -1; > > + } > > + > > + virDomainRedirdevDefFree(virDomainRedirdevDefRemove(vmdef, > > idx)); > > + break; > > + > > + case VIR_DOMAIN_DEVICE_SHMEM: > > + if ((idx = virDomainShmemDefFind(vmdef, dev->data.shmem)) > > < 0) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("matching shmem device was not > > found")); > > + return -1; > > + } > > + > > + virDomainShmemDefFree(virDomainShmemDefRemove(vmdef, > > idx)); > > + break; > > + > > + > > + case VIR_DOMAIN_DEVICE_WATCHDOG: > > + if (!vmdef->watchdog) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("domain has no watchdog")); > > + return -1; > > + } > > + virDomainWatchdogDefFree(vmdef->watchdog); > > + vmdef->watchdog = NULL; > > + break; > > + > > + case VIR_DOMAIN_DEVICE_INPUT: > > + if ((idx = virDomainInputDefFind(vmdef, dev->data.input)) > > < 0) { > > + virReportError(VIR_ERR_DEVICE_MISSING, "%s", > > + _("matching input device not found")); > > + return -1; > > + } > > + virDomainInputDefFree(vmdef->inputs[idx]); > > + VIR_DELETE_ELEMENT(vmdef->inputs, idx, vmdef->ninputs); > > + break; > > + > > + case VIR_DOMAIN_DEVICE_VSOCK: > > + if (!vmdef->vsock || > > + !virDomainVsockDefEquals(dev->data.vsock, vmdef- > > >vsock)) { > > + virReportError(VIR_ERR_OPERATION_FAILED, "%s", > > + _("matching vsock device not found")); > > + return -1; > > + } > > + virDomainVsockDefFree(vmdef->vsock); > > + vmdef->vsock = NULL; > > + break; > > + > > There is lot of duplication here. These are already used in multiple > places and de-duplicating would help us tremendously, even more than > in > the previous case with virDomainGetMessages. The lxc, libxl, qemu > and > possibly other drivers would benefit from many of these, some of them > could then easily add more functionality and it would be even more > tested once we start testing these properly. It is not going to be a > trivial task to properlyd ecide where to split this and how, but I > believe you can figure out a nice, clean way. But do not hesitate to > let know if you need help with that. > Yes, I was thinking if I can extract them for these drivers, but I noticed that there may be some differences, like we have to check ACL for some devices when using other drivers, and at least for QEMU driver, there is no virDomainInputDefFree(vmdef->inputs[idx]); line in `case VIR_DOMAIN_DEVICE_INPUT:` in QEMU driver, I'm not pretty sure whether this will lead to memory leak for QEMU or not, or it's by design, but at least it do leak memory when using test driver. So we may need to handle them for different drivers' case, and I am not pretty sure whether I should extract or not, so I decide to send the patches first. I was wondering if we can add a flag to indicate which driver is using the function, will give it a try. > > + case VIR_DOMAIN_DEVICE_CHR: > > + case VIR_DOMAIN_DEVICE_VIDEO: > > + case VIR_DOMAIN_DEVICE_GRAPHICS: > > + case VIR_DOMAIN_DEVICE_HUB: > > + case VIR_DOMAIN_DEVICE_SMARTCARD: > > + case VIR_DOMAIN_DEVICE_MEMBALLOON: > > + case VIR_DOMAIN_DEVICE_NVRAM: > > + case VIR_DOMAIN_DEVICE_NONE: > > + case VIR_DOMAIN_DEVICE_TPM: > > + case VIR_DOMAIN_DEVICE_PANIC: > > + case VIR_DOMAIN_DEVICE_IOMMU: > > + case VIR_DOMAIN_DEVICE_AUDIO: > > What's good about the test driver is that we can support attach and > detach for all the devices. It would be nice to also have a way to > test > something that is not supported, but there is no need to do it with > all > of the above. It makes for example for an IOMMU as that is something > I > cannot imagine being hot(un)plugged. For --config anything is > possible > because it is just the XML that is being changed. > OK, I will try to add more of them. > > + case VIR_DOMAIN_DEVICE_LAST: > > + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, > > + _("persistent detach of device '%s' is not > > supported"), > > Not really persistent when it is called for both live and config. > > > + virDomainDeviceTypeToString(dev->type)); > > + return -1; > > + } > > + > > + return 0; > > +} > > + > > +static int > > +testDomainChgDevice(virDomainPtr dom, > > + virDomainDeviceAction action, > > + const char *xml, > > + const char *alias, > > + unsigned int flags) > > +{ > > + testDriver *driver = dom->conn->privateData; > > + virDomainObj *vm = NULL; > > + virDomainDef *def; > > + virDomainDeviceDef *dev = NULL; > > + unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE; > > + int ret = -1; > > + > > + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | > > + VIR_DOMAIN_AFFECT_CONFIG, -1); > > + > > + if (!(vm = testDomObjFromDomain(dom))) > > + goto cleanup; > > + > > + if (virDomainObjUpdateModificationImpact(vm, &flags) < 0) > > + goto cleanup; > > + > > + if (!(def = virDomainObjGetOneDef(vm, flags))) > > + goto cleanup; > > + > > + if (action == VIR_DOMAIN_DEVICE_ACTION_DETACH) > > + parse_flags |= VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE; > > + > > + if (xml) { > > + if (!(dev = virDomainDeviceDefParse(xml, def, driver- > > >xmlopt, > > + driver->caps, > > parse_flags))) > > + goto cleanup; > > + } else if (alias) { > > + dev = g_new0(virDomainDeviceDef, 1); > > + if (virDomainDefFindDevice(def, alias, dev, true) < 0) > > + goto cleanup; > > + } > > + > > + if (dev == NULL) > > + goto cleanup; > > + > > + switch (action) { > > + case VIR_DOMAIN_DEVICE_ACTION_ATTACH: > > + break; > > So Attach will always succeed, but nothing will show up in the XML. > > > + > > + case VIR_DOMAIN_DEVICE_ACTION_DETACH: > > + if (testDomainDetachDeviceLiveAndConfig(def, dev) < 0) > > + goto cleanup; > > + break; > > + > > + case VIR_DOMAIN_DEVICE_ACTION_UPDATE: > > + break; > > Same here > I thought there is no function would pass VIR_DOMAIN_DEVICE_ACTION_ATTACH or UPDATE to this function at the moment, so I just leave it there. > > + } > > + > > + ret = 0; > > + > > + cleanup: > > + if (xml) { > > + virDomainDeviceDefFree(dev); > > + } else { > > + g_free(dev); > > + } > > + virDomainObjEndAPI(&vm); > > + return ret; > > +} > > + > > +static int > > +testDomainDetachDeviceFlags(virDomainPtr dom, > > + const char *xml, > > + unsigned int flags) > > +{ > > + return testDomainChgDevice(dom, > > VIR_DOMAIN_DEVICE_ACTION_DETACH, > > + xml, NULL, flags); > > +} > > /* > > * Test driver > > */ > > @@ -9541,6 +9810,7 @@ static virHypervisorDriver > > testHypervisorDriver = { > > .domainFSFreeze = testDomainFSFreeze, /* 5.7.0 */ > > .domainFSThaw = testDomainFSThaw, /* 5.7.0 */ > > .domainFSTrim = testDomainFSTrim, /* 5.7.0 */ > > + .domainDetachDeviceFlags = testDomainDetachDeviceFlags, /* > > 7.7.0 */ > > .domainGetAutostart = testDomainGetAutostart, /* 0.3.2 */ > > .domainSetAutostart = testDomainSetAutostart, /* 0.3.2 */ > > .domainGetDiskErrors = testDomainGetDiskErrors, /* 5.4.0 */ > > -- > > 2.32.0 > >