summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api-guide/source/port_with_resource_request.rst9
-rw-r--r--doc/source/admin/ports-with-resource-requests.rst13
-rw-r--r--nova/api/openstack/compute/servers.py2
-rw-r--r--nova/compute/api.py16
-rw-r--r--nova/exception.py6
-rw-r--r--nova/network/neutron.py75
-rw-r--r--nova/objects/service.py5
-rw-r--r--nova/tests/functional/test_servers_resource_request.py68
-rw-r--r--nova/tests/unit/compute/test_api.py2
-rw-r--r--nova/tests/unit/compute/test_compute.py26
-rw-r--r--nova/tests/unit/network/test_neutron.py262
11 files changed, 338 insertions, 146 deletions
diff --git a/api-guide/source/port_with_resource_request.rst b/api-guide/source/port_with_resource_request.rst
index 868ec33a97..0109ec799b 100644
--- a/api-guide/source/port_with_resource_request.rst
+++ b/api-guide/source/port_with_resource_request.rst
@@ -44,10 +44,15 @@ Extended resource request
Since neutron 19.0.0 (Xena), neutron implements an extended resource request
format via the the ``port-resource-request-groups`` neutron API extension. As
-of nova 24.0.0 (Xena), nova does not support the new extension. If the
-extension is enabled in neutron, then nova will reject server create and move
+of nova 24.0.0 (Xena), nova does not fully support the new extension. If the
+extension is enabled in neutron, then nova will reject server move
operations, as well as interface attach operation. Admins should not enable
this API extension in neutron.
+Please note that Nova only supports the server create operation if every
+nova-compute service also upgraded to Xena version and the
+``[upgrade_levels]/compute`` configuration does not prevent
+the computes from using the latest RPC version.
+
See :nova-doc:`the admin guide <admin/port_with_resource_request.html>` for
administrative details.
diff --git a/doc/source/admin/ports-with-resource-requests.rst b/doc/source/admin/ports-with-resource-requests.rst
index 121e52b17c..3765b09afe 100644
--- a/doc/source/admin/ports-with-resource-requests.rst
+++ b/doc/source/admin/ports-with-resource-requests.rst
@@ -71,10 +71,15 @@ Extended resource request
Since neutron 19.0.0 (Xena), neutron implements an extended resource request
format via the the ``port-resource-request-groups`` neutron API extension. As
-of nova 24.0.0 (Xena), Nova does not support the new extension. If the
-extension is enabled in neutron, then nova will reject server create and move
-operations, as well as interface attach operation. Admins should not enable
-this API extension in neutron.
+of nova 24.0.0 (Xena), Nova does not fully support the new extension. If the
+extension is enabled in neutron, then nova will reject server move operations,
+as well as interface attach operation. Admins should not enable this API
+extension in neutron.
+
+Please note that Nova only supports the server create operation if every
+nova-compute service also upgraded to Xena version and the
+:oslo.config:option:`upgrade_levels.compute` configuration does not prevent
+the computes from using the latest RPC version.
The extended resource request allows a single Neutron port to request
resources in more than one request groups. This also means that using just one
diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py
index b4d4cc3ed1..a297d11e57 100644
--- a/nova/api/openstack/compute/servers.py
+++ b/nova/api/openstack/compute/servers.py
@@ -844,7 +844,7 @@ class ServersController(wsgi.Controller):
exception.DeviceProfileError,
exception.ComputeHostNotFound,
exception.ForbiddenPortsWithAccelerator,
- exception.ExtendedResourceRequestNotSupported,
+ exception.ExtendedResourceRequestOldCompute,
) as error:
raise exc.HTTPBadRequest(explanation=error.format_message())
except INVALID_FLAVOR_IMAGE_EXCEPTIONS as error:
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 292284036f..db3e670d41 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -110,6 +110,8 @@ SUPPORT_ACCELERATOR_SERVICE_FOR_REBUILD = 53
SUPPORT_VNIC_TYPE_ACCELERATOR = 57
+MIN_COMPUTE_BOOT_WITH_EXTENDED_RESOURCE_REQUEST = 58
+
# FIXME(danms): Keep a global cache of the cells we find the
# first time we look. This needs to be refreshed on a timer or
# trigger.
@@ -1073,6 +1075,18 @@ class API:
if port_resource_requests and not supports_port_resource_request:
raise exception.CreateWithPortResourceRequestOldVersion()
+ # TODO(gibi): remove this when Nova does not need to support Wallaby
+ # computes any more.
+ if (port_resource_requests and
+ self.network_api.has_extended_resource_request_extension(context)
+ ):
+ # we only support the extended resource request if the computes are
+ # upgraded to Xena.
+ min_version = objects.service.get_minimum_version_all_cells(
+ context, ["nova-compute"])
+ if min_version < MIN_COMPUTE_BOOT_WITH_EXTENDED_RESOURCE_REQUEST:
+ raise exception.ExtendedResourceRequestOldCompute()
+
base_options = {
'reservation_id': reservation_id,
'image_ref': image_href,
@@ -5062,7 +5076,7 @@ class API:
This function is only here temporary to help mocking this check in the
functional test environment.
"""
- if not self.network_api._has_extended_resource_request_extension(
+ if not self.network_api.has_extended_resource_request_extension(
context
):
return True
diff --git a/nova/exception.py b/nova/exception.py
index 6573c8e965..f70edc253d 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -1962,10 +1962,10 @@ class CreateWithPortResourceRequestOldVersion(Invalid):
"until microversion 2.72.")
-class ExtendedResourceRequestNotSupported(Invalid):
+class ExtendedResourceRequestOldCompute(Invalid):
msg_fmt = _("The port-resource-request-groups neutron API extension is "
- "not yet supported by Nova. Please turn off this extension in "
- "Neutron.")
+ "not supported by old nova compute service. Upgrade your "
+ "compute services to Xena (24.0.0) or later.")
class InvalidReservedMemoryPagesOption(Invalid):
diff --git a/nova/network/neutron.py b/nova/network/neutron.py
index 88cd4a74e1..de512e8a51 100644
--- a/nova/network/neutron.py
+++ b/nova/network/neutron.py
@@ -470,10 +470,11 @@ class API:
'error.', {'port_id': port_id},
instance=instance)
- def _create_port_minimal(self, port_client, instance, network_id,
+ def _create_port_minimal(self, context, port_client, instance, network_id,
fixed_ip=None, security_group_ids=None):
"""Attempts to create a port for the instance on the given network.
+ :param context: The request context.
:param port_client: The client to use to create the port.
:param instance: Create the port for the given instance.
:param network_id: Create the port on the given network.
@@ -510,7 +511,7 @@ class API:
# such ports are currently not supported as they would at least
# need resource allocation manipulation in placement but might also
# need a new scheduling if resource on this host is not available.
- if port.get(constants.RESOURCE_REQUEST, None):
+ if self._has_resource_request(context, port, port_client):
msg = (
"The auto-created port %(port_id)s is being deleted due "
"to its network having QoS policy.")
@@ -979,8 +980,8 @@ class API:
if not request.port_id:
# create minimal port, if port not already created by user
created_port = self._create_port_minimal(
- neutron, instance, request.network_id,
- request.address, security_group_ids)
+ context, neutron, instance, request.network_id,
+ request.address, security_group_ids)
created_port_id = created_port['id']
created_port_ids.append(created_port_id)
@@ -997,7 +998,7 @@ class API:
def _has_resource_request(self, context, port, neutron):
resource_request = port.get(constants.RESOURCE_REQUEST) or {}
- if self._has_extended_resource_request_extension(context, neutron):
+ if self.has_extended_resource_request_extension(context, neutron):
return bool(resource_request.get(constants.REQUEST_GROUPS, []))
else:
return bool(resource_request)
@@ -1008,7 +1009,7 @@ class API:
# if we query with a non admin context.
admin_context = nova_context.get_admin_context()
- if not self._has_extended_resource_request_extension(admin_context):
+ if not self.has_extended_resource_request_extension(admin_context):
# Short circuit if the extended resource request API extension is
# not available
return False
@@ -1026,6 +1027,44 @@ class API:
return True
return False
+ def _get_binding_profile_allocation(
+ self, context, port, neutron, resource_provider_mapping
+ ):
+ # TODO(gibi): remove this condition and the else branch once Nova does
+ # not need to support old Neutron sending the legacy resource request
+ # extension
+ if self.has_extended_resource_request_extension(
+ context, neutron
+ ):
+ # The extended resource request format also means that a
+ # port has more than a one request groups
+ request_groups = port.get(
+ constants.RESOURCE_REQUEST, {}).get(
+ constants.REQUEST_GROUPS, [])
+ # Each request group id from the port needs to be mapped to
+ # a single provider id from the provider mappings. Each
+ # group from the port is mapped to a numbered request group
+ # in placement so we can assume that they are mapped to
+ # a single provider and therefore the provider mapping list
+ # has a single provider id.
+ allocation = {
+ group['id']: resource_provider_mapping[group['id']][0]
+ for group in request_groups
+ }
+ else:
+ # This is the legacy resource request format where a port
+ # is mapped to a single request group
+ # NOTE(gibi): In the resource provider mapping there can be
+ # more than one RP fulfilling a request group. But resource
+ # requests of a Neutron port is always mapped to a
+ # numbered request group that is always fulfilled by one
+ # resource provider. So we only pass that single RP UUID
+ # here.
+ allocation = resource_provider_mapping[
+ port['id']][0]
+
+ return allocation
+
def allocate_for_instance(self, context, instance,
requested_networks,
security_groups=None, bind_host_id=None,
@@ -1095,15 +1134,12 @@ class API:
for port in requested_ports_dict.values():
# only communicate the allocations if the port has resource
# requests
- if port.get(constants.RESOURCE_REQUEST):
+ if self._has_resource_request(context, port, neutron):
+
profile = get_binding_profile(port)
- # NOTE(gibi): In the resource provider mapping there can be
- # more than one RP fulfilling a request group. But resource
- # requests of a Neutron port is always mapped to a
- # numbered request group that is always fulfilled by one
- # resource provider. So we only pass that single RP UUID here.
- profile[constants.ALLOCATION] = resource_provider_mapping[
- port['id']][0]
+ profile[constants.ALLOCATION] = (
+ self._get_binding_profile_allocation(
+ context, port, neutron, resource_provider_mapping))
port[constants.BINDING_PROFILE] = profile
# Create ports from the list of ordered_networks. The returned
@@ -1305,7 +1341,7 @@ class API:
# the this extension mandatory. In Xena this extension will be optional to
# support the scenario where Neutron upgraded first. So Neutron can mark
# this mandatory earliest in Yoga.
- def _has_extended_resource_request_extension(self, context, neutron=None):
+ def has_extended_resource_request_extension(self, context, neutron=None):
self._refresh_neutron_extensions_cache(context, neutron=neutron)
return constants.RESOURCE_REQUEST_GROUPS_EXTENSION in self.extensions
@@ -2032,7 +2068,7 @@ class API:
This function is only here temporarily to help mocking this check in
the functional test environment.
"""
- return not (self._has_extended_resource_request_extension(context))
+ return not (self.has_extended_resource_request_extension(context))
def create_resource_requests(
self, context, requested_networks, pci_requests=None,
@@ -2048,8 +2084,6 @@ class API:
:type pci_requests: nova.objects.InstancePCIRequests
:param affinity_policy: requested pci numa affinity policy
:type affinity_policy: nova.objects.fields.PCINUMAAffinityPolicy
- :raises ExtendedResourceRequestNotSupported: if the
- extended-resource-request Neutron API extension is enabled.
:returns: A three tuple with an instance of ``objects.NetworkMetadata``
for use by the scheduler or None, a list of RequestGroup
@@ -2060,15 +2094,12 @@ class API:
if not requested_networks or requested_networks.no_allocate:
return None, [], None
- if not self.support_create_with_resource_request(context):
- raise exception.ExtendedResourceRequestNotSupported()
-
physnets = set()
tunneled = False
neutron = get_client(context, admin=True)
has_extended_resource_request_extension = (
- self._has_extended_resource_request_extension(context, neutron))
+ self.has_extended_resource_request_extension(context, neutron))
resource_requests = []
request_level_params = objects.RequestLevelParams()
diff --git a/nova/objects/service.py b/nova/objects/service.py
index 6871a99593..33e6c7d43f 100644
--- a/nova/objects/service.py
+++ b/nova/objects/service.py
@@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__)
# NOTE(danms): This is the global service version counter
-SERVICE_VERSION = 57
+SERVICE_VERSION = 58
# NOTE(danms): This is our SERVICE_VERSION history. The idea is that any
@@ -202,6 +202,9 @@ SERVICE_VERSION_HISTORY = (
# Version 57: Compute RPC v6.0:
# Add support for vnic 'accelerator-direct'.
{'compute_rpc': '6.0'},
+ # Version 58: Compute RPC v6.0:
+ # Add support for booting with neutron extended resource request
+ {'compute_rpc': '6.0'},
)
# This is used to raise an error at service startup if older than N-1 computes
diff --git a/nova/tests/functional/test_servers_resource_request.py b/nova/tests/functional/test_servers_resource_request.py
index e77adcd275..8be374b868 100644
--- a/nova/tests/functional/test_servers_resource_request.py
+++ b/nova/tests/functional/test_servers_resource_request.py
@@ -1642,15 +1642,9 @@ class MultiGroupResourceRequestBasedSchedulingTest(
super().setUp()
self.neutron = self.useFixture(
MultiGroupResourceRequestNeutronFixture(self))
- # Turn off the blanket rejections of the extended resource request.
- # This test class wants to prove that the extended resource request is
- # supported.
- patcher = mock.patch(
- 'nova.network.neutron.API.support_create_with_resource_request',
- return_value=True,
- )
- self.addCleanup(patcher.stop)
- patcher.start()
+ # Turn off the blanket rejections of the extended resource request in
+ # port attach. This test class wants to prove that the extended
+ # resource request is supported.
patcher = mock.patch(
'nova.compute.api.API.support_port_attach',
return_value=True,
@@ -1659,14 +1653,6 @@ class MultiGroupResourceRequestBasedSchedulingTest(
patcher.start()
@unittest.expectedFailure
- def test_boot_server_with_two_ports_one_having_resource_request(self):
- super().test_boot_server_with_two_ports_one_having_resource_request()
-
- @unittest.expectedFailure
- def test_one_ovs_one_sriov_port(self):
- super().test_one_ovs_one_sriov_port()
-
- @unittest.expectedFailure
def test_interface_attach_with_resource_request(self):
super().test_interface_attach_with_resource_request()
@@ -1682,14 +1668,6 @@ class MultiGroupResourceRequestBasedSchedulingTest(
def test_delete_bound_port_in_neutron_with_resource_request(self):
super().test_delete_bound_port_in_neutron_with_resource_request()
- @unittest.expectedFailure
- def test_two_sriov_ports_one_with_request_two_available_pfs(self):
- super().test_two_sriov_ports_one_with_request_two_available_pfs()
-
- @unittest.expectedFailure
- def test_sriov_macvtap_port_with_resource_request(self):
- super().test_sriov_macvtap_port_with_resource_request()
-
class ServerMoveWithPortResourceRequestTest(
PortResourceRequestBasedSchedulingTestBase):
@@ -2609,12 +2587,6 @@ class ServerMoveWithMultiGroupResourceRequestBasedSchedulingTest(
# This test class wants to prove that the extended resource request is
# supported.
patcher = mock.patch(
- 'nova.network.neutron.API.support_create_with_resource_request',
- return_value=True,
- )
- self.addCleanup(patcher.stop)
- patcher.start()
- patcher = mock.patch(
'nova.network.neutron.API.instance_has_extended_resource_request',
return_value=False,
)
@@ -2903,38 +2875,42 @@ class CrossCellResizeWithQoSPort(PortResourceRequestBasedSchedulingTestBase):
server, qos_normal_port, qos_sriov_port)
-class ExtendedResourceRequestTempNegativeTest(
+class ExtendedResourceRequestOldCompute(
PortResourceRequestBasedSchedulingTestBase):
- """A set of temporary tests to show that nova currently rejects requests
- that uses the extended-resource-request Neutron API extension. These test
- are expected to be removed when support for the extension is implemented
- in nova.
+ """Tests that simulate that there are compute services in the system that
+ hasn't been upgraded to a version that support extended resource request.
+ So nova rejects the operations due to the old compute.
"""
+ @mock.patch.object(
+ objects.service, 'get_minimum_version_all_cells',
+ new=mock.Mock(return_value=57)
+ )
def test_boot(self):
- """The neutron fixture used in this test enables the
- extended-resource-request API extension. This results in any new
- server to boot. This is harsh but without nova support for this
- extension there is no way that this extension is helpful. So treat
- this as a deployment configuration error.
- """
self.neutron = self.useFixture(
ExtendedResourceRequestNeutronFixture(self))
ex = self.assertRaises(
client.OpenStackApiException,
self._create_server,
flavor=self.flavor,
- networks=[{'port': self.neutron.port_1['id']}],
+ networks=[{'port': self.neutron.port_with_resource_request['id']}],
)
-
self.assertEqual(400, ex.response.status_code)
self.assertIn(
'The port-resource-request-groups neutron API extension is not '
- 'yet supported by Nova. Please turn off this extension in '
- 'Neutron.',
+ 'supported by old nova compute service. Upgrade your compute '
+ 'services to Xena (24.0.0) or later.',
str(ex)
)
+
+class ExtendedResourceRequestTempNegativeTest(
+ PortResourceRequestBasedSchedulingTestBase):
+ """A set of temporary tests to show that nova currently rejects requests
+ that uses the extended-resource-request Neutron API extension. These test
+ are expected to be removed when support for the extension is implemented
+ in nova.
+ """
def _test_operation(self, op_name, op_callable):
# boot a server with a qos port still using the old Neutron resource
# request API extension
diff --git a/nova/tests/unit/compute/test_api.py b/nova/tests/unit/compute/test_api.py
index a8f91a0458..d0d7e5ba1d 100644
--- a/nova/tests/unit/compute/test_api.py
+++ b/nova/tests/unit/compute/test_api.py
@@ -7234,7 +7234,7 @@ class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
self.context, instance.host, 'nova-compute')
@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)
)
@mock.patch('nova.compute.rpcapi.ComputeAPI.attach_interface')
diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py
index a7b009d377..003114192b 100644
--- a/nova/tests/unit/compute/test_compute.py
+++ b/nova/tests/unit/compute/test_compute.py
@@ -9820,6 +9820,32 @@ class ComputeAPITestCase(BaseTestCase):
self.assertEqual(refs[i]['display_name'], name)
self.assertEqual(refs[i]['hostname'], name)
+ @mock.patch("nova.objects.service.get_minimum_version_all_cells")
+ @mock.patch(
+ "nova.network.neutron.API.has_extended_resource_request_extension")
+ @mock.patch("nova.network.neutron.API.create_resource_requests")
+ def test_instance_create_with_extended_res_req_old_compute(
+ self, mock_create_res_req, mock_has_extended_res_req,
+ mock_get_min_svc_version
+ ):
+ requested_networks = objects.NetworkRequestList(
+ objects=[
+ objects.NetworkRequest(port_id=uuids.port1)
+ ]
+ )
+ mock_create_res_req.return_value = (
+ None, [objects.RequestGroup()], None)
+ mock_has_extended_res_req.return_value = True
+ mock_get_min_svc_version.return_value = 57
+
+ self.assertRaises(
+ exception.ExtendedResourceRequestOldCompute,
+ self.compute_api.create, self.context, self.default_flavor,
+ image_href=uuids.image_href_id,
+ requested_networks=requested_networks,
+ supports_port_resource_request=True,
+ )
+
def test_instance_architecture(self):
# Test the instance architecture.
i_ref = self._create_fake_instance_obj()
diff --git a/nova/tests/unit/network/test_neutron.py b/nova/tests/unit/network/test_neutron.py
index 3fb6d5f2fe..e511b925a8 100644
--- a/nova/tests/unit/network/test_neutron.py
+++ b/nova/tests/unit/network/test_neutron.py
@@ -493,12 +493,11 @@ class TestAPIBase(test.TestCase):
**kwargs)
@mock.patch.object(neutronapi.API, '_populate_neutron_extension_values')
- @mock.patch.object(neutronapi.API, '_refresh_neutron_extensions_cache')
@mock.patch.object(neutronapi.API, 'get_instance_nw_info',
return_value=None)
@mock.patch.object(neutronapi, 'get_client')
def _test_allocate_for_instance(self, mock_get_client, mock_get_nw,
- mock_refresh, mock_populate, net_idx=1,
+ mock_populate, net_idx=1,
requested_networks=None,
exception=None,
context=None,
@@ -651,8 +650,6 @@ class TestAPIBase(test.TestCase):
mock.call(ctxt), mock.call(ctxt, admin=True)],
any_order=True)
- mock_refresh.assert_not_called()
-
if requested_networks:
mocked_client.show_port.assert_has_calls(expected_show_port_calls)
self.assertEqual(len(expected_show_port_calls),
@@ -4017,10 +4014,13 @@ class TestAPI(TestAPIBase):
instance = fake_instance.fake_instance_obj(self.context)
create_port_mock.side_effect = \
exceptions.IpAddressGenerationFailureClient()
- self.assertRaises(exception.NoMoreFixedIps,
- self.api._create_port_minimal,
- neutronapi.get_client(self.context),
- instance, uuids.my_netid1)
+ self.assertRaises(
+ exception.NoMoreFixedIps,
+ self.api._create_port_minimal,
+ self.context,
+ neutronapi.get_client(self.context),
+ instance, uuids.my_netid1
+ )
self.assertTrue(create_port_mock.called)
@mock.patch.object(client.Client, 'update_port',
@@ -4082,10 +4082,13 @@ class TestAPI(TestAPIBase):
instance = fake_instance.fake_instance_obj(self.context)
fake_ip = '1.1.1.1'
- self.assertRaises(exception.FixedIpAlreadyInUse,
- self.api._create_port_minimal,
- neutronapi.get_client(self.context),
- instance, uuids.my_netid1, fixed_ip=fake_ip)
+ self.assertRaises(
+ exception.FixedIpAlreadyInUse,
+ self.api._create_port_minimal,
+ self.context,
+ neutronapi.get_client(self.context),
+ instance, uuids.my_netid1, fixed_ip=fake_ip
+ )
self.assertTrue(create_port_mock.called)
@mock.patch.object(client.Client, 'create_port',
@@ -4095,10 +4098,13 @@ class TestAPI(TestAPIBase):
instance = fake_instance.fake_instance_obj(self.context)
fake_ip = '1.1.1.1'
- self.assertRaises(exception.FixedIpAlreadyInUse,
- self.api._create_port_minimal,
- neutronapi.get_client(self.context),
- instance, uuids.my_netid1, fixed_ip=fake_ip)
+ self.assertRaises(
+ exception.FixedIpAlreadyInUse,
+ self.api._create_port_minimal,
+ self.context,
+ neutronapi.get_client(self.context),
+ instance, uuids.my_netid1, fixed_ip=fake_ip
+ )
self.assertTrue(create_port_mock.called)
@mock.patch.object(client.Client, 'create_port',
@@ -4107,10 +4113,13 @@ class TestAPI(TestAPIBase):
instance = fake_instance.fake_instance_obj(self.context)
fake_ip = '1.1.1.1'
- exc = self.assertRaises(exception.InvalidInput,
- self.api._create_port_minimal,
- neutronapi.get_client(self.context),
- instance, uuids.my_netid1, fixed_ip=fake_ip)
+ exc = self.assertRaises(
+ exception.InvalidInput,
+ self.api._create_port_minimal,
+ self.context,
+ neutronapi.get_client(self.context),
+ instance, uuids.my_netid1, fixed_ip=fake_ip
+ )
expected_exception_msg = ('Invalid input received: Fixed IP %(ip)s is '
'not a valid ip address for network '
@@ -4119,6 +4128,10 @@ class TestAPI(TestAPIBase):
self.assertEqual(expected_exception_msg, str(exc))
self.assertTrue(create_port_mock.called)
+ @mock.patch(
+ 'nova.network.neutron.API.has_extended_resource_request_extension',
+ new=mock.Mock(return_value=False),
+ )
def test_create_port_minimal_raise_qos_not_supported(self):
instance = fake_instance.fake_instance_obj(self.context)
mock_client = mock.MagicMock()
@@ -4128,9 +4141,47 @@ class TestAPI(TestAPIBase):
'resources': {'CUSTOM_RESOURCE_CLASS': 42}}
}}
- exc = self.assertRaises(exception.NetworksWithQoSPolicyNotSupported,
- self.api._create_port_minimal,
- mock_client, instance, uuids.my_netid1)
+ exc = self.assertRaises(
+ exception.NetworksWithQoSPolicyNotSupported,
+ self.api._create_port_minimal,
+ self.context,
+ mock_client, instance, uuids.my_netid1
+ )
+ expected_exception_msg = ('Using networks with QoS policy is not '
+ 'supported for instance %(instance)s. '
+ '(Network ID is %(net_id)s)' %
+ {'instance': instance.uuid,
+ 'net_id': uuids.my_netid1})
+ self.assertEqual(expected_exception_msg, str(exc))
+ mock_client.delete_port.assert_called_once_with(uuids.port_id)
+
+ @mock.patch(
+ 'nova.network.neutron.API.has_extended_resource_request_extension',
+ new=mock.Mock(return_value=True),
+ )
+ def test_create_port_minimal_raise_extended_qos_not_supported(self):
+ instance = fake_instance.fake_instance_obj(self.context)
+ mock_client = mock.MagicMock()
+ mock_client.create_port.return_value = {
+ 'port': {
+ 'id': uuids.port_id,
+ constants.RESOURCE_REQUEST: {
+ 'request_groups': [
+ {
+ "id": uuids.group1,
+ 'resources': {'CUSTOM_RESOURCE_CLASS': 42},
+ }
+ ],
+ }
+ }
+ }
+
+ exc = self.assertRaises(
+ exception.NetworksWithQoSPolicyNotSupported,
+ self.api._create_port_minimal,
+ self.context,
+ mock_client, instance, uuids.my_netid1
+ )
expected_exception_msg = ('Using networks with QoS policy is not '
'supported for instance %(instance)s. '
'(Network ID is %(net_id)s)' %
@@ -4152,9 +4203,12 @@ class TestAPI(TestAPIBase):
mock_client.delete_port.side_effect = \
exceptions.NeutronClientException()
- exc = self.assertRaises(exception.NetworksWithQoSPolicyNotSupported,
- self.api._create_port_minimal,
- mock_client, instance, uuids.my_netid1)
+ exc = self.assertRaises(
+ exception.NetworksWithQoSPolicyNotSupported,
+ self.api._create_port_minimal,
+ self.context,
+ mock_client, instance, uuids.my_netid1
+ )
expected_exception_msg = ('Using networks with QoS policy is not '
'supported for instance %(instance)s. '
'(Network ID is %(net_id)s)' %
@@ -5112,6 +5166,10 @@ class TestAPI(TestAPIBase):
api._unbind_ports(mock_ctx, [None], mock_client, mock_client)
self.assertFalse(mock_update_port.called)
+ @mock.patch(
+ 'nova.network.neutron.API.has_extended_resource_request_extension',
+ new=mock.Mock()
+ )
@mock.patch('nova.network.neutron.API.get_instance_nw_info')
@mock.patch('nova.network.neutron.excutils')
@mock.patch('nova.network.neutron.API._delete_ports')
@@ -5343,6 +5401,10 @@ class TestAPI(TestAPIBase):
self.api._delete_nic_metadata(instance, vif)
instance.save.assert_not_called()
+ @mock.patch(
+ 'nova.network.neutron.API.has_extended_resource_request_extension',
+ new=mock.Mock(return_value=False),
+ )
@mock.patch('nova.network.neutron.API._check_external_network_attach')
@mock.patch('nova.network.neutron.API._populate_neutron_extension_values')
@mock.patch('nova.network.neutron.API._get_available_networks')
@@ -5439,6 +5501,69 @@ class TestAPI(TestAPIBase):
mock.sentinel.ctx, uuids.portid_1,
neutron_client=mock_get_client.return_value)
+ @mock.patch(
+ "nova.network.neutron.API.has_extended_resource_request_extension")
+ @mock.patch('nova.objects.virtual_interface.VirtualInterface.create')
+ @mock.patch('nova.network.neutron.API._check_external_network_attach')
+ @mock.patch('nova.network.neutron.API._show_port')
+ @mock.patch('nova.network.neutron.API._update_port')
+ @mock.patch('nova.network.neutron.get_client')
+ def test_port_with_extended_resource_request_has_allocation_in_binding(
+ self, mock_get_client, mock_update_port, mock_show_port,
+ mock_check_external, mock_vif_create, mock_has_extended_res_req):
+
+ nw_req = objects.NetworkRequestList(
+ objects=[objects.NetworkRequest(port_id=uuids.portid_1)])
+ mock_inst = mock.Mock(
+ uuid=uuids.instance_uuid,
+ project_id=uuids.project_id,
+ availability_zone='nova',
+ )
+ port = {
+ 'id': uuids.portid_1,
+ 'tenant_id': uuids.project_id,
+ 'network_id': uuids.networkid_1,
+ 'mac_address': 'fake-mac',
+ constants.RESOURCE_REQUEST: {
+ "request_groups": [
+ {"id": uuids.group1},
+ {"id": uuids.group2},
+ ]
+ }
+ }
+ mock_show_port.return_value = port
+ mock_get_client.return_value.list_networks.return_value = {
+ "networks": [{'id': uuids.networkid_1,
+ 'port_security_enabled': False}]}
+ mock_update_port.return_value = port
+ mock_has_extended_res_req.return_value = True
+
+ with mock.patch.object(self.api, 'get_instance_nw_info'):
+ self.api.allocate_for_instance(
+ mock.sentinel.ctx, mock_inst,
+ requested_networks=nw_req,
+ resource_provider_mapping={
+ uuids.group1: [uuids.rp1],
+ uuids.group2: [uuids.rp2],
+ })
+
+ mock_update_port.assert_called_once_with(
+ mock_get_client.return_value, mock_inst, uuids.portid_1,
+ {
+ 'port': {
+ 'binding:host_id': None,
+ 'device_id': uuids.instance_uuid,
+ 'binding:profile': {
+ 'allocation': {
+ uuids.group1: uuids.rp1,
+ uuids.group2: uuids.rp2,
+ }
+ },
+ 'device_owner': 'compute:nova'}})
+ mock_show_port.assert_called_once_with(
+ mock.sentinel.ctx, uuids.portid_1,
+ neutron_client=mock_get_client.return_value)
+
@mock.patch('nova.network.neutron.get_client')
def test_get_floating_ip_by_address_not_found_neutron_not_found(self,
mock_ntrn):
@@ -5796,7 +5921,7 @@ class TestAPI(TestAPIBase):
self.assertEqual([], port_resource_requests)
@mock.patch.object(
- neutronapi.API, '_has_extended_resource_request_extension',
+ neutronapi.API, 'has_extended_resource_request_extension',
return_value=False)
@mock.patch.object(neutronapi.API, '_get_physnet_tunneled_info')
@mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock())
@@ -5824,7 +5949,7 @@ class TestAPI(TestAPIBase):
self.assertEqual([], port_resource_requests)
@mock.patch.object(
- neutronapi.API, '_has_extended_resource_request_extension',
+ neutronapi.API, 'has_extended_resource_request_extension',
return_value=False)
@mock.patch('nova.objects.request_spec.RequestGroup.from_port_request')
@mock.patch.object(neutronapi.API, '_get_physnet_tunneled_info')
@@ -5920,31 +6045,8 @@ class TestAPI(TestAPIBase):
port_uuid=uuids.trusted_port,
port_resource_request=mock.sentinel.resource_request2),
])
- mock_has_extended_res_req.assert_has_calls(
- [
- mock.call(self.context),
- mock.call(self.context, getclient.return_value),
- ]
- )
-
- @mock.patch.object(
- neutronapi.API, '_has_extended_resource_request_extension',
- return_value=True)
- def test_create_resource_request_extended_not_supported(
- self, mock_has_extended_extension
- ):
- requested_networks = objects.NetworkRequestList(
- objects=[
- objects.NetworkRequest(port_id=uuids.portid_1),
- ]
- )
- pci_requests = objects.InstancePCIRequests(requests=[])
- self.assertRaises(
- exception.ExtendedResourceRequestNotSupported,
- neutronapi.API().create_resource_requests,
- self.context, requested_networks, pci_requests
- )
- mock_has_extended_extension.assert_called_once_with(self.context)
+ mock_has_extended_res_req.assert_called_once_with(
+ self.context, getclient.return_value)
@mock.patch(
'nova.accelerator.cyborg._CyborgClient.get_device_request_groups')
@@ -6044,7 +6146,7 @@ class TestAPI(TestAPIBase):
'nova.network.neutron.API.support_create_with_resource_request',
new=mock.Mock(return_value=True))
@mock.patch.object(
- neutronapi.API, '_has_extended_resource_request_extension',
+ neutronapi.API, 'has_extended_resource_request_extension',
return_value=True)
@mock.patch(
'nova.objects.request_spec.RequestLevelParams.extend_with'
@@ -6711,7 +6813,7 @@ class TestAPI(TestAPIBase):
@mock.patch(
'nova.network.neutron.API.'
- '_has_extended_resource_request_extension')
+ 'has_extended_resource_request_extension')
def test__has_resource_request(self, mock_has_extended_res_req):
# Old format, resource_request in None. That is Neutron current
# behavior if the port has no QoS policy associated.
@@ -7623,6 +7725,10 @@ class TestAllocateForInstance(test.NoDBTestCase):
exception.NetworkAmbiguous,
[{'id': "net1"}, {'id': "net2"}])
+ @mock.patch(
+ 'nova.network.neutron.API.has_extended_resource_request_extension',
+ new=mock.Mock(return_value=False),
+ )
def test_create_ports_for_instance_no_security(self):
api = neutronapi.API()
ordered_networks = [objects.NetworkRequest(network_id=uuids.net)]
@@ -7639,6 +7745,10 @@ class TestAllocateForInstance(test.NoDBTestCase):
'network_id': uuids.net, 'tenant_id': uuids.tenant_id,
'admin_state_up': True, 'device_id': self.instance.uuid}})
+ @mock.patch(
+ 'nova.network.neutron.API.has_extended_resource_request_extension',
+ new=mock.Mock(return_value=False),
+ )
def test_create_ports_for_instance_with_security_groups(self):
api = neutronapi.API()
ordered_networks = [objects.NetworkRequest(network_id=uuids.net)]
@@ -7657,6 +7767,10 @@ class TestAllocateForInstance(test.NoDBTestCase):
'admin_state_up': True, 'security_groups': security_groups,
'device_id': self.instance.uuid}})
+ @mock.patch(
+ 'nova.network.neutron.API.has_extended_resource_request_extension',
+ new=mock.Mock(return_value=False),
+ )
def test_create_ports_for_instance_with_cleanup_after_pc_failure(self):
api = neutronapi.API()
ordered_networks = [
@@ -7688,6 +7802,10 @@ class TestAllocateForInstance(test.NoDBTestCase):
mock_client.delete_port.call_args_list)
self.assertEqual(3, mock_client.create_port.call_count)
+ @mock.patch(
+ 'nova.network.neutron.API.has_extended_resource_request_extension',
+ new=mock.Mock(return_value=False),
+ )
def test_create_ports_for_instance_with_cleanup_after_sg_failure(self):
api = neutronapi.API()
ordered_networks = [
@@ -7843,6 +7961,10 @@ class TestAPINeutronHostnameDNSPortbinding(TestAPIBase):
bind_host_id=self.instance.get('host'),
requested_networks=requested_networks)
+ @mock.patch(
+ 'nova.network.neutron.API.has_extended_resource_request_extension',
+ new=mock.Mock(return_value=False)
+ )
def test_allocate_for_instance_create_port_with_dns_domain(self):
# The port's dns_name attribute should be set by the port update
# request in _update_port_dns_name. This should happen only when the
@@ -7851,6 +7973,10 @@ class TestAPINeutronHostnameDNSPortbinding(TestAPIBase):
self._test_allocate_for_instance_with_virtual_interface(
11, dns_extension=True, bind_host_id=self.instance.get('host'))
+ @mock.patch(
+ "nova.network.neutron.API._has_dns_extension",
+ new=mock.Mock(return_value=True)
+ )
def test_allocate_for_instance_with_requested_port_with_dns_domain(self):
# The port's dns_name attribute should be set by the port update
# request in _update_port_dns_name. This should happen only when the
@@ -7999,14 +8125,16 @@ class TestNeutronPortSecurity(test.NoDBTestCase):
api = neutronapi.API()
mock_create_port.return_value = {'id': 'foo', 'mac_address': 'bar'}
api.allocate_for_instance(
- 'context', instance, requested_networks=onets,
+ mock.sentinel.context, instance, requested_networks=onets,
security_groups=secgroups)
mock_process_security_groups.assert_called_once_with(
instance, mock.ANY, [])
mock_create_port.assert_has_calls([
- mock.call(mock.ANY, instance, u'net1', None, []),
- mock.call(mock.ANY, instance, u'net2', None, [])],
+ mock.call(
+ mock.sentinel.context, mock.ANY, instance, u'net1', None, []),
+ mock.call(
+ mock.sentinel.context, mock.ANY, instance, u'net2', None, [])],
any_order=True)
@mock.patch.object(neutronapi.API, 'get_instance_nw_info')
@@ -8052,13 +8180,15 @@ class TestNeutronPortSecurity(test.NoDBTestCase):
api = neutronapi.API()
mock_create_port.return_value = {'id': 'foo', 'mac_address': 'bar'}
api.allocate_for_instance(
- 'context', instance, requested_networks=onets,
+ mock.sentinel.context, instance, requested_networks=onets,
security_groups=secgroups)
mock_create_port.assert_has_calls([
- mock.call(mock.ANY, instance, u'net1', None,
+ mock.call(
+ mock.sentinel.context, mock.ANY, instance, u'net1', None,
['default-uuid', 'secgrp-uuid1', 'secgrp-uuid2']),
- mock.call(mock.ANY, instance, u'net2', None,
+ mock.call(
+ mock.sentinel.context, mock.ANY, instance, u'net2', None,
['default-uuid', 'secgrp-uuid1', 'secgrp-uuid2'])],
any_order=True)
@@ -8103,14 +8233,16 @@ class TestNeutronPortSecurity(test.NoDBTestCase):
api = neutronapi.API()
mock_create_port.return_value = {'id': 'foo', 'mac_address': 'bar'}
api.allocate_for_instance(
- 'context', instance, requested_networks=onets,
+ mock.sentinel.context, instance, requested_networks=onets,
security_groups=secgroups)
mock_process_security_groups.assert_called_once_with(
instance, mock.ANY, [])
mock_create_port.assert_has_calls([
- mock.call(mock.ANY, instance, u'net1', None, []),
- mock.call(mock.ANY, instance, u'net2', None, [])],
+ mock.call(
+ mock.sentinel.context, mock.ANY, instance, u'net1', None, []),
+ mock.call(
+ mock.sentinel.context, mock.ANY, instance, u'net2', None, [])],
any_order=True)
@mock.patch.object(neutronapi.API, 'get_instance_nw_info')
@@ -8363,7 +8495,7 @@ class TestAPIAutoAllocateNetwork(test.NoDBTestCase):
# was auto-allocated
port_req_body = mock.ANY
create_port_mock.assert_called_once_with(
- ntrn, instance, uuids.network_id,
+ self.context, ntrn, instance, uuids.network_id,
None, # request.address (fixed IP)
[], # security_group_ids - we didn't request any
)