diff --git a/bin/nova-manage b/bin/nova-manage index 6a51f1102..a271cf2ff 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -1607,7 +1607,7 @@ class VsaDriveTypeCommands(object): """Marks volume types as deleted""" try: volume_types.destroy(self.context, name) - except exception.ApiError: + except exception.InvalidVolumeType: print "Valid volume type name is required" sys.exit(1) except exception.DBError, e: @@ -1786,7 +1786,7 @@ class InstanceTypeCommands(object): """Marks instance types / flavors as deleted""" try: instance_types.destroy(name) - except exception.ApiError: + except exception.InstanceTypeNotFound: print "Valid instance type name is required" sys.exit(1) except exception.DBError, e: diff --git a/nova/exception.py b/nova/exception.py index 6f3d84717..dfa20dd25 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -80,7 +80,7 @@ class Error(Exception): super(Error, self).__init__(message) -class ApiError(Error): +class EC2APIError(Error): def __init__(self, message='Unknown', code=None): self.msg = message self.code = code @@ -88,7 +88,7 @@ class ApiError(Error): outstr = '%s: %s' % (code, message) else: outstr = '%s' % message - super(ApiError, self).__init__(outstr) + super(EC2APIError, self).__init__(outstr) class DBError(Error): @@ -223,6 +223,14 @@ class Invalid(NovaException): message = _("Unacceptable parameters.") +class InvalidSnapshot(Invalid): + message = _("Invalid snapshot") + ": %(reason)s" + + +class VolumeUnattached(Invalid): + message = _("Volume %(volume_id)s is not attached to anything") + + class InvalidKeypair(Invalid): message = _("Keypair data is invalid") @@ -248,7 +256,11 @@ class InvalidInstanceType(Invalid): class InvalidVolumeType(Invalid): - message = _("Invalid volume type %(volume_type)s.") + message = _("Invalid volume type") + ": %(reason)s" + + +class InvalidVolume(Invalid): + message = _("Invalid volume") + ": %(reason)s" class InvalidPortRange(Invalid): @@ -930,9 +942,8 @@ class WillNotSchedule(NovaException): message = _("Host %(host)s is not up or doesn't exist.") -class QuotaError(ApiError): - """Quota Exceeded.""" - pass +class QuotaError(NovaException): + message = _("Quota exceeded") + ": code=%(code)s" class AggregateNotFound(NotFound): @@ -962,3 +973,24 @@ class AggregateHostExists(Duplicate): class DuplicateSfVolumeNames(Duplicate): message = _("Detected more than one volume with name %(vol_name)") + + +class VolumeTypeCreateFailed(NovaException): + message = _("Cannot create volume_type with " + "name %(name)s and specs %(extra_specs)s") + + +class InstanceTypeCreateFailed(NovaException): + message = _("Unable to create instance type") + + +class SolidFireAPIException(NovaException): + message = _("Bad response from SolidFire API") + + +class SolidFireAPIStatusException(SolidFireAPIException): + message = _("Error in SolidFire API response: status=%(status)s") + + +class SolidFireAPIDataException(SolidFireAPIException): + message = _("Error in SolidFire API response: data=%(data)s") diff --git a/nova/tests/scheduler/test_vsa_scheduler.py b/nova/tests/scheduler/test_vsa_scheduler.py index c4cf1fd3c..8e465271d 100644 --- a/nova/tests/scheduler/test_vsa_scheduler.py +++ b/nova/tests/scheduler/test_vsa_scheduler.py @@ -13,14 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. -from nova import context from nova import db from nova import exception from nova import flags from nova import log as logging from nova import rpc from nova.scheduler import vsa as vsa_sched -from nova import test from nova.tests.scheduler import test_scheduler from nova import utils from nova.volume import volume_types diff --git a/nova/tests/test_SolidFireSanISCSIDriver.py b/nova/tests/test_SolidFireSanISCSIDriver.py index f5f4f47eb..0f3addddc 100644 --- a/nova/tests/test_SolidFireSanISCSIDriver.py +++ b/nova/tests/test_SolidFireSanISCSIDriver.py @@ -176,5 +176,5 @@ class SolidFireVolumeTestCase(test.TestCase): def test_get_cluster_info_fail(self): SFID._issue_api_request = self.fake_issue_api_request_fails sfv = SFID() - self.assertRaises(exception.ApiError, + self.assertRaises(exception.SolidFireAPIException, sfv._get_cluster_info) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 3bab360c4..c6e3201d3 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -2357,7 +2357,7 @@ class ComputeAPITestCase(BaseTestCase): address = '0.1.2.3' self.compute.run_instance(self.context, instance_uuid) - self.assertRaises(exception.ApiError, + self.assertRaises(exception.FixedIpNotFoundForInstance, self.compute_api.associate_floating_ip, self.context, instance, @@ -2967,7 +2967,7 @@ class ComputeAPITestCase(BaseTestCase): self.compute_api.delete(self.context, instance) def test_attach_volume_invalid(self): - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InvalidDevicePath, self.compute_api.attach_volume, self.context, None, diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 7baa9542f..dfa7d6136 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -85,7 +85,7 @@ class InstanceTypeTestCase(test.TestCase): 'instance type was not created') instance_types.destroy(name) - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InstanceTypeNotFound, instance_types.get_instance_type, inst_type_id) # deleted instance should not be in list anymoer @@ -133,7 +133,7 @@ class InstanceTypeTestCase(test.TestCase): 'unknown_flavor') def test_duplicate_names_fail(self): - """Ensures that name duplicates raise ApiError""" + """Ensures that name duplicates raise InstanceTypeCreateFailed""" name = 'some_name' instance_types.create(name, 256, 1, 120, 200, 'flavor1') self.assertRaises(exception.InstanceTypeExists, @@ -141,7 +141,7 @@ class InstanceTypeTestCase(test.TestCase): name, 256, 1, 120, 200, 'flavor2') def test_duplicate_flavorids_fail(self): - """Ensures that flavorid duplicates raise ApiError""" + """Ensures that flavorid duplicates raise InstanceTypeCreateFailed""" flavorid = 'flavor1' instance_types.create('name one', 256, 1, 120, 200, flavorid) self.assertRaises(exception.InstanceTypeExists, @@ -156,7 +156,7 @@ class InstanceTypeTestCase(test.TestCase): def test_will_not_get_bad_default_instance_type(self): """ensures error raised on bad default instance type""" self.flags(default_instance_type='unknown_flavor') - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InstanceTypeNotFound, instance_types.get_default_instance_type) def test_will_get_instance_type_by_id(self): @@ -167,12 +167,12 @@ class InstanceTypeTestCase(test.TestCase): def test_will_not_get_instance_type_by_unknown_id(self): """Ensure get by name returns default flavor with no name""" - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InstanceTypeNotFound, instance_types.get_instance_type, 10000) def test_will_not_get_instance_type_with_bad_id(self): """Ensure get by name returns default flavor with bad name""" - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InstanceTypeNotFound, instance_types.get_instance_type, 'asdf') def test_instance_type_get_by_None_name_returns_default(self): @@ -183,7 +183,7 @@ class InstanceTypeTestCase(test.TestCase): def test_will_not_get_instance_type_with_bad_name(self): """Ensure get by name returns default flavor with bad name""" - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InstanceTypeNotFound, instance_types.get_instance_type_by_name, 10000) def test_will_not_get_instance_by_unknown_flavor_id(self): diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py index 3f3893e39..7d7ed614c 100644 --- a/nova/tests/test_volume.py +++ b/nova/tests/test_volume.py @@ -249,7 +249,7 @@ class VolumeTestCase(test.TestCase): volume_api = nova.volume.api.API() volume = volume_api.get(self.context, volume['id']) - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InvalidVolume, volume_api.create_snapshot, self.context, volume, 'fake_name', 'fake_description') diff --git a/nova/tests/test_volume_types.py b/nova/tests/test_volume_types.py index 7702d0ecc..b0663d1b6 100644 --- a/nova/tests/test_volume_types.py +++ b/nova/tests/test_volume_types.py @@ -85,7 +85,7 @@ class VolumeTypeTestCase(test.TestCase): def test_non_existant_vol_type_shouldnt_delete(self): """Ensures that volume type creation fails with invalid args""" - self.assertRaises(exception.ApiError, + self.assertRaises(exception.VolumeTypeNotFoundByName, volume_types.destroy, self.ctxt, "sfsfsdfdfs") def test_repeated_vol_types_shouldnt_raise(self): diff --git a/nova/tests/test_vsa.py b/nova/tests/test_vsa.py index 21084d378..0089d8a2f 100644 --- a/nova/tests/test_vsa.py +++ b/nova/tests/test_vsa.py @@ -90,7 +90,7 @@ class VsaTestCase(test.TestCase): def test_vsa_create_wrong_image_name(self): param = {'image_name': 'wrong_image_name'} - self.assertRaises(exception.ApiError, + self.assertRaises(exception.ImageNotFound, self.vsa_api.create, self.context, **param) def test_vsa_create_db_error(self): @@ -100,19 +100,19 @@ class VsaTestCase(test.TestCase): raise exception.Error self.stubs.Set(nova.db, 'vsa_create', fake_vsa_create) - self.assertRaises(exception.ApiError, + self.assertRaises(exception.Error, self.vsa_api.create, self.context) def test_vsa_create_wrong_storage_params(self): vsa_list1 = self.vsa_api.get_all(self.context) param = {'storage': [{'stub': 1}]} - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InvalidVolumeType, self.vsa_api.create, self.context, **param) vsa_list2 = self.vsa_api.get_all(self.context) self.assertEqual(len(vsa_list2), len(vsa_list1)) param = {'storage': [{'drive_name': 'wrong name'}]} - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InvalidVolumeType, self.vsa_api.create, self.context, **param) def test_vsa_create_with_storage(self, multi_vol_creation=True): diff --git a/nova/tests/test_vsa_volumes.py b/nova/tests/test_vsa_volumes.py index a4bab624a..bd0ce2921 100644 --- a/nova/tests/test_vsa_volumes.py +++ b/nova/tests/test_vsa_volumes.py @@ -107,14 +107,14 @@ class VsaVolumesTestCase(test.TestCase): 'deleting') def test_vsa_volume_delete_nonavail_volume(self): - """ Check volume deleton in different states. """ + """ Check volume deletion in different states. """ volume_param = self._default_volume_param() volume_ref = self.volume_api.create(self.context, **volume_param) self.volume_api.update(self.context, volume_ref, {'status': 'in-use'}) - self.assertRaises(exception.ApiError, + self.assertRaises(exception.InvalidVolume, self.volume_api.delete, self.context, volume_ref) diff --git a/nova/vsa/api.py b/nova/vsa/api.py index 63e8e6bee..6482a11c1 100644 --- a/nova/vsa/api.py +++ b/nova/vsa/api.py @@ -23,8 +23,6 @@ For assistance and guidelines pls contact Zadara Storage Inc & Openstack community """ -import sys - from nova import compute from nova import exception from nova import flags @@ -81,8 +79,8 @@ class API(base.Base): vol_type['extra_specs'].get('drive_type') is None or vol_type['extra_specs'].get('drive_size') is None): - raise exception.ApiError(_("Invalid drive type %s") - % vol_type['name']) + msg = _("invalid drive data") + raise exception.InvalidVolumeType(reason=msg) def _get_default_vsa_instance_type(self): return instance_types.get_instance_type_by_name( @@ -104,13 +102,14 @@ class API(base.Base): num_disks = node.get('num_drives', 1) if name is None: - raise exception.ApiError(_("No drive_name param found in %s") - % node) + msg = _("drive_name not defined") + raise exception.InvalidVolumeType(reason=msg) + try: vol_type = volume_types.get_volume_type_by_name(context, name) except exception.NotFound: - raise exception.ApiError(_("Invalid drive type name %s") - % name) + msg = _("invalid drive type name %s") + raise exception.InvalidVolumeType(reason=msg % name) self._check_volume_type_correctness(vol_type) @@ -177,13 +176,10 @@ class API(base.Base): # check if image is ready before starting any work if image_name is None: image_name = FLAGS.vc_image_name - try: - image_service = self.compute_api.image_service - vc_image = image_service.show_by_name(context, image_name) - vc_image_href = vc_image['id'] - except exception.ImageNotFound: - raise exception.ApiError(_("Failed to find configured image %s") - % image_name) + + image_service = self.compute_api.image_service + vc_image = image_service.show_by_name(context, image_name) + vc_image_href = vc_image['id'] options = { 'display_name': display_name, @@ -198,10 +194,8 @@ class API(base.Base): LOG.info(_("Creating VSA: %s") % options) # create DB entry for VSA instance - try: - vsa_ref = self.db.vsa_create(context, options) - except exception.Error: - raise exception.ApiError(_(sys.exc_info()[1])) + vsa_ref = self.db.vsa_create(context, options) + vsa_id = vsa_ref['id'] vsa_name = vsa_ref['name'] @@ -209,10 +203,9 @@ class API(base.Base): try: volume_params = self._check_storage_parameters(context, vsa_name, storage, shared) - except exception.ApiError: + except exception.InvalidVolumeType: self.db.vsa_destroy(context, vsa_id) - raise exception.ApiError(_("Error in storage parameters: %s") - % storage) + raise # after creating DB entry, re-check and set some defaults updates = {} @@ -358,7 +351,7 @@ class API(base.Base): LOG.info(_("VSA ID %(vsa_id)s: Deleting %(direction)s " "volume %(vol_name)s"), locals()) self.volume_api.delete(context, volume) - except exception.ApiError: + except exception.InvalidVolume: LOG.info(_("Unable to delete volume %s"), volume['name']) if force_delete: LOG.info(_("VSA ID %(vsa_id)s: Forced delete. "