Skip to content

Commit

Permalink
Allow to specify zone by ID instead of name (#7)
Browse files Browse the repository at this point in the history
* Allow to specify zone by ID instead of name.

* Handle 403 (Forbidden) as well.

* Handle empty error messages better, which seems to be common in errors from the real API.

* Forgot version_added, improve indentation.

* Fix syntax error.
  • Loading branch information
felixfontein committed Apr 24, 2021
1 parent 5c651f2 commit da53182
Show file tree
Hide file tree
Showing 11 changed files with 316 additions and 74 deletions.
3 changes: 3 additions & 0 deletions changelogs/fragments/7-hosttech-zone_id.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minor_changes:
- "hosttech_dns_record* modules - return ``zone_id`` on success (https://github.com/ansible-collections/community.dns/pull/7)."
- "hosttech_dns_record* modules - allow to specify zone by ID with the ``zone_id`` parameter, alternatively to the ``zone`` parameter (https://github.com/ansible-collections/community.dns/pull/7)."
31 changes: 18 additions & 13 deletions plugins/doc_fragments/module_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,50 @@ class ModuleDocFragment(object):
options:
state:
description:
- Specifies the state of the resource record.
- Specifies the state of the resource record.
required: true
choices: ['present', 'absent']
type: str
zone:
description:
- The DNS zone to modify.
required: true
- The DNS zone to modify.
- Exactly one of I(zone) and I(zone_id) must be specified.
type: str
zone_id:
description:
- The ID of the DNS zone to modify.
- Exactly one of I(zone) and I(zone_id) must be specified.
version_added: 0.2.0
record:
description:
- The full DNS record to create or delete.
- The full DNS record to create or delete.
required: true
type: str
ttl:
description:
- The TTL to give the new record, in seconds.
- The TTL to give the new record, in seconds.
default: 3600
type: int
type:
description:
- The type of DNS record to create or delete.
- The type of DNS record to create or delete.
required: true
choices: ['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'CAA']
type: str
value:
description:
- The new value when creating a DNS record.
- YAML lists or multiple comma-spaced values are allowed.
- When deleting a record all values for the record must be specified or it will
not be deleted.
- The new value when creating a DNS record.
- YAML lists or multiple comma-spaced values are allowed.
- When deleting a record all values for the record must be specified or it will
not be deleted.
required: true
type: list
elements: str
overwrite:
description:
- If I(state=present), whether an existing record should be overwritten on create if values do not
match.
- If I(state=absent), whether existing records should be deleted if values do not match.
- If I(state=present), whether an existing record should be overwritten on create if values do not
match.
- If I(state=absent), whether existing records should be deleted if values do not match.
default: false
type: bool
Expand Down
23 changes: 14 additions & 9 deletions plugins/doc_fragments/module_record_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,31 @@ class ModuleDocFragment(object):
options:
what:
description:
- Describes whether to fetch a single record and type combination, all types for a
record, or all records. By default, a single record and type combination is fetched.
- Note that the return value structure depends on this option.
- Describes whether to fetch a single record and type combination, all types for a
record, or all records. By default, a single record and type combination is fetched.
- Note that the return value structure depends on this option.
choices: ['single_record', 'all_types_for_record', 'all_records']
default: single_record
type: str
zone:
description:
- The DNS zone to modify.
required: yes
- The DNS zone to modify.
- Exactly one of I(zone) and I(zone_id) must be specified.
type: str
zone_id:
description:
- The ID of the DNS zone to modify.
- Exactly one of I(zone) and I(zone_id) must be specified.
version_added: 0.2.0
record:
description:
- The full DNS record to retrieve.
- Required if I(what) is C(single_record) or C(all_types_for_record).
- The full DNS record to retrieve.
- Required if I(what) is C(single_record) or C(all_types_for_record).
type: str
type:
description:
- The type of DNS record to retrieve.
- Required if I(what) is C(single_record).
- The type of DNS record to retrieve.
- Required if I(what) is C(single_record).
choices: ['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'CAA']
type: str
Expand Down
7 changes: 6 additions & 1 deletion plugins/module_utils/argspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,35 @@


class ArgumentSpec(object):
def __init__(self, argument_spec=None, required_together=None, required_if=None, mutually_exclusive=None):
def __init__(self, argument_spec=None, required_together=None, required_if=None, required_one_of=None, mutually_exclusive=None):
self.argument_spec = {}
self.required_together = []
self.required_if = []
self.required_one_of = []
self.mutually_exclusive = []
if argument_spec:
self.argument_spec.update(argument_spec)
if required_together:
self.required_together.extend(required_together)
if required_if:
self.required_if.extend(required_if)
if required_one_of:
self.required_one_of.extend(required_one_of)
if mutually_exclusive:
self.mutually_exclusive.extend(mutually_exclusive)

def merge(self, other):
self.argument_spec.update(other.argument_spec)
self.required_together.extend(other.required_together)
self.required_if.extend(other.required_if)
self.required_one_of.extend(other.required_one_of)
self.mutually_exclusive.extend(other.mutually_exclusive)

def to_kwargs(self):
return {
'argument_spec': self.argument_spec,
'required_together': self.required_together,
'required_if': self.required_if,
'required_one_of': self.required_one_of,
'mutually_exclusive': self.mutually_exclusive,
}
17 changes: 14 additions & 3 deletions plugins/module_utils/hosttech/json_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def _extract_error_message(self, result):
return ''
if isinstance(result, dict):
res = ''
if 'message' in result:
if result.get('message'):
res = '{0} with message "{1}"'.format(res, result['message'])
if 'errors' in result:
if isinstance(result['errors'], dict):
Expand Down Expand Up @@ -222,9 +222,20 @@ def _process_json_result(self, response, info, must_have_content=True, method='G
content = info.pop('body', None)
# Check for unauthenticated
if info['status'] == 401:
message = 'Unauthorized (HTTP status 401)'
message = 'Unauthorized: the authentication parameters are incorrect (HTTP status 401)'
try:
message = '{0}: {1}'.format(message, self._module.from_json(content.decode('utf8'))['message'])
body = self._module.from_json(content.decode('utf8'))
if body['message']:
message = '{0}: {1}'.format(message, body['message'])
except Exception:
pass
raise DNSAPIAuthenticationError(message)
if info['status'] == 403:
message = 'Forbidden: you do not have access to this resource (HTTP status 403)'
try:
body = self._module.from_json(content.decode('utf8'))
if body['message']:
message = '{0}: {1}'.format(message, body['message'])
except Exception:
pass
raise DNSAPIAuthenticationError(message)
Expand Down
42 changes: 30 additions & 12 deletions plugins/module_utils/module/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,34 +31,46 @@
)


def create_module_argument_spec():
def create_module_argument_spec(zone_id_type='str'):
return ArgumentSpec(
argument_spec=dict(
state=dict(type='str', choices=['present', 'absent'], required=True),
zone=dict(type='str', required=True),
zone=dict(type='str'),
zone_id=dict(type=zone_id_type),
record=dict(type='str', required=True),
ttl=dict(type='int', default=3600),
type=dict(choices=['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'CAA'], required=True),
value=dict(required=True, type='list', elements='str'),
overwrite=dict(default=False, type='bool'),
),
required_one_of=[
('zone', 'zone_id'),
],
mutually_exclusive=[
('zone', 'zone_id'),
],
)


def run_module(module, create_api):
# Get zone and record
zone_in = normalize_dns_name(module.params.get('zone'))
record_in = normalize_dns_name(module.params.get('record'))

try:
# Convert record to prefix
prefix = get_prefix(record_in, zone_in)
# Create API
api = create_api()

# Get zone information
zone = api.get_zone_with_records_by_name(zone_in)
if zone is None:
module.fail_json(msg='Zone not found')
if module.params.get('zone') is not None:
zone_in = normalize_dns_name(module.params.get('zone'))
prefix = get_prefix(record_in, zone_in)
zone = api.get_zone_with_records_by_name(zone_in)
if zone is None:
module.fail_json(msg='Zone not found')
else:
zone = api.get_zone_with_records_by_id(module.params.get('zone_id'))
if zone is None:
module.fail_json(msg='Zone not found')
zone_in = normalize_dns_name(zone.zone.name)
prefix = get_prefix(record_in, zone_in)

# Find matching records
type_in = module.params.get('type')
Expand Down Expand Up @@ -128,7 +140,10 @@ def run_module(module, create_api):

# Is there nothing to change?
if len(to_create) == 0 and len(to_delete) == 0 and len(to_change) == 0:
module.exit_json(changed=False)
module.exit_json(
changed=False,
zone_id=zone.zone.id,
)

# Actually do something
if not module.check_mode:
Expand All @@ -139,7 +154,10 @@ def run_module(module, create_api):
for record in to_create:
api.add_record(zone.zone.id, record)

result = dict(changed=True)
result = dict(
changed=True,
zone_id=zone.zone.id,
)
if module._diff:
result['diff'] = dict(
before=format_records_for_output(sorted(before, key=lambda record: record.target), record_in) if before else dict(),
Expand Down
33 changes: 23 additions & 10 deletions plugins/module_utils/module/record_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,46 @@
)


def create_module_argument_spec():
def create_module_argument_spec(zone_id_type='str'):
return ArgumentSpec(
argument_spec=dict(
what=dict(type='str', choices=['single_record', 'all_types_for_record', 'all_records'], default='single_record'),
zone=dict(type='str', required=True),
zone=dict(type='str'),
zone_id=dict(type=zone_id_type),
record=dict(type='str', default=None),
type=dict(type='str', choices=['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'CAA'], default=None),
),
required_if=[
('what', 'single_record', ['record', 'type']),
('what', 'all_types_for_record', ['record']),
],
required_one_of=[
('zone', 'zone_id'),
],
mutually_exclusive=[
('zone', 'zone_id'),
],
)


def run_module(module, create_api):
# Get zone and record
zone_in = module.params.get('zone').lower()
if zone_in[-1:] == '.':
zone_in = zone_in[:-1]

try:
# Create API
api = create_api()

# Get zone information
zone = api.get_zone_with_records_by_name(zone_in)
if zone is None:
module.fail_json(msg='Zone not found')
if module.params.get('zone') is not None:
zone_in = normalize_dns_name(module.params.get('zone'))
zone = api.get_zone_with_records_by_name(zone_in)
if zone is None:
module.fail_json(msg='Zone not found')
else:
zone = api.get_zone_with_records_by_id(module.params.get('zone_id'))
if zone is None:
module.fail_json(msg='Zone not found')
zone_in = normalize_dns_name(zone.zone.name)

# Retrieve requested information
if module.params.get('what') == 'single_record':
# Extract prefix
record_in = normalize_dns_name(module.params.get('record'))
Expand All @@ -76,6 +87,7 @@ def run_module(module, create_api):
module.exit_json(
changed=False,
set=data,
zone_id=zone.zone.id,
)
else:
# Extract prefix if necessary
Expand Down Expand Up @@ -103,6 +115,7 @@ def run_module(module, create_api):
module.exit_json(
changed=False,
sets=data,
zone_id=zone.zone.id,
)
except DNSAPIAuthenticationError as e:
module.fail_json(msg='Cannot authenticate: {0}'.format(e), error=str(e), exception=traceback.format_exc())
Expand Down
14 changes: 12 additions & 2 deletions plugins/modules/hosttech_dns_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
- community.dns.hosttech
- community.dns.module_record
options:
zone_id:
type: int
author:
- Felix Fontein (@felixfontein)
'''
Expand Down Expand Up @@ -181,7 +185,13 @@
hosttech_password: bar
'''

RETURN = ''' # '''
RETURN = '''
zone_id:
description: The ID of the zone.
type: int
returned: success
version_added: 0.2.0
'''

from ansible.module_utils.basic import AnsibleModule

Expand All @@ -198,7 +208,7 @@

def main():
argument_spec = create_hosttech_argument_spec()
argument_spec.merge(create_module_argument_spec())
argument_spec.merge(create_module_argument_spec(zone_id_type='int'))
module = AnsibleModule(supports_check_mode=True, **argument_spec.to_kwargs())
run_module(module, lambda: create_hosttech_api(module))

Expand Down
Loading

0 comments on commit da53182

Please sign in to comment.