Improved support for VPC Address filtering
This commit is contained in:
parent
44bd6e8684
commit
8776129816
@ -2959,6 +2959,27 @@ class ElasticAddress(object):
|
|||||||
return self.allocation_id
|
return self.allocation_id
|
||||||
raise UnformattedGetAttTemplateException()
|
raise UnformattedGetAttTemplateException()
|
||||||
|
|
||||||
|
def get_filter_value(self, filter_name):
|
||||||
|
if filter_name == 'allocation-id':
|
||||||
|
return self.allocation_id
|
||||||
|
elif filter_name == 'association-id':
|
||||||
|
return self.association_id
|
||||||
|
elif filter_name == 'domain':
|
||||||
|
return self.domain
|
||||||
|
elif filter_name == 'instance-id' and self.instance:
|
||||||
|
return self.instance.id
|
||||||
|
elif filter_name == 'network-interface-id' and self.eni:
|
||||||
|
return self.eni.id
|
||||||
|
elif filter_name == 'network-interface-owner-id':
|
||||||
|
msg = "The filter '{0}' for DescribeAddresses has not been" \
|
||||||
|
" implemented in Moto yet. Feel free to open an issue at" \
|
||||||
|
" https://github.com/spulec/moto/issues".format(filter_name)
|
||||||
|
raise NotImplementedError(msg)
|
||||||
|
elif filter_name == 'private-ip-address' and self.eni:
|
||||||
|
return self.eni.private_ip_address
|
||||||
|
elif filter_name == 'public-ip':
|
||||||
|
return self.public_ip
|
||||||
|
|
||||||
|
|
||||||
class ElasticAddressBackend(object):
|
class ElasticAddressBackend(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -3019,6 +3040,9 @@ class ElasticAddressBackend(object):
|
|||||||
if new_instance_association or new_eni_association or reassociate:
|
if new_instance_association or new_eni_association or reassociate:
|
||||||
eip.instance = instance
|
eip.instance = instance
|
||||||
eip.eni = eni
|
eip.eni = eni
|
||||||
|
if not eip.eni and instance:
|
||||||
|
# default to primary network interface
|
||||||
|
eip.eni = instance.nics[0]
|
||||||
if eip.eni:
|
if eip.eni:
|
||||||
eip.eni.public_ip = eip.public_ip
|
eip.eni.public_ip = eip.public_ip
|
||||||
if eip.domain == "vpc":
|
if eip.domain == "vpc":
|
||||||
@ -3030,8 +3054,24 @@ class ElasticAddressBackend(object):
|
|||||||
|
|
||||||
raise ResourceAlreadyAssociatedError(eip.public_ip)
|
raise ResourceAlreadyAssociatedError(eip.public_ip)
|
||||||
|
|
||||||
def describe_addresses(self):
|
def describe_addresses(self, allocation_ids=None, public_ips=None, filters=None):
|
||||||
return self.addresses
|
matches = self.addresses
|
||||||
|
if allocation_ids:
|
||||||
|
matches = [addr for addr in matches
|
||||||
|
if addr.allocation_id in allocation_ids]
|
||||||
|
if len(allocation_ids) > len(matches):
|
||||||
|
unknown_ids = set(allocation_ids) - set(matches)
|
||||||
|
raise InvalidAllocationIdError(unknown_ids)
|
||||||
|
if public_ips:
|
||||||
|
matches = [addr for addr in matches
|
||||||
|
if addr.public_ip in public_ips]
|
||||||
|
if len(public_ips) > len(matches):
|
||||||
|
unknown_ips = set(allocation_ids) - set(matches)
|
||||||
|
raise InvalidAddressError(unknown_ips)
|
||||||
|
if filters:
|
||||||
|
matches = generic_filter(filters, matches)
|
||||||
|
|
||||||
|
return matches
|
||||||
|
|
||||||
def disassociate_address(self, address=None, association_id=None):
|
def disassociate_address(self, address=None, association_id=None):
|
||||||
eips = []
|
eips = []
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
from moto.ec2.utils import sequence_from_querystring
|
from moto.ec2.utils import filters_from_querystring, sequence_from_querystring
|
||||||
|
|
||||||
|
|
||||||
class ElasticIPAddresses(BaseResponse):
|
class ElasticIPAddresses(BaseResponse):
|
||||||
@ -51,29 +51,12 @@ class ElasticIPAddresses(BaseResponse):
|
|||||||
return template.render(address=eip)
|
return template.render(address=eip)
|
||||||
|
|
||||||
def describe_addresses(self):
|
def describe_addresses(self):
|
||||||
|
allocation_ids = sequence_from_querystring('AllocationId', self.querystring)
|
||||||
|
public_ips = sequence_from_querystring('PublicIp', self.querystring)
|
||||||
|
filters = filters_from_querystring(self.querystring)
|
||||||
|
addresses = self.ec2_backend.describe_addresses(
|
||||||
|
allocation_ids, public_ips, filters)
|
||||||
template = self.response_template(DESCRIBE_ADDRESS_RESPONSE)
|
template = self.response_template(DESCRIBE_ADDRESS_RESPONSE)
|
||||||
|
|
||||||
if "Filter.1.Name" in self.querystring:
|
|
||||||
filter_by = sequence_from_querystring(
|
|
||||||
"Filter.1.Name", self.querystring)[0]
|
|
||||||
filter_value = sequence_from_querystring(
|
|
||||||
"Filter.1.Value", self.querystring)
|
|
||||||
if filter_by == 'instance-id':
|
|
||||||
addresses = filter(lambda x: x.instance.id == filter_value[
|
|
||||||
0], self.ec2_backend.describe_addresses())
|
|
||||||
else:
|
|
||||||
raise NotImplementedError(
|
|
||||||
"Filtering not supported in describe_address.")
|
|
||||||
elif "PublicIp.1" in self.querystring:
|
|
||||||
public_ips = sequence_from_querystring(
|
|
||||||
"PublicIp", self.querystring)
|
|
||||||
addresses = self.ec2_backend.address_by_ip(public_ips)
|
|
||||||
elif "AllocationId.1" in self.querystring:
|
|
||||||
allocation_ids = sequence_from_querystring(
|
|
||||||
"AllocationId", self.querystring)
|
|
||||||
addresses = self.ec2_backend.address_by_allocation(allocation_ids)
|
|
||||||
else:
|
|
||||||
addresses = self.ec2_backend.describe_addresses()
|
|
||||||
return template.render(addresses=addresses)
|
return template.render(addresses=addresses)
|
||||||
|
|
||||||
def disassociate_address(self):
|
def disassociate_address(self):
|
||||||
|
@ -402,3 +402,84 @@ def test_eip_describe_none():
|
|||||||
cm.exception.code.should.equal('InvalidAddress.NotFound')
|
cm.exception.code.should.equal('InvalidAddress.NotFound')
|
||||||
cm.exception.status.should.equal(400)
|
cm.exception.status.should.equal(400)
|
||||||
cm.exception.request_id.should_not.be.none
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_eip_filters():
|
||||||
|
service = boto3.resource('ec2', region_name='us-west-1')
|
||||||
|
client = boto3.client('ec2', region_name='us-west-1')
|
||||||
|
vpc_res = client.create_vpc(CidrBlock='10.0.0.0/24')
|
||||||
|
subnet_res = client.create_subnet(
|
||||||
|
VpcId=vpc_res['Vpc']['VpcId'], CidrBlock='10.0.0.0/24')
|
||||||
|
|
||||||
|
def create_inst_with_eip():
|
||||||
|
instance = service.create_instances(**{
|
||||||
|
'InstanceType': 't2.micro',
|
||||||
|
'ImageId': 'ami-test',
|
||||||
|
'MinCount': 1,
|
||||||
|
'MaxCount': 1,
|
||||||
|
'SubnetId': subnet_res['Subnet']['SubnetId']
|
||||||
|
})[0]
|
||||||
|
allocation_id = client.allocate_address(Domain='vpc')['AllocationId']
|
||||||
|
_ = client.associate_address(
|
||||||
|
InstanceId=instance.id,
|
||||||
|
AllocationId=allocation_id,
|
||||||
|
AllowReassociation=False)
|
||||||
|
instance.load()
|
||||||
|
address = service.VpcAddress(allocation_id)
|
||||||
|
address.load()
|
||||||
|
return instance, address
|
||||||
|
|
||||||
|
inst1, eip1 = create_inst_with_eip()
|
||||||
|
inst2, eip2 = create_inst_with_eip()
|
||||||
|
inst3, eip3 = create_inst_with_eip()
|
||||||
|
|
||||||
|
# Param search by AllocationId
|
||||||
|
addresses = list(service.vpc_addresses.filter(AllocationIds=[eip2.allocation_id]))
|
||||||
|
len(addresses).should.be.equal(1)
|
||||||
|
addresses[0].public_ip.should.equal(eip2.public_ip)
|
||||||
|
inst2.public_ip_address.should.equal(addresses[0].public_ip)
|
||||||
|
|
||||||
|
# Param search by PublicIp
|
||||||
|
addresses = list(service.vpc_addresses.filter(PublicIps=[eip3.public_ip]))
|
||||||
|
len(addresses).should.be.equal(1)
|
||||||
|
addresses[0].public_ip.should.equal(eip3.public_ip)
|
||||||
|
inst3.public_ip_address.should.equal(addresses[0].public_ip)
|
||||||
|
|
||||||
|
# Param search by Filter
|
||||||
|
def check_vpc_filter_valid(filter_name, filter_values):
|
||||||
|
addresses = list(service.vpc_addresses.filter(
|
||||||
|
Filters=[{'Name': filter_name,
|
||||||
|
'Values': filter_values}]))
|
||||||
|
len(addresses).should.equal(2)
|
||||||
|
ips = [addr.public_ip for addr in addresses]
|
||||||
|
set(ips).should.equal(set([eip1.public_ip, eip2.public_ip]))
|
||||||
|
ips.should.contain(inst1.public_ip_address)
|
||||||
|
|
||||||
|
def check_vpc_filter_invalid(filter_name):
|
||||||
|
addresses = list(service.vpc_addresses.filter(
|
||||||
|
Filters=[{'Name': filter_name,
|
||||||
|
'Values': ['dummy1', 'dummy2']}]))
|
||||||
|
len(addresses).should.equal(0)
|
||||||
|
|
||||||
|
def check_vpc_filter(filter_name, filter_values):
|
||||||
|
check_vpc_filter_valid(filter_name, filter_values)
|
||||||
|
check_vpc_filter_invalid(filter_name)
|
||||||
|
|
||||||
|
check_vpc_filter('allocation-id', [eip1.allocation_id, eip2.allocation_id])
|
||||||
|
check_vpc_filter('association-id', [eip1.association_id, eip2.association_id])
|
||||||
|
check_vpc_filter('instance-id', [inst1.id, inst2.id])
|
||||||
|
check_vpc_filter(
|
||||||
|
'network-interface-id',
|
||||||
|
[inst1.network_interfaces_attribute[0].get('NetworkInterfaceId'),
|
||||||
|
inst2.network_interfaces_attribute[0].get('NetworkInterfaceId')])
|
||||||
|
check_vpc_filter(
|
||||||
|
'private-ip-address',
|
||||||
|
[inst1.network_interfaces_attribute[0].get('PrivateIpAddress'),
|
||||||
|
inst2.network_interfaces_attribute[0].get('PrivateIpAddress')])
|
||||||
|
check_vpc_filter('public-ip', [inst1.public_ip_address, inst2.public_ip_address])
|
||||||
|
|
||||||
|
# all the ips are in a VPC
|
||||||
|
addresses = list(service.vpc_addresses.filter(
|
||||||
|
Filters=[{'Name': 'domain', 'Values': ['vpc']}]))
|
||||||
|
len(addresses).should.equal(3)
|
||||||
|
Loading…
Reference in New Issue
Block a user