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
+
+
+
+ Parameter |
+ Choices/Defaults |
+ Configuration |
+ Comments |
+
+
+
+
+ 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
+
+
+
+ Key |
+ Returned |
+ Description |
+
+
+
+
+ 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)