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
This commit is contained in:
parent
94f47471e0
commit
c3886c3ca7
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
@ -1658,14 +1652,6 @@ class MultiGroupResourceRequestBasedSchedulingTest(
|
|||
self.addCleanup(patcher.stop)
|
||||
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):
|
||||
|
@ -2608,12 +2586,6 @@ class ServerMoveWithMultiGroupResourceRequestBasedSchedulingTest(
|
|||
# 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()
|
||||
patcher = mock.patch(
|
||||
'nova.network.neutron.API.instance_has_extended_resource_request',
|
||||
return_value=False,
|
||||
|
@ -2903,6 +2875,35 @@ class CrossCellResizeWithQoSPort(PortResourceRequestBasedSchedulingTestBase):
|
|||
server, qos_normal_port, qos_sriov_port)
|
||||
|
||||
|
||||
class ExtendedResourceRequestOldCompute(
|
||||
PortResourceRequestBasedSchedulingTestBase):
|
||||
"""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):
|
||||
self.neutron = self.useFixture(
|
||||
ExtendedResourceRequestNeutronFixture(self))
|
||||
ex = self.assertRaises(
|
||||
client.OpenStackApiException,
|
||||
self._create_server,
|
||||
flavor=self.flavor,
|
||||
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 '
|
||||
'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
|
||||
|
@ -2910,31 +2911,6 @@ class ExtendedResourceRequestTempNegativeTest(
|
|||
are expected to be removed when support for the extension is implemented
|
||||
in nova.
|
||||
"""
|
||||
|
||||
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']}],
|
||||
)
|
||||
|
||||
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.',
|
||||
str(ex)
|
||||
)
|
||||
|
||||
def _test_operation(self, op_name, op_callable):
|
||||
# boot a server with a qos port still using the old Neutron resource
|
||||
# request API extension
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue