Added all validators for main Controller.

Change-Id: Iea65e7e9f111593a7c009e6315f1d4f821535a35
This commit is contained in:
alevine
2015-01-21 16:47:52 +04:00
parent 278b9ba1ca
commit 918e56aeb0
13 changed files with 131 additions and 66 deletions

View File

@@ -53,6 +53,9 @@ LOG = logging.getLogger(__name__)
"""
Validator = common.Validator
class AvailabilityZoneDescriber(common.UniversalDescriber):
KIND = 'az'

View File

@@ -36,7 +36,7 @@ from ec2api.api import route_table
from ec2api.api import security_group
from ec2api.api import snapshot
from ec2api.api import subnet
from ec2api.api import tag as tag_api
from ec2api.api import tag
from ec2api.api import volume
from ec2api.api import vpc
from ec2api import exception
@@ -374,16 +374,16 @@ class CloudController(object):
This action doesn't apply to security groups for use in EC2-Classic.
"""
@module_and_param_types(instance, 'ami_id', 'dummy', 'dummy',
@module_and_param_types(instance, 'ami_id', 'int', 'int',
'str255', 'sg_ids',
'str255s', 'dummy', 'dummy',
'security_group_strs', 'str', 'str',
'dummy', 'ami_id', 'ami_id',
'dummy', 'dummy',
'subnet_id', 'dummy',
'dummy',
'subnet_id', 'bool',
'str',
'ip', 'str64',
'dummy', 'dummy',
'dummy', 'dummy',
'dummy')
'bool')
def run_instances(self, context, image_id, min_count, max_count,
key_name=None, security_group_id=None,
security_group=None, user_data=None, instance_type=None,
@@ -514,7 +514,7 @@ class CloudController(object):
"""
@module_and_param_types(instance, 'i_ids', 'filter',
'dummy', 'dummy')
'int', 'str')
def describe_instances(self, context, instance_id=None, filter=None,
max_results=None, next_token=None):
"""Describes one or more of your instances.
@@ -551,7 +551,7 @@ class CloudController(object):
true if the request succeeds.
"""
@module_and_param_types(instance, 'i_ids', 'dummy')
@module_and_param_types(instance, 'i_ids', 'bool')
def stop_instances(self, context, instance_id, force=False):
"""Stops one or more instances.
@@ -598,6 +598,7 @@ class CloudController(object):
Specified attribute.
"""
@module_and_param_types(key_pair, 'str255s', 'filter')
def describe_key_pairs(self, context, key_name=None, filter=None):
"""Describes one or more of your key pairs.
@@ -609,8 +610,8 @@ class CloudController(object):
Returns:
Specified keypairs.
"""
return key_pair.describe_key_pairs(context, key_name, filter)
@module_and_param_types(key_pair, 'str255')
def create_key_pair(self, context, key_name):
"""Creates a 2048-bit RSA key pair with the specified name.
@@ -621,8 +622,8 @@ class CloudController(object):
Returns:
Created keypair.
"""
return key_pair.create_key_pair(context, key_name)
@module_and_param_types(key_pair, 'str255')
def delete_key_pair(self, context, key_name):
"""Deletes the specified key pair.
@@ -633,8 +634,8 @@ class CloudController(object):
Returns:
Returns true if the request succeeds.
"""
return key_pair.delete_key_pair(context, key_name)
@module_and_param_types(key_pair, 'str255', 'str')
def import_key_pair(self, context, key_name, public_key_material):
"""Imports the public key from an existing RSA key pair.
@@ -647,9 +648,8 @@ class CloudController(object):
Returns:
Imported keypair.
"""
return key_pair.import_key_pair(context, key_name,
public_key_material)
@module_and_param_types(availability_zone, 'strs', 'filter')
def describe_availability_zones(self, context, zone_name=None,
filter=None):
"""Describes one or more of the available Availability Zones.
@@ -662,10 +662,8 @@ class CloudController(object):
Returns:
Specified availability zones.
"""
return availability_zone.describe_availability_zones(context,
zone_name,
filter)
@module_and_param_types(availability_zone, 'strs', 'filter')
def describe_regions(self, context, region_name=None, filter=None):
"""Describes one or more regions that are currently available to you.
@@ -677,10 +675,8 @@ class CloudController(object):
Returns:
Specified regions.
"""
return availability_zone.describe_regions(context,
region_name,
filter)
@module_and_param_types(instance, 'i_id')
def get_password_data(self, context, instance_id):
"""Retrieves the encrypted administrator password for Windows instance.
@@ -694,8 +690,8 @@ class CloudController(object):
The password is encrypted using the key pair that you specified when
you launched the instance.
"""
return instance.get_password_data(context, instance_id)
@module_and_param_types(instance, 'i_id')
def get_console_output(self, context, instance_id):
"""Gets the console output for the specified instance.
@@ -706,8 +702,10 @@ class CloudController(object):
Returns:
The console output of the instance, timestamp and instance id.
"""
return instance.get_console_output(context, instance_id)
@module_and_param_types(volume, 'str', 'int',
'snap_id', 'str', 'int',
'bool', 'str')
def create_volume(self, context, availability_zone=None, size=None,
snapshot_id=None, volume_type=None, iops=None,
encrypted=None, kms_key_id=None):
@@ -741,10 +739,8 @@ class CloudController(object):
You can create a new empty volume or restore a volume from an EBS
snapshot.
"""
return volume.create_volume(context, availability_zone, size,
snapshot_id, volume_type, iops,
encrypted, kms_key_id)
@module_and_param_types(volume, 'vol_id', 'i_id', 'str')
def attach_volume(self, context, volume_id, instance_id, device):
"""Attaches an EBS volume to a running or stopped instance.
@@ -759,8 +755,8 @@ class CloudController(object):
The instance and volume must be in the same Availability Zone.
"""
return volume.attach_volume(context, volume_id, instance_id, device)
@module_and_param_types(volume, 'vol_id', 'i_id', 'str')
def detach_volume(self, context, volume_id, instance_id=None, device=None,
force=None):
"""Detaches an EBS volume from an instance.
@@ -778,9 +774,8 @@ class CloudController(object):
Returns:
Information about the detachment.
"""
return volume.detach_volume(context, volume_id, instance_id, device,
force)
@module_and_param_types(volume, 'vol_id')
def delete_volume(self, context, volume_id):
"""Deletes the specified EBS volume.
@@ -793,8 +788,9 @@ class CloudController(object):
The volume must be in the available state.
"""
return volume.delete_volume(context, volume_id)
@module_and_param_types(volume, 'vol_ids', 'filter',
'int', 'str')
def describe_volumes(self, context, volume_id=None, filter=None,
max_results=None, next_token=None):
"""Describes the specified EBS volumes.
@@ -812,9 +808,8 @@ class CloudController(object):
Returns:
A list of volumes.
"""
return volume.describe_volumes(context, volume_id, filter,
max_results, next_token)
@module_and_param_types(snapshot, 'vol_id', 'str')
def create_snapshot(self, context, volume_id, description=None):
"""Creates a snapshot of an EBS volume.
@@ -826,8 +821,8 @@ class CloudController(object):
Returns:
Information about the snapshot.
"""
return snapshot.create_snapshot(context, volume_id, description)
@module_and_param_types(snapshot, 'snap_id')
def delete_snapshot(self, context, snapshot_id):
"""Deletes the specified snapshot.
@@ -838,8 +833,9 @@ class CloudController(object):
Returns:
Returns true if the request succeeds.
"""
return snapshot.delete_snapshot(context, snapshot_id)
@module_and_param_types(snapshot, 'snap_ids', 'strs',
'strs', 'filter')
def describe_snapshots(self, context, snapshot_id=None, owner=None,
restorable_by=None, filter=None):
"""Describes one or more of the snapshots available to you.
@@ -859,9 +855,9 @@ class CloudController(object):
Returns:
A list of snapshots.
"""
return snapshot.describe_snapshots(context, snapshot_id, owner,
restorable_by, filter)
@module_and_param_types(image, 'i_id', 'str', 'str',
'bool', 'dummy')
def create_image(self, context, instance_id, name=None, description=None,
no_reboot=False, block_device_mapping=None):
"""Creates an EBS-backed AMI from an EBS-backed instance.
@@ -900,6 +896,11 @@ class CloudController(object):
return image.create_image(context, instance_id, name, description,
no_reboot, block_device_mapping)
@module_and_param_types(image, 'str', 'str',
'str', 'str',
'str', 'dummy',
'str', 'ami_id',
'ami_id', 'str')
def register_image(self, context, name=None, image_location=None,
description=None, architecture=None,
root_device_name=None, block_device_mapping=None,
@@ -946,12 +947,8 @@ class CloudController(object):
Returns:
The ID of the new AMI.
"""
return image.register_image(context, name, image_location,
description, architecture,
root_device_name, block_device_mapping,
virtualization_type, kernel_id,
ramdisk_id, sriov_net_support)
@module_and_param_types(image, 'ami_id')
def deregister_image(self, context, image_id):
"""Deregisters the specified AMI.
@@ -962,8 +959,9 @@ class CloudController(object):
Returns:
true if the request succeeds.
"""
return image.deregister_image(context, image_id)
@module_and_param_types(image, 'strs', 'ami_ids',
'strs', 'filter')
def describe_images(self, context, executable_by=None, image_id=None,
owner=None, filter=None):
"""Describes one or more of the images available to you.
@@ -982,9 +980,8 @@ class CloudController(object):
Returns:
A list of images.
"""
return image.describe_images(context, executable_by, image_id,
owner, filter)
@module_and_param_types(image, 'ami_id', 'str')
def describe_image_attribute(self, context, image_id, attribute):
"""Describes the specified attribute of the specified AMI.
@@ -1001,6 +998,10 @@ class CloudController(object):
"""
return image.describe_image_attribute(context, image_id, attribute)
@module_and_param_types(image, 'ami_id', 'str',
'strs', 'str',
'dummy', 'dummy',
'dummy', 'dummy', 'dummy')
def modify_image_attribute(self, context, image_id, attribute,
user_group, operation_type,
description=None, launch_permission=None,
@@ -1027,11 +1028,8 @@ class CloudController(object):
Returns:
true if the request succeeds.
"""
return image.modify_image_attribute(context, image_id, attribute,
user_group, operation_type,
description, launch_permission,
product_code, user_id, value)
@module_and_param_types(tag, 'ec2_ids', 'dummy')
def create_tags(self, context, resource_id, tag):
"""Adds or overwrites one or more tags for the specified resources.
@@ -1045,8 +1043,8 @@ class CloudController(object):
Returns:
true if the request succeeds.
"""
return tag_api.create_tags(context, resource_id, tag)
@module_and_param_types(tag, 'ec2_ids', 'dummy')
def delete_tags(self, context, resource_id, tag=None):
"""Deletes the specified tags from the specified resources.
@@ -1065,8 +1063,9 @@ class CloudController(object):
its value. If you specify this parameter with an empty string as the
value, we delete the key only if its value is an empty string.
"""
return tag_api.delete_tags(context, resource_id, tag)
@module_and_param_types(tag, 'filter', 'int',
'str')
def describe_tags(self, context, filter=None, max_results=None,
next_token=None):
"""Describes one or more of the tags for your EC2 resources.
@@ -1083,7 +1082,6 @@ class CloudController(object):
Returns:
A list of tags.
"""
return tag_api.describe_tags(context, filter, max_results, next_token)
class VpcCloudController(CloudController):

View File

@@ -40,20 +40,32 @@ class Validator(object):
self.action = action
self.params = params
def multi(self, items, validation_func):
validator.validate_list(items, self.param_name)
for item in items:
validation_func(item)
def dummy(self, value):
pass
def bool(self, value):
validator.validate_bool(value, self.param_name)
def int(self, value):
validator.validate_int(value, self.param_name)
def str(self, value):
validator.validate_str(value, self.param_name)
def strs(self, values):
self.multi(values, self.str)
def str64(self, value):
validator.validate_str(value, self.param_name, 64)
def str255(self, value):
validator.validate_str(value, self.param_name, 255)
def multi(self, items, validation_func):
validator.validate_list(items, self.param_name)
for item in items:
validation_func(item)
def str255s(self, values):
self.multi(values, self.str255)
@@ -75,9 +87,12 @@ class Validator(object):
def filter(self, filter):
validator.validate_filter(filter)
def ec2_id(self, id, prefices):
def ec2_id(self, id, prefices=[]):
validator.validate_ec2_id(id, self.param_name, prefices)
def ec2_ids(self, ids):
self.multi(ids, self.ec2_id)
def i_id(self, id):
self.ec2_id(id, ['i'])
@@ -88,7 +103,7 @@ class Validator(object):
self.ec2_id(id, ['ami', 'ari', 'aki'])
def ami_ids(self, ids):
self.multi(ids, self.aki_id)
self.multi(ids, self.ami_id)
def sg_id(self, id):
self.ec2_id(id, ['sg'])
@@ -141,6 +156,18 @@ class Validator(object):
def sg_ids(self, ids):
self.multi(ids, self.sg_id)
def snap_id(self, id):
self.ec2_id(id, ['snap'])
def snap_ids(self, ids):
self.multi(ids, self.snap_id)
def vol_id(self, id):
self.ec2_id(id, ['vol'])
def vol_ids(self, ids):
self.multi(ids, self.vol_id)
def security_group_str(self, value):
validator.validate_security_group_str(value, self.param_name,
self.params.get('vpc_id'))

View File

@@ -74,6 +74,13 @@ rpcapi_opts = [
CONF.register_opts(rpcapi_opts)
"""Volume related API implementation
"""
Validator = common.Validator
CONTAINER_TO_KIND = {'aki': 'aki',
'ari': 'ari',
'ami': 'ami',

View File

@@ -31,6 +31,9 @@ LOG = logging.getLogger(__name__)
"""
Validator = common.Validator
class KeyPairDescriber(common.UniversalDescriber):
KIND = 'kp'

View File

@@ -23,6 +23,13 @@ from ec2api import exception
from ec2api.openstack.common.gettextutils import _
"""Snapshot related API implementation
"""
Validator = common.Validator
def create_snapshot(context, volume_id, description=None):
volume = ec2utils.get_db_item(context, 'vol', volume_id)
cinder = clients.cinder(context)

View File

@@ -19,6 +19,13 @@ from ec2api import exception
from ec2api.openstack.common.gettextutils import _
"""Tag related API implementation
"""
Validator = common.Validator
RESOURCE_TYPES = {
'dopt': 'dhcp-options',
'ami': 'image',

View File

@@ -40,6 +40,14 @@ def validate_bool(val, parameter_name):
reason=_("Expected a boolean value for parameter %s") % parameter_name)
def validate_int(val, parameter_name):
if isinstance(val, int):
return True
raise exception.ValidationError(
reason=(_("Expected an integer value for parameter %s") %
parameter_name))
def validate_list(items, parameter_name):
if not isinstance(items, list):
raise exception.InvalidParameterValue(
@@ -129,7 +137,7 @@ def validate_ec2_id(val, parameter_name, prefices):
try:
prefix, value = val.rsplit('-', 1)
int(value, 16)
if prefix in prefices:
if not prefices or prefix in prefices:
return True
except Exception:
pass

View File

@@ -23,6 +23,13 @@ from ec2api import exception
from ec2api.openstack.common.gettextutils import _
"""Volume related API implementation
"""
Validator = common.Validator
def create_volume(context, availability_zone=None, size=None,
snapshot_id=None, volume_type=None, iops=None,
encrypted=None, kms_key_id=None):

View File

@@ -34,7 +34,7 @@ class AvailabilityZoneCase(base.ApiTestCase):
fakes.NovaAvailabilityZone(fakes.OS_AVAILABILITY_ZONE),
fakes.NovaAvailabilityZone(fakes.OS_AVAILABILITY_ZONE_INTERNAL)]
resp = self.execute('DescribeAvailabilityZones',
{'zoneName': 'verbose'})
{'zoneName.1': 'verbose'})
self.assertEqual(200, resp['http_status_code'])
self.assertEqual(len(resp['availabilityZoneInfo']), 7)
self.nova_availability_zones.list.assert_called_once()

View File

@@ -176,11 +176,9 @@ class InstanceTestCase(base.ApiTestCase):
'security_group_id': [fakes.ID_EC2_SECURITY_GROUP_1,
fakes.ID_EC2_SECURITY_GROUP_2]})
do_check({'SubnetId': fakes.ID_EC2_SUBNET_1,
'PrivateIpAddress.1': fakes.IP_FIRST_SUBNET_1,
'PrivateIpAddress.2': fakes.IP_LAST_SUBNET_1},
'PrivateIpAddress': fakes.IP_FIRST_SUBNET_1},
create_network_interface_kwargs={
'private_ip_address': [fakes.IP_FIRST_SUBNET_1,
fakes.IP_LAST_SUBNET_1]})
'private_ip_address': fakes.IP_FIRST_SUBNET_1})
do_check({'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1,
'NetworkInterface.1.SecurityGroupId.1':

View File

@@ -41,7 +41,7 @@ class KeyPairCase(base.ApiTestCase):
self.assertEqual('InvalidKeyPair.Duplicate', resp['Error']['Code'])
resp = self.execute('CreateKeyPair', {'KeyName': 'k' * 256})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('InvalidParameterValue', resp['Error']['Code'])
self.assertEqual('ValidationError', resp['Error']['Code'])
self.nova_key_pairs.create.side_effect = (
nova_exception.OverLimit(413))
resp = self.execute('CreateKeyPair', {'KeyName': fakes.NAME_KEY_PAIR})
@@ -95,7 +95,7 @@ class KeyPairCase(base.ApiTestCase):
def test_describe_key_pairs_invalid(self):
self.nova_key_pairs.list.return_value = [fakes.NovaKeyPair(
fakes.OS_KEY_PAIR)]
resp = self.execute('DescribeKeyPairs', {'KeyName': 'badname'})
resp = self.execute('DescribeKeyPairs', {'KeyName.1': 'badname'})
self.assertEqual(404, resp['http_status_code'])
self.assertEqual('InvalidKeyPair.NotFound', resp['Error']['Code'])
self.nova_key_pairs.list.assert_called_once()

View File

@@ -189,7 +189,7 @@ class TagTestCase(base.ApiTestCase):
filter_fields = ['resource-type', 'resource-id', 'key', 'value']
filter_param = dict(('Filter.%s.Name' % num, field)
for num, field in enumerate(filter_fields))
filter_param.update(dict(('Filter.%s.Value' % num, 'fake')
filter_param.update(dict(('Filter.%s.Value.1' % num, 'fake')
for num, field in enumerate(filter_fields)))
resp = self.execute('DescribeTags', filter_param)
self.assertEqual({'http_status_code': 200,