|
|
|
|
@ -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"
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
@ -166,11 +217,21 @@ try:
|
|
|
|
|
import boto.ec2
|
|
|
|
|
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."""
|
|
|
|
|
@ -200,6 +261,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)
|
|
|
|
|
@ -217,6 +283,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)
|
|
|
|
|
|
|
|
|
|
@ -249,32 +320,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
|
|
|
|
|
@ -282,6 +355,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
|
|
|
|
|
@ -291,8 +369,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)
|
|
|
|
|
@ -300,26 +379,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)
|
|
|
|
|
|