Repository: ansible/ansible-modules-core

Issue:
add support for routing policy to route53 module
##### Issue Type:

Feature Idea
##### Ansible Version:

ansible 1.7.2
##### Environment:

Ubuntu 14.04
##### Summary:

route53 module does not let you set routing policy. http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy.html

(Filing on behalf of user).



Discussion:
+1

@jimi-c would you accept PR?

we promise to review it, if you submit it

This would be awesome.  I'd like to update a weighted routing policy with the hosts of number of load balancers.  The number may change over time, so not having to hit the AWS console to update this config is pretty important.

+1

:+1: 

+1

Just a heads up that I'm working on this. I'll be submitting a PR when I have something working. In the mean time, feel free to ping me if you want to help out.


Resolving patch:
diff --git a/cloud/amazon/route53.py b/cloud/amazon/route53.py
--- a/cloud/amazon/route53.py
+++ b/cloud/amazon/route53.py
@@ -93,6 +93,45 @@
     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 @@
       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 @@
     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)


Test patch: