diff --git a/nova/api/openstack/compute/volumes.py b/nova/api/openstack/compute/volumes.py index 7dc2caaebed8..c16944157d28 100644 --- a/nova/api/openstack/compute/volumes.py +++ b/nova/api/openstack/compute/volumes.py @@ -549,7 +549,7 @@ class SnapshotController(wsgi.Controller): res = [entity_maker(context, snapshot) for snapshot in limited_list] return {'snapshots': res} - @extensions.expected_errors(400) + @extensions.expected_errors((400, 403)) @validation.schema(volumes_schema.snapshot_create) def create(self, req, body): """Creates a new snapshot.""" @@ -566,9 +566,12 @@ class SnapshotController(wsgi.Controller): else: create_func = self.volume_api.create_snapshot - new_snapshot = create_func(context, volume_id, - snapshot.get('display_name'), - snapshot.get('display_description')) + try: + new_snapshot = create_func(context, volume_id, + snapshot.get('display_name'), + snapshot.get('display_description')) + except exception.OverQuota as e: + raise exc.HTTPForbidden(explanation=e.format_message()) retval = _translate_snapshot_detail_view(context, new_snapshot) return {'snapshot': retval} diff --git a/nova/tests/functional/api/client.py b/nova/tests/functional/api/client.py index ef20474cc25c..565ff78552cb 100644 --- a/nova/tests/functional/api/client.py +++ b/nova/tests/functional/api/client.py @@ -317,6 +317,19 @@ class TestOpenStackClient(object): def delete_volume(self, volume_id): return self.api_delete('/os-volumes/%s' % volume_id) + def get_snapshot(self, snap_id): + return self.api_get('/os-snapshots/%s' % snap_id).body['snapshot'] + + def get_snapshots(self, detail=True): + rel_url = '/os-snapshots/detail' if detail else '/os-snapshots' + return self.api_get(rel_url).body['snapshots'] + + def post_snapshot(self, snapshot): + return self.api_post('/os-snapshots', snapshot).body['snapshot'] + + def delete_snapshot(self, snap_id): + return self.api_delete('/os-snapshots/%s' % snap_id) + def get_server_volume(self, server_id, attachment_id): return self.api_get('/servers/%s/os-volume_attachments/%s' % (server_id, attachment_id) diff --git a/nova/tests/functional/regressions/test_bug_1554631.py b/nova/tests/functional/regressions/test_bug_1554631.py index e7a6d5f50f7a..f24fb07ea3b9 100644 --- a/nova/tests/functional/regressions/test_bug_1554631.py +++ b/nova/tests/functional/regressions/test_bug_1554631.py @@ -45,3 +45,68 @@ class TestCinderForbidden(test.TestCase): e = self.assertRaises(client.OpenStackApiException, self.api.post_volume, {'volume': volume}) self.assertEqual(403, e.response.status_code) + + +class TestCinderOverLimit(test.TestCase): + def setUp(self): + super(TestCinderOverLimit, self).setUp() + self.useFixture(policy_fixture.RealPolicyFixture()) + api_fixture = self.useFixture(nova_fixtures.OSAPIFixture( + api_version='v2.1')) + + self.api = api_fixture.api + + @mock.patch('nova.volume.cinder.cinderclient') + def test_over_limit_volumes(self, mock_cinder): + """Regression test for bug #1554631. + + When the Cinder client returns OverLimit when trying to create + a volume, an OverQuota exception should be raised with the value being + volumes. + """ + cinder_client = mock.Mock() + mock_cinder.return_value = cinder_client + exc = cinder_exceptions.OverLimit('') + cinder_client.volumes.create.side_effect = exc + + volume = {'display_name': 'vol1', 'size': 3} + e = self.assertRaises(client.OpenStackApiException, + self.api.post_volume, {'volume': volume}) + self.assertEqual(403, e.response.status_code) + # Make sure we went over on volumes + self.assertIn('volumes', e.response.content) + + @mock.patch('nova.volume.cinder.cinderclient') + def test_over_limit_snapshots(self, mock_cinder): + """Regression test for bug #1554631. + + When the Cinder client returns OverLimit when trying to create a + snapshot, an OverQuota exception should be raised with the value being + snapshots. + """ + self._do_snapshot_over_test(mock_cinder) + + @mock.patch('nova.volume.cinder.cinderclient') + def test_over_limit_snapshots_force(self, mock_cinder): + """Regression test for bug #1554631. + + When the Cinder client returns OverLimit when trying to create a + snapshot, an OverQuota exception should be raised with the value being + snapshots. (create_snapshot_force version) + """ + self._do_snapshot_over_test(mock_cinder, force=True) + + def _do_snapshot_over_test(self, mock_cinder, force=False): + cinder_client = mock.Mock() + mock_cinder.return_value = cinder_client + exc = cinder_exceptions.OverLimit('') + cinder_client.volume_snapshots.create.side_effect = exc + + snap = {'display_name': 'snap1', + 'volume_id': '521752a6-acf6-4b2d-bc7a-119f9148cd8c', + 'force': force} + e = self.assertRaises(client.OpenStackApiException, + self.api.post_snapshot, {'snapshot': snap}) + self.assertEqual(403, e.response.status_code) + # Make sure we went over on snapshots + self.assertIn('snapshots', e.response.content)