Added option to specify tags or route-table-id, quoted doc strings, added more detail to returned route table object, numerous minor fixes

pull/18777/head
whiter 9 years ago committed by Matt Clay
parent 6cde07994e
commit 3961f30f66

@ -20,50 +20,47 @@ short_description: Manage route tables for AWS virtual private clouds
description:
- Manage route tables for AWS virtual private clouds
version_added: "2.0"
author: Robert Estelle (@erydo)
author: Robert Estelle (@erydo), Rob White (@wimnat)
options:
vpc_id:
lookup:
description:
- VPC ID of the VPC in which to create the route table.
required: true
route_table_id:
- "Look up route table by either tags or by route table ID. Non-unique tag lookup will fail. If no tags are specifed then no lookup for an existing route table is performed and a new route table will be created. To change tags of a route table, you must look up by id."
required: false
default: tag
choices: [ 'tag', 'id' ]
propagating_vgw_ids:
description:
- The ID of the route table to update or delete.
- "Enable route propagation from virtual gateways specified by ID."
required: false
default: null
resource_tags:
route_table_id:
description:
- A dictionary array of resource tags of the form: { tag1: value1, tag2: value2 }. Tags in this list are used to uniquely identify route tables within a VPC when the route_table_id is not supplied.
- "The ID of the route table to update or delete."
required: false
default: null
routes:
description:
- List of routes in the route table. Routes are specified as dicts containing the keys 'dest' and one of 'gateway_id', 'instance_id', 'interface_id', or 'vpc_peering_connection'. If 'gateway_id' is specified, you can refer to the VPC's IGW by using the value 'igw'.
- "List of routes in the route table. Routes are specified as dicts containing the keys 'dest' and one of 'gateway_id', 'instance_id', 'interface_id', or 'vpc_peering_connection'. If 'gateway_id' is specified, you can refer to the VPC's IGW by using the value 'igw'."
required: true
state:
description:
- "Create or destroy the VPC route table"
required: false
default: present
choices: [ 'present', 'absent' ]
subnets:
description:
- An array of subnets to add to this route table. Subnets may be specified by either subnet ID, Name tag, or by a CIDR such as '10.0.0.0/24'.
- "An array of subnets to add to this route table. Subnets may be specified by either subnet ID, Name tag, or by a CIDR such as '10.0.0.0/24'."
required: true
propagating_vgw_ids:
tags:
description:
- Enable route propagation from virtual gateways specified by ID.
- "A dictionary array of resource tags of the form: { tag1: value1, tag2: value2 }. Tags in this list are used to uniquely identify route tables within a VPC when the route_table_id is not supplied."
required: false
wait:
description:
- Wait for the VPC to be in state 'available' before returning.
required: false
default: "no"
choices: [ "yes", "no" ]
wait_timeout:
description:
- How long before wait gives up, in seconds.
default: 300
state:
default: null
aliases: [ "resource_tags" ]
vpc_id:
description:
- Create or destroy the VPC route table
required: false
default: present
choices: [ 'present', 'absent' ]
- "VPC ID of the VPC in which to create the route table."
required: true
extends_documentation_fragment: aws
'''
@ -73,36 +70,35 @@ EXAMPLES = '''
# Basic creation example:
- name: Set up public subnet route table
local_action:
module: ec2_vpc_route_table
ec2_vpc_route_table:
vpc_id: vpc-1245678
region: us-west-1
resource_tags:
tags:
Name: Public
subnets:
- '{{jumpbox_subnet.subnet_id}}'
- '{{frontend_subnet.subnet_id}}'
- '{{vpn_subnet.subnet_id}}'
- "{{ jumpbox_subnet.subnet_id }}"
- "{{ frontend_subnet.subnet_id }}"
- "{{ vpn_subnet.subnet_id }}"
routes:
- dest: 0.0.0.0/0
gateway_id: '{{igw.gateway_id}}'
gateway_id: "{{ igw.gateway_id }}"
register: public_route_table
- name: Set up NAT-protected route table
local_action:
module: ec2_vpc_route_table
ec2_vpc_route_table:
vpc_id: vpc-1245678
region: us-west-1
resource_tags:
tags:
- Name: Internal
subnets:
- '{{application_subnet.subnet_id}}'
- "{{ application_subnet.subnet_id }}"
- 'Database Subnet'
- '10.0.0.0/8'
routes:
- dest: 0.0.0.0/0
instance_id: '{{nat.instance_id}}'
instance_id: "{{ nat.instance_id }}"
register: nat_route_table
'''
@ -210,12 +206,12 @@ def find_igw(vpc_conn, vpc_id):
filters={'attachment.vpc-id': vpc_id})
if not igw:
return AnsibleIgwSearchException('No IGW found for VPC "{0}"'.
raise AnsibleIgwSearchException('No IGW found for VPC {0}'.
format(vpc_id))
elif len(igw) == 1:
return igw[0].id
else:
raise AnsibleIgwSearchException('Multiple IGWs found for VPC "{0}"'.
raise AnsibleIgwSearchException('Multiple IGWs found for VPC {0}'.
format(vpc_id))
@ -251,17 +247,29 @@ def ensure_tags(vpc_conn, resource_id, tags, add_only, check_mode):
def get_route_table_by_id(vpc_conn, vpc_id, route_table_id):
route_tables = vpc_conn.get_all_route_tables(
route_table_ids=[route_table_id], filters={'vpc_id': vpc_id})
return route_tables[0] if route_tables else None
route_table = None
route_tables = vpc_conn.get_all_route_tables(route_table_ids=[route_table_id], filters={'vpc_id': vpc_id})
if route_tables:
route_table = route_tables[0]
return route_table
def get_route_table_by_tags(vpc_conn, vpc_id, tags):
count = 0
route_table = None
route_tables = vpc_conn.get_all_route_tables(filters={'vpc_id': vpc_id})
for route_table in route_tables:
this_tags = get_resource_tags(vpc_conn, route_table.id)
for table in route_tables:
this_tags = get_resource_tags(vpc_conn, table.id)
if tags_match(tags, this_tags):
return route_table
route_table = table
count +=1
if count > 1:
raise RuntimeError("Tags provided do not identify a unique route table")
else:
return route_table
def route_spec_matches_route(route_spec, route):
@ -391,75 +399,132 @@ def ensure_propagation(vpc_conn, route_table, propagating_vgw_ids,
return {'changed': changed}
def ensure_route_table_absent(vpc_conn, vpc_id, route_table_id, resource_tags,
check_mode):
if route_table_id:
route_table = get_route_table_by_id(vpc_conn, vpc_id, route_table_id)
elif resource_tags:
route_table = get_route_table_by_tags(vpc_conn, vpc_id, resource_tags)
else:
raise AnsibleRouteTableException(
'must provide route_table_id or resource_tags')
def ensure_route_table_absent(connection, module):
lookup = module.params.get('lookup')
route_table_id = module.params.get('route_table_id')
tags = module.params.get('tags')
vpc_id = module.params.get('vpc_id')
check_mode = module.params.get('check_mode')
if lookup == 'tag':
if tags is not None:
try:
route_table = get_route_table_by_tags(connection, vpc_id, tags)
except EC2ResponseError as e:
module.fail_json(msg=e.message)
except RuntimeError as e:
module.fail_json(msg=e.args[0])
else:
route_table = None
elif lookup == 'id':
try:
route_table = get_route_table_by_id(connection, vpc_id, route_table_id)
except EC2ResponseError as e:
module.fail_json(msg=e.message)
if route_table is None:
return {'changed': False}
vpc_conn.delete_route_table(route_table.id, dry_run=check_mode)
try:
connection.delete_route_table(route_table.id, dry_run=check_mode)
except EC2ResponseError as e:
module.fail_json(msg=e.message)
return {'changed': True}
def ensure_route_table_present(vpc_conn, vpc_id, route_table_id, resource_tags,
routes, subnets, propagating_vgw_ids,
check_mode):
def get_route_table_info(route_table):
# Add any routes to array
routes = []
for route in route_table.routes:
routes.append(route.__dict__)
route_table_info = { 'id': route_table.id,
'routes': routes,
'tags': route_table.tags,
'vpc_id': route_table.vpc_id
}
return route_table_info
def create_route_spec(connection, routes, vpc_id):
for route_spec in routes:
rename_key(route_spec, 'dest', 'destination_cidr_block')
if 'gateway_id' in route_spec and route_spec['gateway_id'] and \
route_spec['gateway_id'].lower() == 'igw':
igw = find_igw(connection, vpc_id)
route_spec['gateway_id'] = igw
return routes
def ensure_route_table_present(connection, module):
lookup = module.params.get('lookup')
propagating_vgw_ids = module.params.get('propagating_vgw_ids', [])
route_table_id = module.params.get('route_table_id')
subnets = module.params.get('subnets')
tags = module.params.get('tags')
vpc_id = module.params.get('vpc_id')
check_mode = module.params.get('check_mode')
try:
routes = create_route_spec(connection, module.params.get('routes'), vpc_id)
except AnsibleIgwSearchException as e:
module.fail_json(msg=e[0])
changed = False
tags_valid = False
if route_table_id:
route_table = get_route_table_by_id(vpc_conn, vpc_id, route_table_id)
elif resource_tags:
route_table = get_route_table_by_tags(vpc_conn, vpc_id, resource_tags)
tags_valid = route_table is not None
else:
raise AnsibleRouteTableException(
'must provide route_table_id or resource_tags')
if check_mode and route_table is None:
return {'changed': True}
if lookup == 'tag':
if tags is not None:
try:
route_table = get_route_table_by_tags(connection, vpc_id, tags)
except EC2ResponseError as e:
module.fail_json(msg=e.message)
except RuntimeError as e:
module.fail_json(msg=e.args[0])
else:
route_table = None
elif lookup == 'id':
try:
route_table = get_route_table_by_id(connection, vpc_id, route_table_id)
except EC2ResponseError as e:
module.fail_json(msg=e.message)
# If no route table returned then create new route table
if route_table is None:
print route_table.keys()
try:
route_table = connection.create_route_table(vpc_id, check_mode)
changed = True
except EC2ResponseError, e:
module.fail_json(msg=e.message)
if routes is not None:
try:
route_table = vpc_conn.create_route_table(vpc_id)
result = ensure_routes(connection, route_table, routes, propagating_vgw_ids, check_mode)
changed = changed or result['changed']
except EC2ResponseError as e:
raise AnsibleRouteTableException(
'Unable to create route table {0}, error: {1}'
.format(route_table_id or resource_tags, e)
)
module.fail_json(msg=e.message)
if propagating_vgw_ids is not None:
result = ensure_propagation(vpc_conn, route_table,
result = ensure_propagation(vpc_conn, route_table_id,
propagating_vgw_ids,
check_mode=check_mode)
changed = changed or result['changed']
if not tags_valid and resource_tags is not None:
result = ensure_tags(vpc_conn, route_table.id, resource_tags,
if not tags_valid and tags is not None:
result = ensure_tags(connection, route_table.id, tags,
add_only=True, check_mode=check_mode)
changed = changed or result['changed']
if routes is not None:
try:
result = ensure_routes(vpc_conn, route_table, routes,
propagating_vgw_ids, check_mode)
changed = changed or result['changed']
except EC2ResponseError as e:
raise AnsibleRouteTableException(
'Unable to ensure routes for route table {0}, error: {1}'
.format(route_table, e)
)
if subnets:
associated_subnets = []
try:
associated_subnets = find_subnets(vpc_conn, vpc_id, subnets)
associated_subnets = find_subnets(connection, vpc_id, subnets)
except EC2ResponseError as e:
raise AnsibleRouteTableException(
'Unable to find subnets for route table {0}, error: {1}'
@ -467,8 +532,7 @@ def ensure_route_table_present(vpc_conn, vpc_id, route_table_id, resource_tags,
)
try:
result = ensure_subnet_associations(
vpc_conn, vpc_id, route_table, associated_subnets, check_mode)
result = ensure_subnet_associations(connection, vpc_id, route_table, associated_subnets, check_mode)
changed = changed or result['changed']
except EC2ResponseError as e:
raise AnsibleRouteTableException(
@ -476,23 +540,21 @@ def ensure_route_table_present(vpc_conn, vpc_id, route_table_id, resource_tags,
.format(route_table, e)
)
return {
'changed': changed,
'route_table_id': route_table.id,
}
module.exit_json(changed=changed, route_table=get_route_table_info(route_table))
def main():
argument_spec = ec2_argument_spec()
argument_spec.update(
dict(
vpc_id = dict(default=None, required=True),
route_table_id = dict(default=None, required=False),
lookup = dict(default='tag', required=False, choices=['tag', 'id']),
propagating_vgw_ids = dict(default=None, required=False, type='list'),
resource_tags = dict(default=None, required=False, type='dict'),
route_table_id = dict(default=None, required=False),
routes = dict(default=None, required=False, type='list'),
state = dict(default='present', choices=['present', 'absent']),
subnets = dict(default=None, required=False, type='list'),
state = dict(default='present', choices=['present', 'absent'])
tags = dict(default=None, required=False, type='dict', aliases=['resource_tags']),
vpc_id = dict(default=None, required=True)
)
)
@ -511,34 +573,18 @@ def main():
else:
module.fail_json(msg="region must be specified")
vpc_id = module.params.get('vpc_id')
lookup = module.params.get('lookup')
route_table_id = module.params.get('route_table_id')
resource_tags = module.params.get('resource_tags')
propagating_vgw_ids = module.params.get('propagating_vgw_ids', [])
routes = module.params.get('routes')
for route_spec in routes:
rename_key(route_spec, 'dest', 'destination_cidr_block')
if 'gateway_id' in route_spec and route_spec['gateway_id'] and \
route_spec['gateway_id'].lower() == 'igw':
igw = find_igw(connection, vpc_id)
route_spec['gateway_id'] = igw
subnets = module.params.get('subnets')
state = module.params.get('state', 'present')
if lookup == 'id' and route_table_id is None:
module.fail_json("You must specify route_table_id if lookup is set to id")
try:
if state == 'present':
result = ensure_route_table_present(
connection, vpc_id, route_table_id, resource_tags,
routes, subnets, propagating_vgw_ids, module.check_mode
)
result = ensure_route_table_present(connection, module)
elif state == 'absent':
result = ensure_route_table_absent(
connection, vpc_id, route_table_id, resource_tags,
module.check_mode
)
result = ensure_route_table_absent(connection, module)
except AnsibleRouteTableException as e:
module.fail_json(msg=str(e))
@ -549,4 +595,4 @@ from ansible.module_utils.ec2 import * # noqa
if __name__ == '__main__':
main()

Loading…
Cancel
Save