diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 35b11e754dd9..6db63a0e1522 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1733,6 +1733,12 @@ class ComputeManager(manager.Manager): block_device_info['swap']) return block_device_info + except exception.OverQuota: + msg = ('Failed to create block device for instance due to being ' + 'over volume resource quota') + LOG.debug(msg, instance=instance) + raise exception.InvalidBDM() + except Exception: LOG.exception(_('Instance failed block device setup'), instance=instance) diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index a61a45fcc733..13fbc0d6cd86 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -1035,6 +1035,27 @@ class ComputeVolumeTestCase(BaseTestCase): self.compute.volume_snapshot_delete, self.context, self.instance_object, 'fake_id', 'fake_id2', {}) + @mock.patch.object(cinder.API, 'create', + side_effect=exception.OverQuota(overs='volumes')) + def test_prep_block_device_over_quota_failure(self, mock_create): + instance = self._create_fake_instance() + bdms = [ + block_device.BlockDeviceDict({ + 'boot_index': 0, + 'guest_format': None, + 'connection_info': None, + 'device_type': u'disk', + 'source_type': 'image', + 'destination_type': 'volume', + 'volume_size': 1, + 'image_id': 1, + 'device_name': '/dev/vdb', + })] + self.assertRaises(exception.InvalidBDM, + compute_manager.ComputeManager()._prep_block_device, + self.context, instance, bdms) + mock_create.assert_called_once() + class ComputeTestCase(BaseTestCase): def test_wrap_instance_fault(self): @@ -1513,6 +1534,30 @@ class ComputeTestCase(BaseTestCase): self._assert_state({'vm_state': vm_states.ERROR, 'task_state': None}) + @mock.patch('nova.compute.manager.ComputeManager._prep_block_device', + side_effect=exception.OverQuota(overs='volumes')) + def test_setup_block_device_over_quota_fail(self, mock_prep_block_dev): + """block device mapping over quota failure test. + + Make sure when we're over volume quota according to Cinder client, the + appropriate exception is raised and the instances to ERROR state, keep + the task state. + """ + instance = self._create_fake_instance() + self.assertRaises(exception.OverQuota, self.compute.run_instance, + self.context, instance=instance, request_spec={}, + filter_properties={}, requested_networks=[], + injected_files=None, admin_password=None, + is_first_time=True, node=None, + legacy_bdm_in_spec=False) + #check state is failed even after the periodic poll + self._assert_state({'vm_state': vm_states.ERROR, + 'task_state': None}) + self.compute.periodic_tasks(context.get_admin_context()) + self._assert_state({'vm_state': vm_states.ERROR, + 'task_state': None}) + mock_prep_block_dev.assert_called_once() + def test_run_instance_spawn_fail(self): """spawn failure test. diff --git a/nova/tests/volume/test_cinder.py b/nova/tests/volume/test_cinder.py index c12bdc2dc866..9aabdff8c905 100644 --- a/nova/tests/volume/test_cinder.py +++ b/nova/tests/volume/test_cinder.py @@ -14,6 +14,7 @@ # under the License. from cinderclient import exceptions as cinder_exception +import mock from nova import context from nova import exception @@ -84,6 +85,17 @@ class CinderApiTestCase(test.NoDBTestCase): self.assertRaises(exception.InvalidInput, self.api.create, self.ctx, 1, '', '') + @mock.patch('nova.volume.cinder.cinderclient') + def test_create_over_quota_failed(self, mock_cinderclient): + mock_cinderclient.return_value.volumes.create.side_effect = ( + cinder_exception.OverLimit(413)) + self.assertRaises(exception.OverQuota, self.api.create, self.ctx, + 1, '', '') + mock_cinderclient.return_value.volumes.create.assert_called_once_with( + 1, user_id=None, imageRef=None, availability_zone=None, + volume_type=None, display_description='', snapshot_id=None, + display_name='', project_id=None, metadata=None) + def test_get_all(self): cinder.cinderclient(self.ctx).AndReturn(self.cinderclient) cinder._untranslate_volume_summary_view(self.ctx, diff --git a/nova/volume/cinder.py b/nova/volume/cinder.py index f55ee61d4a1e..48c5f5ea65c4 100644 --- a/nova/volume/cinder.py +++ b/nova/volume/cinder.py @@ -301,6 +301,8 @@ class API(object): try: item = cinderclient(context).volumes.create(size, **kwargs) return _untranslate_volume_summary_view(context, item) + except cinder_exception.OverLimit: + raise exception.OverQuota(overs='volumes') except cinder_exception.BadRequest as e: raise exception.InvalidInput(reason=unicode(e))