Introduce testDomainChgDevice for further development (just like what we
did for IOThread). And introduce testDomainDetachDeviceLiveAndConfig for
detaching devices.
Signed-off-by: Luke Yue <lukedyue@gmail.com>
---
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;
+
+ 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:
+ case VIR_DOMAIN_DEVICE_LAST:
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+ _("persistent detach of device '%s' is not supported"),
+ 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;
+
+ case VIR_DOMAIN_DEVICE_ACTION_DETACH:
+ if (testDomainDetachDeviceLiveAndConfig(def, dev) < 0)
+ goto cleanup;
+ break;
+
+ case VIR_DOMAIN_DEVICE_ACTION_UPDATE:
+ break;
+ }
+
+ 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
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@gmail.com>
>---
> 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.
>+ 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.
>+ 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
>+ }
>+
>+ 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
>
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@gmail.com>
> > ---
> > 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
> >
On Tue, Aug 17, 2021 at 08:50:59PM +0800, Luke Yue wrote:
>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@gmail.com>
>> > ---
>> > src/test/test_driver.c | 270
>> > +++++++++++++++++++++++++++++++++++++++++
>> > 1 file changed, 270 insertions(+)
>> >
[...]
>> > +
>> > + 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.
>
This whole thing does not have to be extracted as one function. Or it
can and there can be either flags or some callbacks, for example passed
inside xmlopt. Whatever makes it more readable, nicer etc. There will
inevitably be numerous ways to approach this and the real work is
figuring out which is the cleaner one.
>> > + 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.
>
Of course first figuring out how to approach the above may make it way
easier to then add more of these.
[...]
>> > + 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.
>
Well yes, but then it can make it hard to find an error if someone wants
to add support for attaching and they use the function, based on the
name it sounds alright and then there is no error, it just does not
work. It's not that it is bad now, but for the purpose of
future-proofing it is beneficial to handle those cases clearly in some
way.
© 2016 - 2026 Red Hat, Inc.