Replace ApiError with new exceptions

* Convert ApiError to EC2APIError
* Add new exceptions to replace ApiError where it didn't belong
* Fixes bug 926250

Change-Id: Ia711440ee0313faf8ea8c87e2c0a2f5b39cc55a2
This commit is contained in:
Brian Waldon
2012-02-03 13:29:57 -08:00
parent c9ca372b0b
commit afd5b22368
35 changed files with 237 additions and 229 deletions

View File

@@ -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:

View File

@@ -598,8 +598,8 @@ class Executor(wsgi.Application):
except exception.NotFound as ex:
LOG.info(_('NotFound raised: %s'), unicode(ex), context=context)
return ec2_error(req, request_id, type(ex).__name__, unicode(ex))
except exception.ApiError as ex:
LOG.exception(_('ApiError raised: %s'), unicode(ex),
except exception.EC2APIError as ex:
LOG.exception(_('EC2APIError raised: %s'), unicode(ex),
context=context)
if ex.code:
return ec2_error(req, request_id, ex.code, unicode(ex))

View File

@@ -600,7 +600,7 @@ class CloudController(object):
group_id=None, **kwargs):
if not group_name and not group_id:
err = "Not enough parameters, need group_name or group_id"
raise exception.ApiError(_(err))
raise exception.EC2APIError(_(err))
self.compute_api.ensure_default_security_group(context)
notfound = exception.SecurityGroupNotFound
if group_name:
@@ -626,7 +626,7 @@ class CloudController(object):
rulesvalues = self._rule_args_to_dict(context, values)
if not rulesvalues:
err = "%s Not enough parameters to build a valid rule"
raise exception.ApiError(_(err % rulesvalues))
raise exception.EC2APIError(_(err % rulesvalues))
for values_for_rule in rulesvalues:
values_for_rule['parent_group_id'] = security_group.id
@@ -640,7 +640,7 @@ class CloudController(object):
context,
security_group_id=security_group['id'])
return True
raise exception.ApiError(_("No rule for the specified parameters."))
raise exception.EC2APIError(_("No rule for the specified parameters."))
# TODO(soren): This has only been tested with Boto as the client.
# Unfortunately, it seems Boto is using an old API
@@ -650,7 +650,7 @@ class CloudController(object):
group_id=None, **kwargs):
if not group_name and not group_id:
err = "Not enough parameters, need group_name or group_id"
raise exception.ApiError(_(err))
raise exception.EC2APIError(_(err))
self.compute_api.ensure_default_security_group(context)
notfound = exception.SecurityGroupNotFound
if group_name:
@@ -676,13 +676,13 @@ class CloudController(object):
rulesvalues = self._rule_args_to_dict(context, values)
if not rulesvalues:
err = "%s Not enough parameters to build a valid rule"
raise exception.ApiError(_(err % rulesvalues))
raise exception.EC2APIError(_(err % rulesvalues))
for values_for_rule in rulesvalues:
values_for_rule['parent_group_id'] = security_group.id
if self._security_group_rule_exists(security_group,
values_for_rule):
err = '%s - This rule already exists in group'
raise exception.ApiError(_(err) % values_for_rule)
raise exception.EC2APIError(_(err) % values_for_rule)
postvalues.append(values_for_rule)
for values_for_rule in postvalues:
@@ -696,7 +696,7 @@ class CloudController(object):
security_group_id=security_group['id'])
return True
raise exception.ApiError(_("No rule for the specified parameters."))
raise exception.EC2APIError(_("No rule for the specified parameters."))
def _get_source_project_id(self, context, source_security_group_owner_id):
if source_security_group_owner_id:
@@ -735,7 +735,8 @@ class CloudController(object):
LOG.audit(_("Create Security Group %s"), group_name, context=context)
self.compute_api.ensure_default_security_group(context)
if db.security_group_exists(context, context.project_id, group_name):
raise exception.ApiError(_('group %s already exists') % group_name)
msg = _('group %s already exists')
raise exception.EC2APIError(msg % group_name)
group = {'user_id': context.user_id,
'project_id': context.project_id,
@@ -750,7 +751,7 @@ class CloudController(object):
**kwargs):
if not group_name and not group_id:
err = "Not enough parameters, need group_name or group_id"
raise exception.ApiError(_(err))
raise exception.EC2APIError(_(err))
notfound = exception.SecurityGroupNotFound
if group_name:
security_group = db.security_group_get_by_name(context,
@@ -906,7 +907,7 @@ class CloudController(object):
def describe_instance_attribute(self, context, instance_id, attribute,
**kwargs):
def _unsupported_attribute(instance, result):
raise exception.ApiError(_('attribute not supported: %s') %
raise exception.EC2APIError(_('attribute not supported: %s') %
attribute)
def _format_attr_block_device_mapping(instance, result):
@@ -962,7 +963,7 @@ class CloudController(object):
fn = attribute_formatter.get(attribute)
if fn is None:
raise exception.ApiError(
raise exception.EC2APIError(
_('attribute not supported: %s') % attribute)
ec2_instance_id = instance_id
@@ -1225,7 +1226,7 @@ class CloudController(object):
raise exception.ImageNotFound(image_id=kwargs['image_id'])
if image_state != 'available':
raise exception.ApiError(_('Image must be available'))
raise exception.EC2APIError(_('Image must be available'))
(instances, resv_id) = self.compute_api.create(context,
instance_type=instance_types.get_instance_type_by_name(
@@ -1425,7 +1426,7 @@ class CloudController(object):
fn = supported_attributes.get(attribute)
if fn is None:
raise exception.ApiError(_('attribute not supported: %s')
raise exception.EC2APIError(_('attribute not supported: %s')
% attribute)
try:
image = self._get_image(context, image_id)
@@ -1440,14 +1441,15 @@ class CloudController(object):
operation_type, **kwargs):
# TODO(devcamcar): Support users and groups other than 'all'.
if attribute != 'launchPermission':
raise exception.ApiError(_('attribute not supported: %s')
raise exception.EC2APIError(_('attribute not supported: %s')
% attribute)
if not 'user_group' in kwargs:
raise exception.ApiError(_('user or group not specified'))
raise exception.EC2APIError(_('user or group not specified'))
if len(kwargs['user_group']) != 1 and kwargs['user_group'][0] != 'all':
raise exception.ApiError(_('only group "all" is supported'))
raise exception.EC2APIError(_('only group "all" is supported'))
if not operation_type in ['add', 'remove']:
raise exception.ApiError(_('operation_type must be add or remove'))
msg = _('operation_type must be add or remove')
raise exception.EC2APIError(msg)
LOG.audit(_("Updating image %s publicity"), image_id, context=context)
try:
@@ -1504,7 +1506,7 @@ class CloudController(object):
# Or is there any better way?
timeout = 1 * 60 * 60 * 60
if time.time() > start_time + timeout:
raise exception.ApiError(
raise exception.EC2APIError(
_('Couldn\'t stop instance with in %d sec') % timeout)
src_image = self._get_image(context, instance['image_ref'])

View File

@@ -116,7 +116,7 @@ class CloudpipeController(object):
except db.NoMoreNetworks:
msg = _("Unable to claim IP for VPN instances, ensure it "
"isn't running, and try again in a few minutes")
raise exception.ApiError(msg)
raise exception.HTTPBadRequest(explanation=msg)
instance = self._get_cloudpipe_for_project(ctxt, proj)
return {'instance_id': instance['uuid']}

View File

@@ -54,8 +54,8 @@ class ConsoleOutputController(wsgi.Controller):
output = self.compute_api.get_console_output(context,
instance,
length)
except exception.ApiError, e:
raise webob.exc.HTTPBadRequest(explanation=e.message)
except exception.NotFound:
raise webob.exc.HTTPNotFound(_('Instance not found'))
return {'output': output}

View File

@@ -54,10 +54,10 @@ class ConsolesController(wsgi.Controller):
console_type)
except exception.ConsoleTypeInvalid, e:
raise webob.exc.HTTPBadRequest(_('Invalid type specification'))
except exception.ApiError, e:
raise webob.exc.HTTPBadRequest(explanation=e.message)
except exception.NotAuthorized, e:
raise webob.exc.HTTPUnauthorized()
except exception.NotFound:
raise webob.exc.HTTPNotFound(_('Instance not found'))
return {'console': {'type': console_type, 'url': output['url']}}

View File

@@ -204,14 +204,14 @@ class FloatingIPActionController(wsgi.Controller):
msg = _("Address not specified")
raise webob.exc.HTTPBadRequest(explanation=msg)
try:
instance = self.compute_api.get(context, id)
try:
self.compute_api.associate_floating_ip(context, instance,
address)
except exception.ApiError, e:
raise webob.exc.HTTPBadRequest(explanation=e.message)
except exception.NotAuthorized, e:
raise webob.exc.HTTPUnauthorized()
except exception.FixedIpNotFoundForInstance:
msg = _("No fixed ips associated to instance")
raise webob.exc.HTTPBadRequest(explanation=msg)
except rpc.RemoteError:
msg = _("Associate floating ip failed")
raise webob.exc.HTTPInternalServerError(explanation=msg)

View File

@@ -31,35 +31,29 @@ class ServerStartStopActionController(wsgi.Controller):
super(ServerStartStopActionController, self).__init__(*args, **kwargs)
self.compute_api = compute.API()
def _get_instance(self, context, instance_uuid):
try:
return self.compute_api.get(context, instance_uuid)
except exception.NotFound:
msg = _("Instance not found")
raise webob.exc.HTTPNotFound(explanation=msg)
@wsgi.action('os-start')
def _start_server(self, req, id, body):
"""Start an instance. """
context = req.environ['nova.context']
try:
LOG.debug(_("start instance %r"), id)
instance = self.compute_api.get(context, id)
instance = self._get_instance(context, id)
self.compute_api.start(context, instance)
except exception.ApiError, e:
raise webob.exc.HTTPBadRequest(explanation=e.message)
except exception.NotAuthorized, e:
raise webob.exc.HTTPUnauthorized()
return webob.Response(status_int=202)
@wsgi.action('os-stop')
def _stop_server(self, req, id, body):
"""Stop an instance."""
context = req.environ['nova.context']
try:
LOG.debug(_("stop instance %r"), id)
instance = self.compute_api.get(context, id)
instance = self._get_instance(context, id)
self.compute_api.stop(context, instance)
except exception.ApiError, e:
raise webob.exc.HTTPBadRequest(explanation=e.message)
except exception.NotAuthorized, e:
raise webob.exc.HTTPUnauthorized()
return webob.Response(status_int=202)

View File

@@ -100,7 +100,7 @@ class VolumeTypesController(object):
try:
vol_type = volume_types.get_volume_type(context, id)
except exception.NotFound or exception.ApiError:
except exception.NotFound:
raise exc.HTTPNotFound()
return {'volume_type': vol_type}

View File

@@ -165,7 +165,7 @@ class Controller(object):
def _handle_quota_error(self, error):
"""Reraise quota errors as api-specific http exceptions."""
if error.code == "MetadataLimitExceeded":
if error.kwargs['code'] == "MetadataLimitExceeded":
raise exc.HTTPRequestEntityTooLarge(explanation=error.message,
headers={'Retry-After': 0})
raise error

View File

@@ -496,13 +496,9 @@ class Controller(wsgi.Controller):
"InstanceLimitExceeded": error.message,
}
expl = code_mappings.get(error.code)
if expl:
expl = code_mappings.get(error.kwargs['code'], error.message)
raise exc.HTTPRequestEntityTooLarge(explanation=expl,
headers={'Retry-After': 0})
# if the original error is okay, just reraise it
raise exc.HTTPRequestEntityTooLarge(explanation=error.msg,
headers={'Retry-After': 0})
def _validate_server_name(self, value):
if not isinstance(value, basestring):

View File

@@ -64,7 +64,7 @@ class VolumeTypesController(object):
try:
vol_type = volume_types.get_volume_type(context, id)
except exception.NotFound or exception.ApiError:
except exception.NotFound:
raise exc.HTTPNotFound()
return {'volume_type': vol_type}

View File

@@ -136,8 +136,8 @@ class API(base.Base):
content_limit = quota.allowed_injected_file_content_bytes(
context, len(content))
if len(content) > content_limit:
raise exception.QuotaError(
code="OnsetFileContentLimitExceeded")
code = "OnsetFileContentLimitExceeded"
raise exception.QuotaError(code=code)
def _check_metadata_properties_quota(self, context, metadata=None):
"""Enforce quota limits on metadata properties."""
@@ -150,7 +150,7 @@ class API(base.Base):
msg = _("Quota exceeded for %(pid)s, tried to set "
"%(num_metadata)s metadata properties") % locals()
LOG.warn(msg)
raise exception.QuotaError(msg, "MetadataLimitExceeded")
raise exception.QuotaError(code="MetadataLimitExceeded")
# Because metadata is stored in the DB, we hard-code the size limits
# In future, we may support more variable length strings, so we act
@@ -161,7 +161,7 @@ class API(base.Base):
msg = _("Quota exceeded for %(pid)s, metadata property "
"key or value too long") % locals()
LOG.warn(msg)
raise exception.QuotaError(msg, "MetadataLimitExceeded")
raise exception.QuotaError(code="MetadataLimitExceeded")
def _check_requested_networks(self, context, requested_networks):
""" Check if the networks requested belongs to the project
@@ -218,7 +218,7 @@ class API(base.Base):
else:
message = _("Instance quota exceeded. You can only run %s "
"more instances of this type.") % num_instances
raise exception.QuotaError(message, "InstanceLimitExceeded")
raise exception.QuotaError(code="InstanceLimitExceeded")
self._check_metadata_properties_quota(context, metadata)
self._check_injected_file_quota(context, injected_files)
@@ -1631,8 +1631,7 @@ class API(base.Base):
def attach_volume(self, context, instance, volume_id, device):
"""Attach an existing volume to an existing instance."""
if not re.match("^/dev/x{0,1}[a-z]d[a-z]+$", device):
raise exception.ApiError(_("Invalid device specified: %s. "
"Example device: /dev/vdb") % device)
raise exception.InvalidDevicePath(path=device)
volume = self.volume_api.get(context, volume_id)
self.volume_api.check_attach(context, volume)
params = {"volume_id": volume_id,
@@ -1647,7 +1646,7 @@ class API(base.Base):
"""Detach a volume from an instance."""
instance = self.db.volume_get_instance(context.elevated(), volume_id)
if not instance:
raise exception.ApiError(_("Volume isn't attached to anything!"))
raise exception.VolumeUnattached(volume_id=volume_id)
check_policy(context, 'detach_volume', instance)
@@ -1672,30 +1671,29 @@ class API(base.Base):
# in its info, if this changes, the next few lines will need to
# accommodate the info containing floating as well as fixed ip
# addresses
fail_bag = _('instance |%s| has no fixed ips. '
'unable to associate floating ip') % instance_uuid
nw_info = self.network_api.get_instance_nw_info(context.elevated(),
instance)
if nw_info:
if not nw_info:
raise exception.FixedIpNotFoundForInstance(
instance_id=instance_uuid)
ips = [ip for ip in nw_info[0].fixed_ips()]
# TODO(tr3buchet): this will associate the floating IP with the
# first # fixed_ip (lowest id) an instance has. This should be
# changed to # support specifying a particular fixed_ip if
# multiple exist.
if not ips:
raise exception.ApiError(fail_bag)
raise exception.FixedIpNotFoundForInstance(
instance_id=instance_uuid)
# TODO(tr3buchet): this will associate the floating IP with the
# first fixed_ip (lowest id) an instance has. This should be
# changed to support specifying a particular fixed_ip if
# multiple exist.
if len(ips) > 1:
LOG.warning(_('multiple fixedips exist, using the first: %s'),
ips[0]['address'])
msg = _('multiple fixedips exist, using the first: %s')
LOG.warning(msg, ips[0]['address'])
self.network_api.associate_floating_ip(context,
floating_address=address,
fixed_address=ips[0]['address'])
return
raise exception.ApiError(fail_bag)
floating_address=address, fixed_address=ips[0]['address'])
@wrap_check_policy
def get_instance_metadata(self, context, instance):

View File

@@ -72,9 +72,7 @@ def create(name, memory, vcpus, root_gb, ephemeral_gb, flavorid, swap=None,
return db.instance_type_create(context.get_admin_context(), kwargs)
except exception.DBError, e:
LOG.exception(_('DB error: %s') % e)
msg = _("Cannot create instance_type with name %(name)s and "
"flavorid %(flavorid)s") % locals()
raise exception.ApiError(msg)
raise exception.InstanceTypeCreateFailed()
def destroy(name):
@@ -106,10 +104,7 @@ get_all_flavors = get_all_types
def get_default_instance_type():
"""Get the default instance type."""
name = FLAGS.default_instance_type
try:
return get_instance_type_by_name(name)
except exception.InstanceTypeNotFound as e:
raise exception.ApiError(e)
def get_instance_type(instance_type_id):
@@ -118,10 +113,7 @@ def get_instance_type(instance_type_id):
return get_default_instance_type()
ctxt = context.get_admin_context()
try:
return db.instance_type_get(ctxt, instance_type_id)
except exception.InstanceTypeNotFound as e:
raise exception.ApiError(e)
def get_instance_type_by_name(name):
@@ -130,11 +122,7 @@ def get_instance_type_by_name(name):
return get_default_instance_type()
ctxt = context.get_admin_context()
try:
return db.instance_type_get_by_name(ctxt, name)
except exception.InstanceTypeNotFound as e:
raise exception.ApiError(e)
# TODO(termie): flavor-specific code should probably be in the API that uses

View File

@@ -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")

View File

@@ -368,11 +368,9 @@ class FloatingIP(object):
# NOTE(tr3buchet): all network hosts in zone now use the same pool
LOG.debug("QUOTA: %s" % quota.allowed_floating_ips(context, 1))
if quota.allowed_floating_ips(context, 1) < 1:
LOG.warn(_('Quota exceeded for %s, tried to allocate '
'address'),
LOG.warn(_('Quota exceeded for %s, tried to allocate address'),
context.project_id)
raise exception.QuotaError(_('Address quota exceeded. You cannot '
'allocate any more addresses'))
raise exception.QuotaError(code='AddressLimitExceeded')
pool = pool or FLAGS.default_floating_pool
return self.db.floating_ip_allocate_address(context,
project_id,

View File

@@ -285,7 +285,7 @@ class CloudTestCase(test.TestCase):
def test_delete_security_group_no_params(self):
delete = self.cloud.delete_security_group
self.assertRaises(exception.ApiError, delete, self.context)
self.assertRaises(exception.EC2APIError, delete, self.context)
def test_authorize_security_group_ingress(self):
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
@@ -415,12 +415,12 @@ class CloudTestCase(test.TestCase):
{'project_id': self.context.project_id,
'name': 'test'})
authz = self.cloud.authorize_security_group_ingress
self.assertRaises(exception.ApiError, authz, self.context, 'test')
self.assertRaises(exception.EC2APIError, authz, self.context, 'test')
def test_authorize_security_group_ingress_missing_group_name_or_id(self):
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
authz = self.cloud.authorize_security_group_ingress
self.assertRaises(exception.ApiError, authz, self.context, **kwargs)
self.assertRaises(exception.EC2APIError, authz, self.context, **kwargs)
def test_authorize_security_group_ingress_already_exists(self):
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
@@ -428,13 +428,14 @@ class CloudTestCase(test.TestCase):
authz = self.cloud.authorize_security_group_ingress
kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
authz(self.context, group_name=sec['name'], **kwargs)
self.assertRaises(exception.ApiError, authz, self.context,
self.assertRaises(exception.EC2APIError, authz, self.context,
group_name=sec['name'], **kwargs)
def test_revoke_security_group_ingress_missing_group_name_or_id(self):
kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
revoke = self.cloud.revoke_security_group_ingress
self.assertRaises(exception.ApiError, revoke, self.context, **kwargs)
self.assertRaises(exception.EC2APIError, revoke,
self.context, **kwargs)
def test_describe_volumes(self):
"""Makes sure describe_volumes works and filters results."""
@@ -1315,7 +1316,7 @@ class CloudTestCase(test.TestCase):
self.stubs.UnsetAll()
self.stubs.Set(fake._FakeImageService, 'show', fake_show_no_state)
self.assertRaises(exception.ApiError, run_instances,
self.assertRaises(exception.EC2APIError, run_instances,
self.context, **kwargs)
def test_run_instances_image_state_invalid(self):
@@ -1334,7 +1335,7 @@ class CloudTestCase(test.TestCase):
self.stubs.UnsetAll()
self.stubs.Set(fake._FakeImageService, 'show', fake_show_decrypt)
self.assertRaises(exception.ApiError, run_instances,
self.assertRaises(exception.EC2APIError, run_instances,
self.context, **kwargs)
def test_run_instances_image_status_active(self):

View File

@@ -40,7 +40,7 @@ def fake_get(self, context, instance_uuid):
return {'uuid': instance_uuid}
def fake_get_not_found(self, context, instance_uuid):
def fake_get_not_found(*args, **kwargs):
raise exception.NotFound()
@@ -86,6 +86,17 @@ class ConsoleOutputExtensionTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
def test_get_text_console_no_instance_on_get_output(self):
self.stubs.Set(compute.API, 'get_console_output', fake_get_not_found)
body = {'os-getConsoleOutput': {}}
req = webob.Request.blank('/v2/fake/servers/1/action')
req.method = "POST"
req.body = json.dumps(body)
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
def test_get_text_console_bad_body(self):
body = {}
req = webob.Request.blank('/v2/fake/servers/1/action')

View File

@@ -36,7 +36,7 @@ def fake_get(self, context, instance_uuid):
return {'uuid': instance_uuid}
def fake_get_not_found(self, context, instance_uuid):
def fake_get_not_found(*args, **kwargs):
raise exception.NotFound()
@@ -83,6 +83,17 @@ class ConsolesExtensionTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
def test_get_vnc_console_no_instance_on_console_get(self):
self.stubs.Set(compute.API, 'get_vnc_console', fake_get_not_found)
body = {'os-getVNCConsole': {'type': 'novnc'}}
req = webob.Request.blank('/v2/fake/servers/1/action')
req.method = "POST"
req.body = json.dumps(body)
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
def test_get_vnc_console_invalid_type(self):
self.stubs.Set(compute.API, 'get', fake_get)
body = {'os-getVNCConsole': {'type': 'invalid'}}

View File

@@ -13,11 +13,12 @@
# under the License.
import unittest
import mox
import webob
from nova.api.openstack.compute.contrib import server_start_stop
from nova import context
from nova import exception
from nova import compute
from nova import test
from nova.tests.api.openstack import fakes
@@ -60,13 +61,13 @@ class ServerStartStopTest(test.TestCase):
def test_start_with_bogus_id(self):
req = fakes.HTTPRequest.blank('/v2/fake/servers/test_inst/action')
body = dict(start="")
self.assertRaises(exception.InstanceNotFound,
self.assertRaises(webob.exc.HTTPNotFound,
self.controller._start_server, req, 'test_inst', body)
def test_stop_with_bogus_id(self):
req = fakes.HTTPRequest.blank('/v2/fake/servers/test_inst/action')
body = dict(start="")
self.assertRaises(exception.InstanceNotFound,
self.assertRaises(webob.exc.HTTPNotFound,
self.controller._stop_server, req, 'test_inst', body)

View File

@@ -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

View File

@@ -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)

View File

@@ -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,

View File

@@ -20,15 +20,15 @@ from nova import test
from nova import exception
class ApiErrorTestCase(test.TestCase):
class EC2APIErrorTestCase(test.TestCase):
def test_return_valid_error(self):
# without 'code' arg
err = exception.ApiError('fake error')
err = exception.EC2APIError('fake error')
self.assertEqual(err.__str__(), 'fake error')
self.assertEqual(err.code, None)
self.assertEqual(err.msg, 'fake error')
# with 'code' arg
err = exception.ApiError('fake error', 'blah code')
err = exception.EC2APIError('fake error', 'blah code')
self.assertEqual(err.__str__(), 'blah code: fake error')
self.assertEqual(err.code, 'blah code')
self.assertEqual(err.msg, 'fake error')

View File

@@ -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):

View File

@@ -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')

View File

@@ -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):

View File

@@ -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):

View File

@@ -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)

View File

@@ -551,8 +551,8 @@ class ProxyConnection(driver.ComputeDriver):
raise NotImplementedError()
def get_diagnostics(self, instance_name):
raise exception.ApiError(_("diagnostics are not supported "
"for baremetal"))
# diagnostics are not supported for baremetal
raise NotImplementedError()
def get_disks(self, instance_name):
raise NotImplementedError()

View File

@@ -610,12 +610,12 @@ class VMWareVMOps(object):
LOG.exception(exc)
def pause(self, instance):
"""Pause a VM instance."""
raise exception.ApiError("pause not supported for vmwareapi")
msg = _("pause not supported for vmwareapi")
raise NotImplementedError(msg)
def unpause(self, instance):
"""Un-Pause a VM instance."""
raise exception.ApiError("unpause not supported for vmwareapi")
msg = _("unpause not supported for vmwareapi")
raise NotImplementedError(msg)
def suspend(self, instance):
"""Suspend the specified instance."""
@@ -694,8 +694,8 @@ class VMWareVMOps(object):
def get_diagnostics(self, instance):
"""Return data about VM diagnostics."""
raise exception.ApiError("get_diagnostics not implemented for "
"vmwareapi")
msg = _("get_diagnostics not implemented for vmwareapi")
raise NotImplementedError(msg)
def get_console_output(self, instance):
"""Return snapshot of console."""

View File

@@ -71,8 +71,8 @@ class API(base.Base):
check_policy(context, 'create')
if snapshot is not None:
if snapshot['status'] != "available":
raise exception.ApiError(
_("Snapshot status must be available"))
msg = _("status must be available")
raise exception.InvalidSnapshot(reason=msg)
if not size:
size = snapshot['volume_size']
@@ -84,8 +84,7 @@ class API(base.Base):
pid = context.project_id
LOG.warn(_("Quota exceeded for %(pid)s, tried to create"
" %(size)sG volume") % locals())
raise exception.QuotaError(_("Volume quota exceeded. You cannot "
"create a volume of size %sG") % size)
raise exception.QuotaError(code="VolumeSizeTooLarge")
if availability_zone is None:
availability_zone = FLAGS.storage_availability_zone
@@ -131,7 +130,8 @@ class API(base.Base):
def delete(self, context, volume):
volume_id = volume['id']
if volume['status'] != "available":
raise exception.ApiError(_("Volume status must be available"))
msg = _("Volume status must be available")
raise exception.InvalidVolume(reason=msg)
now = utils.utcnow()
self.db.volume_update(context, volume_id, {'status': 'deleting',
'terminated_at': now})
@@ -207,15 +207,18 @@ class API(base.Base):
def check_attach(self, context, volume):
# TODO(vish): abstract status checking?
if volume['status'] != "available":
raise exception.ApiError(_("Volume status must be available"))
msg = _("status must be available")
raise exception.InvalidVolume(reason=msg)
if volume['attach_status'] == "attached":
raise exception.ApiError(_("Volume is already attached"))
msg = _("already attached")
raise exception.InvalidVolume(reason=msg)
@wrap_check_policy
def check_detach(self, context, volume):
# TODO(vish): abstract status checking?
if volume['status'] == "available":
raise exception.ApiError(_("Volume is already detached"))
msg = _("already detached")
raise exception.InvalidVolume(reason=msg)
def remove_from_compute(self, context, volume, instance_id, host):
"""Remove volume from specified compute host."""
@@ -266,7 +269,8 @@ class API(base.Base):
check_policy(context, 'create_snapshot', volume)
if ((not force) and (volume['status'] != "available")):
raise exception.ApiError(_("Volume status must be available"))
msg = _("must be available")
raise exception.InvalidVolume(reason=msg)
options = {
'volume_id': volume['id'],
@@ -298,7 +302,8 @@ class API(base.Base):
@wrap_check_policy
def delete_snapshot(self, context, snapshot):
if snapshot['status'] != "available":
raise exception.ApiError(_("Snapshot status must be available"))
msg = _("must be available")
raise exception.InvalidVolume(reason=msg)
self.db.snapshot_update(context, snapshot['id'],
{'status': 'deleting'})
rpc.cast(context,

View File

@@ -702,9 +702,7 @@ class SolidFireSanISCSIDriver(SanISCSIDriver):
if response.status != 200:
connection.close()
msg = _("Error in SolidFire API response, status was: %s"
% response.status)
raise exception.ApiError(msg)
raise exception.SolidFireAPIException(status=response.status)
else:
data = response.read()
@@ -763,9 +761,7 @@ class SolidFireSanISCSIDriver(SanISCSIDriver):
params = {}
data = self._issue_api_request('GetClusterInfo', params)
if 'result' not in data:
msg = _("Error in SolidFire API response data was: %s"
% data)
raise exception.ApiError(msg)
raise exception.SolidFireAPIDataException(data=data)
return data['result']
@@ -827,14 +823,10 @@ class SolidFireSanISCSIDriver(SanISCSIDriver):
'attributes': attributes}
data = self._issue_api_request('CreateVolume', params)
if 'result' not in data:
msg = _("Error in SolidFire API response data was: %s"
% data)
raise exception.ApiError(msg)
if 'volumeID' not in data['result']:
msg = _("Error in SolidFire API response data was: %s"
% data)
raise exception.ApiError(msg)
if 'result' not in data or 'volumeID' not in data['result']:
raise exception.SolidFireAPIDataException(data=data)
volume_id = data['result']['volumeID']
volume_list = self._get_volumes_by_sfaccount(account_id)
@@ -873,9 +865,7 @@ class SolidFireSanISCSIDriver(SanISCSIDriver):
params = {'accountID': sfaccount['accountID']}
data = self._issue_api_request('ListVolumesForAccount', params)
if 'result' not in data:
msg = _("Error in SolidFire API response, data was: %s"
% data)
raise exception.ApiError(msg)
raise exception.SolidFireAPIDataException(data=data)
found_count = 0
volid = -1
@@ -891,9 +881,7 @@ class SolidFireSanISCSIDriver(SanISCSIDriver):
params = {'volumeID': volid}
data = self._issue_api_request('DeleteVolume', params)
if 'result' not in data:
msg = _("Error in SolidFire API response, data was: %s"
% data)
raise exception.ApiError(msg)
raise exception.SolidFireAPIDataException(data=data)
LOG.debug(_("Leaving SolidFire delete_volume"))

View File

@@ -39,21 +39,17 @@ def create(context, name, extra_specs={}):
extra_specs=extra_specs))
except exception.DBError, e:
LOG.exception(_('DB error: %s') % e)
raise exception.ApiError(_("Cannot create volume_type with "
"name %(name)s and specs %(extra_specs)s")
% locals())
raise exception.VolumeTypeCreateFailed(name=name,
extra_specs=extra_specs)
def destroy(context, name):
"""Marks volume types as deleted."""
if name is None:
raise exception.InvalidVolumeType(volume_type=name)
msg = _("name cannot be None")
raise exception.InvalidVolumeType(reason=msg)
else:
try:
db.volume_type_destroy(context, name)
except exception.NotFound:
LOG.exception(_('Volume type %s not found for deletion') % name)
raise exception.ApiError(_("Unknown volume type: %s") % name)
def get_all_types(context, inactive=0, search_opts={}):
@@ -97,26 +93,22 @@ def get_all_types(context, inactive=0, search_opts={}):
def get_volume_type(ctxt, id):
"""Retrieves single volume type by id."""
if id is None:
raise exception.InvalidVolumeType(volume_type=id)
msg = _("id cannot be None")
raise exception.InvalidVolumeType(reason=msg)
if ctxt is None:
ctxt = context.get_admin_context()
try:
return db.volume_type_get(ctxt, id)
except exception.DBError:
raise exception.ApiError(_("Unknown volume type: %s") % id)
def get_volume_type_by_name(context, name):
"""Retrieves single volume type by name."""
if name is None:
raise exception.InvalidVolumeType(volume_type=name)
msg = _("name cannot be None")
raise exception.InvalidVolumeType(reason=msg)
try:
return db.volume_type_get_by_name(context, name)
except exception.DBError:
raise exception.ApiError(_("Unknown volume type: %s") % name)
def is_key_value_present(volume_type_id, key, value, volume_type=None):

View File

@@ -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)
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_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. "