Improve parameter related EC2 error codes

This patch replaces parameter/attribute related raises of EC2APIError
with specific exceptions.

 * Introduce new MissingParameter exception with 'MissingParameter'
   ec2_code.
 * Introduce new InvalidAttribute exception which isn't a valid EC2
   error code but it's needed nonetheless as the nova EC2 API isn't
   fully compatible.
 * Add 'InvalidParameterValue' ec2_code to existing
   InvalidParameterValue exception and use it where appropriate. Also
   add it to existing InvalidPortRange exception.

Implements: blueprint ec2-error-codes

Change-Id: I7820edfd9045fdc817a8538a754c9add20b68cf4
This commit is contained in:
Jakub Ruzicka 2013-08-12 12:26:27 -04:00
parent 1d6044fbba
commit 4f702880d5
4 changed files with 78 additions and 49 deletions

View File

@ -565,7 +565,10 @@ class Executor(wsgi.Application):
exception.InvalidPortRange,
exception.NotAuthorized,
exception.InvalidRequest,
exception.InvalidAttribute,
exception.InvalidPortRange,
exception.QuotaError,
exception.MissingParameter,
exception.InvalidInstanceIDMalformedEC2) as ex:
return ec2_error_ex(ex, req)
except Exception as ex:

View File

@ -618,20 +618,22 @@ class CloudController(object):
def _validate_group_identifier(self, group_name, group_id):
if not group_name and not group_id:
err = _("Not enough parameters, need group_name or group_id")
raise exception.EC2APIError(err)
err = _("need group_name or group_id")
raise exception.MissingParameter(reason=err)
def _validate_rulevalues(self, rulesvalues):
if not rulesvalues:
err = _("%s Not enough parameters to build a valid rule")
raise exception.EC2APIError(err % rulesvalues)
err = _("can't build a valid rule")
raise exception.MissingParameter(reason=err)
def _validate_security_group_protocol(self, values):
validprotocols = ['tcp', 'udp', 'icmp', '6', '17', '1']
if 'ip_protocol' in values and \
values['ip_protocol'] not in validprotocols:
err = _('Invalid IP protocol %s.') % values['ip_protocol']
raise exception.EC2APIError(message=err, code="400")
protocol = values['ip_protocol']
err = _("Invalid IP protocol %(protocol)s") % \
{'protocol': protocol}
raise exception.InvalidParameterValue(message=err)
def revoke_security_group_ingress(self, context, group_name=None,
group_id=None, **kwargs):
@ -660,7 +662,8 @@ class CloudController(object):
return True
raise exception.EC2APIError(_("No rule for the specified parameters."))
msg = _("No rule for the specified parameters.")
raise exception.InvalidParameterValue(message=msg)
# TODO(soren): This has only been tested with Boto as the client.
# Unfortunately, it seems Boto is using an old API
@ -692,7 +695,8 @@ class CloudController(object):
security_group['name'], postvalues)
return True
raise exception.EC2APIError(_("No rule for the specified parameters."))
msg = _("No rule for the specified parameters.")
raise exception.InvalidParameterValue(message=msg)
def _get_source_project_id(self, context, source_security_group_owner_id):
if source_security_group_owner_id:
@ -738,8 +742,8 @@ class CloudController(object):
def delete_security_group(self, context, group_name=None, group_id=None,
**kwargs):
if not group_name and not group_id:
err = _("Not enough parameters, need group_name or group_id")
raise exception.EC2APIError(err)
err = _("need group_name or group_id")
raise exception.MissingParameter(reason=err)
security_group = self.security_group_api.get(context, group_name,
group_id)
@ -947,8 +951,7 @@ class CloudController(object):
def describe_instance_attribute(self, context, instance_id, attribute,
**kwargs):
def _unsupported_attribute(instance, result):
raise exception.EC2APIError(_('attribute not supported: %s') %
attribute)
raise exception.InvalidAttribute(attr=attribute)
def _format_attr_block_device_mapping(instance, result):
tmp = {}
@ -1003,8 +1006,7 @@ class CloudController(object):
fn = attribute_formatter.get(attribute)
if fn is None:
raise exception.EC2APIError(
_('attribute not supported: %s') % attribute)
raise exception.InvalidAttribute(attr=attribute)
validate_ec2_id(instance_id)
instance_uuid = ec2utils.ec2_inst_id_to_uuid(context, instance_id)
@ -1559,7 +1561,8 @@ class CloudController(object):
if image_location is None and kwargs.get('name'):
image_location = kwargs['name']
if image_location is None:
raise exception.EC2APIError(_('imageLocation is required'))
msg = _('imageLocation is required')
raise exception.MissingParameter(reason=msg)
metadata = {'properties': {'image_location': image_location}}
@ -1623,8 +1626,7 @@ class CloudController(object):
fn = supported_attributes.get(attribute)
if fn is None:
raise exception.EC2APIError(_('attribute not supported: %s')
% attribute)
raise exception.InvalidAttribute(attr=attribute)
try:
image = self._get_image(context, image_id)
except exception.NotFound:
@ -1638,15 +1640,16 @@ class CloudController(object):
operation_type, **kwargs):
# TODO(devcamcar): Support users and groups other than 'all'.
if attribute != 'launchPermission':
raise exception.EC2APIError(_('attribute not supported: %s')
% attribute)
raise exception.InvalidAttribute(attr=attribute)
if 'user_group' not in kwargs:
raise exception.EC2APIError(_('user or group not specified'))
msg = _('user or group not specified')
raise exception.MissingParameter(reason=msg)
if len(kwargs['user_group']) != 1 and kwargs['user_group'][0] != 'all':
raise exception.EC2APIError(_('only group "all" is supported'))
msg = _('only group "all" is supported')
raise exception.InvalidParameterValue(message=msg)
if operation_type not in ['add', 'remove']:
msg = _('operation_type must be add or remove')
raise exception.EC2APIError(msg)
raise exception.InvalidParameterValue(message=msg)
LOG.audit(_("Updating image %s publicity"), image_id, context=context)
try:
@ -1764,30 +1767,34 @@ class CloudController(object):
tags = kwargs.get('tag', None)
if resources is None or tags is None:
raise exception.EC2APIError(_('resource_id and tag are required'))
msg = _('resource_id and tag are required')
raise exception.MissingParameter(reason=msg)
if not isinstance(resources, (tuple, list, set)):
raise exception.EC2APIError(_('Expecting a list of resources'))
msg = _('Expecting a list of resources')
raise exception.InvalidParameterValue(message=msg)
for r in resources:
if ec2utils.resource_type_from_id(context, r) != 'instance':
raise exception.EC2APIError(_('Only instances implemented'))
msg = _('Only instances implemented')
raise exception.InvalidParameterValue(message=msg)
if not isinstance(tags, (tuple, list, set)):
raise exception.EC2APIError(_('Expecting a list of tagSets'))
msg = _('Expecting a list of tagSets')
raise exception.InvalidParameterValue(message=msg)
metadata = {}
for tag in tags:
if not isinstance(tag, dict):
raise exception.EC2APIError(_
('Expecting tagSet to be key/value pairs'))
err = _('Expecting tagSet to be key/value pairs')
raise exception.InvalidParameterValue(message=err)
key = tag.get('key', None)
val = tag.get('value', None)
if key is None or val is None:
raise exception.EC2APIError(_
('Expecting both key and value to be set'))
err = _('Expecting both key and value to be set')
raise exception.InvalidParameterValue(message=err)
metadata[key] = val
@ -1809,29 +1816,34 @@ class CloudController(object):
resources = kwargs.get('resource_id', None)
tags = kwargs.get('tag', None)
if resources is None or tags is None:
raise exception.EC2APIError(_('resource_id and tag are required'))
msg = _('resource_id and tag are required')
raise exception.MissingParameter(reason=msg)
if not isinstance(resources, (tuple, list, set)):
raise exception.EC2APIError(_('Expecting a list of resources'))
msg = _('Expecting a list of resources')
raise exception.InvalidParameterValue(message=msg)
for r in resources:
if ec2utils.resource_type_from_id(context, r) != 'instance':
raise exception.EC2APIError(_('Only instances implemented'))
msg = _('Only instances implemented')
raise exception.InvalidParameterValue(message=msg)
if not isinstance(tags, (tuple, list, set)):
raise exception.EC2APIError(_('Expecting a list of tagSets'))
msg = _('Expecting a list of tagSets')
raise exception.InvalidParameterValue(message=msg)
for ec2_id in resources:
instance_uuid = ec2utils.ec2_inst_id_to_uuid(context, ec2_id)
instance = self.compute_api.get(context, instance_uuid)
for tag in tags:
if not isinstance(tag, dict):
raise exception.EC2APIError(_
('Expecting tagSet to be key/value pairs'))
msg = _('Expecting tagSet to be key/value pairs')
raise exception.InvalidParameterValue(message=msg)
key = tag.get('key', None)
if key is None:
raise exception.EC2APIError(_('Expecting key to be set'))
msg = _('Expecting key to be set')
raise exception.InvalidParameterValue(message=msg)
self.compute_api.delete_instance_metadata(context,
instance, key)
@ -1869,8 +1881,8 @@ class CloudController(object):
elif key_name in ('resource_type', 'resource-type'):
for res_type in val:
if res_type != 'instance':
raise exception.EC2APIError(_
('Only instances implemented'))
raise exception.InvalidParameterValue(
message=_('Only instances implemented'))
search_block[key_name] = 'instance'
if len(search_block.keys()) > 0:
search_filts.append(search_block)
@ -1889,7 +1901,7 @@ class CloudController(object):
class EC2SecurityGroupExceptions(object):
@staticmethod
def raise_invalid_property(msg):
raise exception.InvalidParameterValue(err=msg)
raise exception.InvalidParameterValue(message=msg)
@staticmethod
def raise_group_already_exists(msg):
@ -1904,7 +1916,7 @@ class EC2SecurityGroupExceptions(object):
if decoding_exception:
raise decoding_exception
else:
raise exception.EC2APIError(_("Invalid CIDR"))
raise exception.InvalidParameterValue(message=_("Invalid CIDR"))
@staticmethod
def raise_over_quota(msg):

View File

@ -245,6 +245,10 @@ class InvalidBDMForLegacy(InvalidBDM):
"be converted to legacy format. ")
class InvalidAttribute(Invalid):
msg_fmt = _("Attribute not supported: %(attr)s")
class VolumeUnattached(Invalid):
msg_fmt = _("Volume %(volume_id)s is not attached to anything")
@ -280,6 +284,7 @@ class InvalidMetadataSize(Invalid):
class InvalidPortRange(Invalid):
ec2_code = 'InvalidParameterValue'
msg_fmt = _("Invalid port range %(from_port)s:%(to_port)s. %(msg)s")
@ -303,6 +308,7 @@ class InvalidUnicodeParameter(Invalid):
# Cannot be templated as the error syntax varies.
# msg needs to be constructed when raised.
class InvalidParameterValue(Invalid):
ec2_code = 'InvalidParameterValue'
msg_fmt = _("%(err)s")
@ -1407,3 +1413,9 @@ class PciInvalidAlias(NovaException):
class PciRequestAliasNotDefined(NovaException):
msg_fmt = _("PCI alias %(alias)s is not defined")
class MissingParameter(NovaException):
ec2_code = 'MissingParameter'
msg_fmt = _("Not enough parameters: %(reason)s")
code = 400

View File

@ -459,7 +459,7 @@ class CloudTestCase(test.TestCase):
def test_delete_security_group_no_params(self):
delete = self.cloud.delete_security_group
self.assertRaises(exception.EC2APIError, delete, self.context)
self.assertRaises(exception.MissingParameter, delete, self.context)
def test_authorize_security_group_ingress(self):
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
@ -589,12 +589,14 @@ class CloudTestCase(test.TestCase):
{'project_id': self.context.project_id,
'name': 'test'})
authz = self.cloud.authorize_security_group_ingress
self.assertRaises(exception.EC2APIError, authz, self.context, 'test')
self.assertRaises(exception.MissingParameter, 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.EC2APIError, authz, self.context, **kwargs)
self.assertRaises(exception.MissingParameter, authz, self.context,
**kwargs)
def test_authorize_security_group_ingress_already_exists(self):
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
@ -655,7 +657,7 @@ class CloudTestCase(test.TestCase):
authz = self.cloud.authorize_security_group_ingress
auth_kwargs = {'ip_protocol': proto}
self.assertRaises(exception.EC2APIError, authz, self.context,
self.assertRaises(exception.MissingParameter, authz, self.context,
group_name=sec['name'], **auth_kwargs)
db.security_group_destroy(self.context, sec['id'])
@ -675,7 +677,7 @@ class CloudTestCase(test.TestCase):
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.EC2APIError, revoke,
self.assertRaises(exception.MissingParameter, revoke,
self.context, **kwargs)
def test_delete_security_group_in_use_by_group(self):
@ -1585,8 +1587,8 @@ class CloudTestCase(test.TestCase):
def test_register_image_empty(self):
register_image = self.cloud.register_image
self.assertRaises(exception.EC2APIError, register_image, self.context,
image_location=None)
self.assertRaises(exception.MissingParameter, register_image,
self.context, image_location=None)
def test_register_image_name(self):
register_image = self.cloud.register_image
@ -2789,7 +2791,7 @@ class CloudTestCase(test.TestCase):
self.assertEqualSorted(tags, [inst2_key_baz])
# And we should fail on supported resource types
self.assertRaises(exception.EC2APIError,
self.assertRaises(exception.InvalidParameterValue,
self.cloud.describe_tags,
self.context,
filter=[{'name': 'resource-type',