From b164aaac1f036455205d2457abc3ae75769bf42f Mon Sep 17 00:00:00 2001 From: Balazs Gibizer Date: Fri, 11 Jan 2019 16:52:27 +0100 Subject: [PATCH] Reject evacuate with port having resource request Nova does not consider the resource request of a Neutron port as of now. So this patch makes sure that server evacuate request is rejected if it involves a port that has resource request. When the feature is ready on the nova side this limitation will be lifted. blueprint: bandwidth-resource-provider Change-Id: Ic70d2bb781b6a844849a5cf2fe4d271b5a81093d --- nova/api/openstack/compute/evacuate.py | 9 +++++++ .../api_sample_tests/test_evacuate.py | 1 + nova/tests/functional/test_servers.py | 24 ++++++++++++++++++ .../api/openstack/compute/test_evacuate.py | 25 +++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/nova/api/openstack/compute/evacuate.py b/nova/api/openstack/compute/evacuate.py index fc3a1eeb3986..7fa2bab8d0da 100644 --- a/nova/api/openstack/compute/evacuate.py +++ b/nova/api/openstack/compute/evacuate.py @@ -25,6 +25,7 @@ from nova import compute import nova.conf from nova import exception from nova.i18n import _ +from nova import network from nova.policies import evacuate as evac_policies from nova import utils @@ -36,6 +37,7 @@ class EvacuateController(wsgi.Controller): super(EvacuateController, self).__init__(*args, **kwargs) self.compute_api = compute.API() self.host_api = compute.HostAPI() + self.network_api = network.API() def _get_on_shared_storage(self, req, evacuate_body): if api_version_request.is_supported(req, min_version='2.14'): @@ -114,6 +116,13 @@ class EvacuateController(wsgi.Controller): msg = _("The target host can't be the same one.") raise exc.HTTPBadRequest(explanation=msg) + if (common.instance_has_port_with_resource_request( + context, instance.uuid, self.network_api) and not + common.supports_port_resource_request_during_move(req)): + msg = _("The evacuate server operation with port having QoS " + "policy is not supported.") + raise exc.HTTPBadRequest(explanation=msg) + try: self.compute_api.evacuate(context, instance, host, on_shared_storage, password, force) diff --git a/nova/tests/functional/api_sample_tests/test_evacuate.py b/nova/tests/functional/api_sample_tests/test_evacuate.py index b8103e47bfbc..165c696be00d 100644 --- a/nova/tests/functional/api_sample_tests/test_evacuate.py +++ b/nova/tests/functional/api_sample_tests/test_evacuate.py @@ -22,6 +22,7 @@ from nova.tests.functional.api_sample_tests import test_servers class EvacuateJsonTest(test_servers.ServersSampleBase): ADMIN_API = True sample_dir = "os-evacuate" + USE_NEUTRON = True def _test_evacuate(self, req_subs, server_req, server_resp, expected_resp_code): diff --git a/nova/tests/functional/test_servers.py b/nova/tests/functional/test_servers.py index a3ebdc58f2f2..8bf5f440ffe2 100644 --- a/nova/tests/functional/test_servers.py +++ b/nova/tests/functional/test_servers.py @@ -5539,3 +5539,27 @@ class PortResourceRequestBasedSchedulingTest( self.assertIn( 'The live migrate server operation with port having QoS policy is ' 'not supported.', six.text_type(ex)) + + def test_evacuate_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 + + ex = self.assertRaises( + client.OpenStackApiException, + self.api.post_server_action, server['id'], {'evacuate': {}}) + + self.assertEqual(400, ex.response.status_code) + self.assertIn( + 'The evacuate server operation with port having QoS policy is ' + 'not supported.', six.text_type(ex)) diff --git a/nova/tests/unit/api/openstack/compute/test_evacuate.py b/nova/tests/unit/api/openstack/compute/test_evacuate.py index ee4dddbc80b0..72604305788a 100644 --- a/nova/tests/unit/api/openstack/compute/test_evacuate.py +++ b/nova/tests/unit/api/openstack/compute/test_evacuate.py @@ -12,8 +12,10 @@ # License for the specific language governing permissions and limitations # under the License. +import fixtures import mock from oslo_utils.fixture import uuidsentinel as uuids +import six import testtools import webob @@ -66,6 +68,10 @@ class EvacuateTestV21(test.NoDBTestCase): self.stub_out('nova.compute.api.API.get', fake_compute_api_get) self.stub_out('nova.compute.api.HostAPI.service_get_by_compute_host', fake_service_get_by_compute_host) + self.mock_list_port = self.useFixture( + fixtures.MockPatch( + 'nova.network.neutronv2.api.API.list_ports')).mock + self.mock_list_port.return_value = {'ports': []} self.UUID = uuids.fake for _method in self._methods: self.stub_out('nova.compute.api.API.%s' % _method, @@ -238,6 +244,21 @@ class EvacuateTestV21(test.NoDBTestCase): else: self.assertIsNone(res) + def test_evacuate_with_port_resource_request_old_microversion(self): + self.mock_list_port.return_value = {'ports': [ + {'resource_request': { + "resources": {'CUSTOM_FOO': 1}}}] + } + + admin_pass = 'MyNewPass' + ex = self.assertRaises( + webob.exc.HTTPBadRequest, self._get_evacuate_response, + {'host': 'my-host', 'onSharedStorage': 'False', + 'adminPass': admin_pass}) + + self.assertIn('The evacuate server operation with port having QoS ' + 'policy is not supported.', six.text_type(ex)) + class EvacuatePolicyEnforcementv21(test.NoDBTestCase): @@ -257,6 +278,10 @@ class EvacuatePolicyEnforcementv21(test.NoDBTestCase): self.stub_out( 'nova.api.openstack.common.get_instance', fake_get_instance) + self.mock_list_port = self.useFixture( + fixtures.MockPatch( + 'nova.network.neutronv2.api.API.list_ports')).mock + self.mock_list_port.return_value = {'ports': []} def test_evacuate_policy_failed_with_other_project(self): rule_name = "os_compute_api:os-evacuate"