mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-26 04:11:25 -07:00
* Filter DNSimple request by record name. The request was not filtered and DNSimple returns only the first 100 records so if the number of records is larger the check could fail. This patch fixes the issue and also makes the check to perform better. * Add changelog fragment.
332 lines
12 KiB
Python
332 lines
12 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# Copyright: Ansible Project
|
|
#
|
|
# 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
|
|
|
|
|
|
ANSIBLE_METADATA = {
|
|
'metadata_version': '1.1',
|
|
'status': ['preview'],
|
|
'supported_by': 'community'
|
|
}
|
|
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: dnsimple
|
|
version_added: "1.6"
|
|
short_description: Interface with dnsimple.com (a DNS hosting service)
|
|
description:
|
|
- "Manages domains and records via the DNSimple API, see the docs: U(http://developer.dnsimple.com/)."
|
|
notes:
|
|
- DNSimple API v1 is deprecated. Please install dnsimple-python>=1.0.0 which uses v2 API.
|
|
options:
|
|
account_email:
|
|
description:
|
|
- Account email. If omitted, the environment variables C(DNSIMPLE_EMAIL) and C(DNSIMPLE_API_TOKEN) will be looked for.
|
|
- "If those aren't found, a C(.dnsimple) file will be looked for, see: U(https://github.com/mikemaccana/dnsimple-python#getting-started)."
|
|
account_api_token:
|
|
description:
|
|
- Account API token. See I(account_email) for more information.
|
|
domain:
|
|
description:
|
|
- Domain to work with. Can be the domain name (e.g. "mydomain.com") or the numeric ID of the domain in DNSimple.
|
|
- If omitted, a list of domains will be returned.
|
|
- If domain is present but the domain doesn't exist, it will be created.
|
|
record:
|
|
description:
|
|
- Record to add, if blank a record for the domain will be created, supports the wildcard (*).
|
|
record_ids:
|
|
description:
|
|
- List of records to ensure they either exist or do not exist.
|
|
type:
|
|
description:
|
|
- The type of DNS record to create.
|
|
choices: [ 'A', 'ALIAS', 'CNAME', 'MX', 'SPF', 'URL', 'TXT', 'NS', 'SRV', 'NAPTR', 'PTR', 'AAAA', 'SSHFP', 'HINFO', 'POOL' ]
|
|
ttl:
|
|
description:
|
|
- The TTL to give the new record in seconds.
|
|
default: 3600
|
|
value:
|
|
description:
|
|
- Record value.
|
|
- Must be specified when trying to ensure a record exists.
|
|
priority:
|
|
description:
|
|
- Record priority.
|
|
state:
|
|
description:
|
|
- whether the record should exist or not.
|
|
choices: [ 'present', 'absent' ]
|
|
solo:
|
|
description:
|
|
- Whether the record should be the only one for that record type and record name.
|
|
- Only use with C(state) is set to C(present) on a record.
|
|
type: 'bool'
|
|
requirements:
|
|
- "dnsimple >= 1.0.0"
|
|
author: "Alex Coomans (@drcapulet)"
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
- name: Authenticate using email and API token and fetch all domains
|
|
dnsimple:
|
|
account_email: test@example.com
|
|
account_api_token: dummyapitoken
|
|
delegate_to: localhost
|
|
|
|
- name: Fetch my.com domain records
|
|
dnsimple:
|
|
domain: my.com
|
|
state: present
|
|
delegate_to: localhost
|
|
register: records
|
|
|
|
- name: Delete a domain
|
|
dnsimple:
|
|
domain: my.com
|
|
state: absent
|
|
delegate_to: localhost
|
|
|
|
- name: Create a test.my.com A record to point to 127.0.0.1
|
|
dnsimple:
|
|
domain: my.com
|
|
record: test
|
|
type: A
|
|
value: 127.0.0.1
|
|
delegate_to: localhost
|
|
register: record
|
|
|
|
- name: Delete record using record_ids
|
|
dnsimple:
|
|
domain: my.com
|
|
record_ids: '{{ record["id"] }}'
|
|
state: absent
|
|
delegate_to: localhost
|
|
|
|
- name: Create a my.com CNAME record to example.com
|
|
dnsimple:
|
|
domain: my.com
|
|
record: ''
|
|
type: CNAME
|
|
value: example.com
|
|
state: present
|
|
delegate_to: localhost
|
|
|
|
- name: change TTL value for a record
|
|
dnsimple:
|
|
domain: my.com
|
|
record: ''
|
|
type: CNAME
|
|
value: example.com
|
|
ttl: 600
|
|
state: present
|
|
delegate_to: localhost
|
|
|
|
- name: Delete the record
|
|
dnsimple:
|
|
domain: my.com
|
|
record: ''
|
|
type: CNAME
|
|
value: example.com
|
|
state: absent
|
|
delegate_to: localhost
|
|
'''
|
|
|
|
RETURN = r"""# """
|
|
|
|
import os
|
|
import traceback
|
|
from distutils.version import LooseVersion
|
|
|
|
DNSIMPLE_IMP_ERR = None
|
|
try:
|
|
from dnsimple import DNSimple
|
|
from dnsimple.dnsimple import __version__ as dnsimple_version
|
|
from dnsimple.dnsimple import DNSimpleException
|
|
HAS_DNSIMPLE = True
|
|
except ImportError:
|
|
DNSIMPLE_IMP_ERR = traceback.format_exc()
|
|
HAS_DNSIMPLE = False
|
|
|
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
account_email=dict(required=False),
|
|
account_api_token=dict(required=False, no_log=True),
|
|
domain=dict(required=False),
|
|
record=dict(required=False),
|
|
record_ids=dict(required=False, type='list'),
|
|
type=dict(required=False, choices=['A', 'ALIAS', 'CNAME', 'MX', 'SPF', 'URL', 'TXT', 'NS', 'SRV', 'NAPTR', 'PTR', 'AAAA', 'SSHFP', 'HINFO',
|
|
'POOL']),
|
|
ttl=dict(required=False, default=3600, type='int'),
|
|
value=dict(required=False),
|
|
priority=dict(required=False, type='int'),
|
|
state=dict(required=False, choices=['present', 'absent']),
|
|
solo=dict(required=False, type='bool'),
|
|
),
|
|
required_together=[
|
|
['record', 'value']
|
|
],
|
|
supports_check_mode=True,
|
|
)
|
|
|
|
if not HAS_DNSIMPLE:
|
|
module.fail_json(msg=missing_required_lib('dnsimple'), exception=DNSIMPLE_IMP_ERR)
|
|
|
|
if LooseVersion(dnsimple_version) < LooseVersion('1.0.0'):
|
|
module.fail_json(msg="Current version of dnsimple Python module [%s] uses 'v1' API which is deprecated."
|
|
" Please upgrade to version 1.0.0 and above to use dnsimple 'v2' API." % dnsimple_version)
|
|
|
|
account_email = module.params.get('account_email')
|
|
account_api_token = module.params.get('account_api_token')
|
|
domain = module.params.get('domain')
|
|
record = module.params.get('record')
|
|
record_ids = module.params.get('record_ids')
|
|
record_type = module.params.get('type')
|
|
ttl = module.params.get('ttl')
|
|
value = module.params.get('value')
|
|
priority = module.params.get('priority')
|
|
state = module.params.get('state')
|
|
is_solo = module.params.get('solo')
|
|
|
|
if account_email and account_api_token:
|
|
client = DNSimple(email=account_email, api_token=account_api_token)
|
|
elif os.environ.get('DNSIMPLE_EMAIL') and os.environ.get('DNSIMPLE_API_TOKEN'):
|
|
client = DNSimple(email=os.environ.get('DNSIMPLE_EMAIL'), api_token=os.environ.get('DNSIMPLE_API_TOKEN'))
|
|
else:
|
|
client = DNSimple()
|
|
|
|
try:
|
|
# Let's figure out what operation we want to do
|
|
|
|
# No domain, return a list
|
|
if not domain:
|
|
domains = client.domains()
|
|
module.exit_json(changed=False, result=[d['domain'] for d in domains])
|
|
|
|
# Domain & No record
|
|
if domain and record is None and not record_ids:
|
|
domains = [d['domain'] for d in client.domains()]
|
|
if domain.isdigit():
|
|
dr = next((d for d in domains if d['id'] == int(domain)), None)
|
|
else:
|
|
dr = next((d for d in domains if d['name'] == domain), None)
|
|
if state == 'present':
|
|
if dr:
|
|
module.exit_json(changed=False, result=dr)
|
|
else:
|
|
if module.check_mode:
|
|
module.exit_json(changed=True)
|
|
else:
|
|
module.exit_json(changed=True, result=client.add_domain(domain)['domain'])
|
|
elif state == 'absent':
|
|
if dr:
|
|
if not module.check_mode:
|
|
client.delete(domain)
|
|
module.exit_json(changed=True)
|
|
else:
|
|
module.exit_json(changed=False)
|
|
else:
|
|
module.fail_json(msg="'%s' is an unknown value for the state argument" % state)
|
|
|
|
# need the not none check since record could be an empty string
|
|
if domain and record is not None:
|
|
records = [r['record'] for r in client.records(str(domain), params={'name': record})]
|
|
|
|
if not record_type:
|
|
module.fail_json(msg="Missing the record type")
|
|
|
|
if not value:
|
|
module.fail_json(msg="Missing the record value")
|
|
|
|
rr = next((r for r in records if r['name'] == record and r['type'] == record_type and r['content'] == value), None)
|
|
|
|
if state == 'present':
|
|
changed = False
|
|
if is_solo:
|
|
# delete any records that have the same name and record type
|
|
same_type = [r['id'] for r in records if r['name'] == record and r['type'] == record_type]
|
|
if rr:
|
|
same_type = [rid for rid in same_type if rid != rr['id']]
|
|
if same_type:
|
|
if not module.check_mode:
|
|
for rid in same_type:
|
|
client.delete_record(str(domain), rid)
|
|
changed = True
|
|
if rr:
|
|
# check if we need to update
|
|
if rr['ttl'] != ttl or rr['priority'] != priority:
|
|
data = {}
|
|
if ttl:
|
|
data['ttl'] = ttl
|
|
if priority:
|
|
data['priority'] = priority
|
|
if module.check_mode:
|
|
module.exit_json(changed=True)
|
|
else:
|
|
module.exit_json(changed=True, result=client.update_record(str(domain), str(rr['id']), data)['record'])
|
|
else:
|
|
module.exit_json(changed=changed, result=rr)
|
|
else:
|
|
# create it
|
|
data = {
|
|
'name': record,
|
|
'type': record_type,
|
|
'content': value,
|
|
}
|
|
if ttl:
|
|
data['ttl'] = ttl
|
|
if priority:
|
|
data['priority'] = priority
|
|
if module.check_mode:
|
|
module.exit_json(changed=True)
|
|
else:
|
|
module.exit_json(changed=True, result=client.add_record(str(domain), data)['record'])
|
|
elif state == 'absent':
|
|
if rr:
|
|
if not module.check_mode:
|
|
client.delete_record(str(domain), rr['id'])
|
|
module.exit_json(changed=True)
|
|
else:
|
|
module.exit_json(changed=False)
|
|
else:
|
|
module.fail_json(msg="'%s' is an unknown value for the state argument" % state)
|
|
|
|
# Make sure these record_ids either all exist or none
|
|
if domain and record_ids:
|
|
current_records = [str(r['record']['id']) for r in client.records(str(domain))]
|
|
wanted_records = [str(r) for r in record_ids]
|
|
if state == 'present':
|
|
difference = list(set(wanted_records) - set(current_records))
|
|
if difference:
|
|
module.fail_json(msg="Missing the following records: %s" % difference)
|
|
else:
|
|
module.exit_json(changed=False)
|
|
elif state == 'absent':
|
|
difference = list(set(wanted_records) & set(current_records))
|
|
if difference:
|
|
if not module.check_mode:
|
|
for rid in difference:
|
|
client.delete_record(str(domain), rid)
|
|
module.exit_json(changed=True)
|
|
else:
|
|
module.exit_json(changed=False)
|
|
else:
|
|
module.fail_json(msg="'%s' is an unknown value for the state argument" % state)
|
|
|
|
except DNSimpleException as e:
|
|
module.fail_json(msg="Unable to contact DNSimple: %s" % e.message)
|
|
|
|
module.fail_json(msg="Unknown what you wanted me to do")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|