mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-11-16 01:50:39 +00:00
330 lines
12 KiB
Python
330 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
|
||
|
from distutils.version import LooseVersion
|
||
|
|
||
|
try:
|
||
|
from dnsimple import DNSimple
|
||
|
from dnsimple.dnsimple import __version__ as dnsimple_version
|
||
|
from dnsimple.dnsimple import DNSimpleException
|
||
|
HAS_DNSIMPLE = True
|
||
|
except ImportError:
|
||
|
HAS_DNSIMPLE = False
|
||
|
|
||
|
from ansible.module_utils.basic import AnsibleModule
|
||
|
|
||
|
|
||
|
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="dnsimple required for this module")
|
||
|
|
||
|
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()
|