From c2a635e2ce961a72f7fb992222549b0ad3376d4e Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 2 Oct 2023 11:39:13 -0400 Subject: [PATCH] virtual: get real PCI address for each device found We can't rely on the PCI address from the metadata so we will lookup the real PCI address for the NIC that matches the MAC address. Libvirt/QEMU cannot guarantee that the address specified in the XML will match the address seen by the guest. This is a well known limitation: https://libvirt.org/pci-addresses.html When using the q35 machine type, it highlights this issue due to the change from using PCI to PCI-E bus for virtual devices. With that said, the PCI value in Nova Metadata is a best effort hint due to the limitations mentioned above. Therefore we will lookup the real PCI address for the NIC that matches the MAC address. --- pkg/utils/utils_virtual.go | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/pkg/utils/utils_virtual.go b/pkg/utils/utils_virtual.go index 193e67d53..f25370b3a 100644 --- a/pkg/utils/utils_virtual.go +++ b/pkg/utils/utils_virtual.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "github.com/golang/glog" "github.com/hashicorp/go-retryablehttp" @@ -118,6 +119,27 @@ func GetOpenstackData(useHostPath bool) (metaData *OSPMetaData, networkData *OSP if err != nil { metaData, networkData, err = getOpenstackDataFromMetadataService() } + + // We can't rely on the PCI address from the metadata so we will lookup the real PCI address + // for the NIC that matches the MAC address. + // + // Libvirt/QEMU cannot guarantee that the address specified in the XML will match the address seen by the guest. + // This is a well known limitation: https://libvirt.org/pci-addresses.html + // When using the q35 machine type, it highlights this issue due to the change from using PCI to PCI-E bus for virtual devices. + // + // With that said, the PCI value in Nova Metadata is a best effort hint due to the limitations mentioned above. Therefore + // we will lookup the real PCI address for the NIC that matches the MAC address. + for _, device := range metaData.Devices { + realPCIAddr, err := getPCIAddressFromMACAddress(device.Mac) + if err != nil { + return metaData, networkData, fmt.Errorf("GetOpenstackData(): error getting PCI address for device %s: %w", device.Mac, err) + } + if realPCIAddr != device.Address { + glog.V(2).Infof("GetOpenstackData(): PCI address for device %s does not match Nova metadata value %s, it'll be overwritten with %s", device.Mac, device.Address, realPCIAddr) + } + metaData.Devices[i].Address = realPCIAddr + } + return metaData, networkData, err } @@ -205,6 +227,33 @@ func getOpenstackDataFromMetadataService() (metaData *OSPMetaData, networkData * return metaData, networkData, nil } +// getPCIAddressFromMACAddress returns the PCI address of a device given its MAC address +func getPCIAddressFromMACAddress(macAddress string) (string, error) { + nics, err := ghw.Network() + if err != nil { + return "", fmt.Errorf("error getting network info: %w", err) + } + + var pciAddress string + for _, nic := range nics.NICs { + if strings.EqualFold(nic.MacAddress, macAddress) { + if pciAddress == "" { + pciAddress = *nic.PCIAddress + } else { + // Check if there are more than one device with the same MAC address + // and return an error if that's the case, we don't support that scenario for now. + return "", fmt.Errorf("more than one device found with MAC address %s", macAddress) + } + } + } + + if pciAddress != "" { + return pciAddress, nil + } + + return "", fmt.Errorf("no device found with MAC address %s", macAddress) +} + // CreateOpenstackDevicesInfo create the openstack device info map func CreateOpenstackDevicesInfo(metaData *OSPMetaData, networkData *OSPNetworkData) (OSPDevicesInfo, error) { glog.Infof("CreateOpenstackDevicesInfo()")