On Mon, Aug 5, 2013 at 8:13 PM, Laine Stump <laine@xxxxxxxxx> wrote: > This patch addresses two concerns with the error reporting when an > incompatible PCI address is specified for a device: > > 1) It wasn't always apparent which device had the problem. With this > patch applied, any error about an incompatible address will always > contain the full address as given in the config, so it will be easier > to determine which device's config aused the problem. > > 2) In some cases when the problem came from bad config, the error > message was erroneously classified as VIR_ERR_INTERNAL_ERROR. With > this patch applied, the same error message will be changed to indicate > either "internal" or "xml" error depending on whether the address came > from the config, or was automatically generated by libvirt. > > Note that in the case of "internal" (due to bad auto-generation) > errors, the PCI address won't be of much use in finding the location > in config to change (because it was automatically generated). Of > course that makes perfect sense, but still the address could provide a > clue about a bug in libvirt attempting to use a type of pci bus that > doesn't have its flags set correctly (or something similar). In other > words, it's not perfect, but it is definitely better. > --- > src/qemu/qemu_command.c | 224 +++++++++++++++++++++++++++++------------------- > 1 file changed, 138 insertions(+), 86 deletions(-) > > diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c > index 3d670f9..6060bb0 100644 > --- a/src/qemu/qemu_command.c > +++ b/src/qemu/qemu_command.c > @@ -1445,10 +1445,15 @@ struct _qemuDomainPCIAddressSet { > > static bool > qemuDomainPCIAddressFlagsCompatible(virDevicePCIAddressPtr addr, > + char *addrStr, Slight nitpick. const char maybe? > qemuDomainPCIConnectFlags busFlags, > qemuDomainPCIConnectFlags devFlags, > - bool reportError) > + bool reportError, > + bool fromConfig) > { > + virErrorNumber errType = (fromConfig > + ? VIR_ERR_XML_ERROR : VIR_ERR_INTERNAL_ERROR); > + > /* If this bus doesn't allow the type of connection (PCI > * vs. PCIe) required by the device, or if the device requires > * hot-plug and this bus doesn't have it, return false. > @@ -1456,24 +1461,24 @@ qemuDomainPCIAddressFlagsCompatible(virDevicePCIAddressPtr addr, > if (!(devFlags & busFlags & QEMU_PCI_CONNECT_TYPES_MASK)) { > if (reportError) { > if (devFlags & QEMU_PCI_CONNECT_TYPE_PCI) { > - virReportError(VIR_ERR_INTERNAL_ERROR, > - _("PCI bus %.4x:%.2x is not compatible with the " > - "device. Device requires a standard PCI slot, " > - "which is not provided by this bus"), > - addr->domain, addr->bus); > + virReportError(errType, > + _("PCI bus is not compatible with the device " > + "at %s. Device requires a standard PCI slot, " > + "which is not provided by bus %.4x:%.2x"), > + addrStr, addr->domain, addr->bus); > } else if (devFlags & QEMU_PCI_CONNECT_TYPE_PCIE) { > - virReportError(VIR_ERR_INTERNAL_ERROR, > - _("PCI bus %.4x:%.2x is not compatible with the " > - "device. Device requires a PCI Express slot, " > - "which is not provided by this bus"), > - addr->domain, addr->bus); > + virReportError(errType, > + _("PCI bus is not compatible with the device " > + "at %s. Device requires a PCI Express slot, " > + "which is not provided by bus %.4x:%.2x"), > + addrStr, addr->domain, addr->bus); > } else { > /* this should never happen. If it does, there is a > * bug in the code that sets the flag bits for devices. > */ > - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > - _("The device information has no PCI " > - "connection types listed")); > + virReportError(errType, > + _("The device information for %s has no PCI " > + "connection types listed"), addrStr); > } > } > return false; > @@ -1481,11 +1486,11 @@ qemuDomainPCIAddressFlagsCompatible(virDevicePCIAddressPtr addr, > if ((devFlags & QEMU_PCI_CONNECT_HOTPLUGGABLE) && > !(busFlags & QEMU_PCI_CONNECT_HOTPLUGGABLE)) { > if (reportError) { > - virReportError(VIR_ERR_INTERNAL_ERROR, > - _("PCI bus %.4x:%.2x is not compatible with the " > - "device. Device requires hot-plug capability, " > - "which is not provided by the bus"), > - addr->domain, addr->bus); > + virReportError(errType, > + _("PCI bus is not compatible with the device " > + "at %s. Device requires hot-plug capability, " > + "which is not provided by bus %.4x:%.2x"), > + addrStr, addr->domain, addr->bus); > } > return false; > } > @@ -1500,23 +1505,30 @@ qemuDomainPCIAddressFlagsCompatible(virDevicePCIAddressPtr addr, > static bool > qemuDomainPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs, > virDevicePCIAddressPtr addr, > - qemuDomainPCIConnectFlags flags) > + char *addrStr, Same here? const char? > + qemuDomainPCIConnectFlags flags, > + bool fromConfig) > { > qemuDomainPCIAddressBusPtr bus; > + virErrorNumber errType = (fromConfig > + ? VIR_ERR_XML_ERROR : VIR_ERR_INTERNAL_ERROR); > > if (addrs->nbuses == 0) { > - virReportError(VIR_ERR_XML_ERROR, "%s", _("No PCI buses available")); > + virReportError(errType, "%s", _("No PCI buses available")); > return false; > } > if (addr->domain != 0) { > - virReportError(VIR_ERR_XML_ERROR, "%s", > - _("Only PCI domain 0 is available")); > + virReportError(errType, > + _("Invalid PCI address %s. " > + "Only PCI domain 0 is available"), > + addrStr); > return false; > } > if (addr->bus >= addrs->nbuses) { > - virReportError(VIR_ERR_XML_ERROR, > - _("Only PCI buses up to %zu are available"), > - addrs->nbuses - 1); > + virReportError(errType, > + _("Invalid PCI address %s. " > + "Only PCI buses up to %zu are available"), > + addrStr, addrs->nbuses - 1); > return false; > } > > @@ -1525,26 +1537,27 @@ qemuDomainPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs, > /* assure that at least one of the requested connection types is > * provided by this bus > */ > - if (!qemuDomainPCIAddressFlagsCompatible(addr, bus->flags, flags, true)) > + if (!qemuDomainPCIAddressFlagsCompatible(addr, addrStr, bus->flags, > + flags, true, fromConfig)) > return false; > > /* some "buses" are really just a single port */ > if (bus->minSlot && addr->slot < bus->minSlot) { > - virReportError(VIR_ERR_XML_ERROR, > - _("Invalid PCI address: slot must be >= %zu"), > - bus->minSlot); > + virReportError(errType, > + _("Invalid PCI address %s. slot must be >= %zu"), > + addrStr, bus->minSlot); > return false; > } > if (addr->slot > bus->maxSlot) { > - virReportError(VIR_ERR_XML_ERROR, > - _("Invalid PCI address: slot must be <= %zu"), > - bus->maxSlot); > + virReportError(errType, > + _("Invalid PCI address %s. slot must be <= %zu"), > + addrStr, bus->maxSlot); > return false; > } > if (addr->function > QEMU_PCI_ADDRESS_FUNCTION_LAST) { > - virReportError(VIR_ERR_XML_ERROR, > - _("Invalid PCI address: function must be <= %u"), > - QEMU_PCI_ADDRESS_FUNCTION_LAST); > + virReportError(errType, > + _("Invalid PCI address %s. function must be <= %u"), > + addrStr, QEMU_PCI_ADDRESS_FUNCTION_LAST); > return false; > } > return true; > @@ -1953,12 +1966,12 @@ qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs, > bool fromConfig) > { > int ret = -1; > - char *str = NULL; > + char *addrStr = NULL; > qemuDomainPCIAddressBusPtr bus; > virErrorNumber errType = (fromConfig > ? VIR_ERR_XML_ERROR : VIR_ERR_INTERNAL_ERROR); > > - if (!(str = qemuDomainPCIAddressAsString(addr))) > + if (!(addrStr = qemuDomainPCIAddressAsString(addr))) > goto cleanup; > > /* Add an extra bus if necessary */ > @@ -1967,7 +1980,7 @@ qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs, > /* Check that the requested bus exists, is the correct type, and we > * are asking for a valid slot > */ > - if (!qemuDomainPCIAddressValidate(addrs, addr, flags)) > + if (!qemuDomainPCIAddressValidate(addrs, addr, addrStr, flags, fromConfig)) > goto cleanup; > > bus = &addrs->buses[addr->bus]; > @@ -1977,31 +1990,32 @@ qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs, > virReportError(errType, > _("Attempted double use of PCI slot %s " > "(may need \"multifunction='on'\" for " > - "device on function 0)"), str); > + "device on function 0)"), addrStr); > goto cleanup; > } > bus->slots[addr->slot] = 0xFF; /* reserve all functions of slot */ > - VIR_DEBUG("Reserving PCI slot %s (multifunction='off')", str); > + VIR_DEBUG("Reserving PCI slot %s (multifunction='off')", addrStr); > } else { > if (bus->slots[addr->slot] & (1 << addr->function)) { > if (addr->function == 0) { > virReportError(errType, > - _("Attempted double use of PCI Address %s"), str); > + _("Attempted double use of PCI Address %s"), > + addrStr); > } else { > virReportError(errType, > _("Attempted double use of PCI Address %s " > "(may need \"multifunction='on'\" " > - "for device on function 0)"), str); > + "for device on function 0)"), addrStr); > } > goto cleanup; > } > bus->slots[addr->slot] |= (1 << addr->function); > - VIR_DEBUG("Reserving PCI address %s", str); > + VIR_DEBUG("Reserving PCI address %s", addrStr); > } > > ret = 0; > cleanup: > - VIR_FREE(str); > + VIR_FREE(addrStr); > return ret; > } > > @@ -2017,7 +2031,8 @@ qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs, > int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs, > virDomainDeviceInfoPtr dev) > { > - int ret = 0; > + int ret = -1; > + char *addrStr = NULL; > /* Flags should be set according to the particular device, > * but only the caller knows the type of device. Currently this > * function is only used for hot-plug, though, and hot-plug is > @@ -2026,6 +2041,9 @@ int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs, > qemuDomainPCIConnectFlags flags = (QEMU_PCI_CONNECT_HOTPLUGGABLE | > QEMU_PCI_CONNECT_TYPE_PCI); > > + if (!(addrStr = qemuDomainPCIAddressAsString(&dev->addr.pci))) > + goto cleanup; > + > if (dev->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { > /* We do not support hotplug multi-function PCI device now, so we should > * reserve the whole slot. The function of the PCI device must be 0. > @@ -2034,16 +2052,20 @@ int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs, > virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > _("Only PCI device addresses with function=0" > " are supported")); > - return -1; > + goto cleanup; > } > > - if (!qemuDomainPCIAddressValidate(addrs, &dev->addr.pci, flags)) > - return -1; > + if (!qemuDomainPCIAddressValidate(addrs, &dev->addr.pci, > + addrStr, flags, true)) > + goto cleanup; > > ret = qemuDomainPCIAddressReserveSlot(addrs, &dev->addr.pci, flags); > } else { > ret = qemuDomainPCIAddressReserveNextSlot(addrs, dev, flags); > } > + > +cleanup: > + VIR_FREE(addrStr); > return ret; > } > > @@ -2063,12 +2085,20 @@ qemuDomainPCIAddressReleaseSlot(qemuDomainPCIAddressSetPtr addrs, > * already had it, and are giving it back. > */ > qemuDomainPCIConnectFlags flags = QEMU_PCI_CONNECT_TYPES_MASK; > + int ret = -1; > + char *addrStr = NULL; > > - if (!qemuDomainPCIAddressValidate(addrs, addr, flags)) > - return -1; > + if (!(addrStr = qemuDomainPCIAddressAsString(addr))) > + goto cleanup; > + > + if (!qemuDomainPCIAddressValidate(addrs, addr, addrStr, flags, false)) > + goto cleanup; > > addrs->buses[addr->bus].slots[addr->slot] = 0; > - return 0; > + ret = 0; > +cleanup: > + VIR_FREE(addrStr); > + return ret; > } > > void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs) > @@ -2090,6 +2120,7 @@ qemuDomainPCIAddressGetNextSlot(qemuDomainPCIAddressSetPtr addrs, > * 0000:00:00.0 > */ > virDevicePCIAddress a = { 0, 0, 0, 0, false }; > + char *addrStr = NULL; > > /* except if this search is for the exact same type of device as > * last time, continue the search from the previous match > @@ -2099,13 +2130,17 @@ qemuDomainPCIAddressGetNextSlot(qemuDomainPCIAddressSetPtr addrs, > > if (addrs->nbuses == 0) { > virReportError(VIR_ERR_XML_ERROR, "%s", _("No PCI buses available")); > - return -1; > + goto error; > } > > /* Start the search at the last used bus and slot */ > for (a.slot++; a.bus < addrs->nbuses; a.bus++) { > - if (!qemuDomainPCIAddressFlagsCompatible(&a, addrs->buses[a.bus].flags, > - flags, false)) { > + addrStr = NULL; > + if (!(addrStr = qemuDomainPCIAddressAsString(&a))) > + goto error; > + if (!qemuDomainPCIAddressFlagsCompatible(&a, addrStr, > + addrs->buses[a.bus].flags, > + flags, false, false)) { > VIR_DEBUG("PCI bus %.4x:%.2x is not compatible with the device", > a.domain, a.bus); > continue; > @@ -2124,13 +2159,17 @@ qemuDomainPCIAddressGetNextSlot(qemuDomainPCIAddressSetPtr addrs, > if (addrs->dryRun) { > /* a is already set to the first new bus and slot 1 */ > if (qemuDomainPCIAddressSetGrow(addrs, &a, flags) < 0) > - return -1; > + goto error; > goto success; > } else if (flags == addrs->lastFlags) { > /* Check the buses from 0 up to the last used one */ > for (a.bus = 0; a.bus <= addrs->lastaddr.bus; a.bus++) { > - if (!qemuDomainPCIAddressFlagsCompatible(&a, addrs->buses[a.bus].flags, > - flags, false)) { > + addrStr = NULL; > + if (!(addrStr = qemuDomainPCIAddressAsString(&a))) > + goto error; > + if (!qemuDomainPCIAddressFlagsCompatible(&a, addrStr, > + addrs->buses[a.bus].flags, > + flags, false, false)) { > VIR_DEBUG("PCI bus %.4x:%.2x is not compatible with the device", > a.domain, a.bus); > continue; > @@ -2147,12 +2186,15 @@ qemuDomainPCIAddressGetNextSlot(qemuDomainPCIAddressSetPtr addrs, > > virReportError(VIR_ERR_INTERNAL_ERROR, > "%s", _("No more available PCI slots")); > +error: > + VIR_FREE(addrStr); > return -1; > > success: > VIR_DEBUG("Found free PCI slot %.4x:%.2x:%.2x", > a.domain, a.bus, a.slot); > *next_addr = a; > + VIR_FREE(addrStr); > return 0; > } > > @@ -2217,10 +2259,12 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, > virQEMUCapsPtr qemuCaps, > qemuDomainPCIAddressSetPtr addrs) > { > + int ret = -1; > size_t i; > virDevicePCIAddress tmp_addr; > bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY); > virDevicePCIAddressPtr addrptr; > + char *addrStr = NULL; > qemuDomainPCIConnectFlags flags = QEMU_PCI_CONNECT_HOTPLUGGABLE | QEMU_PCI_CONNECT_TYPE_PCI; > > /* Verify that first IDE and USB controllers (if any) is on the PIIX3, fn 1 */ > @@ -2235,7 +2279,7 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, > def->controllers[i]->info.addr.pci.function != 1) { > virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > _("Primary IDE controller must have PCI address 0:0:1.1")); > - goto error; > + goto cleanup; > } > } else { > def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; > @@ -2255,7 +2299,7 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, > def->controllers[i]->info.addr.pci.function != 2) { > virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > _("PIIX3 USB controller must have PCI address 0:0:1.2")); > - goto error; > + goto cleanup; > } > } else { > def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; > @@ -2274,7 +2318,7 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, > memset(&tmp_addr, 0, sizeof(tmp_addr)); > tmp_addr.slot = 1; > if (qemuDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) > - goto error; > + goto cleanup; > } > > if (def->nvideos > 0) { > @@ -2293,8 +2337,11 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, > primaryVideo->info.addr.pci.function = 0; > addrptr = &primaryVideo->info.addr.pci; > > - if (!qemuDomainPCIAddressValidate(addrs, addrptr, flags)) > - goto error; > + if (!(addrStr = qemuDomainPCIAddressAsString(addrptr))) > + goto cleanup; > + if (!qemuDomainPCIAddressValidate(addrs, addrptr, > + addrStr, flags, false)) > + goto cleanup; > > if (qemuDomainPCIAddressSlotInUse(addrs, addrptr)) { > if (qemuDeviceVideoUsable) { > @@ -2302,15 +2349,15 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, > if (qemuDomainPCIAddressReserveNextSlot(addrs, > &primaryVideo->info, > flags) < 0) > - goto error; > + goto cleanup; > } else { > virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > _("PCI address 0:0:2.0 is in use, " > "QEMU needs it for primary video")); > - goto error; > + goto cleanup; > } > } else if (qemuDomainPCIAddressReserveSlot(addrs, addrptr, flags) < 0) { > - goto error; > + goto cleanup; > } > } else if (!qemuDeviceVideoUsable) { > if (primaryVideo->info.addr.pci.domain != 0 || > @@ -2319,7 +2366,7 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, > primaryVideo->info.addr.pci.function != 0) { > virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > _("Primary video card must have PCI address 0:0:2.0")); > - goto error; > + goto cleanup; > } > /* If TYPE==PCI, then qemuCollectPCIAddress() function > * has already reserved the address, so we must skip */ > @@ -2334,13 +2381,13 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, > " intervention"); > virResetLastError(); > } else if (qemuDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) { > - goto error; > + goto cleanup; > } > } > - return 0; > - > -error: > - return -1; > + ret = 0; > +cleanup: > + VIR_FREE(addrStr); > + return ret; > } > > > @@ -2357,10 +2404,12 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, > virQEMUCapsPtr qemuCaps, > qemuDomainPCIAddressSetPtr addrs) > { > + int ret = -1; > size_t i; > virDevicePCIAddress tmp_addr; > bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY); > virDevicePCIAddressPtr addrptr; > + char *addrStr = NULL; > qemuDomainPCIConnectFlags flags = QEMU_PCI_CONNECT_TYPE_PCIE; > > /* Verify that the first SATA controller is at 00:1F.2 */ > @@ -2375,7 +2424,7 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, > def->controllers[i]->info.addr.pci.function != 2) { > virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > _("Primary SATA controller must have PCI address 0:0:1f.2")); > - goto error; > + goto cleanup; > } > } else { > def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; > @@ -2399,12 +2448,12 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, > tmp_addr.multi = 1; > if (qemuDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, > false, false) < 0) > - goto error; > + goto cleanup; > tmp_addr.function = 3; > tmp_addr.multi = 0; > if (qemuDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, > false, false) < 0) > - goto error; > + goto cleanup; > } > > if (def->nvideos > 0) { > @@ -2423,8 +2472,11 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, > primaryVideo->info.addr.pci.function = 0; > addrptr = &primaryVideo->info.addr.pci; > > - if (!qemuDomainPCIAddressValidate(addrs, addrptr, flags)) > - goto error; > + if (!(addrStr = qemuDomainPCIAddressAsString(addrptr))) > + goto cleanup; > + if (!qemuDomainPCIAddressValidate(addrs, addrptr, > + addrStr, flags, false)) > + goto cleanup; > > if (qemuDomainPCIAddressSlotInUse(addrs, addrptr)) { > if (qemuDeviceVideoUsable) { > @@ -2432,15 +2484,15 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, > if (qemuDomainPCIAddressReserveNextSlot(addrs, > &primaryVideo->info, > flags) < 0) > - goto error; > + goto cleanup; > } else { > virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > _("PCI address 0:0:1.0 is in use, " > "QEMU needs it for primary video")); > - goto error; > + goto cleanup; > } > } else if (qemuDomainPCIAddressReserveSlot(addrs, addrptr, flags) < 0) { > - goto error; > + goto cleanup; > } > } else if (!qemuDeviceVideoUsable) { > if (primaryVideo->info.addr.pci.domain != 0 || > @@ -2449,7 +2501,7 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, > primaryVideo->info.addr.pci.function != 0) { > virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > _("Primary video card must have PCI address 0:0:1.0")); > - goto error; > + goto cleanup; > } > /* If TYPE==PCI, then qemuCollectPCIAddress() function > * has already reserved the address, so we must skip */ > @@ -2464,13 +2516,13 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, > " intervention"); > virResetLastError(); > } else if (qemuDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) { > - goto error; > + goto cleanup; > } > } > - return 0; > - > -error: > - return -1; > + ret = 0; > +cleanup: > + VIR_FREE(addrStr); > + return ret; > } > > > -- > 1.7.11.7 > Other than my slight nitpick, visually this looks good. I'll give it a whirl with some of my bad configs tonight. -- Doug Goldstein -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list