Network Interfaces: Initial implementation.
This commit is contained in:
parent
22d9141122
commit
42f8cea5e6
@ -72,6 +72,14 @@ class InvalidSubnetIdError(EC2ClientError):
|
||||
.format(subnet_id))
|
||||
|
||||
|
||||
class InvalidNetworkInterfaceIdError(EC2ClientError):
|
||||
def __init__(self, eni_id):
|
||||
super(InvalidNetworkInterfaceIdError, self).__init__(
|
||||
"InvalidNetworkInterfaceID.NotFound",
|
||||
"The network interface ID '{0}' does not exist"
|
||||
.format(eni_id))
|
||||
|
||||
|
||||
class InvalidSecurityGroupDuplicateError(EC2ClientError):
|
||||
def __init__(self, name):
|
||||
super(InvalidSecurityGroupDuplicateError, self).__init__(
|
||||
|
@ -26,6 +26,7 @@ from .exceptions import (
|
||||
ResourceAlreadyAssociatedError,
|
||||
InvalidVPCIdError,
|
||||
InvalidSubnetIdError,
|
||||
InvalidNetworkInterfaceIdError,
|
||||
InvalidSecurityGroupDuplicateError,
|
||||
InvalidSecurityGroupNotFoundError,
|
||||
InvalidPermissionNotFoundError,
|
||||
@ -48,11 +49,14 @@ from .utils import (
|
||||
random_dhcp_option_id,
|
||||
random_eip_allocation_id,
|
||||
random_eip_association_id,
|
||||
random_eni_attach_id,
|
||||
random_eni_id,
|
||||
random_internet_gateway_id,
|
||||
random_instance_id,
|
||||
random_internet_gateway_id,
|
||||
random_ip,
|
||||
random_key_pair,
|
||||
random_public_ip,
|
||||
random_reservation_id,
|
||||
random_route_table_id,
|
||||
random_security_group_id,
|
||||
@ -77,6 +81,172 @@ class TaggedEC2Instance(object):
|
||||
return tags
|
||||
|
||||
|
||||
class NetworkInterface(object):
|
||||
def __init__(self, subnet, private_ip_address, device_index=0, public_ip_auto_assign=True, group_ids=None):
|
||||
self.id = random_eni_id()
|
||||
self.device_index = device_index
|
||||
self.private_ip_address = private_ip_address
|
||||
self.subnet = subnet
|
||||
self.instance = None
|
||||
|
||||
self.public_ip = None
|
||||
self.public_ip_auto_assign = public_ip_auto_assign
|
||||
self.start()
|
||||
|
||||
self.attachments = []
|
||||
self.group_set = []
|
||||
|
||||
group = None
|
||||
if group_ids:
|
||||
for group_id in group_ids:
|
||||
group = ec2_backend.get_security_group_from_id(group_id)
|
||||
if not group:
|
||||
# Create with specific group ID.
|
||||
group = SecurityGroup(group_id, group_id, group_id, vpc_id=subnet.vpc_id)
|
||||
ec2_backend.groups[subnet.vpc_id][group_id] = group
|
||||
if group:
|
||||
self.group_set.append(group)
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
security_group_ids = properties.get('SecurityGroups', [])
|
||||
|
||||
subnet_id = properties['SubnetId']
|
||||
subnet = ec2_backend.get_subnet(subnet_id)
|
||||
|
||||
private_ip_address = properties.get('PrivateIpAddress', None)
|
||||
|
||||
network_interface = ec2_backend.create_network_interface(
|
||||
subnet,
|
||||
private_ip_address,
|
||||
group_ids=security_group_ids
|
||||
)
|
||||
return network_interface
|
||||
|
||||
def stop(self):
|
||||
if self.public_ip_auto_assign:
|
||||
self.public_ip = None
|
||||
|
||||
def start(self):
|
||||
self.check_auto_public_ip()
|
||||
|
||||
def check_auto_public_ip(self):
|
||||
if self.public_ip_auto_assign:
|
||||
self.public_ip = random_public_ip()
|
||||
|
||||
def attach(self, instance_id, device_index):
|
||||
attachment = {'attachmentId': random_eni_attach_id(),
|
||||
'instanceId': instance_id,
|
||||
'deviceIndex': device_index}
|
||||
self.attachments.append(attachment)
|
||||
|
||||
|
||||
class NetworkInterfaceBackend(object):
|
||||
def __init__(self):
|
||||
self.enis = {}
|
||||
super(NetworkInterfaceBackend, self).__init__()
|
||||
|
||||
def create_network_interface(self, subnet, private_ip_address, group_ids=None, **kwargs):
|
||||
eni = NetworkInterface(subnet, private_ip_address, group_ids=group_ids)
|
||||
self.enis[eni.id] = eni
|
||||
return eni
|
||||
|
||||
def get_network_interface(self, eni_id):
|
||||
for eni in self.enis.values():
|
||||
if eni_id == eni.id:
|
||||
return eni
|
||||
raise InvalidNetworkInterfaceIdError(eni_id)
|
||||
|
||||
def delete_network_interface(self, eni_id):
|
||||
deleted = self.enis.pop(eni_id, None)
|
||||
if not deleted:
|
||||
raise InvalidNetworkInterfaceIdError(eni_id)
|
||||
return deleted
|
||||
|
||||
def describe_network_interfaces(self, filters=None):
|
||||
enis = self.enis.values()
|
||||
|
||||
if filters:
|
||||
for (_filter, _filter_value) in filters.items():
|
||||
if _filter == 'network-interface-id':
|
||||
_filter = 'id'
|
||||
enis = [ eni for eni in enis if getattr(eni, _filter) in _filter_value ]
|
||||
elif _filter == 'group-id':
|
||||
original_enis = enis
|
||||
enis = []
|
||||
for eni in original_enis:
|
||||
group_ids = []
|
||||
for group in eni.group_set:
|
||||
if group.id in _filter_value:
|
||||
enis.append(eni)
|
||||
break
|
||||
else:
|
||||
ec2_backend.raise_not_implemented_error("The filter '{0}' for DescribeNetworkInterfaces".format(_filter))
|
||||
return enis
|
||||
|
||||
def modify_network_interface_attribute(self, eni_id, group_id):
|
||||
eni = self.get_network_interface(eni_id)
|
||||
group = self.get_security_group_from_id(group_id)
|
||||
eni.group_set = [group]
|
||||
|
||||
def prep_nics_for_instance(self, instance, nic_spec, subnet_id=None, private_ip=None, associate_public_ip=None):
|
||||
nics = {}
|
||||
|
||||
# Primary NIC defaults
|
||||
primary_nic = {'SubnetId': subnet_id,
|
||||
'PrivateIpAddress': private_ip,
|
||||
'AssociatePublicIpAddress': associate_public_ip}
|
||||
primary_nic = dict((k,v) for k, v in primary_nic.items() if v)
|
||||
|
||||
# If empty NIC spec but primary NIC values provided, create NIC from them.
|
||||
if primary_nic and not nic_spec:
|
||||
nic_spec[0] = primary_nic
|
||||
nic_spec[0]['DeviceIndex'] = 0
|
||||
|
||||
# Flesh out data structures and associations
|
||||
for nic in nic_spec.values():
|
||||
use_eni = None
|
||||
security_group_ids = []
|
||||
|
||||
device_index = int(nic.get('DeviceIndex'))
|
||||
|
||||
nic_id = nic.get('NetworkInterfaceId', None)
|
||||
if nic_id:
|
||||
# If existing NIC found, use it.
|
||||
use_nic = ec2_backend.get_network_interface(nic_id)
|
||||
use_nic.device_index = device_index
|
||||
use_nic.public_ip_auto_assign = False
|
||||
|
||||
else:
|
||||
# If primary NIC values provided, use them for the primary NIC.
|
||||
if device_index == 0 and primary_nic:
|
||||
nic.update(primary_nic)
|
||||
|
||||
subnet = ec2_backend.get_subnet(nic['SubnetId'])
|
||||
|
||||
group_id = nic.get('SecurityGroupId',None)
|
||||
group_ids = [group_id] if group_id else []
|
||||
|
||||
use_nic = ec2_backend.create_network_interface(subnet,
|
||||
nic.get('PrivateIpAddress',None),
|
||||
device_index=device_index,
|
||||
public_ip_auto_assign=nic.get('AssociatePublicIpAddress',False),
|
||||
group_ids=group_ids)
|
||||
|
||||
use_nic.instance = instance # This is used upon associate/disassociate public IP.
|
||||
|
||||
if use_nic.instance.security_groups:
|
||||
use_nic.group_set.extend(use_nic.instance.security_groups)
|
||||
|
||||
use_nic.attach(instance.id, device_index)
|
||||
|
||||
nics[device_index] = use_nic
|
||||
|
||||
return nics
|
||||
|
||||
|
||||
class Instance(BotoInstance, TaggedEC2Instance):
|
||||
def __init__(self, image_id, user_data, security_groups, **kwargs):
|
||||
super(Instance, self).__init__()
|
||||
@ -108,6 +278,12 @@ class Instance(BotoInstance, TaggedEC2Instance):
|
||||
# string will have a "u" prefix -- need to get rid of it
|
||||
self.user_data[0] = self.user_data[0].encode('utf-8')
|
||||
|
||||
self.nics = ec2_backend.prep_nics_for_instance(self,
|
||||
kwargs.get("nics", {}),
|
||||
subnet_id=kwargs.get("subnet_id",None),
|
||||
private_ip=kwargs.get("private_ip",None),
|
||||
associate_public_ip=kwargs.get("associate_public_ip",None))
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
@ -131,14 +307,23 @@ class Instance(BotoInstance, TaggedEC2Instance):
|
||||
return self.id
|
||||
|
||||
def start(self, *args, **kwargs):
|
||||
for nic in self.nics.values():
|
||||
nic.start()
|
||||
|
||||
self._state.name = "running"
|
||||
self._state.code = 16
|
||||
|
||||
def stop(self, *args, **kwargs):
|
||||
for nic in self.nics.values():
|
||||
nic.stop()
|
||||
|
||||
self._state.name = "stopped"
|
||||
self._state.code = 80
|
||||
|
||||
def terminate(self, *args, **kwargs):
|
||||
for nic in self.nics.values():
|
||||
nic.stop()
|
||||
|
||||
self._state.name = "terminated"
|
||||
self._state.code = 48
|
||||
|
||||
@ -146,6 +331,21 @@ class Instance(BotoInstance, TaggedEC2Instance):
|
||||
self._state.name = "running"
|
||||
self._state.code = 16
|
||||
|
||||
def get_tags(self):
|
||||
tags = ec2_backend.describe_tags(self.id)
|
||||
return tags
|
||||
|
||||
@property
|
||||
def dynamic_group_list(self):
|
||||
if self.nics:
|
||||
groups = []
|
||||
for nic in self.nics.values():
|
||||
for group in nic.group_set:
|
||||
groups.append(group)
|
||||
return groups
|
||||
else:
|
||||
return self.security_groups
|
||||
|
||||
|
||||
class InstanceBackend(object):
|
||||
|
||||
@ -511,6 +711,7 @@ class SecurityGroup(object):
|
||||
self.description = description
|
||||
self.ingress_rules = []
|
||||
self.egress_rules = []
|
||||
self.enis = {}
|
||||
self.vpc_id = vpc_id
|
||||
|
||||
@classmethod
|
||||
@ -569,18 +770,23 @@ class SecurityGroupBackend(object):
|
||||
def describe_security_groups(self):
|
||||
return itertools.chain(*[x.values() for x in self.groups.values()])
|
||||
|
||||
def _delete_security_group(self, vpc_id, group_id):
|
||||
if self.groups[vpc_id][group_id].enis:
|
||||
raise DependencyViolationError("{0} is being utilized by {1}".format(group_id, 'ENIs'))
|
||||
return self.groups[vpc_id].pop(group_id)
|
||||
|
||||
def delete_security_group(self, name=None, group_id=None):
|
||||
if group_id:
|
||||
# loop over all the SGs, find the right one
|
||||
for vpc in self.groups.values():
|
||||
if group_id in vpc:
|
||||
return vpc.pop(group_id)
|
||||
for vpc_id, groups in self.groups.items():
|
||||
if group_id in groups:
|
||||
return self._delete_security_group(vpc_id, group_id)
|
||||
raise InvalidSecurityGroupNotFoundError(group_id)
|
||||
elif name:
|
||||
# Group Name. Has to be in standard EC2, VPC needs to be identified by group_id
|
||||
group = self.get_security_group_from_name(name)
|
||||
if group:
|
||||
return self.groups[None].pop(group.id)
|
||||
return self._delete_security_group(None, group.id)
|
||||
raise InvalidSecurityGroupNotFoundError(name)
|
||||
|
||||
def get_security_group_from_id(self, group_id):
|
||||
@ -1003,6 +1209,12 @@ class SubnetBackend(object):
|
||||
self.subnets = {}
|
||||
super(SubnetBackend, self).__init__()
|
||||
|
||||
def get_subnet(self, subnet_id):
|
||||
subnet = self.subnets.get(subnet_id, None)
|
||||
if not subnet:
|
||||
raise InvalidSubnetIdError(subnet_id)
|
||||
return subnet
|
||||
|
||||
def create_subnet(self, vpc_id, cidr_block):
|
||||
subnet_id = random_subnet_id()
|
||||
subnet = Subnet(subnet_id, vpc_id, cidr_block)
|
||||
@ -1289,6 +1501,7 @@ class ElasticAddress(object):
|
||||
self.allocation_id = random_eip_allocation_id() if domain == "vpc" else None
|
||||
self.domain = domain
|
||||
self.instance = None
|
||||
self.eni = None
|
||||
self.association_id = None
|
||||
|
||||
@classmethod
|
||||
@ -1355,7 +1568,7 @@ class ElasticAddressBackend(object):
|
||||
|
||||
return eips
|
||||
|
||||
def associate_address(self, instance, address=None, allocation_id=None, reassociate=False):
|
||||
def associate_address(self, instance=None, eni=None, address=None, allocation_id=None, reassociate=False):
|
||||
eips = []
|
||||
if address:
|
||||
eips = self.address_by_ip([address])
|
||||
@ -1363,13 +1576,20 @@ class ElasticAddressBackend(object):
|
||||
eips = self.address_by_allocation([allocation_id])
|
||||
eip = eips[0]
|
||||
|
||||
if eip.instance and not reassociate:
|
||||
raise ResourceAlreadyAssociatedError(eip.public_ip)
|
||||
new_instance_association = bool(instance and (not eip.instance or eip.instance.id == instance.id))
|
||||
new_eni_association = bool(eni and (not eip.eni or eni.id == eip.eni.id))
|
||||
|
||||
eip.instance = instance
|
||||
if eip.domain == "vpc":
|
||||
eip.association_id = random_eip_association_id()
|
||||
return eip
|
||||
if new_instance_association or new_eni_association or reassociate:
|
||||
eip.instance = instance
|
||||
eip.eni = eni
|
||||
if eip.eni:
|
||||
eip.eni.public_ip = eip.public_ip
|
||||
if eip.domain == "vpc":
|
||||
eip.association_id = random_eip_association_id()
|
||||
|
||||
return eip
|
||||
|
||||
raise ResourceAlreadyAssociatedError(eip.public_ip)
|
||||
|
||||
def describe_addresses(self):
|
||||
return self.addresses
|
||||
@ -1382,6 +1602,13 @@ class ElasticAddressBackend(object):
|
||||
eips = self.address_by_association([association_id])
|
||||
eip = eips[0]
|
||||
|
||||
if eip.eni:
|
||||
if eip.eni.instance and eip.eni.instance._state.name == "running":
|
||||
eip.eni.check_auto_public_ip()
|
||||
else:
|
||||
eip.eni.public_ip = None
|
||||
eip.eni = None
|
||||
|
||||
eip.instance = None
|
||||
eip.association_id = None
|
||||
return True
|
||||
@ -1474,6 +1701,7 @@ class DHCPOptionsSetBackend(object):
|
||||
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
||||
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
|
||||
VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend,
|
||||
NetworkInterfaceBackend,
|
||||
VPCPeeringConnectionBackend,
|
||||
RouteTableBackend, RouteBackend, InternetGatewayBackend,
|
||||
VPCGatewayAttachmentBackend, SpotRequestBackend,
|
||||
|
@ -17,10 +17,12 @@ class ElasticIPAddresses(BaseResponse):
|
||||
return template.render(address=address)
|
||||
|
||||
def associate_address(self):
|
||||
instance = eni = None
|
||||
|
||||
if "InstanceId" in self.querystring:
|
||||
instance = ec2_backend.get_instance(self.querystring['InstanceId'][0])
|
||||
elif "NetworkInterfaceId" in self.querystring:
|
||||
raise NotImplementedError("Lookup by allocation id not implemented")
|
||||
eni = ec2_backend.get_network_interface(self.querystring['NetworkInterfaceId'][0])
|
||||
else:
|
||||
ec2_backend.raise_error("MissingParameter", "Invalid request, expect InstanceId/NetworkId parameter.")
|
||||
|
||||
@ -28,12 +30,15 @@ class ElasticIPAddresses(BaseResponse):
|
||||
if "AllowReassociation" in self.querystring:
|
||||
reassociate = self.querystring['AllowReassociation'][0] == "true"
|
||||
|
||||
if "PublicIp" in self.querystring:
|
||||
eip = ec2_backend.associate_address(instance, address=self.querystring['PublicIp'][0], reassociate=reassociate)
|
||||
elif "AllocationId" in self.querystring:
|
||||
eip = ec2_backend.associate_address(instance, allocation_id=self.querystring['AllocationId'][0], reassociate=reassociate)
|
||||
if instance or eni:
|
||||
if "PublicIp" in self.querystring:
|
||||
eip = ec2_backend.associate_address(instance=instance, eni=eni, address=self.querystring['PublicIp'][0], reassociate=reassociate)
|
||||
elif "AllocationId" in self.querystring:
|
||||
eip = ec2_backend.associate_address(instance=instance, eni=eni, allocation_id=self.querystring['AllocationId'][0], reassociate=reassociate)
|
||||
else:
|
||||
ec2_backend.raise_error("MissingParameter", "Invalid request, expect PublicIp/AllocationId parameter.")
|
||||
else:
|
||||
ec2_backend.raise_error("MissingParameter", "Invalid request, expect PublicIp/AllocationId parameter.")
|
||||
ec2_backend.raise_error("MissingParameter", "Invalid request, expect either instance or ENI.")
|
||||
|
||||
template = Template(ASSOCIATE_ADDRESS_RESPONSE)
|
||||
return template.render(address=eip)
|
||||
@ -103,6 +108,11 @@ DESCRIBE_ADDRESS_RESPONSE = """<DescribeAddressesResponse xmlns="http://ec2.amaz
|
||||
{% else %}
|
||||
<instanceId/>
|
||||
{% endif %}
|
||||
{% if address.eni %}
|
||||
<networkInterfaceId>{{ address.eni.id }}</networkInterfaceId>
|
||||
{% else %}
|
||||
<networkInterfaceId/>
|
||||
{% endif %}
|
||||
{% if address.allocation_id %}
|
||||
<allocationId>{{ address.allocation_id }}</allocationId>
|
||||
{% endif %}
|
||||
|
@ -1,5 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
from jinja2 import Template
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.ec2.models import ec2_backend
|
||||
from moto.ec2.utils import sequence_from_querystring, resource_ids_from_querystring, filters_from_querystring
|
||||
|
||||
|
||||
class ElasticNetworkInterfaces(BaseResponse):
|
||||
@ -7,22 +11,157 @@ class ElasticNetworkInterfaces(BaseResponse):
|
||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).attach_network_interface is not yet implemented')
|
||||
|
||||
def create_network_interface(self):
|
||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).create_network_interface is not yet implemented')
|
||||
subnet_id = self.querystring.get('SubnetId')[0]
|
||||
private_ip_address = self.querystring.get('PrivateIpAddress', [None])[0]
|
||||
groups = sequence_from_querystring('SecurityGroupId', self.querystring)
|
||||
subnet = ec2_backend.get_subnet(subnet_id)
|
||||
eni = ec2_backend.create_network_interface(subnet, private_ip_address, groups)
|
||||
template = Template(CREATE_NETWORK_INTERFACE_RESPONSE)
|
||||
return template.render(eni=eni)
|
||||
|
||||
def delete_network_interface(self):
|
||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).delete_network_interface is not yet implemented')
|
||||
eni_id = self.querystring.get('NetworkInterfaceId')[0]
|
||||
eni = ec2_backend.delete_network_interface(eni_id)
|
||||
template = Template(DELETE_NETWORK_INTERFACE_RESPONSE)
|
||||
return template.render()
|
||||
|
||||
def describe_network_interface_attribute(self):
|
||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).describe_network_interface_attribute is not yet implemented')
|
||||
|
||||
def describe_network_interfaces(self):
|
||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).describe_network_interfaces is not yet implemented')
|
||||
#Partially implemented. Supports only network-interface-id and group-id filters
|
||||
filters = filters_from_querystring(self.querystring)
|
||||
enis = ec2_backend.describe_network_interfaces(filters)
|
||||
template = Template(DESCRIBE_NETWORK_INTERFACES_RESPONSE)
|
||||
return template.render(enis=enis)
|
||||
|
||||
def detach_network_interface(self):
|
||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).detach_network_interface is not yet implemented')
|
||||
|
||||
def modify_network_interface_attribute(self):
|
||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).modify_network_interface_attribute is not yet implemented')
|
||||
#Currently supports modifying one and only one security group
|
||||
eni_id = self.querystring.get('NetworkInterfaceId')[0]
|
||||
group_id = self.querystring.get('SecurityGroupId.1')[0]
|
||||
ec2_backend.modify_network_interface_attribute(eni_id, group_id)
|
||||
return MODIFY_NETWORK_INTERFACE_ATTRIBUTE_RESPONSE
|
||||
|
||||
def reset_network_interface_attribute(self):
|
||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).reset_network_interface_attribute is not yet implemented')
|
||||
|
||||
CREATE_NETWORK_INTERFACE_RESPONSE = """
|
||||
<CreateNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||
<requestId>2c6021ec-d705-445a-9780-420d0c7ab793</requestId>
|
||||
<networkInterface>
|
||||
<networkInterfaceId>{{ eni.id }}</networkInterfaceId>
|
||||
<subnetId>{{ eni.subnet.id }}</subnetId>
|
||||
<vpcId>{{ eni.subnet.vpc_id }}</vpcId>
|
||||
<availabilityZone>us-west-2a</availabilityZone>
|
||||
<description/>
|
||||
<ownerId>498654062920</ownerId>
|
||||
<requesterManaged>false</requesterManaged>
|
||||
<status>pending</status>
|
||||
<macAddress>02:07:a9:b6:12:51</macAddress>
|
||||
{% if eni.private_ip_address %}
|
||||
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||
{% endif %}
|
||||
<sourceDestCheck>true</sourceDestCheck>
|
||||
<groupSet>
|
||||
{% for group in eni.group_set %}
|
||||
<item>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</groupSet>
|
||||
<tagSet/>
|
||||
{% if eni.private_ip_address %}
|
||||
<privateIpAddressesSet>
|
||||
<item>
|
||||
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||
<primary>true</primary>
|
||||
</item>
|
||||
</privateIpAddressesSet>
|
||||
{% else %}
|
||||
<privateIpAddressesSet/>
|
||||
{% endif %}
|
||||
</networkInterface>
|
||||
</CreateNetworkInterfaceResponse>
|
||||
"""
|
||||
|
||||
DESCRIBE_NETWORK_INTERFACES_RESPONSE = """<DescribeNetworkInterfacesResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||
<requestId>ddb0aaf1-8b65-4f0a-94fa-654b18b8a204</requestId>
|
||||
<networkInterfaceSet>
|
||||
{% for eni in enis %}
|
||||
<item>
|
||||
<networkInterfaceId>{{ eni.id }}</networkInterfaceId>
|
||||
<subnetId>{{ eni.subnet.id }}</subnetId>
|
||||
<vpcId>vpc-9367a6f8</vpcId>
|
||||
<availabilityZone>us-west-2a</availabilityZone>
|
||||
<description>Primary network interface</description>
|
||||
<ownerId>190610284047</ownerId>
|
||||
<requesterManaged>false</requesterManaged>
|
||||
<status>in-use</status>
|
||||
<macAddress>0e:a3:a7:7b:95:a7</macAddress>
|
||||
{% if eni.private_ip_address %}
|
||||
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||
{% endif %}
|
||||
<privateDnsName>ip-10-0-0-134.us-west-2.compute.internal</privateDnsName>
|
||||
<sourceDestCheck>true</sourceDestCheck>
|
||||
<groupSet>
|
||||
{% for group in eni.group_set %}
|
||||
<item>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</groupSet>
|
||||
{% for attachment in eni.attachments %}
|
||||
<attachment>
|
||||
<attachmentId>{{ attachment['attachmentId'] }}</attachmentId>
|
||||
<instanceId>{{ attachment['instanceId'] }}</instanceId>
|
||||
<instanceOwnerId>190610284047</instanceOwnerId>
|
||||
<deviceIndex>{{ attachment['deviceIndex'] }}</deviceIndex>
|
||||
<status>attached</status>
|
||||
<attachTime>2013-10-04T17:38:53.000Z</attachTime>
|
||||
<deleteOnTermination>true</deleteOnTermination>
|
||||
</attachment>
|
||||
{% endfor %}
|
||||
<association>
|
||||
<publicIp>{{ eni.public_ip }}</publicIp>
|
||||
<publicDnsName>ec2-54-200-86-47.us-west-2.compute.amazonaws.com</publicDnsName>
|
||||
<ipOwnerId>amazon</ipOwnerId>
|
||||
</association>
|
||||
<tagSet/>
|
||||
{% if eni.private_ip_address %}
|
||||
<privateIpAddressesSet>
|
||||
<item>
|
||||
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||
<privateDnsName>ip-10-0-0-134.us-west-2.compute.internal</privateDnsName>
|
||||
<primary>true</primary>
|
||||
{% if eni.public_ip %}
|
||||
<association>
|
||||
<publicIp>{{ eni.public_ip }}</publicIp>
|
||||
<publicDnsName>ec2-54-200-86-47.us-west-2.compute.amazonaws.com</publicDnsName>
|
||||
<ipOwnerId>amazon</ipOwnerId>
|
||||
</association>
|
||||
{% endif %}
|
||||
</item>
|
||||
</privateIpAddressesSet>
|
||||
{% else %}
|
||||
<privateIpAddressesSet/>
|
||||
{% endif %}
|
||||
</item>
|
||||
{% endfor %}
|
||||
</networkInterfaceSet>
|
||||
</DescribeNetworkInterfacesResponse>"""
|
||||
|
||||
MODIFY_NETWORK_INTERFACE_ATTRIBUTE_RESPONSE = """<ModifyNetworkInterfaceAttributeResponse xmlns="http://ec2.amazonaws.com/doc/2012-12-01/">
|
||||
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||
<return>true</return>
|
||||
</ModifyNetworkInterfaceAttributeResponse>"""
|
||||
|
||||
DELETE_NETWORK_INTERFACE_RESPONSE = """
|
||||
<DeleteNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||
<requestId>34b5b3b4-d0c5-49b9-b5e2-a468ef6adcd8</requestId>
|
||||
<return>true</return>
|
||||
</DeleteNetworkInterfaceResponse>"""
|
||||
|
@ -3,7 +3,7 @@ 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, filter_reservations, dict_from_querystring
|
||||
|
||||
|
||||
class InstanceResponse(BaseResponse):
|
||||
@ -26,13 +26,19 @@ class InstanceResponse(BaseResponse):
|
||||
user_data = self.querystring.get('UserData')
|
||||
security_group_names = self._get_multi_param('SecurityGroup')
|
||||
security_group_ids = self._get_multi_param('SecurityGroupId')
|
||||
nics = dict_from_querystring("NetworkInterface", self.querystring)
|
||||
instance_type = self.querystring.get("InstanceType", ["m1.small"])[0]
|
||||
subnet_id = self.querystring.get("SubnetId", [None])[0]
|
||||
private_ip = self.querystring.get("PrivateIpAddress", [None])[0]
|
||||
associate_public_ip = self.querystring.get("AssociatePublicIpAddress", [None])[0]
|
||||
key_name = self.querystring.get("KeyName", [None])[0]
|
||||
|
||||
new_reservation = self.ec2_backend.add_instances(
|
||||
image_id, min_count, user_data, security_group_names,
|
||||
instance_type=instance_type, subnet_id=subnet_id,
|
||||
key_name=key_name, security_group_ids=security_group_ids)
|
||||
key_name=key_name, security_group_ids=security_group_ids,
|
||||
nics=nics, private_ip=private_ip, associate_public_ip=associate_public_ip)
|
||||
|
||||
template = Template(EC2_RUN_INSTANCES)
|
||||
return template.render(reservation=new_reservation)
|
||||
|
||||
@ -189,10 +195,19 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
|
||||
<monitoring>
|
||||
<state>enabled</state>
|
||||
</monitoring>
|
||||
<subnetId>{{ instance.subnet_id }}</subnetId>
|
||||
{% if instance.nics %}
|
||||
<subnetId>{{ instance.nics[0].subnet.id }}</subnetId>
|
||||
<vpcId>{{ instance.nics[0].subnet.vpc_id }}</vpcId>
|
||||
<privateIpAddress>{{ instance.nics[0].private_ip_address }}</privateIpAddress>
|
||||
{% if instance.nics[0].public_ip %}
|
||||
<ipAddress>46.51.219.63</ipAddress>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<subnetId>{{ instance.subnet_id }}</subnetId>
|
||||
{% endif %}
|
||||
<sourceDestCheck>true</sourceDestCheck>
|
||||
<groupSet>
|
||||
{% for group in instance.security_groups %}
|
||||
{% for group in instance.dynamic_group_list %}
|
||||
<item>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
@ -208,6 +223,54 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
|
||||
<clientToken/>
|
||||
<hypervisor>xen</hypervisor>
|
||||
<ebsOptimized>false</ebsOptimized>
|
||||
<networkInterfaceSet>
|
||||
{% for nic in instance.nics.values() %}
|
||||
<item>
|
||||
<networkInterfaceId>{{ nic.id }}</networkInterfaceId>
|
||||
<subnetId>{{ nic.subnet.id }}</subnetId>
|
||||
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
|
||||
<description>Primary network interface</description>
|
||||
<ownerId>111122223333</ownerId>
|
||||
<status>in-use</status>
|
||||
<macAddress>1b:2b:3c:4d:5e:6f</macAddress>
|
||||
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||
<sourceDestCheck>true</sourceDestCheck>
|
||||
<groupSet>
|
||||
{% for group in nic.group_set %}
|
||||
<item>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</groupSet>
|
||||
<attachment>
|
||||
<attachmentId>eni-attach-1a2b3c4d</attachmentId>
|
||||
<deviceIndex>{{ nic.device_index }}</deviceIndex>
|
||||
<status>attached</status>
|
||||
<attachTime>YYYY-MM-DDTHH:MM:SS+0000</attachTime>
|
||||
<deleteOnTermination>true</deleteOnTermination>
|
||||
</attachment>
|
||||
{% if nic.public_ip %}
|
||||
<association>
|
||||
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||
<ipOwnerId>111122223333</ipOwnerId>
|
||||
</association>
|
||||
{% endif %}
|
||||
<privateIpAddressesSet>
|
||||
<item>
|
||||
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||
<primary>true</primary>
|
||||
{% if nic.public_ip %}
|
||||
<association>
|
||||
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||
<ipOwnerId>111122223333</ipOwnerId>
|
||||
</association>
|
||||
{% endif %}
|
||||
</item>
|
||||
</privateIpAddressesSet>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</networkInterfaceSet>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</instancesSet>
|
||||
@ -220,7 +283,14 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
||||
<item>
|
||||
<reservationId>{{ reservation.id }}</reservationId>
|
||||
<ownerId>111122223333</ownerId>
|
||||
<groupSet></groupSet>
|
||||
<groupSet>
|
||||
{% for group in reservation.dynamic_group_list %}
|
||||
<item>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</groupSet>
|
||||
<instancesSet>
|
||||
{% for instance in reservation.instances %}
|
||||
<item>
|
||||
@ -249,13 +319,17 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
||||
<monitoring>
|
||||
<state>disabled</state>
|
||||
</monitoring>
|
||||
<subnetId>{{ instance.subnet_id }}</subnetId>
|
||||
<vpcId>vpc-1a2b3c4d</vpcId>
|
||||
<privateIpAddress>10.0.0.12</privateIpAddress>
|
||||
<ipAddress>46.51.219.63</ipAddress>
|
||||
{% if instance.nics %}
|
||||
<subnetId>{{ instance.nics[0].subnet.id }}</subnetId>
|
||||
<vpcId>{{ instance.nics[0].subnet.vpc_id }}</vpcId>
|
||||
<privateIpAddress>{{ instance.nics[0].private_ip_address }}</privateIpAddress>
|
||||
{% if instance.nics[0].public_ip %}
|
||||
<ipAddress>46.51.219.63</ipAddress>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<sourceDestCheck>true</sourceDestCheck>
|
||||
<groupSet>
|
||||
{% for group in instance.security_groups %}
|
||||
{% for group in instance.dynamic_group_list %}
|
||||
<item>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
@ -280,7 +354,54 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
||||
{% endfor %}
|
||||
</tagSet>
|
||||
<hypervisor>xen</hypervisor>
|
||||
<networkInterfaceSet />
|
||||
<networkInterfaceSet>
|
||||
{% for nic in instance.nics.values() %}
|
||||
<item>
|
||||
<networkInterfaceId>{{ nic.id }}</networkInterfaceId>
|
||||
<subnetId>{{ nic.subnet.id }}</subnetId>
|
||||
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
|
||||
<description>Primary network interface</description>
|
||||
<ownerId>111122223333</ownerId>
|
||||
<status>in-use</status>
|
||||
<macAddress>1b:2b:3c:4d:5e:6f</macAddress>
|
||||
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||
<sourceDestCheck>true</sourceDestCheck>
|
||||
<groupSet>
|
||||
{% for group in nic.group_set %}
|
||||
<item>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</groupSet>
|
||||
<attachment>
|
||||
<attachmentId>eni-attach-1a2b3c4d</attachmentId>
|
||||
<deviceIndex>{{ nic.device_index }}</deviceIndex>
|
||||
<status>attached</status>
|
||||
<attachTime>YYYY-MM-DDTHH:MM:SS+0000</attachTime>
|
||||
<deleteOnTermination>true</deleteOnTermination>
|
||||
</attachment>
|
||||
{% if nic.public_ip %}
|
||||
<association>
|
||||
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||
<ipOwnerId>111122223333</ipOwnerId>
|
||||
</association>
|
||||
{% endif %}
|
||||
<privateIpAddressesSet>
|
||||
<item>
|
||||
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||
<primary>true</primary>
|
||||
{% if nic.public_ip %}
|
||||
<association>
|
||||
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||
<ipOwnerId>111122223333</ipOwnerId>
|
||||
</association>
|
||||
{% endif %}
|
||||
</item>
|
||||
</privateIpAddressesSet>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</networkInterfaceSet>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</instancesSet>
|
||||
|
@ -72,6 +72,18 @@ def random_dhcp_option_id():
|
||||
return random_id(prefix='dopt')
|
||||
|
||||
|
||||
def random_eni_id():
|
||||
return random_id(prefix='eni')
|
||||
|
||||
|
||||
def random_eni_attach_id():
|
||||
return random_id(prefix='eni-attach')
|
||||
|
||||
|
||||
def random_public_ip():
|
||||
return '54.214.{0}.{1}'.format(random.choice(range(255)),
|
||||
random.choice(range(255)))
|
||||
|
||||
def random_ip():
|
||||
return "127.{0}.{1}.{2}".format(
|
||||
random.randint(0, 255),
|
||||
@ -172,6 +184,21 @@ def filters_from_querystring(querystring_dict):
|
||||
return response_values
|
||||
|
||||
|
||||
def dict_from_querystring(parameter, querystring_dict):
|
||||
use_dict={}
|
||||
for key, value in querystring_dict.items():
|
||||
match = re.search(r"{0}.(\d).(\w+)".format(parameter), key)
|
||||
if match:
|
||||
use_dict_index = match.groups()[0]
|
||||
use_dict_element_property = match.groups()[1]
|
||||
|
||||
if not use_dict.get(use_dict_index):
|
||||
use_dict[use_dict_index] = {}
|
||||
use_dict[use_dict_index][use_dict_element_property]=value[0]
|
||||
|
||||
return use_dict
|
||||
|
||||
|
||||
def keypair_names_from_querystring(querystring_dict):
|
||||
keypair_names = []
|
||||
for key, value in querystring_dict.items():
|
||||
|
@ -110,29 +110,89 @@ def test_eip_associate_vpc():
|
||||
|
||||
instance.terminate()
|
||||
|
||||
@mock_ec2
|
||||
def test_eip_associate_network_interface():
|
||||
"""Associate/Disassociate EIP to NIC"""
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
eni = conn.create_network_interface(subnet.id)
|
||||
|
||||
eip = conn.allocate_address(domain='vpc')
|
||||
eip.network_interface_id.should.be.none
|
||||
|
||||
with assert_raises(EC2ResponseError) as cm:
|
||||
conn.associate_address(network_interface_id=eni.id)
|
||||
cm.exception.code.should.equal('MissingParameter')
|
||||
cm.exception.status.should.equal(400)
|
||||
cm.exception.request_id.should_not.be.none
|
||||
|
||||
conn.associate_address(network_interface_id=eni.id, allocation_id=eip.allocation_id)
|
||||
eip = conn.get_all_addresses(addresses=[eip.public_ip])[0] # no .update() on address ):
|
||||
eip.network_interface_id.should.be.equal(eni.id)
|
||||
conn.disassociate_address(association_id=eip.association_id)
|
||||
eip = conn.get_all_addresses(addresses=[eip.public_ip])[0] # no .update() on address ):
|
||||
eip.network_interface_id.should.be.equal(u'')
|
||||
eip.association_id.should.be.none
|
||||
eip.release()
|
||||
eip = None
|
||||
|
||||
@mock_ec2
|
||||
def test_eip_reassociate():
|
||||
"""reassociate EIP"""
|
||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||
|
||||
reservation = conn.run_instances('ami-1234abcd')
|
||||
instance = reservation.instances[0]
|
||||
reservation = conn.run_instances('ami-1234abcd', min_count=2)
|
||||
instance1, instance2 = reservation.instances
|
||||
|
||||
eip = conn.allocate_address()
|
||||
conn.associate_address(instance_id=instance.id, public_ip=eip.public_ip)
|
||||
conn.associate_address(instance_id=instance1.id, public_ip=eip.public_ip)
|
||||
|
||||
# Same ID is idempotent
|
||||
conn.associate_address(instance_id=instance1.id, public_ip=eip.public_ip)
|
||||
|
||||
# Different ID detects resource association
|
||||
with assert_raises(EC2ResponseError) as cm:
|
||||
conn.associate_address(instance_id=instance.id, public_ip=eip.public_ip, allow_reassociation=False)
|
||||
conn.associate_address(instance_id=instance2.id, public_ip=eip.public_ip, allow_reassociation=False)
|
||||
cm.exception.code.should.equal('Resource.AlreadyAssociated')
|
||||
cm.exception.status.should.equal(400)
|
||||
cm.exception.request_id.should_not.be.none
|
||||
|
||||
conn.associate_address.when.called_with(instance_id=instance.id, public_ip=eip.public_ip, allow_reassociation=True).should_not.throw(EC2ResponseError)
|
||||
conn.associate_address.when.called_with(instance_id=instance2.id, public_ip=eip.public_ip, allow_reassociation=True).should_not.throw(EC2ResponseError)
|
||||
|
||||
eip.release()
|
||||
eip = None
|
||||
|
||||
instance.terminate()
|
||||
instance1.terminate()
|
||||
instance2.terminate()
|
||||
|
||||
@mock_ec2
|
||||
def test_eip_reassociate_nic():
|
||||
"""reassociate EIP"""
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
eni1 = conn.create_network_interface(subnet.id)
|
||||
eni2 = conn.create_network_interface(subnet.id)
|
||||
|
||||
eip = conn.allocate_address()
|
||||
conn.associate_address(network_interface_id=eni1.id, public_ip=eip.public_ip)
|
||||
|
||||
# Same ID is idempotent
|
||||
conn.associate_address(network_interface_id=eni1.id, public_ip=eip.public_ip)
|
||||
|
||||
# Different ID detects resource association
|
||||
with assert_raises(EC2ResponseError) as cm:
|
||||
conn.associate_address(network_interface_id=eni2.id, public_ip=eip.public_ip)
|
||||
cm.exception.code.should.equal('Resource.AlreadyAssociated')
|
||||
cm.exception.status.should.equal(400)
|
||||
cm.exception.request_id.should_not.be.none
|
||||
|
||||
conn.associate_address.when.called_with(network_interface_id=eni2.id, public_ip=eip.public_ip, allow_reassociation=True).should_not.throw(EC2ResponseError)
|
||||
|
||||
eip.release()
|
||||
eip = None
|
||||
|
||||
@mock_ec2
|
||||
def test_eip_associate_invalid_args():
|
||||
|
@ -1,5 +1,10 @@
|
||||
from __future__ import unicode_literals
|
||||
# Ensure 'assert_raises' context manager support for Python 2.6
|
||||
import tests.backport_assert_raises
|
||||
from nose.tools import assert_raises
|
||||
|
||||
import boto
|
||||
from boto.exception import EC2ResponseError
|
||||
import sure # noqa
|
||||
|
||||
from moto import mock_ec2
|
||||
@ -7,4 +12,115 @@ from moto import mock_ec2
|
||||
|
||||
@mock_ec2
|
||||
def test_elastic_network_interfaces():
|
||||
pass
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
eni = conn.create_network_interface(subnet.id)
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(1)
|
||||
eni = all_enis[0]
|
||||
eni.groups.should.have.length_of(0)
|
||||
eni.private_ip_addresses.should.have.length_of(0)
|
||||
|
||||
conn.delete_network_interface(eni.id)
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(0)
|
||||
|
||||
with assert_raises(EC2ResponseError) as cm:
|
||||
conn.delete_network_interface(eni.id)
|
||||
cm.exception.code.should.equal('InvalidNetworkInterfaceID.NotFound')
|
||||
cm.exception.status.should.equal(400)
|
||||
cm.exception.request_id.should_not.be.none
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_elastic_network_interfaces_subnet_validation():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
|
||||
with assert_raises(EC2ResponseError) as cm:
|
||||
conn.create_network_interface("subnet-abcd1234")
|
||||
cm.exception.code.should.equal('InvalidSubnetID.NotFound')
|
||||
cm.exception.status.should.equal(400)
|
||||
cm.exception.request_id.should_not.be.none
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_elastic_network_interfaces_with_private_ip():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
private_ip = "54.0.0.1"
|
||||
eni = conn.create_network_interface(subnet.id, private_ip)
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(1)
|
||||
|
||||
eni = all_enis[0]
|
||||
eni.groups.should.have.length_of(0)
|
||||
|
||||
eni.private_ip_addresses.should.have.length_of(1)
|
||||
eni.private_ip_addresses[0].private_ip_address.should.equal(private_ip)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_elastic_network_interfaces_with_groups():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
security_group1 = conn.create_security_group('test security group #1', 'this is a test security group')
|
||||
security_group2 = conn.create_security_group('test security group #2', 'this is a test security group')
|
||||
conn.create_network_interface(subnet.id, groups=[security_group1.id,security_group2.id])
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(1)
|
||||
|
||||
eni = all_enis[0]
|
||||
eni.groups.should.have.length_of(2)
|
||||
set([group.id for group in eni.groups]).should.equal(set([security_group1.id,security_group2.id]))
|
||||
|
||||
conn.modify_network_interface_attribute(eni.id, 'groupset', [security_group1.id])
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(1)
|
||||
|
||||
eni = all_enis[0]
|
||||
eni.groups.should.have.length_of(1)
|
||||
eni.groups[0].id.should.equal(security_group1.id)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_elastic_network_interfaces_filtering():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
|
||||
security_group1 = conn.create_security_group('test security group #1', 'this is a test security group')
|
||||
security_group2 = conn.create_security_group('test security group #2', 'this is a test security group')
|
||||
|
||||
eni1 = conn.create_network_interface(subnet.id, groups=[security_group1.id,security_group2.id])
|
||||
eni2 = conn.create_network_interface(subnet.id, groups=[security_group1.id])
|
||||
eni3 = conn.create_network_interface(subnet.id)
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(3)
|
||||
|
||||
# Filter by ENI ID
|
||||
enis_by_id = conn.get_all_network_interfaces(filters={'network-interface-id':eni1.id})
|
||||
enis_by_id.should.have.length_of(1)
|
||||
set([eni.id for eni in enis_by_id]).should.equal(set([eni1.id]))
|
||||
|
||||
# Filter by Security Group
|
||||
enis_by_group = conn.get_all_network_interfaces(filters={'group-id':security_group1.id})
|
||||
enis_by_group.should.have.length_of(2)
|
||||
set([eni.id for eni in enis_by_group]).should.equal(set([eni1.id,eni2.id]))
|
||||
|
||||
# Filter by ENI ID and Security Group
|
||||
enis_by_group = conn.get_all_network_interfaces(filters={'network-interface-id':eni1.id, 'group-id':security_group1.id})
|
||||
enis_by_group.should.have.length_of(1)
|
||||
set([eni.id for eni in enis_by_group]).should.equal(set([eni1.id]))
|
||||
|
||||
# Unsupported filter
|
||||
conn.get_all_network_interfaces.when.called_with(filters={'not-implemented-filter': 'foobar'}).should.throw(NotImplementedError)
|
||||
|
||||
|
@ -233,12 +233,86 @@ def test_run_instance_with_instance_type():
|
||||
|
||||
@mock_ec2
|
||||
def test_run_instance_with_subnet():
|
||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||
reservation = conn.run_instances('ami-1234abcd',
|
||||
subnet_id="subnet-abcd1234")
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
reservation = conn.run_instances('ami-1234abcd', subnet_id=subnet.id)
|
||||
instance = reservation.instances[0]
|
||||
|
||||
instance.subnet_id.should.equal("subnet-abcd1234")
|
||||
instance.subnet_id.should.equal(subnet.id)
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(1)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_run_instance_with_nic_autocreated():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
security_group1 = conn.create_security_group('test security group #1', 'this is a test security group')
|
||||
security_group2 = conn.create_security_group('test security group #2', 'this is a test security group')
|
||||
private_ip = "54.0.0.1"
|
||||
|
||||
reservation = conn.run_instances('ami-1234abcd', subnet_id=subnet.id,
|
||||
security_groups=[security_group1.name],
|
||||
security_group_ids=[security_group2.id],
|
||||
private_ip_address=private_ip)
|
||||
instance = reservation.instances[0]
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(1)
|
||||
eni = all_enis[0]
|
||||
|
||||
instance.interfaces.should.have.length_of(1)
|
||||
instance.interfaces[0].id.should.equal(eni.id)
|
||||
|
||||
instance.subnet_id.should.equal(subnet.id)
|
||||
instance.groups.should.have.length_of(2)
|
||||
set([group.id for group in instance.groups]).should.equal(set([security_group1.id,security_group2.id]))
|
||||
|
||||
eni.subnet_id.should.equal(subnet.id)
|
||||
eni.groups.should.have.length_of(2)
|
||||
set([group.id for group in eni.groups]).should.equal(set([security_group1.id,security_group2.id]))
|
||||
eni.private_ip_addresses.should.have.length_of(1)
|
||||
eni.private_ip_addresses[0].private_ip_address.should.equal(private_ip)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_run_instance_with_nic_preexisting():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
security_group1 = conn.create_security_group('test security group #1', 'this is a test security group')
|
||||
security_group2 = conn.create_security_group('test security group #2', 'this is a test security group')
|
||||
private_ip = "54.0.0.1"
|
||||
eni = conn.create_network_interface(subnet.id, private_ip, groups=[security_group1.id])
|
||||
|
||||
# Boto requires NetworkInterfaceCollection of NetworkInterfaceSpecifications...
|
||||
# annoying, but generates the desired querystring.
|
||||
from boto.ec2.networkinterface import NetworkInterfaceSpecification, NetworkInterfaceCollection
|
||||
interface = NetworkInterfaceSpecification(network_interface_id=eni.id, device_index=0)
|
||||
interfaces = NetworkInterfaceCollection(interface)
|
||||
# end Boto objects
|
||||
|
||||
reservation = conn.run_instances('ami-1234abcd', network_interfaces=interfaces,
|
||||
security_group_ids=[security_group2.id])
|
||||
instance = reservation.instances[0]
|
||||
|
||||
instance.subnet_id.should.equal(subnet.id)
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(1)
|
||||
|
||||
instance.interfaces.should.have.length_of(1)
|
||||
instance_eni = instance.interfaces[0]
|
||||
instance_eni.id.should.equal(eni.id)
|
||||
|
||||
instance_eni.subnet_id.should.equal(subnet.id)
|
||||
instance_eni.groups.should.have.length_of(2)
|
||||
set([group.id for group in instance_eni.groups]).should.equal(set([security_group1.id,security_group2.id]))
|
||||
instance_eni.private_ip_addresses.should.have.length_of(1)
|
||||
instance_eni.private_ip_addresses[0].private_ip_address.should.equal(private_ip)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
|
Loading…
Reference in New Issue
Block a user