diff --git a/README.md b/README.md index 3d8f788..f6a79b2 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Name | Description [ansible.utils.private](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.private_test.rst)|Test if an IP address is private [ansible.utils.public](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.public_test.rst)|Test if an IP address is public [ansible.utils.reserved](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.reserved_test.rst)|Test for a reserved IP address +[ansible.utils.resolvable](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.resolvable_test.rst)|Test if an IP or name can be resolved via /etc/hosts or DNS [ansible.utils.subnet_of](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.subnet_of_test.rst)|Test if a network is a subnet of another network [ansible.utils.supernet_of](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.supernet_of_test.rst)|Test if a network is a supernet of another network [ansible.utils.unspecified](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.unspecified_test.rst)|Test for an unspecified IP address diff --git a/changelogs/fragments/add_netaddr_test_plugins_8.yml b/changelogs/fragments/add_netaddr_test_plugins_8.yml new file mode 100644 index 0000000..925b869 --- /dev/null +++ b/changelogs/fragments/add_netaddr_test_plugins_8.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Add resolvable test plugins diff --git a/docs/ansible.utils.resolvable_test.rst b/docs/ansible.utils.resolvable_test.rst new file mode 100644 index 0000000..45dda16 --- /dev/null +++ b/docs/ansible.utils.resolvable_test.rst @@ -0,0 +1,176 @@ +.. _ansible.utils.resolvable_test: + + +************************ +ansible.utils.resolvable +************************ + +**Test if an IP or name can be resolved via /etc/hosts or DNS** + + +Version added: 2.2.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This plugin checks if the provided IP address of host name can be resolved using /etc/hosts or DNS + + + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + +
ParameterChoices/DefaultsConfigurationComments
+
+ host + +
+ string + / required +
+
+ + +
A string that represents the IP address or the host name
+
{'For example': ['docs.ansible.com', '127.0.0.1', '::1']}
+
+
+ + + + +Examples +-------- + +.. code-block:: yaml + + #### Simple examples + + - name: Check if docs.ansible.com is resolvable or not + ansible.builtin.set_fact: + data: "{{ 'docs.ansible.com' is ansible.utils.resolvable }}" + + # TASK [Check if docs.ansible.com is resolvable or not] ********************************** + # ok: [localhost] => { + # "ansible_facts": { + # "data": true + # }, + # "changed": false + # } + + - name: Set host name variables + ansible.builtin.set_fact: + good_name: www.google.com + bad_name: foo.google.com + + - name: Assert good_name's resolvability + assert: + that: "{{ 'www.google.com' is ansible.utils.resolvable }}" + + - name: Assert bad_name's resolvability + assert: + that: "{{ 'foo.google.com' is not ansible.utils.resolvable }}" + + # TASK [Assert good_name's resolvability] ************************************************ + # ok: [localhost] => { + # "changed": false, + # "msg": "All assertions passed" + # } + + # TASK [Assert bad_name's resolvability] ************************************************* + # ok: [localhost] => { + # "changed": false, + # "msg": "All assertions passed" + # } + + - name: Set ip variables + ansible.builtin.set_fact: + ipv4_localhost: "127.0.0.1" + ipv6_localhost: "::1" + + - name: Assert ipv4_localhost's resolvability + assert: + that: "{{ ipv4_localhost is ansible.utils.resolvable }}" + + - name: Assert ipv6_localhost's resolvability + assert: + that: "{{ ipv6_localhost is ansible.utils.resolvable }}" + + # TASK [Assert ipv4_localhost's resolvability] ******************************************* + # ok: [localhost] => { + # "changed": false, + # "msg": "All assertions passed" + # } + + # TASK [Assert ipv6_localhost's resolvability] ******************************************* + # ok: [localhost] => { + # "changed": false, + # "msg": "All assertions passed" + # } + + + +Return Values +------------- +Common return values are documented `here `_, the following are the fields unique to this test: + +.. raw:: html + + + + + + + + + + + + +
KeyReturnedDescription
+
+ data + +
+ - +
+
+
If jinja test satisfies plugin expression true
+
If jinja test does not satisfy plugin expression false
+
+
+

+ + +Status +------ + + +Authors +~~~~~~~ + +- Priyam Sahoo (@priyamsahoo) + + +.. hint:: + Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. diff --git a/plugins/test/resolvable.py b/plugins/test/resolvable.py new file mode 100644 index 0000000..2033ab5 --- /dev/null +++ b/plugins/test/resolvable.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +Test plugin file for netaddr tests: resolvable +""" +from __future__ import absolute_import, division, print_function +from ansible_collections.ansible.utils.plugins.plugin_utils.base.ipaddress_utils import ( + _need_ipaddress, + _validate_args, +) + +import socket + +try: + import ipaddress + + HAS_IPADDRESS = True +except ImportError: + HAS_IPADDRESS = False + +__metaclass__ = type + +DOCUMENTATION = """ + name: resolvable + author: Priyam Sahoo (@priyamsahoo) + version_added: "2.2.0" + short_description: Test if an IP or name can be resolved via /etc/hosts or DNS + description: + - This plugin checks if the provided IP address of host name can be resolved using /etc/hosts or DNS + options: + host: + description: + - A string that represents the IP address or the host name + - For example: + - "docs.ansible.com" + - 127.0.0.1 + - ::1 + type: str + required: True + notes: +""" + +EXAMPLES = r""" + +#### Simple examples + +- name: Check if docs.ansible.com is resolvable or not + ansible.builtin.set_fact: + data: "{{ 'docs.ansible.com' is ansible.utils.resolvable }}" + +# TASK [Check if docs.ansible.com is resolvable or not] ********************************** +# ok: [localhost] => { +# "ansible_facts": { +# "data": true +# }, +# "changed": false +# } + +- name: Set host name variables + ansible.builtin.set_fact: + good_name: www.google.com + bad_name: foo.google.com + +- name: Assert good_name's resolvability + assert: + that: "{{ 'www.google.com' is ansible.utils.resolvable }}" + +- name: Assert bad_name's resolvability + assert: + that: "{{ 'foo.google.com' is not ansible.utils.resolvable }}" + +# TASK [Assert good_name's resolvability] ************************************************ +# ok: [localhost] => { +# "changed": false, +# "msg": "All assertions passed" +# } + +# TASK [Assert bad_name's resolvability] ************************************************* +# ok: [localhost] => { +# "changed": false, +# "msg": "All assertions passed" +# } + +- name: Set ip variables + ansible.builtin.set_fact: + ipv4_localhost: "127.0.0.1" + ipv6_localhost: "::1" + +- name: Assert ipv4_localhost's resolvability + assert: + that: "{{ ipv4_localhost is ansible.utils.resolvable }}" + +- name: Assert ipv6_localhost's resolvability + assert: + that: "{{ ipv6_localhost is ansible.utils.resolvable }}" + +# TASK [Assert ipv4_localhost's resolvability] ******************************************* +# ok: [localhost] => { +# "changed": false, +# "msg": "All assertions passed" +# } + +# TASK [Assert ipv6_localhost's resolvability] ******************************************* +# ok: [localhost] => { +# "changed": false, +# "msg": "All assertions passed" +# } + +""" + +RETURN = """ + data: + description: + - If jinja test satisfies plugin expression C(true) + - If jinja test does not satisfy plugin expression C(false) +""" + + +@_need_ipaddress +def _resolvable(host): + """ Test if an IP or name can be resolved via /etc/hosts or DNS """ + + params = {"host": host} + _validate_args("resolvable", DOCUMENTATION, params) + + try: + ipaddress.ip_address(host) + ip = True + except Exception: + ip = False + if ip: + try: + socket.gethostbyaddr(host) + return True + except Exception: + return False + else: + try: + socket.getaddrinfo(host, None) + return True + except Exception: + return False + + +class TestModule(object): + """ network jinja tests """ + + test_map = {"resolvable": _resolvable} + + def tests(self): + return self.test_map diff --git a/tests/integration/targets/netaddr_test/tasks/include/resolvable.yml b/tests/integration/targets/netaddr_test/tasks/include/resolvable.yml new file mode 100644 index 0000000..8d375c7 --- /dev/null +++ b/tests/integration/targets/netaddr_test/tasks/include/resolvable.yml @@ -0,0 +1,28 @@ +--- +- name: "resolvable: Check if {{ good_name }} is resolvable or not" + assert: + that: "{{ good_name is ansible.utils.resolvable }}" + +- name: "resolvable: Check if {{ ipv4_localhost }} is resolvable or not" + assert: + that: "{{ ipv4_localhost is ansible.utils.resolvable }}" + +- name: "resolvable: Check if {{ ipv6_localhost }} is resolvable or not" + assert: + that: "{{ ipv6_localhost is ansible.utils.resolvable }}" + +- name: "resolvable: Test invalidness" + ansible.builtin.set_fact: + criteria_check1: "{{ bad_name is ansible.utils.resolvable }}" + +- name: "resolvable: Assert invalidness" + assert: + that: "{{ criteria_check1 == false }}" + +- name: "resolvable: Test invalidness" + ansible.builtin.set_fact: + criteria_check2: "{{ 'invalidhost' is ansible.utils.resolvable }}" + +- name: "resolvable: Assert invalidness" + assert: + that: "{{ criteria_check2 == false }}" diff --git a/tests/integration/targets/netaddr_test/vars/main.yaml b/tests/integration/targets/netaddr_test/vars/main.yaml index ae08764..6505ab2 100644 --- a/tests/integration/targets/netaddr_test/vars/main.yaml +++ b/tests/integration/targets/netaddr_test/vars/main.yaml @@ -3,3 +3,9 @@ networks: - "10.0.0.0/8" - "192.168.1.0/24" - "172.16.0.0/16" + +good_name: www.google.com +bad_name: foo.google.com + +ipv4_localhost: "127.0.0.1" +ipv6_localhost: "::1" diff --git a/tests/unit/plugins/test/test_resolvable.py b/tests/unit/plugins/test/test_resolvable.py new file mode 100644 index 0000000..09b6799 --- /dev/null +++ b/tests/unit/plugins/test/test_resolvable.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +Unit test file for netaddr test plugin: resolvable +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import unittest +from ansible_collections.ansible.utils.plugins.test.resolvable import ( + _resolvable, +) + + +class TestResolvable(unittest.TestCase): + def setUp(self): + pass + + def test_invalid_data(self): + """Check passing invalid argspec""" + + # missing argument + with self.assertRaises(TypeError) as error: + _resolvable() + self.assertIn("argument", str(error.exception)) + + def test_valid_data(self): + """Check passing valid data as per criteria""" + + result = _resolvable(host="www.google.com") + self.assertEqual(result, True) + + result = _resolvable(host="127.0.0.1") + self.assertEqual(result, True) + + result = _resolvable(host="::1") + self.assertEqual(result, True) + + result = _resolvable(host="foo.google.com") + self.assertEqual(result, False) + + result = _resolvable(host="invalidhost") + self.assertEqual(result, False)