Merge pull request #254 from spulec/internet-gateway-filters

Implement InternetGateway filters support
This commit is contained in:
Steve Pulec 2014-11-05 14:52:42 -05:00
commit d8dee720ab
5 changed files with 165 additions and 42 deletions

View File

@ -83,7 +83,10 @@ from .utils import (
is_valid_resource_id,
get_prefix,
simple_aws_filter_to_re,
is_valid_cidr)
is_valid_cidr,
filter_internet_gateways,
filter_reservations,
)
def validate_resource_ids(resource_ids):
@ -586,7 +589,7 @@ class InstanceBackend(object):
if instance.id == instance_id:
return instance
def get_reservations_by_instance_ids(self, instance_ids):
def get_reservations_by_instance_ids(self, instance_ids, filters=None):
""" Go through all of the reservations and filter to only return those
associated with the given instance_ids.
"""
@ -603,15 +606,20 @@ class InstanceBackend(object):
if len(found_instance_ids) != len(instance_ids):
invalid_id = list(set(instance_ids).difference(set(found_instance_ids)))[0]
raise InvalidInstanceIdError(invalid_id)
if filters is not None:
reservations = filter_reservations(reservations, filters)
return reservations
def all_reservations(self, make_copy=False):
def all_reservations(self, make_copy=False, filters=None):
if make_copy:
# Return copies so that other functions can modify them with changing
# the originals
return [copy.deepcopy(reservation) for reservation in self.reservations.values()]
reservations = [copy.deepcopy(reservation) for reservation in self.reservations.values()]
else:
return [reservation for reservation in self.reservations.values()]
reservations = [reservation for reservation in self.reservations.values()]
if filters is not None:
reservations = filter_reservations(reservations, filters)
return reservations
class KeyPairBackend(object):
@ -1867,6 +1875,13 @@ class InternetGateway(TaggedEC2Resource):
def physical_resource_id(self):
return self.id
@property
def attachment_state(self):
if self.vpc:
return "available"
else:
return "detached"
class InternetGatewayBackend(object):
def __init__(self):
@ -1878,14 +1893,19 @@ class InternetGatewayBackend(object):
self.internet_gateways[igw.id] = igw
return igw
def describe_internet_gateways(self, internet_gateway_ids=None):
def describe_internet_gateways(self, internet_gateway_ids=None, filters=None):
igws = []
for igw_id in internet_gateway_ids or []:
if igw_id in self.internet_gateways:
igws.append(self.internet_gateways[igw_id])
else:
raise InvalidInternetGatewayIdError(igw_id)
return igws or self.internet_gateways.values()
if internet_gateway_ids is None:
igws = self.internet_gateways.values()
else:
for igw_id in internet_gateway_ids:
if igw_id in self.internet_gateways:
igws.append(self.internet_gateways[igw_id])
else:
raise InvalidInternetGatewayIdError(igw_id)
if filters is not None:
igws = filter_internet_gateways(igws, filters)
return igws
def delete_internet_gateway(self, internet_gateway_id):
igw = self.get_internet_gateway(internet_gateway_id)

View File

@ -3,20 +3,18 @@ from jinja2 import Template
from moto.core.responses import BaseResponse
from moto.core.utils import camelcase_to_underscores
from moto.ec2.utils import instance_ids_from_querystring, filters_from_querystring, filter_reservations, \
from moto.ec2.utils import instance_ids_from_querystring, filters_from_querystring, \
dict_from_querystring, optional_from_querystring
class InstanceResponse(BaseResponse):
def describe_instances(self):
filter_dict = filters_from_querystring(self.querystring)
instance_ids = instance_ids_from_querystring(self.querystring)
if instance_ids:
reservations = self.ec2_backend.get_reservations_by_instance_ids(instance_ids)
reservations = self.ec2_backend.get_reservations_by_instance_ids(instance_ids, filters=filter_dict)
else:
reservations = self.ec2_backend.all_reservations(make_copy=True)
filter_dict = filters_from_querystring(self.querystring)
reservations = filter_reservations(reservations, filter_dict)
reservations = self.ec2_backend.all_reservations(make_copy=True, filters=filter_dict)
template = Template(EC2_DESCRIBE_INSTANCES)
return template.render(reservations=reservations)

View File

@ -1,8 +1,12 @@
from __future__ import unicode_literals
from moto.core.responses import BaseResponse
from moto.ec2.utils import sequence_from_querystring
from moto.ec2.utils import (
sequence_from_querystring,
filters_from_querystring,
)
from jinja2 import Template
class InternetGateways(BaseResponse):
def attach_internet_gateway(self):
igw_id = self.querystring.get("InternetGatewayId", [None])[0]
@ -23,15 +27,14 @@ class InternetGateways(BaseResponse):
return template.render()
def describe_internet_gateways(self):
if "Filter.1.Name" in self.querystring:
raise NotImplementedError(
"Filtering not supported in describe_internet_gateways.")
elif "InternetGatewayId.1" in self.querystring:
filter_dict = filters_from_querystring(self.querystring)
if "InternetGatewayId.1" in self.querystring:
igw_ids = sequence_from_querystring(
"InternetGatewayId", self.querystring)
igws = self.ec2_backend.describe_internet_gateways(igw_ids)
igws = self.ec2_backend.describe_internet_gateways(igw_ids, filters=filter_dict)
else:
igws = self.ec2_backend.describe_internet_gateways()
igws = self.ec2_backend.describe_internet_gateways(filters=filter_dict)
template = Template(DESCRIBE_INTERNET_GATEWAYS_RESPONSE)
return template.render(internet_gateways=igws)

View File

@ -269,17 +269,9 @@ def keypair_names_from_querystring(querystring_dict):
return keypair_names
filter_dict_attribute_mapping = {
'instance-state-name': 'state',
'instance-id': 'id',
'state-reason-code': '_state_reason.code',
'source-dest-check': 'source_dest_check',
'vpc-id': 'vpc_id',
}
def get_instance_value(instance, instance_attr):
keys = instance_attr.split('.')
val = instance
def get_object_value(obj, attr):
keys = attr.split('.')
val = obj
for key in keys:
if hasattr(val, key):
val = getattr(val, key)
@ -289,18 +281,40 @@ def get_instance_value(instance, instance_attr):
return None
return val
def is_tag_filter(filter_name):
return filter_name.startswith('tag:')
def get_obj_tag(obj, filter_name):
tag_name = filter_name.replace('tag:', '', 1)
tags = dict((tag['key'], tag['value']) for tag in obj.get_tags())
return tags.get(tag_name)
def tag_filter_matches(obj, filter_name, filter_values):
tag_value = get_obj_tag(obj, filter_name)
return tag_value in filter_values
filter_dict_attribute_mapping = {
'instance-state-name': 'state',
'instance-id': 'id',
'state-reason-code': '_state_reason.code',
'source-dest-check': 'source_dest_check',
'vpc-id': 'vpc_id',
}
def passes_filter_dict(instance, filter_dict):
for filter_name, filter_values in filter_dict.items():
if filter_name in filter_dict_attribute_mapping:
instance_attr = filter_dict_attribute_mapping[filter_name]
instance_value = get_instance_value(instance, instance_attr)
instance_value = get_object_value(instance, instance_attr)
if instance_value not in filter_values:
return False
elif filter_name.startswith('tag:'):
tags = dict((tag['key'], tag['value']) for tag in instance.get_tags())
tag_name = filter_name.replace('tag:', '', 1)
tag_value = tags.get(tag_name)
if tag_value not in filter_values:
elif is_tag_filter(filter_name):
if not tag_filter_matches(instance, filter_name, filter_values):
return False
else:
raise NotImplementedError(
@ -322,6 +336,37 @@ def filter_reservations(reservations, filter_dict):
return result
filter_dict_igw_mapping = {
"attachment.vpc-id": "vpc.id",
"attachment.state": "attachment_state",
"internet-gateway-id": "id",
}
def passes_igw_filter_dict(igw, filter_dict):
for filter_name, filter_values in filter_dict.items():
if filter_name in filter_dict_igw_mapping:
igw_attr = filter_dict_igw_mapping[filter_name]
if get_object_value(igw, igw_attr) not in filter_values:
return False
elif is_tag_filter(filter_name):
if not tag_filter_matches(igw, filter_name, filter_values):
return False
else:
raise NotImplementedError(
"Internet Gateway filter dicts have not been implemented in Moto for '%s' yet. Feel free to open an issue at https://github.com/spulec/moto/issues",
filter_name)
return True
def filter_internet_gateways(igws, filter_dict):
result = []
for igw in igws:
if passes_igw_filter_dict(igw, filter_dict):
result.append(igw)
return result
def is_filter_matching(obj, filter, filter_value):
value = obj.get_filter_value(filter)

View File

@ -163,3 +163,60 @@ def test_igw_desribe_bad_id():
cm.exception.code.should.equal('InvalidInternetGatewayID.NotFound')
cm.exception.status.should.equal(400)
cm.exception.request_id.should_not.be.none
@mock_ec2
def test_igw_filter_by_vpc_id():
""" internet gateway filter by vpc id """
conn = boto.connect_vpc('the_key', 'the_secret')
igw1 = conn.create_internet_gateway()
igw2 = conn.create_internet_gateway()
vpc = conn.create_vpc(VPC_CIDR)
conn.attach_internet_gateway(igw1.id, vpc.id)
result = conn.get_all_internet_gateways(filters={"attachment.vpc-id": vpc.id})
result.should.have.length_of(1)
result[0].id.should.equal(igw1.id)
@mock_ec2
def test_igw_filter_by_tags():
""" internet gateway filter by vpc id """
conn = boto.connect_vpc('the_key', 'the_secret')
igw1 = conn.create_internet_gateway()
igw2 = conn.create_internet_gateway()
igw1.add_tag("tests", "yes")
result = conn.get_all_internet_gateways(filters={"tag:tests": "yes"})
result.should.have.length_of(1)
result[0].id.should.equal(igw1.id)
@mock_ec2
def test_igw_filter_by_internet_gateway_id():
""" internet gateway filter by internet gateway id """
conn = boto.connect_vpc('the_key', 'the_secret')
igw1 = conn.create_internet_gateway()
igw2 = conn.create_internet_gateway()
result = conn.get_all_internet_gateways(filters={"internet-gateway-id": igw1.id})
result.should.have.length_of(1)
result[0].id.should.equal(igw1.id)
@mock_ec2
def test_igw_filter_by_attachment_state():
""" internet gateway filter by attachment state """
conn = boto.connect_vpc('the_key', 'the_secret')
igw1 = conn.create_internet_gateway()
igw2 = conn.create_internet_gateway()
vpc = conn.create_vpc(VPC_CIDR)
conn.attach_internet_gateway(igw1.id, vpc.id)
result = conn.get_all_internet_gateways(filters={"attachment.state": "available"})
result.should.have.length_of(1)
result[0].id.should.equal(igw1.id)