Route Tables / Routes: Initial implementation.
This commit is contained in:
parent
22d9141122
commit
22e6166e4e
@ -95,6 +95,22 @@ class InvalidPermissionNotFoundError(EC2ClientError):
|
|||||||
"Could not find a matching ingress rule")
|
"Could not find a matching ingress rule")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidRouteTableIdError(EC2ClientError):
|
||||||
|
def __init__(self, route_table_id):
|
||||||
|
super(InvalidRouteTableIdError, self).__init__(
|
||||||
|
"InvalidRouteTableID.NotFound",
|
||||||
|
"The routeTable ID '{0}' does not exist"
|
||||||
|
.format(route_table_id))
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidRouteError(EC2ClientError):
|
||||||
|
def __init__(self, route_table_id, cidr):
|
||||||
|
super(InvalidRouteError, self).__init__(
|
||||||
|
"InvalidRoute.NotFound",
|
||||||
|
"no route with destination-cidr-block {0} in route table {1}"
|
||||||
|
.format(cidr, route_table_id))
|
||||||
|
|
||||||
|
|
||||||
class InvalidInstanceIdError(EC2ClientError):
|
class InvalidInstanceIdError(EC2ClientError):
|
||||||
def __init__(self, instance_id):
|
def __init__(self, instance_id):
|
||||||
super(InvalidInstanceIdError, self).__init__(
|
super(InvalidInstanceIdError, self).__init__(
|
||||||
|
@ -29,6 +29,8 @@ from .exceptions import (
|
|||||||
InvalidSecurityGroupDuplicateError,
|
InvalidSecurityGroupDuplicateError,
|
||||||
InvalidSecurityGroupNotFoundError,
|
InvalidSecurityGroupNotFoundError,
|
||||||
InvalidPermissionNotFoundError,
|
InvalidPermissionNotFoundError,
|
||||||
|
InvalidRouteTableIdError,
|
||||||
|
InvalidRouteError,
|
||||||
InvalidInstanceIdError,
|
InvalidInstanceIdError,
|
||||||
MalformedAMIIdError,
|
MalformedAMIIdError,
|
||||||
InvalidAMIIdError,
|
InvalidAMIIdError,
|
||||||
@ -55,6 +57,8 @@ from .utils import (
|
|||||||
random_key_pair,
|
random_key_pair,
|
||||||
random_reservation_id,
|
random_reservation_id,
|
||||||
random_route_table_id,
|
random_route_table_id,
|
||||||
|
generate_route_id,
|
||||||
|
split_route_id,
|
||||||
random_security_group_id,
|
random_security_group_id,
|
||||||
random_snapshot_id,
|
random_snapshot_id,
|
||||||
random_spot_request_id,
|
random_spot_request_id,
|
||||||
@ -856,6 +860,10 @@ class VPCBackend(object):
|
|||||||
vpc_id = random_vpc_id()
|
vpc_id = random_vpc_id()
|
||||||
vpc = VPC(vpc_id, cidr_block)
|
vpc = VPC(vpc_id, cidr_block)
|
||||||
self.vpcs[vpc_id] = vpc
|
self.vpcs[vpc_id] = vpc
|
||||||
|
|
||||||
|
# AWS creates a default main route table.
|
||||||
|
main_route_table = self.create_route_table(vpc_id, main=True)
|
||||||
|
|
||||||
return vpc
|
return vpc
|
||||||
|
|
||||||
def get_vpc(self, vpc_id):
|
def get_vpc(self, vpc_id):
|
||||||
@ -870,6 +878,7 @@ class VPCBackend(object):
|
|||||||
vpc = self.vpcs.pop(vpc_id, None)
|
vpc = self.vpcs.pop(vpc_id, None)
|
||||||
if not vpc:
|
if not vpc:
|
||||||
raise InvalidVPCIdError(vpc_id)
|
raise InvalidVPCIdError(vpc_id)
|
||||||
|
self.delete_route_table_for_vpc(vpc.id)
|
||||||
if vpc.dhcp_options:
|
if vpc.dhcp_options:
|
||||||
vpc.dhcp_options.vpc = None
|
vpc.dhcp_options.vpc = None
|
||||||
self.delete_dhcp_options_set(vpc.dhcp_options.id)
|
self.delete_dhcp_options_set(vpc.dhcp_options.id)
|
||||||
@ -1057,9 +1066,13 @@ class SubnetRouteTableAssociationBackend(object):
|
|||||||
|
|
||||||
|
|
||||||
class RouteTable(object):
|
class RouteTable(object):
|
||||||
def __init__(self, route_table_id, vpc_id):
|
def __init__(self, route_table_id, vpc_id, main=False):
|
||||||
self.id = route_table_id
|
self.id = route_table_id
|
||||||
self.vpc_id = vpc_id
|
self.vpc_id = vpc_id
|
||||||
|
self.main = main
|
||||||
|
self.association_id = None
|
||||||
|
self.subnet_id = None
|
||||||
|
self.routes = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||||
@ -1075,49 +1088,156 @@ class RouteTable(object):
|
|||||||
def physical_resource_id(self):
|
def physical_resource_id(self):
|
||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
|
def get_filter_value(self, filter_name):
|
||||||
|
if filter_name == "association.main":
|
||||||
|
# Note: Boto only supports 'true'.
|
||||||
|
# https://github.com/boto/boto/issues/1742
|
||||||
|
if self.main:
|
||||||
|
return 'true'
|
||||||
|
else:
|
||||||
|
return 'false'
|
||||||
|
elif filter_name == "vpc-id":
|
||||||
|
return self.vpc_id
|
||||||
|
else:
|
||||||
|
msg = "The filter '{0}' for DescribeRouteTables 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)
|
||||||
|
|
||||||
|
|
||||||
class RouteTableBackend(object):
|
class RouteTableBackend(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.route_tables = {}
|
self.route_tables = {}
|
||||||
super(RouteTableBackend, self).__init__()
|
super(RouteTableBackend, self).__init__()
|
||||||
|
|
||||||
def create_route_table(self, vpc_id):
|
def create_route_table(self, vpc_id, main=False):
|
||||||
route_table_id = random_route_table_id()
|
route_table_id = random_route_table_id()
|
||||||
route_table = RouteTable(route_table_id, vpc_id)
|
vpc = self.get_vpc(vpc_id) # Validate VPC exists
|
||||||
|
route_table = RouteTable(route_table_id, vpc_id, main=main)
|
||||||
self.route_tables[route_table_id] = route_table
|
self.route_tables[route_table_id] = route_table
|
||||||
|
|
||||||
|
# AWS creates a default local route.
|
||||||
|
self.create_route(route_table_id, vpc.cidr_block, local=True)
|
||||||
|
|
||||||
return route_table
|
return route_table
|
||||||
|
|
||||||
|
def get_route_table(self, route_table_id):
|
||||||
|
route_table = self.route_tables.get(route_table_id, None)
|
||||||
|
if not route_table:
|
||||||
|
raise InvalidRouteTableIdError(route_table_id)
|
||||||
|
return route_table
|
||||||
|
|
||||||
|
def get_all_route_tables(self, route_table_ids=None, filters=None):
|
||||||
|
route_tables = self.route_tables.values()
|
||||||
|
|
||||||
|
if route_table_ids:
|
||||||
|
route_tables = [ route_table for route_table in route_tables if route_table.id in route_table_ids ]
|
||||||
|
if len(route_tables) != len(route_table_ids):
|
||||||
|
invalid_id = list(set(route_table_ids).difference(set([route_table.id for route_table in route_tables])))[0]
|
||||||
|
raise InvalidRouteTableIdError(invalid_id)
|
||||||
|
|
||||||
|
if filters:
|
||||||
|
for (_filter, _filter_value) in filters.items():
|
||||||
|
route_tables = [ route_table for route_table in route_tables if route_table.get_filter_value(_filter) in _filter_value ]
|
||||||
|
|
||||||
|
return route_tables
|
||||||
|
|
||||||
|
def delete_route_table(self, route_table_id):
|
||||||
|
deleted = self.route_tables.pop(route_table_id, None)
|
||||||
|
if not deleted:
|
||||||
|
raise InvalidRouteTableIdError(route_table_id)
|
||||||
|
return deleted
|
||||||
|
|
||||||
|
def delete_route_table_for_vpc(self, vpc_id):
|
||||||
|
for route_table in self.route_tables.values():
|
||||||
|
if route_table.vpc_id == vpc_id:
|
||||||
|
self.delete_route_table(route_table.id)
|
||||||
|
|
||||||
|
|
||||||
class Route(object):
|
class Route(object):
|
||||||
def __init__(self, route_table_id, destination_cidr_block, gateway_id):
|
def __init__(self, route_table, destination_cidr_block, local=False,
|
||||||
self.route_table_id = route_table_id
|
internet_gateway=None, instance=None, interface=None, vpc_pcx=None):
|
||||||
|
self.id = generate_route_id(route_table.id, destination_cidr_block)
|
||||||
|
self.route_table = route_table
|
||||||
self.destination_cidr_block = destination_cidr_block
|
self.destination_cidr_block = destination_cidr_block
|
||||||
self.gateway_id = gateway_id
|
self.local = local
|
||||||
|
self.internet_gateway = internet_gateway
|
||||||
|
self.instance = instance
|
||||||
|
self.interface = interface
|
||||||
|
self.vpc_pcx = vpc_pcx
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||||
properties = cloudformation_json['Properties']
|
properties = cloudformation_json['Properties']
|
||||||
|
|
||||||
gateway_id = properties.get('GatewayId')
|
gateway_id = properties.get('GatewayId')
|
||||||
|
instance_id = properties.get('InstanceId')
|
||||||
|
interface_id = properties.get('NetworkInterfaceId')
|
||||||
|
pcx_id = properties.get('VpcPeeringConnectionId')
|
||||||
|
|
||||||
route_table_id = properties['RouteTableId']
|
route_table_id = properties['RouteTableId']
|
||||||
route_table = ec2_backend.create_route(
|
route_table = ec2_backend.create_route(
|
||||||
route_table_id=route_table_id,
|
route_table_id=route_table_id,
|
||||||
destination_cidr_block=properties['DestinationCidrBlock'],
|
destination_cidr_block=properties['DestinationCidrBlock'],
|
||||||
gateway_id=gateway_id,
|
gateway_id=gateway_id,
|
||||||
|
instance_id=instance_id,
|
||||||
|
interface_id=interface_id,
|
||||||
|
vpc_peering_connection_id=pcx_id,
|
||||||
)
|
)
|
||||||
return route_table
|
return route_table
|
||||||
|
|
||||||
|
|
||||||
class RouteBackend(object):
|
class RouteBackend(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.routes = {}
|
|
||||||
super(RouteBackend, self).__init__()
|
super(RouteBackend, self).__init__()
|
||||||
|
|
||||||
def create_route(self, route_table_id, destination_cidr_block, gateway_id):
|
def create_route(self, route_table_id, destination_cidr_block, local=False,
|
||||||
route = Route(route_table_id, destination_cidr_block, gateway_id)
|
gateway_id=None, instance_id=None, interface_id=None,
|
||||||
self.routes[destination_cidr_block] = route
|
vpc_peering_connection_id=None):
|
||||||
|
route_table = self.get_route_table(route_table_id)
|
||||||
|
|
||||||
|
if interface_id:
|
||||||
|
ec2_backend.raise_not_implemented_error("CreateRoute to NetworkInterfaceId")
|
||||||
|
|
||||||
|
route = Route(route_table, destination_cidr_block, local=local,
|
||||||
|
internet_gateway=self.get_internet_gateway(gateway_id) if gateway_id else None,
|
||||||
|
instance=self.get_instance(instance_id) if instance_id else None,
|
||||||
|
interface=None,
|
||||||
|
vpc_pcx=self.get_vpc_peering_connection(vpc_peering_connection_id) if vpc_peering_connection_id else None)
|
||||||
|
route_table.routes[route.id] = route
|
||||||
return route
|
return route
|
||||||
|
|
||||||
|
def replace_route(self, route_table_id, destination_cidr_block,
|
||||||
|
gateway_id=None, instance_id=None, interface_id=None,
|
||||||
|
vpc_peering_connection_id=None):
|
||||||
|
route_table = self.get_route_table(route_table_id)
|
||||||
|
route_id = generate_route_id(route_table.id, destination_cidr_block)
|
||||||
|
route = route_table.routes[route_id]
|
||||||
|
|
||||||
|
if interface_id:
|
||||||
|
ec2_backend.raise_not_implemented_error("ReplaceRoute to NetworkInterfaceId")
|
||||||
|
|
||||||
|
route.internet_gateway = self.get_internet_gateway(gateway_id) if gateway_id else None
|
||||||
|
route.instance = self.get_instance(instance_id) if instance_id else None
|
||||||
|
route.interface = None
|
||||||
|
route.vpc_pcx = self.get_vpc_peering_connection(vpc_peering_connection_id) if vpc_peering_connection_id else None
|
||||||
|
|
||||||
|
route_table.routes[route.id] = route
|
||||||
|
return route
|
||||||
|
|
||||||
|
def get_route(self, route_id):
|
||||||
|
route_table_id, destination_cidr_block = split_route_id(route_id)
|
||||||
|
route_table = self.get_route_table(route_table_id)
|
||||||
|
return route_table.get(route_id)
|
||||||
|
|
||||||
|
def delete_route(self, route_table_id, destination_cidr_block):
|
||||||
|
route_table = self.get_route_table(route_table_id)
|
||||||
|
route_id = generate_route_id(route_table_id, destination_cidr_block)
|
||||||
|
deleted = route_table.routes.pop(route_id, None)
|
||||||
|
if not deleted:
|
||||||
|
raise InvalidRouteError(route_table_id, destination_cidr_block)
|
||||||
|
return deleted
|
||||||
|
|
||||||
|
|
||||||
class InternetGateway(TaggedEC2Instance):
|
class InternetGateway(TaggedEC2Instance):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -1153,8 +1273,7 @@ class InternetGatewayBackend(object):
|
|||||||
return igws or self.internet_gateways.values()
|
return igws or self.internet_gateways.values()
|
||||||
|
|
||||||
def delete_internet_gateway(self, internet_gateway_id):
|
def delete_internet_gateway(self, internet_gateway_id):
|
||||||
igw_ids = [internet_gateway_id]
|
igw = self.get_internet_gateway(internet_gateway_id)
|
||||||
igw = self.describe_internet_gateways(internet_gateway_ids=igw_ids)[0]
|
|
||||||
if igw.vpc:
|
if igw.vpc:
|
||||||
raise DependencyViolationError(
|
raise DependencyViolationError(
|
||||||
"{0} is being utilized by {1}"
|
"{0} is being utilized by {1}"
|
||||||
@ -1164,22 +1283,24 @@ class InternetGatewayBackend(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def detach_internet_gateway(self, internet_gateway_id, vpc_id):
|
def detach_internet_gateway(self, internet_gateway_id, vpc_id):
|
||||||
igw_ids = [internet_gateway_id]
|
igw = self.get_internet_gateway(internet_gateway_id)
|
||||||
igw = self.describe_internet_gateways(internet_gateway_ids=igw_ids)[0]
|
|
||||||
if not igw.vpc or igw.vpc.id != vpc_id:
|
if not igw.vpc or igw.vpc.id != vpc_id:
|
||||||
raise GatewayNotAttachedError(internet_gateway_id, vpc_id)
|
raise GatewayNotAttachedError(internet_gateway_id, vpc_id)
|
||||||
igw.vpc = None
|
igw.vpc = None
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def attach_internet_gateway(self, internet_gateway_id, vpc_id):
|
def attach_internet_gateway(self, internet_gateway_id, vpc_id):
|
||||||
igw_ids = [internet_gateway_id]
|
igw = self.get_internet_gateway(internet_gateway_id)
|
||||||
igw = self.describe_internet_gateways(internet_gateway_ids=igw_ids)[0]
|
|
||||||
if igw.vpc:
|
if igw.vpc:
|
||||||
raise ResourceAlreadyAssociatedError(internet_gateway_id)
|
raise ResourceAlreadyAssociatedError(internet_gateway_id)
|
||||||
vpc = self.get_vpc(vpc_id)
|
vpc = self.get_vpc(vpc_id)
|
||||||
igw.vpc = vpc
|
igw.vpc = vpc
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_internet_gateway(self, internet_gateway_id):
|
||||||
|
igw_ids = [internet_gateway_id]
|
||||||
|
return self.describe_internet_gateways(internet_gateway_ids=igw_ids)[0]
|
||||||
|
|
||||||
|
|
||||||
class VPCGatewayAttachment(object):
|
class VPCGatewayAttachment(object):
|
||||||
def __init__(self, gateway_id, vpc_id):
|
def __init__(self, gateway_id, vpc_id):
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
|
from moto.ec2.models import ec2_backend
|
||||||
|
from moto.ec2.utils import route_table_ids_from_querystring, filters_from_querystring, optional_from_querystring
|
||||||
|
|
||||||
|
|
||||||
class RouteTables(BaseResponse):
|
class RouteTables(BaseResponse):
|
||||||
@ -7,25 +11,176 @@ class RouteTables(BaseResponse):
|
|||||||
raise NotImplementedError('RouteTables(AmazonVPC).associate_route_table is not yet implemented')
|
raise NotImplementedError('RouteTables(AmazonVPC).associate_route_table is not yet implemented')
|
||||||
|
|
||||||
def create_route(self):
|
def create_route(self):
|
||||||
raise NotImplementedError('RouteTables(AmazonVPC).create_route is not yet implemented')
|
route_table_id = self.querystring.get('RouteTableId')[0]
|
||||||
|
destination_cidr_block = self.querystring.get('DestinationCidrBlock')[0]
|
||||||
|
|
||||||
|
internet_gateway_id = optional_from_querystring('GatewayId', self.querystring)
|
||||||
|
instance_id = optional_from_querystring('InstanceId', self.querystring)
|
||||||
|
interface_id = optional_from_querystring('NetworkInterfaceId', self.querystring)
|
||||||
|
pcx_id = optional_from_querystring('VpcPeeringConnectionId', self.querystring)
|
||||||
|
|
||||||
|
route = ec2_backend.create_route(route_table_id, destination_cidr_block,
|
||||||
|
gateway_id=internet_gateway_id,
|
||||||
|
instance_id=instance_id,
|
||||||
|
interface_id=interface_id,
|
||||||
|
vpc_peering_connection_id=pcx_id)
|
||||||
|
|
||||||
|
template = Template(CREATE_ROUTE_RESPONSE)
|
||||||
|
return template.render()
|
||||||
|
|
||||||
def create_route_table(self):
|
def create_route_table(self):
|
||||||
raise NotImplementedError('RouteTables(AmazonVPC).create_route_table is not yet implemented')
|
vpc_id = self.querystring.get('VpcId')[0]
|
||||||
|
route_table = ec2_backend.create_route_table(vpc_id)
|
||||||
|
template = Template(CREATE_ROUTE_TABLE_RESPONSE)
|
||||||
|
return template.render(route_table=route_table)
|
||||||
|
|
||||||
def delete_route(self):
|
def delete_route(self):
|
||||||
raise NotImplementedError('RouteTables(AmazonVPC).delete_route is not yet implemented')
|
route_table_id = self.querystring.get('RouteTableId')[0]
|
||||||
|
destination_cidr_block = self.querystring.get('DestinationCidrBlock')[0]
|
||||||
|
ec2_backend.delete_route(route_table_id, destination_cidr_block)
|
||||||
|
template = Template(DELETE_ROUTE_RESPONSE)
|
||||||
|
return template.render()
|
||||||
|
|
||||||
def delete_route_table(self):
|
def delete_route_table(self):
|
||||||
raise NotImplementedError('RouteTables(AmazonVPC).delete_route_table is not yet implemented')
|
route_table_id = self.querystring.get('RouteTableId')[0]
|
||||||
|
ec2_backend.delete_route_table(route_table_id)
|
||||||
|
template = Template(DELETE_ROUTE_TABLE_RESPONSE)
|
||||||
|
return template.render()
|
||||||
|
|
||||||
def describe_route_tables(self):
|
def describe_route_tables(self):
|
||||||
raise NotImplementedError('RouteTables(AmazonVPC).describe_route_tables is not yet implemented')
|
route_table_ids = route_table_ids_from_querystring(self.querystring)
|
||||||
|
filters = filters_from_querystring(self.querystring)
|
||||||
|
route_tables = ec2_backend.get_all_route_tables(route_table_ids, filters)
|
||||||
|
template = Template(DESCRIBE_ROUTE_TABLES_RESPONSE)
|
||||||
|
return template.render(route_tables=route_tables)
|
||||||
|
|
||||||
def disassociate_route_table(self):
|
def disassociate_route_table(self):
|
||||||
raise NotImplementedError('RouteTables(AmazonVPC).disassociate_route_table is not yet implemented')
|
raise NotImplementedError('RouteTables(AmazonVPC).disassociate_route_table is not yet implemented')
|
||||||
|
|
||||||
def replace_route(self):
|
def replace_route(self):
|
||||||
raise NotImplementedError('RouteTables(AmazonVPC).replace_route is not yet implemented')
|
route_table_id = self.querystring.get('RouteTableId')[0]
|
||||||
|
destination_cidr_block = self.querystring.get('DestinationCidrBlock')[0]
|
||||||
|
|
||||||
|
internet_gateway_id = optional_from_querystring('GatewayId', self.querystring)
|
||||||
|
instance_id = optional_from_querystring('InstanceId', self.querystring)
|
||||||
|
interface_id = optional_from_querystring('NetworkInterfaceId', self.querystring)
|
||||||
|
pcx_id = optional_from_querystring('VpcPeeringConnectionId', self.querystring)
|
||||||
|
|
||||||
|
route = ec2_backend.replace_route(route_table_id, destination_cidr_block,
|
||||||
|
gateway_id=internet_gateway_id,
|
||||||
|
instance_id=instance_id,
|
||||||
|
interface_id=interface_id,
|
||||||
|
vpc_peering_connection_id=pcx_id)
|
||||||
|
|
||||||
|
template = Template(REPLACE_ROUTE_RESPONSE)
|
||||||
|
return template.render()
|
||||||
|
|
||||||
def replace_route_table_association(self):
|
def replace_route_table_association(self):
|
||||||
raise NotImplementedError('RouteTables(AmazonVPC).replace_route_table_association is not yet implemented')
|
raise NotImplementedError('RouteTables(AmazonVPC).replace_route_table_association is not yet implemented')
|
||||||
|
|
||||||
|
|
||||||
|
CREATE_ROUTE_RESPONSE = """
|
||||||
|
<CreateRouteResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<return>true</return>
|
||||||
|
</CreateRouteResponse>
|
||||||
|
"""
|
||||||
|
|
||||||
|
REPLACE_ROUTE_RESPONSE = """
|
||||||
|
<ReplaceRouteResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<return>true</return>
|
||||||
|
</ReplaceRouteResponse>
|
||||||
|
"""
|
||||||
|
|
||||||
|
CREATE_ROUTE_TABLE_RESPONSE = """
|
||||||
|
<CreateRouteTableResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<routeTable>
|
||||||
|
<routeTableId>{{ route_table.id }}</routeTableId>
|
||||||
|
<vpcId>{{ route_table.vpc_id }}</vpcId>
|
||||||
|
<routeSet>
|
||||||
|
{% for route in route_table.routes.values() %}
|
||||||
|
{% if route.local %}
|
||||||
|
<item>
|
||||||
|
<destinationCidrBlock>{{ route.destination_cidr_block }}</destinationCidrBlock>
|
||||||
|
<gatewayId>local</gatewayId>
|
||||||
|
<state>active</state>
|
||||||
|
</item>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</routeSet>
|
||||||
|
<associationSet/>
|
||||||
|
<tagSet/>
|
||||||
|
</routeTable>
|
||||||
|
</CreateRouteTableResponse>
|
||||||
|
"""
|
||||||
|
|
||||||
|
DESCRIBE_ROUTE_TABLES_RESPONSE = """
|
||||||
|
<DescribeRouteTablesResponse xmlns="http://ec2.amazonaws.com/doc/2013-08-15/">
|
||||||
|
<requestId>6f570b0b-9c18-4b07-bdec-73740dcf861a</requestId>
|
||||||
|
<routeTableSet>
|
||||||
|
{% for route_table in route_tables %}
|
||||||
|
<item>
|
||||||
|
<routeTableId>{{ route_table.id }}</routeTableId>
|
||||||
|
<vpcId>{{ route_table.vpc_id }}</vpcId>
|
||||||
|
<routeSet>
|
||||||
|
{% for route in route_table.routes.values() %}
|
||||||
|
<item>
|
||||||
|
<destinationCidrBlock>{{ route.destination_cidr_block }}</destinationCidrBlock>
|
||||||
|
{% if route.local %}
|
||||||
|
<gatewayId>local</gatewayId>
|
||||||
|
<origin>CreateRouteTable</origin>
|
||||||
|
<state>active</state>
|
||||||
|
{% endif %}
|
||||||
|
{% if route.internet_gateway %}
|
||||||
|
<gatewayId>{{ route.internet_gateway.id }}</gatewayId>
|
||||||
|
<origin>CreateRoute</origin>
|
||||||
|
<state>active</state>
|
||||||
|
{% endif %}
|
||||||
|
{% if route.instance %}
|
||||||
|
<instanceId>{{ route.instance.id }}</instanceId>
|
||||||
|
<origin>CreateRoute</origin>
|
||||||
|
<state>active</state>
|
||||||
|
{% endif %}
|
||||||
|
{% if route.vpc_pcx %}
|
||||||
|
<origin>CreateRoute</origin>
|
||||||
|
<state>blackhole</state>
|
||||||
|
{% endif %}
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</routeSet>
|
||||||
|
<associationSet>
|
||||||
|
{% if route_table.association_id %}
|
||||||
|
<item>
|
||||||
|
<routeTableAssociationId>{{ route_table.association_id }}</routeTableAssociationId>
|
||||||
|
<routeTableId>{{ route_table.id }}</routeTableId>
|
||||||
|
{% if not route_table.subnet_id %}
|
||||||
|
<main>true</main>
|
||||||
|
{% endif %}
|
||||||
|
{% if route_table.subnet_id %}
|
||||||
|
<subnetId>{{ route_table.subnet_id }}</subnetId>
|
||||||
|
{% endif %}
|
||||||
|
</item>
|
||||||
|
{% endif %}
|
||||||
|
</associationSet>
|
||||||
|
<tagSet/>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</routeTableSet>
|
||||||
|
</DescribeRouteTablesResponse>
|
||||||
|
"""
|
||||||
|
|
||||||
|
DELETE_ROUTE_RESPONSE = """
|
||||||
|
<DeleteRouteResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<return>true</return>
|
||||||
|
</DeleteRouteResponse>
|
||||||
|
"""
|
||||||
|
|
||||||
|
DELETE_ROUTE_TABLE_RESPONSE = """
|
||||||
|
<DeleteRouteTableResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<return>true</return>
|
||||||
|
</DeleteRouteTableResponse>
|
||||||
|
"""
|
||||||
|
@ -80,6 +80,15 @@ def random_ip():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_route_id(route_table_id, cidr_block):
|
||||||
|
return "%s~%s" % (route_table_id, cidr_block)
|
||||||
|
|
||||||
|
|
||||||
|
def split_route_id(route_id):
|
||||||
|
values = string.split(route_id,'~')
|
||||||
|
return values[0], values[1]
|
||||||
|
|
||||||
|
|
||||||
def instance_ids_from_querystring(querystring_dict):
|
def instance_ids_from_querystring(querystring_dict):
|
||||||
instance_ids = []
|
instance_ids = []
|
||||||
for key, value in querystring_dict.items():
|
for key, value in querystring_dict.items():
|
||||||
@ -96,6 +105,14 @@ def image_ids_from_querystring(querystring_dict):
|
|||||||
return image_ids
|
return image_ids
|
||||||
|
|
||||||
|
|
||||||
|
def route_table_ids_from_querystring(querystring_dict):
|
||||||
|
route_table_ids = []
|
||||||
|
for key, value in querystring_dict.iteritems():
|
||||||
|
if 'RouteTableId' in key:
|
||||||
|
route_table_ids.append(value[0])
|
||||||
|
return route_table_ids
|
||||||
|
|
||||||
|
|
||||||
def sequence_from_querystring(parameter, querystring_dict):
|
def sequence_from_querystring(parameter, querystring_dict):
|
||||||
parameter_values = []
|
parameter_values = []
|
||||||
for key, value in querystring_dict.items():
|
for key, value in querystring_dict.items():
|
||||||
@ -160,6 +177,11 @@ def dhcp_configuration_from_querystring(querystring, option=u'DhcpConfiguration'
|
|||||||
return response_values
|
return response_values
|
||||||
|
|
||||||
|
|
||||||
|
def optional_from_querystring(parameter, querystring):
|
||||||
|
parameter_array = querystring.get(parameter)
|
||||||
|
return parameter_array[0] if parameter_array else None
|
||||||
|
|
||||||
|
|
||||||
def filters_from_querystring(querystring_dict):
|
def filters_from_querystring(querystring_dict):
|
||||||
response_values = {}
|
response_values = {}
|
||||||
for key, value in querystring_dict.items():
|
for key, value in querystring_dict.items():
|
||||||
|
@ -1,10 +1,224 @@
|
|||||||
from __future__ import unicode_literals
|
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
|
import boto
|
||||||
|
from boto.exception import EC2ResponseError
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
|
|
||||||
from moto import mock_ec2
|
from moto import mock_ec2
|
||||||
|
from tests.helpers import requires_boto_gte
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_route_tables():
|
def test_route_tables_defaults():
|
||||||
pass
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
|
||||||
|
all_route_tables = conn.get_all_route_tables()
|
||||||
|
all_route_tables.should.have.length_of(1)
|
||||||
|
|
||||||
|
main_route_table = all_route_tables[0]
|
||||||
|
main_route_table.vpc_id.should.equal(vpc.id)
|
||||||
|
|
||||||
|
routes = main_route_table.routes
|
||||||
|
routes.should.have.length_of(1)
|
||||||
|
|
||||||
|
local_route = routes[0]
|
||||||
|
local_route.gateway_id.should.equal('local')
|
||||||
|
local_route.state.should.equal('active')
|
||||||
|
local_route.destination_cidr_block.should.equal(vpc.cidr_block)
|
||||||
|
|
||||||
|
vpc.delete()
|
||||||
|
|
||||||
|
all_route_tables = conn.get_all_route_tables()
|
||||||
|
all_route_tables.should.have.length_of(0)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_route_tables_additional():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
route_table = conn.create_route_table(vpc.id)
|
||||||
|
|
||||||
|
all_route_tables = conn.get_all_route_tables()
|
||||||
|
all_route_tables.should.have.length_of(2)
|
||||||
|
all_route_tables[0].vpc_id.should.equal(vpc.id)
|
||||||
|
all_route_tables[1].vpc_id.should.equal(vpc.id)
|
||||||
|
|
||||||
|
all_route_table_ids = [route_table.id for route_table in all_route_tables]
|
||||||
|
all_route_table_ids.should.contain(route_table.id)
|
||||||
|
|
||||||
|
routes = route_table.routes
|
||||||
|
routes.should.have.length_of(1)
|
||||||
|
|
||||||
|
local_route = routes[0]
|
||||||
|
local_route.gateway_id.should.equal('local')
|
||||||
|
local_route.state.should.equal('active')
|
||||||
|
local_route.destination_cidr_block.should.equal(vpc.cidr_block)
|
||||||
|
|
||||||
|
conn.delete_route_table(route_table.id)
|
||||||
|
|
||||||
|
all_route_tables = conn.get_all_route_tables()
|
||||||
|
all_route_tables.should.have.length_of(1)
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.delete_route_table("rtb-1234abcd")
|
||||||
|
cm.exception.code.should.equal('InvalidRouteTableID.NotFound')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_route_tables_filters():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
|
||||||
|
vpc1 = conn.create_vpc("10.0.0.0/16")
|
||||||
|
route_table1 = conn.create_route_table(vpc1.id)
|
||||||
|
|
||||||
|
vpc2 = conn.create_vpc("10.0.0.0/16")
|
||||||
|
route_table2 = conn.create_route_table(vpc2.id)
|
||||||
|
|
||||||
|
all_route_tables = conn.get_all_route_tables()
|
||||||
|
all_route_tables.should.have.length_of(4)
|
||||||
|
|
||||||
|
# Filter by main route table
|
||||||
|
main_route_tables = conn.get_all_route_tables(filters={'association.main':'true'})
|
||||||
|
main_route_tables.should.have.length_of(2)
|
||||||
|
main_route_table_ids = [route_table.id for route_table in main_route_tables]
|
||||||
|
main_route_table_ids.should_not.contain(route_table1.id)
|
||||||
|
main_route_table_ids.should_not.contain(route_table2.id)
|
||||||
|
|
||||||
|
# Filter by VPC
|
||||||
|
vpc1_route_tables = conn.get_all_route_tables(filters={'vpc-id':vpc1.id})
|
||||||
|
vpc1_route_tables.should.have.length_of(2)
|
||||||
|
vpc1_route_table_ids = [route_table.id for route_table in vpc1_route_tables]
|
||||||
|
vpc1_route_table_ids.should.contain(route_table1.id)
|
||||||
|
vpc1_route_table_ids.should_not.contain(route_table2.id)
|
||||||
|
|
||||||
|
# Filter by VPC and main route table
|
||||||
|
vpc2_main_route_tables = conn.get_all_route_tables(filters={'association.main':'true', 'vpc-id':vpc2.id})
|
||||||
|
vpc2_main_route_tables.should.have.length_of(1)
|
||||||
|
vpc2_main_route_table_ids = [route_table.id for route_table in vpc2_main_route_tables]
|
||||||
|
vpc2_main_route_table_ids.should_not.contain(route_table1.id)
|
||||||
|
vpc2_main_route_table_ids.should_not.contain(route_table2.id)
|
||||||
|
|
||||||
|
# Unsupported filter
|
||||||
|
conn.get_all_route_tables.when.called_with(filters={'not-implemented-filter': 'foobar'}).should.throw(NotImplementedError)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_routes_additional():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
main_route_table = conn.get_all_route_tables()[0]
|
||||||
|
local_route = main_route_table.routes[0]
|
||||||
|
igw = conn.create_internet_gateway()
|
||||||
|
ROUTE_CIDR = "10.0.0.4/24"
|
||||||
|
|
||||||
|
conn.create_route(main_route_table.id, ROUTE_CIDR, gateway_id=igw.id)
|
||||||
|
|
||||||
|
main_route_table = conn.get_all_route_tables()[0] # Refresh route table
|
||||||
|
|
||||||
|
main_route_table.routes.should.have.length_of(2)
|
||||||
|
new_routes = [route for route in main_route_table.routes if route.destination_cidr_block != vpc.cidr_block]
|
||||||
|
new_routes.should.have.length_of(1)
|
||||||
|
|
||||||
|
new_route = new_routes[0]
|
||||||
|
new_route.gateway_id.should.equal(igw.id)
|
||||||
|
new_route.instance_id.should.be.none
|
||||||
|
new_route.state.should.equal('active')
|
||||||
|
new_route.destination_cidr_block.should.equal(ROUTE_CIDR)
|
||||||
|
|
||||||
|
conn.delete_route(main_route_table.id, ROUTE_CIDR)
|
||||||
|
|
||||||
|
main_route_table = conn.get_all_route_tables()[0] # Refresh route table
|
||||||
|
|
||||||
|
main_route_table.routes.should.have.length_of(1)
|
||||||
|
new_routes = [route for route in main_route_table.routes if route.destination_cidr_block != vpc.cidr_block]
|
||||||
|
new_routes.should.have.length_of(0)
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.delete_route(main_route_table.id, ROUTE_CIDR)
|
||||||
|
cm.exception.code.should.equal('InvalidRoute.NotFound')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
conn.create_route.when.called_with(main_route_table.id, ROUTE_CIDR, interface_id='eni-1234abcd').should.throw(NotImplementedError)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_routes_replace():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
main_route_table = conn.get_all_route_tables(filters={'association.main':'true','vpc-id':vpc.id})[0]
|
||||||
|
local_route = main_route_table.routes[0]
|
||||||
|
ROUTE_CIDR = "10.0.0.4/24"
|
||||||
|
|
||||||
|
# Various route targets
|
||||||
|
igw = conn.create_internet_gateway()
|
||||||
|
|
||||||
|
reservation = conn.run_instances('ami-1234abcd')
|
||||||
|
instance = reservation.instances[0]
|
||||||
|
|
||||||
|
# Create initial route
|
||||||
|
conn.create_route(main_route_table.id, ROUTE_CIDR, gateway_id=igw.id)
|
||||||
|
|
||||||
|
# Replace...
|
||||||
|
def get_target_route():
|
||||||
|
route_table = conn.get_all_route_tables(main_route_table.id)[0]
|
||||||
|
routes = [route for route in route_table.routes if route.destination_cidr_block != vpc.cidr_block]
|
||||||
|
routes.should.have.length_of(1)
|
||||||
|
return routes[0]
|
||||||
|
|
||||||
|
conn.replace_route(main_route_table.id, ROUTE_CIDR, instance_id=instance.id)
|
||||||
|
|
||||||
|
target_route = get_target_route()
|
||||||
|
target_route.gateway_id.should.be.none
|
||||||
|
target_route.instance_id.should.equal(instance.id)
|
||||||
|
target_route.state.should.equal('active')
|
||||||
|
target_route.destination_cidr_block.should.equal(ROUTE_CIDR)
|
||||||
|
|
||||||
|
conn.replace_route(main_route_table.id, ROUTE_CIDR, gateway_id=igw.id)
|
||||||
|
|
||||||
|
target_route = get_target_route()
|
||||||
|
target_route.gateway_id.should.equal(igw.id)
|
||||||
|
target_route.instance_id.should.be.none
|
||||||
|
target_route.state.should.equal('active')
|
||||||
|
target_route.destination_cidr_block.should.equal(ROUTE_CIDR)
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.replace_route('rtb-1234abcd', ROUTE_CIDR, gateway_id=igw.id)
|
||||||
|
cm.exception.code.should.equal('InvalidRouteTableID.NotFound')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
conn.replace_route.when.called_with(main_route_table.id, ROUTE_CIDR, interface_id='eni-1234abcd').should.throw(NotImplementedError)
|
||||||
|
|
||||||
|
|
||||||
|
@requires_boto_gte("2.32.0")
|
||||||
|
@mock_ec2
|
||||||
|
def test_routes_vpc_peering_connection():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
main_route_table = conn.get_all_route_tables(filters={'association.main':'true','vpc-id':vpc.id})[0]
|
||||||
|
local_route = main_route_table.routes[0]
|
||||||
|
ROUTE_CIDR = "10.0.0.4/24"
|
||||||
|
|
||||||
|
peer_vpc = conn.create_vpc("11.0.0.0/16")
|
||||||
|
vpc_pcx = conn.create_vpc_peering_connection(vpc.id, peer_vpc.id)
|
||||||
|
|
||||||
|
conn.create_route(main_route_table.id, ROUTE_CIDR, vpc_peering_connection_id=vpc_pcx.id)
|
||||||
|
|
||||||
|
# Refresh route table
|
||||||
|
main_route_table = conn.get_all_route_tables(main_route_table.id)[0]
|
||||||
|
new_routes = [route for route in main_route_table.routes if route.destination_cidr_block != vpc.cidr_block]
|
||||||
|
new_routes.should.have.length_of(1)
|
||||||
|
|
||||||
|
new_route = new_routes[0]
|
||||||
|
new_route.gateway_id.should.be.none
|
||||||
|
new_route.instance_id.should.be.none
|
||||||
|
new_route.state.should.equal('blackhole')
|
||||||
|
new_route.destination_cidr_block.should.equal(ROUTE_CIDR)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user