summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBalazs Gibizer <balazs.gibizer@est.tech>2021-07-15 10:51:33 +0200
committerBalazs Gibizer <balazs.gibizer@est.tech>2021-08-27 15:51:12 +0200
commitc3886c3ca758b32df8a58388d967dc3c0a5aebec (patch)
treee7e352ba9aceaf63183eb18cc5fb989cb4f0d5e2
parent94f47471e058e46441a63d5aaf77776ef75b3fba (diff)
downloadnova-c3886c3ca758b32df8a58388d967dc3c0a5aebec.tar.gz
Support boot with extended resource request
This adds the final missing pieces to support creating servers with ports having extended resource request. As the changes in the neutron interface code is called from nova-compute service during the port binding the compute service version is bumped. And a check is added to the compute-api to reject such server create requests if there are old computes in the cluster. Note that some of the negative and SRIOV related interface attach tests are also started to pass as they are not dependent on any of the interface attach specific implementation. Still interface attach is broken here as the failing of the positive tests show. blueprint: qos-minimum-guaranteed-packet-rate Change-Id: I9060cc9cb9e0d5de641ade78c5fd7e1cc77ade46
-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
)