diff --git a/ec2api/api/image.py b/ec2api/api/image.py index 9a2fed17..1ad1941d 100644 --- a/ec2api/api/image.py +++ b/ec2api/api/image.py @@ -207,9 +207,9 @@ class ImageDescriber(common.TaggableItemsDescriber): 'is-public': 'isPublic', 'kernel_id': 'kernelId', 'name': 'name', - 'owner-id': 'ownerId', + 'owner-id': 'imageOwnerId', 'ramdisk-id': 'ramdiskId', - 'state': 'state', + 'state': 'imageState', } def format(self, image, os_image): diff --git a/ec2api/api/internet_gateway.py b/ec2api/api/internet_gateway.py index 5922bfd7..d4ac6751 100644 --- a/ec2api/api/internet_gateway.py +++ b/ec2api/api/internet_gateway.py @@ -124,7 +124,7 @@ class InternetGatewayDescriber(common.TaggableItemsDescriber, common.NonOpenstackItemsDescriber): KIND = 'igw' - FILTER_MAP = {'internet-gateway-id': 'id', + FILTER_MAP = {'internet-gateway-id': 'internetGatewayId', 'attachment.state': ['attachmentSet', 'state'], 'attachment.vpc-id': ['attachmentSet', 'vpcId']} diff --git a/ec2api/api/route_table.py b/ec2api/api/route_table.py index 8261d4c1..996a8b96 100644 --- a/ec2api/api/route_table.py +++ b/ec2api/api/route_table.py @@ -209,14 +209,14 @@ class RouteTableDescriber(common.TaggableItemsDescriber, 'association.route-table-id': ['associationSet', 'routeTableId'], 'association.subnet-id': ['associationSet', 'subnetId'], - 'association.main': ['association', 'main'], + 'association.main': ['associationSet', 'main'], 'route-table-id': 'routeTableId', 'route.destination-cidr-block': ['routeSet', 'destinationCidrBlock'], 'route.gateway-id': ['routeSet', 'gatewayId'], 'route.instance-id': ['routeSet', 'instanceId'], - 'route.origin': ['route', 'origin'], - 'route.state': ['route', 'state'], + 'route.origin': ['routeSet', 'origin'], + 'route.state': ['routeSet', 'state'], 'vpc-id': 'vpcId'} def format(self, route_table): diff --git a/ec2api/api/subnet.py b/ec2api/api/subnet.py index 7a209b3f..b367f06a 100644 --- a/ec2api/api/subnet.py +++ b/ec2api/api/subnet.py @@ -137,7 +137,7 @@ class SubnetDescriber(common.TaggableItemsDescriber): 'cidr': 'cidrBlock', 'cidrBlock': 'cidrBlock', 'cidr-block': 'cidrBlock', - 'subnet-id': 'id', + 'subnet-id': 'subnetId', 'state': 'state', 'vpc-id': 'vpcId'} diff --git a/ec2api/tests/base.py b/ec2api/tests/base.py index 40706a69..4ffea3f0 100644 --- a/ec2api/tests/base.py +++ b/ec2api/tests/base.py @@ -116,3 +116,18 @@ class ApiTestCase(test_base.BaseTestCase): if matchers.ListMatches(call_args, args, orderless_lists=True): return self.assertEqual(False, True) + + def check_filtering(self, operation, resultset_key, filters): + for name, value in filters: + resp = self.execute(operation, + {'Filter.1.Name': name, + 'Filter.1.Value.1': str(value)}) + self.assertEqual(200, resp['http_status_code']) + self.assertTrue(len(resp[resultset_key]) > 0) + + resp = self.execute(operation, + {'Filter.1.Name': name, + 'Filter.1.Value.1': 'dummy filter value'}) + self.assertEqual(200, resp['http_status_code']) + self.assertTrue(resp[resultset_key] is None or + len(resp[resultset_key]) == 0) diff --git a/ec2api/tests/fakes.py b/ec2api/tests/fakes.py index 5471671b..bdf3d0a0 100644 --- a/ec2api/tests/fakes.py +++ b/ec2api/tests/fakes.py @@ -176,6 +176,7 @@ ID_OS_FLOATING_IP_2 = random_os_id() IP_ADDRESS_1 = '192.168.1.100' IP_ADDRESS_2 = '192.168.1.200' +IP_ADDRESS_NOVA_1 = '192.168.2.100' # security group constants @@ -604,7 +605,7 @@ EC2_INSTANCE_2 = { 'privateIpAddress': None, 'amiLaunchIndex': 0, 'placement': {'availabilityZone': NAME_AVAILABILITY_ZONE}, - 'dnsName': None, + 'dnsName': IP_ADDRESS_NOVA_1, 'dnsNameV6': IPV6_INSTANCE_2, 'instanceState': {'code': 0, 'name': 'pending'}, 'imageId': None, @@ -620,6 +621,7 @@ EC2_INSTANCE_2 = { 'volumeId': ID_EC2_VOLUME_2, 'attachTime': None}}], 'instanceType': 'fake_flavor', + 'ipAddress': IP_ADDRESS_NOVA_1, 'rootDeviceName': ROOT_DEVICE_NAME_INSTANCE_2, 'clientToken': CLIENT_TOKEN_INSTANCE_2, } @@ -707,7 +709,10 @@ OS_INSTANCE_2 = OSInstance( addresses={ ID_EC2_SUBNET_1: [{'addr': IPV6_INSTANCE_2, 'version': 6, - 'OS-EXT-IPS:type': 'fixed'}]}, + 'OS-EXT-IPS:type': 'fixed'}, + {'addr': IP_ADDRESS_NOVA_1, + 'version': 4, + 'OS-EXT-IPS:type': 'floating'}]}, ) @@ -1147,6 +1152,7 @@ EC2_IMAGE_2 = { 'architecture': None, 'rootDeviceType': 'ebs', 'rootDeviceName': ROOT_DEVICE_NAME_IMAGE_2, + 'architecture': 'x86_64', 'blockDeviceMapping': [ {'deviceName': '/dev/sdb1', 'ebs': {'snapshotId': ID_EC2_SNAPSHOT_1}}], @@ -1225,6 +1231,7 @@ OS_IMAGE_2 = { 'properties': { 'type': 'machine', 'root_device_name': '/dev/sdb1', + 'architecture': 'x86_64', 'mappings': [{'device': '/dev/sda1', 'virtual': 'root'}], 'block_device_mapping': [ diff --git a/ec2api/tests/test_address.py b/ec2api/tests/test_address.py index 3a259912..2971ef5d 100644 --- a/ec2api/tests/test_address.py +++ b/ec2api/tests/test_address.py @@ -617,6 +617,17 @@ class AddressTestCase(base.ApiTestCase): matchers.ListMatches([fakes.EC2_ADDRESS_1, fakes.EC2_ADDRESS_2])) + self.check_filtering( + 'DescribeAddresses', 'addressesSet', + [('allocation-id', fakes.ID_EC2_ADDRESS_1), + ('association-id', fakes.ID_EC2_ASSOCIATION_2), + ('domain', 'vpc'), + ('instance-id', fakes.ID_EC2_INSTANCE_1), + ('network-interface-id', fakes.ID_EC2_NETWORK_INTERFACE_2), + ('network-interface-owner-id', fakes.ID_OS_PROJECT), + ('privateIpAddress', fakes.IP_NETWORK_INTERFACE_2), + ('public-ip', fakes.IP_ADDRESS_2)]) + def test_describe_addresses_ec2_classic(self): address.address_engine = ( address.AddressEngineNova()) diff --git a/ec2api/tests/test_availability_zone.py b/ec2api/tests/test_availability_zone.py index 00432620..7b900f9e 100644 --- a/ec2api/tests/test_availability_zone.py +++ b/ec2api/tests/test_availability_zone.py @@ -29,6 +29,11 @@ class AvailabilityZoneCase(base.ApiTestCase): matchers.ListMatches([fakes.EC2_AVAILABILITY_ZONE])) self.nova_availability_zones.list.assert_called_once() + self.check_filtering( + 'DescribeAvailabilityZones', 'availabilityZoneInfo', + [('state', 'available'), + ('zone-name', fakes.NAME_AVAILABILITY_ZONE)]) + def test_describe_availability_zones_verbose(self): self.nova_availability_zones.list.return_value = [ fakes.NovaAvailabilityZone(fakes.OS_AVAILABILITY_ZONE), diff --git a/ec2api/tests/test_dhcp_options.py b/ec2api/tests/test_dhcp_options.py index 98102171..f8ca74d3 100644 --- a/ec2api/tests/test_dhcp_options.py +++ b/ec2api/tests/test_dhcp_options.py @@ -102,6 +102,11 @@ class DhcpOptionsTestCase(base.ApiTestCase): fakes.EC2_DHCP_OPTIONS_2], orderless_lists=True)) + self.check_filtering( + 'DescribeDhcpOptions', 'dhcpOptionsSet', + [('dhcp_options_id', fakes.ID_EC2_DHCP_OPTIONS_1), + ('key', 'netbios-node-type')]) + def test_associate_dhcp_options(self): self.db_api.get_item_by_id.side_effect = ( fakes.get_db_api_get_item_by_id( diff --git a/ec2api/tests/test_image.py b/ec2api/tests/test_image.py index e8d72319..1a396164 100644 --- a/ec2api/tests/test_image.py +++ b/ec2api/tests/test_image.py @@ -231,6 +231,21 @@ class ImageTestCase(base.ApiTestCase): self.db_api.get_items_by_ids.assert_any_call( mock.ANY, 'ami', set([fakes.ID_EC2_IMAGE_1])) + self.check_filtering( + 'DescribeImages', 'imagesSet', + [('architecture', 'x86_64'), + # TODO(ft): store a description in DB +# ('description', ''), + ('image-id', fakes.ID_EC2_IMAGE_1), + ('image-type', 'machine'), + # TODO(ft): support filtering by a boolean value +# ('is-public', True), + ('kernel_id', fakes.ID_EC2_IMAGE_AKI_1,), + ('name', 'fake_name'), + ('owner-id', fakes.ID_OS_PROJECT), + ('ramdisk-id', fakes.ID_EC2_IMAGE_ARI_1), + ('state', 'available')]) + def test_describe_images_invalid_parameters(self): self._setup_model() diff --git a/ec2api/tests/test_instance.py b/ec2api/tests/test_instance.py index 0e8bee6a..ac8977a5 100644 --- a/ec2api/tests/test_instance.py +++ b/ec2api/tests/test_instance.py @@ -898,16 +898,6 @@ class InstanceTestCase(base.ApiTestCase): fakes.EC2_RESERVATION_2]}, orderless_lists=True)) - resp = self.execute('DescribeInstances', - {'Filter.1.Name': 'key-name', - 'Filter.1.Value.1': 'a', - 'Filter.1.Value.2': 'b', - 'Filter.2.Name': 'client-token', - 'Filter.2.Value.1': 'a string'}) - self.assertEqual({'http_status_code': 200, - 'reservationSet': []}, - resp) - self.db_api.get_items_by_ids.return_value = [fakes.DB_INSTANCE_2] resp = self.execute('DescribeInstances', {'InstanceId.1': fakes.ID_EC2_INSTANCE_2}) @@ -918,6 +908,46 @@ class InstanceTestCase(base.ApiTestCase): {'reservationSet': [fakes.EC2_RESERVATION_2]}, orderless_lists=True)) + self.check_filtering( + 'DescribeInstances', 'reservationSet', + [('block-device-mapping.device-name', + fakes.ROOT_DEVICE_NAME_INSTANCE_2), + ('client-token', fakes.CLIENT_TOKEN_INSTANCE_2), + # TODO(ft): support filtering by none/empty value +# ('dns-name', ''), + ('image-id', fakes.ID_EC2_IMAGE_1), + ('instance-id', fakes.ID_EC2_INSTANCE_2), + ('instance-type', 'fake_flavor'), + ('ip-address', fakes.IP_ADDRESS_2), + ('kernel-id', fakes.ID_EC2_IMAGE_AKI_1), + ('key-name', fakes.NAME_KEY_PAIR), + # TODO(ft): support filtering by a none/empty value +# ('launch-index', 0), + # TODO(ft): fill the field in fakes with correct value +# ('launch-time', ), + ('private-dns-name', fakes.ID_EC2_INSTANCE_1), + ('private-ip-address', fakes.IP_NETWORK_INTERFACE_2), + ('ramdisk-id', fakes.ID_EC2_IMAGE_ARI_1), + ('root-device-name', fakes.ROOT_DEVICE_NAME_INSTANCE_1), + ('root-device-type', 'ebs'), + ('subnet-id', fakes.ID_EC2_SUBNET_2), + ('vpc-id', fakes.ID_EC2_VPC_1), + ('network-interface.description', + fakes.DESCRIPTION_NETWORK_INTERFACE_2), + ('network-interface.subnet-id', fakes.ID_EC2_SUBNET_2), + ('network-interface.vpc-id', fakes.ID_EC2_VPC_1), + ('network-interface.network-interface.id', + fakes.ID_EC2_NETWORK_INTERFACE_2), + ('network-interface.owner-id', fakes.ID_OS_PROJECT), + # TODO(ft): support filtering by a boolean value +# ('network-interface.requester-managed', False), + ('network-interface.status', 'in-use'), + # TODO(ft): declare a constant for the mac in fakes + ('network-interface.mac-address', 'fb:10:2e:b2:ba:b7'), + # TODO(ft): support filtering by a boolean value +# ('network-interface.source-destination-check', True), + ]) + def test_describe_instances_ec2_classic(self): instance_api.instance_engine = ( instance_api.InstanceEngineNova()) diff --git a/ec2api/tests/test_internet_gateway.py b/ec2api/tests/test_internet_gateway.py index 9f811ed5..8ce5001f 100644 --- a/ec2api/tests/test_internet_gateway.py +++ b/ec2api/tests/test_internet_gateway.py @@ -274,6 +274,12 @@ class IgwTestCase(base.ApiTestCase): matchers.ListMatches([fakes.EC2_IGW_1, fakes.EC2_IGW_2])) + self.check_filtering( + 'DescribeInternetGateways', 'internetGatewaySet', + [('internet-gateway-id', fakes.ID_EC2_IGW_2), + ('attachment.state', 'available'), + ('attachment.vpc-id', fakes.ID_EC2_VPC_1)]) + @base.skip_not_implemented def test_describe_igw_no_vpc(self): pass diff --git a/ec2api/tests/test_key_pair.py b/ec2api/tests/test_key_pair.py index 3997a554..0bcd8991 100644 --- a/ec2api/tests/test_key_pair.py +++ b/ec2api/tests/test_key_pair.py @@ -92,6 +92,11 @@ class KeyPairCase(base.ApiTestCase): {'keyMaterial'})])) self.nova_key_pairs.list.assert_called_once() + self.check_filtering( + 'DescribeKeyPairs', 'keySet', + [('fingerprint', fakes.FINGERPRINT_KEY_PAIR), + ('key-name', fakes.NAME_KEY_PAIR)]) + def test_describe_key_pairs_invalid(self): self.nova_key_pairs.list.return_value = [fakes.NovaKeyPair( fakes.OS_KEY_PAIR)] diff --git a/ec2api/tests/test_network_interface.py b/ec2api/tests/test_network_interface.py index b685ec44..7dcec150 100644 --- a/ec2api/tests/test_network_interface.py +++ b/ec2api/tests/test_network_interface.py @@ -365,6 +365,29 @@ class NetworkInterfaceTestCase(base.ApiTestCase): [fakes.EC2_NETWORK_INTERFACE_1, fakes.EC2_NETWORK_INTERFACE_2])) + self.check_filtering( + 'DescribeNetworkInterfaces', 'networkInterfaceSet', + [('addresses.private-ip-address', + fakes.IP_NETWORK_INTERFACE_2_EXT_1,), + # TODO(ft): support filtering by a boolean value +# ('addresses.primary', False), + ('description', fakes.DESCRIPTION_NETWORK_INTERFACE_1), + # TODO(ft): add security groups to fake data +# ('group-id', ), +# ('group-name'), + # TODO(ft): declare a constant for the mac in fakes + ('mac-address', 'fb:10:2e:b2:ba:b7'), + ('network-interface-id', fakes.ID_EC2_NETWORK_INTERFACE_1), + ('owner-id', fakes.ID_OS_PROJECT), + ('private-ip-address', fakes.IP_NETWORK_INTERFACE_1), + # TODO(ft): support filtering by a boolean value +# ('requester-managed', False), + # TODO(ft): support filtering by a boolean value +# ('source-dest-check', True), + ('status', 'available'), + ('vpc-id', fakes.ID_EC2_VPC_1), + ('subnet-id', fakes.ID_EC2_SUBNET_2)]) + def test_describe_network_interface_attribute(self): self.db_api.get_item_by_id.return_value = fakes.DB_NETWORK_INTERFACE_1 diff --git a/ec2api/tests/test_route_table.py b/ec2api/tests/test_route_table.py index 7c169068..240b6855 100644 --- a/ec2api/tests/test_route_table.py +++ b/ec2api/tests/test_route_table.py @@ -696,6 +696,24 @@ class RouteTableTestCase(base.ApiTestCase): matchers.ListMatches([fakes.EC2_ROUTE_TABLE_1, fakes.EC2_ROUTE_TABLE_2])) + self.check_filtering( + 'DescribeRouteTables', 'routeTableSet', + [('association.route-table-association-id', + fakes.ID_EC2_ROUTE_TABLE_ASSOCIATION_1), + ('association.route-table-id', fakes.ID_EC2_ROUTE_TABLE_1), + # TODO(ft): add fake data for this case +# ('association.subnet-id', ), + # TODO(ft): support filtering by a boolean value +# ('association.main', True), + ('route-table-id', fakes.ID_EC2_ROUTE_TABLE_1), + ('route.destination-cidr-block', fakes.CIDR_EXTERNAL_NETWORK), + ('route.gateway-id', 'local'), + ('route.instance-id', fakes.ID_EC2_INSTANCE_1), + ('route.origin', 'CreateRouteTable'), + ('route.state', 'active'), + ('vpc-id', fakes.ID_EC2_VPC_1) + ]) + def test_describe_route_tables_variations(self): igw_1 = tools.purge_dict(fakes.DB_IGW_1, ('vpc_id',)) igw_2 = tools.update_dict(fakes.DB_IGW_2, diff --git a/ec2api/tests/test_security_group.py b/ec2api/tests/test_security_group.py index 732ac9dd..5bebd1c0 100644 --- a/ec2api/tests/test_security_group.py +++ b/ec2api/tests/test_security_group.py @@ -281,6 +281,13 @@ class SecurityGroupTestCase(base.ApiTestCase): [fakes.EC2_SECURITY_GROUP_2], orderless_lists=True)) + self.check_filtering( + 'DescribeSecurityGroups', 'securityGroupInfo', + [('vpc-id', fakes.ID_EC2_VPC_1), + # TODO(ft): declare a constant for the group name in fakes + ('group-name', 'groupname'), + ('group-id', fakes.ID_EC2_SECURITY_GROUP_1)]) + def test_describe_security_groups_nova(self): security_group.security_group_engine = ( security_group.SecurityGroupEngineNova()) diff --git a/ec2api/tests/test_snapshot.py b/ec2api/tests/test_snapshot.py index 0533246f..f230805c 100644 --- a/ec2api/tests/test_snapshot.py +++ b/ec2api/tests/test_snapshot.py @@ -57,6 +57,22 @@ class SnapshotTestCase(base.ApiTestCase): self.db_api.get_items_by_ids.assert_any_call( mock.ANY, 'snap', set([fakes.ID_EC2_SNAPSHOT_1])) + self.check_filtering( + 'DescribeSnapshots', 'snapshotSet', + [ + # NOTE(ft): declare a constant for the description in fakes + ('description', 'fake description'), + ('owner-id', fakes.ID_OS_PROJECT), + ('progress', '100%'), + ('snapshot-id', fakes.ID_EC2_SNAPSHOT_1), + ('start-time', fakes.TIME_CREATE_SNAPSHOT_2), + ('status', 'completed'), + ('volume-id', fakes.ID_EC2_VOLUME_2), + # TODO(ft): support filtering by a number value + # NOTE(ft): declare a constant for the volume size in fakes +# ('volume-size', 1) + ]) + def test_describe_snapshots_invalid_parameters(self): self.cinder.volume_snapshots.list.return_value = [ fakes.OSSnapshot(fakes.OS_SNAPSHOT_1), diff --git a/ec2api/tests/test_subnet.py b/ec2api/tests/test_subnet.py index 3456938f..6f03f35a 100644 --- a/ec2api/tests/test_subnet.py +++ b/ec2api/tests/test_subnet.py @@ -253,6 +253,19 @@ class SubnetTestCase(base.ApiTestCase): matchers.ListMatches([fakes.EC2_SUBNET_1, fakes.EC2_SUBNET_2])) + self.check_filtering( + 'DescribeSubnets', 'subnetSet', + [ + # TODO(ft): support filtering by a number value + # NOTE(ft): declare a constant for the count in fakes +# ('available-ip-address-count', 253), + ('cidr', fakes.CIDR_SUBNET_2), + ('cidrBlock', fakes.CIDR_SUBNET_2), + ('cidr-block', fakes.CIDR_SUBNET_2), + ('subnet-id', fakes.ID_EC2_SUBNET_2), + ('state', 'available'), + ('vpc-id', fakes.ID_EC2_VPC_1)]) + @base.skip_not_implemented def test_describe_subnets_no_vpc(self): pass diff --git a/ec2api/tests/test_tag.py b/ec2api/tests/test_tag.py index 5a45c170..7f5acf72 100644 --- a/ec2api/tests/test_tag.py +++ b/ec2api/tests/test_tag.py @@ -193,16 +193,12 @@ class TagTestCase(base.ApiTestCase): orderless_lists=True), verbose=True) - # NOTE(ft): check filtering is plugged - 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.1' % num, 'fake') - for num, field in enumerate(filter_fields))) - resp = self.execute('DescribeTags', filter_param) - self.assertEqual({'http_status_code': 200, - 'tagSet': []}, - resp) + self.check_filtering( + 'DescribeTags', 'tagSet', + [('resource-type', 'vpc'), + ('resource-id', fakes.ID_EC2_VPC_1), + ('key', 'key1'), + ('value', 'value2')]) # NOTE(ft): check all resource types are displayed correctly for r_id, r_type in [('dopt', 'dhcp-options'), diff --git a/ec2api/tests/test_volume.py b/ec2api/tests/test_volume.py index f69c0bc5..2455fae6 100644 --- a/ec2api/tests/test_volume.py +++ b/ec2api/tests/test_volume.py @@ -62,6 +62,17 @@ class VolumeTestCase(base.ApiTestCase): self.db_api.get_items_by_ids.assert_any_call( mock.ANY, 'vol', set([fakes.ID_EC2_VOLUME_1])) + self.check_filtering( + 'DescribeVolumes', 'volumeSet', + [('availability-zone', fakes.NAME_AVAILABILITY_ZONE), + ('create-time', fakes.TIME_CREATE_VOLUME_2), + # TODO(ft): support filtering by a number value + # NOTE(ft): declare a constant for the volume size in fakes +# ('size', 1), + ('snapshot-id', fakes.ID_EC2_SNAPSHOT_1), + ('status', 'available'), + ('volume-id', fakes.ID_EC2_VOLUME_1)]) + def test_describe_volumes_invalid_parameters(self): self.cinder.volumes.list.return_value = [ fakes.CinderVolume(fakes.OS_VOLUME_1), diff --git a/ec2api/tests/test_vpc.py b/ec2api/tests/test_vpc.py index a823a787..426919de 100644 --- a/ec2api/tests/test_vpc.py +++ b/ec2api/tests/test_vpc.py @@ -231,6 +231,15 @@ class VpcTestCase(base.ApiTestCase): fakes.EC2_VPC_2])) self.db_api.get_items.assert_called_once_with(mock.ANY, 'vpc') + self.check_filtering( + 'DescribeVpcs', 'vpcSet', + [('cidr', fakes.CIDR_VPC_1), + ('dhcp-options-id', 'default'), + # TODO(ft): support filtering by a boolean value +# ('is-default', False), + ('state', 'available'), + ('vpc-id', fakes.ID_EC2_VPC_1)]) + def test_describe_vpcs_no_router(self): self.neutron.list_routers.return_value = {'routers': []} self.db_api.get_items.return_value = [fakes.DB_VPC_1]