Browse Source

Explicitly fail if trying to attach SR-IOV port

Attaching SR-IOV ports to existing instances is not supported
since the compute service does not perform any kind of PCI
device allocation, so we should fail fast with a clear error
if attempted. Note that the compute RPC API "attach_interface"
method is an RPC call from nova-api to nova-compute so the error
raised here will result in a 400 response to the user.

Blueprint sriov-interface-attach-detach would need to be
implemented to support this use case, and could arguably involve
a microversion to indicate when the feature was made available.

A related neutron docs patch https://review.openstack.org/695409
is posted for mentioning the limitation with SR-IOV port attach
as well.

Conflicts due to no having
Ifcc327f9f97e57d3d6f0db7045b56ffe60203eb9 and
I4440a19370da9807cc8c32b681542c7048c9977e in Pike.

Change-Id: Ibbf2bd3cdd45bcd61eebff883c30ded525b2495d
Closes-Bug: #1708433
(cherry picked from commit 68011c40ae)
(cherry picked from commit e1d55af408)
(cherry picked from commit 7827890421)
changes/08/695408/2
Matt Riedemann 3 years ago
committed by Elod Illes
parent
commit
af5df70f61
  1. 6
      doc/source/admin/pci-passthrough.rst
  2. 6
      nova/exception.py
  3. 28
      nova/network/neutronv2/api.py
  4. 12
      nova/tests/unit/network/test_neutronv2.py

6
doc/source/admin/pci-passthrough.rst

@ -17,8 +17,10 @@ assigned to only one guest and cannot be shared.
.. note::
For information on attaching virtual SR-IOV devices to guests, refer to the
`Networking Guide`_.
For information on creating servers with virtual SR-IOV devices to guests,
refer to the `Networking Guide`_. Attaching SR-IOV ports to existing servers
is not currently supported, see
`bug 1708433 <https://bugs.launchpad.net/nova/+bug/1708433>`_ for details.
To enable PCI passthrough, follow the steps below:

6
nova/exception.py

@ -837,6 +837,12 @@ class PortUpdateFailed(Invalid):
msg_fmt = _("Port update failed for port %(port_id)s: %(reason)s")
class AttachSRIOVPortNotSupported(Invalid):
msg_fmt = _('Attaching SR-IOV port %(port_id)s to server '
'%(instance_uuid)s is not supported. SR-IOV ports must be '
'specified during server creation.')
class FixedIpExists(NovaException):
msg_fmt = _("Fixed IP %(address)s already exists.")

28
nova/network/neutronv2/api.py

@ -524,7 +524,7 @@ class API(base_api.NetworkAPI):
"for port '%s'"), port_id)
def _validate_requested_port_ids(self, context, instance, neutron,
requested_networks):
requested_networks, attach=False):
"""Processes and validates requested networks for allocation.
Iterates over the list of NetworkRequest objects, validating the
@ -535,6 +535,11 @@ class API(base_api.NetworkAPI):
:type instance: nova.objects.Instance
:param neutron: neutron client session
:type neutron: neutronclient.v2_0.client.Client
:param requested_networks: List of user-requested networks and/or ports
:type requested_networks: nova.objects.NetworkRequestList
:param attach: Boolean indicating if a port is being attached to an
existing running instance. Should be False during server create.
:type attach: bool
:returns: tuple of:
- ports: dict mapping of port id to port dict
- ordered_networks: list of nova.objects.NetworkRequest objects
@ -548,6 +553,8 @@ class API(base_api.NetworkAPI):
attached to another instance.
:raises nova.exception.PortNotUsableDNS: If a requested port has a
value assigned to its dns_name attribute.
:raises nova.exception.AttachSRIOVPortNotSupported: If a requested port
is an SR-IOV port and ``attach=True``.
"""
ports = {}
ordered_networks = []
@ -585,6 +592,16 @@ class API(base_api.NetworkAPI):
# Make sure the port is usable
_ensure_no_port_binding_failure(port)
# Make sure the port can be attached.
if attach:
# SR-IOV port attach is not supported.
vnic_type = port.get('binding:vnic_type',
network_model.VNIC_TYPE_NORMAL)
if vnic_type in network_model.VNIC_TYPES_SRIOV:
raise exception.AttachSRIOVPortNotSupported(
port_id=port['id'],
instance_uuid=instance.uuid)
# If requesting a specific port, automatically process
# the network for that port as if it were explicitly
# requested.
@ -814,7 +831,8 @@ class API(base_api.NetworkAPI):
def allocate_for_instance(self, context, instance, vpn,
requested_networks, macs=None,
security_groups=None,
dhcp_options=None, bind_host_id=None):
dhcp_options=None, bind_host_id=None,
attach=False):
"""Allocate network resources for the instance.
:param context: The request context.
@ -837,6 +855,8 @@ class API(base_api.NetworkAPI):
are already formatted for the neutron v2 api.
See nova/virt/driver.py:dhcp_options_for_instance for an example.
:param bind_host_id: the host ID to attach to the ports being created.
:param attach: Boolean indicating if a port is being attached to an
existing running instance. Should be False during server create.
:returns: network info as from get_instance_nw_info()
"""
LOG.debug('allocate_for_instance()', instance=instance)
@ -852,7 +872,7 @@ class API(base_api.NetworkAPI):
# Validate ports and networks with neutron
#
ports, ordered_networks = self._validate_requested_port_ids(
context, instance, neutron, requested_networks)
context, instance, neutron, requested_networks, attach=attach)
nets = self._validate_requested_network_ids(
context, instance, neutron, requested_networks, ordered_networks)
@ -1242,7 +1262,7 @@ class API(base_api.NetworkAPI):
tag=tag)])
return self.allocate_for_instance(context, instance, vpn=False,
requested_networks=requested_networks,
bind_host_id=bind_host_id)
bind_host_id=bind_host_id, attach=True)
def deallocate_port_for_instance(self, context, instance, port_id):
"""Remove a specified port from the instance.

12
nova/tests/unit/network/test_neutronv2.py

@ -5460,7 +5460,8 @@ class TestAllocateForInstance(test.NoDBTestCase):
self.assertEqual(requested_networks[0], ordered_networks[0])
self.assertEqual('net-2', ordered_networks[1].network_id)
def _assert_validate_requested_port_ids_raises(self, exception, extras):
def _assert_validate_requested_port_ids_raises(self, exception, extras,
attach=False):
api = neutronapi.API()
mock_client = mock.Mock()
requested_networks = objects.NetworkRequestList(objects=[
@ -5474,7 +5475,8 @@ class TestAllocateForInstance(test.NoDBTestCase):
mock_client.show_port.return_value = {"port": port}
self.assertRaises(exception, api._validate_requested_port_ids,
self.context, self.instance, mock_client, requested_networks)
self.context, self.instance, mock_client, requested_networks,
attach=attach)
def test_validate_requested_port_ids_raise_not_usable(self):
self._assert_validate_requested_port_ids_raises(
@ -5496,6 +5498,12 @@ class TestAllocateForInstance(test.NoDBTestCase):
exception.PortBindingFailed,
{"binding:vif_type": model.VIF_TYPE_BINDING_FAILED})
def test_validate_requested_port_ids_raise_sriov(self):
self._assert_validate_requested_port_ids_raises(
exception.AttachSRIOVPortNotSupported,
{"binding:vnic_type": model.VNIC_TYPE_DIRECT},
attach=True)
def test_validate_requested_network_ids_success_auto_net(self):
requested_networks = []
ordered_networks = []

Loading…
Cancel
Save