summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/dns/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
commit7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch)
treeefb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/community/dns/tests
parentReleasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff)
downloadansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.tar.xz
ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.zip
Merging upstream version 9.4.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/dns/tests')
-rw-r--r--ansible_collections/community/dns/tests/ee/roles/wait_for_txt/tasks/main.yml8
-rwxr-xr-xansible_collections/community/dns/tests/integration/replace-requirements.sh10
-rw-r--r--ansible_collections/community/dns/tests/integration/requirements-stable-2.10.txt (renamed from ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.yml)7
-rw-r--r--ansible_collections/community/dns/tests/integration/requirements-stable-2.9.txt7
-rw-r--r--ansible_collections/community/dns/tests/integration/requirements.txt7
-rw-r--r--ansible_collections/community/dns/tests/integration/targets/lookup_lookup/aliases5
-rw-r--r--ansible_collections/community/dns/tests/integration/targets/lookup_lookup/tasks/main.yml26
-rw-r--r--ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/aliases5
-rw-r--r--ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/tasks/main.yml31
-rw-r--r--ansible_collections/community/dns/tests/integration/targets/nameserver_info/tasks/main.yml78
-rw-r--r--ansible_collections/community/dns/tests/integration/targets/nameserver_record_info/tasks/main.yml56
-rw-r--r--ansible_collections/community/dns/tests/integration/targets/wait_for_txt/aliases1
-rwxr-xr-xansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.sh16
-rw-r--r--ansible_collections/community/dns/tests/integration/targets/wait_for_txt/tasks/main.yml2
-rwxr-xr-xansible_collections/community/dns/tests/sanity/extra/extra-docs.py2
-rw-r--r--ansible_collections/community/dns/tests/sanity/ignore-2.10.txt7
-rw-r--r--ansible_collections/community/dns/tests/sanity/ignore-2.11.txt4
-rw-r--r--ansible_collections/community/dns/tests/sanity/ignore-2.12.txt4
-rw-r--r--ansible_collections/community/dns/tests/sanity/ignore-2.13.txt4
-rw-r--r--ansible_collections/community/dns/tests/sanity/ignore-2.14.txt4
-rw-r--r--ansible_collections/community/dns/tests/sanity/ignore-2.17.txt1
-rw-r--r--ansible_collections/community/dns/tests/sanity/ignore-2.17.txt.license3
-rw-r--r--ansible_collections/community/dns/tests/sanity/ignore-2.9.txt7
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/inventory/test_hetzner_dns_records.py3
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/inventory/test_hosttech_dns_records.py3
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup.py498
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup_as_dict.py461
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/module_utils/conversion/test_txt.py10
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/module_utils/resolver_helper.py18
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/module_utils/test_dnspython_records.py376
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/module_utils/test_ips.py62
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/module_utils/test_resolver.py541
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_info.py395
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_record_info.py592
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/modules/test_wait_for_txt.py58
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_ips.py39
-rw-r--r--ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_resolver.py36
-rwxr-xr-xansible_collections/community/dns/tests/unit/replace-requirements.sh10
-rw-r--r--ansible_collections/community/dns/tests/unit/requirements-stable-2.10.txt13
-rw-r--r--ansible_collections/community/dns/tests/unit/requirements-stable-2.9.txt13
-rw-r--r--ansible_collections/community/dns/tests/unit/requirements.txt2
41 files changed, 3371 insertions, 54 deletions
diff --git a/ansible_collections/community/dns/tests/ee/roles/wait_for_txt/tasks/main.yml b/ansible_collections/community/dns/tests/ee/roles/wait_for_txt/tasks/main.yml
index 43941d042..7c7de6b70 100644
--- a/ansible_collections/community/dns/tests/ee/roles/wait_for_txt/tasks/main.yml
+++ b/ansible_collections/community/dns/tests/ee/roles/wait_for_txt/tasks/main.yml
@@ -3,16 +3,12 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Get TXT records for github.com
- shell: |
- dig -t TXT github.com +short | sed -e 's/" "//g' -e 's/"//g'
- register: dig
-
- name: Wait for existing TXT entry
community.dns.wait_for_txt:
records:
- name: github.com
- values: "{{ dig.stdout_lines }}"
+ values: |
+ {{ query('community.dns.lookup_as_dict', 'github.com', type='TXT') | map(attribute='value') | list }}
- name: github.io
values: []
query_timeout: 20
diff --git a/ansible_collections/community/dns/tests/integration/replace-requirements.sh b/ansible_collections/community/dns/tests/integration/replace-requirements.sh
new file mode 100755
index 000000000..c3a52f5d1
--- /dev/null
+++ b/ansible_collections/community/dns/tests/integration/replace-requirements.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+DIRECTORY="$(dirname "$0")"
+
+if [ -e "${DIRECTORY}/$1" ]; then
+ cp "${DIRECTORY}/$1" "${DIRECTORY}/requirements.txt"
+fi
diff --git a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.yml b/ansible_collections/community/dns/tests/integration/requirements-stable-2.10.txt
index bcecb88e9..af90be668 100644
--- a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.yml
+++ b/ansible_collections/community/dns/tests/integration/requirements-stable-2.10.txt
@@ -1,8 +1,7 @@
----
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- hosts: localhost
- roles:
- - { role: wait_for_txt }
+ipaddress ; python_version < '3.3'
+
+dnspython < 2.4.0
diff --git a/ansible_collections/community/dns/tests/integration/requirements-stable-2.9.txt b/ansible_collections/community/dns/tests/integration/requirements-stable-2.9.txt
new file mode 100644
index 000000000..af90be668
--- /dev/null
+++ b/ansible_collections/community/dns/tests/integration/requirements-stable-2.9.txt
@@ -0,0 +1,7 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+ipaddress ; python_version < '3.3'
+
+dnspython < 2.4.0
diff --git a/ansible_collections/community/dns/tests/integration/requirements.txt b/ansible_collections/community/dns/tests/integration/requirements.txt
new file mode 100644
index 000000000..5e4bca63f
--- /dev/null
+++ b/ansible_collections/community/dns/tests/integration/requirements.txt
@@ -0,0 +1,7 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+ipaddress ; python_version < '3.3'
+
+dnspython
diff --git a/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/aliases b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/aliases
new file mode 100644
index 000000000..abc0d5476
--- /dev/null
+++ b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+shippable/posix/group1
diff --git a/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/tasks/main.yml
new file mode 100644
index 000000000..52ba9a46c
--- /dev/null
+++ b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/tasks/main.yml
@@ -0,0 +1,26 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Look up some records
+ ansible.builtin.set_fact:
+ ansible_a: >-
+ {{ query('community.dns.lookup', 'ansible.com', type='A') }}
+ ansible_aaaa: >-
+ {{ query('community.dns.lookup', 'ansible.com', type='AAAA', server='9.9.9.9') }}
+ ansible_txt: >-
+ {{ query('community.dns.lookup', 'ansible.com', type='TXT', server=['1.1.1.1', '8.8.8.8', 'dns9.quad9.net.']) }}
+ ansible_empty: >-
+ {{ query('community.dns.lookup', 'does-not-exist.ansible.com') }}
+
+- name: Check results
+ assert:
+ that:
+ - ansible_a | length > 0
+ - ansible_a[0] is regex("^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$")
+ - ansible_aaaa | length > 0
+ - ansible_aaaa[0] is regex("^[0-9a-fA-F:]+$")
+ - ansible_txt | length > 0
+ - ansible_txt[0] is string
+ - ansible_empty | length == 0
diff --git a/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/aliases b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/aliases
new file mode 100644
index 000000000..abc0d5476
--- /dev/null
+++ b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+shippable/posix/group1
diff --git a/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/tasks/main.yml
new file mode 100644
index 000000000..3ba6f52f0
--- /dev/null
+++ b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/tasks/main.yml
@@ -0,0 +1,31 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Look up some records
+ ansible.builtin.set_fact:
+ ansible_a: >-
+ {{ query('community.dns.lookup_as_dict', 'ansible.com', type='A') }}
+ ansible_aaaa: >-
+ {{ query('community.dns.lookup_as_dict', 'ansible.com', type='AAAA', server='9.9.9.9') }}
+ ansible_txt: >-
+ {{ query('community.dns.lookup_as_dict', 'ansible.com', type='TXT', server=['1.1.1.1', '8.8.8.8', 'dns9.quad9.net.']) }}
+ ansible_empty: >-
+ {{ query('community.dns.lookup_as_dict', 'does-not-exist.ansible.com') }}
+
+- name: Check results
+ assert:
+ that:
+ - ansible_a | length > 0
+ - ansible_a[0].address is string
+ - ansible_a[0].address is regex("^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$")
+ - ansible_aaaa | length > 0
+ - ansible_aaaa[0].address is string
+ - ansible_aaaa[0].address is regex("^[0-9a-fA-F:]+$")
+ - ansible_txt | length > 0
+ - ansible_txt[0].strings is sequence
+ - ansible_txt[0].strings[0] is string
+ - ansible_txt[0].value is string
+ - ansible_txt | map(attribute='strings') | map('join') | list == ansible_txt | map(attribute='value') | list
+ - ansible_empty | length == 0
diff --git a/ansible_collections/community/dns/tests/integration/targets/nameserver_info/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/nameserver_info/tasks/main.yml
new file mode 100644
index 000000000..0b074df5a
--- /dev/null
+++ b/ansible_collections/community/dns/tests/integration/targets/nameserver_info/tasks/main.yml
@@ -0,0 +1,78 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Retrieve name servers of two DNS names
+ community.dns.nameserver_info:
+ name:
+ - www.example.com
+ - example.org
+ register: result
+
+- name: Show TXT values for www.example.com for all nameservers
+ ansible.builtin.debug:
+ msg: '{{ result }}'
+
+- name: Show nameservers for www.example.com
+ ansible.builtin.debug:
+ msg: '{{ result.results[0].nameservers }}'
+
+- name: Show TXT values for www.example.com for all nameservers
+ ansible.builtin.debug:
+ msg: '{{ result.results[0].nameservers[0] }}'
+
+- name: Validate results
+ assert:
+ that:
+ - result.results[0].nameservers[0] == 'a.iana-servers.net.'
+ - result.results[0].nameservers[1] == 'b.iana-servers.net.'
+ - result.results[0].nameservers[0] != 'b.iana-servers.net.'
+
+- name: Retrieve name servers of two DNS names using custom DNS servers
+ community.dns.nameserver_info:
+ name:
+ - www.example.com
+ - example.org
+ server:
+ # Quad9 servers (https://en.wikipedia.org/wiki/Quad9#Service)
+ - 9.9.9.9
+ - 149.112.112.112
+ - 2620:fe::9
+ - 2620:fe::fe
+ register: result
+
+- name: Show TXT values for www.example.com for all nameservers
+ ansible.builtin.debug:
+ msg: '{{ result }}'
+
+- name: Show nameservers for www.example.com
+ ansible.builtin.debug:
+ msg: '{{ result.results[0].nameservers }}'
+
+- name: Show TXT values for www.example.com for first nameserver
+ ansible.builtin.debug:
+ msg: '{{ result.results[0].nameservers[0] }}'
+
+- name: Validate results
+ assert:
+ that:
+ - result.results[0].nameservers[0] == 'a.iana-servers.net.'
+ - result.results[0].nameservers[1] == 'b.iana-servers.net.'
+ - result.results[0].nameservers[0] != 'b.iana-servers.net.'
+
+- name: Retrieve name servers of a DNS name that do not exist
+ community.dns.nameserver_info:
+ name:
+ - foo.bar.example.com
+ register: result
+
+- name: Show TXT values for www.example.com for all nameservers
+ ansible.builtin.debug:
+ msg: '{{ result }}'
+
+- name: Validate results
+ assert:
+ that:
+ - result.results[0].nameservers[0] == 'a.iana-servers.net.'
+ - result.results[0].nameservers[1] == 'b.iana-servers.net.'
diff --git a/ansible_collections/community/dns/tests/integration/targets/nameserver_record_info/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/nameserver_record_info/tasks/main.yml
new file mode 100644
index 000000000..b03b641fe
--- /dev/null
+++ b/ansible_collections/community/dns/tests/integration/targets/nameserver_record_info/tasks/main.yml
@@ -0,0 +1,56 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Retrieve TXT values from all nameservers for two DNS names
+ community.dns.nameserver_record_info:
+ name:
+ - www.example.com
+ - example.org
+ type: TXT
+ register: result
+
+- name: Show TXT values for www.example.com for all nameservers
+ ansible.builtin.debug:
+ msg: '{{ result }}'
+
+- name: Show TXT values for www.example.com for first nameserver
+ ansible.builtin.debug:
+ msg: '{{ result.results[0].result[0].nameserver }}'
+
+- name: Validate results
+ assert:
+ that:
+ - result.results[0].result[0].nameserver == 'a.iana-servers.net.'
+ - result.results[0].result[1].nameserver == 'b.iana-servers.net.'
+ - result.results[0].result[0].nameserver != 'b.iana-servers.net.'
+
+- name: Retrieve TXT values from all nameservers for two DNS names using custom DNS servers
+ community.dns.nameserver_record_info:
+ name:
+ - www.example.com
+ - example.org
+ type: TXT
+ server:
+ # Quad9 servers (https://en.wikipedia.org/wiki/Quad9#Service)
+ - 9.9.9.9
+ - 149.112.112.112
+ - 2620:fe::9
+ - 2620:fe::fe
+ register: result
+
+- name: Show TXT values for www.example.com for all nameservers
+ ansible.builtin.debug:
+ msg: '{{ result }}'
+
+- name: Show TXT values for www.example.com for all nameservers
+ ansible.builtin.debug:
+ msg: '{{ result.results[0].result[0].nameserver }}'
+
+- name: Validate results
+ assert:
+ that:
+ - result.results[0].result[0].nameserver == 'a.iana-servers.net.'
+ - result.results[0].result[1].nameserver == 'b.iana-servers.net.'
+ - result.results[0].result[0].nameserver != 'b.iana-servers.net.'
diff --git a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/aliases b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/aliases
index 34334fd82..abc0d5476 100644
--- a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/aliases
+++ b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
shippable/posix/group1
-destructive
diff --git a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.sh b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.sh
deleted file mode 100755
index 9265e7ee1..000000000
--- a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-set -eux
-
-export ANSIBLE_TEST_PREFER_VENV=1 # see https://github.com/ansible/ansible/pull/73000#issuecomment-757012395; can be removed once Ansible 2.9 and ansible-base 2.10 support has been dropped
-source virtualenv.sh
-
-# Requirements have to be installed prior to running ansible-playbook
-# because plugins and requirements are loaded before the task runs
-
-pip install dnspython
-
-ANSIBLE_ROLES_PATH=../ ansible-playbook runme.yml "$@"
diff --git a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/tasks/main.yml
index fce443423..b706cfcae 100644
--- a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/tasks/main.yml
+++ b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/tasks/main.yml
@@ -8,7 +8,7 @@
records:
- name: github.com
values: |
- {{ query('community.general.dig', 'github.com', 'qtype=TXT') | map('regex_replace', '" "', '') | list }}
+ {{ query('community.dns.lookup_as_dict', 'github.com', type='TXT') | map(attribute='value') | list }}
- name: github.io
values: []
query_timeout: 20
diff --git a/ansible_collections/community/dns/tests/sanity/extra/extra-docs.py b/ansible_collections/community/dns/tests/sanity/extra/extra-docs.py
index c636beb08..251e6d70f 100755
--- a/ansible_collections/community/dns/tests/sanity/extra/extra-docs.py
+++ b/ansible_collections/community/dns/tests/sanity/extra/extra-docs.py
@@ -17,7 +17,7 @@ def main():
suffix = ':{env}'.format(env=env["ANSIBLE_COLLECTIONS_PATH"]) if 'ANSIBLE_COLLECTIONS_PATH' in env else ''
env['ANSIBLE_COLLECTIONS_PATH'] = '{root}{suffix}'.format(root=os.path.dirname(os.path.dirname(os.path.dirname(os.getcwd()))), suffix=suffix)
p = subprocess.run(
- ['antsibull-docs', 'lint-collection-docs', '--plugin-docs', '--disallow-semantic-markup', '--skip-rstcheck', '.'],
+ ['antsibull-docs', 'lint-collection-docs', '--plugin-docs', '--skip-rstcheck', '.'],
env=env,
check=False,
)
diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.10.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.10.txt
index dc9da1161..14ec3bedc 100644
--- a/ansible_collections/community/dns/tests/sanity/ignore-2.10.txt
+++ b/ansible_collections/community/dns/tests/sanity/ignore-2.10.txt
@@ -1 +1,8 @@
+docs/docsite/rst/filter_guide.rst rstcheck
+docs/docsite/rst/hosttech_guide.rst rstcheck
+docs/docsite/rst/hetzner_guide.rst rstcheck
+plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation
plugins/public_suffix_list.dat no-smart-quotes
diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.11.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.11.txt
index dc9da1161..593a5082f 100644
--- a/ansible_collections/community/dns/tests/sanity/ignore-2.11.txt
+++ b/ansible_collections/community/dns/tests/sanity/ignore-2.11.txt
@@ -1 +1,5 @@
+plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation
plugins/public_suffix_list.dat no-smart-quotes
diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.12.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.12.txt
index dc9da1161..593a5082f 100644
--- a/ansible_collections/community/dns/tests/sanity/ignore-2.12.txt
+++ b/ansible_collections/community/dns/tests/sanity/ignore-2.12.txt
@@ -1 +1,5 @@
+plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation
plugins/public_suffix_list.dat no-smart-quotes
diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.13.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.13.txt
index dc9da1161..593a5082f 100644
--- a/ansible_collections/community/dns/tests/sanity/ignore-2.13.txt
+++ b/ansible_collections/community/dns/tests/sanity/ignore-2.13.txt
@@ -1 +1,5 @@
+plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation
plugins/public_suffix_list.dat no-smart-quotes
diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.14.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.14.txt
index dc9da1161..593a5082f 100644
--- a/ansible_collections/community/dns/tests/sanity/ignore-2.14.txt
+++ b/ansible_collections/community/dns/tests/sanity/ignore-2.14.txt
@@ -1 +1,5 @@
+plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation
plugins/public_suffix_list.dat no-smart-quotes
diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt
new file mode 100644
index 000000000..dc9da1161
--- /dev/null
+++ b/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt
@@ -0,0 +1 @@
+plugins/public_suffix_list.dat no-smart-quotes
diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt.license b/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt.license
new file mode 100644
index 000000000..edff8c768
--- /dev/null
+++ b/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt.license
@@ -0,0 +1,3 @@
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+SPDX-FileCopyrightText: Ansible Project
diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.9.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.9.txt
index dc9da1161..14ec3bedc 100644
--- a/ansible_collections/community/dns/tests/sanity/ignore-2.9.txt
+++ b/ansible_collections/community/dns/tests/sanity/ignore-2.9.txt
@@ -1 +1,8 @@
+docs/docsite/rst/filter_guide.rst rstcheck
+docs/docsite/rst/hosttech_guide.rst rstcheck
+docs/docsite/rst/hetzner_guide.rst rstcheck
+plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation
+plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation
plugins/public_suffix_list.dat no-smart-quotes
diff --git a/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hetzner_dns_records.py b/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hetzner_dns_records.py
index 3b01de586..bfaa4886d 100644
--- a/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hetzner_dns_records.py
+++ b/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hetzner_dns_records.py
@@ -12,6 +12,7 @@ import textwrap
from ansible import constants as C
from ansible.inventory.manager import InventoryManager
from ansible.module_utils.common.text.converters import to_native
+from ansible.utils.unsafe_proxy import AnsibleUnsafe
from ansible_collections.community.internal_test_tools.tests.unit.mock.path import mock_unfrackpath_noop
from ansible_collections.community.internal_test_tools.tests.unit.mock.loader import DictDataLoader
@@ -208,6 +209,8 @@ def test_inventory_file_simple(mocker):
assert im._inventory.get_host('*.example.com') in im._inventory.groups['ungrouped'].hosts
assert im._inventory.get_host('example.com').get_vars()['ansible_host'] == '1.2.3.4'
assert im._inventory.get_host('*.example.com').get_vars()['ansible_host'] == '1.2.3.5'
+ assert isinstance(im._inventory.get_host('example.com').get_vars()['ansible_host'], AnsibleUnsafe)
+ assert isinstance(im._inventory.get_host('*.example.com').get_vars()['ansible_host'], AnsibleUnsafe)
assert len(im._inventory.groups['ungrouped'].hosts) == 2
assert len(im._inventory.groups['all'].hosts) == 0
diff --git a/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hosttech_dns_records.py b/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hosttech_dns_records.py
index a7adb2c09..cefd02a06 100644
--- a/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hosttech_dns_records.py
+++ b/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hosttech_dns_records.py
@@ -12,6 +12,7 @@ import textwrap
from ansible import constants as C
from ansible.inventory.manager import InventoryManager
from ansible.module_utils.common.text.converters import to_native
+from ansible.utils.unsafe_proxy import AnsibleUnsafe
from ansible_collections.community.internal_test_tools.tests.unit.mock.path import mock_unfrackpath_noop
from ansible_collections.community.internal_test_tools.tests.unit.mock.loader import DictDataLoader
@@ -185,6 +186,8 @@ def test_inventory_file_simple(mocker):
assert im._inventory.get_host('*.example.com') in im._inventory.groups['ungrouped'].hosts
assert im._inventory.get_host('example.com').get_vars()['ansible_host'] == '1.2.3.4'
assert im._inventory.get_host('*.example.com').get_vars()['ansible_host'] == '1.2.3.5'
+ assert isinstance(im._inventory.get_host('example.com').get_vars()['ansible_host'], AnsibleUnsafe)
+ assert isinstance(im._inventory.get_host('*.example.com').get_vars()['ansible_host'], AnsibleUnsafe)
assert len(im._inventory.groups['ungrouped'].hosts) == 2
assert len(im._inventory.groups['all'].hosts) == 0
diff --git a/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup.py b/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup.py
new file mode 100644
index 000000000..0982d8704
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup.py
@@ -0,0 +1,498 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2023, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+import pytest
+
+from ansible.errors import AnsibleLookupError
+from ansible.plugins.loader import lookup_loader
+
+from ansible_collections.community.internal_test_tools.tests.unit.compat.unittest import TestCase
+from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock
+
+from ..module_utils.resolver_helper import (
+ mock_resolver,
+ mock_query_udp,
+ create_mock_answer,
+)
+
+# We need dnspython
+dns = pytest.importorskip('dns')
+
+
+class TestLookup(TestCase):
+ def setUp(self):
+ self.lookup = lookup_loader.get("community.dns.lookup")
+
+ def test_simple(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'www.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.2'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'])
+
+ print(result)
+ assert len(result) == 2
+ assert result[0] == '127.0.0.1'
+ assert result[1] == '127.0.0.2'
+
+ def test_retry_success(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'www.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.2'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'])
+
+ print(result)
+ assert len(result) == 2
+ assert result[0] == '127.0.0.1'
+ assert result[1] == '127.0.0.2'
+
+ def test_retry_fail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'])
+
+ print(exc.value.args[0])
+ assert exc.value.args[0].startswith('Unexpected DNS error for www.example.com: The DNS operation timed out after 10')
+
+ def test_simple_nxdomain(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'])
+
+ print(result)
+ assert len(result) == 0
+
+ def test_simple_nxdomain(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'])
+
+ print(result)
+ assert len(result) == 0
+
+ def test_simple_nxdomain_empty(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'], nxdomain_handling='empty')
+
+ print(result)
+ assert len(result) == 0
+
+ def test_simple_nxdomain_message(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'], nxdomain_handling='message')
+
+ print(result)
+ assert len(result) == 1
+ assert result[0] == 'NXDOMAIN'
+
+ def test_simple_nxdomain_fail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'], nxdomain_handling='fail')
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == 'Got NXDOMAIN when querying www.example.com'
+
+ def test_simple_servfail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'])
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == "Unexpected resolving error for www.example.com: Error SERVFAIL while querying ['1.1.1.1']"
+
+ def test_retry_servfail_success(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'], servfail_retries=2)
+
+ print(result)
+ assert len(result) == 0
+
+ def test_retry_servfail_fail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'], servfail_retries=2)
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == "Unexpected resolving error for www.example.com: Error SERVFAIL while querying ['1.1.1.1']"
+
+ def test_type(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, '"foo bar"'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'baz bam'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'bar'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['example.com'], type='TXT')
+
+ print(result)
+ assert len(result) == 3
+ assert result[0] == '"foo bar"'
+ assert result[1] == '"baz" "bam"'
+ assert result[2] == '"bar"'
+
+ def test_server(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('2.2.2.2', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'example.org'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'example.org',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::1'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['example.org'], type='AAAA', server=['2.2.2.2', '3.3.3.3'])
+
+ print(result)
+ assert len(result) == 1
+ assert result[0] == '::1'
+
+ def test_server_resolve(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '1.2.3.4'),
+ )),
+ },
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1::2'),
+ )),
+ },
+ ],
+ ('1.2.3.4', '1::2', '2.2.2.2', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'example.org'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'example.org',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::1'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['example.org'], type='AAAA', server=['2.2.2.2', 'ns.example.com', '3.3.3.3'])
+
+ print(result)
+ assert len(result) == 1
+ assert result[0] == '::1'
+
+ def test_server_resolve_nxdomain(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'], server=['2.2.2.2', 'ns.example.com'])
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == 'Unexpected DNS error for ns.example.com: The DNS query name does not exist: ns.example.com.'
+
+ def test_server_resolve_servfail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'], server=['2.2.2.2', 'ns.example.com'])
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == "Unexpected resolving error for ns.example.com: Error SERVFAIL while querying ['1.1.1.1']"
+
+ def test_server_resolve_empty_lifetime(self):
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 5,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 5,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ ],
+ ('3.3.3.3', ): [
+ {
+ 'target': dns.name.from_unicode(u'example.org'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 5,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(
+ ['example.org'],
+ type='AAAA',
+ server=['ns.example.com', '3.3.3.3'],
+ query_timeout=5,
+ )
+
+ print(result)
+ assert len(result) == 0
diff --git a/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup_as_dict.py b/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup_as_dict.py
new file mode 100644
index 000000000..f29d73143
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup_as_dict.py
@@ -0,0 +1,461 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2023, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+import pytest
+
+from ansible.errors import AnsibleLookupError
+from ansible.plugins.loader import lookup_loader
+
+from ansible_collections.community.internal_test_tools.tests.unit.compat.unittest import TestCase
+from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock
+
+from ..module_utils.resolver_helper import (
+ mock_resolver,
+ mock_query_udp,
+ create_mock_answer,
+)
+
+# We need dnspython
+dns = pytest.importorskip('dns')
+
+
+class TestLookupAsDict(TestCase):
+ def setUp(self):
+ self.lookup = lookup_loader.get("community.dns.lookup_as_dict")
+
+ def test_simple(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'www.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.2'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'])
+
+ print(result)
+ assert len(result) == 2
+ assert result[0] == {
+ 'address': '127.0.0.1',
+ }
+ assert result[1] == {
+ 'address': '127.0.0.2',
+ }
+
+ def test_retry_success(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'www.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.2'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'])
+
+ print(result)
+ assert len(result) == 2
+ assert result[0] == {
+ 'address': '127.0.0.1',
+ }
+ assert result[1] == {
+ 'address': '127.0.0.2',
+ }
+
+ def test_retry_fail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'raise': dns.exception.Timeout(timeout=10),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'])
+
+ print(exc.value.args[0])
+ assert exc.value.args[0].startswith('Unexpected DNS error for www.example.com: The DNS operation timed out after 10')
+
+ def test_simple_nxdomain_empty(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'], nxdomain_handling='empty')
+
+ print(result)
+ assert len(result) == 0
+
+ def test_simple_nxdomain_fail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'], nxdomain_handling='fail')
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == 'Got NXDOMAIN when querying www.example.com'
+
+ def test_simple_servfail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'])
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == "Unexpected resolving error for www.example.com: Error SERVFAIL while querying ['1.1.1.1']"
+
+ def test_retry_servfail_success(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['www.example.com'], servfail_retries=2)
+
+ print(result)
+ assert len(result) == 0
+
+ def test_retry_servfail_fail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'], servfail_retries=2)
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == "Unexpected resolving error for www.example.com: Error SERVFAIL while querying ['1.1.1.1']"
+
+ def test_type(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, '"foo bar"'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'baz bam'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'bar'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['example.com'], type='TXT')
+
+ print(result)
+ assert len(result) == 3
+ assert result[0] == {
+ 'strings': ['foo bar'],
+ 'value': 'foo bar',
+ }
+ assert result[1] == {
+ 'strings': ['baz', 'bam'],
+ 'value': 'bazbam',
+ }
+ assert result[2] == {
+ 'strings': ['bar'],
+ 'value': 'bar',
+ }
+
+ def test_server(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('2.2.2.2', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'example.org'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'example.org',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::1'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['example.org'], type='AAAA', server=['2.2.2.2', '3.3.3.3'])
+
+ print(result)
+ assert len(result) == 1
+ assert result[0] == {
+ 'address': '::1',
+ }
+
+ def test_server_resolve(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '1.2.3.4'),
+ )),
+ },
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1::2'),
+ )),
+ },
+ ],
+ ('1.2.3.4', '1::2', '2.2.2.2', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'example.org'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'example.org',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::1'),
+ )),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(['example.org'], type='AAAA', server=['2.2.2.2', 'ns.example.com', '3.3.3.3'])
+
+ print(result)
+ assert len(result) == 1
+ assert result[0] == {
+ 'address': '::1',
+ }
+
+ def test_server_resolve_nxdomain(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'], server=['2.2.2.2', 'ns.example.com'])
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == 'Unexpected DNS error for ns.example.com: The DNS query name does not exist: ns.example.com.'
+
+ def test_server_resolve_servfail(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ with pytest.raises(AnsibleLookupError) as exc:
+ self.lookup.run(['www.example.com'], server=['2.2.2.2', 'ns.example.com'])
+
+ print(exc.value.args[0])
+ assert exc.value.args[0] == "Unexpected resolving error for ns.example.com: Error SERVFAIL while querying ['1.1.1.1']"
+
+ def test_server_resolve_empty_lifetime(self):
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 5,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ {
+ 'target': dns.name.from_unicode(u'ns.example.com'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 5,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ ],
+ ('3.3.3.3', ): [
+ {
+ 'target': dns.name.from_unicode(u'example.org'),
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 5,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ ],
+ })
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp([])):
+ result = self.lookup.run(
+ ['example.org'],
+ type='AAAA',
+ server=['ns.example.com', '3.3.3.3'],
+ query_timeout=5,
+ )
+
+ print(result)
+ assert len(result) == 0
diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/conversion/test_txt.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/conversion/test_txt.py
index e5984444b..664b6db6d 100644
--- a/ansible_collections/community/dns/tests/unit/plugins/module_utils/conversion/test_txt.py
+++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/conversion/test_txt.py
@@ -138,6 +138,16 @@ TEST_ENCODE_DECODE = [
True, True, 'decimal'
),
(
+ # Avoid splitting up an escape into multiple TXT strings
+ u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzAB'
+ u'CDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123'
+ u'456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdef"\\',
+ u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzAB'
+ u'CDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123'
+ u'456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdef \\"\\\\',
+ False, True, 'decimal'
+ ),
+ (
# Avoid splitting up an decimal sequence into multiple TXT strings
u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzAB'
u'CDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123'
diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/resolver_helper.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/resolver_helper.py
index e410d2076..7fa213c9f 100644
--- a/ansible_collections/community/dns/tests/unit/plugins/module_utils/resolver_helper.py
+++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/resolver_helper.py
@@ -11,6 +11,11 @@ __metaclass__ = type
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import MagicMock
+try:
+ import dns.rcode
+except ImportError:
+ pass
+
def mock_resolver(default_nameservers, nameserver_resolve_sequence):
def create_resolver(configure=True):
@@ -59,15 +64,16 @@ def mock_query_udp(call_sequence):
return udp
-def create_mock_answer(rrset=None):
- answer = MagicMock()
- answer.rrset = rrset
- return answer
-
-
def create_mock_response(rcode, authority=None, answer=None):
response = MagicMock()
response.rcode = MagicMock(return_value=rcode)
response.authority = authority or []
response.answer = answer or []
return response
+
+
+def create_mock_answer(rrset=None, rcode=None):
+ answer = MagicMock()
+ answer.response = create_mock_response(dns.rcode.NOERROR if rcode is None else rcode, answer=[rrset] if rrset else None)
+ answer.rrset = rrset
+ return answer
diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_dnspython_records.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_dnspython_records.py
new file mode 100644
index 000000000..9594b8097
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_dnspython_records.py
@@ -0,0 +1,376 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2022, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+import pytest
+
+from ansible_collections.community.dns.plugins.module_utils.dnspython_records import (
+ RDTYPE_TO_FIELDS,
+ convert_rdata_to_dict,
+)
+
+# We need dnspython
+dns = pytest.importorskip('dns')
+
+import dns.version
+
+
+TEST_CONVERT_RDATA_TO_DICT = [
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ {'to_unicode': True, 'add_synthetic': False},
+ {
+ 'address': '3.3.3.3',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'),
+ {'to_unicode': True, 'add_synthetic': False},
+ {
+ 'address': '1:2::3',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::'),
+ {'to_unicode': False, 'add_synthetic': True},
+ {
+ 'address': '::',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CAA, '10 issue letsencrypt.org'),
+ {'to_unicode': True, 'add_synthetic': False},
+ {
+ 'flags': 10,
+ 'tag': 'issue',
+ 'value': 'letsencrypt.org',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'foo.example.com.'),
+ {'to_unicode': True, 'add_synthetic': False},
+ {
+ 'target': 'foo.example.com.',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNAME, 'foo.example.com.'),
+ {'to_unicode': True, 'add_synthetic': False},
+ {
+ 'target': 'foo.example.com.',
+ },
+ ),
+ (
+ dns.rdata.from_text(
+ dns.rdataclass.IN,
+ dns.rdatatype.DNSKEY,
+ '512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3'
+ ' GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o jqf0BaqHT+8=',
+ ),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'flags': 512,
+ 'algorithm': 1,
+ 'protocol': 255,
+ 'key': (
+ 'AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aRyzWZriO6i2odGWWQVucZqKVsENW9'
+ '1IOW4vqudngPZsY3GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5ojqf0BaqHT+8='
+ ),
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, '12345 3 1 123456789abcdef67890123456789abcdef67890'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'algorithm': 3,
+ 'digest_type': 1,
+ 'key_tag': 12345,
+ 'digest': '123456789abcdef67890123456789abcdef67890',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.HINFO, '"Generic PC clone" "NetBSD-1.4"'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'cpu': b'Generic PC clone',
+ 'os': b'NetBSD-1.4',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.LOC, '60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'latitude': [60, 9, 0, 0, 1],
+ 'longitude': [24, 39, 0, 0, 1],
+ 'altitude': 1000.0,
+ 'size': 2000.0,
+ 'horizontal_precision': 200000.0,
+ 'vertical_precision': 2000.0,
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '10 mail.example.com'),
+ {'to_unicode': True, 'add_synthetic': False},
+ {
+ 'preference': 10,
+ 'exchange': 'mail.example.com',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NAPTR, '65535 65535 "text 1" "text 2" "text 3" example.com.'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'order': 65535,
+ 'preference': 65535,
+ 'flags': b'text 1',
+ 'service': b'text 2',
+ 'regexp': b'text 3',
+ 'replacement': 'example.com.',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.org.'),
+ {'to_unicode': True, 'add_synthetic': False},
+ {
+ 'target': 'ns.example.org.',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC, 'a.secure A MX RRSIG NSEC TYPE1234'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'next': 'a.secure',
+ 'windows': 'A MX RRSIG NSEC TYPE1234',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC3, '1 1 123 f00baa23 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'algorithm': 1,
+ 'flags': 1,
+ 'iterations': 123,
+ 'salt': 'f00baa23',
+ 'next': '2t7b4g4vsa5smi47k61mv5bv1a22bojr',
+ 'windows': 'NS SOA MX RRSIG DNSKEY NSEC3PARAM',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC3PARAM, '1 1 123 f00baa23'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'algorithm': 1,
+ 'flags': 1,
+ 'iterations': 123,
+ 'salt': 'f00baa23',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.PTR, 'example.com.'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'target': 'example.com.',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.RP, 'mbox-dname txt-dname'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'mbox': 'mbox-dname',
+ 'txt': 'txt-dname',
+ },
+ ),
+ (
+ dns.rdata.from_text(
+ dns.rdataclass.IN,
+ dns.rdatatype.RRSIG,
+ 'SOA 5 2 3600 20101127004331 20101119213831 61695 dnspython.org. sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDw'
+ ' t6dvM9jAXdIEi03l9H/RAd9xNNW6gvGMHsBGzpvvqFQxIBR2PoiZA1mX /SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd0 8vs=',
+ ),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'type_covered': 'SOA',
+ 'algorithm': 5,
+ 'labels': 2,
+ 'original_ttl': 3600,
+ 'expiration': 1290818611,
+ 'inception': 1290202711,
+ 'key_tag': 61695,
+ 'signer': 'dnspython.org.',
+ 'signature': (
+ 'sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDwt6dvM9jAXdIEi03l9H/RAd9xNNW6gv'
+ 'GMHsBGzpvvqFQxIBR2PoiZA1mX/SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd08vs='
+ ),
+ },
+ ),
+ (
+ dns.rdata.from_text(
+ dns.rdataclass.IN,
+ dns.rdatatype.RRSIG,
+ 'NSEC 1 3 3600 20200101000000 20030101000000 2143 foo. MxFcby9k/yvedMfQgKzhH5er0Mu/vILz'
+ ' 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY=',
+ ),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'type_covered': 'NSEC',
+ 'algorithm': 1,
+ 'labels': 3,
+ 'original_ttl': 3600,
+ 'expiration': 1577836800,
+ 'inception': 1041379200,
+ 'key_tag': 2143,
+ 'signer': 'foo.',
+ 'signature': 'MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY=',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.org. 1 7200 900 1209600 86400'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'mname': 'ns.example.com.',
+ 'rname': 'ns.example.org.',
+ 'serial': 1,
+ 'refresh': 7200,
+ 'retry': 900,
+ 'expire': 1209600,
+ 'minimum': 86400,
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SPF, '"v=spf1 a mx" " -all"'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'strings': [b'v=spf1 a mx', b' -all'],
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SPF, '"v=spf1 a mx" " -all"'),
+ {'to_unicode': False, 'add_synthetic': True},
+ {
+ 'strings': [b'v=spf1 a mx', b' -all'],
+ 'value': b'v=spf1 a mx -all',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SRV, r'0 1 443 exchange.example.com'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'priority': 0,
+ 'weight': 1,
+ 'port': 443,
+ 'target': 'exchange.example.com',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SSHFP, r'1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'algorithm': 1,
+ 'fp_type': 1,
+ 'fingerprint': 'aa549bfe898489c02d1715d97d79c57ba2fa76ab',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TLSA, r'3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'usage': 3,
+ 'selector': 1,
+ 'mtype': 1,
+ 'cert': 'a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo bar"'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'strings': [b'asdf', b'foo bar'],
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo bar"'),
+ {'to_unicode': False, 'add_synthetic': True},
+ {
+ 'strings': [b'asdf', b'foo bar'],
+ 'value': b'asdffoo bar',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo bar"'),
+ {'to_unicode': True, 'add_synthetic': False},
+ {
+ 'strings': [u'asdf', u'foo bar'],
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo bar"'),
+ {'to_unicode': True, 'add_synthetic': True},
+ {
+ 'strings': [u'asdf', u'foo bar'],
+ 'value': u'asdffoo bar',
+ },
+ ),
+]
+
+
+if dns.version.MAJOR >= 2:
+ # https://github.com/rthalley/dnspython/issues/321 makes this not working on dnspython < 2.0.0,
+ # which affects Python 3.5 and 2.x since these are only supported by dnspython < 2.0.0.
+ TEST_CONVERT_RDATA_TO_DICT.extend([
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo \195\164"'),
+ {'to_unicode': False, 'add_synthetic': False},
+ {
+ 'strings': [b'asdf', b'foo \xC3\xA4'],
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo \195\164"'),
+ {'to_unicode': False, 'add_synthetic': True},
+ {
+ 'strings': [b'asdf', b'foo \xC3\xA4'],
+ 'value': b'asdffoo \xC3\xA4',
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo \195\164"'),
+ {'to_unicode': True, 'add_synthetic': False},
+ {
+ 'strings': [u'asdf', u'foo ä'],
+ },
+ ),
+ (
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo \195\164"'),
+ {'to_unicode': True, 'add_synthetic': True},
+ {
+ 'strings': [u'asdf', u'foo ä'],
+ 'value': u'asdffoo ä',
+ },
+ ),
+ ])
+
+
+@pytest.mark.parametrize("rdata, kwarg, expected_result", TEST_CONVERT_RDATA_TO_DICT)
+def test_convert_rdata_to_dict(rdata, kwarg, expected_result):
+ result = convert_rdata_to_dict(rdata, **kwarg)
+ print(expected_result)
+ print(result)
+ assert expected_result == result
+
+
+def test_error():
+ v = RDTYPE_TO_FIELDS.pop(dns.rdatatype.A)
+ try:
+ with pytest.raises(ValueError) as exc:
+ convert_rdata_to_dict(dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'))
+ finally:
+ RDTYPE_TO_FIELDS[dns.rdatatype.A] = v
+ print(exc.value.args)
+ assert exc.value.args == ('Unsupported record type 1', )
diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_ips.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_ips.py
new file mode 100644
index 000000000..660f9adc1
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_ips.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2023, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+import pytest
+
+from ansible_collections.community.dns.plugins.module_utils import ips
+
+from ansible_collections.community.dns.plugins.module_utils.ips import (
+ assert_requirements_present,
+ is_ip_address,
+)
+
+# We need ipaddress
+ipaddress = pytest.importorskip('ipaddress')
+
+
+def test_assert_requirements_present():
+ class ModuleFailException(Exception):
+ pass
+
+ class FakeModule(object):
+ def fail_json(self, **kwargs):
+ raise ModuleFailException(kwargs)
+
+ module = FakeModule()
+
+ orig_importerror = ips.IPADDRESS_IMPORT_EXC
+ try:
+ ips.IPADDRESS_IMPORT_EXC = None
+ assert_requirements_present(module)
+
+ ips.IPADDRESS_IMPORT_EXC = 'asdf'
+ with pytest.raises(ModuleFailException) as exc:
+ assert_requirements_present(module)
+
+ assert 'ipaddress' in exc.value.args[0]['msg']
+ assert 'asdf' == exc.value.args[0]['exception']
+
+ finally:
+ ips.IPADDRESS_IMPORT_EXC = orig_importerror
+
+
+IS_IP_ADDRESS_DATA = [
+ ('foo.bar', False),
+ ('foo', False),
+ ('123', False),
+ ('1.2.3.4', True),
+ ('::', True),
+]
+
+
+@pytest.mark.parametrize("input, output", IS_IP_ADDRESS_DATA)
+def test_is_ip_address(input, output):
+ assert is_ip_address(input) == output
diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_resolver.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_resolver.py
index 1f51601e0..cea7fb1d6 100644
--- a/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_resolver.py
+++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_resolver.py
@@ -36,24 +36,26 @@ def test_assert_requirements_present():
class ModuleFailException(Exception):
pass
- def fail_json(**kwargs):
- raise ModuleFailException(kwargs)
+ class FakeModule(object):
+ def fail_json(self, **kwargs):
+ raise ModuleFailException(kwargs)
- module = MagicMock()
- module.fail_json = MagicMock(side_effect=fail_json)
+ module = FakeModule()
orig_importerror = resolver.DNSPYTHON_IMPORTERROR
- resolver.DNSPYTHON_IMPORTERROR = None
- assert_requirements_present(module)
-
- resolver.DNSPYTHON_IMPORTERROR = 'asdf'
- with pytest.raises(ModuleFailException) as exc:
+ try:
+ resolver.DNSPYTHON_IMPORTERROR = None
assert_requirements_present(module)
- assert 'dnspython' in exc.value.args[0]['msg']
- assert 'asdf' == exc.value.args[0]['exception']
+ resolver.DNSPYTHON_IMPORTERROR = 'asdf'
+ with pytest.raises(ModuleFailException) as exc:
+ assert_requirements_present(module)
+
+ assert 'dnspython' in exc.value.args[0]['msg']
+ assert 'asdf' == exc.value.args[0]['exception']
- resolver.DNSPYTHON_IMPORTERROR = orig_importerror
+ finally:
+ resolver.DNSPYTHON_IMPORTERROR = orig_importerror
def test_lookup_ns_names():
@@ -449,7 +451,69 @@ def test_timeout_failure():
assert exc.value.kwargs['timeout'] == 4
-def test_error_nxdomain():
+def test_error_nameserver_nxdomain_none():
+ resolver = mock_resolver(['1.1.1.1'], {})
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NXDOMAIN),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NXDOMAIN),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ resolver = ResolveDirectlyFromNameServers()
+ assert resolver.resolve_nameservers('example.com') == []
+
+
+def test_error_nameserver_nxdomain_partial_first():
+ resolver = mock_resolver(['1.1.1.1'], {})
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NXDOMAIN),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ resolver = ResolveDirectlyFromNameServers()
+ assert resolver.resolve_nameservers('example.com') == ['ns.com']
+
+
+def test_error_nameserver_nxdomain_partial_second():
resolver = mock_resolver(['1.1.1.1'], {})
udp_sequence = [
{
@@ -461,14 +525,159 @@ def test_error_nxdomain():
},
'result': create_mock_response(dns.rcode.NXDOMAIN),
},
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
]
with patch('dns.resolver.get_default_resolver', resolver):
with patch('dns.resolver.Resolver', resolver):
with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ resolver = ResolveDirectlyFromNameServers()
+ assert resolver.resolve_nameservers('example.com') == ['ns.com']
+
+
+def test_error_nxdomain_ok():
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '2:3::4'),
+ )),
+ },
+ ],
+ ('1:2::3', '2:3::4', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'example.com'),
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NXDOMAIN),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ resolver = ResolveDirectlyFromNameServers()
+ rrset_dict = resolver.resolve('example.com', nxdomain_is_empty=True)
+ print(rrset_dict)
+ assert sorted(rrset_dict.keys()) == ['ns.com']
+ assert rrset_dict['ns.com'] == []
+
+
+def test_error_nxdomain_fail():
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '2:3::4'),
+ )),
+ },
+ ],
+ ('1:2::3', '2:3::4', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'example.com'),
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NXDOMAIN),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ resolver = ResolveDirectlyFromNameServers()
with pytest.raises(dns.resolver.NXDOMAIN) as exc:
- resolver = ResolveDirectlyFromNameServers()
- resolver.resolve_nameservers('example.com')
- assert exc.value.kwargs['qnames'] == [dns.name.from_unicode(u'com')]
+ resolver.resolve('example.com', nxdomain_is_empty=False)
+ print(exc.value.args[0])
+ assert exc.value.args[0] == 'The DNS query name does not exist: example.com.'
def test_error_servfail():
@@ -493,6 +702,306 @@ def test_error_servfail():
assert exc.value.args[0] == 'Error SERVFAIL while querying 1.1.1.1 with query get NS for "com."'
+def test_error_servfail_retry_success():
+ resolver = mock_resolver(['1.1.1.1'], {})
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.SERVFAIL),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.SERVFAIL),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ resolver = ResolveDirectlyFromNameServers(servfail_retries=2)
+ assert resolver.resolve_nameservers('com') == ['ns.com']
+
+
+def test_error_servfail_retry_fail():
+ resolver = mock_resolver(['1.1.1.1'], {})
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.SERVFAIL),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.SERVFAIL),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.SERVFAIL),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(ResolverError) as exc:
+ resolver = ResolveDirectlyFromNameServers(servfail_retries=2)
+ resolver.resolve_nameservers('example.com')
+ assert exc.value.args[0] == 'Error SERVFAIL while querying 1.1.1.1 with query get NS for "com."'
+
+
+def test_servfail_handling():
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '2.2.2.2'),
+ )),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ resolver = ResolveDirectlyFromNameServers(servfail_retries=2)
+ assert resolver.resolve_nameservers('example.com', resolve_addresses=True) == ['3.3.3.3']
+ # The following results should be cached:
+ assert resolver.resolve_nameservers('com') == ['ns.com']
+ assert resolver.resolve_nameservers('com', resolve_addresses=True) == ['2.2.2.2']
+ assert resolver.resolve_nameservers('example.com') == ['ns.example.com']
+ assert resolver.resolve_nameservers('example.com', resolve_addresses=True) == ['3.3.3.3']
+
+
+def test_servfail_failing():
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '2.2.2.2'),
+ )),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ {
+ 'target': 'ns.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ resolver = ResolveDirectlyFromNameServers(servfail_retries=2)
+ assert resolver.resolve_nameservers('example.com', resolve_addresses=True) == ['3.3.3.3']
+ # The following results should be cached:
+ assert resolver.resolve_nameservers('com') == ['ns.com']
+ with pytest.raises(ResolverError) as exc:
+ resolver.resolve_nameservers('com', resolve_addresses=True)
+ assert exc.value.args[0] == "Error SERVFAIL while querying ['1.1.1.1']"
+
+
def test_no_response():
fake_query = MagicMock()
fake_query.question = 'Doctor Who?'
diff --git a/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_info.py b/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_info.py
new file mode 100644
index 000000000..e8fd4a6ed
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_info.py
@@ -0,0 +1,395 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2022, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+import pytest
+
+from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import MagicMock, patch
+
+from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import (
+ set_module_args,
+ ModuleTestCase,
+ AnsibleExitJson,
+ AnsibleFailJson,
+)
+
+from ansible_collections.community.dns.plugins.modules import nameserver_info
+
+from ..module_utils.resolver_helper import (
+ mock_resolver,
+ mock_query_udp,
+ create_mock_answer,
+ create_mock_response,
+)
+
+# We need dnspython
+dns = pytest.importorskip('dns')
+
+
+class TestNameserverInfo(ModuleTestCase):
+ def test_single(self):
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {})
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'),
+ ), dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'example.org')
+ )]),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com'],
+ })
+ nameserver_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['changed'] is False
+ assert len(exc.value.args[0]['results']) == 1
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert exc.value.args[0]['results'][0]['nameservers'] == [
+ 'ns.example.com',
+ ]
+
+ def test_single_ips(self):
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'),
+ )),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'),
+ ), dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'example.org')
+ )]),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com'],
+ 'resolve_addresses': True,
+ })
+ nameserver_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['changed'] is False
+ assert len(exc.value.args[0]['results']) == 1
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert exc.value.args[0]['results'][0]['nameservers'] == [
+ '1:2::3',
+ '3.3.3.3',
+ ]
+
+ def test_timeout(self):
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {})
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'raise': dns.exception.Timeout(timeout=9),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'mail.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'raise': dns.exception.Timeout(timeout=9),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'mail.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'raise': dns.exception.Timeout(timeout=9),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com', 'mail.example.com'],
+ 'query_timeout': 9,
+ 'query_retry': 1,
+ })
+ nameserver_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['msg'] in (
+ 'Unexpected DNS error: The DNS operation timed out after 9 seconds',
+ 'Unexpected DNS error: The DNS operation timed out after 9.000 seconds',
+ )
+ assert len(exc.value.args[0]['results']) == 2
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert exc.value.args[0]['results'][0]['nameservers'] == ['ns.example.com']
+ assert exc.value.args[0]['results'][1]['name'] == 'mail.example.com'
+ assert 'nameservers' not in exc.value.args[0]['results'][1]
+
+ def test_nxdomain(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'),
+ )),
+ },
+ ],
+ ('1:2::3', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NXDOMAIN),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NXDOMAIN),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com'],
+ })
+ nameserver_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['changed'] is False
+ assert len(exc.value.args[0]['results']) == 1
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert exc.value.args[0]['results'][0]['nameservers'] == ['ns.example.com']
+
+ def test_servfail(self):
+ resolver = mock_resolver(['1.1.1.1'], {})
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.SERVFAIL),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com'],
+ })
+ nameserver_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['msg'] == 'Unexpected resolving error: Error SERVFAIL while querying 1.1.1.1 with query get NS for "com."'
+ assert len(exc.value.args[0]['results']) == 1
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert 'nameservers' not in exc.value.args[0]['results'][0]
diff --git a/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_record_info.py b/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_record_info.py
new file mode 100644
index 000000000..ee68586ff
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_record_info.py
@@ -0,0 +1,592 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2022, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+import pytest
+
+from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import MagicMock, patch
+
+from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import (
+ set_module_args,
+ ModuleTestCase,
+ AnsibleExitJson,
+ AnsibleFailJson,
+)
+
+from ansible_collections.community.dns.plugins.modules import nameserver_record_info
+
+from ..module_utils.resolver_helper import (
+ mock_resolver,
+ mock_query_udp,
+ create_mock_answer,
+ create_mock_response,
+)
+
+# We need dnspython
+dns = pytest.importorskip('dns')
+
+
+class TestNameserverRecordInfo(ModuleTestCase):
+ def test_single(self):
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.org',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.org',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '4.4.4.4'),
+ )),
+ },
+ {
+ 'target': 'ns.example.org',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ ],
+ ('1:2::3', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'example.org'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'example.org',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'asdf'),
+ )),
+ },
+ ],
+ ('4.4.4.4', ): [
+ {
+ 'target': dns.name.from_unicode(u'example.org'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'example.org',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'asdf'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'fdsa'),
+ )),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'),
+ ), dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'example.org')
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'org'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'org',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.org'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.org'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.org',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.org'),
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com'],
+ 'type': 'TXT',
+ })
+ nameserver_record_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['changed'] is False
+ assert len(exc.value.args[0]['results']) == 1
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert len(exc.value.args[0]['results'][0]['result']) == 2
+ assert exc.value.args[0]['results'][0]['result'][0]['nameserver'] == 'ns.example.com'
+ assert exc.value.args[0]['results'][0]['result'][0]['values'] == [
+ {
+ 'strings': ['asdf'],
+ 'value': 'asdf',
+ }
+ ]
+ assert exc.value.args[0]['results'][0]['result'][1]['nameserver'] == 'ns.example.org'
+ assert exc.value.args[0]['results'][0]['result'][1]['values'] == [
+ {
+ 'strings': ['asdf'],
+ 'value': 'asdf',
+ },
+ {
+ 'strings': ['fdsa'],
+ 'value': 'fdsa',
+ },
+ ]
+
+ def test_timeout(self):
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 9,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 9,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ ],
+ ('3.3.3.3', ): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 9,
+ 'raise': dns.exception.Timeout(timeout=9),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 9,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'www.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'fdsa asdf'),
+ )),
+ },
+ {
+ 'target': dns.name.from_unicode(u'mail.example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 9,
+ 'raise': dns.exception.Timeout(timeout=9),
+ },
+ {
+ 'target': dns.name.from_unicode(u'mail.example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 9,
+ 'raise': dns.exception.Timeout(timeout=9),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'mail.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 9,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata(
+ 'mail.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'),
+ )]),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com', 'mail.example.com'],
+ 'type': 'TXT',
+ 'query_timeout': 9,
+ 'query_retry': 1,
+ })
+ nameserver_record_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['msg'] in (
+ 'Unexpected DNS error: The DNS operation timed out after 9 seconds',
+ 'Unexpected DNS error: The DNS operation timed out after 9.000 seconds',
+ )
+ assert len(exc.value.args[0]['results']) == 2
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert exc.value.args[0]['results'][0]['result'] == [
+ {
+ 'nameserver': 'ns.example.com',
+ 'values': [
+ {
+ 'strings': ['fdsa', 'asdf'],
+ 'value': 'fdsaasdf',
+ }
+ ],
+ }
+ ]
+ assert exc.value.args[0]['results'][1]['name'] == 'mail.example.com'
+ assert exc.value.args[0]['results'][1]['result'] == []
+
+ def test_nxdomain(self):
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'),
+ )),
+ },
+ ],
+ ('1:2::3', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NXDOMAIN),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NXDOMAIN),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com'],
+ 'type': 'TXT',
+ })
+ nameserver_record_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['changed'] is False
+ assert len(exc.value.args[0]['results']) == 1
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert len(exc.value.args[0]['results'][0]['result']) == 1
+ assert exc.value.args[0]['results'][0]['result'][0]['nameserver'] == 'ns.example.com'
+ assert exc.value.args[0]['results'][0]['result'][0]['values'] == []
+
+ def test_servfail(self):
+ resolver = mock_resolver(['1.1.1.1'], {})
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.SERVFAIL),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com'],
+ 'type': 'TXT',
+ })
+ nameserver_record_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['msg'] == 'Unexpected resolving error: Error SERVFAIL while querying 1.1.1.1 with query get NS for "com."'
+ assert len(exc.value.args[0]['results']) == 1
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert exc.value.args[0]['results'][0]['result'] == []
+
+ def test_cname_loop(self):
+ fake_query = MagicMock()
+ fake_query.question = 'Doctor Who?'
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ {
+ 'target': 'ns.example.org',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.org',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '4.4.4.4'),
+ )),
+ },
+ {
+ 'target': 'ns.example.org',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'raise': dns.resolver.NoAnswer(response=fake_query),
+ },
+ ],
+ })
+ udp_sequence = [
+ {
+ 'query_target': dns.name.from_unicode(u'com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'),
+ ), dns.rrset.from_rdata(
+ 'www.example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'example.org')
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'org'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'org',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.org'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'example.org'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.org',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.org'),
+ ), dns.rrset.from_rdata(
+ 'example.org',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.example.com')
+ )]),
+ },
+ ]
+ with patch('dns.resolver.get_default_resolver', resolver):
+ with patch('dns.resolver.Resolver', resolver):
+ with patch('dns.query.udp', mock_query_udp(udp_sequence)):
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({
+ 'name': ['www.example.com'],
+ 'type': 'TXT',
+ })
+ nameserver_record_info.main()
+
+ print(exc.value.args[0])
+ assert exc.value.args[0]['msg'] == 'Unexpected resolving error: Found CNAME loop starting at www.example.com'
+ assert len(exc.value.args[0]['results']) == 1
+ assert exc.value.args[0]['results'][0]['name'] == 'www.example.com'
+ assert exc.value.args[0]['results'][0]['result'] == []
diff --git a/ansible_collections/community/dns/tests/unit/plugins/modules/test_wait_for_txt.py b/ansible_collections/community/dns/tests/unit/plugins/modules/test_wait_for_txt.py
index 829d6465e..9ee935ba0 100644
--- a/ansible_collections/community/dns/tests/unit/plugins/modules/test_wait_for_txt.py
+++ b/ansible_collections/community/dns/tests/unit/plugins/modules/test_wait_for_txt.py
@@ -1195,7 +1195,44 @@ class TestWaitForTXT(ModuleTestCase):
assert exc.value.args[0]['records'][1]['check_count'] == 1
def test_nxdomain(self):
- resolver = mock_resolver(['1.1.1.1'], {})
+ resolver = mock_resolver(['1.1.1.1'], {
+ ('1.1.1.1', ): [
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.A,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'),
+ )),
+ },
+ {
+ 'target': 'ns.example.com',
+ 'rdtype': dns.rdatatype.AAAA,
+ 'lifetime': 10,
+ 'result': create_mock_answer(dns.rrset.from_rdata(
+ 'ns.example.com',
+ 300,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'),
+ )),
+ },
+ ],
+ ('1:2::3', '3.3.3.3'): [
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ {
+ 'target': dns.name.from_unicode(u'www.example.com'),
+ 'rdtype': dns.rdatatype.TXT,
+ 'lifetime': 10,
+ 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN),
+ },
+ ],
+ })
udp_sequence = [
{
'query_target': dns.name.from_unicode(u'com'),
@@ -1207,7 +1244,20 @@ class TestWaitForTXT(ModuleTestCase):
'result': create_mock_response(dns.rcode.NXDOMAIN),
},
{
- 'query_target': dns.name.from_unicode(u'com'),
+ 'query_target': dns.name.from_unicode(u'example.com'),
+ 'query_type': dns.rdatatype.NS,
+ 'nameserver': '1.1.1.1',
+ 'kwargs': {
+ 'timeout': 10,
+ },
+ 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata(
+ 'example.com',
+ 3600,
+ dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'),
+ )]),
+ },
+ {
+ 'query_target': dns.name.from_unicode(u'www.example.com'),
'query_type': dns.rdatatype.NS,
'nameserver': '1.1.1.1',
'kwargs': {
@@ -1237,12 +1287,14 @@ class TestWaitForTXT(ModuleTestCase):
wait_for_txt.main()
print(exc.value.args[0])
+ assert exc.value.args[0]['failed'] is True
assert exc.value.args[0]['msg'] == 'Timeout (0 out of 1 check(s) passed).'
assert exc.value.args[0]['completed'] == 0
assert len(exc.value.args[0]['records']) == 1
assert exc.value.args[0]['records'][0]['name'] == 'www.example.com'
assert exc.value.args[0]['records'][0]['done'] is False
- assert exc.value.args[0]['records'][0]['values'] == {}
+ assert len(exc.value.args[0]['records'][0]['values']) == 1
+ assert exc.value.args[0]['records'][0]['values']['ns.example.com'] == []
assert exc.value.args[0]['records'][0]['check_count'] == 2
def test_servfail(self):
diff --git a/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_ips.py b/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_ips.py
new file mode 100644
index 000000000..211bad1bf
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_ips.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2023, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+import pytest
+
+from ansible.errors import AnsibleError
+
+from ansible_collections.community.dns.plugins.plugin_utils import ips
+
+from ansible_collections.community.dns.plugins.plugin_utils.ips import (
+ assert_requirements_present,
+)
+
+# We need ipaddress
+ipaddress = pytest.importorskip('ipaddress')
+
+
+def test_assert_requirements_present():
+ orig_importerror = ips.IPADDRESS_IMPORT_EXC
+ try:
+ ips.IPADDRESS_IMPORT_EXC = None
+ assert_requirements_present('community.dns.foo', 'lookup')
+
+ ips.IPADDRESS_IMPORT_EXC = Exception('asdf')
+ with pytest.raises(AnsibleError) as exc:
+ assert_requirements_present('community.dns.foo', 'lookup')
+
+ assert 'ipaddress' in exc.value.args[0]
+
+ finally:
+ ips.IPADDRESS_IMPORT_EXC = orig_importerror
diff --git a/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_resolver.py b/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_resolver.py
new file mode 100644
index 000000000..578155a68
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_resolver.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2023, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+import pytest
+
+from ansible.errors import AnsibleError
+
+from ansible_collections.community.dns.plugins.plugin_utils import resolver
+
+from ansible_collections.community.dns.plugins.plugin_utils.resolver import (
+ assert_requirements_present,
+)
+
+
+def test_assert_requirements_present():
+ orig_importerror = resolver.DNSPYTHON_IMPORTERROR
+ try:
+ resolver.DNSPYTHON_IMPORTERROR = None
+ assert_requirements_present('community.dns.foo', 'lookup')
+
+ resolver.DNSPYTHON_IMPORTERROR = Exception('asdf')
+ with pytest.raises(AnsibleError) as exc:
+ assert_requirements_present('community.dns.foo', 'lookup')
+
+ assert 'dnspython' in exc.value.args[0]
+
+ finally:
+ resolver.DNSPYTHON_IMPORTERROR = orig_importerror
diff --git a/ansible_collections/community/dns/tests/unit/replace-requirements.sh b/ansible_collections/community/dns/tests/unit/replace-requirements.sh
new file mode 100755
index 000000000..c3a52f5d1
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/replace-requirements.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+DIRECTORY="$(dirname "$0")"
+
+if [ -e "${DIRECTORY}/$1" ]; then
+ cp "${DIRECTORY}/$1" "${DIRECTORY}/requirements.txt"
+fi
diff --git a/ansible_collections/community/dns/tests/unit/requirements-stable-2.10.txt b/ansible_collections/community/dns/tests/unit/requirements-stable-2.10.txt
new file mode 100644
index 000000000..126caabdc
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/requirements-stable-2.10.txt
@@ -0,0 +1,13 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+unittest2 ; python_version < '2.7'
+importlib ; python_version < '2.7'
+
+ipaddress ; python_version < '3.3'
+
+dnspython < 2.4.0
+
+lxml < 4.3.0 ; python_version < '2.7' # lxml 4.3.0 and later require python 2.7 or later
+lxml ; python_version >= '2.7'
diff --git a/ansible_collections/community/dns/tests/unit/requirements-stable-2.9.txt b/ansible_collections/community/dns/tests/unit/requirements-stable-2.9.txt
new file mode 100644
index 000000000..126caabdc
--- /dev/null
+++ b/ansible_collections/community/dns/tests/unit/requirements-stable-2.9.txt
@@ -0,0 +1,13 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+unittest2 ; python_version < '2.7'
+importlib ; python_version < '2.7'
+
+ipaddress ; python_version < '3.3'
+
+dnspython < 2.4.0
+
+lxml < 4.3.0 ; python_version < '2.7' # lxml 4.3.0 and later require python 2.7 or later
+lxml ; python_version >= '2.7'
diff --git a/ansible_collections/community/dns/tests/unit/requirements.txt b/ansible_collections/community/dns/tests/unit/requirements.txt
index 8b7c7cd61..dff61418a 100644
--- a/ansible_collections/community/dns/tests/unit/requirements.txt
+++ b/ansible_collections/community/dns/tests/unit/requirements.txt
@@ -5,6 +5,8 @@
unittest2 ; python_version < '2.7'
importlib ; python_version < '2.7'
+ipaddress ; python_version < '3.3'
+
dnspython
lxml < 4.3.0 ; python_version < '2.7' # lxml 4.3.0 and later require python 2.7 or later