#!/usr/bin/env python # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. DOCUMENTATION = """ --- module: ec2_rt short_description: Create or delete AWS Route Table description: - Can create or delete AwS Subnets version_added: "1.8" author: Edward Zarecor options: state: description: - create, update or delete the subnet required: true choices: ['present', 'absent'] name: description: - Unique name for subnet required: true destination_cidr: description: - The cidr block of the subnet aliases: ['cidr'] vpc_id: description: - The VPC that this acl belongs to required: true default: null extends_documentation_fragment: aws """ EXAMPLES = ''' ''' from ansible.module_utils.basic import * from ansible.module_utils.ec2 import * import sys try: import boto.vpc except ImportError: print "failed=True msg={0}".format(sys.executable) sys.exit(1) class DuplicateRouteTableError(Exception): pass class InconsistentRouteError(Exception): pass class RTManager(): def __init__(self, connection, vpc_id, route_name, routes, tags): self.connection = connection self.vpc_id = vpc_id self.name = route_name self.routes = routes self.tags = tags self.rt = None def get_rt(self): rt_filter = { "vpc_id": self.vpc_id, "tag:Name": self.name, } results = self.connection.get_all_route_tables(filters=rt_filter) if len(results) == 1: self.rt = results[0] elif len(results) > 1: msg = "Found multiple route tables with name '{}' in vpc with id '{}'." raise DuplicateRouteTableError(msg.format(self.name, self.vpc_id)) else: pass # Doesn't exist yet return self.rt def do_tags(self): tags = { "Name" : self.name } if self.tags: for tag in self.tags: tags[tag['key']] = tag['value'] self.rt.add_tags(tags) def create_rt(self): self.rt = self.connection.create_route_table(self.vpc_id) changed = True self.do_tags() return changed def routes_match(self, new_route, existing_route): # Not the same route if new_route['cidr'] != existing_route.destination_cidr_block: return False instance_matches = existing_route.instance_id \ and existing_route.instance_id == new_route['instance'] gateway_matches = existing_route.gateway_id \ and existing_route.gateway_id == new_route['gateway'] return instance_matches or gateway_matches def update_routes(self): changed = False existing_routes = { x.destination_cidr_block : x for x in self.rt.routes } for route in self.routes: # Build the args used to call the boto API call_args = { "route_table_id": self.rt.id, "destination_cidr_block": route['cidr'], } if "gateway" in route and "instance" in route: msg = "Both gateway and instance specified for route" + \ "with CIDR {}" raise InconsistentRouteError(msg.format(route['cidr'])) elif "gateway" in route: call_args['gateway_id'] = route['gateway'] elif "instance" in route: call_args['instance_id'] = route['instance'] else: msg = "No gateway or instance provided for route with" + \ "CIDR {}" raise InconsistentRouteError(msg.format(route['cidr'])) if route['cidr'] in existing_routes: # Update the route existing_route = existing_routes[route['cidr']] if self.routes_match(route, existing_route): continue self.connection.replace_route(**call_args) changed = True else: # Create a new route self.connection.create_route(**call_args) changed = True return changed def present(self): changed = False existing = self.get_rt() if existing: changed = self.update_routes() else: changed = self.create_rt() self.update_routes() results = dict(changed=changed, id=self.rt.id, name=self.name, routes=self.routes, ) return results def absent(self): rt = self.get_rt() changed = False if rt: changed = self.connection.delet_route_table(rt.id) results = dict(changed=changed, id=self.rt.id, name=self.name, ) return results def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict( name=dict(required=True, type='str'), state=dict(default='present', choices=['present', 'absent']), vpc_id=dict(required=True, type='str'), routes=dict(required=True, type='list', aliases=['dest_routes']), tags=dict(type='list'), ) ) module = AnsibleModule(argument_spec=argument_spec) ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module) profile = module.params.get('profile') vpc_id = module.params.get('vpc_id') route_name = module.params.get('name') routes = module.params.get('routes') tags = module.params.get('tags') if region: try: connection = boto.vpc.connect_to_region(region,profile_name=profile) except boto.exception.NoAuthHandlerFound, e: module.fail_json(msg = str(e)) else: module.fail_json(msg="region must be specified") manager = RTManager(connection, vpc_id, route_name, routes, tags) state = module.params.get('state') results = dict() if state == 'present': results = manager.present() elif state == 'absent': results = manager.absent() module.exit_json(**results) main()