From 1b76548d9d5bd808e571165907546e1fdaa7fb75 Mon Sep 17 00:00:00 2001
From: Priyam Sahoo <42550351+priyamsahoo@users.noreply.github.com>
Date: Thu, 3 Jun 2021 12:47:07 +0530
Subject: [PATCH] Added 'usable_range' filter plugin (#77)
Added 'usable_range' filter plugin
Reviewed-by: https://github.com/apps/ansible-zuul
---
README.md | 1 +
.../add_usable_range_filter_plugin.yml | 3 +
docs/ansible.utils.usable_range_filter.rst | 213 ++++++++++++++++++
plugins/filter/usable_range.py | 194 ++++++++++++++++
.../usable_range/tasks/include/argspec.yml | 77 +++++++
.../tasks/include/example_filter.yml | 43 ++++
.../targets/usable_range/tasks/main.yml | 13 ++
.../targets/usable_range/vars/main.yml | 41 ++++
.../unit/plugins/filter/test_usable_range.py | 125 ++++++++++
9 files changed, 710 insertions(+)
create mode 100644 changelogs/fragments/add_usable_range_filter_plugin.yml
create mode 100644 docs/ansible.utils.usable_range_filter.rst
create mode 100644 plugins/filter/usable_range.py
create mode 100644 tests/integration/targets/usable_range/tasks/include/argspec.yml
create mode 100644 tests/integration/targets/usable_range/tasks/include/example_filter.yml
create mode 100644 tests/integration/targets/usable_range/tasks/main.yml
create mode 100644 tests/integration/targets/usable_range/vars/main.yml
create mode 100644 tests/unit/plugins/filter/test_usable_range.py
diff --git a/README.md b/README.md
index f6a79b2..2645d9b 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@ Name | Description
[ansible.utils.index_of](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.index_of_filter.rst)|Find the indices of items in a list matching some criteria
[ansible.utils.to_paths](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.to_paths_filter.rst)|Flatten a complex object into a dictionary of paths and values
[ansible.utils.to_xml](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.to_xml_filter.rst)|Convert given JSON string to XML
+[ansible.utils.usable_range](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.usable_range_filter.rst)|Expand the usable IP addresses
[ansible.utils.validate](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.validate_filter.rst)|Validate data with provided criteria
### Lookup plugins
diff --git a/changelogs/fragments/add_usable_range_filter_plugin.yml b/changelogs/fragments/add_usable_range_filter_plugin.yml
new file mode 100644
index 0000000..c864d4f
--- /dev/null
+++ b/changelogs/fragments/add_usable_range_filter_plugin.yml
@@ -0,0 +1,3 @@
+---
+minor_changes:
+ - Add usable_range test plugin
diff --git a/docs/ansible.utils.usable_range_filter.rst b/docs/ansible.utils.usable_range_filter.rst
new file mode 100644
index 0000000..7f32fad
--- /dev/null
+++ b/docs/ansible.utils.usable_range_filter.rst
@@ -0,0 +1,213 @@
+.. _ansible.utils.usable_range_filter:
+
+
+**************************
+ansible.utils.usable_range
+**************************
+
+**Expand the usable IP addresses**
+
+
+Version added: 2.3.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- For a given IP address (IPv4 or IPv6) in CIDR form, the plugin generates a list of usable IP addresses belonging to the network.
+
+
+
+
+Parameters
+----------
+
+.. raw:: html
+
+
+
+ Parameter |
+ Choices/Defaults |
+ Configuration |
+ Comments |
+
+
+
+
+ ip
+
+
+ string
+ / required
+
+ |
+
+ |
+
+ |
+
+ A string that represents an IP address of network in CIDR form
+ {'For example': ['10.0.0.0/24', '2001:db8:abcd:0012::0/124']}
+ |
+
+
+
+
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ #### Simple examples
+
+ - name: Expand and produce list of usable IP addresses in 10.0.0.0/28
+ ansible.builtin.set_fact:
+ data: "{{ '10.0.0.0/28' | ansible.utils.usable_range }}"
+
+ # TASK [Expand and produce list of usable IP addresses in 10.0.0.0/28] ************************
+ # ok: [localhost] => {
+ # "ansible_facts": {
+ # "data": {
+ # "number_of_ips": 16,
+ # "usable_ips": [
+ # "10.0.0.0",
+ # "10.0.0.1",
+ # "10.0.0.2",
+ # "10.0.0.3",
+ # "10.0.0.4",
+ # "10.0.0.5",
+ # "10.0.0.6",
+ # "10.0.0.7",
+ # "10.0.0.8",
+ # "10.0.0.9",
+ # "10.0.0.10",
+ # "10.0.0.11",
+ # "10.0.0.12",
+ # "10.0.0.13",
+ # "10.0.0.14",
+ # "10.0.0.15"
+ # ]
+ # }
+ # },
+ # "changed": false
+ # }
+
+ - name: Expand and produce list of usable IP addresses in 2001:db8:abcd:0012::0/126
+ ansible.builtin.set_fact:
+ data1: "{{ '2001:db8:abcd:0012::0/126' | ansible.utils.usable_range }}"
+
+ # TASK [Expand and produce list of usable IP addresses in 2001:db8:abcd:0012::0/126] ***
+ # ok: [localhost] => {
+ # "ansible_facts": {
+ # "data1": {
+ # "number_of_ips": 4,
+ # "usable_ips": [
+ # "2001:db8:abcd:12::",
+ # "2001:db8:abcd:12::1",
+ # "2001:db8:abcd:12::2",
+ # "2001:db8:abcd:12::3"
+ # ]
+ # }
+ # },
+ # "changed": false
+ # }
+
+ - name: Expand and produce list of usable IP addresses in 10.1.1.1
+ ansible.builtin.set_fact:
+ data: "{{ '10.1.1.1' | ansible.utils.usable_range }}"
+
+ # TASK [Expand and produce list of usable IP addresses in 10.1.1.1] ***************************
+ # ok: [localhost] => {
+ # "ansible_facts": {
+ # "data": {
+ # "number_of_ips": 1,
+ # "usable_ips": [
+ # "10.1.1.1"
+ # ]
+ # }
+ # },
+ # "changed": false
+ # }
+
+ #### Simple Use-case (looping through the list result)
+
+ - name: Expand and produce list of usable IP addresses in 192.0.2.0/28
+ ansible.builtin.set_fact:
+ data1: "{{ '127.0.0.0/28' | ansible.utils.usable_range }}"
+
+ - name: Ping all but first IP addresses from the generated list
+ shell: "ping -c 1 {{ item }}"
+ loop: "{{ data1.usable_ips[1:] }}"
+
+ # TASK [Expand and produce list of usable IP addresses in 192.0.2.0/28] ******************************
+ # ok: [localhost]
+
+ # TASK [Ping all but first IP addresses from the generated list] *************************************
+ # changed: [localhost] => (item=127.0.0.1)
+ # changed: [localhost] => (item=127.0.0.2)
+ # changed: [localhost] => (item=127.0.0.3)
+ # changed: [localhost] => (item=127.0.0.4)
+ # changed: [localhost] => (item=127.0.0.5)
+ # changed: [localhost] => (item=127.0.0.6)
+ # changed: [localhost] => (item=127.0.0.7)
+ # changed: [localhost] => (item=127.0.0.8)
+ # changed: [localhost] => (item=127.0.0.9)
+ # changed: [localhost] => (item=127.0.0.10)
+ # changed: [localhost] => (item=127.0.0.11)
+ # changed: [localhost] => (item=127.0.0.12)
+ # changed: [localhost] => (item=127.0.0.13)
+ # changed: [localhost] => (item=127.0.0.14)
+ # changed: [localhost] => (item=127.0.0.15)
+
+
+
+Return Values
+-------------
+Common return values are documented `here `_, the following are the fields unique to this filter:
+
+.. raw:: html
+
+
+
+ Key |
+ Returned |
+ Description |
+
+
+
+
+ data
+
+
+ -
+
+ |
+ |
+
+ Total number of usable IP addresses under the key number_of_ips
+ List of usable IP addresses under the key usable_ips
+
+ |
+
+
+
+
+
+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/filter/usable_range.py b/plugins/filter/usable_range.py
new file mode 100644
index 0000000..dc4ce11
--- /dev/null
+++ b/plugins/filter/usable_range.py
@@ -0,0 +1,194 @@
+# -*- 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)
+
+"""
+Filter plugin file for usable_range
+"""
+
+from __future__ import absolute_import, division, print_function
+from ipaddress import IPv4Network, IPv6Network
+
+from ansible_collections.ansible.utils.plugins.plugin_utils.base.ipaddress_utils import (
+ _validate_args,
+ ip_network,
+ _need_ipaddress,
+)
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+ name: usable_range
+ author: Priyam Sahoo (@priyamsahoo)
+ version_added: "2.3.0"
+ short_description: Expand the usable IP addresses
+ description:
+ - For a given IP address (IPv4 or IPv6) in CIDR form, the plugin generates a list of usable IP addresses belonging to the network.
+ options:
+ ip:
+ description:
+ - A string that represents an IP address of network in CIDR form
+ - For example:
+ - "10.0.0.0/24"
+ - "2001:db8:abcd:0012::0/124"
+ type: str
+ required: True
+ notes:
+"""
+
+EXAMPLES = r"""
+
+#### Simple examples
+
+- name: Expand and produce list of usable IP addresses in 10.0.0.0/28
+ ansible.builtin.set_fact:
+ data: "{{ '10.0.0.0/28' | ansible.utils.usable_range }}"
+
+# TASK [Expand and produce list of usable IP addresses in 10.0.0.0/28] ************************
+# ok: [localhost] => {
+# "ansible_facts": {
+# "data": {
+# "number_of_ips": 16,
+# "usable_ips": [
+# "10.0.0.0",
+# "10.0.0.1",
+# "10.0.0.2",
+# "10.0.0.3",
+# "10.0.0.4",
+# "10.0.0.5",
+# "10.0.0.6",
+# "10.0.0.7",
+# "10.0.0.8",
+# "10.0.0.9",
+# "10.0.0.10",
+# "10.0.0.11",
+# "10.0.0.12",
+# "10.0.0.13",
+# "10.0.0.14",
+# "10.0.0.15"
+# ]
+# }
+# },
+# "changed": false
+# }
+
+- name: Expand and produce list of usable IP addresses in 2001:db8:abcd:0012::0/126
+ ansible.builtin.set_fact:
+ data1: "{{ '2001:db8:abcd:0012::0/126' | ansible.utils.usable_range }}"
+
+# TASK [Expand and produce list of usable IP addresses in 2001:db8:abcd:0012::0/126] ***
+# ok: [localhost] => {
+# "ansible_facts": {
+# "data1": {
+# "number_of_ips": 4,
+# "usable_ips": [
+# "2001:db8:abcd:12::",
+# "2001:db8:abcd:12::1",
+# "2001:db8:abcd:12::2",
+# "2001:db8:abcd:12::3"
+# ]
+# }
+# },
+# "changed": false
+# }
+
+- name: Expand and produce list of usable IP addresses in 10.1.1.1
+ ansible.builtin.set_fact:
+ data: "{{ '10.1.1.1' | ansible.utils.usable_range }}"
+
+# TASK [Expand and produce list of usable IP addresses in 10.1.1.1] ***************************
+# ok: [localhost] => {
+# "ansible_facts": {
+# "data": {
+# "number_of_ips": 1,
+# "usable_ips": [
+# "10.1.1.1"
+# ]
+# }
+# },
+# "changed": false
+# }
+
+#### Simple Use-case (looping through the list result)
+
+- name: Expand and produce list of usable IP addresses in 192.0.2.0/28
+ ansible.builtin.set_fact:
+ data1: "{{ '127.0.0.0/28' | ansible.utils.usable_range }}"
+
+- name: Ping all but first IP addresses from the generated list
+ shell: "ping -c 1 {{ item }}"
+ loop: "{{ data1.usable_ips[1:] }}"
+
+# TASK [Expand and produce list of usable IP addresses in 192.0.2.0/28] ******************************
+# ok: [localhost]
+
+# TASK [Ping all but first IP addresses from the generated list] *************************************
+# changed: [localhost] => (item=127.0.0.1)
+# changed: [localhost] => (item=127.0.0.2)
+# changed: [localhost] => (item=127.0.0.3)
+# changed: [localhost] => (item=127.0.0.4)
+# changed: [localhost] => (item=127.0.0.5)
+# changed: [localhost] => (item=127.0.0.6)
+# changed: [localhost] => (item=127.0.0.7)
+# changed: [localhost] => (item=127.0.0.8)
+# changed: [localhost] => (item=127.0.0.9)
+# changed: [localhost] => (item=127.0.0.10)
+# changed: [localhost] => (item=127.0.0.11)
+# changed: [localhost] => (item=127.0.0.12)
+# changed: [localhost] => (item=127.0.0.13)
+# changed: [localhost] => (item=127.0.0.14)
+# changed: [localhost] => (item=127.0.0.15)
+
+"""
+
+RETURN = """
+ data:
+ description:
+ - Total number of usable IP addresses under the key C(number_of_ips)
+ - List of usable IP addresses under the key C(usable_ips)
+"""
+
+from ansible.errors import AnsibleFilterError
+from ansible.module_utils.common.text.converters import to_text
+from ansible.module_utils.six import ensure_text
+
+
+@_need_ipaddress
+def _usable_range(ip):
+ """Expand the usable IP addresses"""
+
+ params = {"ip": ip}
+ _validate_args("usable_range", DOCUMENTATION, params)
+
+ try:
+ if ip_network(ip).version == 4:
+ ips = [
+ to_text(usable_ips)
+ for usable_ips in IPv4Network(ensure_text(ip))
+ ]
+ no_of_ips = IPv4Network(ensure_text(ip)).num_addresses
+ if ip_network(ip).version == 6:
+ ips = [
+ to_text(usable_ips)
+ for usable_ips in IPv6Network(ensure_text(ip))
+ ]
+ no_of_ips = IPv6Network(ensure_text(ip)).num_addresses
+
+ except Exception as e:
+ raise AnsibleFilterError(
+ "Error while using plugin 'usable_range': {msg}".format(
+ msg=to_text(e)
+ )
+ )
+
+ return {"usable_ips": ips, "number_of_ips": no_of_ips}
+
+
+class FilterModule(object):
+ """ usable_range """
+
+ def filters(self):
+
+ """a mapping of filter names to functions"""
+ return {"usable_range": _usable_range}
diff --git a/tests/integration/targets/usable_range/tasks/include/argspec.yml b/tests/integration/targets/usable_range/tasks/include/argspec.yml
new file mode 100644
index 0000000..0faba9a
--- /dev/null
+++ b/tests/integration/targets/usable_range/tasks/include/argspec.yml
@@ -0,0 +1,77 @@
+---
+- name: Check argspec validation with filter (missing arg)
+ ansible.builtin.set_fact:
+ _result1: "{{ '' | ansible.utils.usable_range }}"
+ ignore_errors: true
+ register: result1
+
+- assert:
+ that: "{{ msg in result1.msg }}"
+ vars:
+ msg: "does not appear to be an IPv4 or IPv6 network"
+
+- name: Check argspec validation with filter (random string arg)
+ ansible.builtin.set_fact:
+ _result2: "{{ 'helloworld' | ansible.utils.usable_range }}"
+ ignore_errors: true
+ register: result2
+
+- assert:
+ that: "{{ msg in result2.msg }}"
+ vars:
+ msg: "does not appear to be an IPv4 or IPv6 network"
+
+- name: Check argspec validation with filter (invalid arg for expansion)
+ ansible.builtin.set_fact:
+ _result3: "{{ '192.168.1.25/24' | ansible.utils.usable_range }}"
+ ignore_errors: true
+ register: result3
+
+- assert:
+ that: "{{ msg in result3.msg }}"
+ vars:
+ msg: "has host bits set"
+
+- name: Check argspec validation with filter (invalid format for arg)
+ ansible.builtin.set_fact:
+ _result4: "{{ '192.0.2.0/23/24' | ansible.utils.usable_range }}"
+ ignore_errors: true
+ register: result4
+
+- assert:
+ that: "{{ msg in result4.msg }}"
+ vars:
+ msg: "does not appear to be an IPv4 or IPv6 network"
+
+- name: Check argspec validation with filter (invalid format for arg)
+ ansible.builtin.set_fact:
+ _result5: "{{ '::/20/30' | ansible.utils.usable_range }}"
+ ignore_errors: true
+ register: result5
+
+- assert:
+ that: "{{ msg in result5.msg }}"
+ vars:
+ msg: "does not appear to be an IPv4 or IPv6 network"
+
+- name: Check argspec validation with filter (invalid netmask)
+ ansible.builtin.set_fact:
+ _result6: "{{ '10.0.0.0/322' | ansible.utils.usable_range }}"
+ ignore_errors: true
+ register: result6
+
+- assert:
+ that: "{{ msg in result6.msg }}"
+ vars:
+ msg: "does not appear to be an IPv4 or IPv6 network"
+
+- name: Check argspec validation with filter (invalid netmask)
+ ansible.builtin.set_fact:
+ _result7: "{{ '2001:db8:abcd:0012::0/129' | ansible.utils.usable_range }}"
+ ignore_errors: true
+ register: result7
+
+- assert:
+ that: "{{ msg in result7.msg }}"
+ vars:
+ msg: "does not appear to be an IPv4 or IPv6 network"
diff --git a/tests/integration/targets/usable_range/tasks/include/example_filter.yml b/tests/integration/targets/usable_range/tasks/include/example_filter.yml
new file mode 100644
index 0000000..d68176e
--- /dev/null
+++ b/tests/integration/targets/usable_range/tasks/include/example_filter.yml
@@ -0,0 +1,43 @@
+---
+# IPv4
+- name: Expand and produce list of usable IP addresses in 10.1.1.1
+ ansible.builtin.set_fact:
+ result1: "{{ '10.1.1.1' | ansible.utils.usable_range }}"
+
+- name: Assert result for 10.1.1.1
+ assert:
+ that: "{{ result1 == result1_val }}"
+
+- name: Expand and produce list of usable IP addresses in 10.0.0.0/28
+ ansible.builtin.set_fact:
+ result2: "{{ '10.0.0.0/28' | ansible.utils.usable_range }}"
+
+- name: Assert result for 10.0.0.0/28
+ assert:
+ that: "{{ result2 == result2_val }}"
+
+- name: Expand and produce list of usable IP addresses in 192.0.2.0/24
+ ansible.builtin.set_fact:
+ result3: "{{ '192.0.2.0/24' | ansible.utils.usable_range }}"
+
+- name: Assert result for 192.0.2.0/24
+ assert:
+ # Since the list is huge, asserting only on number of ips
+ that: "{{ result3.number_of_ips == result3_val.number_of_ips }}"
+
+# IPv6
+- name: Expand and produce list of usable IP addresses in 2001:db8:abcd:0012::0/126
+ ansible.builtin.set_fact:
+ result4: "{{ '2001:db8:abcd:0012::0/126' | ansible.utils.usable_range }}"
+
+- name: Assert result for 2001:db8:abcd:0012::0/126
+ assert:
+ that: "{{ result4 == result4_val }}"
+
+- name: "Expand and produce list of usable IP addresses in 2001:db8:abcd:12::"
+ ansible.builtin.set_fact:
+ result5: "{{ '2001:db8:abcd:12::' | ansible.utils.usable_range }}"
+
+- name: "Assert result for 2001:db8:abcd:12::"
+ assert:
+ that: "{{ result5 == result5_val }}"
diff --git a/tests/integration/targets/usable_range/tasks/main.yml b/tests/integration/targets/usable_range/tasks/main.yml
new file mode 100644
index 0000000..b1ea41b
--- /dev/null
+++ b/tests/integration/targets/usable_range/tasks/main.yml
@@ -0,0 +1,13 @@
+---
+- name: Recursively find all test files
+ find:
+ file_type: file
+ paths: "{{ role_path }}/tasks/include"
+ recurse: true
+ use_regex: true
+ patterns:
+ - "^(?!_).+$"
+ register: found
+
+- include: "{{ item.path }}"
+ loop: "{{ found.files }}"
diff --git a/tests/integration/targets/usable_range/vars/main.yml b/tests/integration/targets/usable_range/vars/main.yml
new file mode 100644
index 0000000..800517f
--- /dev/null
+++ b/tests/integration/targets/usable_range/vars/main.yml
@@ -0,0 +1,41 @@
+---
+result1_val:
+ number_of_ips: 1
+ usable_ips:
+ - "10.1.1.1"
+
+result2_val:
+ number_of_ips: 16
+ usable_ips:
+ - "10.0.0.0"
+ - "10.0.0.1"
+ - "10.0.0.2"
+ - "10.0.0.3"
+ - "10.0.0.4"
+ - "10.0.0.5"
+ - "10.0.0.6"
+ - "10.0.0.7"
+ - "10.0.0.8"
+ - "10.0.0.9"
+ - "10.0.0.10"
+ - "10.0.0.11"
+ - "10.0.0.12"
+ - "10.0.0.13"
+ - "10.0.0.14"
+ - "10.0.0.15"
+
+result3_val:
+ number_of_ips: 256
+
+result4_val:
+ number_of_ips: 4
+ usable_ips:
+ - "2001:db8:abcd:12::"
+ - "2001:db8:abcd:12::1"
+ - "2001:db8:abcd:12::2"
+ - "2001:db8:abcd:12::3"
+
+result5_val:
+ number_of_ips: 1
+ usable_ips:
+ - "2001:db8:abcd:12::"
diff --git a/tests/unit/plugins/filter/test_usable_range.py b/tests/unit/plugins/filter/test_usable_range.py
new file mode 100644
index 0000000..dc6bd87
--- /dev/null
+++ b/tests/unit/plugins/filter/test_usable_range.py
@@ -0,0 +1,125 @@
+# -*- 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 usable_range filter plugin
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import unittest
+from ansible.errors import AnsibleError
+from ansible_collections.ansible.utils.plugins.filter.usable_range import (
+ _usable_range,
+)
+
+INVALID_DATA_1 = [
+ "helloworld",
+ "192.0.2.0/23/24",
+ "::/20/30",
+ "10.0.0.0/322",
+ "2001:db8:abcd:0012::0/129",
+]
+INVALID_DATA_2 = ["192.168.1.25/24", "2001:db8:abcd:12::2/126"]
+
+VALID_DATA = [
+ "10.0.0.8/30",
+ "192.0.2.0/28",
+ "2001:db8:abcd:0012::0/126",
+ "2001:DB8:ABCD:12::",
+]
+
+VALID_OUTPUT_1 = {
+ "number_of_ips": 4,
+ "usable_ips": ["10.0.0.8", "10.0.0.9", "10.0.0.10", "10.0.0.11"],
+}
+VALID_OUTPUT_2 = {
+ "number_of_ips": 16,
+ "usable_ips": [
+ "192.0.2.0",
+ "192.0.2.1",
+ "192.0.2.2",
+ "192.0.2.3",
+ "192.0.2.4",
+ "192.0.2.5",
+ "192.0.2.6",
+ "192.0.2.7",
+ "192.0.2.8",
+ "192.0.2.9",
+ "192.0.2.10",
+ "192.0.2.11",
+ "192.0.2.12",
+ "192.0.2.13",
+ "192.0.2.14",
+ "192.0.2.15",
+ ],
+}
+
+VALID_OUTPUT_3 = {
+ "number_of_ips": 4,
+ "usable_ips": [
+ "2001:db8:abcd:12::",
+ "2001:db8:abcd:12::1",
+ "2001:db8:abcd:12::2",
+ "2001:db8:abcd:12::3",
+ ],
+}
+VALID_OUTPUT_4 = {"number_of_ips": 1, "usable_ips": ["2001:db8:abcd:12::"]}
+
+
+class TestUsableRange(unittest.TestCase):
+ def setUp(self):
+ pass
+
+ def test_missing_data(self):
+ """Check passing missing argspec"""
+
+ # missing required arguments
+ ip = ""
+ with self.assertRaises(AnsibleError) as error:
+ _usable_range(ip)
+ self.assertIn(
+ "does not appear to be an IPv4 or IPv6 network",
+ str(error.exception),
+ )
+
+ def test_invalid_data(self):
+ """Check passing invalid argspec"""
+
+ # invalid required arguments
+
+ for invalid_data in INVALID_DATA_1:
+ with self.assertRaises(AnsibleError) as error:
+ _usable_range(invalid_data)
+ self.assertIn(
+ "does not appear to be an IPv4 or IPv6 network",
+ str(error.exception),
+ )
+
+ for invalid_data in INVALID_DATA_2:
+ with self.assertRaises(AnsibleError) as error:
+ _usable_range(invalid_data)
+ self.assertIn("has host bits set", str(error.exception))
+
+ def test_valid_data(self):
+ """Check passing valid data as per criteria"""
+
+ ip = VALID_DATA[0]
+ result = _usable_range(ip)
+ self.assertEqual(result, VALID_OUTPUT_1)
+
+ ip = VALID_DATA[1]
+ result = _usable_range(ip)
+ self.assertEqual(result, VALID_OUTPUT_2)
+
+ ip = VALID_DATA[2]
+ result = _usable_range(ip)
+ self.assertEqual(result, VALID_OUTPUT_3)
+
+ ip = VALID_DATA[3]
+ result = _usable_range(ip)
+ self.assertEqual(result, VALID_OUTPUT_4)