route53: add support for routing policies
It is now possible to pass various routing policies if an identity is provided. This commit also introduces multiple optimisations: * Only fetch records for the given domain * Use UPSERT instead of DELETE+CREATE to update existing records
This commit is contained in:
parent
86e679fe3c
commit
99e2557b42
1 changed files with 101 additions and 32 deletions
|
@ -93,6 +93,45 @@ options:
|
|||
required: false
|
||||
default: false
|
||||
version_added: "1.9"
|
||||
identifier:
|
||||
description:
|
||||
- Weighted and latency-based resource record sets only. An identifier
|
||||
that differentiates among multiple resource record sets that have the
|
||||
same combination of DNS name and type.
|
||||
required: false
|
||||
default: null
|
||||
version_added: "2.0"
|
||||
weight:
|
||||
description:
|
||||
- Weighted resource record sets only. Among resource record sets that
|
||||
have the same combination of DNS name and type, a value that
|
||||
determines what portion of traffic for the current resource record set
|
||||
is routed to the associated location.
|
||||
required: false
|
||||
default: null
|
||||
version_added: "2.0"
|
||||
region:
|
||||
description:
|
||||
- Latency-based resource record sets only Among resource record sets
|
||||
that have the same combination of DNS name and type, a value that
|
||||
determines which region this should be associated with for the
|
||||
latency-based routing
|
||||
required: false
|
||||
default: null
|
||||
version_added: "2.0"
|
||||
health_check:
|
||||
description:
|
||||
- Health check to associate with this record
|
||||
required: false
|
||||
default: null
|
||||
version_added: "2.0"
|
||||
failover:
|
||||
description:
|
||||
- Failover resource record sets only. Whether this is the primary or
|
||||
secondary resource record set.
|
||||
required: false
|
||||
default: null
|
||||
version_added: "2.0"
|
||||
author: "Bruce Pennypacker (@bpennypacker)"
|
||||
extends_documentation_fragment: aws
|
||||
'''
|
||||
|
@ -156,6 +195,18 @@ EXAMPLES = '''
|
|||
alias=True
|
||||
alias_hosted_zone_id="{{ elb_zone_id }}"
|
||||
|
||||
# Use a routing policy to distribute traffic:
|
||||
- route53:
|
||||
command: "create"
|
||||
zone: "foo.com"
|
||||
record: "www.foo.com"
|
||||
type: "CNAME"
|
||||
value: "host1.foo.com"
|
||||
ttl: 30
|
||||
# Routing policy
|
||||
identifier: "host1@www"
|
||||
weight: 100
|
||||
health_check: "d994b780-3150-49fd-9205-356abdd42e75"
|
||||
|
||||
'''
|
||||
|
||||
|
@ -165,11 +216,21 @@ try:
|
|||
import boto
|
||||
from boto import route53
|
||||
from boto.route53 import Route53Connection
|
||||
from boto.route53.record import ResourceRecordSets
|
||||
from boto.route53.record import Record, ResourceRecordSets
|
||||
HAS_BOTO = True
|
||||
except ImportError:
|
||||
HAS_BOTO = False
|
||||
|
||||
def get_zone_by_name(conn, module, zone_name, want_private):
|
||||
"""Finds a zone by name"""
|
||||
for zone in conn.get_zones():
|
||||
# only save this zone id if the private status of the zone matches
|
||||
# the private_zone_in boolean specified in the params
|
||||
private_zone = module.boolean(zone.config.get('PrivateZone', False))
|
||||
if private_zone == want_private and zone.name == zone_name:
|
||||
return zone
|
||||
return None
|
||||
|
||||
|
||||
def commit(changes, retry_interval):
|
||||
"""Commit changes, but retry PriorRequestNotComplete errors."""
|
||||
|
@ -199,6 +260,11 @@ def main():
|
|||
overwrite = dict(required=False, type='bool'),
|
||||
retry_interval = dict(required=False, default=500)
|
||||
private_zone = dict(required=False, type='bool', default=False),
|
||||
identifier = dict(required=False),
|
||||
weight = dict(required=False, type='int'),
|
||||
region = dict(required=False),
|
||||
health_check = dict(required=False),
|
||||
failover = dict(required=False),
|
||||
)
|
||||
)
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
|
@ -215,6 +281,11 @@ def main():
|
|||
alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id')
|
||||
retry_interval_in = module.params.get('retry_interval')
|
||||
private_zone_in = module.params.get('private_zone')
|
||||
identifier_in = module.params.get('identifier')
|
||||
weight_in = module.params.get('weight')
|
||||
region_in = module.params.get('region')
|
||||
health_check_in = module.params.get('health_check')
|
||||
failover_in = module.params.get('failover')
|
||||
|
||||
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)
|
||||
|
||||
|
@ -247,32 +318,34 @@ def main():
|
|||
except boto.exception.BotoServerError, e:
|
||||
module.fail_json(msg = e.error_message)
|
||||
|
||||
# Get all the existing hosted zones and save their ID's
|
||||
zones = {}
|
||||
results = conn.get_all_hosted_zones()
|
||||
for r53zone in results['ListHostedZonesResponse']['HostedZones']:
|
||||
# only save this zone id if the private status of the zone matches
|
||||
# the private_zone_in boolean specified in the params
|
||||
if module.boolean(r53zone['Config'].get('PrivateZone', False)) == private_zone_in:
|
||||
zone_id = r53zone['Id'].replace('/hostedzone/', '')
|
||||
zones[r53zone['Name']] = zone_id
|
||||
# Find the named zone ID
|
||||
zone = get_zone_by_name(conn, module, zone_in, private_zone_in)
|
||||
|
||||
# Verify that the requested zone is already defined in Route53
|
||||
if not zone_in in zones:
|
||||
if zone is None:
|
||||
errmsg = "Zone %s does not exist in Route53" % zone_in
|
||||
module.fail_json(msg = errmsg)
|
||||
|
||||
record = {}
|
||||
|
||||
found_record = False
|
||||
sets = conn.get_all_rrsets(zones[zone_in])
|
||||
wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in,
|
||||
identifier=identifier_in, weight=weight_in, region=region_in,
|
||||
health_check=health_check_in, failover=failover_in)
|
||||
for v in value_list:
|
||||
if alias_in:
|
||||
wanted_rset.set_alias(alias_hosted_zone_id_in, v)
|
||||
else:
|
||||
wanted_rset.add_value(v)
|
||||
|
||||
sets = conn.get_all_rrsets(zone.id, name=record_in, type=type_in, identifier=identifier_in)
|
||||
for rset in sets:
|
||||
# Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round
|
||||
# tripping of things like * and @.
|
||||
decoded_name = rset.name.replace(r'\052', '*')
|
||||
decoded_name = decoded_name.replace(r'\100', '@')
|
||||
|
||||
if rset.type == type_in and decoded_name.lower() == record_in.lower():
|
||||
if rset.type == type_in and decoded_name.lower() == record_in.lower() and rset.identifier == identifier_in:
|
||||
found_record = True
|
||||
record['zone'] = zone_in
|
||||
record['type'] = rset.type
|
||||
|
@ -280,6 +353,11 @@ def main():
|
|||
record['ttl'] = rset.ttl
|
||||
record['value'] = ','.join(sorted(rset.resource_records))
|
||||
record['values'] = sorted(rset.resource_records)
|
||||
record['identifier'] = rset.identifier
|
||||
record['weight'] = rset.weight
|
||||
record['region'] = rset.region
|
||||
record['failover'] = rset.failover
|
||||
record['health_check'] = rset.health_check
|
||||
if rset.alias_dns_name:
|
||||
record['alias'] = True
|
||||
record['value'] = rset.alias_dns_name
|
||||
|
@ -289,8 +367,9 @@ def main():
|
|||
record['alias'] = False
|
||||
record['value'] = ','.join(sorted(rset.resource_records))
|
||||
record['values'] = sorted(rset.resource_records)
|
||||
if value_list == sorted(rset.resource_records) and int(record['ttl']) == ttl_in and command_in == 'create':
|
||||
if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml():
|
||||
module.exit_json(changed=False)
|
||||
break
|
||||
|
||||
if command_in == 'get':
|
||||
module.exit_json(changed=False, set=record)
|
||||
|
@ -298,26 +377,16 @@ def main():
|
|||
if command_in == 'delete' and not found_record:
|
||||
module.exit_json(changed=False)
|
||||
|
||||
changes = ResourceRecordSets(conn, zones[zone_in])
|
||||
|
||||
if command_in == 'create' and found_record:
|
||||
if not module.params['overwrite']:
|
||||
module.fail_json(msg = "Record already exists with different value. Set 'overwrite' to replace it")
|
||||
else:
|
||||
change = changes.add_change("DELETE", record_in, type_in, record['ttl'])
|
||||
for v in record['values']:
|
||||
if record['alias']:
|
||||
change.set_alias(record['alias_hosted_zone_id'], v)
|
||||
else:
|
||||
change.add_value(v)
|
||||
changes = ResourceRecordSets(conn, zone.id)
|
||||
|
||||
if command_in == 'create' or command_in == 'delete':
|
||||
change = changes.add_change(command_in.upper(), record_in, type_in, ttl_in)
|
||||
for v in value_list:
|
||||
if module.params['alias']:
|
||||
change.set_alias(alias_hosted_zone_id_in, v)
|
||||
else:
|
||||
change.add_value(v)
|
||||
if command_in == 'create' and found_record:
|
||||
if not module.params['overwrite']:
|
||||
module.fail_json(msg = "Record already exists with different value. Set 'overwrite' to replace it")
|
||||
command = 'UPSERT'
|
||||
else:
|
||||
command = command_in.upper()
|
||||
changes.add_change_record(command, wanted_rset)
|
||||
|
||||
try:
|
||||
result = commit(changes, retry_interval_in)
|
||||
|
|
Loading…
Reference in a new issue