Merge "Record SRIOV PF MAC in the binding profile"
This commit is contained in:
commit
93a65f06df
|
@ -10929,6 +10929,9 @@ class ComputeManager(manager.Manager):
|
||||||
if profile.get('vf_num'):
|
if profile.get('vf_num'):
|
||||||
profile['vf_num'] = pci_dev.sriov_cap['vf_num']
|
profile['vf_num'] = pci_dev.sriov_cap['vf_num']
|
||||||
|
|
||||||
|
if pci_dev.mac_address:
|
||||||
|
profile['device_mac_address'] = pci_dev.mac_address
|
||||||
|
|
||||||
mig_vif.profile = profile
|
mig_vif.profile = profile
|
||||||
LOG.debug("Updating migrate VIF profile for port %(port_id)s:"
|
LOG.debug("Updating migrate VIF profile for port %(port_id)s:"
|
||||||
"%(profile)s", {'port_id': port_id,
|
"%(profile)s", {'port_id': port_id,
|
||||||
|
|
|
@ -684,7 +684,8 @@ class API:
|
||||||
for profile_key in ('pci_vendor_info', 'pci_slot',
|
for profile_key in ('pci_vendor_info', 'pci_slot',
|
||||||
constants.ALLOCATION, 'arq_uuid',
|
constants.ALLOCATION, 'arq_uuid',
|
||||||
'physical_network', 'card_serial_number',
|
'physical_network', 'card_serial_number',
|
||||||
'vf_num', 'pf_mac_address'):
|
'vf_num', 'pf_mac_address',
|
||||||
|
'device_mac_address'):
|
||||||
if profile_key in port_profile:
|
if profile_key in port_profile:
|
||||||
del port_profile[profile_key]
|
del port_profile[profile_key]
|
||||||
port_req_body['port'][constants.BINDING_PROFILE] = port_profile
|
port_req_body['port'][constants.BINDING_PROFILE] = port_profile
|
||||||
|
@ -1307,6 +1308,10 @@ class API:
|
||||||
network=network, neutron=neutron,
|
network=network, neutron=neutron,
|
||||||
bind_host_id=bind_host_id,
|
bind_host_id=bind_host_id,
|
||||||
port_arq=port_arq)
|
port_arq=port_arq)
|
||||||
|
# NOTE(gibi): Remove this once we are sure that the fix for
|
||||||
|
# bug 1942329 is always present in the deployed neutron. The
|
||||||
|
# _populate_neutron_extension_values() call above already
|
||||||
|
# populated this MAC to the binding profile instead.
|
||||||
self._populate_pci_mac_address(instance,
|
self._populate_pci_mac_address(instance,
|
||||||
request.pci_request_id, port_req_body)
|
request.pci_request_id, port_req_body)
|
||||||
|
|
||||||
|
@ -1598,6 +1603,18 @@ class API:
|
||||||
if pci_dev.dev_type == obj_fields.PciDeviceType.SRIOV_VF:
|
if pci_dev.dev_type == obj_fields.PciDeviceType.SRIOV_VF:
|
||||||
dev_profile.update(
|
dev_profile.update(
|
||||||
self._get_vf_pci_device_profile(pci_dev))
|
self._get_vf_pci_device_profile(pci_dev))
|
||||||
|
|
||||||
|
if pci_dev.dev_type == obj_fields.PciDeviceType.SRIOV_PF:
|
||||||
|
# In general the MAC address information flows fom the neutron
|
||||||
|
# port to the device in the backend. Except for direct-physical
|
||||||
|
# ports. In that case the MAC address flows from the physical
|
||||||
|
# device, the PF, to the neutron port. So when such a port is
|
||||||
|
# being bound to a host the port's MAC address needs to be
|
||||||
|
# updated. Nova needs to put the new MAC into the binding
|
||||||
|
# profile.
|
||||||
|
if pci_dev.mac_address:
|
||||||
|
dev_profile['device_mac_address'] = pci_dev.mac_address
|
||||||
|
|
||||||
return dev_profile
|
return dev_profile
|
||||||
|
|
||||||
raise exception.PciDeviceNotFound(node_id=pci_dev.compute_node_id,
|
raise exception.PciDeviceNotFound(node_id=pci_dev.compute_node_id,
|
||||||
|
@ -3639,11 +3656,10 @@ class API:
|
||||||
revert = migration and migration.status == 'reverted'
|
revert = migration and migration.status == 'reverted'
|
||||||
return instance.migration_context.get_pci_mapping_for_migration(revert)
|
return instance.migration_context.get_pci_mapping_for_migration(revert)
|
||||||
|
|
||||||
def _get_port_pci_dev(self, context, instance, port):
|
def _get_port_pci_dev(self, instance, port):
|
||||||
"""Find the PCI device corresponding to the port.
|
"""Find the PCI device corresponding to the port.
|
||||||
Assumes the port is an SRIOV one.
|
Assumes the port is an SRIOV one.
|
||||||
|
|
||||||
:param context: The request context.
|
|
||||||
:param instance: The instance to which the port is attached.
|
:param instance: The instance to which the port is attached.
|
||||||
:param port: The Neutron port, as obtained from the Neutron API
|
:param port: The Neutron port, as obtained from the Neutron API
|
||||||
JSON form.
|
JSON form.
|
||||||
|
@ -3731,15 +3747,14 @@ class API:
|
||||||
raise exception.PortUpdateFailed(port_id=p['id'],
|
raise exception.PortUpdateFailed(port_id=p['id'],
|
||||||
reason=_("Unable to correlate PCI slot %s") %
|
reason=_("Unable to correlate PCI slot %s") %
|
||||||
pci_slot)
|
pci_slot)
|
||||||
# NOTE(artom) If migration is None, this is an unshevle, and we
|
# NOTE(artom) If migration is None, this is an unshelve, and we
|
||||||
# need to figure out the pci_slot from the InstancePCIRequest
|
# need to figure out the pci related binding information from
|
||||||
# and PciDevice objects.
|
# the InstancePCIRequest and PciDevice objects.
|
||||||
else:
|
else:
|
||||||
pci_dev = self._get_port_pci_dev(context, instance, p)
|
pci_dev = self._get_port_pci_dev(instance, p)
|
||||||
if pci_dev:
|
if pci_dev:
|
||||||
binding_profile.update(
|
binding_profile.update(
|
||||||
self._get_pci_device_profile(pci_dev)
|
self._get_pci_device_profile(pci_dev))
|
||||||
)
|
|
||||||
updates[constants.BINDING_PROFILE] = binding_profile
|
updates[constants.BINDING_PROFILE] = binding_profile
|
||||||
|
|
||||||
# NOTE(gibi): during live migration the conductor already sets the
|
# NOTE(gibi): during live migration the conductor already sets the
|
||||||
|
|
|
@ -148,6 +148,12 @@ class PciDevice(base.NovaPersistentObject, base.NovaObject):
|
||||||
reason='dev_type=%s not supported in version %s' % (
|
reason='dev_type=%s not supported in version %s' % (
|
||||||
dev_type, target_version))
|
dev_type, target_version))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
f'PciDevice(address={self.address}, '
|
||||||
|
f'compute_node_id={self.compute_node_id})'
|
||||||
|
)
|
||||||
|
|
||||||
def update_device(self, dev_dict):
|
def update_device(self, dev_dict):
|
||||||
"""Sync the content from device dictionary to device object.
|
"""Sync the content from device dictionary to device object.
|
||||||
|
|
||||||
|
@ -175,6 +181,9 @@ class PciDevice(base.NovaPersistentObject, base.NovaObject):
|
||||||
# NOTE(ralonsoh): list of parameters currently added to
|
# NOTE(ralonsoh): list of parameters currently added to
|
||||||
# "extra_info" dict:
|
# "extra_info" dict:
|
||||||
# - "capabilities": dict of (strings/list of strings)
|
# - "capabilities": dict of (strings/list of strings)
|
||||||
|
# - "parent_ifname": the netdev name of the parent (PF)
|
||||||
|
# device of a VF
|
||||||
|
# - "mac_address": the MAC address of the PF
|
||||||
extra_info = self.extra_info
|
extra_info = self.extra_info
|
||||||
data = v if isinstance(v, str) else jsonutils.dumps(v)
|
data = v if isinstance(v, str) else jsonutils.dumps(v)
|
||||||
extra_info.update({k: data})
|
extra_info.update({k: data})
|
||||||
|
@ -572,6 +581,13 @@ class PciDevice(base.NovaPersistentObject, base.NovaObject):
|
||||||
caps = jsonutils.loads(caps_json)
|
caps = jsonutils.loads(caps_json)
|
||||||
return caps.get('sriov', {})
|
return caps.get('sriov', {})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mac_address(self):
|
||||||
|
"""The MAC address of the PF physical device or None if the device is
|
||||||
|
not a PF or if the MAC is not available.
|
||||||
|
"""
|
||||||
|
return self.extra_info.get('mac_address')
|
||||||
|
|
||||||
|
|
||||||
@base.NovaObjectRegistry.register
|
@base.NovaObjectRegistry.register
|
||||||
class PciDeviceList(base.ObjectListBase, base.NovaObject):
|
class PciDeviceList(base.ObjectListBase, base.NovaObject):
|
||||||
|
@ -611,3 +627,6 @@ class PciDeviceList(base.ObjectListBase, base.NovaObject):
|
||||||
parent_addr)
|
parent_addr)
|
||||||
return base.obj_make_list(context, cls(context), objects.PciDevice,
|
return base.obj_make_list(context, cls(context), objects.PciDevice,
|
||||||
db_dev_list)
|
db_dev_list)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"PciDeviceList(objects={[repr(obj) for obj in self.objects]})"
|
||||||
|
|
|
@ -309,7 +309,7 @@ class FakePCIDevice(object):
|
||||||
self, dev_type, bus, slot, function, iommu_group, numa_node, *,
|
self, dev_type, bus, slot, function, iommu_group, numa_node, *,
|
||||||
vf_ratio=None, multiple_gpu_types=False, generic_types=False,
|
vf_ratio=None, multiple_gpu_types=False, generic_types=False,
|
||||||
parent=None, vend_id=None, vend_name=None, prod_id=None,
|
parent=None, vend_id=None, vend_name=None, prod_id=None,
|
||||||
prod_name=None, driver_name=None, vpd_fields=None
|
prod_name=None, driver_name=None, vpd_fields=None, mac_address=None,
|
||||||
):
|
):
|
||||||
"""Populate pci devices
|
"""Populate pci devices
|
||||||
|
|
||||||
|
@ -331,6 +331,8 @@ class FakePCIDevice(object):
|
||||||
:param prod_id: (str) The product ID.
|
:param prod_id: (str) The product ID.
|
||||||
:param prod_name: (str) The product name.
|
:param prod_name: (str) The product name.
|
||||||
:param driver_name: (str) The driver name.
|
:param driver_name: (str) The driver name.
|
||||||
|
:param mac_address: (str) The MAC of the device.
|
||||||
|
Used in case of SRIOV PFs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.dev_type = dev_type
|
self.dev_type = dev_type
|
||||||
|
@ -349,6 +351,7 @@ class FakePCIDevice(object):
|
||||||
self.prod_id = prod_id
|
self.prod_id = prod_id
|
||||||
self.prod_name = prod_name
|
self.prod_name = prod_name
|
||||||
self.driver_name = driver_name
|
self.driver_name = driver_name
|
||||||
|
self.mac_address = mac_address
|
||||||
|
|
||||||
self.vpd_fields = vpd_fields
|
self.vpd_fields = vpd_fields
|
||||||
|
|
||||||
|
@ -364,7 +367,9 @@ class FakePCIDevice(object):
|
||||||
assert not self.vf_ratio, 'vf_ratio does not apply for PCI devices'
|
assert not self.vf_ratio, 'vf_ratio does not apply for PCI devices'
|
||||||
|
|
||||||
if self.dev_type in ('PF', 'VF'):
|
if self.dev_type in ('PF', 'VF'):
|
||||||
assert self.vf_ratio, 'require vf_ratio for PFs and VFs'
|
assert (
|
||||||
|
self.vf_ratio is not None
|
||||||
|
), 'require vf_ratio for PFs and VFs'
|
||||||
|
|
||||||
if self.dev_type == 'VF':
|
if self.dev_type == 'VF':
|
||||||
assert self.parent, 'require parent for VFs'
|
assert self.parent, 'require parent for VFs'
|
||||||
|
@ -497,6 +502,10 @@ class FakePCIDevice(object):
|
||||||
def XMLDesc(self, flags):
|
def XMLDesc(self, flags):
|
||||||
return self.pci_device
|
return self.pci_device
|
||||||
|
|
||||||
|
@property
|
||||||
|
def address(self):
|
||||||
|
return "0000:%02x:%02x.%1x" % (self.bus, self.slot, self.function)
|
||||||
|
|
||||||
|
|
||||||
# TODO(stephenfin): Remove all of these HostFooDevicesInfo objects in favour of
|
# TODO(stephenfin): Remove all of these HostFooDevicesInfo objects in favour of
|
||||||
# a unified devices object
|
# a unified devices object
|
||||||
|
@ -609,7 +618,7 @@ class HostPCIDevicesInfo(object):
|
||||||
self, dev_type, bus, slot, function, iommu_group, numa_node,
|
self, dev_type, bus, slot, function, iommu_group, numa_node,
|
||||||
vf_ratio=None, multiple_gpu_types=False, generic_types=False,
|
vf_ratio=None, multiple_gpu_types=False, generic_types=False,
|
||||||
parent=None, vend_id=None, vend_name=None, prod_id=None,
|
parent=None, vend_id=None, vend_name=None, prod_id=None,
|
||||||
prod_name=None, driver_name=None, vpd_fields=None,
|
prod_name=None, driver_name=None, vpd_fields=None, mac_address=None,
|
||||||
):
|
):
|
||||||
pci_dev_name = _get_libvirt_nodedev_name(bus, slot, function)
|
pci_dev_name = _get_libvirt_nodedev_name(bus, slot, function)
|
||||||
|
|
||||||
|
@ -632,6 +641,7 @@ class HostPCIDevicesInfo(object):
|
||||||
prod_name=prod_name,
|
prod_name=prod_name,
|
||||||
driver_name=driver_name,
|
driver_name=driver_name,
|
||||||
vpd_fields=vpd_fields,
|
vpd_fields=vpd_fields,
|
||||||
|
mac_address=mac_address,
|
||||||
)
|
)
|
||||||
self.devices[pci_dev_name] = dev
|
self.devices[pci_dev_name] = dev
|
||||||
return dev
|
return dev
|
||||||
|
@ -651,6 +661,13 @@ class HostPCIDevicesInfo(object):
|
||||||
return [dev for dev in self.devices
|
return [dev for dev in self.devices
|
||||||
if self.devices[dev].is_capable_of_mdevs]
|
if self.devices[dev].is_capable_of_mdevs]
|
||||||
|
|
||||||
|
def get_pci_address_mac_mapping(self):
|
||||||
|
return {
|
||||||
|
device.address: device.mac_address
|
||||||
|
for dev_addr, device in self.devices.items()
|
||||||
|
if device.mac_address
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class FakeMdevDevice(object):
|
class FakeMdevDevice(object):
|
||||||
template = """
|
template = """
|
||||||
|
@ -2182,6 +2199,15 @@ class LibvirtFixture(fixtures.Fixture):
|
||||||
|
|
||||||
def __init__(self, stub_os_vif=True):
|
def __init__(self, stub_os_vif=True):
|
||||||
self.stub_os_vif = stub_os_vif
|
self.stub_os_vif = stub_os_vif
|
||||||
|
self.pci_address_to_mac_map = collections.defaultdict(
|
||||||
|
lambda: '52:54:00:1e:59:c6')
|
||||||
|
|
||||||
|
def update_sriov_mac_address_mapping(self, pci_address_to_mac_map):
|
||||||
|
self.pci_address_to_mac_map.update(pci_address_to_mac_map)
|
||||||
|
|
||||||
|
def fake_get_mac_by_pci_address(self, pci_addr, pf_interface=False):
|
||||||
|
res = self.pci_address_to_mac_map[pci_addr]
|
||||||
|
return res
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
@ -2205,7 +2231,7 @@ class LibvirtFixture(fixtures.Fixture):
|
||||||
|
|
||||||
self.useFixture(fixtures.MockPatch(
|
self.useFixture(fixtures.MockPatch(
|
||||||
'nova.pci.utils.get_mac_by_pci_address',
|
'nova.pci.utils.get_mac_by_pci_address',
|
||||||
return_value='52:54:00:1e:59:c6'))
|
new=self.fake_get_mac_by_pci_address))
|
||||||
|
|
||||||
# libvirt calls out to sysfs to get the vfs ID during macvtap plug
|
# libvirt calls out to sysfs to get the vfs ID during macvtap plug
|
||||||
self.useFixture(fixtures.MockPatch(
|
self.useFixture(fixtures.MockPatch(
|
||||||
|
|
|
@ -42,7 +42,7 @@ class ServersTestBase(integrated_helpers._IntegratedTestBase):
|
||||||
super(ServersTestBase, self).setUp()
|
super(ServersTestBase, self).setUp()
|
||||||
|
|
||||||
self.useFixture(nova_fixtures.LibvirtImageBackendFixture())
|
self.useFixture(nova_fixtures.LibvirtImageBackendFixture())
|
||||||
self.useFixture(nova_fixtures.LibvirtFixture())
|
self.libvirt = self.useFixture(nova_fixtures.LibvirtFixture())
|
||||||
self.useFixture(nova_fixtures.OSBrickFixture())
|
self.useFixture(nova_fixtures.OSBrickFixture())
|
||||||
|
|
||||||
self.useFixture(fixtures.MockPatch(
|
self.useFixture(fixtures.MockPatch(
|
||||||
|
@ -134,6 +134,12 @@ class ServersTestBase(integrated_helpers._IntegratedTestBase):
|
||||||
host_info, pci_info, mdev_info, vdpa_info, libvirt_version,
|
host_info, pci_info, mdev_info, vdpa_info, libvirt_version,
|
||||||
qemu_version, hostname,
|
qemu_version, hostname,
|
||||||
)
|
)
|
||||||
|
# If the compute is configured with PCI devices then we need to
|
||||||
|
# make sure that the stubs around sysfs has the MAC address
|
||||||
|
# information for the PCI PF devices
|
||||||
|
if pci_info:
|
||||||
|
self.libvirt.update_sriov_mac_address_mapping(
|
||||||
|
pci_info.get_pci_address_mac_mapping())
|
||||||
# This is fun. Firstly we need to do a global'ish mock so we can
|
# This is fun. Firstly we need to do a global'ish mock so we can
|
||||||
# actually start the service.
|
# actually start the service.
|
||||||
with mock.patch('nova.virt.libvirt.host.Host.get_connection',
|
with mock.patch('nova.virt.libvirt.host.Host.get_connection',
|
||||||
|
@ -392,6 +398,22 @@ class LibvirtNeutronFixture(nova_fixtures.NeutronFixture):
|
||||||
'binding:vnic_type': 'remote-managed',
|
'binding:vnic_type': 'remote-managed',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
network_4_port_pf = {
|
||||||
|
'id': 'c6f51315-9202-416f-9e2f-eb78b3ac36d9',
|
||||||
|
'network_id': network_4['id'],
|
||||||
|
'status': 'ACTIVE',
|
||||||
|
'mac_address': 'b5:bc:2e:e7:51:01',
|
||||||
|
'fixed_ips': [
|
||||||
|
{
|
||||||
|
'ip_address': '192.168.4.8',
|
||||||
|
'subnet_id': subnet_4['id']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'binding:vif_details': {'vlan': 42},
|
||||||
|
'binding:vif_type': 'hostdev_physical',
|
||||||
|
'binding:vnic_type': 'direct-physical',
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, test):
|
def __init__(self, test):
|
||||||
super(LibvirtNeutronFixture, self).__init__(test)
|
super(LibvirtNeutronFixture, self).__init__(test)
|
||||||
self._networks = {
|
self._networks = {
|
||||||
|
|
|
@ -364,31 +364,66 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
expect_fail=False):
|
expect_fail=False):
|
||||||
# The purpose here is to force an observable PCI slot update when
|
# The purpose here is to force an observable PCI slot update when
|
||||||
# moving from source to dest. This is accomplished by having a single
|
# moving from source to dest. This is accomplished by having a single
|
||||||
# PCI device on the source, 2 PCI devices on the test, and relying on
|
# PCI VF device on the source, 2 PCI VF devices on the dest, and
|
||||||
# the fact that our fake HostPCIDevicesInfo creates predictable PCI
|
# relying on the fact that our fake HostPCIDevicesInfo creates
|
||||||
# addresses. The PCI device on source and the first PCI device on dest
|
# predictable PCI addresses. The PCI VF device on source and the first
|
||||||
# will have identical PCI addresses. By sticking a "placeholder"
|
# PCI VF device on dest will have identical PCI addresses. By sticking
|
||||||
# instance on that first PCI device on the dest, the incoming instance
|
# a "placeholder" instance on that first PCI VF device on the dest, the
|
||||||
# from source will be forced to consume the second dest PCI device,
|
# incoming instance from source will be forced to consume the second
|
||||||
# with a different PCI address.
|
# dest PCI VF device, with a different PCI address.
|
||||||
|
# We want to test server operations with SRIOV VFs and SRIOV PFs so
|
||||||
|
# the config of the compute hosts also have one extra PCI PF devices
|
||||||
|
# without any VF children. But the two compute has different PCI PF
|
||||||
|
# addresses and MAC so that the test can observe the slot update as
|
||||||
|
# well as the MAC updated during migration and after revert.
|
||||||
|
source_pci_info = fakelibvirt.HostPCIDevicesInfo(num_pfs=1, num_vfs=1)
|
||||||
|
# add an extra PF without VF to be used by direct-physical ports
|
||||||
|
source_pci_info.add_device(
|
||||||
|
dev_type='PF',
|
||||||
|
bus=0x82, # the HostPCIDevicesInfo use the 0x81 by default
|
||||||
|
slot=0x0,
|
||||||
|
function=0,
|
||||||
|
iommu_group=42,
|
||||||
|
numa_node=0,
|
||||||
|
vf_ratio=0,
|
||||||
|
mac_address='b4:96:91:34:f4:aa',
|
||||||
|
)
|
||||||
self.start_compute(
|
self.start_compute(
|
||||||
hostname='source',
|
hostname='source',
|
||||||
pci_info=fakelibvirt.HostPCIDevicesInfo(
|
pci_info=source_pci_info)
|
||||||
num_pfs=1, num_vfs=1))
|
|
||||||
|
dest_pci_info = fakelibvirt.HostPCIDevicesInfo(num_pfs=1, num_vfs=2)
|
||||||
|
# add an extra PF without VF to be used by direct-physical ports
|
||||||
|
dest_pci_info.add_device(
|
||||||
|
dev_type='PF',
|
||||||
|
bus=0x82, # the HostPCIDevicesInfo use the 0x81 by default
|
||||||
|
slot=0x6, # make it different from the source host
|
||||||
|
function=0,
|
||||||
|
iommu_group=42,
|
||||||
|
numa_node=0,
|
||||||
|
vf_ratio=0,
|
||||||
|
mac_address='b4:96:91:34:f4:bb',
|
||||||
|
)
|
||||||
self.start_compute(
|
self.start_compute(
|
||||||
hostname='dest',
|
hostname='dest',
|
||||||
pci_info=fakelibvirt.HostPCIDevicesInfo(
|
pci_info=dest_pci_info)
|
||||||
num_pfs=1, num_vfs=2))
|
|
||||||
|
|
||||||
source_port = self.neutron.create_port(
|
source_port = self.neutron.create_port(
|
||||||
{'port': self.neutron.network_4_port_1})
|
{'port': self.neutron.network_4_port_1})
|
||||||
|
source_pf_port = self.neutron.create_port(
|
||||||
|
{'port': self.neutron.network_4_port_pf})
|
||||||
dest_port1 = self.neutron.create_port(
|
dest_port1 = self.neutron.create_port(
|
||||||
{'port': self.neutron.network_4_port_2})
|
{'port': self.neutron.network_4_port_2})
|
||||||
dest_port2 = self.neutron.create_port(
|
dest_port2 = self.neutron.create_port(
|
||||||
{'port': self.neutron.network_4_port_3})
|
{'port': self.neutron.network_4_port_3})
|
||||||
|
|
||||||
source_server = self._create_server(
|
source_server = self._create_server(
|
||||||
networks=[{'port': source_port['port']['id']}], host='source')
|
networks=[
|
||||||
|
{'port': source_port['port']['id']},
|
||||||
|
{'port': source_pf_port['port']['id']}
|
||||||
|
],
|
||||||
|
host='source',
|
||||||
|
)
|
||||||
dest_server1 = self._create_server(
|
dest_server1 = self._create_server(
|
||||||
networks=[{'port': dest_port1['port']['id']}], host='dest')
|
networks=[{'port': dest_port1['port']['id']}], host='dest')
|
||||||
dest_server2 = self._create_server(
|
dest_server2 = self._create_server(
|
||||||
|
@ -396,6 +431,7 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
|
|
||||||
# Refresh the ports.
|
# Refresh the ports.
|
||||||
source_port = self.neutron.show_port(source_port['port']['id'])
|
source_port = self.neutron.show_port(source_port['port']['id'])
|
||||||
|
source_pf_port = self.neutron.show_port(source_pf_port['port']['id'])
|
||||||
dest_port1 = self.neutron.show_port(dest_port1['port']['id'])
|
dest_port1 = self.neutron.show_port(dest_port1['port']['id'])
|
||||||
dest_port2 = self.neutron.show_port(dest_port2['port']['id'])
|
dest_port2 = self.neutron.show_port(dest_port2['port']['id'])
|
||||||
|
|
||||||
|
@ -411,11 +447,24 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
same_slot_port = dest_port2
|
same_slot_port = dest_port2
|
||||||
self._delete_server(dest_server1)
|
self._delete_server(dest_server1)
|
||||||
|
|
||||||
# Before moving, explictly assert that the servers on source and dest
|
# Before moving, explicitly assert that the servers on source and dest
|
||||||
# have the same pci_slot in their port's binding profile
|
# have the same pci_slot in their port's binding profile
|
||||||
self.assertEqual(source_port['port']['binding:profile']['pci_slot'],
|
self.assertEqual(source_port['port']['binding:profile']['pci_slot'],
|
||||||
same_slot_port['port']['binding:profile']['pci_slot'])
|
same_slot_port['port']['binding:profile']['pci_slot'])
|
||||||
|
|
||||||
|
# Assert that the direct-physical port got the pci_slot information
|
||||||
|
# according to the source host PF PCI device.
|
||||||
|
self.assertEqual(
|
||||||
|
'0000:82:00.0', # which is in sync with the source host pci_info
|
||||||
|
source_pf_port['port']['binding:profile']['pci_slot']
|
||||||
|
)
|
||||||
|
# Assert that the direct-physical port is updated with the MAC address
|
||||||
|
# of the PF device from the source host
|
||||||
|
self.assertEqual(
|
||||||
|
'b4:96:91:34:f4:aa',
|
||||||
|
source_pf_port['port']['binding:profile']['device_mac_address']
|
||||||
|
)
|
||||||
|
|
||||||
# Before moving, assert that the servers on source and dest have the
|
# Before moving, assert that the servers on source and dest have the
|
||||||
# same PCI source address in their XML for their SRIOV nic.
|
# same PCI source address in their XML for their SRIOV nic.
|
||||||
source_conn = self.computes['source'].driver._host.get_connection()
|
source_conn = self.computes['source'].driver._host.get_connection()
|
||||||
|
@ -432,14 +481,28 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
move_operation(source_server)
|
move_operation(source_server)
|
||||||
|
|
||||||
# Refresh the ports again, keeping in mind the source_port is now bound
|
# Refresh the ports again, keeping in mind the source_port is now bound
|
||||||
# on the dest after unshelving.
|
# on the dest after the move.
|
||||||
source_port = self.neutron.show_port(source_port['port']['id'])
|
source_port = self.neutron.show_port(source_port['port']['id'])
|
||||||
same_slot_port = self.neutron.show_port(same_slot_port['port']['id'])
|
same_slot_port = self.neutron.show_port(same_slot_port['port']['id'])
|
||||||
|
source_pf_port = self.neutron.show_port(source_pf_port['port']['id'])
|
||||||
|
|
||||||
self.assertNotEqual(
|
self.assertNotEqual(
|
||||||
source_port['port']['binding:profile']['pci_slot'],
|
source_port['port']['binding:profile']['pci_slot'],
|
||||||
same_slot_port['port']['binding:profile']['pci_slot'])
|
same_slot_port['port']['binding:profile']['pci_slot'])
|
||||||
|
|
||||||
|
# Assert that the direct-physical port got the pci_slot information
|
||||||
|
# according to the dest host PF PCI device.
|
||||||
|
self.assertEqual(
|
||||||
|
'0000:82:06.0', # which is in sync with the dest host pci_info
|
||||||
|
source_pf_port['port']['binding:profile']['pci_slot']
|
||||||
|
)
|
||||||
|
# Assert that the direct-physical port is updated with the MAC address
|
||||||
|
# of the PF device from the dest host
|
||||||
|
self.assertEqual(
|
||||||
|
'b4:96:91:34:f4:bb',
|
||||||
|
source_pf_port['port']['binding:profile']['device_mac_address']
|
||||||
|
)
|
||||||
|
|
||||||
conn = self.computes['dest'].driver._host.get_connection()
|
conn = self.computes['dest'].driver._host.get_connection()
|
||||||
vms = [vm._def for vm in conn._vms.values()]
|
vms = [vm._def for vm in conn._vms.values()]
|
||||||
self.assertEqual(2, len(vms))
|
self.assertEqual(2, len(vms))
|
||||||
|
@ -467,6 +530,169 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
self._confirm_resize(source_server)
|
self._confirm_resize(source_server)
|
||||||
self._test_move_operation_with_neutron(move_operation)
|
self._test_move_operation_with_neutron(move_operation)
|
||||||
|
|
||||||
|
def test_cold_migrate_and_rever_server_with_neutron(self):
|
||||||
|
# The purpose here is to force an observable PCI slot update when
|
||||||
|
# moving from source to dest and the from dest to source after the
|
||||||
|
# revert. This is accomplished by having a single
|
||||||
|
# PCI VF device on the source, 2 PCI VF devices on the dest, and
|
||||||
|
# relying on the fact that our fake HostPCIDevicesInfo creates
|
||||||
|
# predictable PCI addresses. The PCI VF device on source and the first
|
||||||
|
# PCI VF device on dest will have identical PCI addresses. By sticking
|
||||||
|
# a "placeholder" instance on that first PCI VF device on the dest, the
|
||||||
|
# incoming instance from source will be forced to consume the second
|
||||||
|
# dest PCI VF device, with a different PCI address.
|
||||||
|
# We want to test server operations with SRIOV VFs and SRIOV PFs so
|
||||||
|
# the config of the compute hosts also have one extra PCI PF devices
|
||||||
|
# without any VF children. But the two compute has different PCI PF
|
||||||
|
# addresses and MAC so that the test can observe the slot update as
|
||||||
|
# well as the MAC updated during migration and after revert.
|
||||||
|
source_pci_info = fakelibvirt.HostPCIDevicesInfo(num_pfs=1, num_vfs=1)
|
||||||
|
# add an extra PF without VF to be used by direct-physical ports
|
||||||
|
source_pci_info.add_device(
|
||||||
|
dev_type='PF',
|
||||||
|
bus=0x82, # the HostPCIDevicesInfo use the 0x81 by default
|
||||||
|
slot=0x0,
|
||||||
|
function=0,
|
||||||
|
iommu_group=42,
|
||||||
|
numa_node=0,
|
||||||
|
vf_ratio=0,
|
||||||
|
mac_address='b4:96:91:34:f4:aa',
|
||||||
|
)
|
||||||
|
self.start_compute(
|
||||||
|
hostname='source',
|
||||||
|
pci_info=source_pci_info)
|
||||||
|
dest_pci_info = fakelibvirt.HostPCIDevicesInfo(num_pfs=1, num_vfs=2)
|
||||||
|
# add an extra PF without VF to be used by direct-physical ports
|
||||||
|
dest_pci_info.add_device(
|
||||||
|
dev_type='PF',
|
||||||
|
bus=0x82, # the HostPCIDevicesInfo use the 0x81 by default
|
||||||
|
slot=0x6, # make it different from the source host
|
||||||
|
function=0,
|
||||||
|
iommu_group=42,
|
||||||
|
numa_node=0,
|
||||||
|
vf_ratio=0,
|
||||||
|
mac_address='b4:96:91:34:f4:bb',
|
||||||
|
)
|
||||||
|
self.start_compute(
|
||||||
|
hostname='dest',
|
||||||
|
pci_info=dest_pci_info)
|
||||||
|
source_port = self.neutron.create_port(
|
||||||
|
{'port': self.neutron.network_4_port_1})
|
||||||
|
source_pf_port = self.neutron.create_port(
|
||||||
|
{'port': self.neutron.network_4_port_pf})
|
||||||
|
dest_port1 = self.neutron.create_port(
|
||||||
|
{'port': self.neutron.network_4_port_2})
|
||||||
|
dest_port2 = self.neutron.create_port(
|
||||||
|
{'port': self.neutron.network_4_port_3})
|
||||||
|
source_server = self._create_server(
|
||||||
|
networks=[
|
||||||
|
{'port': source_port['port']['id']},
|
||||||
|
{'port': source_pf_port['port']['id']}
|
||||||
|
],
|
||||||
|
host='source',
|
||||||
|
)
|
||||||
|
dest_server1 = self._create_server(
|
||||||
|
networks=[{'port': dest_port1['port']['id']}], host='dest')
|
||||||
|
dest_server2 = self._create_server(
|
||||||
|
networks=[{'port': dest_port2['port']['id']}], host='dest')
|
||||||
|
# Refresh the ports.
|
||||||
|
source_port = self.neutron.show_port(source_port['port']['id'])
|
||||||
|
source_pf_port = self.neutron.show_port(source_pf_port['port']['id'])
|
||||||
|
dest_port1 = self.neutron.show_port(dest_port1['port']['id'])
|
||||||
|
dest_port2 = self.neutron.show_port(dest_port2['port']['id'])
|
||||||
|
# Find the server on the dest compute that's using the same pci_slot as
|
||||||
|
# the server on the source compute, and delete the other one to make
|
||||||
|
# room for the incoming server from the source.
|
||||||
|
source_pci_slot = source_port['port']['binding:profile']['pci_slot']
|
||||||
|
dest_pci_slot1 = dest_port1['port']['binding:profile']['pci_slot']
|
||||||
|
if dest_pci_slot1 == source_pci_slot:
|
||||||
|
same_slot_port = dest_port1
|
||||||
|
self._delete_server(dest_server2)
|
||||||
|
else:
|
||||||
|
same_slot_port = dest_port2
|
||||||
|
self._delete_server(dest_server1)
|
||||||
|
# Before moving, explicitly assert that the servers on source and dest
|
||||||
|
# have the same pci_slot in their port's binding profile
|
||||||
|
self.assertEqual(source_port['port']['binding:profile']['pci_slot'],
|
||||||
|
same_slot_port['port']['binding:profile']['pci_slot'])
|
||||||
|
# Assert that the direct-physical port got the pci_slot information
|
||||||
|
# according to the source host PF PCI device.
|
||||||
|
self.assertEqual(
|
||||||
|
'0000:82:00.0', # which is in sync with the source host pci_info
|
||||||
|
source_pf_port['port']['binding:profile']['pci_slot']
|
||||||
|
)
|
||||||
|
# Assert that the direct-physical port is updated with the MAC address
|
||||||
|
# of the PF device from the source host
|
||||||
|
self.assertEqual(
|
||||||
|
'b4:96:91:34:f4:aa',
|
||||||
|
source_pf_port['port']['binding:profile']['device_mac_address']
|
||||||
|
)
|
||||||
|
# Before moving, assert that the servers on source and dest have the
|
||||||
|
# same PCI source address in their XML for their SRIOV nic.
|
||||||
|
source_conn = self.computes['source'].driver._host.get_connection()
|
||||||
|
dest_conn = self.computes['source'].driver._host.get_connection()
|
||||||
|
source_vms = [vm._def for vm in source_conn._vms.values()]
|
||||||
|
dest_vms = [vm._def for vm in dest_conn._vms.values()]
|
||||||
|
self.assertEqual(1, len(source_vms))
|
||||||
|
self.assertEqual(1, len(dest_vms))
|
||||||
|
self.assertEqual(1, len(source_vms[0]['devices']['nics']))
|
||||||
|
self.assertEqual(1, len(dest_vms[0]['devices']['nics']))
|
||||||
|
self.assertEqual(source_vms[0]['devices']['nics'][0]['source'],
|
||||||
|
dest_vms[0]['devices']['nics'][0]['source'])
|
||||||
|
|
||||||
|
# TODO(stephenfin): The mock of 'migrate_disk_and_power_off' should
|
||||||
|
# probably be less...dumb
|
||||||
|
with mock.patch('nova.virt.libvirt.driver.LibvirtDriver'
|
||||||
|
'.migrate_disk_and_power_off', return_value='{}'):
|
||||||
|
self._migrate_server(source_server)
|
||||||
|
|
||||||
|
# Refresh the ports again, keeping in mind the ports are now bound
|
||||||
|
# on the dest after migrating.
|
||||||
|
source_port = self.neutron.show_port(source_port['port']['id'])
|
||||||
|
same_slot_port = self.neutron.show_port(same_slot_port['port']['id'])
|
||||||
|
source_pf_port = self.neutron.show_port(source_pf_port['port']['id'])
|
||||||
|
self.assertNotEqual(
|
||||||
|
source_port['port']['binding:profile']['pci_slot'],
|
||||||
|
same_slot_port['port']['binding:profile']['pci_slot'])
|
||||||
|
# Assert that the direct-physical port got the pci_slot information
|
||||||
|
# according to the dest host PF PCI device.
|
||||||
|
self.assertEqual(
|
||||||
|
'0000:82:06.0', # which is in sync with the dest host pci_info
|
||||||
|
source_pf_port['port']['binding:profile']['pci_slot']
|
||||||
|
)
|
||||||
|
# Assert that the direct-physical port is updated with the MAC address
|
||||||
|
# of the PF device from the dest host
|
||||||
|
self.assertEqual(
|
||||||
|
'b4:96:91:34:f4:bb',
|
||||||
|
source_pf_port['port']['binding:profile']['device_mac_address']
|
||||||
|
)
|
||||||
|
conn = self.computes['dest'].driver._host.get_connection()
|
||||||
|
vms = [vm._def for vm in conn._vms.values()]
|
||||||
|
self.assertEqual(2, len(vms))
|
||||||
|
for vm in vms:
|
||||||
|
self.assertEqual(1, len(vm['devices']['nics']))
|
||||||
|
self.assertNotEqual(vms[0]['devices']['nics'][0]['source'],
|
||||||
|
vms[1]['devices']['nics'][0]['source'])
|
||||||
|
|
||||||
|
self._revert_resize(source_server)
|
||||||
|
|
||||||
|
# Refresh the ports again, keeping in mind the ports are now bound
|
||||||
|
# on the source as the migration is reverted
|
||||||
|
source_pf_port = self.neutron.show_port(source_pf_port['port']['id'])
|
||||||
|
|
||||||
|
# Assert that the direct-physical port got the pci_slot information
|
||||||
|
# according to the source host PF PCI device.
|
||||||
|
self.assertEqual(
|
||||||
|
'0000:82:00.0', # which is in sync with the source host pci_info
|
||||||
|
source_pf_port['port']['binding:profile']['pci_slot']
|
||||||
|
)
|
||||||
|
# Assert that the direct-physical port is updated with the MAC address
|
||||||
|
# of the PF device from the source host
|
||||||
|
self.assertEqual(
|
||||||
|
'b4:96:91:34:f4:aa',
|
||||||
|
source_pf_port['port']['binding:profile']['device_mac_address']
|
||||||
|
)
|
||||||
|
|
||||||
def test_evacuate_server_with_neutron(self):
|
def test_evacuate_server_with_neutron(self):
|
||||||
def move_operation(source_server):
|
def move_operation(source_server):
|
||||||
# Down the source compute to enable the evacuation
|
# Down the source compute to enable the evacuation
|
||||||
|
@ -484,17 +710,44 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# start two compute services with differing PCI device inventory
|
# start two compute services with differing PCI device inventory
|
||||||
self.start_compute(
|
source_pci_info = fakelibvirt.HostPCIDevicesInfo(
|
||||||
hostname='test_compute0',
|
num_pfs=2, num_vfs=8, numa_node=0)
|
||||||
pci_info=fakelibvirt.HostPCIDevicesInfo(
|
# add an extra PF without VF to be used by direct-physical ports
|
||||||
num_pfs=2, num_vfs=8, numa_node=0))
|
source_pci_info.add_device(
|
||||||
self.start_compute(
|
dev_type='PF',
|
||||||
hostname='test_compute1',
|
bus=0x82, # the HostPCIDevicesInfo use the 0x81 by default
|
||||||
pci_info=fakelibvirt.HostPCIDevicesInfo(
|
slot=0x0,
|
||||||
num_pfs=1, num_vfs=2, numa_node=1))
|
function=0,
|
||||||
|
iommu_group=42,
|
||||||
|
numa_node=0,
|
||||||
|
vf_ratio=0,
|
||||||
|
mac_address='b4:96:91:34:f4:aa',
|
||||||
|
)
|
||||||
|
self.start_compute(hostname='test_compute0', pci_info=source_pci_info)
|
||||||
|
|
||||||
# create the port
|
dest_pci_info = fakelibvirt.HostPCIDevicesInfo(
|
||||||
self.neutron.create_port({'port': self.neutron.network_4_port_1})
|
num_pfs=1, num_vfs=2, numa_node=1)
|
||||||
|
# add an extra PF without VF to be used by direct-physical ports
|
||||||
|
dest_pci_info.add_device(
|
||||||
|
dev_type='PF',
|
||||||
|
bus=0x82, # the HostPCIDevicesInfo use the 0x81 by default
|
||||||
|
slot=0x6, # make it different from the source host
|
||||||
|
function=0,
|
||||||
|
iommu_group=42,
|
||||||
|
# numa node needs to be aligned with the other pci devices in this
|
||||||
|
# host as the instance needs to fit into a single host numa node
|
||||||
|
numa_node=1,
|
||||||
|
vf_ratio=0,
|
||||||
|
mac_address='b4:96:91:34:f4:bb',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.start_compute(hostname='test_compute1', pci_info=dest_pci_info)
|
||||||
|
|
||||||
|
# create the ports
|
||||||
|
port = self.neutron.create_port(
|
||||||
|
{'port': self.neutron.network_4_port_1})['port']
|
||||||
|
pf_port = self.neutron.create_port(
|
||||||
|
{'port': self.neutron.network_4_port_pf})['port']
|
||||||
|
|
||||||
# create a server using the VF via neutron
|
# create a server using the VF via neutron
|
||||||
extra_spec = {'hw:cpu_policy': 'dedicated'}
|
extra_spec = {'hw:cpu_policy': 'dedicated'}
|
||||||
|
@ -502,7 +755,8 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
server = self._create_server(
|
server = self._create_server(
|
||||||
flavor_id=flavor_id,
|
flavor_id=flavor_id,
|
||||||
networks=[
|
networks=[
|
||||||
{'port': base.LibvirtNeutronFixture.network_4_port_1['id']},
|
{'port': port['id']},
|
||||||
|
{'port': pf_port['id']},
|
||||||
],
|
],
|
||||||
host='test_compute0',
|
host='test_compute0',
|
||||||
)
|
)
|
||||||
|
@ -510,8 +764,8 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
# our source host should have marked two PCI devices as used, the VF
|
# our source host should have marked two PCI devices as used, the VF
|
||||||
# and the parent PF, while the future destination is currently unused
|
# and the parent PF, while the future destination is currently unused
|
||||||
self.assertEqual('test_compute0', server['OS-EXT-SRV-ATTR:host'])
|
self.assertEqual('test_compute0', server['OS-EXT-SRV-ATTR:host'])
|
||||||
self.assertPCIDeviceCounts('test_compute0', total=10, free=8)
|
self.assertPCIDeviceCounts('test_compute0', total=11, free=8)
|
||||||
self.assertPCIDeviceCounts('test_compute1', total=3, free=3)
|
self.assertPCIDeviceCounts('test_compute1', total=4, free=4)
|
||||||
|
|
||||||
# the instance should be on host NUMA node 0, since that's where our
|
# the instance should be on host NUMA node 0, since that's where our
|
||||||
# PCI devices are
|
# PCI devices are
|
||||||
|
@ -540,13 +794,26 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
port['binding:profile'],
|
port['binding:profile'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ensure the binding details sent to "neutron" are correct
|
||||||
|
pf_port = self.neutron.show_port(pf_port['id'],)['port']
|
||||||
|
self.assertIn('binding:profile', pf_port)
|
||||||
|
self.assertEqual(
|
||||||
|
{
|
||||||
|
'pci_vendor_info': '8086:1528',
|
||||||
|
'pci_slot': '0000:82:00.0',
|
||||||
|
'physical_network': 'physnet4',
|
||||||
|
'device_mac_address': 'b4:96:91:34:f4:aa',
|
||||||
|
},
|
||||||
|
pf_port['binding:profile'],
|
||||||
|
)
|
||||||
|
|
||||||
# now live migrate that server
|
# now live migrate that server
|
||||||
self._live_migrate(server, 'completed')
|
self._live_migrate(server, 'completed')
|
||||||
|
|
||||||
# we should now have transitioned our usage to the destination, freeing
|
# we should now have transitioned our usage to the destination, freeing
|
||||||
# up the source in the process
|
# up the source in the process
|
||||||
self.assertPCIDeviceCounts('test_compute0', total=10, free=10)
|
self.assertPCIDeviceCounts('test_compute0', total=11, free=11)
|
||||||
self.assertPCIDeviceCounts('test_compute1', total=3, free=1)
|
self.assertPCIDeviceCounts('test_compute1', total=4, free=1)
|
||||||
|
|
||||||
# the instance should now be on host NUMA node 1, since that's where
|
# the instance should now be on host NUMA node 1, since that's where
|
||||||
# our PCI devices are for this second host
|
# our PCI devices are for this second host
|
||||||
|
@ -571,6 +838,18 @@ class SRIOVServersTest(_PCIServersWithMigrationTestBase):
|
||||||
},
|
},
|
||||||
port['binding:profile'],
|
port['binding:profile'],
|
||||||
)
|
)
|
||||||
|
# ensure the binding details sent to "neutron" are correct
|
||||||
|
pf_port = self.neutron.show_port(pf_port['id'],)['port']
|
||||||
|
self.assertIn('binding:profile', pf_port)
|
||||||
|
self.assertEqual(
|
||||||
|
{
|
||||||
|
'pci_vendor_info': '8086:1528',
|
||||||
|
'pci_slot': '0000:82:06.0',
|
||||||
|
'physical_network': 'physnet4',
|
||||||
|
'device_mac_address': 'b4:96:91:34:f4:bb',
|
||||||
|
},
|
||||||
|
pf_port['binding:profile'],
|
||||||
|
)
|
||||||
|
|
||||||
def test_get_server_diagnostics_server_with_VF(self):
|
def test_get_server_diagnostics_server_with_VF(self):
|
||||||
"""Ensure server disagnostics include info on VF-type PCI devices."""
|
"""Ensure server disagnostics include info on VF-type PCI devices."""
|
||||||
|
|
|
@ -5714,13 +5714,15 @@ class ComputeTestCase(BaseTestCase,
|
||||||
objects=[objects.PciDevice(vendor_id='1377',
|
objects=[objects.PciDevice(vendor_id='1377',
|
||||||
product_id='0047',
|
product_id='0047',
|
||||||
address='0000:0a:00.1',
|
address='0000:0a:00.1',
|
||||||
request_id=uuids.req1)])
|
request_id=uuids.req1,
|
||||||
|
compute_node_id=1)])
|
||||||
|
|
||||||
new_pci_devices = objects.PciDeviceList(
|
new_pci_devices = objects.PciDeviceList(
|
||||||
objects=[objects.PciDevice(vendor_id='1377',
|
objects=[objects.PciDevice(vendor_id='1377',
|
||||||
product_id='0047',
|
product_id='0047',
|
||||||
address='0000:0b:00.1',
|
address='0000:0b:00.1',
|
||||||
request_id=uuids.req2)])
|
request_id=uuids.req2,
|
||||||
|
compute_node_id=2)])
|
||||||
|
|
||||||
if expected_pci_addr == old_pci_devices[0].address:
|
if expected_pci_addr == old_pci_devices[0].address:
|
||||||
expected_pci_device = old_pci_devices[0]
|
expected_pci_device = old_pci_devices[0]
|
||||||
|
|
|
@ -8988,10 +8988,12 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase,
|
||||||
self._mock_rt()
|
self._mock_rt()
|
||||||
old_devs = objects.PciDeviceList(
|
old_devs = objects.PciDeviceList(
|
||||||
objects=[objects.PciDevice(
|
objects=[objects.PciDevice(
|
||||||
|
compute_node_id=1,
|
||||||
address='0000:04:00.2',
|
address='0000:04:00.2',
|
||||||
request_id=uuids.pcidev1)])
|
request_id=uuids.pcidev1)])
|
||||||
new_devs = objects.PciDeviceList(
|
new_devs = objects.PciDeviceList(
|
||||||
objects=[objects.PciDevice(
|
objects=[objects.PciDevice(
|
||||||
|
compute_node_id=2,
|
||||||
address='0000:05:00.3',
|
address='0000:05:00.3',
|
||||||
request_id=uuids.pcidev1)])
|
request_id=uuids.pcidev1)])
|
||||||
self.instance.migration_context = objects.MigrationContext(
|
self.instance.migration_context = objects.MigrationContext(
|
||||||
|
@ -10962,40 +10964,94 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase,
|
||||||
_test()
|
_test()
|
||||||
|
|
||||||
def test__update_migrate_vifs_profile_with_pci(self):
|
def test__update_migrate_vifs_profile_with_pci(self):
|
||||||
# Define two migrate vifs with only one pci that is required
|
# Define three migrate vifs with two pci devs that are required
|
||||||
# to be updated. Make sure method under test updated the correct one
|
# to be updated, one VF and on PF.
|
||||||
|
# Make sure method under test updated the correct devs with the correct
|
||||||
|
# values.
|
||||||
nw_vifs = network_model.NetworkInfo(
|
nw_vifs = network_model.NetworkInfo(
|
||||||
[network_model.VIF(
|
[
|
||||||
id=uuids.port0,
|
network_model.VIF(
|
||||||
vnic_type='direct',
|
id=uuids.port0,
|
||||||
type=network_model.VIF_TYPE_HW_VEB,
|
vnic_type='direct',
|
||||||
profile={'pci_slot': '0000:04:00.3',
|
type=network_model.VIF_TYPE_HW_VEB,
|
||||||
'pci_vendor_info': '15b3:1018',
|
profile={
|
||||||
'physical_network': 'default'}),
|
'pci_slot': '0000:04:00.3',
|
||||||
network_model.VIF(
|
'pci_vendor_info': '15b3:1018',
|
||||||
id=uuids.port1,
|
'physical_network': 'default',
|
||||||
vnic_type='normal',
|
},
|
||||||
type=network_model.VIF_TYPE_OVS,
|
),
|
||||||
profile={'some': 'attribute'})])
|
network_model.VIF(
|
||||||
pci_dev = objects.PciDevice(request_id=uuids.pci_req,
|
id=uuids.port1,
|
||||||
address='0000:05:00.4',
|
vnic_type='normal',
|
||||||
vendor_id='15b3',
|
type=network_model.VIF_TYPE_OVS,
|
||||||
product_id='1018')
|
profile={'some': 'attribute'},
|
||||||
port_id_to_pci_dev = {uuids.port0: pci_dev}
|
),
|
||||||
mig_vifs = migrate_data_obj.VIFMigrateData.\
|
network_model.VIF(
|
||||||
create_skeleton_migrate_vifs(nw_vifs)
|
id=uuids.port2,
|
||||||
self.compute._update_migrate_vifs_profile_with_pci(mig_vifs,
|
vnic_type='direct-physical',
|
||||||
port_id_to_pci_dev)
|
type=network_model.VIF_TYPE_HOSTDEV,
|
||||||
|
profile={
|
||||||
|
'pci_slot': '0000:01:00',
|
||||||
|
'pci_vendor_info': '8086:154d',
|
||||||
|
'physical_network': 'physnet2',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
pci_vf_dev = objects.PciDevice(
|
||||||
|
request_id=uuids.pci_req,
|
||||||
|
address='0000:05:00.4',
|
||||||
|
parent_addr='0000:05:00',
|
||||||
|
vendor_id='15b3',
|
||||||
|
product_id='1018',
|
||||||
|
compute_node_id=13,
|
||||||
|
dev_type=fields.PciDeviceType.SRIOV_VF,
|
||||||
|
)
|
||||||
|
pci_pf_dev = objects.PciDevice(
|
||||||
|
request_id=uuids.pci_req2,
|
||||||
|
address='0000:01:00',
|
||||||
|
parent_addr='0000:02:00',
|
||||||
|
vendor_id='8086',
|
||||||
|
product_id='154d',
|
||||||
|
compute_node_id=13,
|
||||||
|
dev_type=fields.PciDeviceType.SRIOV_PF,
|
||||||
|
extra_info={'mac_address': 'b4:96:91:34:f4:36'},
|
||||||
|
)
|
||||||
|
port_id_to_pci_dev = {
|
||||||
|
uuids.port0: pci_vf_dev,
|
||||||
|
uuids.port2: pci_pf_dev,
|
||||||
|
}
|
||||||
|
mig_vifs = (
|
||||||
|
migrate_data_obj.VIFMigrateData.create_skeleton_migrate_vifs(
|
||||||
|
nw_vifs)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.compute._update_migrate_vifs_profile_with_pci(
|
||||||
|
mig_vifs, port_id_to_pci_dev)
|
||||||
|
|
||||||
# Make sure method under test updated the correct one.
|
# Make sure method under test updated the correct one.
|
||||||
changed_mig_vif = mig_vifs[0]
|
changed_vf_mig_vif = mig_vifs[0]
|
||||||
unchanged_mig_vif = mig_vifs[1]
|
unchanged_mig_vif = mig_vifs[1]
|
||||||
|
changed_pf_mig_vif = mig_vifs[2]
|
||||||
# Migrate vifs profile was updated with pci_dev.address
|
# Migrate vifs profile was updated with pci_dev.address
|
||||||
# for port ID uuids.port0.
|
# for port ID uuids.port0.
|
||||||
self.assertEqual(changed_mig_vif.profile['pci_slot'],
|
self.assertEqual(changed_vf_mig_vif.profile['pci_slot'],
|
||||||
pci_dev.address)
|
pci_vf_dev.address)
|
||||||
|
# MAC is not added as this is a VF
|
||||||
|
self.assertNotIn('device_mac_address', changed_vf_mig_vif.profile)
|
||||||
# Migrate vifs profile was unchanged for port ID uuids.port1.
|
# Migrate vifs profile was unchanged for port ID uuids.port1.
|
||||||
# i.e 'profile' attribute does not exist.
|
# i.e 'profile' attribute does not exist.
|
||||||
self.assertNotIn('profile', unchanged_mig_vif)
|
self.assertNotIn('profile', unchanged_mig_vif)
|
||||||
|
# Migrate vifs profile was updated with pci_dev.address
|
||||||
|
# for port ID uuids.port2.
|
||||||
|
self.assertEqual(changed_pf_mig_vif.profile['pci_slot'],
|
||||||
|
pci_pf_dev.address)
|
||||||
|
# MAC is updated as this is a PF
|
||||||
|
self.assertEqual(
|
||||||
|
'b4:96:91:34:f4:36',
|
||||||
|
changed_pf_mig_vif.profile['device_mac_address']
|
||||||
|
)
|
||||||
|
|
||||||
def test_get_updated_nw_info_with_pci_mapping(self):
|
def test_get_updated_nw_info_with_pci_mapping(self):
|
||||||
old_dev = objects.PciDevice(address='0000:04:00.2')
|
old_dev = objects.PciDevice(address='0000:04:00.2')
|
||||||
|
|
|
@ -4805,6 +4805,174 @@ class TestAPI(TestAPIBase):
|
||||||
constants.BINDING_HOST_ID],
|
constants.BINDING_HOST_ID],
|
||||||
'new-host')
|
'new-host')
|
||||||
|
|
||||||
|
@mock.patch(
|
||||||
|
'nova.network.neutron.API.has_extended_resource_request_extension',
|
||||||
|
new=mock.Mock(return_value=False),
|
||||||
|
)
|
||||||
|
@mock.patch.object(pci_whitelist.Whitelist, 'get_devspec')
|
||||||
|
@mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock())
|
||||||
|
def test_update_port_bindings_for_instance_with_sriov_pf(
|
||||||
|
self, get_client_mock, get_pci_device_devspec_mock
|
||||||
|
):
|
||||||
|
devspec = mock.Mock()
|
||||||
|
devspec.get_tags.return_value = {'physical_network': 'physnet1'}
|
||||||
|
get_pci_device_devspec_mock.return_value = devspec
|
||||||
|
|
||||||
|
instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
instance.migration_context = objects.MigrationContext()
|
||||||
|
instance.migration_context.old_pci_devices = objects.PciDeviceList(
|
||||||
|
objects=[
|
||||||
|
objects.PciDevice(
|
||||||
|
vendor_id='8086',
|
||||||
|
product_id='154d',
|
||||||
|
address='0000:0a:01',
|
||||||
|
compute_node_id=1,
|
||||||
|
request_id=uuids.pci_req,
|
||||||
|
dev_type=obj_fields.PciDeviceType.SRIOV_PF,
|
||||||
|
extra_info={'mac_address': 'b4:96:91:34:f4:36'},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
instance.pci_devices = instance.migration_context.old_pci_devices
|
||||||
|
instance.migration_context.new_pci_devices = objects.PciDeviceList(
|
||||||
|
objects=[
|
||||||
|
objects.PciDevice(
|
||||||
|
vendor_id='8086',
|
||||||
|
product_id='154d',
|
||||||
|
address='0000:0a:02',
|
||||||
|
compute_node_id=2,
|
||||||
|
request_id=uuids.pci_req,
|
||||||
|
dev_type=obj_fields.PciDeviceType.SRIOV_PF,
|
||||||
|
extra_info={'mac_address': 'b4:96:91:34:f4:dd'},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
instance.pci_devices = instance.migration_context.new_pci_devices
|
||||||
|
|
||||||
|
fake_ports = {
|
||||||
|
'ports': [
|
||||||
|
{
|
||||||
|
'id': uuids.port,
|
||||||
|
'binding:vnic_type': 'direct-physical',
|
||||||
|
constants.BINDING_HOST_ID: 'fake-host-old',
|
||||||
|
constants.BINDING_PROFILE: {
|
||||||
|
'pci_slot': '0000:0a:01',
|
||||||
|
'physical_network': 'old_phys_net',
|
||||||
|
'pci_vendor_info': 'old_pci_vendor_info',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
migration = objects.Migration(
|
||||||
|
status='confirmed', migration_type='migration')
|
||||||
|
list_ports_mock = mock.Mock(return_value=fake_ports)
|
||||||
|
get_client_mock.return_value.list_ports = list_ports_mock
|
||||||
|
|
||||||
|
update_port_mock = mock.Mock()
|
||||||
|
get_client_mock.return_value.update_port = update_port_mock
|
||||||
|
|
||||||
|
self.api._update_port_binding_for_instance(
|
||||||
|
self.context, instance, instance.host, migration)
|
||||||
|
|
||||||
|
# Assert that update_port is called with the binding:profile
|
||||||
|
# corresponding to the PCI device specified including MAC address.
|
||||||
|
update_port_mock.assert_called_once_with(
|
||||||
|
uuids.port,
|
||||||
|
{
|
||||||
|
'port': {
|
||||||
|
constants.BINDING_HOST_ID: 'fake-host',
|
||||||
|
'device_owner': 'compute:%s' % instance.availability_zone,
|
||||||
|
constants.BINDING_PROFILE: {
|
||||||
|
'pci_slot': '0000:0a:02',
|
||||||
|
'physical_network': 'physnet1',
|
||||||
|
'pci_vendor_info': '8086:154d',
|
||||||
|
'device_mac_address': 'b4:96:91:34:f4:dd',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch(
|
||||||
|
'nova.network.neutron.API.has_extended_resource_request_extension',
|
||||||
|
new=mock.Mock(return_value=False),
|
||||||
|
)
|
||||||
|
@mock.patch.object(pci_whitelist.Whitelist, 'get_devspec')
|
||||||
|
@mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock())
|
||||||
|
def test_update_port_bindings_for_instance_with_sriov_pf_no_migration(
|
||||||
|
self, get_client_mock, get_pci_device_devspec_mock
|
||||||
|
):
|
||||||
|
devspec = mock.Mock()
|
||||||
|
devspec.get_tags.return_value = {'physical_network': 'physnet1'}
|
||||||
|
get_pci_device_devspec_mock.return_value = devspec
|
||||||
|
|
||||||
|
instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
instance.pci_requests = objects.InstancePCIRequests(
|
||||||
|
instance_uuid=instance.uuid,
|
||||||
|
requests=[
|
||||||
|
objects.InstancePCIRequest(
|
||||||
|
requester_id=uuids.port,
|
||||||
|
request_id=uuids.pci_req,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
instance.pci_devices = objects.PciDeviceList(
|
||||||
|
objects=[
|
||||||
|
objects.PciDevice(
|
||||||
|
vendor_id='8086',
|
||||||
|
product_id='154d',
|
||||||
|
address='0000:0a:02',
|
||||||
|
compute_node_id=2,
|
||||||
|
request_id=uuids.pci_req,
|
||||||
|
dev_type=obj_fields.PciDeviceType.SRIOV_PF,
|
||||||
|
extra_info={'mac_address': 'b4:96:91:34:f4:36'},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_ports = {
|
||||||
|
'ports': [
|
||||||
|
{
|
||||||
|
'id': uuids.port,
|
||||||
|
'binding:vnic_type': 'direct-physical',
|
||||||
|
constants.BINDING_HOST_ID: 'fake-host-old',
|
||||||
|
constants.BINDING_PROFILE: {
|
||||||
|
'pci_slot': '0000:0a:01',
|
||||||
|
'physical_network': 'old_phys_net',
|
||||||
|
'pci_vendor_info': 'old_pci_vendor_info',
|
||||||
|
'device_mac_address': 'b4:96:91:34:f4:dd'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
list_ports_mock = mock.Mock(return_value=fake_ports)
|
||||||
|
get_client_mock.return_value.list_ports = list_ports_mock
|
||||||
|
|
||||||
|
update_port_mock = mock.Mock()
|
||||||
|
get_client_mock.return_value.update_port = update_port_mock
|
||||||
|
|
||||||
|
self.api._update_port_binding_for_instance(
|
||||||
|
self.context, instance, instance.host)
|
||||||
|
|
||||||
|
# Assert that update_port is called with the binding:profile
|
||||||
|
# corresponding to the PCI device specified including MAC address.
|
||||||
|
update_port_mock.assert_called_once_with(
|
||||||
|
uuids.port,
|
||||||
|
{
|
||||||
|
'port': {
|
||||||
|
constants.BINDING_HOST_ID: 'fake-host',
|
||||||
|
'device_owner': 'compute:%s' % instance.availability_zone,
|
||||||
|
constants.BINDING_PROFILE: {
|
||||||
|
'pci_slot': '0000:0a:02',
|
||||||
|
'physical_network': 'physnet1',
|
||||||
|
'pci_vendor_info': '8086:154d',
|
||||||
|
'device_mac_address': 'b4:96:91:34:f4:36',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'nova.network.neutron.API.has_extended_resource_request_extension',
|
'nova.network.neutron.API.has_extended_resource_request_extension',
|
||||||
new=mock.Mock(return_value=False),
|
new=mock.Mock(return_value=False),
|
||||||
|
@ -7190,23 +7358,21 @@ class TestAPI(TestAPIBase):
|
||||||
request_id=uuids.pci_request_id)
|
request_id=uuids.pci_request_id)
|
||||||
bad_request = objects.InstancePCIRequest(
|
bad_request = objects.InstancePCIRequest(
|
||||||
requester_id=uuids.wrong_port_id)
|
requester_id=uuids.wrong_port_id)
|
||||||
device = objects.PciDevice(request_id=uuids.pci_request_id,
|
device = objects.PciDevice(request_id=uuids.pci_request_id)
|
||||||
address='fake-pci-address')
|
|
||||||
bad_device = objects.PciDevice(request_id=uuids.wrong_request_id)
|
bad_device = objects.PciDevice(request_id=uuids.wrong_request_id)
|
||||||
# Test the happy path
|
# Test the happy path
|
||||||
instance = objects.Instance(
|
instance = objects.Instance(
|
||||||
pci_requests=objects.InstancePCIRequests(requests=[request]),
|
pci_requests=objects.InstancePCIRequests(requests=[request]),
|
||||||
pci_devices=objects.PciDeviceList(objects=[device]))
|
pci_devices=objects.PciDeviceList(objects=[device]))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'fake-pci-address',
|
device,
|
||||||
self.api._get_port_pci_dev(
|
self.api._get_port_pci_dev(instance, fake_port))
|
||||||
self.context, instance, fake_port).address)
|
|
||||||
# Test not finding the request
|
# Test not finding the request
|
||||||
instance = objects.Instance(
|
instance = objects.Instance(
|
||||||
pci_requests=objects.InstancePCIRequests(
|
pci_requests=objects.InstancePCIRequests(
|
||||||
requests=[objects.InstancePCIRequest(bad_request)]))
|
requests=[objects.InstancePCIRequest(bad_request)]))
|
||||||
self.assertIsNone(
|
self.assertIsNone(
|
||||||
self.api._get_port_pci_dev(self.context, instance, fake_port))
|
self.api._get_port_pci_dev(instance, fake_port))
|
||||||
mock_debug.assert_called_with('No PCI request found for port %s',
|
mock_debug.assert_called_with('No PCI request found for port %s',
|
||||||
uuids.fake_port_id, instance=instance)
|
uuids.fake_port_id, instance=instance)
|
||||||
mock_debug.reset_mock()
|
mock_debug.reset_mock()
|
||||||
|
@ -7215,7 +7381,7 @@ class TestAPI(TestAPIBase):
|
||||||
pci_requests=objects.InstancePCIRequests(requests=[request]),
|
pci_requests=objects.InstancePCIRequests(requests=[request]),
|
||||||
pci_devices=objects.PciDeviceList(objects=[bad_device]))
|
pci_devices=objects.PciDeviceList(objects=[bad_device]))
|
||||||
self.assertIsNone(
|
self.assertIsNone(
|
||||||
self.api._get_port_pci_dev(self.context, instance, fake_port))
|
self.api._get_port_pci_dev(instance, fake_port))
|
||||||
mock_debug.assert_called_with('No PCI device found for request %s',
|
mock_debug.assert_called_with('No PCI device found for request %s',
|
||||||
uuids.pci_request_id, instance=instance)
|
uuids.pci_request_id, instance=instance)
|
||||||
|
|
||||||
|
@ -7740,6 +7906,45 @@ class TestAPIPortbinding(TestAPIBase):
|
||||||
port_req_body['port'][
|
port_req_body['port'][
|
||||||
constants.BINDING_PROFILE])
|
constants.BINDING_PROFILE])
|
||||||
|
|
||||||
|
@mock.patch.object(pci_whitelist.Whitelist, 'get_devspec')
|
||||||
|
@mock.patch.object(pci_manager, 'get_instance_pci_devs')
|
||||||
|
def test_populate_neutron_extension_values_binding_sriov_pf(
|
||||||
|
self, mock_get_instance_pci_devs, mock_get_devspec
|
||||||
|
):
|
||||||
|
host_id = 'my_host_id'
|
||||||
|
instance = {'host': host_id}
|
||||||
|
port_req_body = {'port': {}}
|
||||||
|
|
||||||
|
pci_dev = objects.PciDevice(
|
||||||
|
request_id=uuids.pci_req,
|
||||||
|
address='0000:01:00',
|
||||||
|
parent_addr='0000:02:00',
|
||||||
|
vendor_id='8086',
|
||||||
|
product_id='154d',
|
||||||
|
dev_type=obj_fields.PciDeviceType.SRIOV_PF,
|
||||||
|
extra_info={'mac_address': 'b4:96:91:34:f4:36'}
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_profile = {
|
||||||
|
'pci_vendor_info': '8086:154d',
|
||||||
|
'pci_slot': '0000:01:00',
|
||||||
|
'physical_network': 'physnet1',
|
||||||
|
'device_mac_address': 'b4:96:91:34:f4:36',
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_get_instance_pci_devs.return_value = [pci_dev]
|
||||||
|
devspec = mock.Mock()
|
||||||
|
devspec.get_tags.return_value = {'physical_network': 'physnet1'}
|
||||||
|
mock_get_devspec.return_value = devspec
|
||||||
|
|
||||||
|
self.api._populate_neutron_binding_profile(
|
||||||
|
instance, uuids.pci_req, port_req_body, None)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
expected_profile,
|
||||||
|
port_req_body['port'][constants.BINDING_PROFILE]
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
pci_utils, 'get_vf_num_by_pci_address',
|
pci_utils, 'get_vf_num_by_pci_address',
|
||||||
new=mock.MagicMock(side_effect=(lambda vf_a: 1
|
new=mock.MagicMock(side_effect=(lambda vf_a: 1
|
||||||
|
@ -7811,21 +8016,29 @@ class TestAPIPortbinding(TestAPIBase):
|
||||||
devspec.get_tags.return_value = {'physical_network': 'physnet1'}
|
devspec.get_tags.return_value = {'physical_network': 'physnet1'}
|
||||||
mock_get_pci_device_devspec.return_value = devspec
|
mock_get_pci_device_devspec.return_value = devspec
|
||||||
|
|
||||||
pci_dev = {'vendor_id': 'a2d6',
|
pci_dev = objects.PciDevice(
|
||||||
'product_id': '15b3',
|
request_id=uuids.pci_req,
|
||||||
'address': '0000:0a:00.0',
|
address='0000:0a:00.0',
|
||||||
'card_serial_number': 'MT2113X00000',
|
parent_addr='0000:02:00',
|
||||||
'dev_type': obj_fields.PciDeviceType.SRIOV_PF,
|
vendor_id='a2d6',
|
||||||
}
|
product_id='15b3',
|
||||||
PciDevice = collections.namedtuple('PciDevice',
|
dev_type=obj_fields.PciDeviceType.SRIOV_PF,
|
||||||
['vendor_id', 'product_id', 'address',
|
extra_info={
|
||||||
'card_serial_number', 'dev_type'])
|
'capabilities': jsonutils.dumps(
|
||||||
mydev = PciDevice(**pci_dev)
|
{'card_serial_number': 'MT2113X00000'}),
|
||||||
|
'mac_address': 'b4:96:91:34:f4:36',
|
||||||
|
},
|
||||||
|
|
||||||
self.assertEqual({'pci_slot': '0000:0a:00.0',
|
)
|
||||||
'pci_vendor_info': 'a2d6:15b3',
|
self.assertEqual(
|
||||||
'physical_network': 'physnet1'},
|
{
|
||||||
self.api._get_pci_device_profile(mydev))
|
'pci_slot': '0000:0a:00.0',
|
||||||
|
'pci_vendor_info': 'a2d6:15b3',
|
||||||
|
'physical_network': 'physnet1',
|
||||||
|
'device_mac_address': 'b4:96:91:34:f4:36',
|
||||||
|
},
|
||||||
|
self.api._get_pci_device_profile(pci_dev),
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(pci_whitelist.Whitelist, 'get_devspec')
|
@mock.patch.object(pci_whitelist.Whitelist, 'get_devspec')
|
||||||
@mock.patch.object(pci_manager, 'get_instance_pci_devs')
|
@mock.patch.object(pci_manager, 'get_instance_pci_devs')
|
||||||
|
|
|
@ -17669,7 +17669,10 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||||
"vendor_id": '8086',
|
"vendor_id": '8086',
|
||||||
"dev_type": fields.PciDeviceType.SRIOV_PF,
|
"dev_type": fields.PciDeviceType.SRIOV_PF,
|
||||||
"phys_function": None,
|
"phys_function": None,
|
||||||
"numa_node": None},
|
"numa_node": None,
|
||||||
|
# value defined in the LibvirtFixture
|
||||||
|
"mac_address": "52:54:00:1e:59:c6",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dev_id": "pci_0000_04_10_7",
|
"dev_id": "pci_0000_04_10_7",
|
||||||
"domain": 0,
|
"domain": 0,
|
||||||
|
|
|
@ -1161,9 +1161,9 @@ Active: 8381604 kB
|
||||||
dev for dev in node_devs.values() if dev.name() in devs]
|
dev for dev in node_devs.values() if dev.name() in devs]
|
||||||
|
|
||||||
name = "pci_0000_04_00_3"
|
name = "pci_0000_04_00_3"
|
||||||
actual_vf = self.host._get_pcidev_info(
|
actual_pf = self.host._get_pcidev_info(
|
||||||
name, node_devs[name], net_devs, [], [])
|
name, node_devs[name], net_devs, [], [])
|
||||||
expect_vf = {
|
expect_pf = {
|
||||||
"dev_id": "pci_0000_04_00_3",
|
"dev_id": "pci_0000_04_00_3",
|
||||||
"address": "0000:04:00.3",
|
"address": "0000:04:00.3",
|
||||||
"product_id": '1521',
|
"product_id": '1521',
|
||||||
|
@ -1171,8 +1171,10 @@ Active: 8381604 kB
|
||||||
"vendor_id": '8086',
|
"vendor_id": '8086',
|
||||||
"label": 'label_8086_1521',
|
"label": 'label_8086_1521',
|
||||||
"dev_type": obj_fields.PciDeviceType.SRIOV_PF,
|
"dev_type": obj_fields.PciDeviceType.SRIOV_PF,
|
||||||
|
# value defined in the LibvirtFixture
|
||||||
|
"mac_address": "52:54:00:1e:59:c6",
|
||||||
}
|
}
|
||||||
self.assertEqual(expect_vf, actual_vf)
|
self.assertEqual(expect_pf, actual_pf)
|
||||||
|
|
||||||
name = "pci_0000_04_10_7"
|
name = "pci_0000_04_10_7"
|
||||||
actual_vf = self.host._get_pcidev_info(
|
actual_vf = self.host._get_pcidev_info(
|
||||||
|
@ -1231,9 +1233,9 @@ Active: 8381604 kB
|
||||||
self.assertEqual(expect_vf, actual_vf)
|
self.assertEqual(expect_vf, actual_vf)
|
||||||
|
|
||||||
name = "pci_0000_03_00_0"
|
name = "pci_0000_03_00_0"
|
||||||
actual_vf = self.host._get_pcidev_info(
|
actual_pf = self.host._get_pcidev_info(
|
||||||
name, node_devs[name], net_devs, [], [])
|
name, node_devs[name], net_devs, [], [])
|
||||||
expect_vf = {
|
expect_pf = {
|
||||||
"dev_id": "pci_0000_03_00_0",
|
"dev_id": "pci_0000_03_00_0",
|
||||||
"address": "0000:03:00.0",
|
"address": "0000:03:00.0",
|
||||||
"product_id": '1013',
|
"product_id": '1013',
|
||||||
|
@ -1241,13 +1243,15 @@ Active: 8381604 kB
|
||||||
"vendor_id": '15b3',
|
"vendor_id": '15b3',
|
||||||
"label": 'label_15b3_1013',
|
"label": 'label_15b3_1013',
|
||||||
"dev_type": obj_fields.PciDeviceType.SRIOV_PF,
|
"dev_type": obj_fields.PciDeviceType.SRIOV_PF,
|
||||||
|
# value defined in the LibvirtFixture
|
||||||
|
"mac_address": "52:54:00:1e:59:c6",
|
||||||
}
|
}
|
||||||
self.assertEqual(expect_vf, actual_vf)
|
self.assertEqual(expect_pf, actual_pf)
|
||||||
|
|
||||||
name = "pci_0000_03_00_1"
|
name = "pci_0000_03_00_1"
|
||||||
actual_vf = self.host._get_pcidev_info(
|
actual_pf = self.host._get_pcidev_info(
|
||||||
name, node_devs[name], net_devs, [], [])
|
name, node_devs[name], net_devs, [], [])
|
||||||
expect_vf = {
|
expect_pf = {
|
||||||
"dev_id": "pci_0000_03_00_1",
|
"dev_id": "pci_0000_03_00_1",
|
||||||
"address": "0000:03:00.1",
|
"address": "0000:03:00.1",
|
||||||
"product_id": '1013',
|
"product_id": '1013',
|
||||||
|
@ -1255,8 +1259,10 @@ Active: 8381604 kB
|
||||||
"vendor_id": '15b3',
|
"vendor_id": '15b3',
|
||||||
"label": 'label_15b3_1013',
|
"label": 'label_15b3_1013',
|
||||||
"dev_type": obj_fields.PciDeviceType.SRIOV_PF,
|
"dev_type": obj_fields.PciDeviceType.SRIOV_PF,
|
||||||
|
# value defined in the LibvirtFixture
|
||||||
|
"mac_address": "52:54:00:1e:59:c6",
|
||||||
}
|
}
|
||||||
self.assertEqual(expect_vf, actual_vf)
|
self.assertEqual(expect_pf, actual_pf)
|
||||||
|
|
||||||
# Parent PF with a VPD cap.
|
# Parent PF with a VPD cap.
|
||||||
name = "pci_0000_82_00_0"
|
name = "pci_0000_82_00_0"
|
||||||
|
@ -1273,6 +1279,8 @@ Active: 8381604 kB
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
# Should be obtained from the parent PF in this case.
|
# Should be obtained from the parent PF in this case.
|
||||||
"vpd": {"card_serial_number": "MT2113X00000"}},
|
"vpd": {"card_serial_number": "MT2113X00000"}},
|
||||||
|
# value defined in the LibvirtFixture
|
||||||
|
"mac_address": "52:54:00:1e:59:c6",
|
||||||
}
|
}
|
||||||
self.assertEqual(expect_pf, actual_pf)
|
self.assertEqual(expect_pf, actual_pf)
|
||||||
|
|
||||||
|
|
|
@ -891,6 +891,36 @@ class FakeLiveMigrateDriverWithNestedCustomResources(
|
||||||
|
|
||||||
|
|
||||||
class FakeDriverWithPciResources(SmallFakeDriver):
|
class FakeDriverWithPciResources(SmallFakeDriver):
|
||||||
|
"""NOTE: this driver provides symmetric compute nodes. Each compute will
|
||||||
|
have the same resources with the same addresses. It is dangerous as using
|
||||||
|
this driver can hide issues when in an asymmetric environment nova fails to
|
||||||
|
update entities according to the host specific addresses (e.g. pci_slot of
|
||||||
|
the neutron port bindings).
|
||||||
|
|
||||||
|
The current non virt driver specific functional test environment has many
|
||||||
|
shortcomings making it really hard to simulate host specific virt drivers.
|
||||||
|
|
||||||
|
1) The virt driver is instantiated by the service logic from the name of
|
||||||
|
the driver class. This makes passing input to the driver instance from the
|
||||||
|
test at init time pretty impossible. This could be solved with some
|
||||||
|
fixtures around nova.virt.driver.load_compute_driver()
|
||||||
|
|
||||||
|
2) The compute service access the hypervisor not only via the virt
|
||||||
|
interface but also reads the sysfs of the host. So simply providing a fake
|
||||||
|
virt driver instance is not enough to isolate simulated compute services
|
||||||
|
that are running on the same host. Also these low level sysfs reads are not
|
||||||
|
having host specific information in the call params. So simply mocking the
|
||||||
|
low level call does not give a way to provide host specific return values.
|
||||||
|
|
||||||
|
3) CONF is global, and it is read dynamically by the driver. So
|
||||||
|
providing host specific CONF to driver instances without race conditions
|
||||||
|
between the drivers are extremely hard especially if periodic tasks are
|
||||||
|
enabled.
|
||||||
|
|
||||||
|
The libvirt based functional test env under nova.tests.functional.libvirt
|
||||||
|
has better support to create asymmetric environments. So please consider
|
||||||
|
using that if possible instead.
|
||||||
|
"""
|
||||||
|
|
||||||
PCI_ADDR_PF1 = '0000:01:00.0'
|
PCI_ADDR_PF1 = '0000:01:00.0'
|
||||||
PCI_ADDR_PF1_VF1 = '0000:01:00.1'
|
PCI_ADDR_PF1_VF1 = '0000:01:00.1'
|
||||||
|
@ -955,6 +985,11 @@ class FakeDriverWithPciResources(SmallFakeDriver):
|
||||||
],
|
],
|
||||||
group='pci')
|
group='pci')
|
||||||
|
|
||||||
|
# These mocks should be removed after bug
|
||||||
|
# https://bugs.launchpad.net/nova/+bug/1961587 has been fixed and
|
||||||
|
# every SRIOV device related information is transferred through the
|
||||||
|
# virt driver and the PciDevice object instead of queried with
|
||||||
|
# sysfs calls by the network.neutron.API code.
|
||||||
self.useFixture(fixtures.MockPatch(
|
self.useFixture(fixtures.MockPatch(
|
||||||
'nova.pci.utils.get_mac_by_pci_address',
|
'nova.pci.utils.get_mac_by_pci_address',
|
||||||
return_value='52:54:00:1e:59:c6'))
|
return_value='52:54:00:1e:59:c6'))
|
||||||
|
|
|
@ -1268,6 +1268,20 @@ class Host(object):
|
||||||
return None
|
return None
|
||||||
return vpd_cap.card_serial_number
|
return vpd_cap.card_serial_number
|
||||||
|
|
||||||
|
def _get_pf_details(self, device: dict, pci_address: str) -> dict:
|
||||||
|
if device.get('dev_type') != fields.PciDeviceType.SRIOV_PF:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
return {
|
||||||
|
'mac_address': pci_utils.get_mac_by_pci_address(pci_address)
|
||||||
|
}
|
||||||
|
except exception.PciDeviceNotFoundById:
|
||||||
|
LOG.debug(
|
||||||
|
'Cannot get MAC address of the PF %s. It is probably attached '
|
||||||
|
'to a guest already', pci_address)
|
||||||
|
return {}
|
||||||
|
|
||||||
def _get_pcidev_info(
|
def _get_pcidev_info(
|
||||||
self,
|
self,
|
||||||
devname: str,
|
devname: str,
|
||||||
|
@ -1470,6 +1484,7 @@ class Host(object):
|
||||||
_get_device_type(cfgdev, address, dev, net_devs, vdpa_devs))
|
_get_device_type(cfgdev, address, dev, net_devs, vdpa_devs))
|
||||||
device.update(_get_device_capabilities(device, dev,
|
device.update(_get_device_capabilities(device, dev,
|
||||||
pci_devs, net_devs))
|
pci_devs, net_devs))
|
||||||
|
device.update(self._get_pf_details(device, address))
|
||||||
return device
|
return device
|
||||||
|
|
||||||
def get_vdpa_nodedev_by_address(
|
def get_vdpa_nodedev_by_address(
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
As a fix for `bug 1942329 <https://bugs.launchpad.net/neutron/+bug/1942329>`_
|
||||||
|
nova now updates the MAC address of the ``direct-physical`` ports during
|
||||||
|
mova operations to reflect the MAC address of the physical device on the
|
||||||
|
destination host. Those servers that were created before this fix need to be
|
||||||
|
moved or the port needs to be detached and the re-attached to synchronize the
|
||||||
|
MAC address.
|
Loading…
Reference in New Issue