Skip to content

Commit

Permalink
virtual: get real PCI address for each device found
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
EmilienM committed Oct 5, 2023
1 parent d8a33a8 commit 415a6aa
Showing 1 changed file with 49 additions and 0 deletions.
49 changes: 49 additions & 0 deletions pkg/utils/utils_virtual.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"strconv"
"strings"

"github.com/golang/glog"
"github.com/hashicorp/go-retryablehttp"
Expand Down Expand Up @@ -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.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)
}
device.Address = realPCIAddr
}

return metaData, networkData, err
}

Expand Down Expand Up @@ -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()")
Expand Down

0 comments on commit 415a6aa

Please sign in to comment.