diff options
Diffstat (limited to 'nova/tests/unit/api')
66 files changed, 1881 insertions, 535 deletions
diff --git a/nova/tests/unit/api/openstack/compute/admin_only_action_common.py b/nova/tests/unit/api/openstack/compute/admin_only_action_common.py index 37fd1012b7..f332d9f32f 100644 --- a/nova/tests/unit/api/openstack/compute/admin_only_action_common.py +++ b/nova/tests/unit/api/openstack/compute/admin_only_action_common.py @@ -12,8 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + import fixtures -import mock from oslo_utils import timeutils from oslo_utils import uuidutils import webob diff --git a/nova/tests/unit/api/openstack/compute/test_admin_password.py b/nova/tests/unit/api/openstack/compute/test_admin_password.py index 90a4a2983b..67e4c743d5 100644 --- a/nova/tests/unit/api/openstack/compute/test_admin_password.py +++ b/nova/tests/unit/api/openstack/compute/test_admin_password.py @@ -13,7 +13,9 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -import mock + +from unittest import mock + import webob from nova.api.openstack.compute import admin_password as admin_password_v21 diff --git a/nova/tests/unit/api/openstack/compute/test_aggregates.py b/nova/tests/unit/api/openstack/compute/test_aggregates.py index fb096861eb..21d644f0be 100644 --- a/nova/tests/unit/api/openstack/compute/test_aggregates.py +++ b/nova/tests/unit/api/openstack/compute/test_aggregates.py @@ -15,7 +15,8 @@ """Tests for the aggregates admin api.""" -import mock +from unittest import mock + from oslo_utils.fixture import uuidsentinel from webob import exc diff --git a/nova/tests/unit/api/openstack/compute/test_api.py b/nova/tests/unit/api/openstack/compute/test_api.py index ca54be9a74..d1bb6babb7 100644 --- a/nova/tests/unit/api/openstack/compute/test_api.py +++ b/nova/tests/unit/api/openstack/compute/test_api.py @@ -143,7 +143,7 @@ class APITest(test.NoDBTestCase): self.assertEqual(resp.headers[key], str(value)) def test_quota_error_mapping(self): - self._do_test_exception_mapping(exception.QuotaError, 'too many used') + self._do_test_exception_mapping(exception.OverQuota, 'too many used') def test_non_nova_notfound_exception_mapping(self): class ExceptionWithCode(Exception): diff --git a/nova/tests/unit/api/openstack/compute/test_attach_interfaces.py b/nova/tests/unit/api/openstack/compute/test_attach_interfaces.py index 526cb6011d..e4719ea052 100644 --- a/nova/tests/unit/api/openstack/compute/test_attach_interfaces.py +++ b/nova/tests/unit/api/openstack/compute/test_attach_interfaces.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from webob import exc from nova.api.openstack import common diff --git a/nova/tests/unit/api/openstack/compute/test_availability_zone.py b/nova/tests/unit/api/openstack/compute/test_availability_zone.py index f355eb436a..a408e0d1aa 100644 --- a/nova/tests/unit/api/openstack/compute/test_availability_zone.py +++ b/nova/tests/unit/api/openstack/compute/test_availability_zone.py @@ -13,9 +13,9 @@ # under the License. import datetime +from unittest import mock import iso8601 -import mock from oslo_serialization import jsonutils from oslo_utils.fixture import uuidsentinel diff --git a/nova/tests/unit/api/openstack/compute/test_baremetal_nodes.py b/nova/tests/unit/api/openstack/compute/test_baremetal_nodes.py index 55a8b03216..c8ad907b10 100644 --- a/nova/tests/unit/api/openstack/compute/test_baremetal_nodes.py +++ b/nova/tests/unit/api/openstack/compute/test_baremetal_nodes.py @@ -13,13 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock from ironicclient import exc as ironic_exc -import mock from webob import exc -from nova.api.openstack.compute import baremetal_nodes \ - as b_nodes_v21 +from nova.api.openstack.compute import baremetal_nodes as b_nodes_v21 from nova import context from nova import exception from nova import test diff --git a/nova/tests/unit/api/openstack/compute/test_console_auth_tokens.py b/nova/tests/unit/api/openstack/compute/test_console_auth_tokens.py index 429096d51d..a1f3d1e63d 100644 --- a/nova/tests/unit/api/openstack/compute/test_console_auth_tokens.py +++ b/nova/tests/unit/api/openstack/compute/test_console_auth_tokens.py @@ -14,8 +14,8 @@ # under the License. import copy +from unittest import mock -import mock import webob from nova.api.openstack import api_version_request diff --git a/nova/tests/unit/api/openstack/compute/test_console_output.py b/nova/tests/unit/api/openstack/compute/test_console_output.py index 1a76a445fc..a9dc830255 100644 --- a/nova/tests/unit/api/openstack/compute/test_console_output.py +++ b/nova/tests/unit/api/openstack/compute/test_console_output.py @@ -14,8 +14,8 @@ # under the License. import string +from unittest import mock -import mock import webob from nova.api.openstack.compute import console_output \ diff --git a/nova/tests/unit/api/openstack/compute/test_create_backup.py b/nova/tests/unit/api/openstack/compute/test_create_backup.py index f7280a5a37..9728002e88 100644 --- a/nova/tests/unit/api/openstack/compute/test_create_backup.py +++ b/nova/tests/unit/api/openstack/compute/test_create_backup.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_utils import timeutils import webob @@ -40,10 +41,6 @@ class CreateBackupTestsV21(admin_only_action_common.CommonMixin, self.controller = getattr(self.create_backup, self.controller_name)() self.compute_api = self.controller.compute_api - patch_get = mock.patch.object(self.compute_api, 'get') - self.mock_get = patch_get.start() - self.addCleanup(patch_get.stop) - @mock.patch.object(common, 'check_img_metadata_properties_quota') @mock.patch.object(api.API, 'backup') def test_create_backup_with_metadata(self, mock_backup, mock_check_image): diff --git a/nova/tests/unit/api/openstack/compute/test_deferred_delete.py b/nova/tests/unit/api/openstack/compute/test_deferred_delete.py index db6f774c51..8a1c8efd57 100644 --- a/nova/tests/unit/api/openstack/compute/test_deferred_delete.py +++ b/nova/tests/unit/api/openstack/compute/test_deferred_delete.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + import webob from nova.api.openstack.compute import deferred_delete as dd_v21 diff --git a/nova/tests/unit/api/openstack/compute/test_disk_config.py b/nova/tests/unit/api/openstack/compute/test_disk_config.py index bf3be1d0a3..c5ee59722a 100644 --- a/nova/tests/unit/api/openstack/compute/test_disk_config.py +++ b/nova/tests/unit/api/openstack/compute/test_disk_config.py @@ -14,8 +14,8 @@ # under the License. import datetime +from unittest import mock -import mock from oslo_serialization import jsonutils from nova.api.openstack import compute diff --git a/nova/tests/unit/api/openstack/compute/test_evacuate.py b/nova/tests/unit/api/openstack/compute/test_evacuate.py index 6620d7a180..bd88bb8d6e 100644 --- a/nova/tests/unit/api/openstack/compute/test_evacuate.py +++ b/nova/tests/unit/api/openstack/compute/test_evacuate.py @@ -12,8 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + import fixtures -import mock from oslo_utils.fixture import uuidsentinel as uuids import testtools import webob @@ -415,3 +416,32 @@ class EvacuateTestV268(EvacuateTestV229): def test_forced_evacuate_with_no_host_provided(self): # not applicable for v2.68, which removed the 'force' parameter pass + + +class EvacuateTestV295(EvacuateTestV268): + def setUp(self): + super(EvacuateTestV268, self).setUp() + self.admin_req = fakes.HTTPRequest.blank('', use_admin_context=True, + version='2.95') + self.req = fakes.HTTPRequest.blank('', version='2.95') + self.mock_get_min_ver = self.useFixture(fixtures.MockPatch( + 'nova.objects.service.get_minimum_version_all_cells', + return_value=62)).mock + + def test_evacuate_version_error(self): + self.mock_get_min_ver.return_value = 61 + self.assertRaises(webob.exc.HTTPBadRequest, + self._get_evacuate_response, + {'host': 'my-host', 'adminPass': 'foo'}) + + def test_evacuate_unsupported_rpc(self): + def fake_evacuate(*args, **kwargs): + raise exception.UnsupportedRPCVersion( + api="fakeapi", + required="x.xx") + + self.stub_out('nova.compute.api.API.evacuate', fake_evacuate) + self._check_evacuate_failure(webob.exc.HTTPConflict, + {'host': 'my-host', + 'onSharedStorage': 'False', + 'adminPass': 'MyNewPass'}) diff --git a/nova/tests/unit/api/openstack/compute/test_flavor_access.py b/nova/tests/unit/api/openstack/compute/test_flavor_access.py index 8c25a2efc2..ea9ca2f632 100644 --- a/nova/tests/unit/api/openstack/compute/test_flavor_access.py +++ b/nova/tests/unit/api/openstack/compute/test_flavor_access.py @@ -14,8 +14,8 @@ # under the License. import datetime +from unittest import mock -import mock from webob import exc from nova.api.openstack import api_version_request as api_version @@ -353,14 +353,37 @@ class FlavorAccessTestV21(test.NoDBTestCase): mock_verify.assert_called_once_with( req.environ['nova.context'], 'proj2') + @mock.patch('nova.objects.Flavor.remove_access') @mock.patch('nova.api.openstack.identity.verify_project_id', side_effect=exc.HTTPBadRequest( explanation="Project ID proj2 is not a valid project.")) - def test_remove_tenant_access_with_invalid_tenant(self, mock_verify): + def test_remove_tenant_access_with_invalid_tenant(self, + mock_verify, + mock_remove_access): """Tests the case that the tenant does not exist in Keystone.""" req = fakes.HTTPRequest.blank(self._prefix + '/flavors/2/action', use_admin_context=True) body = {'removeTenantAccess': {'tenant': 'proj2'}} + + self.flavor_action_controller._remove_tenant_access( + req, '2', body=body) + mock_verify.assert_called_once_with( + req.environ['nova.context'], 'proj2') + mock_remove_access.assert_called_once_with('proj2') + + @mock.patch('nova.api.openstack.identity.verify_project_id', + side_effect=exc.HTTPBadRequest( + explanation="Nova was unable to find Keystone " + "service endpoint.")) + def test_remove_tenant_access_missing_keystone_endpoint(self, + mock_verify): + """Tests the case that Keystone identity service endpoint + version 3.0 was not found. + """ + req = fakes.HTTPRequest.blank(self._prefix + '/flavors/2/action', + use_admin_context=True) + body = {'removeTenantAccess': {'tenant': 'proj2'}} + self.assertRaises(exc.HTTPBadRequest, self.flavor_action_controller._remove_tenant_access, req, '2', body=body) diff --git a/nova/tests/unit/api/openstack/compute/test_flavor_manage.py b/nova/tests/unit/api/openstack/compute/test_flavor_manage.py index f8412c772c..948f255f34 100644 --- a/nova/tests/unit/api/openstack/compute/test_flavor_manage.py +++ b/nova/tests/unit/api/openstack/compute/test_flavor_manage.py @@ -14,8 +14,8 @@ # under the License. import copy +from unittest import mock -import mock from oslo_serialization import jsonutils import webob diff --git a/nova/tests/unit/api/openstack/compute/test_flavors.py b/nova/tests/unit/api/openstack/compute/test_flavors.py index 4390b32012..c7fbf5c468 100644 --- a/nova/tests/unit/api/openstack/compute/test_flavors.py +++ b/nova/tests/unit/api/openstack/compute/test_flavors.py @@ -13,9 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock from urllib import parse as urlparse -import mock import webob from nova.api.openstack import common diff --git a/nova/tests/unit/api/openstack/compute/test_flavors_extra_specs.py b/nova/tests/unit/api/openstack/compute/test_flavors_extra_specs.py index e68bf7e306..8355ce59b5 100644 --- a/nova/tests/unit/api/openstack/compute/test_flavors_extra_specs.py +++ b/nova/tests/unit/api/openstack/compute/test_flavors_extra_specs.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + import testtools import webob diff --git a/nova/tests/unit/api/openstack/compute/test_floating_ip_pools.py b/nova/tests/unit/api/openstack/compute/test_floating_ip_pools.py index e25302ee9a..71ca209672 100644 --- a/nova/tests/unit/api/openstack/compute/test_floating_ip_pools.py +++ b/nova/tests/unit/api/openstack/compute/test_floating_ip_pools.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock from nova.api.openstack.compute import floating_ip_pools \ as fipp_v21 diff --git a/nova/tests/unit/api/openstack/compute/test_floating_ips.py b/nova/tests/unit/api/openstack/compute/test_floating_ips.py index 2cb89dfe76..7093c5a80d 100644 --- a/nova/tests/unit/api/openstack/compute/test_floating_ips.py +++ b/nova/tests/unit/api/openstack/compute/test_floating_ips.py @@ -14,7 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_utils.fixture import uuidsentinel as uuids import webob diff --git a/nova/tests/unit/api/openstack/compute/test_hosts.py b/nova/tests/unit/api/openstack/compute/test_hosts.py index 7adc698093..f1cdde2917 100644 --- a/nova/tests/unit/api/openstack/compute/test_hosts.py +++ b/nova/tests/unit/api/openstack/compute/test_hosts.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_utils.fixture import uuidsentinel import testtools import webob.exc diff --git a/nova/tests/unit/api/openstack/compute/test_hypervisors.py b/nova/tests/unit/api/openstack/compute/test_hypervisors.py index facc5389be..a908988811 100644 --- a/nova/tests/unit/api/openstack/compute/test_hypervisors.py +++ b/nova/tests/unit/api/openstack/compute/test_hypervisors.py @@ -14,8 +14,8 @@ # under the License. import copy +from unittest import mock -import mock import netaddr from oslo_serialization import jsonutils from oslo_utils.fixture import uuidsentinel as uuids @@ -368,25 +368,23 @@ class HypervisorsTestV21(test.NoDBTestCase): return TEST_SERVICES[0] raise exception.ComputeHostNotFound(host=host) - @mock.patch.object(self.controller.host_api, 'compute_node_get_all', - return_value=compute_nodes) - @mock.patch.object(self.controller.host_api, - 'service_get_by_compute_host', - fake_service_get_by_compute_host) - def _test(self, compute_node_get_all): - req = self._get_request(True) - result = self.controller.index(req) - self.assertEqual(1, len(result['hypervisors'])) - expected = { - 'id': compute_nodes[0].uuid if self.expect_uuid_for_id - else compute_nodes[0].id, - 'hypervisor_hostname': compute_nodes[0].hypervisor_hostname, - 'state': 'up', - 'status': 'enabled', - } - self.assertDictEqual(expected, result['hypervisors'][0]) + m_get = self.controller.host_api.compute_node_get_all + m_get.side_effect = None + m_get.return_value = compute_nodes + self.controller.host_api.service_get_by_compute_host.side_effect = ( + fake_service_get_by_compute_host) - _test(self) + req = self._get_request(True) + result = self.controller.index(req) + self.assertEqual(1, len(result['hypervisors'])) + expected = { + 'id': compute_nodes[0].uuid if self.expect_uuid_for_id + else compute_nodes[0].id, + 'hypervisor_hostname': compute_nodes[0].hypervisor_hostname, + 'state': 'up', + 'status': 'enabled', + } + self.assertDictEqual(expected, result['hypervisors'][0]) def test_index_compute_host_not_mapped(self): """Tests that we don't fail index if a host is not mapped.""" @@ -402,25 +400,22 @@ class HypervisorsTestV21(test.NoDBTestCase): return TEST_SERVICES[0] raise exception.HostMappingNotFound(name=host) - @mock.patch.object(self.controller.host_api, 'compute_node_get_all', - return_value=compute_nodes) - @mock.patch.object(self.controller.host_api, - 'service_get_by_compute_host', - fake_service_get_by_compute_host) - def _test(self, compute_node_get_all): - req = self._get_request(True) - result = self.controller.index(req) - self.assertEqual(1, len(result['hypervisors'])) - expected = { - 'id': compute_nodes[0].uuid if self.expect_uuid_for_id - else compute_nodes[0].id, - 'hypervisor_hostname': compute_nodes[0].hypervisor_hostname, - 'state': 'up', - 'status': 'enabled', - } - self.assertDictEqual(expected, result['hypervisors'][0]) + self.controller.host_api.compute_node_get_all.return_value = ( + compute_nodes) + self.controller.host_api.service_get_by_compute_host = ( + fake_service_get_by_compute_host) - _test(self) + req = self._get_request(True) + result = self.controller.index(req) + self.assertEqual(1, len(result['hypervisors'])) + expected = { + 'id': compute_nodes[0].uuid if self.expect_uuid_for_id + else compute_nodes[0].id, + 'hypervisor_hostname': compute_nodes[0].hypervisor_hostname, + 'state': 'up', + 'status': 'enabled', + } + self.assertDictEqual(expected, result['hypervisors'][0]) def test_detail(self): req = self._get_request(True) @@ -444,32 +439,30 @@ class HypervisorsTestV21(test.NoDBTestCase): return TEST_SERVICES[0] raise exception.ComputeHostNotFound(host=host) - @mock.patch.object(self.controller.host_api, 'compute_node_get_all', - return_value=compute_nodes) - @mock.patch.object(self.controller.host_api, - 'service_get_by_compute_host', - fake_service_get_by_compute_host) - def _test(self, compute_node_get_all): - req = self._get_request(True) - result = self.controller.detail(req) - self.assertEqual(1, len(result['hypervisors'])) - expected = { - 'id': compute_nodes[0].id, - 'hypervisor_hostname': compute_nodes[0].hypervisor_hostname, - 'state': 'up', - 'status': 'enabled', - } - # we don't care about all of the details, just make sure we get - # the subset we care about and there are more keys than what index - # would return - hypervisor = result['hypervisors'][0] - self.assertTrue( - set(expected.keys()).issubset(set(hypervisor.keys()))) - self.assertGreater(len(hypervisor.keys()), len(expected.keys())) - self.assertEqual(compute_nodes[0].hypervisor_hostname, - hypervisor['hypervisor_hostname']) - - _test(self) + m_get = self.controller.host_api.compute_node_get_all + m_get.side_effect = None + m_get.return_value = compute_nodes + self.controller.host_api.service_get_by_compute_host.side_effect = ( + fake_service_get_by_compute_host) + + req = self._get_request(True) + result = self.controller.detail(req) + self.assertEqual(1, len(result['hypervisors'])) + expected = { + 'id': compute_nodes[0].id, + 'hypervisor_hostname': compute_nodes[0].hypervisor_hostname, + 'state': 'up', + 'status': 'enabled', + } + # we don't care about all of the details, just make sure we get + # the subset we care about and there are more keys than what index + # would return + hypervisor = result['hypervisors'][0] + self.assertTrue( + set(expected.keys()).issubset(set(hypervisor.keys()))) + self.assertGreater(len(hypervisor.keys()), len(expected.keys())) + self.assertEqual(compute_nodes[0].hypervisor_hostname, + hypervisor['hypervisor_hostname']) def test_detail_compute_host_not_mapped(self): """Tests that if a service is deleted but the compute node is not we @@ -487,32 +480,28 @@ class HypervisorsTestV21(test.NoDBTestCase): return TEST_SERVICES[0] raise exception.HostMappingNotFound(name=host) - @mock.patch.object(self.controller.host_api, 'compute_node_get_all', - return_value=compute_nodes) - @mock.patch.object(self.controller.host_api, - 'service_get_by_compute_host', - fake_service_get_by_compute_host) - def _test(self, compute_node_get_all): - req = self._get_request(True) - result = self.controller.detail(req) - self.assertEqual(1, len(result['hypervisors'])) - expected = { - 'id': compute_nodes[0].id, - 'hypervisor_hostname': compute_nodes[0].hypervisor_hostname, - 'state': 'up', - 'status': 'enabled', - } - # we don't care about all of the details, just make sure we get - # the subset we care about and there are more keys than what index - # would return - hypervisor = result['hypervisors'][0] - self.assertTrue( - set(expected.keys()).issubset(set(hypervisor.keys()))) - self.assertGreater(len(hypervisor.keys()), len(expected.keys())) - self.assertEqual(compute_nodes[0].hypervisor_hostname, - hypervisor['hypervisor_hostname']) - - _test(self) + self.controller.host_api.service_get_by_compute_host.side_effect = ( + fake_service_get_by_compute_host) + self.controller.host_api.compute_node_get_all.return_value = ( + compute_nodes) + req = self._get_request(True) + result = self.controller.detail(req) + self.assertEqual(1, len(result['hypervisors'])) + expected = { + 'id': compute_nodes[0].id, + 'hypervisor_hostname': compute_nodes[0].hypervisor_hostname, + 'state': 'up', + 'status': 'enabled', + } + # we don't care about all of the details, just make sure we get + # the subset we care about and there are more keys than what index + # would return + hypervisor = result['hypervisors'][0] + self.assertTrue( + set(expected.keys()).issubset(set(hypervisor.keys()))) + self.assertGreater(len(hypervisor.keys()), len(expected.keys())) + self.assertEqual(compute_nodes[0].hypervisor_hostname, + hypervisor['hypervisor_hostname']) def test_show(self): req = self._get_request(True) @@ -525,21 +514,16 @@ class HypervisorsTestV21(test.NoDBTestCase): """Tests that if a service is deleted but the compute node is not we don't fail when listing hypervisors. """ - - @mock.patch.object(self.controller.host_api, 'compute_node_get', - return_value=self.TEST_HYPERS_OBJ[0]) - @mock.patch.object(self.controller.host_api, - 'service_get_by_compute_host') - def _test(self, mock_service, mock_compute_node_get): - req = self._get_request(True) - mock_service.side_effect = exception.HostMappingNotFound( - name='foo') - hyper_id = self._get_hyper_id() - self.assertRaises(exc.HTTPNotFound, self.controller.show, - req, hyper_id) - self.assertTrue(mock_service.called) - mock_compute_node_get.assert_called_once_with(mock.ANY, hyper_id) - _test(self) + self.controller.host_api.service_get_by_compute_host.side_effect = ( + exception.HostMappingNotFound(name='foo')) + req = self._get_request(True) + hyper_id = self._get_hyper_id() + self.assertRaises( + exc.HTTPNotFound, self.controller.show, req, hyper_id) + self.assertTrue( + self.controller.host_api.service_get_by_compute_host.called) + self.controller.host_api.compute_node_get.assert_called_once_with( + mock.ANY, hyper_id) def test_show_noid(self): req = self._get_request(True) @@ -611,20 +595,15 @@ class HypervisorsTestV21(test.NoDBTestCase): mock.ANY, self.TEST_HYPERS_OBJ[0].host) def test_uptime_hypervisor_not_mapped_service_get(self): - @mock.patch.object(self.controller.host_api, 'compute_node_get') - @mock.patch.object(self.controller.host_api, 'get_host_uptime') - @mock.patch.object(self.controller.host_api, - 'service_get_by_compute_host', - side_effect=exception.HostMappingNotFound( - name='dummy')) - def _test(mock_get, _, __): - req = self._get_request(True) - hyper_id = self._get_hyper_id() - self.assertRaises(exc.HTTPNotFound, - self.controller.uptime, req, hyper_id) - self.assertTrue(mock_get.called) + self.controller.host_api.service_get_by_compute_host.side_effect = ( + exception.HostMappingNotFound(name='dummy')) - _test() + req = self._get_request(True) + hyper_id = self._get_hyper_id() + self.assertRaises(exc.HTTPNotFound, + self.controller.uptime, req, hyper_id) + self.assertTrue( + self.controller.host_api.service_get_by_compute_host.called) def test_uptime_hypervisor_not_mapped(self): with mock.patch.object(self.controller.host_api, 'get_host_uptime', @@ -644,30 +623,26 @@ class HypervisorsTestV21(test.NoDBTestCase): self.assertEqual(dict(hypervisors=self.INDEX_HYPER_DICTS), result) def test_search_non_exist(self): - with mock.patch.object(self.controller.host_api, - 'compute_node_search_by_hypervisor', - return_value=[]) as mock_node_search: - req = self._get_request(True) - self.assertRaises(exc.HTTPNotFound, self.controller.search, - req, 'a') - self.assertEqual(1, mock_node_search.call_count) + m_search = self.controller.host_api.compute_node_search_by_hypervisor + m_search.side_effect = None + m_search.return_value = [] + + req = self._get_request(True) + self.assertRaises(exc.HTTPNotFound, self.controller.search, req, 'a') + self.assertEqual(1, m_search.call_count) def test_search_unmapped(self): + m_search = self.controller.host_api.compute_node_search_by_hypervisor + m_search.side_effect = None + m_search.return_value = [mock.MagicMock()] - @mock.patch.object(self.controller.host_api, - 'compute_node_search_by_hypervisor') - @mock.patch.object(self.controller.host_api, - 'service_get_by_compute_host') - def _test(mock_service, mock_search): - mock_search.return_value = [mock.MagicMock()] - mock_service.side_effect = exception.HostMappingNotFound( - name='foo') - req = self._get_request(True) - self.assertRaises(exc.HTTPNotFound, self.controller.search, - req, 'a') - self.assertTrue(mock_service.called) + self.controller.host_api.service_get_by_compute_host.side_effect = ( + exception.HostMappingNotFound(name='foo')) - _test() + req = self._get_request(True) + self.assertRaises(exc.HTTPNotFound, self.controller.search, req, 'a') + self.assertTrue( + self.controller.host_api.service_get_by_compute_host.called) @mock.patch.object(objects.InstanceList, 'get_by_host', side_effect=fake_instance_get_all_by_host) @@ -702,15 +677,12 @@ class HypervisorsTestV21(test.NoDBTestCase): def test_servers_compute_host_not_found(self): req = self._get_request(True) - with test.nested( - mock.patch.object( - self.controller.host_api, 'instance_get_all_by_host', - side_effect=fake_instance_get_all_by_host, - ), - mock.patch.object( - self.controller.host_api, 'service_get_by_compute_host', - side_effect=exception.ComputeHostNotFound(host='foo'), - ), + self.controller.host_api.service_get_by_compute_host.side_effect = ( + exception.ComputeHostNotFound(host='foo')) + with mock.patch.object( + self.controller.host_api, + 'instance_get_all_by_host', + side_effect=fake_instance_get_all_by_host, ): # The result should be empty since every attempt to fetch the # service for a hypervisor "failed" @@ -718,24 +690,25 @@ class HypervisorsTestV21(test.NoDBTestCase): self.assertEqual({'hypervisors': []}, result) def test_servers_non_id(self): - with mock.patch.object(self.controller.host_api, - 'compute_node_search_by_hypervisor', - return_value=[]) as mock_node_search: - req = self._get_request(True) - self.assertRaises(exc.HTTPNotFound, - self.controller.servers, - req, '115') - self.assertEqual(1, mock_node_search.call_count) + m_search = self.controller.host_api.compute_node_search_by_hypervisor + m_search.side_effect = None + m_search.return_value = [] + + req = self._get_request(True) + self.assertRaises(exc.HTTPNotFound, + self.controller.servers, + req, '115') + self.assertEqual(1, m_search.call_count) def test_servers_with_non_integer_hypervisor_id(self): - with mock.patch.object(self.controller.host_api, - 'compute_node_search_by_hypervisor', - return_value=[]) as mock_node_search: + m_search = self.controller.host_api.compute_node_search_by_hypervisor + m_search.side_effect = None + m_search.return_value = [] - req = self._get_request(True) - self.assertRaises(exc.HTTPNotFound, - self.controller.servers, req, 'abc') - self.assertEqual(1, mock_node_search.call_count) + req = self._get_request(True) + self.assertRaises( + exc.HTTPNotFound, self.controller.servers, req, 'abc') + self.assertEqual(1, m_search.call_count) def test_servers_with_no_servers(self): with mock.patch.object(self.controller.host_api, @@ -1089,15 +1062,13 @@ class HypervisorsTestV253(HypervisorsTestV252): use_admin_context=True, url='/os-hypervisors?with_servers=1') - with test.nested( - mock.patch.object( - self.controller.host_api, 'instance_get_all_by_host', - side_effect=fake_instance_get_all_by_host, - ), - mock.patch.object( - self.controller.host_api, 'service_get_by_compute_host', - side_effect=exception.ComputeHostNotFound(host='foo'), - ), + self.controller.host_api.service_get_by_compute_host.side_effect = ( + exception.ComputeHostNotFound(host='foo')) + + with mock.patch.object( + self.controller.host_api, + "instance_get_all_by_host", + side_effect=fake_instance_get_all_by_host, ): # The result should be empty since every attempt to fetch the # service for a hypervisor "failed" @@ -1157,11 +1128,13 @@ class HypervisorsTestV253(HypervisorsTestV252): use_admin_context=True, url='/os-hypervisors?with_servers=yes&' 'hypervisor_hostname_pattern=shenzhen') - with mock.patch.object(self.controller.host_api, - 'compute_node_search_by_hypervisor', - return_value=objects.ComputeNodeList()) as s: - self.assertRaises(exc.HTTPNotFound, self.controller.index, req) - s.assert_called_once_with(req.environ['nova.context'], 'shenzhen') + m_search = self.controller.host_api.compute_node_search_by_hypervisor + m_search.side_effect = None + m_search.return_value = objects.ComputeNodeList() + + self.assertRaises(exc.HTTPNotFound, self.controller.index, req) + m_search.assert_called_once_with( + req.environ['nova.context'], 'shenzhen') def test_detail_with_hostname_pattern(self): """Test listing hypervisors with details and using the @@ -1170,13 +1143,14 @@ class HypervisorsTestV253(HypervisorsTestV252): req = self._get_request( use_admin_context=True, url='/os-hypervisors?hypervisor_hostname_pattern=shenzhen') - with mock.patch.object( - self.controller.host_api, - 'compute_node_search_by_hypervisor', - return_value=objects.ComputeNodeList(objects=[TEST_HYPERS_OBJ[0]]) - ) as s: - result = self.controller.detail(req) - s.assert_called_once_with(req.environ['nova.context'], 'shenzhen') + m_search = self.controller.host_api.compute_node_search_by_hypervisor + m_search.side_effect = None + m_search.return_value = objects.ComputeNodeList( + objects=[TEST_HYPERS_OBJ[0]]) + + result = self.controller.detail(req) + m_search.assert_called_once_with( + req.environ['nova.context'], 'shenzhen') expected = {'hypervisors': [self.DETAIL_HYPERS_DICTS[0]]} @@ -1483,15 +1457,11 @@ class HypervisorsTestV288(HypervisorsTestV275): self.controller.uptime, req) def test_uptime_old_version(self): - with mock.patch.object( - self.controller.host_api, 'get_host_uptime', - return_value='fake uptime', - ): - req = self._get_request(use_admin_context=True, version='2.87') - hyper_id = self._get_hyper_id() + req = self._get_request(use_admin_context=True, version='2.87') + hyper_id = self._get_hyper_id() - # no exception == pass - self.controller.uptime(req, hyper_id) + # no exception == pass + self.controller.uptime(req, hyper_id) def test_uptime_noid(self): # the separate 'uptime' API has been removed, so skip this test @@ -1526,34 +1496,36 @@ class HypervisorsTestV288(HypervisorsTestV275): pass def test_show_with_uptime_notimplemented(self): - with mock.patch.object( - self.controller.host_api, 'get_host_uptime', - side_effect=NotImplementedError, - ) as mock_get_uptime: - req = self._get_request(use_admin_context=True) - hyper_id = self._get_hyper_id() + self.controller.host_api.get_host_uptime.side_effect = ( + NotImplementedError()) - result = self.controller.show(req, hyper_id) + req = self._get_request(use_admin_context=True) + hyper_id = self._get_hyper_id() - expected_dict = copy.deepcopy(self.DETAIL_HYPERS_DICTS[0]) - expected_dict.update({'uptime': None}) - self.assertEqual({'hypervisor': expected_dict}, result) - self.assertEqual(1, mock_get_uptime.call_count) + result = self.controller.show(req, hyper_id) + + expected_dict = copy.deepcopy(self.DETAIL_HYPERS_DICTS[0]) + expected_dict.update({'uptime': None}) + self.assertEqual({'hypervisor': expected_dict}, result) + self.assertEqual( + 1, self.controller.host_api.get_host_uptime.call_count) def test_show_with_uptime_hypervisor_down(self): - with mock.patch.object( - self.controller.host_api, 'get_host_uptime', - side_effect=exception.ComputeServiceUnavailable(host='dummy') - ) as mock_get_uptime: - req = self._get_request(use_admin_context=True) - hyper_id = self._get_hyper_id() + self.controller.host_api.get_host_uptime.side_effect = ( + exception.ComputeServiceUnavailable(host='dummy')) - result = self.controller.show(req, hyper_id) + req = self._get_request(use_admin_context=True) + hyper_id = self._get_hyper_id() - expected_dict = copy.deepcopy(self.DETAIL_HYPERS_DICTS[0]) - expected_dict.update({'uptime': None}) - self.assertEqual({'hypervisor': expected_dict}, result) - self.assertEqual(1, mock_get_uptime.call_count) + result = self.controller.show(req, hyper_id) + + expected_dict = copy.deepcopy(self.DETAIL_HYPERS_DICTS[0]) + expected_dict.update({'uptime': None}) + self.assertEqual({'hypervisor': expected_dict}, result) + self.assertEqual( + 1, + self.controller.host_api.get_host_uptime.call_count + ) def test_show_old_version(self): # ensure things still work as expected here diff --git a/nova/tests/unit/api/openstack/compute/test_image_metadata.py b/nova/tests/unit/api/openstack/compute/test_image_metadata.py index 2e1c26a712..4072d6f489 100644 --- a/nova/tests/unit/api/openstack/compute/test_image_metadata.py +++ b/nova/tests/unit/api/openstack/compute/test_image_metadata.py @@ -14,8 +14,8 @@ # under the License. import copy +from unittest import mock -import mock from oslo_serialization import jsonutils import webob diff --git a/nova/tests/unit/api/openstack/compute/test_images.py b/nova/tests/unit/api/openstack/compute/test_images.py index fad4fcb5a2..734e755dd5 100644 --- a/nova/tests/unit/api/openstack/compute/test_images.py +++ b/nova/tests/unit/api/openstack/compute/test_images.py @@ -19,9 +19,9 @@ and as a WSGI layer """ import copy +from unittest import mock from urllib import parse as urlparse -import mock import webob from nova.api.openstack.compute import images as images_v21 diff --git a/nova/tests/unit/api/openstack/compute/test_instance_actions.py b/nova/tests/unit/api/openstack/compute/test_instance_actions.py index 04e9ae443e..df13e1d89d 100644 --- a/nova/tests/unit/api/openstack/compute/test_instance_actions.py +++ b/nova/tests/unit/api/openstack/compute/test_instance_actions.py @@ -15,9 +15,9 @@ import copy import datetime +from unittest import mock import iso8601 -import mock from oslo_policy import policy as oslo_policy from oslo_utils.fixture import uuidsentinel as uuids from webob import exc diff --git a/nova/tests/unit/api/openstack/compute/test_keypairs.py b/nova/tests/unit/api/openstack/compute/test_keypairs.py index 657973ffbd..590639d5ed 100644 --- a/nova/tests/unit/api/openstack/compute/test_keypairs.py +++ b/nova/tests/unit/api/openstack/compute/test_keypairs.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + import webob from nova.api.openstack.compute import keypairs as keypairs_v21 @@ -37,6 +38,8 @@ keypair_data = { FAKE_UUID = 'b48316c5-71e8-45e4-9884-6c78055b9b13' +keypair_name_2_92_compatible = 'my-key@ my.host' + def fake_keypair(name): return dict(test_keypair.fake_keypair, @@ -110,16 +113,22 @@ class KeypairsTestV21(test.TestCase): self.assertGreater(len(res_dict['keypair']['private_key']), 0) self._assert_keypair_type(res_dict) - def _test_keypair_create_bad_request_case(self, - body, - exception): - self.assertRaises(exception, - self.controller.create, self.req, body=body) + def _test_keypair_create_bad_request_case( + self, body, exception, error_msg=None + ): + if error_msg: + self.assertRaisesRegex(exception, error_msg, + self.controller.create, + self.req, body=body) + else: + self.assertRaises(exception, + self.controller.create, self.req, body=body) def test_keypair_create_with_empty_name(self): body = {'keypair': {'name': ''}} self._test_keypair_create_bad_request_case(body, - self.validation_error) + self.validation_error, + 'is too short') def test_keypair_create_with_name_too_long(self): body = { @@ -128,7 +137,8 @@ class KeypairsTestV21(test.TestCase): } } self._test_keypair_create_bad_request_case(body, - self.validation_error) + self.validation_error, + 'is too long') def test_keypair_create_with_name_leading_trailing_spaces(self): body = { @@ -136,8 +146,10 @@ class KeypairsTestV21(test.TestCase): 'name': ' test ' } } + expected_msg = 'Can not start or end with whitespace.' self._test_keypair_create_bad_request_case(body, - self.validation_error) + self.validation_error, + expected_msg) def test_keypair_create_with_name_leading_trailing_spaces_compat_mode( self): @@ -152,8 +164,21 @@ class KeypairsTestV21(test.TestCase): 'name': 'test/keypair' } } + expected_msg = 'Only expected characters' self._test_keypair_create_bad_request_case(body, - webob.exc.HTTPBadRequest) + self.validation_error, + expected_msg) + + def test_keypair_create_with_special_characters(self): + body = { + 'keypair': { + 'name': keypair_name_2_92_compatible + } + } + expected_msg = 'Only expected characters' + self._test_keypair_create_bad_request_case(body, + self.validation_error, + expected_msg) def test_keypair_import_bad_key(self): body = { @@ -167,8 +192,10 @@ class KeypairsTestV21(test.TestCase): def test_keypair_create_with_invalid_keypair_body(self): body = {'alpha': {'name': 'create_test'}} + expected_msg = "'keypair' is a required property" self._test_keypair_create_bad_request_case(body, - self.validation_error) + self.validation_error, + expected_msg) def test_keypair_import(self): body = { @@ -228,50 +255,6 @@ class KeypairsTestV21(test.TestCase): self.controller.create, self.req, body=body) self.assertIn('Quota exceeded, too many key pairs.', ex.explanation) - @mock.patch('nova.objects.Quotas.check_deltas') - def test_keypair_create_over_quota_during_recheck(self, mock_check): - # Simulate a race where the first check passes and the recheck fails. - # First check occurs in compute/api. - exc = exception.OverQuota(overs='key_pairs', usages={'key_pairs': 100}) - mock_check.side_effect = [None, exc] - body = { - 'keypair': { - 'name': 'FAKE', - }, - } - - self.assertRaises(webob.exc.HTTPForbidden, - self.controller.create, self.req, body=body) - - ctxt = self.req.environ['nova.context'] - self.assertEqual(2, mock_check.call_count) - call1 = mock.call(ctxt, {'key_pairs': 1}, ctxt.user_id) - call2 = mock.call(ctxt, {'key_pairs': 0}, ctxt.user_id) - mock_check.assert_has_calls([call1, call2]) - - # Verify we removed the key pair that was added after the first - # quota check passed. - key_pairs = objects.KeyPairList.get_by_user(ctxt, ctxt.user_id) - names = [key_pair.name for key_pair in key_pairs] - self.assertNotIn('create_test', names) - - @mock.patch('nova.objects.Quotas.check_deltas') - def test_keypair_create_no_quota_recheck(self, mock_check): - # Disable recheck_quota. - self.flags(recheck_quota=False, group='quota') - - body = { - 'keypair': { - 'name': 'create_test', - }, - } - self.controller.create(self.req, body=body) - - ctxt = self.req.environ['nova.context'] - # check_deltas should have been called only once. - mock_check.assert_called_once_with(ctxt, {'key_pairs': 1}, - ctxt.user_id) - def test_keypair_create_duplicate(self): self.stub_out("nova.objects.KeyPair.create", db_key_pair_create_duplicate) @@ -514,3 +497,82 @@ class KeypairsTestV275(test.TestCase): version='2.75', use_admin_context=True) self.assertRaises(exception.ValidationError, self.controller.delete, req, 1) + + +class KeypairsTestV292(test.TestCase): + wsgi_api_version = '2.92' + wsgi_old_api_version = '2.91' + + def setUp(self): + super(KeypairsTestV292, self).setUp() + self.controller = keypairs_v21.KeypairController() + self.req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + self.old_req = fakes.HTTPRequest.blank( + '', version=self.wsgi_old_api_version) + + def test_keypair_create_no_longer_supported(self): + body = { + 'keypair': { + 'name': keypair_name_2_92_compatible, + } + } + self.assertRaises(exception.ValidationError, self.controller.create, + self.req, body=body) + + def test_keypair_create_works_with_old_version(self): + body = { + 'keypair': { + 'name': 'fake', + } + } + res_dict = self.controller.create(self.old_req, body=body) + self.assertEqual('fake', res_dict['keypair']['name']) + self.assertGreater(len(res_dict['keypair']['private_key']), 0) + + def test_keypair_import_works_with_new_version(self): + body = { + 'keypair': { + 'name': 'fake', + 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBYIznA' + 'x9D7118Q1VKGpXy2HDiKyUTM8XcUuhQpo0srqb9rboUp4' + 'a9NmCwpWpeElDLuva707GOUnfaBAvHBwsRXyxHJjRaI6Y' + 'Qj2oLJwqvaSaWUbyT1vtryRqy6J3TecN0WINY71f4uymi' + 'MZP0wby4bKBcYnac8KiCIlvkEl0ETjkOGUq8OyWRmn7lj' + 'j5SESEUdBP0JnuTFKddWTU/wD6wydeJaUhBTqOlHn0kX1' + 'GyqoNTE1UEhcM5ZRWgfUZfTjVyDF2kGj3vJLCJtJ8LoGc' + 'j7YaN4uPg1rBle+izwE/tLonRrds+cev8p6krSSrxWOwB' + 'bHkXa6OciiJDvkRzJXzf', + } + } + res_dict = self.controller.create(self.req, body=body) + self.assertEqual('fake', res_dict['keypair']['name']) + self.assertNotIn('private_key', res_dict['keypair']) + + def test_keypair_create_refuses_special_chars_with_old_version(self): + body = { + 'keypair': { + 'name': keypair_name_2_92_compatible, + } + } + self.assertRaises(exception.ValidationError, self.controller.create, + self.old_req, body=body) + + def test_keypair_import_with_special_characters(self): + body = { + 'keypair': { + 'name': keypair_name_2_92_compatible, + 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBYIznA' + 'x9D7118Q1VKGpXy2HDiKyUTM8XcUuhQpo0srqb9rboUp4' + 'a9NmCwpWpeElDLuva707GOUnfaBAvHBwsRXyxHJjRaI6Y' + 'Qj2oLJwqvaSaWUbyT1vtryRqy6J3TecN0WINY71f4uymi' + 'MZP0wby4bKBcYnac8KiCIlvkEl0ETjkOGUq8OyWRmn7lj' + 'j5SESEUdBP0JnuTFKddWTU/wD6wydeJaUhBTqOlHn0kX1' + 'GyqoNTE1UEhcM5ZRWgfUZfTjVyDF2kGj3vJLCJtJ8LoGc' + 'j7YaN4uPg1rBle+izwE/tLonRrds+cev8p6krSSrxWOwB' + 'bHkXa6OciiJDvkRzJXzf', + } + } + + res_dict = self.controller.create(self.req, body=body) + self.assertEqual(keypair_name_2_92_compatible, + res_dict['keypair']['name']) diff --git a/nova/tests/unit/api/openstack/compute/test_limits.py b/nova/tests/unit/api/openstack/compute/test_limits.py index 31033e111d..1748023aa8 100644 --- a/nova/tests/unit/api/openstack/compute/test_limits.py +++ b/nova/tests/unit/api/openstack/compute/test_limits.py @@ -19,8 +19,9 @@ Tests dealing with HTTP rate-limiting. from http import client as httplib from io import StringIO +from unittest import mock -import mock +from oslo_limit import fixture as limit_fixture from oslo_serialization import jsonutils from oslo_utils import encodeutils @@ -29,8 +30,10 @@ from nova.api.openstack.compute import views from nova.api.openstack import wsgi import nova.context from nova import exception +from nova.limit import local as local_limit +from nova.limit import placement as placement_limit +from nova import objects from nova.policies import limits as l_policies -from nova import quota from nova import test from nova.tests.unit.api.openstack import fakes from nova.tests.unit import matchers @@ -48,12 +51,12 @@ class BaseLimitTestSuite(test.NoDBTestCase): return {k: dict(limit=v, in_use=v // 2) for k, v in self.absolute_limits.items()} - mock_get_project_quotas = mock.patch.object( + patcher_get_project_quotas = mock.patch.object( nova.quota.QUOTAS, "get_project_quotas", - side_effect = stub_get_project_quotas) - mock_get_project_quotas.start() - self.addCleanup(mock_get_project_quotas.stop) + side_effect=stub_get_project_quotas) + self.mock_get_project_quotas = patcher_get_project_quotas.start() + self.addCleanup(patcher_get_project_quotas.stop) patcher = self.mock_can = mock.patch('nova.context.RequestContext.can') self.mock_can = patcher.start() self.addCleanup(patcher.stop) @@ -150,16 +153,14 @@ class LimitsControllerTestV21(BaseLimitTestSuite): return {k: dict(limit=v, in_use=v // 2) for k, v in self.absolute_limits.items()} - with mock.patch('nova.quota.QUOTAS.get_project_quotas') as \ - get_project_quotas: - get_project_quotas.side_effect = _get_project_quotas + self.mock_get_project_quotas.side_effect = _get_project_quotas - response = request.get_response(self.controller) + response = request.get_response(self.controller) - body = jsonutils.loads(response.body) - self.assertEqual(expected, body) - get_project_quotas.assert_called_once_with(context, tenant_id, - usages=True) + body = jsonutils.loads(response.body) + self.assertEqual(expected, body) + self.mock_get_project_quotas.assert_called_once_with( + context, tenant_id, usages=True) def _do_test_used_limits(self, reserved): request = self._get_index_request(tenant_id=None) @@ -182,8 +183,7 @@ class LimitsControllerTestV21(BaseLimitTestSuite): def stub_get_project_quotas(context, project_id, usages=True): return limits - self.stub_out('nova.quota.QUOTAS.get_project_quotas', - stub_get_project_quotas) + self.mock_get_project_quotas.side_effect = stub_get_project_quotas res = request.get_response(self.controller) body = jsonutils.loads(res.body) @@ -207,15 +207,15 @@ class LimitsControllerTestV21(BaseLimitTestSuite): user_id=user_id, project_id=project_id) context = fake_req.environ["nova.context"] - with mock.patch.object(quota.QUOTAS, 'get_project_quotas', - return_value={}) as mock_get_quotas: - fake_req.get_response(self.controller) - self.assertEqual(2, self.mock_can.call_count) - self.mock_can.assert_called_with( - l_policies.OTHER_PROJECT_LIMIT_POLICY_NAME, - target={"project_id": tenant_id}) - mock_get_quotas.assert_called_once_with(context, - tenant_id, usages=True) + self.mock_get_project_quotas.side_effect = None + self.mock_get_project_quotas.return_value = {} + + fake_req.get_response(self.controller) + self.assertEqual(2, self.mock_can.call_count) + self.mock_can.assert_called_with( + l_policies.OTHER_PROJECT_LIMIT_POLICY_NAME) + self.mock_get_project_quotas.assert_called_once_with(context, + tenant_id, usages=True) def _test_admin_can_fetch_used_limits_for_own_project(self, req_get): project_id = "123456" @@ -227,11 +227,12 @@ class LimitsControllerTestV21(BaseLimitTestSuite): project_id=project_id) context = fake_req.environ["nova.context"] - with mock.patch.object(quota.QUOTAS, 'get_project_quotas', - return_value={}) as mock_get_quotas: - fake_req.get_response(self.controller) - mock_get_quotas.assert_called_once_with(context, - project_id, usages=True) + self.mock_get_project_quotas.side_effect = None + self.mock_get_project_quotas.return_value = {} + + fake_req.get_response(self.controller) + self.mock_get_project_quotas.assert_called_once_with( + context, project_id, usages=True) def test_admin_can_fetch_used_limits_for_own_project(self): req_get = {} @@ -251,7 +252,7 @@ class LimitsControllerTestV21(BaseLimitTestSuite): req_get = {'tenant_id': -1} self._test_admin_can_fetch_used_limits_for_own_project(req_get) - def test_admin_can_fetch_used_limits_with_unkown_param(self): + def test_admin_can_fetch_used_limits_with_unknown_param(self): req_get = {'tenant_id': '123', 'unknown': 'unknown'} self._test_admin_can_fetch_used_limits_for_own_project(req_get) @@ -259,12 +260,13 @@ class LimitsControllerTestV21(BaseLimitTestSuite): project_id = "123456" fake_req = self._get_index_request(project_id=project_id) context = fake_req.environ["nova.context"] - with mock.patch.object(quota.QUOTAS, 'get_project_quotas', - return_value={}) as mock_get_quotas: - fake_req.get_response(self.controller) + self.mock_get_project_quotas.side_effect = None + self.mock_get_project_quotas.return_value = {} - mock_get_quotas.assert_called_once_with(context, - project_id, usages=True) + fake_req.get_response(self.controller) + + self.mock_get_project_quotas.assert_called_once_with( + context, project_id, usages=True) def test_used_ram_added(self): fake_req = self._get_index_request() @@ -272,28 +274,26 @@ class LimitsControllerTestV21(BaseLimitTestSuite): def stub_get_project_quotas(context, project_id, usages=True): return {'ram': {'limit': 512, 'in_use': 256}} - with mock.patch.object(quota.QUOTAS, 'get_project_quotas', - side_effect=stub_get_project_quotas - ) as mock_get_quotas: + self.mock_get_project_quotas.side_effect = stub_get_project_quotas - res = fake_req.get_response(self.controller) - body = jsonutils.loads(res.body) - abs_limits = body['limits']['absolute'] - self.assertIn('totalRAMUsed', abs_limits) - self.assertEqual(256, abs_limits['totalRAMUsed']) - self.assertEqual(1, mock_get_quotas.call_count) + res = fake_req.get_response(self.controller) + body = jsonutils.loads(res.body) + abs_limits = body['limits']['absolute'] + self.assertIn('totalRAMUsed', abs_limits) + self.assertEqual(256, abs_limits['totalRAMUsed']) + self.assertEqual(1, self.mock_get_project_quotas.call_count) def test_no_ram_quota(self): fake_req = self._get_index_request() - with mock.patch.object(quota.QUOTAS, 'get_project_quotas', - return_value={}) as mock_get_quotas: + self.mock_get_project_quotas.side_effect = None + self.mock_get_project_quotas.return_value = {} - res = fake_req.get_response(self.controller) - body = jsonutils.loads(res.body) - abs_limits = body['limits']['absolute'] - self.assertNotIn('totalRAMUsed', abs_limits) - self.assertEqual(1, mock_get_quotas.call_count) + res = fake_req.get_response(self.controller) + body = jsonutils.loads(res.body) + abs_limits = body['limits']['absolute'] + self.assertNotIn('totalRAMUsed', abs_limits) + self.assertEqual(1, self.mock_get_project_quotas.call_count) class FakeHttplibSocket(object): @@ -395,25 +395,24 @@ class LimitsControllerTestV236(BaseLimitTestSuite): return {k: dict(limit=v, in_use=v // 2) for k, v in absolute_limits.items()} - with mock.patch('nova.quota.QUOTAS.get_project_quotas') as \ - get_project_quotas: - get_project_quotas.side_effect = _get_project_quotas - response = self.controller.index(self.req) - expected_response = { - "limits": { - "rate": [], - "absolute": { - "maxTotalRAMSize": 512, - "maxTotalInstances": 5, - "maxTotalCores": 21, - "maxTotalKeypairs": 10, - "totalRAMUsed": 256, - "totalCoresUsed": 10, - "totalInstancesUsed": 2, - }, + self.mock_get_project_quotas.side_effect = _get_project_quotas + + response = self.controller.index(self.req) + expected_response = { + "limits": { + "rate": [], + "absolute": { + "maxTotalRAMSize": 512, + "maxTotalInstances": 5, + "maxTotalCores": 21, + "maxTotalKeypairs": 10, + "totalRAMUsed": 256, + "totalCoresUsed": 10, + "totalInstancesUsed": 2, }, - } - self.assertEqual(expected_response, response) + }, + } + self.assertEqual(expected_response, response) class LimitsControllerTestV239(BaseLimitTestSuite): @@ -433,21 +432,20 @@ class LimitsControllerTestV239(BaseLimitTestSuite): return {k: dict(limit=v, in_use=v // 2) for k, v in absolute_limits.items()} - with mock.patch('nova.quota.QUOTAS.get_project_quotas') as \ - get_project_quotas: - get_project_quotas.side_effect = _get_project_quotas - response = self.controller.index(self.req) - # staring from version 2.39 there is no 'maxImageMeta' field - # in response after removing 'image-metadata' proxy API - expected_response = { - "limits": { - "rate": [], - "absolute": { - "maxServerMeta": 1, - }, + self.mock_get_project_quotas.side_effect = _get_project_quotas + + response = self.controller.index(self.req) + # starting from version 2.39 there is no 'maxImageMeta' field + # in response after removing 'image-metadata' proxy API + expected_response = { + "limits": { + "rate": [], + "absolute": { + "maxServerMeta": 1, }, - } - self.assertEqual(expected_response, response) + }, + } + self.assertEqual(expected_response, response) class LimitsControllerTestV275(BaseLimitTestSuite): @@ -459,21 +457,170 @@ class LimitsControllerTestV275(BaseLimitTestSuite): absolute_limits = { "metadata_items": 1, } - req = fakes.HTTPRequest.blank("/?unkown=fake", + req = fakes.HTTPRequest.blank("/?unknown=fake", version='2.74') def _get_project_quotas(context, project_id, usages=True): return {k: dict(limit=v, in_use=v // 2) for k, v in absolute_limits.items()} - with mock.patch('nova.quota.QUOTAS.get_project_quotas') as \ - get_project_quotas: - get_project_quotas.side_effect = _get_project_quotas - self.controller.index(req) + self.mock_get_project_quotas.side_effect = _get_project_quotas + self.controller.index(req) + self.controller.index(req) def test_index_additional_query_param(self): - req = fakes.HTTPRequest.blank("/?unkown=fake", + req = fakes.HTTPRequest.blank("/?unknown=fake", version='2.75') self.assertRaises( exception.ValidationError, self.controller.index, req=req) + + +class NoopLimitsControllerTest(test.NoDBTestCase): + quota_driver = "nova.quota.NoopQuotaDriver" + + def setUp(self): + super(NoopLimitsControllerTest, self).setUp() + self.flags(driver=self.quota_driver, group="quota") + self.controller = limits_v21.LimitsController() + # remove policy checks + patcher = self.mock_can = mock.patch('nova.context.RequestContext.can') + self.mock_can = patcher.start() + self.addCleanup(patcher.stop) + + def test_index_v21(self): + req = fakes.HTTPRequest.blank("/") + response = self.controller.index(req) + expected_response = { + "limits": { + "rate": [], + "absolute": { + 'maxImageMeta': -1, + 'maxPersonality': -1, + 'maxPersonalitySize': -1, + 'maxSecurityGroupRules': -1, + 'maxSecurityGroups': -1, + 'maxServerGroupMembers': -1, + 'maxServerGroups': -1, + 'maxServerMeta': -1, + 'maxTotalCores': -1, + 'maxTotalFloatingIps': -1, + 'maxTotalInstances': -1, + 'maxTotalKeypairs': -1, + 'maxTotalRAMSize': -1, + 'totalCoresUsed': -1, + 'totalFloatingIpsUsed': -1, + 'totalInstancesUsed': -1, + 'totalRAMUsed': -1, + 'totalSecurityGroupsUsed': -1, + 'totalServerGroupsUsed': -1, + }, + }, + } + self.assertEqual(expected_response, response) + + def test_index_v275(self): + req = fakes.HTTPRequest.blank("/?tenant_id=faketenant", + version='2.75') + response = self.controller.index(req) + expected_response = { + "limits": { + "rate": [], + "absolute": { + 'maxServerGroupMembers': -1, + 'maxServerGroups': -1, + 'maxServerMeta': -1, + 'maxTotalCores': -1, + 'maxTotalInstances': -1, + 'maxTotalKeypairs': -1, + 'maxTotalRAMSize': -1, + 'totalCoresUsed': -1, + 'totalInstancesUsed': -1, + 'totalRAMUsed': -1, + 'totalServerGroupsUsed': -1, + }, + }, + } + self.assertEqual(expected_response, response) + + +class UnifiedLimitsControllerTest(NoopLimitsControllerTest): + quota_driver = "nova.quota.UnifiedLimitsDriver" + + def setUp(self): + super(UnifiedLimitsControllerTest, self).setUp() + reglimits = {local_limit.SERVER_METADATA_ITEMS: 128, + local_limit.INJECTED_FILES: 5, + local_limit.INJECTED_FILES_CONTENT: 10 * 1024, + local_limit.INJECTED_FILES_PATH: 255, + local_limit.KEY_PAIRS: 100, + local_limit.SERVER_GROUPS: 12, + local_limit.SERVER_GROUP_MEMBERS: 10} + self.useFixture(limit_fixture.LimitFixture(reglimits, {})) + + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") + @mock.patch.object(objects.InstanceGroupList, "get_counts") + def test_index_v21(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} + mock_count.return_value = {'project': {'server_groups': 9}} + req = fakes.HTTPRequest.blank("/") + response = self.controller.index(req) + expected_response = { + "limits": { + "rate": [], + "absolute": { + 'maxImageMeta': 128, + 'maxPersonality': 5, + 'maxPersonalitySize': 10240, + 'maxSecurityGroupRules': -1, + 'maxSecurityGroups': -1, + 'maxServerGroupMembers': 10, + 'maxServerGroups': 12, + 'maxServerMeta': 128, + 'maxTotalCores': 2, + 'maxTotalFloatingIps': -1, + 'maxTotalInstances': 1, + 'maxTotalKeypairs': 100, + 'maxTotalRAMSize': 3, + 'totalCoresUsed': 5, + 'totalFloatingIpsUsed': 0, + 'totalInstancesUsed': 4, + 'totalRAMUsed': 6, + 'totalSecurityGroupsUsed': 0, + 'totalServerGroupsUsed': 9, + }, + }, + } + self.assertEqual(expected_response, response) + + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") + @mock.patch.object(objects.InstanceGroupList, "get_counts") + def test_index_v275(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} + mock_count.return_value = {'project': {'server_groups': 9}} + req = fakes.HTTPRequest.blank("/?tenant_id=faketenant", + version='2.75') + response = self.controller.index(req) + expected_response = { + "limits": { + "rate": [], + "absolute": { + 'maxServerGroupMembers': 10, + 'maxServerGroups': 12, + 'maxServerMeta': 128, + 'maxTotalCores': 2, + 'maxTotalInstances': 1, + 'maxTotalKeypairs': 100, + 'maxTotalRAMSize': 3, + 'totalCoresUsed': 5, + 'totalInstancesUsed': 4, + 'totalRAMUsed': 6, + 'totalServerGroupsUsed': 9, + }, + }, + } + self.assertEqual(expected_response, response) diff --git a/nova/tests/unit/api/openstack/compute/test_lock_server.py b/nova/tests/unit/api/openstack/compute/test_lock_server.py index a605e2bcdb..bf49bf2b73 100644 --- a/nova/tests/unit/api/openstack/compute/test_lock_server.py +++ b/nova/tests/unit/api/openstack/compute/test_lock_server.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock from nova.api.openstack import api_version_request from nova.api.openstack import common @@ -114,7 +114,7 @@ class LockServerTestsV273(LockServerTestsV21): self.controller._lock, self.req, instance.uuid, body=body) self.assertIn("256 is not of type 'string'", str(exp)) - def test_lock_with_invalid_paramater(self): + def test_lock_with_invalid_parameter(self): # This will fail from 2.73 since we have a schema check that allows # only locked_reason instance = fake_instance.fake_instance_obj( diff --git a/nova/tests/unit/api/openstack/compute/test_microversions.py b/nova/tests/unit/api/openstack/compute/test_microversions.py index c5b1ddb5e5..9f5dd90889 100644 --- a/nova/tests/unit/api/openstack/compute/test_microversions.py +++ b/nova/tests/unit/api/openstack/compute/test_microversions.py @@ -12,7 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_serialization import jsonutils from nova.api.openstack import api_version_request as api_version diff --git a/nova/tests/unit/api/openstack/compute/test_migrate_server.py b/nova/tests/unit/api/openstack/compute/test_migrate_server.py index 683759eccc..8d1c853206 100644 --- a/nova/tests/unit/api/openstack/compute/test_migrate_server.py +++ b/nova/tests/unit/api/openstack/compute/test_migrate_server.py @@ -13,8 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + import fixtures -import mock from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import uuidutils import webob @@ -530,9 +531,8 @@ class MigrateServerTestsV256(MigrateServerTestsV234): self.req, fakes.FAKE_UUID, body=body) def _test_migrate_exception(self, exc_info, expected_result): - @mock.patch.object(self.compute_api, 'get') @mock.patch.object(self.compute_api, 'resize', side_effect=exc_info) - def _test(mock_resize, mock_get): + def _test(mock_resize): instance = objects.Instance(uuid=uuids.instance) self.assertRaises(expected_result, self.controller._migrate, diff --git a/nova/tests/unit/api/openstack/compute/test_migrations.py b/nova/tests/unit/api/openstack/compute/test_migrations.py index a06d395bea..19bc42a9de 100644 --- a/nova/tests/unit/api/openstack/compute/test_migrations.py +++ b/nova/tests/unit/api/openstack/compute/test_migrations.py @@ -13,9 +13,9 @@ # under the License. import datetime +from unittest import mock import iso8601 -import mock from oslo_utils.fixture import uuidsentinel as uuids from webob import exc diff --git a/nova/tests/unit/api/openstack/compute/test_multinic.py b/nova/tests/unit/api/openstack/compute/test_multinic.py index ceaaebf373..17a872fed2 100644 --- a/nova/tests/unit/api/openstack/compute/test_multinic.py +++ b/nova/tests/unit/api/openstack/compute/test_multinic.py @@ -13,8 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + import fixtures -import mock import webob from nova.api.openstack.compute import multinic as multinic_v21 diff --git a/nova/tests/unit/api/openstack/compute/test_networks.py b/nova/tests/unit/api/openstack/compute/test_networks.py index 595353e7b1..bcbce58483 100644 --- a/nova/tests/unit/api/openstack/compute/test_networks.py +++ b/nova/tests/unit/api/openstack/compute/test_networks.py @@ -26,7 +26,7 @@ from nova import test from nova.tests.unit.api.openstack import fakes -# NOTE(stephenfin): obviously these aren't complete reponses, but this is all +# NOTE(stephenfin): obviously these aren't complete responses, but this is all # we care about FAKE_NETWORKS = [ { diff --git a/nova/tests/unit/api/openstack/compute/test_quota_classes.py b/nova/tests/unit/api/openstack/compute/test_quota_classes.py index bdb33a7e1a..463f8344c0 100644 --- a/nova/tests/unit/api/openstack/compute/test_quota_classes.py +++ b/nova/tests/unit/api/openstack/compute/test_quota_classes.py @@ -12,12 +12,19 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + import copy +from unittest import mock + +from oslo_limit import fixture as limit_fixture import webob from nova.api.openstack.compute import quota_classes \ as quota_classes_v21 from nova import exception +from nova.limit import local as local_limit +from nova.limit import placement as placement_limit +from nova import objects from nova import test from nova.tests.unit.api.openstack import fakes @@ -156,3 +163,220 @@ class QuotaClassSetsTestV257(QuotaClassSetsTestV250): for resource in quota_classes_v21.FILTERED_QUOTAS_2_57: self.quota_resources.pop(resource, None) self.filtered_quotas.extend(quota_classes_v21.FILTERED_QUOTAS_2_57) + + +class NoopQuotaClassesTest(test.NoDBTestCase): + quota_driver = "nova.quota.NoopQuotaDriver" + + def setUp(self): + super(NoopQuotaClassesTest, self).setUp() + self.flags(driver=self.quota_driver, group="quota") + self.controller = quota_classes_v21.QuotaClassSetsController() + + def test_show_v21(self): + req = fakes.HTTPRequest.blank("") + response = self.controller.show(req, "test_class") + expected_response = { + 'quota_class_set': { + 'id': 'test_class', + 'cores': -1, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': -1, + 'injected_file_path_bytes': -1, + 'injected_files': -1, + 'instances': -1, + 'key_pairs': -1, + 'metadata_items': -1, + 'ram': -1, + 'security_group_rules': -1, + 'security_groups': -1 + } + } + self.assertEqual(expected_response, response) + + def test_show_v257(self): + req = fakes.HTTPRequest.blank("", version='2.57') + response = self.controller.show(req, "default") + expected_response = { + 'quota_class_set': { + 'id': 'default', + 'cores': -1, + 'instances': -1, + 'key_pairs': -1, + 'metadata_items': -1, + 'ram': -1, + 'server_group_members': -1, + 'server_groups': -1, + } + } + self.assertEqual(expected_response, response) + + def test_update_v21_still_rejects_badrequests(self): + req = fakes.HTTPRequest.blank("") + body = {'quota_class_set': {'instances': 50, 'cores': 50, + 'ram': 51200, 'unsupported': 12}} + self.assertRaises(exception.ValidationError, self.controller.update, + req, 'test_class', body=body) + + @mock.patch.object(objects.Quotas, "update_class") + def test_update_v21(self, mock_update): + req = fakes.HTTPRequest.blank("") + body = {'quota_class_set': {'ram': 51200}} + response = self.controller.update(req, 'default', body=body) + expected_response = { + 'quota_class_set': { + 'cores': -1, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': -1, + 'injected_file_path_bytes': -1, + 'injected_files': -1, + 'instances': -1, + 'key_pairs': -1, + 'metadata_items': -1, + 'ram': -1, + 'security_group_rules': -1, + 'security_groups': -1 + } + } + self.assertEqual(expected_response, response) + mock_update.assert_called_once_with(req.environ['nova.context'], + "default", "ram", 51200) + + @mock.patch.object(objects.Quotas, "update_class") + def test_update_v257(self, mock_update): + req = fakes.HTTPRequest.blank("", version='2.57') + body = {'quota_class_set': {'ram': 51200}} + response = self.controller.update(req, 'default', body=body) + expected_response = { + 'quota_class_set': { + 'cores': -1, + 'instances': -1, + 'key_pairs': -1, + 'metadata_items': -1, + 'ram': -1, + 'server_group_members': -1, + 'server_groups': -1, + } + } + self.assertEqual(expected_response, response) + mock_update.assert_called_once_with(req.environ['nova.context'], + "default", "ram", 51200) + + +class UnifiedLimitsQuotaClassesTest(NoopQuotaClassesTest): + quota_driver = "nova.quota.UnifiedLimitsDriver" + + def setUp(self): + super(UnifiedLimitsQuotaClassesTest, self).setUp() + # Set server_groups so all config options get a different value + # but we also test as much as possible with the default config + self.flags(driver="nova.quota.UnifiedLimitsDriver", group='quota') + reglimits = {local_limit.SERVER_METADATA_ITEMS: 128, + local_limit.INJECTED_FILES: 5, + local_limit.INJECTED_FILES_CONTENT: 10 * 1024, + local_limit.INJECTED_FILES_PATH: 255, + local_limit.KEY_PAIRS: 100, + local_limit.SERVER_GROUPS: 12, + local_limit.SERVER_GROUP_MEMBERS: 10} + self.useFixture(limit_fixture.LimitFixture(reglimits, {})) + + @mock.patch.object(placement_limit, "get_legacy_default_limits") + def test_show_v21(self, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} + req = fakes.HTTPRequest.blank("") + response = self.controller.show(req, "test_class") + expected_response = { + 'quota_class_set': { + 'id': 'test_class', + 'cores': 2, + 'fixed_ips': -1, + 'floating_ips': -1, + 'ram': 3, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'injected_files': 5, + 'instances': 1, + 'key_pairs': 100, + 'metadata_items': 128, + 'security_group_rules': -1, + 'security_groups': -1, + } + } + self.assertEqual(expected_response, response) + + @mock.patch.object(placement_limit, "get_legacy_default_limits") + def test_show_v257(self, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} + req = fakes.HTTPRequest.blank("", version='2.57') + response = self.controller.show(req, "default") + expected_response = { + 'quota_class_set': { + 'id': 'default', + 'cores': 2, + 'instances': 1, + 'ram': 3, + 'key_pairs': 100, + 'metadata_items': 128, + 'server_group_members': 10, + 'server_groups': 12, + } + } + self.assertEqual(expected_response, response) + + def test_update_still_rejects_badrequests(self): + req = fakes.HTTPRequest.blank("") + body = {'quota_class_set': {'instances': 50, 'cores': 50, + 'ram': 51200, 'unsupported': 12}} + self.assertRaises(exception.ValidationError, self.controller.update, + req, 'test_class', body=body) + + @mock.patch.object(placement_limit, "get_legacy_default_limits") + @mock.patch.object(objects.Quotas, "update_class") + def test_update_v21(self, mock_update, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} + req = fakes.HTTPRequest.blank("") + body = {'quota_class_set': {'ram': 51200}} + response = self.controller.update(req, 'default', body=body) + expected_response = { + 'quota_class_set': { + 'cores': 2, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'injected_files': 5, + 'instances': 1, + 'key_pairs': 100, + 'metadata_items': 128, + 'ram': 3, + 'security_group_rules': -1, + 'security_groups': -1 + } + } + self.assertEqual(expected_response, response) + # TODO(johngarbutt) we should be proxying to keystone + self.assertEqual(0, mock_update.call_count) + + @mock.patch.object(placement_limit, "get_legacy_default_limits") + @mock.patch.object(objects.Quotas, "update_class") + def test_update_v257(self, mock_update, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} + req = fakes.HTTPRequest.blank("", version='2.57') + body = {'quota_class_set': {'ram': 51200}} + response = self.controller.update(req, 'default', body=body) + expected_response = { + 'quota_class_set': { + 'cores': 2, + 'instances': 1, + 'ram': 3, + 'key_pairs': 100, + 'metadata_items': 128, + 'server_group_members': 10, + 'server_groups': 12, + } + } + self.assertEqual(expected_response, response) + # TODO(johngarbutt) we should be proxying to keystone + self.assertEqual(0, mock_update.call_count) diff --git a/nova/tests/unit/api/openstack/compute/test_quotas.py b/nova/tests/unit/api/openstack/compute/test_quotas.py index 545bd51e13..0a1bbd08d8 100644 --- a/nova/tests/unit/api/openstack/compute/test_quotas.py +++ b/nova/tests/unit/api/openstack/compute/test_quotas.py @@ -14,12 +14,18 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + +from oslo_limit import fixture as limit_fixture +from oslo_utils.fixture import uuidsentinel as uuids import webob from nova.api.openstack.compute import quota_sets as quotas_v21 from nova.db import constants as db_const from nova import exception +from nova.limit import local as local_limit +from nova.limit import placement as placement_limit +from nova import objects from nova import quota from nova import test from nova.tests.unit.api.openstack import fakes @@ -660,3 +666,475 @@ class QuotaSetsTestV275(QuotaSetsTestV257): query_string=query_string) self.assertRaises(exception.ValidationError, self.controller.delete, req, 1234) + + +class NoopQuotaSetsTest(test.NoDBTestCase): + quota_driver = "nova.quota.NoopQuotaDriver" + expected_detail = {'in_use': -1, 'limit': -1, 'reserved': -1} + + def setUp(self): + super(NoopQuotaSetsTest, self).setUp() + self.flags(driver=self.quota_driver, group="quota") + self.controller = quotas_v21.QuotaSetsController() + self.stub_out('nova.api.openstack.identity.verify_project_id', + lambda ctx, project_id: True) + + def test_show_v21(self): + req = fakes.HTTPRequest.blank("") + response = self.controller.show(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': -1, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': -1, + 'injected_file_path_bytes': -1, + 'injected_files': -1, + 'instances': -1, + 'key_pairs': -1, + 'metadata_items': -1, + 'ram': -1, + 'security_group_rules': -1, + 'security_groups': -1, + 'server_group_members': -1, + 'server_groups': -1, + } + } + self.assertEqual(expected_response, response) + + def test_show_v257(self): + req = fakes.HTTPRequest.blank("", version='2.57') + response = self.controller.show(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': -1, + 'instances': -1, + 'key_pairs': -1, + 'metadata_items': -1, + 'ram': -1, + 'server_group_members': -1, + 'server_groups': -1}} + self.assertEqual(expected_response, response) + + def test_detail_v21(self): + req = fakes.HTTPRequest.blank("") + response = self.controller.detail(req, uuids.project_id) + + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': self.expected_detail, + 'fixed_ips': self.expected_detail, + 'floating_ips': self.expected_detail, + 'injected_file_content_bytes': self.expected_detail, + 'injected_file_path_bytes': self.expected_detail, + 'injected_files': self.expected_detail, + 'instances': self.expected_detail, + 'key_pairs': self.expected_detail, + 'metadata_items': self.expected_detail, + 'ram': self.expected_detail, + 'security_group_rules': self.expected_detail, + 'security_groups': self.expected_detail, + 'server_group_members': self.expected_detail, + 'server_groups': self.expected_detail, + } + } + self.assertEqual(expected_response, response) + + def test_detail_v21_user(self): + req = fakes.HTTPRequest.blank("?user_id=42") + response = self.controller.detail(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': self.expected_detail, + 'fixed_ips': self.expected_detail, + 'floating_ips': self.expected_detail, + 'injected_file_content_bytes': self.expected_detail, + 'injected_file_path_bytes': self.expected_detail, + 'injected_files': self.expected_detail, + 'instances': self.expected_detail, + 'key_pairs': self.expected_detail, + 'metadata_items': self.expected_detail, + 'ram': self.expected_detail, + 'security_group_rules': self.expected_detail, + 'security_groups': self.expected_detail, + 'server_group_members': self.expected_detail, + 'server_groups': self.expected_detail, + } + } + self.assertEqual(expected_response, response) + + def test_update_still_rejects_badrequests(self): + req = fakes.HTTPRequest.blank("") + body = {'quota_set': {'instances': 50, 'cores': 50, + 'ram': 51200, 'unsupported': 12}} + self.assertRaises(exception.ValidationError, self.controller.update, + req, uuids.project_id, body=body) + + @mock.patch.object(objects.Quotas, "create_limit") + def test_update_v21(self, mock_create): + req = fakes.HTTPRequest.blank("") + body = {'quota_set': {'server_groups': 2}} + response = self.controller.update(req, uuids.project_id, body=body) + expected_response = { + 'quota_set': { + 'cores': -1, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': -1, + 'injected_file_path_bytes': -1, + 'injected_files': -1, + 'instances': -1, + 'key_pairs': -1, + 'metadata_items': -1, + 'ram': -1, + 'security_group_rules': -1, + 'security_groups': -1, + 'server_group_members': -1, + 'server_groups': -1, + } + } + self.assertEqual(expected_response, response) + mock_create.assert_called_once_with(req.environ['nova.context'], + uuids.project_id, "server_groups", + 2, user_id=None) + + @mock.patch.object(objects.Quotas, "create_limit") + def test_update_v21_user(self, mock_create): + req = fakes.HTTPRequest.blank("?user_id=42") + body = {'quota_set': {'key_pairs': 52}} + response = self.controller.update(req, uuids.project_id, body=body) + expected_response = { + 'quota_set': { + 'cores': -1, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': -1, + 'injected_file_path_bytes': -1, + 'injected_files': -1, + 'instances': -1, + 'key_pairs': -1, + 'metadata_items': -1, + 'ram': -1, + 'security_group_rules': -1, + 'security_groups': -1, + 'server_group_members': -1, + 'server_groups': -1, + } + } + self.assertEqual(expected_response, response) + mock_create.assert_called_once_with(req.environ['nova.context'], + uuids.project_id, "key_pairs", 52, + user_id="42") + + def test_defaults_v21(self): + req = fakes.HTTPRequest.blank("") + response = self.controller.defaults(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': -1, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': -1, + 'injected_file_path_bytes': -1, + 'injected_files': -1, + 'instances': -1, + 'key_pairs': -1, + 'metadata_items': -1, + 'ram': -1, + 'security_group_rules': -1, + 'security_groups': -1, + 'server_group_members': -1, + 'server_groups': -1, + } + } + self.assertEqual(expected_response, response) + + @mock.patch('nova.objects.Quotas.destroy_all_by_project') + def test_quotas_delete(self, mock_destroy_all_by_project): + req = fakes.HTTPRequest.blank("") + self.controller.delete(req, "1234") + mock_destroy_all_by_project.assert_called_once_with( + req.environ['nova.context'], "1234") + + @mock.patch('nova.objects.Quotas.destroy_all_by_project_and_user') + def test_user_quotas_delete(self, mock_destroy_all_by_user): + req = fakes.HTTPRequest.blank("?user_id=42") + self.controller.delete(req, "1234") + mock_destroy_all_by_user.assert_called_once_with( + req.environ['nova.context'], "1234", "42") + + +class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): + quota_driver = "nova.quota.UnifiedLimitsDriver" + # this matches what the db driver returns + expected_detail = {'in_use': 0, 'limit': -1, 'reserved': 0} + + def setUp(self): + super(UnifiedLimitsQuotaSetsTest, self).setUp() + reglimits = {local_limit.SERVER_METADATA_ITEMS: 128, + local_limit.INJECTED_FILES: 5, + local_limit.INJECTED_FILES_CONTENT: 10 * 1024, + local_limit.INJECTED_FILES_PATH: 255, + local_limit.KEY_PAIRS: 100, + local_limit.SERVER_GROUPS: 12, + local_limit.SERVER_GROUP_MEMBERS: 10} + self.limit_fixture = self.useFixture( + limit_fixture.LimitFixture(reglimits, {})) + + @mock.patch.object(placement_limit, "get_legacy_project_limits") + def test_show_v21(self, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + req = fakes.HTTPRequest.blank("") + response = self.controller.show(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': 2, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'injected_files': 5, + 'instances': 1, + 'key_pairs': 100, + 'metadata_items': 128, + 'ram': 3, + 'security_group_rules': -1, + 'security_groups': -1, + 'server_group_members': 10, + 'server_groups': 12, + } + } + self.assertEqual(expected_response, response) + + @mock.patch.object(placement_limit, "get_legacy_project_limits") + def test_show_v257(self, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + req = fakes.HTTPRequest.blank("", version='2.57') + response = self.controller.show(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': 2, + 'instances': 1, + 'key_pairs': 100, + 'metadata_items': 128, + 'ram': 3, + 'server_group_members': 10, + 'server_groups': 12}} + self.assertEqual(expected_response, response) + + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") + @mock.patch.object(objects.InstanceGroupList, "get_counts") + def test_detail_v21(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} + mock_count.return_value = {'project': {'server_groups': 9}} + req = fakes.HTTPRequest.blank("") + response = self.controller.detail(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': { + 'in_use': 5, 'limit': 2, 'reserved': 0}, + 'fixed_ips': self.expected_detail, + 'floating_ips': self.expected_detail, + 'injected_file_content_bytes': { + 'in_use': 0, 'limit': 10240, 'reserved': 0}, + 'injected_file_path_bytes': { + 'in_use': 0, 'limit': 255, 'reserved': 0}, + 'injected_files': { + 'in_use': 0, 'limit': 5, 'reserved': 0}, + 'instances': { + 'in_use': 4, 'limit': 1, 'reserved': 0}, + 'key_pairs': { + 'in_use': 0, 'limit': 100, 'reserved': 0}, + 'metadata_items': { + 'in_use': 0, 'limit': 128, 'reserved': 0}, + 'ram': { + 'in_use': 6, 'limit': 3, 'reserved': 0}, + 'security_group_rules': self.expected_detail, + 'security_groups': self.expected_detail, + 'server_group_members': { + 'in_use': 0, 'limit': 10, 'reserved': 0}, + 'server_groups': { + 'in_use': 9, 'limit': 12, 'reserved': 0}, + } + } + self.assertEqual(expected_response, response) + + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") + @mock.patch.object(objects.InstanceGroupList, "get_counts") + def test_detail_v21_user(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} + mock_count.return_value = {'project': {'server_groups': 9}} + req = fakes.HTTPRequest.blank("?user_id=42") + response = self.controller.detail(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': { + 'in_use': 5, 'limit': 2, 'reserved': 0}, + 'fixed_ips': self.expected_detail, + 'floating_ips': self.expected_detail, + 'injected_file_content_bytes': { + 'in_use': 0, 'limit': 10240, 'reserved': 0}, + 'injected_file_path_bytes': { + 'in_use': 0, 'limit': 255, 'reserved': 0}, + 'injected_files': { + 'in_use': 0, 'limit': 5, 'reserved': 0}, + 'instances': { + 'in_use': 4, 'limit': 1, 'reserved': 0}, + 'key_pairs': { + 'in_use': 0, 'limit': 100, 'reserved': 0}, + 'metadata_items': { + 'in_use': 0, 'limit': 128, 'reserved': 0}, + 'ram': { + 'in_use': 6, 'limit': 3, 'reserved': 0}, + 'security_group_rules': self.expected_detail, + 'security_groups': self.expected_detail, + 'server_group_members': { + 'in_use': 0, 'limit': 10, 'reserved': 0}, + 'server_groups': { + 'in_use': 9, 'limit': 12, 'reserved': 0}, + } + } + self.assertEqual(expected_response, response) + + @mock.patch.object(placement_limit, "get_legacy_project_limits") + @mock.patch.object(objects.Quotas, "create_limit") + def test_update_v21(self, mock_create, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + req = fakes.HTTPRequest.blank("") + # TODO(johngarbutt) still need to implement get_settable_quotas + body = {'quota_set': {'server_groups': 2}} + response = self.controller.update(req, uuids.project_id, body=body) + expected_response = { + 'quota_set': { + 'cores': 2, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'injected_files': 5, + 'instances': 1, + 'key_pairs': 100, + 'metadata_items': 128, + 'ram': 3, + 'security_group_rules': -1, + 'security_groups': -1, + 'server_group_members': 10, + 'server_groups': 12, + } + } + self.assertEqual(expected_response, response) + self.assertEqual(0, mock_create.call_count) + + @mock.patch.object(placement_limit, "get_legacy_project_limits") + @mock.patch.object(objects.Quotas, "create_limit") + def test_update_v21_user(self, mock_create, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + req = fakes.HTTPRequest.blank("?user_id=42") + body = {'quota_set': {'key_pairs': 52}} + response = self.controller.update(req, uuids.project_id, body=body) + expected_response = { + 'quota_set': { + 'cores': 2, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'injected_files': 5, + 'instances': 1, + 'key_pairs': 100, + 'metadata_items': 128, + 'ram': 3, + 'security_group_rules': -1, + 'security_groups': -1, + 'server_group_members': 10, + 'server_groups': 12, + } + } + self.assertEqual(expected_response, response) + self.assertEqual(0, mock_create.call_count) + + @mock.patch.object(placement_limit, "get_legacy_default_limits") + def test_defaults_v21(self, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} + req = fakes.HTTPRequest.blank("") + response = self.controller.defaults(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': 2, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': 10240, + 'injected_file_path_bytes': 255, + 'injected_files': 5, + 'instances': 1, + 'key_pairs': 100, + 'metadata_items': 128, + 'ram': 3, + 'security_group_rules': -1, + 'security_groups': -1, + 'server_group_members': 10, + 'server_groups': 12, + } + } + self.assertEqual(expected_response, response) + + def test_defaults_v21_different_limit_values(self): + reglimits = {local_limit.SERVER_METADATA_ITEMS: 7, + local_limit.INJECTED_FILES: 6, + local_limit.INJECTED_FILES_CONTENT: 4, + local_limit.INJECTED_FILES_PATH: 5, + local_limit.KEY_PAIRS: 1, + local_limit.SERVER_GROUPS: 3, + local_limit.SERVER_GROUP_MEMBERS: 2} + self.limit_fixture.reglimits = reglimits + + req = fakes.HTTPRequest.blank("") + response = self.controller.defaults(req, uuids.project_id) + expected_response = { + 'quota_set': { + 'id': uuids.project_id, + 'cores': 0, + 'fixed_ips': -1, + 'floating_ips': -1, + 'injected_file_content_bytes': 4, + 'injected_file_path_bytes': 5, + 'injected_files': 6, + 'instances': 0, + 'key_pairs': 1, + 'metadata_items': 7, + 'ram': 0, + 'security_group_rules': -1, + 'security_groups': -1, + 'server_group_members': 2, + 'server_groups': 3, + } + } + self.assertEqual(expected_response, response) + + @mock.patch('nova.objects.Quotas.destroy_all_by_project') + def test_quotas_delete(self, mock_destroy_all_by_project): + req = fakes.HTTPRequest.blank("") + self.controller.delete(req, "1234") + # Ensure destroy isn't called for unified limits + self.assertEqual(0, mock_destroy_all_by_project.call_count) + + @mock.patch('nova.objects.Quotas.destroy_all_by_project_and_user') + def test_user_quotas_delete(self, mock_destroy_all_by_user): + req = fakes.HTTPRequest.blank("?user_id=42") + self.controller.delete(req, "1234") + # Ensure destroy isn't called for unified limits + self.assertEqual(0, mock_destroy_all_by_user.call_count) diff --git a/nova/tests/unit/api/openstack/compute/test_remote_consoles.py b/nova/tests/unit/api/openstack/compute/test_remote_consoles.py index 6427b1abf0..961f4a02c9 100644 --- a/nova/tests/unit/api/openstack/compute/test_remote_consoles.py +++ b/nova/tests/unit/api/openstack/compute/test_remote_consoles.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + import webob from nova.api.openstack import api_version_request @@ -103,6 +104,18 @@ class ConsolesExtensionTestV21(test.NoDBTestCase): 'get_vnc_console', exception.InstanceNotFound(instance_id=fakes.FAKE_UUID)) + def test_get_vnc_console_instance_invalid_state(self): + body = {'os-getVNCConsole': {'type': 'novnc'}} + self._check_console_failure( + self.controller.get_vnc_console, + webob.exc.HTTPConflict, + body, + 'get_vnc_console', + exception.InstanceInvalidState( + attr='fake-attr', state='fake-state', method='fake-method', + instance_uuid=fakes.FAKE_UUID) + ) + def test_get_vnc_console_invalid_type(self): body = {'os-getVNCConsole': {'type': 'invalid'}} self._check_console_failure( @@ -446,7 +459,7 @@ class ConsolesExtensionTestV26(test.NoDBTestCase): self.req, fakes.FAKE_UUID, body=body) self.assertTrue(mock_handler.called) - def test_create_console_not_found(self,): + def test_create_console_not_found(self): mock_handler = mock.MagicMock() mock_handler.side_effect = exception.InstanceNotFound( instance_id='xxx') diff --git a/nova/tests/unit/api/openstack/compute/test_rescue.py b/nova/tests/unit/api/openstack/compute/test_rescue.py index 28b8217d1a..8a87f52222 100644 --- a/nova/tests/unit/api/openstack/compute/test_rescue.py +++ b/nova/tests/unit/api/openstack/compute/test_rescue.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock import ddt from oslo_utils.fixture import uuidsentinel as uuids diff --git a/nova/tests/unit/api/openstack/compute/test_security_groups.py b/nova/tests/unit/api/openstack/compute/test_security_groups.py index 71cdcbc871..4a85a9997d 100644 --- a/nova/tests/unit/api/openstack/compute/test_security_groups.py +++ b/nova/tests/unit/api/openstack/compute/test_security_groups.py @@ -13,8 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. -import mock from neutronclient.common import exceptions as n_exc +from unittest import mock + from oslo_config import cfg from oslo_serialization import jsonutils from oslo_utils import encodeutils diff --git a/nova/tests/unit/api/openstack/compute/test_server_actions.py b/nova/tests/unit/api/openstack/compute/test_server_actions.py index d07924abe8..08f7a31573 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_actions.py +++ b/nova/tests/unit/api/openstack/compute/test_server_actions.py @@ -13,9 +13,10 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + import ddt import fixtures -import mock from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import uuidutils import webob @@ -66,11 +67,11 @@ class ServerActionsControllerTestV21(test.TestCase): self.controller = self._get_controller() self.compute_api = self.controller.compute_api - # We don't care about anything getting as far as hitting the compute - # RPC API so we just mock it out here. - mock_rpcapi = mock.patch.object(self.compute_api, 'compute_rpcapi') - mock_rpcapi.start() - self.addCleanup(mock_rpcapi.stop) + # In most of the cases we don't care about anything getting as far as + # hitting the compute RPC API so we just mock it out here. + patcher_rpcapi = mock.patch.object(self.compute_api, 'compute_rpcapi') + self.mock_rpcapi = patcher_rpcapi.start() + self.addCleanup(patcher_rpcapi.stop) # The project_id here matches what is used by default in # fake_compute_get which need to match for policy checks. self.req = fakes.HTTPRequest.blank('', @@ -1079,21 +1080,23 @@ class ServerActionsControllerTestV21(test.TestCase): snapshot = dict(id=_fake_id('d')) + self.mock_rpcapi.quiesce_instance.side_effect = ( + exception.InstanceQuiesceNotSupported( + instance_id="fake", reason="test" + ) + ) + with test.nested( mock.patch.object( self.controller.compute_api.volume_api, 'get_absolute_limits', return_value={'totalSnapshotsUsed': 0, 'maxTotalSnapshots': 10}), - mock.patch.object(self.controller.compute_api.compute_rpcapi, - 'quiesce_instance', - side_effect=exception.InstanceQuiesceNotSupported( - instance_id='fake', reason='test')), mock.patch.object(self.controller.compute_api.volume_api, 'get', return_value=volume), mock.patch.object(self.controller.compute_api.volume_api, 'create_snapshot_force', return_value=snapshot), - ) as (mock_get_limits, mock_quiesce, mock_vol_get, mock_vol_create): + ) as (mock_get_limits, mock_vol_get, mock_vol_create): if mock_vol_create_side_effect: mock_vol_create.side_effect = mock_vol_create_side_effect @@ -1125,7 +1128,7 @@ class ServerActionsControllerTestV21(test.TestCase): for k in extra_properties.keys(): self.assertEqual(properties[k], extra_properties[k]) - mock_quiesce.assert_called_once_with(mock.ANY, mock.ANY) + self.mock_rpcapi.quiesce_instance.assert_called_once() mock_vol_get.assert_called_once_with(mock.ANY, volume['id']) mock_vol_create.assert_called_once_with(mock.ANY, volume['id'], mock.ANY, mock.ANY) @@ -1189,21 +1192,23 @@ class ServerActionsControllerTestV21(test.TestCase): snapshot = dict(id=_fake_id('d')) + self.mock_rpcapi.quiesce_instance.side_effect = ( + exception.InstanceQuiesceNotSupported( + instance_id="fake", reason="test" + ) + ) + with test.nested( mock.patch.object( self.controller.compute_api.volume_api, 'get_absolute_limits', return_value={'totalSnapshotsUsed': 0, 'maxTotalSnapshots': 10}), - mock.patch.object(self.controller.compute_api.compute_rpcapi, - 'quiesce_instance', - side_effect=exception.InstanceQuiesceNotSupported( - instance_id='fake', reason='test')), mock.patch.object(self.controller.compute_api.volume_api, 'get', return_value=volume), mock.patch.object(self.controller.compute_api.volume_api, 'create_snapshot_force', return_value=snapshot), - ) as (mock_get_limits, mock_quiesce, mock_vol_get, mock_vol_create): + ) as (mock_get_limits, mock_vol_get, mock_vol_create): response = self.controller._action_create_image(self.req, FAKE_UUID, body=body) @@ -1218,7 +1223,7 @@ class ServerActionsControllerTestV21(test.TestCase): for key, val in extra_metadata.items(): self.assertEqual(properties[key], val) - mock_quiesce.assert_called_once_with(mock.ANY, mock.ANY) + self.mock_rpcapi.quiesce_instance.assert_called_once() mock_vol_get.assert_called_once_with(mock.ANY, volume['id']) mock_vol_create.assert_called_once_with(mock.ANY, volume['id'], mock.ANY, mock.ANY) diff --git a/nova/tests/unit/api/openstack/compute/test_server_diagnostics.py b/nova/tests/unit/api/openstack/compute/test_server_diagnostics.py index d215f3e903..12d8bbb318 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_diagnostics.py +++ b/nova/tests/unit/api/openstack/compute/test_server_diagnostics.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_serialization import jsonutils from oslo_utils.fixture import uuidsentinel as uuids diff --git a/nova/tests/unit/api/openstack/compute/test_server_external_events.py b/nova/tests/unit/api/openstack/compute/test_server_external_events.py index 2ca97fc6d8..e366d0acdd 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_external_events.py +++ b/nova/tests/unit/api/openstack/compute/test_server_external_events.py @@ -12,8 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + import fixtures as fx -import mock from oslo_utils.fixture import uuidsentinel as uuids from nova.api.openstack.compute import server_external_events \ @@ -192,7 +193,7 @@ class ServerExternalEventsTestV21(test.NoDBTestCase): self.api.create, self.req, body=body) def test_create_unknown_events(self): - self.event_1['name'] = 'unkown_event' + self.event_1['name'] = 'unknown_event' body = {'events': self.event_1} self.assertRaises(self.invalid_error, self.api.create, self.req, body=body) diff --git a/nova/tests/unit/api/openstack/compute/test_server_group_quotas.py b/nova/tests/unit/api/openstack/compute/test_server_group_quotas.py index 6b08be6fd9..fe7a60f956 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_group_quotas.py +++ b/nova/tests/unit/api/openstack/compute/test_server_group_quotas.py @@ -13,14 +13,18 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_config import cfg +from oslo_limit import fixture as limit_fixture from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import uuidutils import webob from nova.api.openstack.compute import server_groups as sg_v21 from nova import context +from nova import exception +from nova.limit import local as local_limit from nova import objects from nova import test from nova.tests.unit.api.openstack import fakes @@ -116,14 +120,41 @@ class ServerGroupQuotasTestV21(test.TestCase): self.controller.create, self.req, body={'server_group': sgroup}) + def _test_create_server_group_during_recheck(self, mock_method): + self._setup_quotas() + sgroup = server_group_template() + policies = ['anti-affinity'] + sgroup['policies'] = policies + e = self.assertRaises(webob.exc.HTTPForbidden, + self.controller.create, + self.req, body={'server_group': sgroup}) + self.assertEqual(2, mock_method.call_count) + return e + @mock.patch('nova.objects.Quotas.check_deltas') - def test_create_server_group_recheck_disabled(self, mock_check): + def test_create_server_group_during_recheck(self, mock_check): + """Simulate a race where this request initially has enough quota to + progress partially through the create path but then fails the quota + recheck because a parallel request filled up the quota first. + """ + # First quota check succeeds, second (recheck) fails. + mock_check.side_effect = [None, + exception.OverQuota(overs='server_groups')] + e = self._test_create_server_group_during_recheck(mock_check) + expected = 'Quota exceeded, too many server groups.' + self.assertEqual(expected, str(e)) + + def _test_create_server_group_recheck_disabled(self): self.flags(recheck_quota=False, group='quota') self._setup_quotas() sgroup = server_group_template() policies = ['anti-affinity'] sgroup['policies'] = policies self.controller.create(self.req, body={'server_group': sgroup}) + + @mock.patch('nova.objects.Quotas.check_deltas') + def test_create_server_group_recheck_disabled(self, mock_check): + self._test_create_server_group_recheck_disabled() ctxt = self.req.environ['nova.context'] mock_check.assert_called_once_with(ctxt, {'server_groups': 1}, ctxt.project_id, ctxt.user_id) @@ -170,3 +201,76 @@ class ServerGroupQuotasTestV21(test.TestCase): else: status_int = resp.status_int self.assertEqual(204, status_int) + + +class ServerGroupQuotasUnifiedLimitsTestV21(ServerGroupQuotasTestV21): + + def setUp(self): + super(ServerGroupQuotasUnifiedLimitsTestV21, self).setUp() + self.flags(driver='nova.quota.UnifiedLimitsDriver', group='quota') + self.req = fakes.HTTPRequest.blank('') + self.controller = sg_v21.ServerGroupController() + self.limit_fixture = self.useFixture( + limit_fixture.LimitFixture({'server_groups': 10}, {})) + + @mock.patch('nova.limit.local.enforce_db_limit') + def test_create_server_group_during_recheck(self, mock_enforce): + """Simulate a race where this request initially has enough quota to + progress partially through the create path but then fails the quota + recheck because a parallel request filled up the quota first. + """ + # First quota check succeeds, second (recheck) fails. + mock_enforce.side_effect = [ + None, + exception.ServerGroupLimitExceeded(message='oslo.limit message')] + # Run the test using the unified limits enforce method. + e = self._test_create_server_group_during_recheck(mock_enforce) + expected = 'oslo.limit message' + self.assertEqual(expected, str(e)) + + @mock.patch('nova.limit.local.enforce_db_limit') + def test_create_server_group_recheck_disabled(self, mock_enforce): + # Run the test using the unified limits enforce method. + self._test_create_server_group_recheck_disabled() + ctxt = self.req.environ['nova.context'] + mock_enforce.assert_called_once_with(ctxt, 'server_groups', + entity_scope=ctxt.project_id, + delta=1) + + def test_create_group_fails_with_zero_quota(self): + self.limit_fixture.reglimits = {'server_groups': 0} + sgroup = {'name': 'test', 'policies': ['anti-affinity']} + exc = self.assertRaises(webob.exc.HTTPForbidden, + self.controller.create, + self.req, body={'server_group': sgroup}) + msg = ("Resource %s is over limit" % local_limit.SERVER_GROUPS) + self.assertIn(msg, str(exc)) + + def test_create_only_one_group_when_limit_is_one(self): + self.limit_fixture.reglimits = {'server_groups': 1} + policies = ['anti-affinity'] + sgroup = {'name': 'test', 'policies': policies} + res_dict = self.controller.create( + self.req, body={'server_group': sgroup}) + self.assertEqual(res_dict['server_group']['name'], 'test') + self.assertTrue(uuidutils.is_uuid_like(res_dict['server_group']['id'])) + self.assertEqual(res_dict['server_group']['policies'], policies) + + # prove we can't create two, as limited to one + sgroup2 = {'name': 'test2', 'policies': policies} + exc = self.assertRaises(webob.exc.HTTPForbidden, + self.controller.create, + self.req, body={'server_group': sgroup2}) + msg = ("Resource %s is over limit" % local_limit.SERVER_GROUPS) + self.assertIn(msg, str(exc)) + + # delete first one + self.controller.delete(self.req, res_dict['server_group']['id']) + + # prove we can now create the second one + res_dict2 = self.controller.create( + self.req, body={'server_group': sgroup2}) + self.assertEqual(res_dict2['server_group']['name'], 'test2') + self.assertTrue( + uuidutils.is_uuid_like(res_dict2['server_group']['id'])) + self.assertEqual(res_dict2['server_group']['policies'], policies) diff --git a/nova/tests/unit/api/openstack/compute/test_server_groups.py b/nova/tests/unit/api/openstack/compute/test_server_groups.py index a0d1712343..9d99c3ae6d 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_groups.py +++ b/nova/tests/unit/api/openstack/compute/test_server_groups.py @@ -14,7 +14,8 @@ # under the License. import copy -import mock +from unittest import mock + from oslo_utils.fixture import uuidsentinel from oslo_utils import uuidutils import webob @@ -86,7 +87,8 @@ class ServerGroupTestV21(test.NoDBTestCase): def setUp(self): super(ServerGroupTestV21, self).setUp() self._setup_controller() - self.req = fakes.HTTPRequest.blank('') + self.member_req = fakes.HTTPRequest.member_req('') + self.reader_req = fakes.HTTPRequest.reader_req('') self.admin_req = fakes.HTTPRequest.blank('', use_admin_context=True) self.foo_req = fakes.HTTPRequest.blank('', project_id='foo') self.policy = self.useFixture(fixtures.RealPolicyFixture()) @@ -113,20 +115,20 @@ class ServerGroupTestV21(test.NoDBTestCase): def test_create_server_group_with_no_policies(self): sgroup = server_group_template() self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) def _create_server_group_normal(self, policies=None, policy=None, rules=None): sgroup = server_group_template() sgroup['policies'] = policies - res_dict = self.controller.create(self.req, + res_dict = self.controller.create(self.member_req, body={'server_group': sgroup}) self.assertEqual(res_dict['server_group']['name'], 'test') self.assertTrue(uuidutils.is_uuid_like(res_dict['server_group']['id'])) self.assertEqual(res_dict['server_group']['policies'], policies) def test_create_server_group_with_new_policy_before_264(self): - req = fakes.HTTPRequest.blank('', version='2.63') + req = fakes.HTTPRequest.member_req('', version='2.63') policy = 'anti-affinity' rules = {'max_server_per_host': 3} # 'policy' isn't an acceptable request key before 2.64 @@ -161,7 +163,7 @@ class ServerGroupTestV21(test.NoDBTestCase): self.controller.create(self.admin_req, body={'server_group': sgroup}) # test as non-admin - self.controller.create(self.req, body={'server_group': sgroup}) + self.controller.create(self.member_req, body={'server_group': sgroup}) def _create_instance(self, ctx, cell): with context.target_cell(ctx, cell) as cctx: @@ -288,7 +290,7 @@ class ServerGroupTestV21(test.NoDBTestCase): path = path or '/os-server-groups?all_projects=True' if limited: path += limited - req = fakes.HTTPRequest.blank(path, version=api_version) + reader_req = fakes.HTTPRequest.reader_req(path, version=api_version) admin_req = fakes.HTTPRequest.blank(path, use_admin_context=True, version=api_version) @@ -297,7 +299,7 @@ class ServerGroupTestV21(test.NoDBTestCase): self.assertEqual(all, res_dict) # test as non-admin - res_dict = self.controller.index(req) + res_dict = self.controller.index(reader_req) self.assertEqual(tenant_specific, res_dict) @mock.patch('nova.objects.InstanceGroupList.get_by_project_id') @@ -346,25 +348,27 @@ class ServerGroupTestV21(test.NoDBTestCase): return_get_by_project = return_server_groups() mock_get_by_project.return_value = return_get_by_project path = '/os-server-groups' - req = fakes.HTTPRequest.blank(path, version=api_version) + req = fakes.HTTPRequest.reader_req(path, version=api_version) res_dict = self.controller.index(req) self.assertEqual(expected, res_dict) def test_display_members(self): ctx = context.RequestContext('fake_user', fakes.FAKE_PROJECT_ID) (ig_uuid, instances, members) = self._create_groups_and_instances(ctx) - res_dict = self.controller.show(self.req, ig_uuid) + res_dict = self.controller.show(self.reader_req, ig_uuid) result_members = res_dict['server_group']['members'] self.assertEqual(3, len(result_members)) for member in members: self.assertIn(member, result_members) def test_display_members_with_nonexistent_group(self): - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.show, self.req, uuidsentinel.group) + self.assertRaises( + webob.exc.HTTPNotFound, + self.controller.show, self.reader_req, uuidsentinel.group) def test_display_active_members_only(self): - ctx = context.RequestContext('fake_user', fakes.FAKE_PROJECT_ID) + ctx = context.RequestContext('fake_user', fakes.FAKE_PROJECT_ID, + roles=['member', 'reader']) (ig_uuid, instances, members) = self._create_groups_and_instances(ctx) # delete an instance @@ -378,7 +382,7 @@ class ServerGroupTestV21(test.NoDBTestCase): self.assertRaises(exception.InstanceNotFound, objects.Instance.get_by_uuid, ctx, instances[1].uuid) - res_dict = self.controller.show(self.req, ig_uuid) + res_dict = self.controller.show(self.reader_req, ig_uuid) result_members = res_dict['server_group']['members'] # check that only the active instance is displayed self.assertEqual(2, len(result_members)) @@ -392,7 +396,7 @@ class ServerGroupTestV21(test.NoDBTestCase): self.controller.show(self.admin_req, ig_uuid) # test as non-admin, same project - self.controller.show(self.req, ig_uuid) + self.controller.show(self.reader_req, ig_uuid) # test as non-admin, different project self.assertRaises(webob.exc.HTTPNotFound, @@ -405,7 +409,7 @@ class ServerGroupTestV21(test.NoDBTestCase): sgroup = server_group_template(name='good* $%name', policies=['affinity']) - res_dict = self.controller.create(self.req, + res_dict = self.controller.create(self.member_req, body={'server_group': sgroup}) self.assertEqual(res_dict['server_group']['name'], 'good* $%name') @@ -413,99 +417,99 @@ class ServerGroupTestV21(test.NoDBTestCase): # blank name sgroup = server_group_template(name='', policies=['test_policy']) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # name with length 256 sgroup = server_group_template(name='1234567890' * 26, policies=['test_policy']) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # non-string name sgroup = server_group_template(name=12, policies=['test_policy']) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # name with leading spaces sgroup = server_group_template(name=' leading spaces', policies=['test_policy']) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # name with trailing spaces sgroup = server_group_template(name='trailing space ', policies=['test_policy']) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # name with all spaces sgroup = server_group_template(name=' ', policies=['test_policy']) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # name with unprintable character sgroup = server_group_template(name='bad\x00name', policies=['test_policy']) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # name with out of range char U0001F4A9 sgroup = server_group_template(name=u"\U0001F4A9", policies=['affinity']) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) def test_create_server_group_with_illegal_policies(self): # blank policy sgroup = server_group_template(name='fake-name', policies='') self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # policy as integer sgroup = server_group_template(name='fake-name', policies=7) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # policy as string sgroup = server_group_template(name='fake-name', policies='invalid') self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) # policy as None sgroup = server_group_template(name='fake-name', policies=None) self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) def test_create_server_group_conflicting_policies(self): sgroup = server_group_template() policies = ['anti-affinity', 'affinity'] sgroup['policies'] = policies self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) def test_create_server_group_with_duplicate_policies(self): sgroup = server_group_template() policies = ['affinity', 'affinity'] sgroup['policies'] = policies self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) def test_create_server_group_not_supported(self): sgroup = server_group_template() policies = ['storage-affinity', 'anti-affinity', 'rack-affinity'] sgroup['policies'] = policies self.assertRaises(self.validation_error, self.controller.create, - self.req, body={'server_group': sgroup}) + self.member_req, body={'server_group': sgroup}) def test_create_server_group_with_no_body(self): self.assertRaises(self.validation_error, - self.controller.create, self.req, body=None) + self.controller.create, self.member_req, body=None) def test_create_server_group_with_no_server_group(self): body = {'no-instanceGroup': None} self.assertRaises(self.validation_error, - self.controller.create, self.req, body=body) + self.controller.create, self.member_req, body=body) def test_list_server_group_by_tenant(self): self._test_list_server_group_by_tenant( @@ -527,7 +531,7 @@ class ServerGroupTestV21(test.NoDBTestCase): self.controller.index(self.admin_req) # test as non-admin - self.controller.index(self.req) + self.controller.index(self.reader_req) def test_list_server_group_multiple_param(self): self._test_list_server_group(api_version=self.wsgi_api_version, @@ -597,7 +601,7 @@ class ServerGroupTestV21(test.NoDBTestCase): self.stub_out('nova.objects.InstanceGroup.get_by_uuid', return_server_group) - resp = self.controller.delete(self.req, uuidsentinel.sg1_id) + resp = self.controller.delete(self.member_req, uuidsentinel.sg1_id) mock_destroy.assert_called_once_with() # NOTE: on v2.1, http status code is set as wsgi_code of API @@ -610,7 +614,7 @@ class ServerGroupTestV21(test.NoDBTestCase): def test_delete_non_existing_server_group(self): self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, - self.req, 'invalid') + self.member_req, 'invalid') def test_delete_server_group_rbac_default(self): ctx = context.RequestContext('fake_user', fakes.FAKE_PROJECT_ID) @@ -621,7 +625,7 @@ class ServerGroupTestV21(test.NoDBTestCase): # test as non-admin ig_uuid = self._create_groups_and_instances(ctx)[0] - self.controller.delete(self.req, ig_uuid) + self.controller.delete(self.member_req, ig_uuid) class ServerGroupTestV213(ServerGroupTestV21): @@ -648,7 +652,7 @@ class ServerGroupTestV264(ServerGroupTestV213): def _create_server_group_normal(self, policies=None, policy=None, rules=None): - req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + req = fakes.HTTPRequest.member_req('', version=self.wsgi_api_version) sgroup = server_group_template() sgroup['rules'] = rules or {} sgroup['policy'] = policy @@ -673,7 +677,7 @@ class ServerGroupTestV264(ServerGroupTestV213): self.assertEqual(res_dict['server_group']['rules'], {}) def _display_server_group(self, uuid): - req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + req = fakes.HTTPRequest.reader_req('', version=self.wsgi_api_version) group = self.controller.show(req, uuid) return group @@ -689,7 +693,7 @@ class ServerGroupTestV264(ServerGroupTestV213): self.assertEqual(res_dict['server_group']['rules'], rules) def test_create_affinity_server_group_with_invalid_policy(self): - req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + req = fakes.HTTPRequest.member_req('', version=self.wsgi_api_version) sgroup = server_group_template(policy='affinity', rules={'max_server_per_host': 3}) result = self.assertRaises(webob.exc.HTTPBadRequest, @@ -697,7 +701,7 @@ class ServerGroupTestV264(ServerGroupTestV213): self.assertIn("Only anti-affinity policy supports rules", str(result)) def test_create_anti_affinity_server_group_with_invalid_rules(self): - req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + req = fakes.HTTPRequest.member_req('', version=self.wsgi_api_version) # A negative test for key is unknown, the value is not positive # and not integer invalid_rules = [{'unknown_key': '3'}, @@ -717,7 +721,7 @@ class ServerGroupTestV264(ServerGroupTestV213): return_value=32) def test_create_server_group_with_low_version_compute_service(self, mock_get_v): - req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + req = fakes.HTTPRequest.member_req('', version=self.wsgi_api_version) sgroup = server_group_template(policy='anti-affinity', rules={'max_server_per_host': 3}) result = self.assertRaises( @@ -733,7 +737,7 @@ class ServerGroupTestV264(ServerGroupTestV213): self._create_server_group_normal(policy=policy) def test_policies_since_264(self): - req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + req = fakes.HTTPRequest.member_req('', version=self.wsgi_api_version) # 'policies' isn't allowed in request >= 2.64 sgroup = server_group_template(policies=['anti-affinity']) self.assertRaises( @@ -741,14 +745,14 @@ class ServerGroupTestV264(ServerGroupTestV213): req, body={'server_group': sgroup}) def test_create_server_group_without_policy(self): - req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + req = fakes.HTTPRequest.member_req('', version=self.wsgi_api_version) # 'policy' is required request key in request >= 2.64 sgroup = server_group_template() self.assertRaises(self.validation_error, self.controller.create, req, body={'server_group': sgroup}) def test_create_server_group_with_illegal_policies(self): - req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + req = fakes.HTTPRequest.member_req('', version=self.wsgi_api_version) # blank policy sgroup = server_group_template(policy='') self.assertRaises(self.validation_error, self.controller.create, @@ -770,7 +774,7 @@ class ServerGroupTestV264(ServerGroupTestV213): req, body={'server_group': sgroup}) def test_additional_params(self): - req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version) + req = fakes.HTTPRequest.member_req('', version=self.wsgi_api_version) sgroup = server_group_template(unknown='unknown') self.assertRaises(self.validation_error, self.controller.create, req, body={'server_group': sgroup}) @@ -785,7 +789,7 @@ class ServerGroupTestV275(ServerGroupTestV264): path='/os-server-groups?dummy=False&all_projects=True') def test_list_server_group_additional_param(self): - req = fakes.HTTPRequest.blank('/os-server-groups?dummy=False', - version=self.wsgi_api_version) + req = fakes.HTTPRequest.reader_req('/os-server-groups?dummy=False', + version=self.wsgi_api_version) self.assertRaises(self.validation_error, self.controller.index, req) diff --git a/nova/tests/unit/api/openstack/compute/test_server_metadata.py b/nova/tests/unit/api/openstack/compute/test_server_metadata.py index a454597305..9b420dde17 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_metadata.py +++ b/nova/tests/unit/api/openstack/compute/test_server_metadata.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_config import cfg from oslo_serialization import jsonutils from oslo_utils.fixture import uuidsentinel as uuids diff --git a/nova/tests/unit/api/openstack/compute/test_server_migrations.py b/nova/tests/unit/api/openstack/compute/test_server_migrations.py index 8d798d434c..c5d8556751 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_migrations.py +++ b/nova/tests/unit/api/openstack/compute/test_server_migrations.py @@ -15,8 +15,8 @@ import copy import datetime +from unittest import mock -import mock from oslo_utils.fixture import uuidsentinel as uuids import webob diff --git a/nova/tests/unit/api/openstack/compute/test_server_password.py b/nova/tests/unit/api/openstack/compute/test_server_password.py index e34ceb90e9..2751eee709 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_password.py +++ b/nova/tests/unit/api/openstack/compute/test_server_password.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock from nova.api.openstack.compute import server_password \ as server_password_v21 diff --git a/nova/tests/unit/api/openstack/compute/test_server_reset_state.py b/nova/tests/unit/api/openstack/compute/test_server_reset_state.py index 3462cf21ac..3a0c9ca1e2 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_reset_state.py +++ b/nova/tests/unit/api/openstack/compute/test_server_reset_state.py @@ -12,7 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_utils import uuidutils import webob diff --git a/nova/tests/unit/api/openstack/compute/test_server_start_stop.py b/nova/tests/unit/api/openstack/compute/test_server_start_stop.py index 60d12d0c43..f604652622 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_start_stop.py +++ b/nova/tests/unit/api/openstack/compute/test_server_start_stop.py @@ -12,7 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_utils.fixture import uuidsentinel as uuids import webob diff --git a/nova/tests/unit/api/openstack/compute/test_server_tags.py b/nova/tests/unit/api/openstack/compute/test_server_tags.py index b121c75c3a..4e4609d778 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_tags.py +++ b/nova/tests/unit/api/openstack/compute/test_server_tags.py @@ -10,7 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from webob import exc from nova.api.openstack.compute import server_tags diff --git a/nova/tests/unit/api/openstack/compute/test_server_topology.py b/nova/tests/unit/api/openstack/compute/test_server_topology.py index 3d8f6dc908..63d5f7a5c1 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_topology.py +++ b/nova/tests/unit/api/openstack/compute/test_server_topology.py @@ -11,7 +11,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_utils.fixture import uuidsentinel as uuids from webob import exc diff --git a/nova/tests/unit/api/openstack/compute/test_servers.py b/nova/tests/unit/api/openstack/compute/test_servers.py index 31739ed7ab..8903de0c3c 100644 --- a/nova/tests/unit/api/openstack/compute/test_servers.py +++ b/nova/tests/unit/api/openstack/compute/test_servers.py @@ -17,13 +17,14 @@ import collections import copy import datetime +from unittest import mock + import ddt import functools from urllib import parse as urlparse import fixtures import iso8601 -import mock from oslo_policy import policy as oslo_policy from oslo_serialization import base64 from oslo_serialization import jsonutils @@ -2087,10 +2088,10 @@ class ServersControllerTestV216(_ServersControllerTest): return server_dict - @mock.patch('nova.compute.api.API.get_instance_host_status') - def _verify_host_status_policy_behavior(self, func, mock_get_host_status): + def _verify_host_status_policy_behavior(self, func): # Set policy to disallow both host_status cases and verify we don't # call the get_instance_host_status compute RPC API. + self.mock_get_instance_host_status.reset_mock() rules = { 'os_compute_api:servers:show:host_status': '!', 'os_compute_api:servers:show:host_status:unknown-only': '!', @@ -2098,7 +2099,7 @@ class ServersControllerTestV216(_ServersControllerTest): orig_rules = policy.get_rules() policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=False) func() - mock_get_host_status.assert_not_called() + self.mock_get_instance_host_status.assert_not_called() # Restore the original rules. policy.set_rules(orig_rules) @@ -2638,15 +2639,13 @@ class ServersControllerTestV275(ControllerTest): microversion = '2.75' - @mock.patch('nova.compute.api.API.get_all') - def test_get_servers_additional_query_param_old_version(self, mock_get): + def test_get_servers_additional_query_param_old_version(self): req = fakes.HTTPRequest.blank(self.path_with_query % 'unknown=1', use_admin_context=True, version='2.74') self.controller.index(req) - @mock.patch('nova.compute.api.API.get_all') - def test_get_servers_ignore_sort_key_old_version(self, mock_get): + def test_get_servers_ignore_sort_key_old_version(self): req = fakes.HTTPRequest.blank( self.path_with_query % 'sort_key=deleted', use_admin_context=True, version='2.74') @@ -3584,13 +3583,13 @@ class ServersControllerRebuildTestV263(ControllerTest): }, } - @mock.patch('nova.compute.api.API.get') - def _rebuild_server(self, mock_get, certs=None, - conf_enabled=True, conf_certs=None): + def _rebuild_server(self, certs=None, conf_enabled=True, conf_certs=None): ctx = self.req.environ['nova.context'] - mock_get.return_value = fakes.stub_instance_obj(ctx, - vm_state=vm_states.ACTIVE, trusted_certs=certs, - project_id=self.req_project_id, user_id=self.req_user_id) + self.mock_get.side_effect = None + self.mock_get.return_value = fakes.stub_instance_obj( + ctx, vm_state=vm_states.ACTIVE, trusted_certs=certs, + project_id=self.req_project_id, user_id=self.req_user_id + ) self.flags(default_trusted_certificate_ids=conf_certs, group='glance') @@ -3743,10 +3742,10 @@ class ServersControllerRebuildTestV271(ControllerTest): } } - @mock.patch('nova.compute.api.API.get') - def _rebuild_server(self, mock_get): + def _rebuild_server(self): ctx = self.req.environ['nova.context'] - mock_get.return_value = fakes.stub_instance_obj(ctx, + self.mock_get.side_effect = None + self.mock_get.return_value = fakes.stub_instance_obj(ctx, vm_state=vm_states.ACTIVE, project_id=self.req_project_id, user_id=self.req_user_id) server = self.controller._action_rebuild( @@ -8023,7 +8022,7 @@ class ServersViewBuilderTestV269(_ServersViewBuilderTest): version=self.microversion) def test_get_server_list_detail_with_down_cells(self): - # Fake out 1 partially constructued instance and one full instance. + # Fake out 1 partially constructed instance and one full instance. self.instances = [ self.instance, objects.Instance( @@ -8151,7 +8150,7 @@ class ServersViewBuilderTestV269(_ServersViewBuilderTest): self.assertThat(output, matchers.DictMatches(expected)) def test_get_server_list_with_down_cells(self): - # Fake out 1 partially constructued instance and one full instance. + # Fake out 1 partially constructed instance and one full instance. self.instances = [ self.instance, objects.Instance( @@ -8203,7 +8202,7 @@ class ServersViewBuilderTestV269(_ServersViewBuilderTest): self.assertThat(output, matchers.DictMatches(expected)) def test_get_server_with_down_cells(self): - # Fake out 1 partially constructued instance. + # Fake out 1 partially constructed instance. self.instance = objects.Instance( context=self.ctxt, uuid=self.uuid, @@ -8266,7 +8265,7 @@ class ServersViewBuilderTestV269(_ServersViewBuilderTest): self.assertThat(output, matchers.DictMatches(expected)) def test_get_server_without_image_avz_user_id_set_from_down_cells(self): - # Fake out 1 partially constructued instance. + # Fake out 1 partially constructed instance. self.instance = objects.Instance( context=self.ctxt, uuid=self.uuid, diff --git a/nova/tests/unit/api/openstack/compute/test_services.py b/nova/tests/unit/api/openstack/compute/test_services.py index 5d83bc5a91..f237acc15a 100644 --- a/nova/tests/unit/api/openstack/compute/test_services.py +++ b/nova/tests/unit/api/openstack/compute/test_services.py @@ -14,9 +14,9 @@ import copy import datetime +from unittest import mock from keystoneauth1 import exceptions as ks_exc -import mock from oslo_utils import fixture as utils_fixture from oslo_utils.fixture import uuidsentinel import webob.exc diff --git a/nova/tests/unit/api/openstack/compute/test_shelve.py b/nova/tests/unit/api/openstack/compute/test_shelve.py index 68e523be47..bfa8d2d055 100644 --- a/nova/tests/unit/api/openstack/compute/test_shelve.py +++ b/nova/tests/unit/api/openstack/compute/test_shelve.py @@ -12,10 +12,10 @@ # License for the specific language governing permissions and limitations # under the License. -import fixtures -import mock +from unittest import mock import ddt +import fixtures from oslo_serialization import jsonutils from oslo_utils.fixture import uuidsentinel as uuids import webob @@ -134,13 +134,17 @@ class UnshelveServerControllerTestV277(test.NoDBTestCase): 'availability_zone': 'us-east' }} self.req.body = jsonutils.dump_as_bytes(body) - self.req.api_version_request = (api_version_request. - APIVersionRequest('2.76')) - with mock.patch.object(self.controller.compute_api, - 'unshelve') as mock_unshelve: + self.req.api_version_request = ( + api_version_request.APIVersionRequest('2.76') + ) + with mock.patch.object( + self.controller.compute_api, 'unshelve' + ) as mock_unshelve: self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body) mock_unshelve.assert_called_once_with( - self.req.environ['nova.context'], instance, new_az=None) + self.req.environ['nova.context'], + instance, + ) @mock.patch('nova.compute.api.API.unshelve') @mock.patch('nova.api.openstack.common.get_instance') @@ -158,7 +162,9 @@ class UnshelveServerControllerTestV277(test.NoDBTestCase): APIVersionRequest('2.76')) self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body) mock_unshelve.assert_called_once_with( - self.req.environ['nova.context'], instance, new_az=None) + self.req.environ['nova.context'], + instance, + ) @mock.patch('nova.compute.api.API.unshelve') @mock.patch('nova.api.openstack.common.get_instance') @@ -193,6 +199,238 @@ class UnshelveServerControllerTestV277(test.NoDBTestCase): 'availability_zone': None }} self.req.body = jsonutils.dump_as_bytes(body) + self.assertRaises( + exception.ValidationError, + self.controller._unshelve, + self.req, + fakes.FAKE_UUID, body=body) + + def test_unshelve_with_additional_param(self): + body = { + 'unshelve': { + 'availability_zone': 'us-east', + 'additional_param': 1 + }} + self.req.body = jsonutils.dump_as_bytes(body) + exc = self.assertRaises( + exception.ValidationError, + self.controller._unshelve, self.req, + fakes.FAKE_UUID, body=body) + self.assertIn("Additional properties are not allowed", str(exc)) + + +class UnshelveServerControllerTestV291(test.NoDBTestCase): + """Server controller test for microversion 2.91 + + Add host parameter to unshelve a shelved-offloaded server of + 2.91 microversion. + """ + wsgi_api_version = '2.91' + + def setUp(self): + super(UnshelveServerControllerTestV291, self).setUp() + self.controller = shelve_v21.ShelveController() + self.req = fakes.HTTPRequest.blank( + '/%s/servers/a/action' % fakes.FAKE_PROJECT_ID, + use_admin_context=True, version=self.wsgi_api_version) + + def fake_get_instance(self): + ctxt = self.req.environ['nova.context'] + return fake_instance.fake_instance_obj( + ctxt, uuid=fakes.FAKE_UUID, vm_state=vm_states.SHELVED_OFFLOADED) + + @mock.patch('nova.api.openstack.common.get_instance') + def test_unshelve_with_az_pre_2_91(self, mock_get_instance): + """Make sure specifying an AZ before microversion 2.91 + is still working. + """ + instance = self.fake_get_instance() + mock_get_instance.return_value = instance + + body = { + 'unshelve': { + 'availability_zone': 'us-east', + }} + self.req.body = jsonutils.dump_as_bytes(body) + self.req.api_version_request = ( + api_version_request.APIVersionRequest('2.77')) + with mock.patch.object( + self.controller.compute_api, 'unshelve' + ) as mock_unshelve: + self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body) + mock_unshelve.assert_called_once_with( + self.req.environ['nova.context'], + instance, + new_az='us-east', + ) + + @mock.patch('nova.api.openstack.common.get_instance') + def test_unshelve_without_parameters_2_91(self, mock_get_instance): + """Make sure not specifying parameters with microversion 2.91 + is working. + """ + instance = self.fake_get_instance() + mock_get_instance.return_value = instance + + body = { + 'unshelve': None + } + self.req.body = jsonutils.dump_as_bytes(body) + self.req.api_version_request = ( + api_version_request.APIVersionRequest('2.91')) + with mock.patch.object( + self.controller.compute_api, 'unshelve') as mock_unshelve: + self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body) + mock_unshelve.assert_called_once_with( + self.req.environ['nova.context'], + instance, + ) + + @mock.patch('nova.api.openstack.common.get_instance') + def test_unshelve_with_az_2_91(self, mock_get_instance): + """Make sure specifying an AZ with microversion 2.91 + is working. + """ + instance = self.fake_get_instance() + mock_get_instance.return_value = instance + + body = { + 'unshelve': { + 'availability_zone': 'us-east', + }} + self.req.body = jsonutils.dump_as_bytes(body) + self.req.api_version_request = ( + api_version_request.APIVersionRequest('2.91')) + with mock.patch.object( + self.controller.compute_api, 'unshelve') as mock_unshelve: + self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body) + mock_unshelve.assert_called_once_with( + self.req.environ['nova.context'], + instance, + new_az='us-east', + host=None, + ) + + @mock.patch('nova.api.openstack.common.get_instance') + def test_unshelve_with_az_none_2_91(self, mock_get_instance): + """Make sure specifying an AZ to none (unpin server) + is working. + """ + instance = self.fake_get_instance() + mock_get_instance.return_value = instance + + body = { + 'unshelve': { + 'availability_zone': None, + }} + self.req.body = jsonutils.dump_as_bytes(body) + self.req.api_version_request = ( + api_version_request.APIVersionRequest('2.91')) + with mock.patch.object( + self.controller.compute_api, 'unshelve') as mock_unshelve: + self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body) + mock_unshelve.assert_called_once_with( + self.req.environ['nova.context'], + instance, + new_az=None, + host=None, + ) + + @mock.patch('nova.api.openstack.common.get_instance') + def test_unshelve_with_host_2_91(self, mock_get_instance): + """Make sure specifying a host with microversion 2.91 + is working. + """ + instance = self.fake_get_instance() + mock_get_instance.return_value = instance + + body = { + 'unshelve': { + 'host': 'server02', + }} + self.req.body = jsonutils.dump_as_bytes(body) + self.req.api_version_request = ( + api_version_request.APIVersionRequest('2.91')) + with mock.patch.object( + self.controller.compute_api, 'unshelve') as mock_unshelve: + self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body) + mock_unshelve.assert_called_once_with( + self.req.environ['nova.context'], + instance, + host='server02', + ) + + @mock.patch('nova.compute.api.API.unshelve') + @mock.patch('nova.api.openstack.common.get_instance') + def test_unshelve_with_az_and_host_with_v2_91( + self, mock_get_instance, mock_unshelve): + """Make sure specifying a host and an availability_zone with + microversion 2.91 is working. + """ + instance = self.fake_get_instance() + mock_get_instance.return_value = instance + + body = { + 'unshelve': { + 'availability_zone': 'us-east', + 'host': 'server01', + }} + self.req.body = jsonutils.dump_as_bytes(body) + self.req.api_version_request = ( + api_version_request.APIVersionRequest('2.91')) + + self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body) + self.controller.compute_api.unshelve.assert_called_once_with( + self.req.environ['nova.context'], + instance, + new_az='us-east', + host='server01', + ) + + def test_invalid_az_name_with_int(self): + body = { + 'unshelve': { + 'host': 1234 + }} + self.req.body = jsonutils.dump_as_bytes(body) + self.assertRaises( + exception.ValidationError, + self.controller._unshelve, + self.req, + fakes.FAKE_UUID, + body=body) + + def test_no_az_value(self): + body = { + 'unshelve': { + 'host': None + }} + self.req.body = jsonutils.dump_as_bytes(body) + self.assertRaises( + exception.ValidationError, + self.controller._unshelve, + self.req, + fakes.FAKE_UUID, body=body) + + def test_invalid_host_fqdn_with_int(self): + body = { + 'unshelve': { + 'host': 1234 + }} + self.req.body = jsonutils.dump_as_bytes(body) + self.assertRaises( + exception.ValidationError, + self.controller._unshelve, + self.req, + fakes.FAKE_UUID, + body=body) + + def test_no_host(self): + body = { + 'unshelve': { + 'host': None + }} + self.req.body = jsonutils.dump_as_bytes(body) self.assertRaises(exception.ValidationError, self.controller._unshelve, self.req, fakes.FAKE_UUID, @@ -201,7 +439,7 @@ class UnshelveServerControllerTestV277(test.NoDBTestCase): def test_unshelve_with_additional_param(self): body = { 'unshelve': { - 'availability_zone': 'us-east', + 'host': 'server01', 'additional_param': 1 }} self.req.body = jsonutils.dump_as_bytes(body) @@ -209,4 +447,4 @@ class UnshelveServerControllerTestV277(test.NoDBTestCase): exception.ValidationError, self.controller._unshelve, self.req, fakes.FAKE_UUID, body=body) - self.assertIn("Additional properties are not allowed", str(exc)) + self.assertIn("Invalid input for field/attribute unshelve.", str(exc)) diff --git a/nova/tests/unit/api/openstack/compute/test_simple_tenant_usage.py b/nova/tests/unit/api/openstack/compute/test_simple_tenant_usage.py index 5794fdf061..a7dcfae558 100644 --- a/nova/tests/unit/api/openstack/compute/test_simple_tenant_usage.py +++ b/nova/tests/unit/api/openstack/compute/test_simple_tenant_usage.py @@ -14,8 +14,8 @@ # under the License. import datetime +from unittest import mock -import mock from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import timeutils import webob diff --git a/nova/tests/unit/api/openstack/compute/test_snapshots.py b/nova/tests/unit/api/openstack/compute/test_snapshots.py index b23ed50865..2e133506a3 100644 --- a/nova/tests/unit/api/openstack/compute/test_snapshots.py +++ b/nova/tests/unit/api/openstack/compute/test_snapshots.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + import webob from nova.api.openstack.compute import volumes as volumes_v21 diff --git a/nova/tests/unit/api/openstack/compute/test_suspend_server.py b/nova/tests/unit/api/openstack/compute/test_suspend_server.py index 6eeb2b4549..a44297362c 100644 --- a/nova/tests/unit/api/openstack/compute/test_suspend_server.py +++ b/nova/tests/unit/api/openstack/compute/test_suspend_server.py @@ -12,8 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + import ddt -import mock from oslo_utils.fixture import uuidsentinel as uuids import webob diff --git a/nova/tests/unit/api/openstack/compute/test_tenant_networks.py b/nova/tests/unit/api/openstack/compute/test_tenant_networks.py index d05c85c508..c6de561b11 100644 --- a/nova/tests/unit/api/openstack/compute/test_tenant_networks.py +++ b/nova/tests/unit/api/openstack/compute/test_tenant_networks.py @@ -13,8 +13,8 @@ # under the License. import copy +from unittest import mock -import mock from oslo_config import cfg from oslo_utils.fixture import uuidsentinel as uuids import webob diff --git a/nova/tests/unit/api/openstack/compute/test_volumes.py b/nova/tests/unit/api/openstack/compute/test_volumes.py index a24c104c93..5b4a2d8b1a 100644 --- a/nova/tests/unit/api/openstack/compute/test_volumes.py +++ b/nova/tests/unit/api/openstack/compute/test_volumes.py @@ -15,10 +15,10 @@ # under the License. import datetime +from unittest import mock import urllib import fixtures -import mock from oslo_serialization import jsonutils from oslo_utils import encodeutils from oslo_utils.fixture import uuidsentinel as uuids @@ -1889,8 +1889,7 @@ class AssistedSnapshotDeleteTestCaseV21(test.NoDBTestCase): req, '5') def _test_assisted_delete_instance_conflict(self, api_error): - # unset the stub on volume_snapshot_delete from setUp - self.mock_volume_snapshot_delete.stop() + self.mock_volume_snapshot_delete.side_effect = api_error params = { 'delete_info': jsonutils.dumps({'volume_id': '1'}), } @@ -1899,10 +1898,9 @@ class AssistedSnapshotDeleteTestCaseV21(test.NoDBTestCase): urllib.parse.urlencode(params), version=self.microversion) req.method = 'DELETE' - with mock.patch.object(compute_api.API, 'volume_snapshot_delete', - side_effect=api_error): - self.assertRaises( - webob.exc.HTTPBadRequest, self.controller.delete, req, '5') + + self.assertRaises( + webob.exc.HTTPBadRequest, self.controller.delete, req, '5') def test_assisted_delete_instance_invalid_state(self): api_error = exception.InstanceInvalidState( diff --git a/nova/tests/unit/api/openstack/fakes.py b/nova/tests/unit/api/openstack/fakes.py index 8cf90ddebe..9ac970f787 100644 --- a/nova/tests/unit/api/openstack/fakes.py +++ b/nova/tests/unit/api/openstack/fakes.py @@ -240,6 +240,9 @@ class HTTPRequest(os_wsgi.Request): def blank(cls, *args, **kwargs): defaults = {'base_url': 'http://localhost/v2'} use_admin_context = kwargs.pop('use_admin_context', False) + roles = kwargs.pop('roles', []) + if use_admin_context: + roles.append('admin') project_id = kwargs.pop('project_id', FAKE_PROJECT_ID) version = kwargs.pop('version', os_wsgi.DEFAULT_API_VERSION) defaults.update(kwargs) @@ -247,10 +250,19 @@ class HTTPRequest(os_wsgi.Request): out.environ['nova.context'] = FakeRequestContext( user_id='fake_user', project_id=project_id, - is_admin=use_admin_context) + is_admin=use_admin_context, + roles=roles) out.api_version_request = api_version.APIVersionRequest(version) return out + @classmethod + def member_req(cls, *args, **kwargs): + return cls.blank(*args, roles=['member', 'reader'], **kwargs) + + @classmethod + def reader_req(cls, *args, **kwargs): + return cls.blank(*args, roles=['reader'], **kwargs) + class HTTPRequestV21(HTTPRequest): pass diff --git a/nova/tests/unit/api/openstack/test_common.py b/nova/tests/unit/api/openstack/test_common.py index 4666413e27..7fe98bd52e 100644 --- a/nova/tests/unit/api/openstack/test_common.py +++ b/nova/tests/unit/api/openstack/test_common.py @@ -17,7 +17,8 @@ Test suites for 'common' code used throughout the OpenStack HTTP API. """ -import mock +from unittest import mock + from testtools import matchers import webob import webob.exc diff --git a/nova/tests/unit/api/openstack/test_faults.py b/nova/tests/unit/api/openstack/test_faults.py index 1bd56a87c5..c7dd5c0a9d 100644 --- a/nova/tests/unit/api/openstack/test_faults.py +++ b/nova/tests/unit/api/openstack/test_faults.py @@ -14,7 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_serialization import jsonutils import webob import webob.dec diff --git a/nova/tests/unit/api/openstack/test_requestlog.py b/nova/tests/unit/api/openstack/test_requestlog.py index 0ea91439cc..7e79e1b079 100644 --- a/nova/tests/unit/api/openstack/test_requestlog.py +++ b/nova/tests/unit/api/openstack/test_requestlog.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock import fixtures as fx import testtools diff --git a/nova/tests/unit/api/openstack/test_wsgi.py b/nova/tests/unit/api/openstack/test_wsgi.py index e0cf8f6fd8..76554e1fcb 100644 --- a/nova/tests/unit/api/openstack/test_wsgi.py +++ b/nova/tests/unit/api/openstack/test_wsgi.py @@ -10,7 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_serialization import jsonutils import testscenarios import webob diff --git a/nova/tests/unit/api/openstack/test_wsgi_app.py b/nova/tests/unit/api/openstack/test_wsgi_app.py index 247886b9dd..0eb7011c11 100644 --- a/nova/tests/unit/api/openstack/test_wsgi_app.py +++ b/nova/tests/unit/api/openstack/test_wsgi_app.py @@ -11,9 +11,9 @@ # under the License. import tempfile +from unittest import mock import fixtures -import mock from oslo_config import fixture as config_fixture from oslotest import base @@ -104,3 +104,18 @@ document_root = /tmp 'disable_compute_service_check_for_ffu', True, group='workarounds') wsgi_app._setup_service('myhost', 'api') + + def test__get_config_files_empty_env(self): + env = {} + result = wsgi_app._get_config_files(env) + expected = ['/etc/nova/api-paste.ini', '/etc/nova/nova.conf'] + self.assertEqual(result, expected) + + def test__get_config_files_with_env(self): + env = { + "OS_NOVA_CONFIG_DIR": "/nova", + "OS_NOVA_CONFIG_FILES": "api.conf", + } + result = wsgi_app._get_config_files(env) + expected = ['/nova/api.conf'] + self.assertEqual(result, expected) diff --git a/nova/tests/unit/api/test_auth.py b/nova/tests/unit/api/test_auth.py index 3be245b90e..3bc5f51b04 100644 --- a/nova/tests/unit/api/test_auth.py +++ b/nova/tests/unit/api/test_auth.py @@ -12,7 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_middleware import request_id from oslo_serialization import jsonutils import webob diff --git a/nova/tests/unit/api/test_wsgi.py b/nova/tests/unit/api/test_wsgi.py index b2701dc723..b8f215c730 100644 --- a/nova/tests/unit/api/test_wsgi.py +++ b/nova/tests/unit/api/test_wsgi.py @@ -20,8 +20,8 @@ Test WSGI basics and provide some helper functions for other WSGI tests. """ import sys +from unittest import mock -import mock import routes import webob diff --git a/nova/tests/unit/api/validation/extra_specs/test_validators.py b/nova/tests/unit/api/validation/extra_specs/test_validators.py index 969fb9b648..a8911aadad 100644 --- a/nova/tests/unit/api/validation/extra_specs/test_validators.py +++ b/nova/tests/unit/api/validation/extra_specs/test_validators.py @@ -28,7 +28,7 @@ class TestValidators(test.NoDBTestCase): """ namespaces = { 'accel', 'aggregate_instance_extra_specs', 'capabilities', 'hw', - 'hw_rng', 'hw_video', 'os', 'pci_passthrough', 'powervm', 'quota', + 'hw_rng', 'hw_video', 'os', 'pci_passthrough', 'quota', 'resources(?P<group>([a-zA-Z0-9_-]{1,64})?)', 'trait(?P<group>([a-zA-Z0-9_-]{1,64})?)', 'vmware', } @@ -74,6 +74,10 @@ class TestValidators(test.NoDBTestCase): ('hw:pci_numa_affinity_policy', 'preferred'), ('hw:pci_numa_affinity_policy', 'socket'), ('hw:cpu_policy', 'mixed'), + ('hw:viommu_model', 'auto'), + ('hw:viommu_model', 'intel'), + ('hw:viommu_model', 'smmuv3'), + ('hw:viommu_model', 'virtio'), ) for key, value in valid_specs: validators.validate(key, value) @@ -92,6 +96,7 @@ class TestValidators(test.NoDBTestCase): ('hw:pci_numa_affinity_policy', 'requird'), ('hw:pci_numa_affinity_policy', 'prefrred'), ('hw:pci_numa_affinity_policy', 'socet'), + ('hw:viommu_model', 'autt'), ) for key, value in invalid_specs: with testtools.ExpectedException(exception.ValidationError): @@ -101,9 +106,7 @@ class TestValidators(test.NoDBTestCase): valid_specs = ( ('hw:numa_nodes', '1'), ('os:monitors', '1'), - ('powervm:shared_weight', '1'), ('os:monitors', '8'), - ('powervm:shared_weight', '255'), ) for key, value in valid_specs: validators.validate(key, value) @@ -113,9 +116,7 @@ class TestValidators(test.NoDBTestCase): ('hw:serial_port_count', '!'), # NaN ('hw:numa_nodes', '0'), # has min ('os:monitors', '0'), # has min - ('powervm:shared_weight', '-1'), # has min ('os:monitors', '9'), # has max - ('powervm:shared_weight', '256'), # has max ) for key, value in invalid_specs: with testtools.ExpectedException(exception.ValidationError): |