Merge branch 'master' of https://github.com/spulec/moto into 0.4.1-threadsafe

* 'master' of https://github.com/spulec/moto: (25 commits)
  Add @zkourouma to authors.
  0.4.2
  Fix bug where listener certificate was not being saved correctly when creating an elb. Added test to cover that case.
  [dynamodb2] adds lookup method to Table class
  Add IAM list_groups and list_groups_for_user. Closes #343.
  Fix for deleting Route53 record sets with set identifiers. Closes #342.
  Use dummy date instead of an invalid date
  Adding support for comments on hosted zones.
  Add availability zone support to Subnets created via CloudFormation
  Make availability zone dynamic in Subnet Response templates
  Add filter "availabilityZone" to DescribeSubnets and add availability zone support too
  allow starting without reseting
  Fix bug with empty string for instance vpc_id. Closes #337.
  Fix default security group description.
  Update responses.py
  Add @mrucci to authors.
  Fix merge conflicts.
  Add support for ELB attributes.
  cast to int when doing math.
  General cleanup.
  ...
This commit is contained in:
Jeffrey Gelens 2015-05-29 11:35:14 +02:00
commit e722b67f36
33 changed files with 1381 additions and 167 deletions

View file

@ -1149,6 +1149,10 @@ class SecurityGroupBackend(object):
def __init__(self):
# the key in the dict group is the vpc_id or None (non-vpc)
self.groups = defaultdict(dict)
# Create the default security group
self.create_security_group("default", "default group")
super(SecurityGroupBackend, self).__init__()
def create_security_group(self, name, description, vpc_id=None, force=False):
@ -1212,11 +1216,6 @@ class SecurityGroupBackend(object):
if group.name == name:
return group
if name == 'default':
# If the request is for the default group and it does not exist, create it
default_group = self.create_security_group("default", "The default security group", vpc_id=vpc_id, force=True)
return default_group
def get_security_group_by_name_or_id(self, group_name_or_id, vpc_id):
# try searching by id, fallbacks to name search
group = self.get_security_group_from_id(group_name_or_id)
@ -1309,7 +1308,7 @@ class SecurityGroupIngress(object):
from_port = properties.get("FromPort")
source_security_group_id = properties.get("SourceSecurityGroupId")
source_security_group_name = properties.get("SourceSecurityGroupName")
source_security_owner_id = properties.get("SourceSecurityGroupOwnerId") # IGNORED AT THE MOMENT
# source_security_owner_id = properties.get("SourceSecurityGroupOwnerId") # IGNORED AT THE MOMENT
to_port = properties.get("ToPort")
assert group_id or group_name
@ -1329,7 +1328,6 @@ class SecurityGroupIngress(object):
else:
ip_ranges = []
if group_id:
security_group = ec2_backend.describe_security_groups(group_ids=[group_id])[0]
else:
@ -1697,41 +1695,66 @@ class VPCPeeringConnectionBackend(object):
class Subnet(TaggedEC2Resource):
def __init__(self, ec2_backend, subnet_id, vpc_id, cidr_block):
def __init__(self, ec2_backend, subnet_id, vpc_id, cidr_block, availability_zone):
self.ec2_backend = ec2_backend
self.id = subnet_id
self.vpc_id = vpc_id
self.cidr_block = cidr_block
self._availability_zone = availability_zone
@classmethod
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
properties = cloudformation_json['Properties']
vpc_id = properties['VpcId']
cidr_block = properties['CidrBlock']
availability_zone = properties.get('AvailabilityZone')
ec2_backend = ec2_backends[region_name]
subnet = ec2_backend.create_subnet(
vpc_id=vpc_id,
cidr_block=properties['CidrBlock']
cidr_block=cidr_block,
availability_zone=availability_zone,
)
return subnet
@property
def availability_zone(self):
# This could probably be smarter, but there doesn't appear to be a
# way to pull AZs for a region in boto
return self.ec2_backend.region_name + "a"
if self._availability_zone is None:
# This could probably be smarter, but there doesn't appear to be a
# way to pull AZs for a region in boto
return self.ec2_backend.region_name + "a"
else:
return self._availability_zone
@property
def physical_resource_id(self):
return self.id
def get_filter_value(self, filter_name):
"""
API Version 2014-10-01 defines the following filters for DescribeSubnets:
* availabilityZone
* available-ip-address-count
* cidrBlock
* defaultForAz
* state
* subnet-id
* tag:key=value
* tag-key
* tag-value
* vpc-id
Taken from: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html
"""
if filter_name in ['cidr', 'cidrBlock', 'cidr-block']:
return self.cidr_block
elif filter_name == 'vpc-id':
return self.vpc_id
elif filter_name == 'subnet-id':
return self.id
elif filter_name == 'availabilityZone':
return self.availability_zone
filter_value = super(Subnet, self).get_filter_value(filter_name)
@ -1758,9 +1781,9 @@ class SubnetBackend(object):
raise InvalidSubnetIdError(subnet_id)
return subnet
def create_subnet(self, vpc_id, cidr_block):
def create_subnet(self, vpc_id, cidr_block, availability_zone=None):
subnet_id = random_subnet_id()
subnet = Subnet(self, subnet_id, vpc_id, cidr_block)
subnet = Subnet(self, subnet_id, vpc_id, cidr_block, availability_zone)
self.get_vpc(vpc_id) # Validate VPC exists
# AWS associates a new subnet with the default Network ACL

View file

@ -103,7 +103,7 @@ CREATE_VOLUME_RESPONSE = """<CreateVolumeResponse xmlns="http://ec2.amazonaws.co
<snapshotId/>
<availabilityZone>{{ volume.zone.name }}</availabilityZone>
<status>creating</status>
<createTime>YYYY-MM-DDTHH:MM:SS.000Z</createTime>
<createTime>2013-10-04T17:38:53.000Z</createTime>
<volumeType>standard</volumeType>
</CreateVolumeResponse>"""
@ -117,7 +117,7 @@ DESCRIBE_VOLUMES_RESPONSE = """<DescribeVolumesResponse xmlns="http://ec2.amazon
<snapshotId/>
<availabilityZone>{{ volume.zone.name }}</availabilityZone>
<status>{{ volume.status }}</status>
<createTime>YYYY-MM-DDTHH:MM:SS.SSSZ</createTime>
<createTime>2013-10-04T17:38:53.000Z</createTime>
<attachmentSet>
{% if volume.attachment %}
<item>
@ -125,7 +125,7 @@ DESCRIBE_VOLUMES_RESPONSE = """<DescribeVolumesResponse xmlns="http://ec2.amazon
<instanceId>{{ volume.attachment.instance.id }}</instanceId>
<device>{{ volume.attachment.device }}</device>
<status>attached</status>
<attachTime>YYYY-MM-DDTHH:MM:SS.SSSZ</attachTime>
<attachTime>2013-10-04T17:38:53.000Z</attachTime>
<deleteOnTermination>false</deleteOnTermination>
</item>
{% endif %}
@ -157,7 +157,7 @@ ATTACHED_VOLUME_RESPONSE = """<AttachVolumeResponse xmlns="http://ec2.amazonaws.
<instanceId>{{ attachment.instance.id }}</instanceId>
<device>{{ attachment.device }}</device>
<status>attaching</status>
<attachTime>YYYY-MM-DDTHH:MM:SS.000Z</attachTime>
<attachTime>2013-10-04T17:38:53.000Z</attachTime>
</AttachVolumeResponse>"""
DETATCH_VOLUME_RESPONSE = """<DetachVolumeResponse xmlns="http://ec2.amazonaws.com/doc/2012-12-01/">
@ -166,7 +166,7 @@ DETATCH_VOLUME_RESPONSE = """<DetachVolumeResponse xmlns="http://ec2.amazonaws.c
<instanceId>{{ attachment.instance.id }}</instanceId>
<device>{{ attachment.device }}</device>
<status>detaching</status>
<attachTime>YYYY-MM-DDTHH:MM:SS.000Z</attachTime>
<attachTime>2013-10-04T17:38:53.000Z</attachTime>
</DetachVolumeResponse>"""
CREATE_SNAPSHOT_RESPONSE = """<CreateSnapshotResponse xmlns="http://ec2.amazonaws.com/doc/2012-12-01/">
@ -174,7 +174,7 @@ CREATE_SNAPSHOT_RESPONSE = """<CreateSnapshotResponse xmlns="http://ec2.amazonaw
<snapshotId>{{ snapshot.id }}</snapshotId>
<volumeId>{{ snapshot.volume.id }}</volumeId>
<status>pending</status>
<startTime>YYYY-MM-DDTHH:MM:SS.000Z</startTime>
<startTime>2013-10-04T17:38:53.000Z</startTime>
<progress>60%</progress>
<ownerId>111122223333</ownerId>
<volumeSize>{{ snapshot.volume.size }}</volumeSize>
@ -189,7 +189,7 @@ DESCRIBE_SNAPSHOTS_RESPONSE = """<DescribeSnapshotsResponse xmlns="http://ec2.am
<snapshotId>{{ snapshot.id }}</snapshotId>
<volumeId>{{ snapshot.volume.id }}</volumeId>
<status>pending</status>
<startTime>YYYY-MM-DDTHH:MM:SS.SSSZ</startTime>
<startTime>2013-10-04T17:38:53.000Z</startTime>
<progress>30%</progress>
<ownerId>111122223333</ownerId>
<volumeSize>{{ snapshot.volume.size }}</volumeSize>

View file

@ -214,8 +214,10 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
<state>enabled</state>
</monitoring>
{% if instance.nics %}
<subnetId>{{ instance.nics[0].subnet.id }}</subnetId>
<vpcId>{{ instance.nics[0].subnet.vpc_id }}</vpcId>
{% if instance.nics[0].subnet %}
<subnetId>{{ instance.nics[0].subnet.id }}</subnetId>
<vpcId>{{ instance.nics[0].subnet.vpc_id }}</vpcId>
{% endif %}
<privateIpAddress>{{ instance.private_ip }}</privateIpAddress>
{% if instance.public_ip %}
<ipAddress>{{ instance.public_ip }}</ipAddress>
@ -245,8 +247,10 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
{% for nic in instance.nics.values() %}
<item>
<networkInterfaceId>{{ nic.id }}</networkInterfaceId>
<subnetId>{{ nic.subnet.id }}</subnetId>
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
{% if nic.subnet %}
<subnetId>{{ nic.subnet.id }}</subnetId>
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
{% endif %}
<description>Primary network interface</description>
<ownerId>111122223333</ownerId>
<status>in-use</status>
@ -338,8 +342,10 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
<state>disabled</state>
</monitoring>
{% if instance.nics %}
<subnetId>{{ instance.nics[0].subnet.id }}</subnetId>
<vpcId>{{ instance.nics[0].subnet.vpc_id }}</vpcId>
{% if instance.nics[0].subnet %}
<subnetId>{{ instance.nics[0].subnet.id }}</subnetId>
<vpcId>{{ instance.nics[0].subnet.vpc_id }}</vpcId>
{% endif %}
<privateIpAddress>{{ instance.private_ip }}</privateIpAddress>
{% if instance.nics[0].public_ip %}
<ipAddress>{{ instance.nics[0].public_ip }}</ipAddress>
@ -390,8 +396,10 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
{% for nic in instance.nics.values() %}
<item>
<networkInterfaceId>{{ nic.id }}</networkInterfaceId>
<subnetId>{{ nic.subnet.id }}</subnetId>
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
{% if nic.subnet %}
<subnetId>{{ nic.subnet.id }}</subnetId>
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
{% endif %}
<description>Primary network interface</description>
<ownerId>111122223333</ownerId>
<status>in-use</status>

View file

@ -7,7 +7,15 @@ class Subnets(BaseResponse):
def create_subnet(self):
vpc_id = self.querystring.get('VpcId')[0]
cidr_block = self.querystring.get('CidrBlock')[0]
subnet = self.ec2_backend.create_subnet(vpc_id, cidr_block)
if 'AvailabilityZone' in self.querystring:
availability_zone = self.querystring['AvailabilityZone'][0]
else:
availability_zone = None
subnet = self.ec2_backend.create_subnet(
vpc_id,
cidr_block,
availability_zone,
)
template = self.response_template(CREATE_SUBNET_RESPONSE)
return template.render(subnet=subnet)
@ -33,7 +41,7 @@ CREATE_SUBNET_RESPONSE = """
<vpcId>{{ subnet.vpc_id }}</vpcId>
<cidrBlock>{{ subnet.cidr_block }}</cidrBlock>
<availableIpAddressCount>251</availableIpAddressCount>
<availabilityZone>us-east-1a</availabilityZone>
<availabilityZone>{{ subnet.availability_zone }}</availabilityZone>
<tagSet>
{% for tag in subnet.get_tags() %}
<item>
@ -64,7 +72,7 @@ DESCRIBE_SUBNETS_RESPONSE = """
<vpcId>{{ subnet.vpc_id }}</vpcId>
<cidrBlock>{{ subnet.cidr_block }}</cidrBlock>
<availableIpAddressCount>251</availableIpAddressCount>
<availabilityZone>us-east-1a</availabilityZone>
<availabilityZone>{{ subnet.availability_zone }}</availabilityZone>
<tagSet>
{% for tag in subnet.get_tags() %}
<item>