Signed-off-by: Thanos Makatos <thanos.makatos@xxxxxxxxxxx> --- src/test/test_driver.c | 387 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 385 insertions(+), 2 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index e87d7cfd44..a3b4799a0f 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -26,8 +26,6 @@ #include <unistd.h> #include <sys/stat.h> #include <libxml/xmlsave.h> - - #include "virerror.h" #include "datatypes.h" #include "test_driver.h" @@ -38,10 +36,14 @@ #include "virnetworkobj.h" #include "interface_conf.h" #include "checkpoint_conf.h" +#include "domain_addr.h" +#include "domain_audit.h" #include "domain_conf.h" #include "domain_driver.h" #include "domain_event.h" +#include "domain_validate.h" #include "network_event.h" +#include "nwfilter_conf.h" #include "snapshot_conf.h" #include "virfdstream.h" #include "storage_conf.h" @@ -50,6 +52,7 @@ #include "node_device_conf.h" #include "virnodedeviceobj.h" #include "node_device_event.h" +#include "vircgroup.h" #include "virxml.h" #include "virthread.h" #include "virlog.h" @@ -10035,6 +10038,383 @@ testConnectGetDomainCapabilities(virConnectPtr conn G_GNUC_UNUSED, return virDomainCapsFormat(domCaps); } +static int +testDomainAttachHostPCIDevice(testDriver *driver G_GNUC_UNUSED, + virDomainObj *vm, + virDomainHostdevDef *hostdev) +{ + int backend = hostdev->source.subsys.u.pci.backend; + + switch ((virDomainHostdevSubsysPCIBackendType)backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN: + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("test hypervisor does not support device assignment mode '%s'"), + virDomainHostdevSubsysPCIBackendTypeToString(backend)); + return -1; + } + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest unexpectedly quit during hotplug")); + return -1; + } + + VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1); + + virDomainAuditHostdev(vm, hostdev, "attach", true); + + vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; + + return 0; +} + +static int +testDomainAttachHostDevice(testDriver *driver, + virDomainObj *vm, + virDomainHostdevDef *hostdev) +{ + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hotplug is not supported for hostdev mode '%s'"), + virDomainHostdevModeTypeToString(hostdev->mode)); + return -1; + } + + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hotplug is not supported for hostdev subsys type '%s'"), + virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type)); + return -1; + } + + return testDomainAttachHostPCIDevice(driver, vm, hostdev); +} + +static int +testDomainAttachDeviceLive(virDomainObj *vm, + virDomainDeviceDef *dev, + testDriver *driver) +{ + int ret = -1; + const char *alias = NULL; + + if ((virDomainDeviceType)dev->type != VIR_DOMAIN_DEVICE_HOSTDEV) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("live attach of device '%s' is not supported"), + virDomainDeviceTypeToString(dev->type)); + return -1; + } + + testDomainObjCheckHostdevTaint(vm, dev->data.hostdev); + if ((ret = testDomainAttachHostDevice(driver, vm, dev->data.hostdev)) != 0) + return ret; + + alias = dev->data.hostdev->info->alias; + dev->data.hostdev = NULL; + + if (alias) { + virObjectEvent *event; + event = virDomainEventDeviceAddedNewFromObj(vm, alias); + virObjectEventStateQueue(driver->eventState, event); + } + + return 0; +} + +static int +testDomainAttachDeviceLiveAndConfig(virDomainObj *vm, + testDriver *driver, + const char *xml, + unsigned int flags) +{ + g_autoptr(virDomainDeviceDef) devConf = NULL; + g_autoptr(virDomainDeviceDef) devLive = NULL; + unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE | + VIR_DOMAIN_DEF_PARSE_ABI_UPDATE; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE, -1); + + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + if (!(devLive = virDomainDeviceDefParse(xml, vm->def, + driver->xmlopt, NULL, + parse_flags))) + return -1; + + if (virDomainDeviceValidateAliasForHotplug(vm, devLive, + VIR_DOMAIN_AFFECT_LIVE) < 0) + return -1; + + if (virDomainDefCompatibleDevice(vm->def, devLive, NULL, + VIR_DOMAIN_DEVICE_ACTION_ATTACH, + true) < 0) + return -1; + + if (testDomainAttachDeviceLive(vm, devLive, driver) < 0) + return -1; + } + + return 0; +} + +static int +testDomainAttachDeviceFlags(virDomainPtr domain, + const char *xml, + unsigned int flags) { + + testDriver *driver = domain->conn->privateData; + virDomainObj *vm = NULL; + int ret = -1; + + virNWFilterReadLockFilterUpdates(); + + if (!(vm = testDomObjFromDomain(domain))) + return -1; + + if (virDomainObjUpdateModificationImpact(vm, &flags) < 0) + goto cleanup; + + if (testDomainAttachDeviceLiveAndConfig(vm, driver, xml, flags) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + virNWFilterUnlockFilterUpdates(); + return ret; + +} + +static int +testDomainAttachDevice(virDomainPtr domain, const char *xml) +{ + return testDomainAttachDeviceFlags(domain, xml, 0); +} + +/* search for a hostdev matching dev and detach it */ +static int +testDomainDetachPrepHostdev(virDomainObj *vm, + virDomainHostdevDef *match, + virDomainHostdevDef **detach) +{ + virDomainHostdevSubsys *subsys = &match->source.subsys; + virDomainHostdevSubsysPCI *pcisrc = &subsys->u.pci; + virDomainHostdevDef *hostdev = NULL; + + if (match->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hot unplug is not supported for hostdev mode '%s'"), + virDomainHostdevModeTypeToString(match->mode)); + return -1; + } + + if (virDomainHostdevFind(vm->def, match, &hostdev) < 0) { + if (subsys->type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + virReportError(VIR_ERR_DEVICE_MISSING, + _("host pci device " VIR_PCI_DEVICE_ADDRESS_FMT + " not found"), + pcisrc->addr.domain, pcisrc->addr.bus, + pcisrc->addr.slot, pcisrc->addr.function); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected hostdev type %d"), subsys->type); + } + return -1; + } + + *detach = hostdev; + + return 0; +} + +static int +testDomainRemoveHostDevice(testDriver *driver G_GNUC_UNUSED, + virDomainObj *vm, + virDomainHostdevDef *hostdev) +{ + virDomainNetDef *net = NULL; + size_t i; + + VIR_DEBUG("Removing host device %s from domain %p %s", + hostdev->info->alias, vm, vm->def->name); + + if (hostdev->parentnet) { + net = hostdev->parentnet; + for (i = 0; i < vm->def->nnets; i++) { + if (vm->def->nets[i] == hostdev->parentnet) { + virDomainNetRemove(vm->def, i); + break; + } + } + } + + for (i = 0; i < vm->def->nhostdevs; i++) { + if (vm->def->hostdevs[i] == hostdev) { + virDomainHostdevRemove(vm->def, i); + break; + } + } + + virDomainAuditHostdev(vm, hostdev, "detach", true); + + virDomainHostdevDefFree(hostdev); + + if (net) { + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + g_autoptr(virConnect) conn = virGetConnectNetwork(); + if (conn) + virDomainNetReleaseActualDevice(conn, vm->def, net); + else + VIR_WARN("Unable to release network device '%s'", NULLSTR(net->ifname)); + } + virDomainNetDefFree(net); + } + + return 0; +} + +static int +testDomainRemoveDevice(testDriver *driver, + virDomainObj *vm, + virDomainDeviceDef *dev) +{ + virDomainDeviceInfo *info; + virObjectEvent *event; + g_autofree char *alias = NULL; + + /* + * save the alias to use when sending a DEVICE_REMOVED event after + * all other teardown is complete + */ + if ((info = virDomainDeviceGetInfo(dev))) + alias = g_strdup(info->alias); + info = NULL; + + if ((virDomainDeviceType)dev->type != VIR_DOMAIN_DEVICE_HOSTDEV) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("don't know how to remove a %s device"), + virDomainDeviceTypeToString(dev->type)); + goto out; + } + + if (testDomainRemoveHostDevice(driver, vm, dev->data.hostdev) < 0) + return -1; + +out: + event = virDomainEventDeviceRemovedNewFromObj(vm, alias); + virObjectEventStateQueue(driver->eventState, event); + + return 0; +} + +static int +testDomainDetachDeviceLive(virDomainObj *vm, + virDomainDeviceDef *match, + testDriver *driver) +{ + virDomainDeviceDef detach = { .type = match->type }; + virDomainDeviceInfo *info = NULL; + + if ((virDomainDeviceType)match->type != VIR_DOMAIN_DEVICE_HOSTDEV) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("live detach of device '%s' is not supported"), + virDomainDeviceTypeToString(match->type)); + return -1; + } + + if (testDomainDetachPrepHostdev(vm, match->data.hostdev, + &detach.data.hostdev) < 0) + return -1; + + /* "detach" now points to the actual device we want to detach */ + + if (!(info = virDomainDeviceGetInfo(&detach))) { + /* + * This should never happen, since all of the device types in + * the switch cases that end with a "break" instead of a + * return have a virDeviceInfo in them. + */ + virReportError(VIR_ERR_INTERNAL_ERROR, + _("device of type '%s' has no device info"), + virDomainDeviceTypeToString(detach.type)); + return -1; + } + + /* Make generic validation checks common to all device types */ + + if (!info->alias) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot detach %s device with no alias"), + virDomainDeviceTypeToString(detach.type)); + return -1; + } + + return testDomainRemoveDevice(driver, vm, &detach); +} + +static int +testDomainDetachDeviceAliasLiveAndConfig(testDriver *driver, + virDomainObj *vm, + const char *alias, + unsigned int flags) +{ + virDomainDef *def = NULL; + g_autoptr(virDomainDef) vmdef = NULL; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE, -1); + + if (virDomainObjGetDefs(vm, flags, &def, NULL) < 0) + return -1; + + if (def) { + virDomainDeviceDef dev; + + if (virDomainDefFindDevice(def, alias, &dev, true) < 0) + return -1; + + if (testDomainDetachDeviceLive(vm, &dev, driver) < 0) + return -1; + } + + if (vmdef) { + if (virDomainDefSave(vmdef, driver->xmlopt, NULL) < 0) + return -1; + virDomainObjAssignDef(vm, &vmdef, false, NULL); + } + + return 0; +} + +static int +testDomainDetachDeviceAlias(virDomainPtr dom, + const char *alias, + unsigned int flags) +{ + testDriver *driver = dom->conn->privateData; + virDomainObj *vm = NULL; + int ret = -1; + + if (!(vm = testDomObjFromDomain(dom))) + return -1; + + if (virDomainObjUpdateModificationImpact(vm, &flags) < 0) + goto cleanup; + + if (testDomainDetachDeviceAliasLiveAndConfig(driver, vm, alias, flags) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} /* * Test driver @@ -10058,6 +10438,9 @@ static virHypervisorDriver testHypervisorDriver = { .connectListDomains = testConnectListDomains, /* 0.1.1 */ .connectNumOfDomains = testConnectNumOfDomains, /* 0.1.1 */ .connectListAllDomains = testConnectListAllDomains, /* 0.9.13 */ + .domainAttachDevice = testDomainAttachDevice, /* 9.10.0 */ + .domainAttachDeviceFlags = testDomainAttachDeviceFlags, /* 9.10.0 */ + .domainDetachDeviceAlias = testDomainDetachDeviceAlias, /* 9.10.0 */ .domainCreateXML = testDomainCreateXML, /* 0.1.4 */ .domainCreateXMLWithFiles = testDomainCreateXMLWithFiles, /* 5.7.0 */ .domainLookupByID = testDomainLookupByID, /* 0.1.1 */ -- 2.27.0 _______________________________________________ Devel mailing list -- devel@xxxxxxxxxxxxxxxxx To unsubscribe send an email to devel-leave@xxxxxxxxxxxxxxxxx