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): class AvailabilityZoneDescriber(common.UniversalDescriber):
KIND = 'az' KIND = 'az'

View File

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

View File

@@ -40,20 +40,32 @@ class Validator(object):
self.action = action self.action = action
self.params = params 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): def dummy(self, value):
pass pass
def bool(self, value): def bool(self, value):
validator.validate_bool(value, self.param_name) 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): def str255(self, value):
validator.validate_str(value, self.param_name, 255) 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): def str255s(self, values):
self.multi(values, self.str255) self.multi(values, self.str255)
@@ -75,9 +87,12 @@ class Validator(object):
def filter(self, filter): def filter(self, filter):
validator.validate_filter(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) 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): def i_id(self, id):
self.ec2_id(id, ['i']) self.ec2_id(id, ['i'])
@@ -88,7 +103,7 @@ class Validator(object):
self.ec2_id(id, ['ami', 'ari', 'aki']) self.ec2_id(id, ['ami', 'ari', 'aki'])
def ami_ids(self, ids): def ami_ids(self, ids):
self.multi(ids, self.aki_id) self.multi(ids, self.ami_id)
def sg_id(self, id): def sg_id(self, id):
self.ec2_id(id, ['sg']) self.ec2_id(id, ['sg'])
@@ -141,6 +156,18 @@ class Validator(object):
def sg_ids(self, ids): def sg_ids(self, ids):
self.multi(ids, self.sg_id) 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): def security_group_str(self, value):
validator.validate_security_group_str(value, self.param_name, validator.validate_security_group_str(value, self.param_name,
self.params.get('vpc_id')) self.params.get('vpc_id'))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,6 +23,13 @@ from ec2api import exception
from ec2api.openstack.common.gettextutils import _ from ec2api.openstack.common.gettextutils import _
"""Volume related API implementation
"""
Validator = common.Validator
def create_volume(context, availability_zone=None, size=None, def create_volume(context, availability_zone=None, size=None,
snapshot_id=None, volume_type=None, iops=None, snapshot_id=None, volume_type=None, iops=None,
encrypted=None, kms_key_id=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),
fakes.NovaAvailabilityZone(fakes.OS_AVAILABILITY_ZONE_INTERNAL)] fakes.NovaAvailabilityZone(fakes.OS_AVAILABILITY_ZONE_INTERNAL)]
resp = self.execute('DescribeAvailabilityZones', resp = self.execute('DescribeAvailabilityZones',
{'zoneName': 'verbose'}) {'zoneName.1': 'verbose'})
self.assertEqual(200, resp['http_status_code']) self.assertEqual(200, resp['http_status_code'])
self.assertEqual(len(resp['availabilityZoneInfo']), 7) self.assertEqual(len(resp['availabilityZoneInfo']), 7)
self.nova_availability_zones.list.assert_called_once() 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, 'security_group_id': [fakes.ID_EC2_SECURITY_GROUP_1,
fakes.ID_EC2_SECURITY_GROUP_2]}) fakes.ID_EC2_SECURITY_GROUP_2]})
do_check({'SubnetId': fakes.ID_EC2_SUBNET_1, do_check({'SubnetId': fakes.ID_EC2_SUBNET_1,
'PrivateIpAddress.1': fakes.IP_FIRST_SUBNET_1, 'PrivateIpAddress': fakes.IP_FIRST_SUBNET_1},
'PrivateIpAddress.2': fakes.IP_LAST_SUBNET_1},
create_network_interface_kwargs={ create_network_interface_kwargs={
'private_ip_address': [fakes.IP_FIRST_SUBNET_1, 'private_ip_address': fakes.IP_FIRST_SUBNET_1})
fakes.IP_LAST_SUBNET_1]})
do_check({'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1, do_check({'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1,
'NetworkInterface.1.SecurityGroupId.1': 'NetworkInterface.1.SecurityGroupId.1':

View File

@@ -41,7 +41,7 @@ class KeyPairCase(base.ApiTestCase):
self.assertEqual('InvalidKeyPair.Duplicate', resp['Error']['Code']) self.assertEqual('InvalidKeyPair.Duplicate', resp['Error']['Code'])
resp = self.execute('CreateKeyPair', {'KeyName': 'k' * 256}) resp = self.execute('CreateKeyPair', {'KeyName': 'k' * 256})
self.assertEqual(400, resp['http_status_code']) 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 = ( self.nova_key_pairs.create.side_effect = (
nova_exception.OverLimit(413)) nova_exception.OverLimit(413))
resp = self.execute('CreateKeyPair', {'KeyName': fakes.NAME_KEY_PAIR}) resp = self.execute('CreateKeyPair', {'KeyName': fakes.NAME_KEY_PAIR})
@@ -95,7 +95,7 @@ class KeyPairCase(base.ApiTestCase):
def test_describe_key_pairs_invalid(self): def test_describe_key_pairs_invalid(self):
self.nova_key_pairs.list.return_value = [fakes.NovaKeyPair( self.nova_key_pairs.list.return_value = [fakes.NovaKeyPair(
fakes.OS_KEY_PAIR)] 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(404, resp['http_status_code'])
self.assertEqual('InvalidKeyPair.NotFound', resp['Error']['Code']) self.assertEqual('InvalidKeyPair.NotFound', resp['Error']['Code'])
self.nova_key_pairs.list.assert_called_once() 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_fields = ['resource-type', 'resource-id', 'key', 'value']
filter_param = dict(('Filter.%s.Name' % num, field) filter_param = dict(('Filter.%s.Name' % num, field)
for num, field in enumerate(filter_fields)) 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))) for num, field in enumerate(filter_fields)))
resp = self.execute('DescribeTags', filter_param) resp = self.execute('DescribeTags', filter_param)
self.assertEqual({'http_status_code': 200, self.assertEqual({'http_status_code': 200,