ec2_rt 6.72 KB
Newer Older
e0d committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
#!/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
35
  destination_cidr:
e0d committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
    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)


60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
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:
85 86
            msg = "Found multiple route tables with name '{}' in vpc with id '{}'."
            raise DuplicateRouteTableError(msg.format(self.name, self.vpc_id))
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
        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
e0d committed
190 191 192 193 194 195 196 197

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'),
198
            routes=dict(required=True, type='list', aliases=['dest_routes']),
e0d committed
199 200 201 202 203 204 205
            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')
206 207 208 209 210
    vpc_id = module.params.get('vpc_id')
    route_name = module.params.get('name')
    routes = module.params.get('routes')
    tags = module.params.get('tags')

e0d committed
211 212 213 214 215 216 217 218
    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")

219
    manager = RTManager(connection, vpc_id, route_name, routes, tags)
e0d committed
220 221 222

    state = module.params.get('state')

223
    results = dict()
e0d committed
224
    if state == 'present':
225
        results = manager.present()
e0d committed
226
    elif state == 'absent':
227 228 229
        results = manager.absent()

    module.exit_json(**results)
e0d committed
230 231

main()