Merge "Reject resize with port having resource request"
This commit is contained in:
commit
ac1fafb84c
|
@ -550,3 +550,28 @@ def supports_port_resource_request(req):
|
|||
port resource request support, False otherwise.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
def supports_port_resource_request_during_move(req):
|
||||
"""Check to see if the requested API version is high enough for support
|
||||
port resource request during move operation.
|
||||
|
||||
NOTE: At the moment there is no such microversion that supports port
|
||||
resource request during move. This function is added as a preparation for
|
||||
that microversion.
|
||||
|
||||
:param req: The incoming API request
|
||||
:returns: True if the requested API microversion is high enough for
|
||||
port resource request move support, False otherwise.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
def instance_has_port_with_resource_request(
|
||||
context, instance_uuid, network_api):
|
||||
|
||||
search_opts = {'device_id': instance_uuid}
|
||||
ports = network_api.list_ports(context, **search_opts).get('ports', [])
|
||||
ports_with_resource_request = [port for port in ports
|
||||
if port.get('resource_request', None)]
|
||||
return bool(ports_with_resource_request)
|
||||
|
|
|
@ -41,6 +41,7 @@ from nova import context as nova_context
|
|||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.image import api as image_api
|
||||
from nova import network as network_api
|
||||
from nova import objects
|
||||
from nova.objects import service as service_obj
|
||||
from nova.policies import servers as server_policies
|
||||
|
@ -76,6 +77,7 @@ class ServersController(wsgi.Controller):
|
|||
|
||||
super(ServersController, self).__init__(**kwargs)
|
||||
self.compute_api = compute.API()
|
||||
self.network_api = network_api.API()
|
||||
|
||||
@wsgi.expected_errors((400, 403))
|
||||
@validation.query_schema(schema_servers.query_params_v266, '2.66')
|
||||
|
@ -806,6 +808,13 @@ class ServersController(wsgi.Controller):
|
|||
target={'user_id': instance.user_id,
|
||||
'project_id': instance.project_id})
|
||||
|
||||
if (common.instance_has_port_with_resource_request(
|
||||
context, instance_id, self.network_api) and not
|
||||
common.supports_port_resource_request_during_move(req)):
|
||||
msg = _("The resize server operation with port having QoS policy "
|
||||
"is not supported.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
self.compute_api.resize(context, instance, flavor_id, **kwargs)
|
||||
except exception.InstanceUnknownCell as e:
|
||||
|
|
|
@ -412,6 +412,7 @@ class _ServersActionsJsonTestMixin(object):
|
|||
|
||||
|
||||
class ServersActionsJsonTest(ServersSampleBase, _ServersActionsJsonTestMixin):
|
||||
USE_NEUTRON = True
|
||||
|
||||
def test_server_reboot_hard(self):
|
||||
uuid = self._post_server()
|
||||
|
@ -463,6 +464,12 @@ class ServersActionsJsonTest(ServersSampleBase, _ServersActionsJsonTestMixin):
|
|||
'server-action-confirm-resize',
|
||||
code=204)
|
||||
|
||||
|
||||
class ServersActionsJsonTestNovaNet(
|
||||
ServersSampleBase, _ServersActionsJsonTestMixin):
|
||||
# TODO(gibi): fix the tests to work with neutron as nova net is deprecated
|
||||
USE_NEUTRON = False
|
||||
|
||||
def _wait_for_active_server(self, uuid):
|
||||
"""Wait 10 seconds for the server to be ACTIVE, else fail.
|
||||
|
||||
|
|
|
@ -5480,3 +5480,31 @@ class PortResourceRequestBasedSchedulingTest(
|
|||
self.assertIn(
|
||||
'Creating server with port having QoS policy is not supported.',
|
||||
six.text_type(ex))
|
||||
|
||||
def test_resize_server_with_port_resource_request_old_microversion(self):
|
||||
server = self._create_server(
|
||||
flavor=self.flavor,
|
||||
networks=[{'port': self.neutron.port_1['id']}])
|
||||
self._wait_for_state_change(self.admin_api, server, 'ACTIVE')
|
||||
|
||||
# We need to simulate that the above server has a port that has
|
||||
# resource request, we cannot boot with such a port but legacy servers
|
||||
# can exists with such a port.
|
||||
bound_port = self.neutron._ports[self.neutron.port_1['id']]
|
||||
fake_resource_request = self.neutron.port_with_resource_request[
|
||||
'resource_request']
|
||||
bound_port['resource_request'] = fake_resource_request
|
||||
|
||||
resize_req = {
|
||||
'resize': {
|
||||
'flavorRef': self.flavor['id']
|
||||
}
|
||||
}
|
||||
ex = self.assertRaises(
|
||||
client.OpenStackApiException,
|
||||
self.api.post_server_action, server['id'], resize_req)
|
||||
|
||||
self.assertEqual(400, ex.response.status_code)
|
||||
self.assertIn(
|
||||
'The resize server operation with port having QoS policy is not '
|
||||
'supported.', six.text_type(ex))
|
||||
|
|
|
@ -123,7 +123,10 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||
mock.patch.object(compute_api.API, method,
|
||||
side_effect=exception.InstanceIsLocked(
|
||||
instance_uuid=instance['uuid'])),
|
||||
) as (mock_get, mock_method):
|
||||
mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request',
|
||||
return_value=False)
|
||||
) as (mock_get, mock_method, mock_port_check):
|
||||
|
||||
controller_function = 'self.controller.' + action
|
||||
self.assertRaises(webob.exc.HTTPConflict,
|
||||
|
@ -617,8 +620,11 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||
self.assertEqual(instance_meta['kernel_id'], uuids.kernel_image_id)
|
||||
self.assertEqual(instance_meta['ramdisk_id'], uuids.ramdisk_image_id)
|
||||
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request', return_value=False)
|
||||
@mock.patch.object(compute_api.API, 'rebuild')
|
||||
def test_rebuild_instance_raise_auto_disk_config_exc(self, mock_rebuild):
|
||||
def test_rebuild_instance_raise_auto_disk_config_exc(
|
||||
self, mock_rebuild, mock_port_check):
|
||||
body = {
|
||||
"rebuild": {
|
||||
"imageRef": self._image_href,
|
||||
|
@ -632,7 +638,10 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||
self.controller._action_rebuild,
|
||||
self.req, FAKE_UUID, body=body)
|
||||
|
||||
def test_resize_server(self):
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request',
|
||||
return_value=False)
|
||||
def test_resize_server(self, mock_port_check):
|
||||
|
||||
body = dict(resize=dict(flavorRef="http://localhost/3"))
|
||||
|
||||
|
@ -684,7 +693,9 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||
self.controller._action_resize,
|
||||
self.req, FAKE_UUID, body=body)
|
||||
|
||||
def test_resize_with_image_exceptions(self):
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request', return_value=False)
|
||||
def test_resize_with_image_exceptions(self, mock_port_check):
|
||||
body = dict(resize=dict(flavorRef="http://localhost/3"))
|
||||
self.resize_called = 0
|
||||
image_id = 'fake_image_id'
|
||||
|
@ -725,24 +736,33 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||
' disk resize disabled.')
|
||||
self.assertEqual(self.resize_called, call_no + 1)
|
||||
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request', return_value=False)
|
||||
@mock.patch('nova.compute.api.API.resize',
|
||||
side_effect=exception.CannotResizeDisk(reason=''))
|
||||
def test_resize_raises_cannot_resize_disk(self, mock_resize):
|
||||
def test_resize_raises_cannot_resize_disk(
|
||||
self, mock_resize, mock_port_check):
|
||||
body = dict(resize=dict(flavorRef="http://localhost/3"))
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._action_resize,
|
||||
self.req, FAKE_UUID, body=body)
|
||||
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request', return_value=False)
|
||||
@mock.patch('nova.compute.api.API.resize',
|
||||
side_effect=exception.FlavorNotFound(reason='',
|
||||
flavor_id='fake_id'))
|
||||
def test_resize_raises_flavor_not_found(self, mock_resize):
|
||||
def test_resize_raises_flavor_not_found(
|
||||
self, mock_resize, mock_port_check):
|
||||
body = dict(resize=dict(flavorRef="http://localhost/3"))
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._action_resize,
|
||||
self.req, FAKE_UUID, body=body)
|
||||
|
||||
def test_resize_with_too_many_instances(self):
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request',
|
||||
return_value=False)
|
||||
def test_resize_with_too_many_instances(self, mock_port_check):
|
||||
body = dict(resize=dict(flavorRef="http://localhost/3"))
|
||||
|
||||
def fake_resize(*args, **kwargs):
|
||||
|
@ -754,7 +774,9 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||
self.controller._action_resize,
|
||||
self.req, FAKE_UUID, body=body)
|
||||
|
||||
def test_resize_raises_conflict_on_invalid_state(self):
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request', return_value=False)
|
||||
def test_resize_raises_conflict_on_invalid_state(self, mock_port_check):
|
||||
body = dict(resize=dict(flavorRef="http://localhost/3"))
|
||||
|
||||
def fake_resize(*args, **kwargs):
|
||||
|
@ -768,17 +790,24 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||
self.controller._action_resize,
|
||||
self.req, FAKE_UUID, body=body)
|
||||
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request',
|
||||
return_value=False)
|
||||
@mock.patch('nova.compute.api.API.resize',
|
||||
side_effect=exception.NoValidHost(reason=''))
|
||||
def test_resize_raises_no_valid_host(self, mock_resize):
|
||||
def test_resize_raises_no_valid_host(self, mock_resize, mock_port_check):
|
||||
body = dict(resize=dict(flavorRef="http://localhost/3"))
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._action_resize,
|
||||
self.req, FAKE_UUID, body=body)
|
||||
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request',
|
||||
return_value=False)
|
||||
@mock.patch.object(compute_api.API, 'resize')
|
||||
def test_resize_instance_raise_auto_disk_config_exc(self, mock_resize):
|
||||
def test_resize_instance_raise_auto_disk_config_exc(
|
||||
self, mock_resize, mock_port_check):
|
||||
mock_resize.side_effect = exception.AutoDiskConfigDisabledByImage(
|
||||
image='dummy')
|
||||
|
||||
|
@ -788,10 +817,12 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||
self.controller._action_resize,
|
||||
self.req, FAKE_UUID, body=body)
|
||||
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request', return_value=False)
|
||||
@mock.patch('nova.compute.api.API.resize',
|
||||
side_effect=exception.PciRequestAliasNotDefined(
|
||||
alias='fake_name'))
|
||||
def test_resize_pci_alias_not_defined(self, mock_resize):
|
||||
def test_resize_pci_alias_not_defined(self, mock_resize, mock_check_port):
|
||||
# Tests that PciRequestAliasNotDefined is translated to a 400 error.
|
||||
body = dict(resize=dict(flavorRef="http://localhost/3"))
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
|
|
|
@ -7694,13 +7694,16 @@ class ServersPolicyEnforcementV21(test.NoDBTestCase):
|
|||
rule, rule_name, self.controller.update, self.req,
|
||||
FAKE_UUID, body=body)
|
||||
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request', return_value=False)
|
||||
@mock.patch('nova.api.openstack.compute.views.servers.ViewBuilder.show')
|
||||
@mock.patch.object(compute_api.API, 'update_instance')
|
||||
@mock.patch.object(common, 'get_instance')
|
||||
def test_update_overridden_policy_pass_with_same_user(self,
|
||||
get_instance_mock,
|
||||
update_instance_mock,
|
||||
view_show_mock):
|
||||
view_show_mock,
|
||||
mock_port_check):
|
||||
instance = fake_instance.fake_instance_obj(
|
||||
self.req.environ['nova.context'],
|
||||
user_id=self.req.environ['nova.context'].user_id)
|
||||
|
@ -7747,11 +7750,14 @@ class ServersPolicyEnforcementV21(test.NoDBTestCase):
|
|||
rule, rule_name, self.controller._action_resize, self.req,
|
||||
FAKE_UUID, body=body)
|
||||
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request', return_value=False)
|
||||
@mock.patch('nova.compute.api.API.resize')
|
||||
@mock.patch('nova.api.openstack.common.get_instance')
|
||||
def test_resize_overridden_policy_pass_with_same_project(self,
|
||||
get_instance_mock,
|
||||
resize_mock):
|
||||
resize_mock,
|
||||
mock_post_check):
|
||||
instance = fake_instance.fake_instance_obj(
|
||||
self.req.environ['nova.context'],
|
||||
project_id=self.req.environ['nova.context'].project_id)
|
||||
|
@ -7777,11 +7783,14 @@ class ServersPolicyEnforcementV21(test.NoDBTestCase):
|
|||
rule, rule_name, self.controller._action_resize, self.req,
|
||||
FAKE_UUID, body=body)
|
||||
|
||||
@mock.patch('nova.api.openstack.common.'
|
||||
'instance_has_port_with_resource_request', return_value=False)
|
||||
@mock.patch('nova.compute.api.API.resize')
|
||||
@mock.patch('nova.api.openstack.common.get_instance')
|
||||
def test_resize_overridden_policy_pass_with_same_user(self,
|
||||
get_instance_mock,
|
||||
resize_mock):
|
||||
resize_mock,
|
||||
mock_port_check):
|
||||
instance = fake_instance.fake_instance_obj(
|
||||
self.req.environ['nova.context'],
|
||||
user_id=self.req.environ['nova.context'].user_id)
|
||||
|
|
Loading…
Reference in New Issue