2014-09-26 01:01:01 +00:00
|
|
|
#!/usr/bin/python
|
Introduce new 'required_by' argument_spec option (#28662)
* Introduce new "required_by' argument_spec option
This PR introduces a new **required_by** argument_spec option which allows you to say *"if parameter A is set, parameter B and C are required as well"*.
- The difference with **required_if** is that it can only add dependencies if a parameter is set to a specific value, not when it is just defined.
- The difference with **required_together** is that it has a commutative property, so: *"Parameter A and B are required together, if one of them has been defined"*.
As an example, we need this for the complex options that the xml module provides. One of the issues we often see is that users are not using the correct combination of options, and then are surprised that the module does not perform the requested action(s).
This would be solved by adding the correct dependencies, and mutual exclusives. For us this is important to get this shipped together with the new xml module in Ansible v2.4. (This is related to bugfix https://github.com/ansible/ansible/pull/28657)
```python
module = AnsibleModule(
argument_spec=dict(
path=dict(type='path', aliases=['dest', 'file']),
xmlstring=dict(type='str'),
xpath=dict(type='str'),
namespaces=dict(type='dict', default={}),
state=dict(type='str', default='present', choices=['absent',
'present'], aliases=['ensure']),
value=dict(type='raw'),
attribute=dict(type='raw'),
add_children=dict(type='list'),
set_children=dict(type='list'),
count=dict(type='bool', default=False),
print_match=dict(type='bool', default=False),
pretty_print=dict(type='bool', default=False),
content=dict(type='str', choices=['attribute', 'text']),
input_type=dict(type='str', default='yaml', choices=['xml',
'yaml']),
backup=dict(type='bool', default=False),
),
supports_check_mode=True,
required_by=dict(
add_children=['xpath'],
attribute=['value', 'xpath'],
content=['xpath'],
set_children=['xpath'],
value=['xpath'],
),
required_if=[
['count', True, ['xpath']],
['print_match', True, ['xpath']],
],
required_one_of=[
['path', 'xmlstring'],
['add_children', 'content', 'count', 'pretty_print', 'print_match', 'set_children', 'value'],
],
mutually_exclusive=[
['add_children', 'content', 'count', 'print_match','set_children', 'value'],
['path', 'xmlstring'],
],
)
```
* Rebase and fix conflict
* Add modules that use required_by functionality
* Update required_by schema
* Fix rebase issue
2019-02-15 00:57:45 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2018-11-13 13:08:28 +00:00
|
|
|
|
|
|
|
# Copyright: (c) 2018, Ansible Project
|
2017-08-12 16:27:11 +00:00
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2017-08-16 03:16:38 +00:00
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
2017-03-14 16:07:22 +00:00
|
|
|
'status': ['stableinterface'],
|
|
|
|
'supported_by': 'community'}
|
|
|
|
|
2016-12-06 10:35:05 +00:00
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
DOCUMENTATION = '''
|
|
|
|
---
|
|
|
|
module: route53
|
|
|
|
version_added: "1.3"
|
|
|
|
short_description: add or delete entries in Amazons Route53 DNS service
|
|
|
|
description:
|
|
|
|
- Creates and deletes DNS records in Amazons Route53 service
|
|
|
|
options:
|
2017-04-18 15:49:25 +00:00
|
|
|
state:
|
2014-09-26 01:01:01 +00:00
|
|
|
description:
|
2017-05-05 18:58:34 +00:00
|
|
|
- Specifies the state of the resource record. As of Ansible 2.4, the I(command) option has been changed
|
2017-06-19 19:16:58 +00:00
|
|
|
to I(state) as default and the choices 'present' and 'absent' have been added, but I(command) still works as well.
|
2014-09-26 01:01:01 +00:00
|
|
|
required: true
|
2017-04-18 15:49:25 +00:00
|
|
|
aliases: [ 'command' ]
|
|
|
|
choices: [ 'present', 'absent', 'get', 'create', 'delete' ]
|
2014-09-26 01:01:01 +00:00
|
|
|
zone:
|
|
|
|
description:
|
|
|
|
- The DNS zone to modify
|
|
|
|
required: true
|
2015-04-15 10:43:00 +00:00
|
|
|
hosted_zone_id:
|
|
|
|
description:
|
|
|
|
- The Hosted Zone ID of the DNS zone to modify
|
2016-12-08 02:33:38 +00:00
|
|
|
version_added: "2.0"
|
2014-09-26 01:01:01 +00:00
|
|
|
record:
|
|
|
|
description:
|
|
|
|
- The full DNS record to create or delete
|
|
|
|
required: true
|
|
|
|
ttl:
|
|
|
|
description:
|
|
|
|
- The TTL to give the new record
|
|
|
|
default: 3600 (one hour)
|
|
|
|
type:
|
|
|
|
description:
|
|
|
|
- The type of DNS record to create
|
|
|
|
required: true
|
2017-10-18 02:48:04 +00:00
|
|
|
choices: [ 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'CAA', 'NS', 'SOA' ]
|
2015-02-06 23:19:11 +00:00
|
|
|
alias:
|
|
|
|
description:
|
|
|
|
- Indicates if this is an alias record.
|
2016-12-08 02:33:38 +00:00
|
|
|
version_added: "1.9"
|
2018-03-15 21:15:24 +00:00
|
|
|
type: bool
|
|
|
|
default: 'no'
|
2015-02-06 23:19:11 +00:00
|
|
|
alias_hosted_zone_id:
|
|
|
|
description:
|
|
|
|
- The hosted zone identifier.
|
2016-12-08 02:33:38 +00:00
|
|
|
version_added: "1.9"
|
2015-11-18 18:08:21 +00:00
|
|
|
alias_evaluate_target_health:
|
|
|
|
description:
|
|
|
|
- Whether or not to evaluate an alias target health. Useful for aliases to Elastic Load Balancers.
|
2018-03-15 21:15:24 +00:00
|
|
|
type: bool
|
|
|
|
default: no
|
2016-12-08 02:33:38 +00:00
|
|
|
version_added: "2.1"
|
2014-09-26 01:01:01 +00:00
|
|
|
value:
|
|
|
|
description:
|
2017-04-18 15:49:25 +00:00
|
|
|
- The new value when creating a DNS record. YAML lists or multiple comma-spaced values are allowed for non-alias records.
|
|
|
|
- When deleting a record all values for the record must be specified or Route53 will not delete it.
|
2014-09-26 01:01:01 +00:00
|
|
|
overwrite:
|
|
|
|
description:
|
|
|
|
- Whether an existing record should be overwritten on create if values do not match
|
2018-11-27 15:11:01 +00:00
|
|
|
type: bool
|
2014-09-26 01:01:01 +00:00
|
|
|
retry_interval:
|
|
|
|
description:
|
2017-03-23 01:50:28 +00:00
|
|
|
- In the case that route53 is still servicing a prior request, this module will wait and try again after this many seconds. If you have many
|
|
|
|
domain names, the default of 500 seconds may be too long.
|
2014-09-26 01:01:01 +00:00
|
|
|
default: 500
|
2015-02-27 03:35:10 +00:00
|
|
|
private_zone:
|
|
|
|
description:
|
2018-03-15 21:15:24 +00:00
|
|
|
- If set to C(yes), the private zone matching the requested name within the domain will be used if there are both public and private zones.
|
2017-03-23 01:50:28 +00:00
|
|
|
The default is to use the public zone.
|
2018-03-15 21:15:24 +00:00
|
|
|
type: bool
|
|
|
|
default: 'no'
|
2015-02-27 03:35:10 +00:00
|
|
|
version_added: "1.9"
|
2015-05-05 15:07:18 +00:00
|
|
|
identifier:
|
|
|
|
description:
|
2016-04-12 20:06:41 +00:00
|
|
|
- Have to be specified for Weighted, latency-based and failover resource record sets only. An identifier
|
2015-05-05 15:07:18 +00:00
|
|
|
that differentiates among multiple resource record sets that have the
|
2016-06-06 20:51:25 +00:00
|
|
|
same combination of DNS name and type.
|
2015-05-05 15:07:18 +00:00
|
|
|
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.
|
|
|
|
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
|
|
|
|
version_added: "2.0"
|
|
|
|
health_check:
|
|
|
|
description:
|
|
|
|
- Health check to associate with this record
|
|
|
|
version_added: "2.0"
|
|
|
|
failover:
|
|
|
|
description:
|
|
|
|
- Failover resource record sets only. Whether this is the primary or
|
2016-04-12 20:06:41 +00:00
|
|
|
secondary resource record set. Allowed values are PRIMARY and SECONDARY
|
2015-05-05 15:07:18 +00:00
|
|
|
version_added: "2.0"
|
2015-05-20 19:39:17 +00:00
|
|
|
vpc_id:
|
|
|
|
description:
|
2015-07-14 20:54:49 +00:00
|
|
|
- "When used in conjunction with private_zone: true, this will only modify records in the private hosted zone attached to this VPC."
|
|
|
|
- This allows you to have multiple private hosted zones, all with the same name, attached to different VPCs.
|
2015-05-20 19:39:17 +00:00
|
|
|
version_added: "2.0"
|
2016-01-11 14:45:50 +00:00
|
|
|
wait:
|
|
|
|
description:
|
|
|
|
- Wait until the changes have been replicated to all Amazon Route 53 DNS servers.
|
2018-03-15 21:15:24 +00:00
|
|
|
type: bool
|
|
|
|
default: 'no'
|
2016-01-12 14:33:58 +00:00
|
|
|
version_added: "2.1"
|
2016-01-11 15:43:26 +00:00
|
|
|
wait_timeout:
|
|
|
|
description:
|
|
|
|
- How long to wait for the changes to be replicated, in seconds.
|
|
|
|
default: 300
|
2016-01-12 14:33:58 +00:00
|
|
|
version_added: "2.1"
|
2016-01-28 17:24:02 +00:00
|
|
|
author:
|
2018-11-13 13:08:28 +00:00
|
|
|
- Bruce Pennypacker (@bpennypacker)
|
|
|
|
- Mike Buzzetti (@jimbydamonk)
|
2014-12-25 00:04:25 +00:00
|
|
|
extends_documentation_fragment: aws
|
2014-09-26 01:01:01 +00:00
|
|
|
'''
|
|
|
|
|
2017-10-27 03:27:01 +00:00
|
|
|
RETURN = '''
|
|
|
|
nameservers:
|
|
|
|
description: nameservers associated with the zone
|
|
|
|
returned: when state is 'get'
|
|
|
|
type: list
|
|
|
|
sample:
|
|
|
|
- ns-1036.awsdns-00.org.
|
|
|
|
- ns-516.awsdns-00.net.
|
|
|
|
- ns-1504.awsdns-00.co.uk.
|
|
|
|
- ns-1.awsdns-00.com.
|
|
|
|
set:
|
|
|
|
description: info specific to the resource record
|
|
|
|
returned: when state is 'get'
|
|
|
|
type: complex
|
|
|
|
contains:
|
|
|
|
alias:
|
|
|
|
description: whether this is an alias
|
|
|
|
returned: always
|
|
|
|
type: bool
|
|
|
|
sample: false
|
|
|
|
failover:
|
2017-11-01 05:08:14 +00:00
|
|
|
description: ""
|
2017-10-27 03:27:01 +00:00
|
|
|
returned: always
|
|
|
|
type: NoneType
|
|
|
|
sample: null
|
|
|
|
health_check:
|
|
|
|
description: health_check associated with this record
|
|
|
|
returned: always
|
|
|
|
type: NoneType
|
|
|
|
sample: null
|
|
|
|
identifier:
|
2017-11-01 05:08:14 +00:00
|
|
|
description: ""
|
2017-10-27 03:27:01 +00:00
|
|
|
returned: always
|
|
|
|
type: NoneType
|
|
|
|
sample: null
|
|
|
|
record:
|
|
|
|
description: domain name for the record set
|
|
|
|
returned: always
|
2018-12-18 21:25:30 +00:00
|
|
|
type: str
|
2017-10-27 03:27:01 +00:00
|
|
|
sample: new.foo.com.
|
|
|
|
region:
|
2017-11-01 05:08:14 +00:00
|
|
|
description: ""
|
2017-10-27 03:27:01 +00:00
|
|
|
returned: always
|
|
|
|
type:
|
|
|
|
sample:
|
|
|
|
ttl:
|
|
|
|
description: resource record cache TTL
|
|
|
|
returned: always
|
2018-12-18 21:25:30 +00:00
|
|
|
type: str
|
2017-10-27 03:27:01 +00:00
|
|
|
sample: '3600'
|
|
|
|
type:
|
|
|
|
description: record set type
|
|
|
|
returned: always
|
2018-12-18 21:25:30 +00:00
|
|
|
type: str
|
2017-10-27 03:27:01 +00:00
|
|
|
sample: A
|
|
|
|
value:
|
|
|
|
description: value
|
|
|
|
returned: always
|
2018-12-18 21:25:30 +00:00
|
|
|
type: str
|
2017-10-27 03:27:01 +00:00
|
|
|
sample: 52.43.18.27
|
|
|
|
values:
|
|
|
|
description: values
|
|
|
|
returned: always
|
|
|
|
type: list
|
|
|
|
sample:
|
|
|
|
- 52.43.18.27
|
|
|
|
weight:
|
|
|
|
description: weight of the record
|
|
|
|
returned: always
|
2018-12-18 21:25:30 +00:00
|
|
|
type: str
|
2017-10-27 03:27:01 +00:00
|
|
|
sample: '3'
|
|
|
|
zone:
|
|
|
|
description: zone this record set belongs to
|
|
|
|
returned: always
|
2018-12-18 21:25:30 +00:00
|
|
|
type: str
|
2017-10-27 03:27:01 +00:00
|
|
|
sample: foo.bar.com.
|
|
|
|
'''
|
2014-12-01 20:14:57 +00:00
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
EXAMPLES = '''
|
2016-01-11 14:45:50 +00:00
|
|
|
# Add new.foo.com as an A record with 3 IPs and wait until the changes have been replicated
|
2014-12-01 20:14:57 +00:00
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: present
|
2014-12-01 20:14:57 +00:00
|
|
|
zone: foo.com
|
|
|
|
record: new.foo.com
|
|
|
|
type: A
|
|
|
|
ttl: 7200
|
|
|
|
value: 1.1.1.1,2.2.2.2,3.3.3.3
|
2016-01-11 14:45:50 +00:00
|
|
|
wait: yes
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2017-04-18 15:49:25 +00:00
|
|
|
# Update new.foo.com as an A record with a list of 3 IPs and wait until the changes have been replicated
|
|
|
|
- route53:
|
|
|
|
state: present
|
|
|
|
zone: foo.com
|
|
|
|
record: new.foo.com
|
|
|
|
type: A
|
|
|
|
ttl: 7200
|
|
|
|
value:
|
|
|
|
- 1.1.1.1
|
|
|
|
- 2.2.2.2
|
|
|
|
- 3.3.3.3
|
|
|
|
wait: yes
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
# Retrieve the details for new.foo.com
|
2014-12-01 20:14:57 +00:00
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: get
|
2014-12-01 20:14:57 +00:00
|
|
|
zone: foo.com
|
|
|
|
record: new.foo.com
|
|
|
|
type: A
|
2014-09-26 01:01:01 +00:00
|
|
|
register: rec
|
|
|
|
|
|
|
|
# Delete new.foo.com A record using the results from the get command
|
2014-12-01 20:14:57 +00:00
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: absent
|
2014-12-01 20:14:57 +00:00
|
|
|
zone: foo.com
|
|
|
|
record: "{{ rec.set.record }}"
|
2015-01-19 23:43:22 +00:00
|
|
|
ttl: "{{ rec.set.ttl }}"
|
2014-12-01 20:14:57 +00:00
|
|
|
type: "{{ rec.set.type }}"
|
|
|
|
value: "{{ rec.set.value }}"
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
# Add an AAAA record. Note that because there are colons in the value
|
2017-04-18 15:49:25 +00:00
|
|
|
# that the IPv6 address must be quoted. Also shows using the old form command=create.
|
2014-12-01 20:14:57 +00:00
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
command: create
|
|
|
|
zone: foo.com
|
|
|
|
record: localhost.foo.com
|
|
|
|
type: AAAA
|
|
|
|
ttl: 7200
|
2014-12-01 20:14:57 +00:00
|
|
|
value: "::1"
|
2016-06-06 20:51:25 +00:00
|
|
|
|
2016-05-26 14:45:02 +00:00
|
|
|
# Add a SRV record with multiple fields for a service on port 22222
|
|
|
|
# For more information on SRV records see:
|
|
|
|
# https://en.wikipedia.org/wiki/SRV_record
|
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: present
|
|
|
|
zone: foo.com
|
|
|
|
record: "_example-service._tcp.foo.com"
|
|
|
|
type: SRV
|
|
|
|
value: "0 0 22222 host1.foo.com,0 0 22222 host2.foo.com"
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
# Add a TXT record. Note that TXT and SPF records must be surrounded
|
|
|
|
# by quotes when sent to Route 53:
|
2014-12-01 20:14:57 +00:00
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: present
|
|
|
|
zone: foo.com
|
|
|
|
record: localhost.foo.com
|
|
|
|
type: TXT
|
|
|
|
ttl: 7200
|
2014-12-01 20:14:57 +00:00
|
|
|
value: '"bar"'
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2015-02-06 23:19:11 +00:00
|
|
|
# Add an alias record that points to an Amazon ELB:
|
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: present
|
|
|
|
zone: foo.com
|
|
|
|
record: elb.foo.com
|
|
|
|
type: A
|
|
|
|
value: "{{ elb_dns_name }}"
|
|
|
|
alias: True
|
|
|
|
alias_hosted_zone_id: "{{ elb_zone_id }}"
|
2015-02-06 23:19:11 +00:00
|
|
|
|
2016-06-06 20:51:25 +00:00
|
|
|
# Retrieve the details for elb.foo.com
|
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: get
|
2016-06-06 20:51:25 +00:00
|
|
|
zone: foo.com
|
|
|
|
record: elb.foo.com
|
|
|
|
type: A
|
|
|
|
register: rec
|
|
|
|
|
|
|
|
# Delete an alias record using the results from the get command
|
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: absent
|
2016-06-06 20:51:25 +00:00
|
|
|
zone: foo.com
|
|
|
|
record: "{{ rec.set.record }}"
|
|
|
|
ttl: "{{ rec.set.ttl }}"
|
|
|
|
type: "{{ rec.set.type }}"
|
|
|
|
value: "{{ rec.set.value }}"
|
|
|
|
alias: True
|
|
|
|
alias_hosted_zone_id: "{{ rec.set.alias_hosted_zone_id }}"
|
|
|
|
|
2015-11-18 18:08:21 +00:00
|
|
|
# Add an alias record that points to an Amazon ELB and evaluates it health:
|
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: present
|
2016-11-15 21:00:33 +00:00
|
|
|
zone: foo.com
|
|
|
|
record: elb.foo.com
|
|
|
|
type: A
|
|
|
|
value: "{{ elb_dns_name }}"
|
|
|
|
alias: True
|
|
|
|
alias_hosted_zone_id: "{{ elb_zone_id }}"
|
|
|
|
alias_evaluate_target_health: True
|
2015-11-18 18:08:21 +00:00
|
|
|
|
2017-04-18 15:49:25 +00:00
|
|
|
# Add an AAAA record with Hosted Zone ID.
|
2015-04-15 10:43:00 +00:00
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: present
|
|
|
|
zone: foo.com
|
|
|
|
hosted_zone_id: Z2AABBCCDDEEFF
|
|
|
|
record: localhost.foo.com
|
|
|
|
type: AAAA
|
|
|
|
ttl: 7200
|
2015-04-15 10:43:00 +00:00
|
|
|
value: "::1"
|
|
|
|
|
2016-01-11 14:45:50 +00:00
|
|
|
# Use a routing policy to distribute traffic:
|
|
|
|
- route53:
|
2017-04-18 15:49:25 +00:00
|
|
|
state: present
|
|
|
|
zone: foo.com
|
|
|
|
record: www.foo.com
|
|
|
|
type: CNAME
|
|
|
|
value: host1.foo.com
|
2016-01-11 14:45:50 +00:00
|
|
|
ttl: 30
|
|
|
|
# Routing policy
|
|
|
|
identifier: "host1@www"
|
|
|
|
weight: 100
|
|
|
|
health_check: "d994b780-3150-49fd-9205-356abdd42e75"
|
|
|
|
|
2017-10-18 02:48:04 +00:00
|
|
|
# Add a CAA record (RFC 6844):
|
|
|
|
- route53:
|
|
|
|
state: present
|
|
|
|
zone: example.com
|
|
|
|
record: example.com
|
|
|
|
type: CAA
|
|
|
|
value:
|
|
|
|
- 0 issue "ca.example.net"
|
|
|
|
- 0 issuewild ";"
|
|
|
|
- 0 iodef "mailto:security@example.com"
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
import time
|
2016-03-01 17:49:10 +00:00
|
|
|
import distutils.version
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
import boto
|
2016-12-08 02:33:38 +00:00
|
|
|
import boto.ec2
|
2014-12-25 00:04:25 +00:00
|
|
|
from boto.route53 import Route53Connection
|
2015-05-05 15:07:18 +00:00
|
|
|
from boto.route53.record import Record, ResourceRecordSets
|
2016-01-11 14:45:50 +00:00
|
|
|
from boto.route53.status import Status
|
2015-04-01 23:16:54 +00:00
|
|
|
HAS_BOTO = True
|
2014-09-26 01:01:01 +00:00
|
|
|
except ImportError:
|
2015-04-01 23:16:54 +00:00
|
|
|
HAS_BOTO = False
|
|
|
|
|
2017-08-12 16:27:11 +00:00
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info
|
|
|
|
|
|
|
|
|
|
|
|
MINIMUM_BOTO_VERSION = '2.28.0'
|
|
|
|
WAIT_RETRY_SLEEP = 5 # how many seconds to wait between propagation status polls
|
|
|
|
|
2016-01-11 15:43:26 +00:00
|
|
|
|
|
|
|
class TimeoutError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2015-05-20 19:39:17 +00:00
|
|
|
def get_zone_by_name(conn, module, zone_name, want_private, zone_id, want_vpc_id):
|
2015-07-06 10:02:24 +00:00
|
|
|
"""Finds a zone by name or zone_id"""
|
2017-02-10 18:34:50 +00:00
|
|
|
for zone in invoke_with_throttling_retries(conn.get_zones):
|
2015-05-05 15:07:18 +00:00
|
|
|
# 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))
|
2017-01-28 08:12:11 +00:00
|
|
|
if private_zone == want_private and ((zone.name == zone_name and zone_id is None) or zone.id.replace('/hostedzone/', '') == zone_id):
|
2015-05-20 19:39:17 +00:00
|
|
|
if want_vpc_id:
|
|
|
|
# NOTE: These details aren't available in other boto methods, hence the necessary
|
|
|
|
# extra API call
|
2017-02-10 18:34:50 +00:00
|
|
|
hosted_zone = invoke_with_throttling_retries(conn.get_hosted_zone, zone.id)
|
|
|
|
zone_details = hosted_zone['GetHostedZoneResponse']
|
2015-05-20 19:39:17 +00:00
|
|
|
# this is to deal with this boto bug: https://github.com/boto/boto/pull/2882
|
|
|
|
if isinstance(zone_details['VPCs'], dict):
|
|
|
|
if zone_details['VPCs']['VPC']['VPCId'] == want_vpc_id:
|
|
|
|
return zone
|
2017-04-18 15:49:25 +00:00
|
|
|
else: # Forward compatibility for when boto fixes that bug
|
2015-05-20 19:39:17 +00:00
|
|
|
if want_vpc_id in [v['VPCId'] for v in zone_details['VPCs']]:
|
|
|
|
return zone
|
|
|
|
else:
|
|
|
|
return zone
|
2015-05-05 15:07:18 +00:00
|
|
|
return None
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2016-01-11 15:43:26 +00:00
|
|
|
def commit(changes, retry_interval, wait, wait_timeout):
|
2014-09-26 01:01:01 +00:00
|
|
|
"""Commit changes, but retry PriorRequestNotComplete errors."""
|
2016-01-11 14:45:50 +00:00
|
|
|
result = None
|
2014-09-26 01:01:01 +00:00
|
|
|
retry = 10
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
retry -= 1
|
2016-01-11 14:45:50 +00:00
|
|
|
result = changes.commit()
|
|
|
|
break
|
2016-06-02 19:56:48 +00:00
|
|
|
except boto.route53.exception.DNSServerError as e:
|
2014-09-26 01:01:01 +00:00
|
|
|
code = e.body.split("<Code>")[1]
|
|
|
|
code = code.split("</Code>")[0]
|
|
|
|
if code != 'PriorRequestNotComplete' or retry < 0:
|
|
|
|
raise e
|
2014-12-01 20:21:52 +00:00
|
|
|
time.sleep(float(retry_interval))
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2016-01-11 14:45:50 +00:00
|
|
|
if wait:
|
2016-05-03 14:51:12 +00:00
|
|
|
timeout_time = time.time() + wait_timeout
|
|
|
|
connection = changes.connection
|
|
|
|
change = result['ChangeResourceRecordSetsResponse']['ChangeInfo']
|
|
|
|
status = Status(connection, change)
|
|
|
|
while status.status != 'INSYNC' and time.time() < timeout_time:
|
|
|
|
time.sleep(WAIT_RETRY_SLEEP)
|
|
|
|
status.update()
|
|
|
|
if time.time() >= timeout_time:
|
|
|
|
raise TimeoutError()
|
|
|
|
return result
|
2016-01-11 14:45:50 +00:00
|
|
|
|
2018-05-03 12:29:57 +00:00
|
|
|
|
2016-02-05 07:57:55 +00:00
|
|
|
# Shamelessly copied over from https://git.io/vgmDG
|
|
|
|
IGNORE_CODE = 'Throttling'
|
2017-04-18 15:49:25 +00:00
|
|
|
MAX_RETRIES = 5
|
|
|
|
|
|
|
|
|
2017-02-10 18:34:50 +00:00
|
|
|
def invoke_with_throttling_retries(function_ref, *argv, **kwargs):
|
2017-04-18 15:49:25 +00:00
|
|
|
retries = 0
|
2016-02-05 07:57:55 +00:00
|
|
|
while True:
|
|
|
|
try:
|
2017-04-18 15:49:25 +00:00
|
|
|
retval = function_ref(*argv, **kwargs)
|
2016-02-05 07:57:55 +00:00
|
|
|
return retval
|
2016-06-02 19:56:48 +00:00
|
|
|
except boto.exception.BotoServerError as e:
|
2017-04-18 15:49:25 +00:00
|
|
|
if e.code != IGNORE_CODE or retries == MAX_RETRIES:
|
2016-02-05 07:57:55 +00:00
|
|
|
raise e
|
|
|
|
time.sleep(5 * (2**retries))
|
|
|
|
retries += 1
|
|
|
|
|
2017-04-18 15:49:25 +00:00
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
def main():
|
|
|
|
argument_spec = ec2_argument_spec()
|
|
|
|
argument_spec.update(dict(
|
Introduce new 'required_by' argument_spec option (#28662)
* Introduce new "required_by' argument_spec option
This PR introduces a new **required_by** argument_spec option which allows you to say *"if parameter A is set, parameter B and C are required as well"*.
- The difference with **required_if** is that it can only add dependencies if a parameter is set to a specific value, not when it is just defined.
- The difference with **required_together** is that it has a commutative property, so: *"Parameter A and B are required together, if one of them has been defined"*.
As an example, we need this for the complex options that the xml module provides. One of the issues we often see is that users are not using the correct combination of options, and then are surprised that the module does not perform the requested action(s).
This would be solved by adding the correct dependencies, and mutual exclusives. For us this is important to get this shipped together with the new xml module in Ansible v2.4. (This is related to bugfix https://github.com/ansible/ansible/pull/28657)
```python
module = AnsibleModule(
argument_spec=dict(
path=dict(type='path', aliases=['dest', 'file']),
xmlstring=dict(type='str'),
xpath=dict(type='str'),
namespaces=dict(type='dict', default={}),
state=dict(type='str', default='present', choices=['absent',
'present'], aliases=['ensure']),
value=dict(type='raw'),
attribute=dict(type='raw'),
add_children=dict(type='list'),
set_children=dict(type='list'),
count=dict(type='bool', default=False),
print_match=dict(type='bool', default=False),
pretty_print=dict(type='bool', default=False),
content=dict(type='str', choices=['attribute', 'text']),
input_type=dict(type='str', default='yaml', choices=['xml',
'yaml']),
backup=dict(type='bool', default=False),
),
supports_check_mode=True,
required_by=dict(
add_children=['xpath'],
attribute=['value', 'xpath'],
content=['xpath'],
set_children=['xpath'],
value=['xpath'],
),
required_if=[
['count', True, ['xpath']],
['print_match', True, ['xpath']],
],
required_one_of=[
['path', 'xmlstring'],
['add_children', 'content', 'count', 'pretty_print', 'print_match', 'set_children', 'value'],
],
mutually_exclusive=[
['add_children', 'content', 'count', 'print_match','set_children', 'value'],
['path', 'xmlstring'],
],
)
```
* Rebase and fix conflict
* Add modules that use required_by functionality
* Update required_by schema
* Fix rebase issue
2019-02-15 00:57:45 +00:00
|
|
|
state=dict(type='str', required=True, choices=['absent', 'create', 'delete', 'get', 'present'], aliases=['command']),
|
|
|
|
zone=dict(type='str', required=True),
|
|
|
|
hosted_zone_id=dict(type='str', ),
|
|
|
|
record=dict(type='str', required=True),
|
|
|
|
ttl=dict(type='int', default=3600),
|
|
|
|
type=dict(type='str', required=True, choices=['A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SPF', 'SRV', 'TXT']),
|
|
|
|
alias=dict(type='bool'),
|
|
|
|
alias_hosted_zone_id=dict(type='str'),
|
|
|
|
alias_evaluate_target_health=dict(type='bool', default=False),
|
|
|
|
value=dict(type='list'),
|
|
|
|
overwrite=dict(type='bool'),
|
|
|
|
retry_interval=dict(type='int', default=500),
|
|
|
|
private_zone=dict(type='bool', default=False),
|
|
|
|
identifier=dict(type='str'),
|
|
|
|
weight=dict(type='int'),
|
|
|
|
region=dict(type='str'),
|
|
|
|
health_check=dict(type='str'),
|
|
|
|
failover=dict(type='str', choices=['PRIMARY', 'SECONDARY']),
|
|
|
|
vpc_id=dict(type='str'),
|
|
|
|
wait=dict(type='bool', default=False),
|
|
|
|
wait_timeout=dict(type='int', default=300),
|
2017-04-18 15:49:25 +00:00
|
|
|
))
|
|
|
|
|
Introduce new 'required_by' argument_spec option (#28662)
* Introduce new "required_by' argument_spec option
This PR introduces a new **required_by** argument_spec option which allows you to say *"if parameter A is set, parameter B and C are required as well"*.
- The difference with **required_if** is that it can only add dependencies if a parameter is set to a specific value, not when it is just defined.
- The difference with **required_together** is that it has a commutative property, so: *"Parameter A and B are required together, if one of them has been defined"*.
As an example, we need this for the complex options that the xml module provides. One of the issues we often see is that users are not using the correct combination of options, and then are surprised that the module does not perform the requested action(s).
This would be solved by adding the correct dependencies, and mutual exclusives. For us this is important to get this shipped together with the new xml module in Ansible v2.4. (This is related to bugfix https://github.com/ansible/ansible/pull/28657)
```python
module = AnsibleModule(
argument_spec=dict(
path=dict(type='path', aliases=['dest', 'file']),
xmlstring=dict(type='str'),
xpath=dict(type='str'),
namespaces=dict(type='dict', default={}),
state=dict(type='str', default='present', choices=['absent',
'present'], aliases=['ensure']),
value=dict(type='raw'),
attribute=dict(type='raw'),
add_children=dict(type='list'),
set_children=dict(type='list'),
count=dict(type='bool', default=False),
print_match=dict(type='bool', default=False),
pretty_print=dict(type='bool', default=False),
content=dict(type='str', choices=['attribute', 'text']),
input_type=dict(type='str', default='yaml', choices=['xml',
'yaml']),
backup=dict(type='bool', default=False),
),
supports_check_mode=True,
required_by=dict(
add_children=['xpath'],
attribute=['value', 'xpath'],
content=['xpath'],
set_children=['xpath'],
value=['xpath'],
),
required_if=[
['count', True, ['xpath']],
['print_match', True, ['xpath']],
],
required_one_of=[
['path', 'xmlstring'],
['add_children', 'content', 'count', 'pretty_print', 'print_match', 'set_children', 'value'],
],
mutually_exclusive=[
['add_children', 'content', 'count', 'print_match','set_children', 'value'],
['path', 'xmlstring'],
],
)
```
* Rebase and fix conflict
* Add modules that use required_by functionality
* Update required_by schema
* Fix rebase issue
2019-02-15 00:57:45 +00:00
|
|
|
module = AnsibleModule(
|
|
|
|
argument_spec=argument_spec,
|
|
|
|
supports_check_mode=True,
|
|
|
|
# If alias is True then you must specify alias_hosted_zone as well
|
|
|
|
required_together=[['alias', 'alias_hosted_zone_id']],
|
|
|
|
# state=present, absent, create, delete THEN value is required
|
|
|
|
required_if=(
|
|
|
|
('state', 'present', ['value']),
|
|
|
|
('state', 'create', ['value']),
|
|
|
|
('state', 'absent', ['value']),
|
|
|
|
('state', 'delete', ['value']),
|
|
|
|
),
|
|
|
|
# failover, region and weight are mutually exclusive
|
|
|
|
mutually_exclusive=[('failover', 'region', 'weight')],
|
|
|
|
# failover, region and weight require identifier
|
|
|
|
required_by=dict(
|
2019-02-19 19:29:51 +00:00
|
|
|
failover=('identifier',),
|
|
|
|
region=('identifier',),
|
|
|
|
weight=('identifier',),
|
Introduce new 'required_by' argument_spec option (#28662)
* Introduce new "required_by' argument_spec option
This PR introduces a new **required_by** argument_spec option which allows you to say *"if parameter A is set, parameter B and C are required as well"*.
- The difference with **required_if** is that it can only add dependencies if a parameter is set to a specific value, not when it is just defined.
- The difference with **required_together** is that it has a commutative property, so: *"Parameter A and B are required together, if one of them has been defined"*.
As an example, we need this for the complex options that the xml module provides. One of the issues we often see is that users are not using the correct combination of options, and then are surprised that the module does not perform the requested action(s).
This would be solved by adding the correct dependencies, and mutual exclusives. For us this is important to get this shipped together with the new xml module in Ansible v2.4. (This is related to bugfix https://github.com/ansible/ansible/pull/28657)
```python
module = AnsibleModule(
argument_spec=dict(
path=dict(type='path', aliases=['dest', 'file']),
xmlstring=dict(type='str'),
xpath=dict(type='str'),
namespaces=dict(type='dict', default={}),
state=dict(type='str', default='present', choices=['absent',
'present'], aliases=['ensure']),
value=dict(type='raw'),
attribute=dict(type='raw'),
add_children=dict(type='list'),
set_children=dict(type='list'),
count=dict(type='bool', default=False),
print_match=dict(type='bool', default=False),
pretty_print=dict(type='bool', default=False),
content=dict(type='str', choices=['attribute', 'text']),
input_type=dict(type='str', default='yaml', choices=['xml',
'yaml']),
backup=dict(type='bool', default=False),
),
supports_check_mode=True,
required_by=dict(
add_children=['xpath'],
attribute=['value', 'xpath'],
content=['xpath'],
set_children=['xpath'],
value=['xpath'],
),
required_if=[
['count', True, ['xpath']],
['print_match', True, ['xpath']],
],
required_one_of=[
['path', 'xmlstring'],
['add_children', 'content', 'count', 'pretty_print', 'print_match', 'set_children', 'value'],
],
mutually_exclusive=[
['add_children', 'content', 'count', 'print_match','set_children', 'value'],
['path', 'xmlstring'],
],
)
```
* Rebase and fix conflict
* Add modules that use required_by functionality
* Update required_by schema
* Fix rebase issue
2019-02-15 00:57:45 +00:00
|
|
|
),
|
|
|
|
)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2015-04-01 23:16:54 +00:00
|
|
|
if not HAS_BOTO:
|
|
|
|
module.fail_json(msg='boto required for this module')
|
|
|
|
|
2016-03-01 17:49:10 +00:00
|
|
|
if distutils.version.StrictVersion(boto.__version__) < distutils.version.StrictVersion(MINIMUM_BOTO_VERSION):
|
|
|
|
module.fail_json(msg='Found boto in version %s, but >= %s is required' % (boto.__version__, MINIMUM_BOTO_VERSION))
|
|
|
|
|
2017-04-18 15:49:25 +00:00
|
|
|
if module.params['state'] in ('present', 'create'):
|
|
|
|
command_in = 'create'
|
|
|
|
elif module.params['state'] in ('absent', 'delete'):
|
|
|
|
command_in = 'delete'
|
|
|
|
elif module.params['state'] == 'get':
|
|
|
|
command_in = 'get'
|
|
|
|
|
|
|
|
zone_in = module.params.get('zone').lower()
|
|
|
|
hosted_zone_id_in = module.params.get('hosted_zone_id')
|
|
|
|
ttl_in = module.params.get('ttl')
|
|
|
|
record_in = module.params.get('record').lower()
|
|
|
|
type_in = module.params.get('type')
|
2017-12-08 04:15:41 +00:00
|
|
|
value_in = module.params.get('value') or []
|
2017-04-18 15:49:25 +00:00
|
|
|
alias_in = module.params.get('alias')
|
|
|
|
alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id')
|
2015-11-18 18:08:21 +00:00
|
|
|
alias_evaluate_target_health_in = module.params.get('alias_evaluate_target_health')
|
2017-04-18 15:49:25 +00:00
|
|
|
retry_interval_in = module.params.get('retry_interval')
|
|
|
|
|
|
|
|
if module.params['vpc_id'] is not None:
|
|
|
|
private_zone_in = True
|
|
|
|
else:
|
|
|
|
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')
|
|
|
|
vpc_id_in = module.params.get('vpc_id')
|
|
|
|
wait_in = module.params.get('wait')
|
|
|
|
wait_timeout_in = module.params.get('wait_timeout')
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2014-12-25 00:04:25 +00:00
|
|
|
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
if zone_in[-1:] != '.':
|
|
|
|
zone_in += "."
|
|
|
|
|
|
|
|
if record_in[-1:] != '.':
|
|
|
|
record_in += "."
|
|
|
|
|
|
|
|
if command_in == 'create' or command_in == 'delete':
|
2017-04-18 15:49:25 +00:00
|
|
|
if alias_in and len(value_in) != 1:
|
|
|
|
module.fail_json(msg="parameter 'value' must contain a single dns name for alias records")
|
|
|
|
if (weight_in is None and region_in is None and failover_in is None) and identifier_in is not None:
|
|
|
|
module.fail_json(msg="You have specified identifier which makes sense only if you specify one of: weight, region or failover.")
|
2015-05-20 19:39:17 +00:00
|
|
|
|
2016-01-11 14:45:50 +00:00
|
|
|
# connect to the route53 endpoint
|
2014-09-26 01:01:01 +00:00
|
|
|
try:
|
2014-12-25 00:04:25 +00:00
|
|
|
conn = Route53Connection(**aws_connect_kwargs)
|
2016-06-02 19:56:48 +00:00
|
|
|
except boto.exception.BotoServerError as e:
|
2017-04-18 15:49:25 +00:00
|
|
|
module.fail_json(msg=e.error_message)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2016-12-08 02:33:38 +00:00
|
|
|
# Find the named zone ID
|
|
|
|
zone = get_zone_by_name(conn, module, zone_in, private_zone_in, hosted_zone_id_in, vpc_id_in)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
# Verify that the requested zone is already defined in Route53
|
2016-12-08 02:33:38 +00:00
|
|
|
if zone is None:
|
2014-09-26 01:01:01 +00:00
|
|
|
errmsg = "Zone %s does not exist in Route53" % zone_in
|
2017-04-18 15:49:25 +00:00
|
|
|
module.fail_json(msg=errmsg)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
record = {}
|
2016-01-11 14:45:50 +00:00
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
found_record = False
|
2015-05-05 15:07:18 +00:00
|
|
|
wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in,
|
2017-04-18 15:49:25 +00:00
|
|
|
identifier=identifier_in, weight=weight_in,
|
|
|
|
region=region_in, health_check=health_check_in,
|
|
|
|
failover=failover_in)
|
|
|
|
for v in value_in:
|
2015-05-05 15:07:18 +00:00
|
|
|
if alias_in:
|
2015-11-18 18:08:21 +00:00
|
|
|
wanted_rset.set_alias(alias_hosted_zone_id_in, v, alias_evaluate_target_health_in)
|
2015-05-05 15:07:18 +00:00
|
|
|
else:
|
|
|
|
wanted_rset.add_value(v)
|
|
|
|
|
2018-09-27 19:08:47 +00:00
|
|
|
need_to_sort_records = (type_in == 'CAA')
|
|
|
|
|
|
|
|
# Sort records for wanted_rset if necessary (keep original list)
|
|
|
|
unsorted_records = wanted_rset.resource_records
|
|
|
|
if need_to_sort_records:
|
|
|
|
wanted_rset.resource_records = sorted(unsorted_records)
|
|
|
|
|
2017-02-10 18:34:50 +00:00
|
|
|
sets = invoke_with_throttling_retries(conn.get_all_rrsets, zone.id, name=record_in,
|
|
|
|
type=type_in, identifier=identifier_in)
|
2017-06-20 20:49:05 +00:00
|
|
|
sets_iter = iter(sets)
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
rset = invoke_with_throttling_retries(next, sets_iter)
|
|
|
|
except StopIteration:
|
|
|
|
break
|
2014-09-26 01:01:01 +00:00
|
|
|
# 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', '@')
|
2017-04-18 15:49:25 +00:00
|
|
|
# Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block
|
2015-11-18 18:08:21 +00:00
|
|
|
rset.name = decoded_name
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2016-04-16 19:50:53 +00:00
|
|
|
if identifier_in is not None:
|
|
|
|
identifier_in = str(identifier_in)
|
2016-05-03 14:51:12 +00:00
|
|
|
|
2016-04-16 19:50:53 +00:00
|
|
|
if rset.type == type_in and decoded_name.lower() == record_in.lower() and rset.identifier == identifier_in:
|
2018-09-27 19:08:47 +00:00
|
|
|
if need_to_sort_records:
|
|
|
|
# Sort records
|
|
|
|
rset.resource_records = sorted(rset.resource_records)
|
2014-09-26 01:01:01 +00:00
|
|
|
found_record = True
|
|
|
|
record['zone'] = zone_in
|
|
|
|
record['type'] = rset.type
|
|
|
|
record['record'] = decoded_name
|
|
|
|
record['ttl'] = rset.ttl
|
2016-12-08 02:33:38 +00:00
|
|
|
if hosted_zone_id_in:
|
|
|
|
record['hosted_zone_id'] = hosted_zone_id_in
|
2015-05-05 15:07:18 +00:00
|
|
|
record['identifier'] = rset.identifier
|
|
|
|
record['weight'] = rset.weight
|
|
|
|
record['region'] = rset.region
|
|
|
|
record['failover'] = rset.failover
|
|
|
|
record['health_check'] = rset.health_check
|
2015-04-15 10:43:00 +00:00
|
|
|
if hosted_zone_id_in:
|
|
|
|
record['hosted_zone_id'] = hosted_zone_id_in
|
2015-02-06 23:19:11 +00:00
|
|
|
if rset.alias_dns_name:
|
2017-01-31 02:03:13 +00:00
|
|
|
record['alias'] = True
|
|
|
|
record['value'] = rset.alias_dns_name
|
|
|
|
record['values'] = [rset.alias_dns_name]
|
|
|
|
record['alias_hosted_zone_id'] = rset.alias_hosted_zone_id
|
|
|
|
record['alias_evaluate_target_health'] = rset.alias_evaluate_target_health
|
2015-02-06 23:19:11 +00:00
|
|
|
else:
|
2017-01-31 02:03:13 +00:00
|
|
|
record['alias'] = False
|
|
|
|
record['value'] = ','.join(sorted(rset.resource_records))
|
|
|
|
record['values'] = sorted(rset.resource_records)
|
2015-05-05 15:07:18 +00:00
|
|
|
if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml():
|
2014-09-26 01:01:01 +00:00
|
|
|
module.exit_json(changed=False)
|
2017-06-15 19:06:16 +00:00
|
|
|
|
|
|
|
# We need to look only at the first rrset returned by the above call,
|
|
|
|
# so break here. The returned elements begin with the one matching our
|
|
|
|
# requested name, type, and identifier, if such an element exists,
|
|
|
|
# followed by all others that come after it in alphabetical order.
|
|
|
|
# Therefore, if the first set does not match, no subsequent set will
|
|
|
|
# match either.
|
|
|
|
break
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
if command_in == 'get':
|
2015-02-20 17:22:03 +00:00
|
|
|
if type_in == 'NS':
|
2017-06-13 19:55:19 +00:00
|
|
|
ns = record.get('values', [])
|
2015-02-20 17:22:03 +00:00
|
|
|
else:
|
|
|
|
# Retrieve name servers associated to the zone.
|
2017-02-10 18:34:50 +00:00
|
|
|
z = invoke_with_throttling_retries(conn.get_zone, zone_in)
|
|
|
|
ns = invoke_with_throttling_retries(z.get_nameservers)
|
2015-02-20 17:22:03 +00:00
|
|
|
|
|
|
|
module.exit_json(changed=False, set=record, nameservers=ns)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
if command_in == 'delete' and not found_record:
|
|
|
|
module.exit_json(changed=False)
|
|
|
|
|
2015-05-05 15:07:18 +00:00
|
|
|
changes = ResourceRecordSets(conn, zone.id)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
if command_in == 'create' or command_in == 'delete':
|
2015-05-05 15:07:18 +00:00
|
|
|
if command_in == 'create' and found_record:
|
|
|
|
if not module.params['overwrite']:
|
2017-04-18 15:49:25 +00:00
|
|
|
module.fail_json(msg="Record already exists with different value. Set 'overwrite' to replace it")
|
2015-05-05 15:07:18 +00:00
|
|
|
command = 'UPSERT'
|
|
|
|
else:
|
|
|
|
command = command_in.upper()
|
2018-09-27 19:08:47 +00:00
|
|
|
# Restore original order of records
|
|
|
|
wanted_rset.resource_records = unsorted_records
|
2015-05-05 15:07:18 +00:00
|
|
|
changes.add_change_record(command, wanted_rset)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2018-05-03 12:29:57 +00:00
|
|
|
if not module.check_mode:
|
|
|
|
try:
|
|
|
|
invoke_with_throttling_retries(commit, changes, retry_interval_in, wait_in, wait_timeout_in)
|
|
|
|
except boto.route53.exception.DNSServerError as e:
|
|
|
|
txt = e.body.split("<Message>")[1]
|
|
|
|
txt = txt.split("</Message>")[0]
|
|
|
|
if "but it already exists" in txt:
|
|
|
|
module.exit_json(changed=False)
|
|
|
|
else:
|
|
|
|
module.fail_json(msg=txt)
|
|
|
|
except TimeoutError:
|
|
|
|
module.fail_json(msg='Timeout waiting for changes to replicate')
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
module.exit_json(changed=True)
|
|
|
|
|
2017-08-12 16:27:11 +00:00
|
|
|
|
2016-12-05 17:08:15 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|