From 665beda46615189f204fa7e0414f6385cdf735ae Mon Sep 17 00:00:00 2001 From: Rory-Finnegan Date: Thu, 21 Aug 2014 19:04:48 -0500 Subject: [PATCH] Added support to get_all_security_groups endpoint to actually filter groups. - Filters by groupnames, group_ids and a filters. However, the filters option doesn't support owner-id and tags since neither attribute was readily available via the SecurityGroup object. - Also included a basic test to confirm it works. --- moto/ec2/models.py | 53 +++++++++++++++++++++++++- moto/ec2/responses/security_groups.py | 28 +++++++++++++- tests/test_ec2/test_security_groups.py | 19 +++++++++ 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index bab604890..4c31a1431 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -544,6 +544,43 @@ class SecurityGroup(object): def physical_resource_id(self): return self.id + def matches_filters(self, filters): + result = True + + def to_attr(filter_name): + attr = None + + if attr == 'group-name': + attr = 'name' + elif attr == 'group-id': + attr = 'id' + else: + attr = filter_name.replace('-', '_') + + return attr + + for key, value in filters.iteritems(): + ret = False + + if key.startswith('ip-permission'): + match = re.search(r"ip-permission.(*)", key) + ingress_attr = to_attr(match.groups()[0]) + + for ingress in self.ingress_rules: + if getattr(ingress, ingress_attr) in filters[key]: + ret = True + break + else: + attr_name = to_attr(key) + ret = getattr(self, attr_name) in filters[key] + + if not ret: + break + else: + result = False + + return result + class SecurityGroupBackend(object): @@ -566,8 +603,20 @@ class SecurityGroupBackend(object): self.groups[vpc_id][group_id] = group return group - def describe_security_groups(self): - return itertools.chain(*[x.values() for x in self.groups.values()]) + def describe_security_groups(self, group_ids=None, groupnames=None, filters=None): + all_groups = itertools.chain(*[x.values() for x in self.groups.values()]) + groups = [] + + if group_ids or groupnames or filters: + for group in all_groups: + if ((group_ids and group.id in group_ids) or + (groupnames and group.name in groupnames) or + (filters and group.matches_filters(filters))): + groups.append(group) + else: + groups = all_groups + + return groups def delete_security_group(self, name=None, group_id=None): if group_id: diff --git a/moto/ec2/responses/security_groups.py b/moto/ec2/responses/security_groups.py index d0ebe69a6..a12caec41 100644 --- a/moto/ec2/responses/security_groups.py +++ b/moto/ec2/responses/security_groups.py @@ -3,6 +3,7 @@ from jinja2 import Template from moto.core.responses import BaseResponse from moto.ec2.models import ec2_backend +from moto.ec2.utils import filters_from_querystring def process_rules_from_querystring(querystring): @@ -35,6 +36,22 @@ def process_rules_from_querystring(querystring): return (name, group_id, ip_protocol, from_port, to_port, ip_ranges, source_groups, source_group_ids) +def process_group_ids_from_querystring(querystring): + group_ids = [] + for key, value in querystring.iteritems(): + if 'GroupId' in key: + group_ids.append(value[0]) + return group_ids + + +def process_groupnames_from_querystring(querystring): + groupnames = [] + for key, value in querystring.iteritems(): + if 'GroupName' in key: + groupnames.append(value[0]) + return groupnames + + class SecurityGroups(BaseResponse): def authorize_security_group_egress(self): raise NotImplementedError('SecurityGroups.authorize_security_group_egress is not yet implemented') @@ -65,7 +82,16 @@ class SecurityGroups(BaseResponse): return DELETE_GROUP_RESPONSE def describe_security_groups(self): - groups = ec2_backend.describe_security_groups() + groupnames = process_groupnames_from_querystring(self.querystring) + group_ids = process_group_ids_from_querystring(self.querystring) + filters = filters_from_querystring(self.querystring) + + groups = ec2_backend.describe_security_groups( + group_ids=group_ids, + groupnames=groupnames, + filters=filters + ) + template = Template(DESCRIBE_SECURITY_GROUPS_RESPONSE) return template.render(groups=groups) diff --git a/tests/test_ec2/test_security_groups.py b/tests/test_ec2/test_security_groups.py index c624922ca..a2f90aff6 100644 --- a/tests/test_ec2/test_security_groups.py +++ b/tests/test_ec2/test_security_groups.py @@ -195,3 +195,22 @@ def test_authorize_group_in_vpc(): # And check that it gets revoked security_group = [group for group in conn.get_all_security_groups() if group.name == 'test1'][0] security_group.rules.should.have.length_of(0) + + +@mock_ec2 +def test_get_all_security_groups(): + conn = boto.connect_ec2() + conn.create_security_group(name='test1', description='test1', vpc_id='vpc-mjm05d27') + conn.create_security_group(name='test2', description='test2') + + resp = conn.get_all_security_groups(groupnames=['test1']) + resp.should.have.length_of(1) + + resp = conn.get_all_security_groups(filters={'vpc_id': ['vpc-mjm05d27']}) + resp.should.have.length_of(1) + + resp = conn.get_all_security_groups(filters={'description': ['test1']}) + resp.should.have.length_of(1) + + resp = conn.get_all_security_groups() + resp.should.have.length_of(2)