Add ipv4 plugin (#120)
Add IPV4 filter plugin SUMMARY ISSUE TYPE New Module Pull Request COMPONENT NAME ADDITIONAL INFORMATION Reviewed-by: Nathaniel Case <this.is@nathanielca.se> Reviewed-by: Priyam Sahoo <None> Reviewed-by: None <None>pull/127/head^2
parent
8d2ad3811f
commit
02f2c62386
|
@ -24,6 +24,7 @@ Name | Description
|
|||
[ansible.utils.from_xml](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.from_xml_filter.rst)|Convert given XML string to native python dictionary.
|
||||
[ansible.utils.get_path](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.get_path_filter.rst)|Retrieve the value in a variable using a path
|
||||
[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.ipv4](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.ipv4_filter.rst)|To filter only Ipv4 addresses Ipv4 filter is used.
|
||||
[ansible.utils.param_list_compare](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.param_list_compare_filter.rst)|Generate the final param list combining/comparing base and provided parameters.
|
||||
[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
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
minor_changes:
|
||||
- Add ipv4 filter plugin.
|
|
@ -0,0 +1,184 @@
|
|||
.. _ansible.utils.ipv4_filter:
|
||||
|
||||
|
||||
******************
|
||||
ansible.utils.ipv4
|
||||
******************
|
||||
|
||||
**To filter only Ipv4 addresses Ipv4 filter is used.**
|
||||
|
||||
|
||||
Version added: 2.5.0
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
- Sometimes you need only IPv4 addresses. To filter only Ipv4 addresses Ipv4 filter is used.
|
||||
|
||||
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="1">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th>Configuration</th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>query</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">""</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>You can provide a single argument to each ipv4() filter.</div>
|
||||
<div>Example. query type 'ipv6' to convert ipv4 into ipv6</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>value</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">list</span>
|
||||
/ <span style="color: purple">elements=string</span>
|
||||
/ <span style="color: red">required</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>list of subnets or individual address or any other values input for ipv4 plugin</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
#### examples
|
||||
# Ipv4 filter plugin with different queries.
|
||||
- name: Set value as input list
|
||||
ansible.builtin.set_fact:
|
||||
value:
|
||||
- 192.24.2.1
|
||||
- host.fqdn
|
||||
- ::1
|
||||
- ''
|
||||
- 192.168.32.0/24
|
||||
- fe80::100/10
|
||||
- 42540766412265424405338506004571095040/64
|
||||
- True
|
||||
- name: IPv4 filter to filter Ipv4 Address
|
||||
debug:
|
||||
msg: "{{ value|ansible.utils.ipv4 }}"
|
||||
|
||||
- name: convert IPv4 addresses into IPv6 addresses.
|
||||
debug:
|
||||
msg: "{{ value|ansible.utils.ipv4('ipv6') }}"
|
||||
|
||||
- name: convert IPv4 addresses into IPv6 addresses.
|
||||
debug:
|
||||
msg: "{{ value|ansible.utils.ipv4('address') }}"
|
||||
|
||||
|
||||
# PLAY [Ipv4 filter plugin with different queries.] ******************************************************************
|
||||
# TASK [Set value as input list] ***************************************************************************************
|
||||
# ok: [localhost] => {"ansible_facts": {"value": ["192.24.2.1", "host.fqdn", "::1", "", "192.168.32.0/24",
|
||||
# "fe80::100/10", "42540766412265424405338506004571095040/64", true]}, "changed": false}
|
||||
# TASK [IPv4 filter to filter Ipv4 Address] *******************************************************************
|
||||
# ok: [localhost] => {
|
||||
# "msg": [
|
||||
# "192.24.2.1",
|
||||
# "192.168.32.0/24"
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
# TASK [convert IPv4 addresses into IPv6 addresses.] **********************************************************
|
||||
# ok: [localhost] => {
|
||||
# "msg": [
|
||||
# "::ffff:192.24.2.1/128",
|
||||
# "::ffff:192.168.32.0/120"
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
# TASK [convert IPv4 addresses into IPv6 addresses.] **********************************************************
|
||||
# ok: [localhost] => {
|
||||
# "msg": [
|
||||
# "192.24.2.1"
|
||||
# ]
|
||||
# }
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this filter:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="1">Key</th>
|
||||
<th>Returned</th>
|
||||
<th width="100%">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>data</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">list</span>
|
||||
/ <span style="color: purple">elements=string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<div>Returns list with values valid for a particular query.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/><br/>
|
||||
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
- Ashwini Mhatre (@amhatre)
|
||||
|
||||
|
||||
.. 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.
|
|
@ -0,0 +1,161 @@
|
|||
# -*- 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 ipaddr filters: ipv4
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
from functools import partial
|
||||
from ansible_collections.ansible.utils.plugins.plugin_utils.base.ipaddr_utils import (
|
||||
ipaddr,
|
||||
_need_netaddr,
|
||||
)
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
||||
AnsibleArgSpecValidator,
|
||||
)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
try:
|
||||
from jinja2.filters import pass_environment
|
||||
except ImportError:
|
||||
from jinja2.filters import environmentfilter as pass_environment
|
||||
|
||||
try:
|
||||
import netaddr
|
||||
except ImportError:
|
||||
# in this case, we'll make the filters return error messages (see bottom)
|
||||
netaddr = None
|
||||
else:
|
||||
|
||||
class mac_linux(netaddr.mac_unix):
|
||||
pass
|
||||
|
||||
mac_linux.word_fmt = "%.2x"
|
||||
|
||||
DOCUMENTATION = """
|
||||
name: ipv4
|
||||
author: Ashwini Mhatre (@amhatre)
|
||||
version_added: "2.5.0"
|
||||
short_description: To filter only Ipv4 addresses Ipv4 filter is used.
|
||||
description:
|
||||
- Sometimes you need only IPv4 addresses. To filter only Ipv4 addresses Ipv4 filter is used.
|
||||
options:
|
||||
value:
|
||||
description:
|
||||
- list of subnets or individual address or any other values input for ipv4 plugin
|
||||
type: list
|
||||
elements: str
|
||||
required: True
|
||||
query:
|
||||
description:
|
||||
- You can provide a single argument to each ipv4() filter.
|
||||
- Example. query type 'ipv6' to convert ipv4 into ipv6
|
||||
type: str
|
||||
default: ''
|
||||
notes:
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
#### examples
|
||||
# Ipv4 filter plugin with different queries.
|
||||
- name: Set value as input list
|
||||
ansible.builtin.set_fact:
|
||||
value:
|
||||
- 192.24.2.1
|
||||
- host.fqdn
|
||||
- ::1
|
||||
- ''
|
||||
- 192.168.32.0/24
|
||||
- fe80::100/10
|
||||
- 42540766412265424405338506004571095040/64
|
||||
- True
|
||||
- name: IPv4 filter to filter Ipv4 Address
|
||||
debug:
|
||||
msg: "{{ value|ansible.utils.ipv4 }}"
|
||||
|
||||
- name: convert IPv4 addresses into IPv6 addresses.
|
||||
debug:
|
||||
msg: "{{ value|ansible.utils.ipv4('ipv6') }}"
|
||||
|
||||
- name: convert IPv4 addresses into IPv6 addresses.
|
||||
debug:
|
||||
msg: "{{ value|ansible.utils.ipv4('address') }}"
|
||||
|
||||
|
||||
# PLAY [Ipv4 filter plugin with different queries.] ******************************************************************
|
||||
# TASK [Set value as input list] ***************************************************************************************
|
||||
# ok: [localhost] => {"ansible_facts": {"value": ["192.24.2.1", "host.fqdn", "::1", "", "192.168.32.0/24",
|
||||
# "fe80::100/10", "42540766412265424405338506004571095040/64", true]}, "changed": false}
|
||||
# TASK [IPv4 filter to filter Ipv4 Address] *******************************************************************
|
||||
# ok: [localhost] => {
|
||||
# "msg": [
|
||||
# "192.24.2.1",
|
||||
# "192.168.32.0/24"
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
# TASK [convert IPv4 addresses into IPv6 addresses.] **********************************************************
|
||||
# ok: [localhost] => {
|
||||
# "msg": [
|
||||
# "::ffff:192.24.2.1/128",
|
||||
# "::ffff:192.168.32.0/120"
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
# TASK [convert IPv4 addresses into IPv6 addresses.] **********************************************************
|
||||
# ok: [localhost] => {
|
||||
# "msg": [
|
||||
# "192.24.2.1"
|
||||
# ]
|
||||
# }
|
||||
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
data:
|
||||
type: list
|
||||
elements: str
|
||||
description:
|
||||
- Returns list with values valid for a particular query.
|
||||
"""
|
||||
|
||||
|
||||
@pass_environment
|
||||
def _ipv4(*args, **kwargs):
|
||||
"""This filter is designed to return the input value if a query is True, and False if a query is False"""
|
||||
keys = ["value", "query"]
|
||||
data = dict(zip(keys, args[1:]))
|
||||
data.update(kwargs)
|
||||
aav = AnsibleArgSpecValidator(data=data, schema=DOCUMENTATION, name="ipv4")
|
||||
valid, errors, updated_data = aav.validate()
|
||||
if not valid:
|
||||
raise AnsibleFilterError(errors)
|
||||
return ipv4(**updated_data)
|
||||
|
||||
|
||||
def ipv4(value, query=""):
|
||||
return ipaddr(value, query, version=4, alias="ipv4")
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
"""IP address and network manipulation filters
|
||||
"""
|
||||
|
||||
filter_map = {
|
||||
# IP addresses and networks
|
||||
"ipv4": _ipv4
|
||||
}
|
||||
|
||||
def filters(self):
|
||||
""" ipaddr filter """
|
||||
if netaddr:
|
||||
return self.filter_map
|
||||
else:
|
||||
return dict(
|
||||
(f, partial(_need_netaddr, f)) for f in self.filter_map
|
||||
)
|
|
@ -0,0 +1,606 @@
|
|||
# -*- 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)
|
||||
|
||||
"""
|
||||
The utils file for all ipaddr filters
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from ansible.utils.display import Display
|
||||
import types
|
||||
from ansible.errors import AnsibleFilterError
|
||||
|
||||
try:
|
||||
import netaddr
|
||||
except ImportError:
|
||||
# in this case, we'll make the filters return error messages (see bottom)
|
||||
netaddr = None
|
||||
else:
|
||||
|
||||
class mac_linux(netaddr.mac_unix):
|
||||
pass
|
||||
|
||||
mac_linux.word_fmt = "%.2x"
|
||||
|
||||
display = Display()
|
||||
|
||||
|
||||
# ---- IP address and network query helpers ----
|
||||
def _empty_ipaddr_query(v, vtype):
|
||||
# We don't have any query to process, so just check what type the user
|
||||
# expects, and return the IP address in a correct format
|
||||
if v:
|
||||
if vtype == "address":
|
||||
return str(v.ip)
|
||||
elif vtype == "network":
|
||||
return str(v)
|
||||
|
||||
|
||||
def _first_last(v):
|
||||
if v.size == 2:
|
||||
first_usable = int(netaddr.IPAddress(v.first))
|
||||
last_usable = int(netaddr.IPAddress(v.last))
|
||||
return first_usable, last_usable
|
||||
elif v.size > 1:
|
||||
first_usable = int(netaddr.IPAddress(v.first + 1))
|
||||
last_usable = int(netaddr.IPAddress(v.last - 1))
|
||||
return first_usable, last_usable
|
||||
|
||||
|
||||
def _6to4_query(v, vtype, value):
|
||||
if v.version == 4:
|
||||
if v.size == 1:
|
||||
ipconv = str(v.ip)
|
||||
elif v.size > 1:
|
||||
if v.ip != v.network:
|
||||
ipconv = str(v.ip)
|
||||
else:
|
||||
return False
|
||||
|
||||
if ipaddr(ipconv, "public") or ipaddr(ipconv, "private"):
|
||||
numbers = list(map(int, ipconv.split(".")))
|
||||
|
||||
try:
|
||||
return "2002:{:02x}{:02x}:{:02x}{:02x}::1/48".format(*numbers)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
elif v.version == 6:
|
||||
if vtype == "address":
|
||||
if ipaddr(str(v), "2002::/16"):
|
||||
return value
|
||||
elif vtype == "network":
|
||||
if v.ip != v.network:
|
||||
if ipaddr(str(v.ip), "2002::/16"):
|
||||
return value
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _ip_query(v):
|
||||
if v.size == 1:
|
||||
return str(v.ip)
|
||||
if v.size > 1:
|
||||
# /31 networks in netaddr have no broadcast address
|
||||
if v.ip != v.network or not v.broadcast:
|
||||
return str(v.ip)
|
||||
# For the first IPv6 address in a network, netaddr will return it as a network address, despite it being a valid host address.
|
||||
elif v.version == 6 and v.ip == v.network:
|
||||
return str(v.ip)
|
||||
|
||||
|
||||
def _address_prefix_query(v):
|
||||
if v.size > 2 and v.ip in (v.network, v.broadcast):
|
||||
return False
|
||||
return str(v.ip) + "/" + str(v.prefixlen)
|
||||
|
||||
|
||||
def _bool_ipaddr_query(v):
|
||||
if v:
|
||||
return True
|
||||
|
||||
|
||||
def _broadcast_query(v):
|
||||
if v.size > 2:
|
||||
return str(v.broadcast)
|
||||
|
||||
|
||||
def _cidr_query(v):
|
||||
return str(v)
|
||||
|
||||
|
||||
def _cidr_lookup_query(v, iplist, value):
|
||||
try:
|
||||
if v in iplist:
|
||||
return value
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _first_usable_query(v, vtype):
|
||||
if vtype == "address":
|
||||
# Does it make sense to raise an error
|
||||
raise AnsibleFilterError("Not a network address")
|
||||
elif vtype == "network":
|
||||
if v.size == 2:
|
||||
return str(netaddr.IPAddress(int(v.network)))
|
||||
elif v.size > 1:
|
||||
return str(netaddr.IPAddress(int(v.network) + 1))
|
||||
|
||||
|
||||
def _host_query(v):
|
||||
if v.size == 1:
|
||||
return str(v)
|
||||
elif v.size > 1:
|
||||
if v.ip != v.network or not v.broadcast:
|
||||
return str(v.ip) + "/" + str(v.prefixlen)
|
||||
|
||||
|
||||
def _hostmask_query(v):
|
||||
return str(v.hostmask)
|
||||
|
||||
|
||||
def _int_query(v, vtype):
|
||||
if vtype == "address":
|
||||
return int(v.ip)
|
||||
elif vtype == "network":
|
||||
return str(int(v.ip)) + "/" + str(int(v.prefixlen))
|
||||
|
||||
|
||||
def _ip_prefix_query(v):
|
||||
if v.size == 2:
|
||||
return str(v.ip) + "/" + str(v.prefixlen)
|
||||
elif v.size > 1:
|
||||
if v.ip != v.network:
|
||||
return str(v.ip) + "/" + str(v.prefixlen)
|
||||
|
||||
|
||||
def _ip_netmask_query(v):
|
||||
if v.size == 2:
|
||||
return str(v.ip) + " " + str(v.netmask)
|
||||
elif v.size > 1:
|
||||
if v.ip != v.network:
|
||||
return str(v.ip) + " " + str(v.netmask)
|
||||
|
||||
|
||||
def _ipv4_query(v, value):
|
||||
if v.version == 6:
|
||||
try:
|
||||
return str(v.ipv4())
|
||||
except Exception:
|
||||
return False
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def _ipv6_query(v, value):
|
||||
if v.version == 4:
|
||||
return str(v.ipv6())
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def _last_usable_query(v, vtype):
|
||||
if vtype == "address":
|
||||
# Does it make sense to raise an error
|
||||
raise AnsibleFilterError("Not a network address")
|
||||
elif vtype == "network":
|
||||
if v.size > 1:
|
||||
first_usable, last_usable = _first_last(v)
|
||||
return str(netaddr.IPAddress(last_usable))
|
||||
|
||||
|
||||
def _link_local_query(v, value):
|
||||
v_ip = netaddr.IPAddress(str(v.ip))
|
||||
if v.version == 4:
|
||||
if ipaddr(str(v_ip), "169.254.0.0/24"):
|
||||
return value
|
||||
|
||||
elif v.version == 6:
|
||||
if ipaddr(str(v_ip), "fe80::/10"):
|
||||
return value
|
||||
|
||||
|
||||
def _loopback_query(v, value):
|
||||
v_ip = netaddr.IPAddress(str(v.ip))
|
||||
if v_ip.is_loopback():
|
||||
return value
|
||||
|
||||
|
||||
def _multicast_query(v, value):
|
||||
if v.is_multicast():
|
||||
return value
|
||||
|
||||
|
||||
def _net_query(v):
|
||||
if v.size > 1:
|
||||
if v.ip == v.network:
|
||||
return str(v.network) + "/" + str(v.prefixlen)
|
||||
|
||||
|
||||
def _netmask_query(v):
|
||||
return str(v.netmask)
|
||||
|
||||
|
||||
def _network_query(v):
|
||||
"""Return the network of a given IP or subnet"""
|
||||
return str(v.network)
|
||||
|
||||
|
||||
def _network_netmask_query(v):
|
||||
return str(v.network) + " " + str(v.netmask)
|
||||
|
||||
|
||||
def _network_wildcard_query(v):
|
||||
return str(v.network) + " " + str(v.hostmask)
|
||||
|
||||
|
||||
def _next_usable_query(v, vtype):
|
||||
if vtype == "address":
|
||||
# Does it make sense to raise an error
|
||||
raise AnsibleFilterError("Not a network address")
|
||||
elif vtype == "network":
|
||||
if v.size > 1:
|
||||
first_usable, last_usable = _first_last(v)
|
||||
next_ip = int(netaddr.IPAddress(int(v.ip) + 1))
|
||||
if next_ip >= first_usable and next_ip <= last_usable:
|
||||
return str(netaddr.IPAddress(int(v.ip) + 1))
|
||||
|
||||
|
||||
def _peer_query(v, vtype):
|
||||
if vtype == "address":
|
||||
raise AnsibleFilterError("Not a network address")
|
||||
elif vtype == "network":
|
||||
if v.size == 2:
|
||||
return str(netaddr.IPAddress(int(v.ip) ^ 1))
|
||||
if v.size == 4:
|
||||
if int(v.ip) % 4 == 0:
|
||||
raise AnsibleFilterError("Network address of /30 has no peer")
|
||||
if int(v.ip) % 4 == 3:
|
||||
raise AnsibleFilterError(
|
||||
"Broadcast address of /30 has no peer"
|
||||
)
|
||||
return str(netaddr.IPAddress(int(v.ip) ^ 3))
|
||||
raise AnsibleFilterError("Not a point-to-point network")
|
||||
|
||||
|
||||
def _prefix_query(v):
|
||||
return int(v.prefixlen)
|
||||
|
||||
|
||||
def _previous_usable_query(v, vtype):
|
||||
if vtype == "address":
|
||||
# Does it make sense to raise an error
|
||||
raise AnsibleFilterError("Not a network address")
|
||||
elif vtype == "network":
|
||||
if v.size > 1:
|
||||
first_usable, last_usable = _first_last(v)
|
||||
previous_ip = int(netaddr.IPAddress(int(v.ip) - 1))
|
||||
if previous_ip >= first_usable and previous_ip <= last_usable:
|
||||
return str(netaddr.IPAddress(int(v.ip) - 1))
|
||||
|
||||
|
||||
def _private_query(v, value):
|
||||
if v.is_private():
|
||||
return value
|
||||
|
||||
|
||||
def _public_query(v, value):
|
||||
v_ip = netaddr.IPAddress(str(v.ip))
|
||||
if all(
|
||||
[
|
||||
v_ip.is_unicast(),
|
||||
not v_ip.is_private(),
|
||||
not v_ip.is_loopback(),
|
||||
not v_ip.is_netmask(),
|
||||
not v_ip.is_hostmask(),
|
||||
]
|
||||
):
|
||||
return value
|
||||
|
||||
|
||||
def _range_usable_query(v, vtype):
|
||||
if vtype == "address":
|
||||
# Does it make sense to raise an error
|
||||
raise AnsibleFilterError("Not a network address")
|
||||
elif vtype == "network":
|
||||
if v.size > 1:
|
||||
first_usable, last_usable = _first_last(v)
|
||||
first_usable = str(netaddr.IPAddress(first_usable))
|
||||
last_usable = str(netaddr.IPAddress(last_usable))
|
||||
return "{0}-{1}".format(first_usable, last_usable)
|
||||
|
||||
|
||||
def _revdns_query(v):
|
||||
v_ip = netaddr.IPAddress(str(v.ip))
|
||||
return v_ip.reverse_dns
|
||||
|
||||
|
||||
def _size_query(v):
|
||||
return v.size
|
||||
|
||||
|
||||
def _size_usable_query(v):
|
||||
if v.size == 1:
|
||||
return 0
|
||||
elif v.size == 2:
|
||||
return 2
|
||||
return v.size - 2
|
||||
|
||||
|
||||
def _subnet_query(v):
|
||||
return str(v.cidr)
|
||||
|
||||
|
||||
def _type_query(v):
|
||||
if v.size == 1:
|
||||
return "address"
|
||||
if v.size > 1:
|
||||
if v.ip != v.network:
|
||||
return "address"
|
||||
else:
|
||||
return "network"
|
||||
|
||||
|
||||
def _unicast_query(v, value):
|
||||
if v.is_unicast():
|
||||
return value
|
||||
|
||||
|
||||
def _version_query(v):
|
||||
return v.version
|
||||
|
||||
|
||||
def _wrap_query(v, vtype, value):
|
||||
if v.version == 6:
|
||||
if vtype == "address":
|
||||
return "[" + str(v.ip) + "]"
|
||||
elif vtype == "network":
|
||||
return "[" + str(v.ip) + "]/" + str(v.prefixlen)
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def ipaddr(value, query="", version=False, alias="ipaddr"):
|
||||
""" Check if string is an IP address or network and filter it """
|
||||
|
||||
query_func_extra_args = {
|
||||
"": ("vtype",),
|
||||
"6to4": ("vtype", "value"),
|
||||
"cidr_lookup": ("iplist", "value"),
|
||||
"first_usable": ("vtype",),
|
||||
"int": ("vtype",),
|
||||
"ipv4": ("value",),
|
||||
"ipv6": ("value",),
|
||||
"last_usable": ("vtype",),
|
||||
"link-local": ("value",),
|
||||
"loopback": ("value",),
|
||||
"lo": ("value",),
|
||||
"multicast": ("value",),
|
||||
"next_usable": ("vtype",),
|
||||
"peer": ("vtype",),
|
||||
"previous_usable": ("vtype",),
|
||||
"private": ("value",),
|
||||
"public": ("value",),
|
||||
"unicast": ("value",),
|
||||
"range_usable": ("vtype",),
|
||||
"wrap": ("vtype", "value"),
|
||||
}
|
||||
|
||||
query_func_map = {
|
||||
"": _empty_ipaddr_query,
|
||||
"6to4": _6to4_query,
|
||||
"address": _ip_query,
|
||||
"address/prefix": _address_prefix_query, # deprecate
|
||||
"bool": _bool_ipaddr_query,
|
||||
"broadcast": _broadcast_query,
|
||||
"cidr": _cidr_query,
|
||||
"cidr_lookup": _cidr_lookup_query,
|
||||
"first_usable": _first_usable_query,
|
||||
"gateway": _address_prefix_query, # deprecate
|
||||
"gw": _address_prefix_query, # deprecate
|
||||
"host": _host_query,
|
||||
"host/prefix": _address_prefix_query, # deprecate
|
||||
"hostmask": _hostmask_query,
|
||||
"hostnet": _address_prefix_query, # deprecate
|
||||
"int": _int_query,
|
||||
"ip": _ip_query,
|
||||
"ip/prefix": _ip_prefix_query,
|
||||
"ip_netmask": _ip_netmask_query,
|
||||
# 'ip_wildcard': _ip_wildcard_query, built then could not think of use case
|
||||
"ipv4": _ipv4_query,
|
||||
"ipv6": _ipv6_query,
|
||||
"last_usable": _last_usable_query,
|
||||
"link-local": _link_local_query,
|
||||
"lo": _loopback_query,
|
||||
"loopback": _loopback_query,
|
||||
"multicast": _multicast_query,
|
||||
"net": _net_query,
|
||||
"next_usable": _next_usable_query,
|
||||
"netmask": _netmask_query,
|
||||
"network": _network_query,
|
||||
"network_id": _network_query,
|
||||
"network/prefix": _subnet_query,
|
||||
"network_netmask": _network_netmask_query,
|
||||
"network_wildcard": _network_wildcard_query,
|
||||
"peer": _peer_query,
|
||||
"prefix": _prefix_query,
|
||||
"previous_usable": _previous_usable_query,
|
||||
"private": _private_query,
|
||||
"public": _public_query,
|
||||
"range_usable": _range_usable_query,
|
||||
"revdns": _revdns_query,
|
||||
"router": _address_prefix_query, # deprecate
|
||||
"size": _size_query,
|
||||
"size_usable": _size_usable_query,
|
||||
"subnet": _subnet_query,
|
||||
"type": _type_query,
|
||||
"unicast": _unicast_query,
|
||||
"v4": _ipv4_query,
|
||||
"v6": _ipv6_query,
|
||||
"version": _version_query,
|
||||
"wildcard": _hostmask_query,
|
||||
"wrap": _wrap_query,
|
||||
}
|
||||
|
||||
vtype = None
|
||||
|
||||
# Check if value is a list and parse each element
|
||||
if isinstance(value, (list, tuple, types.GeneratorType)):
|
||||
_ret = [ipaddr(element, str(query), version) for element in value]
|
||||
return [item for item in _ret if item]
|
||||
|
||||
elif not value or value is True:
|
||||
# TODO: Remove this check in a major version release of collection with porting guide
|
||||
# TODO: and raise exception commented out below
|
||||
display.warning(
|
||||
"The value '%s' is not a valid IP address or network, passing this value to ipaddr filter"
|
||||
" might result in breaking change in future." % value
|
||||
)
|
||||
return False
|
||||
|
||||
# Check if value is a number and convert it to an IP address
|
||||
elif str(value).isdigit():
|
||||
|
||||
# We don't know what IP version to assume, so let's check IPv4 first,
|
||||
# then IPv6
|
||||
try:
|
||||
if (not version) or (version and version == 4):
|
||||
v = netaddr.IPNetwork("0.0.0.0/0")
|
||||
v.value = int(value)
|
||||
v.prefixlen = 32
|
||||
elif version and version == 6:
|
||||
v = netaddr.IPNetwork("::/0")
|
||||
v.value = int(value)
|
||||
v.prefixlen = 128
|
||||
|
||||
# IPv4 didn't work the first time, so it definitely has to be IPv6
|
||||
except Exception:
|
||||
try:
|
||||
v = netaddr.IPNetwork("::/0")
|
||||
v.value = int(value)
|
||||
v.prefixlen = 128
|
||||
|
||||
# The value is too big for IPv6. Are you a nanobot?
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
# We got an IP address, let's mark it as such
|
||||
value = str(v)
|
||||
vtype = "address"
|
||||
|
||||
# value has not been recognized, check if it's a valid IP string
|
||||
else:
|
||||
try:
|
||||
v = netaddr.IPNetwork(value)
|
||||
|
||||
# value is a valid IP string, check if user specified
|
||||
# CIDR prefix or just an IP address, this will indicate default
|
||||
# output format
|
||||
try:
|
||||
address, prefix = value.split("/")
|
||||
vtype = "network"
|
||||
except Exception:
|
||||
vtype = "address"
|
||||
|
||||
# value hasn't been recognized, maybe it's a numerical CIDR?
|
||||
except Exception:
|
||||
try:
|
||||
address, prefix = value.split("/")
|
||||
address.isdigit()
|
||||
address = int(address)
|
||||
prefix.isdigit()
|
||||
prefix = int(prefix)
|
||||
|
||||
# It's not numerical CIDR, give up
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
# It is something, so let's try and build a CIDR from the parts
|
||||
try:
|
||||
v = netaddr.IPNetwork("0.0.0.0/0")
|
||||
v.value = address
|
||||
v.prefixlen = prefix
|
||||
|
||||
# It's not a valid IPv4 CIDR
|
||||
except Exception:
|
||||
try:
|
||||
v = netaddr.IPNetwork("::/0")
|
||||
v.value = address
|
||||
v.prefixlen = prefix
|
||||
|
||||
# It's not a valid IPv6 CIDR. Give up.
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
# We have a valid CIDR, so let's write it in correct format
|
||||
value = str(v)
|
||||
vtype = "network"
|
||||
|
||||
# We have a query string but it's not in the known query types. Check if
|
||||
# that string is a valid subnet, if so, we can check later if given IP
|
||||
# address/network is inside that specific subnet
|
||||
try:
|
||||
# ?? 6to4 and link-local were True here before. Should they still?
|
||||
if (
|
||||
query
|
||||
and (query not in query_func_map or query == "cidr_lookup")
|
||||
and not str(query).isdigit()
|
||||
and ipaddr(query, "network")
|
||||
):
|
||||
iplist = netaddr.IPSet([netaddr.IPNetwork(query)])
|
||||
query = "cidr_lookup"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# This code checks if value maches the IP version the user wants, ie. if
|
||||
# it's any version ("ipaddr()"), IPv4 ("ipv4()") or IPv6 ("ipv6()")
|
||||
# If version does not match, return False
|
||||
if version and v.version != version:
|
||||
return False
|
||||
|
||||
extras = []
|
||||
for arg in query_func_extra_args.get(query, tuple()):
|
||||
extras.append(locals()[arg])
|
||||
try:
|
||||
return query_func_map[query](v, *extras)
|
||||
except KeyError:
|
||||
try:
|
||||
float(query)
|
||||
if v.size == 1:
|
||||
if vtype == "address":
|
||||
return str(v.ip)
|
||||
elif vtype == "network":
|
||||
return str(v)
|
||||
|
||||
elif v.size > 1:
|
||||
try:
|
||||
return str(v[query]) + "/" + str(v.prefixlen)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
else:
|
||||
return value
|
||||
|
||||
except Exception:
|
||||
raise AnsibleFilterError(
|
||||
alias + ": unknown filter type: %s" % query
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _need_netaddr(f_name, *args, **kwargs):
|
||||
"""
|
||||
verify python's netaddr for these filters to work
|
||||
"""
|
||||
raise AnsibleFilterError(
|
||||
"The %s filter requires python's netaddr be "
|
||||
"installed on the ansible controller" % f_name
|
||||
)
|
|
@ -2,3 +2,4 @@ jsonschema==3.2.0
|
|||
textfsm
|
||||
ttp
|
||||
xmltodict
|
||||
netaddr
|
||||
|
|
|
@ -5,3 +5,4 @@ ipaddress ; python_version < '3.0'
|
|||
mock ; python_version < '3.5'
|
||||
pytest-xdist
|
||||
yamllint
|
||||
netaddr
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
- name: set ipaddress list
|
||||
ansible.builtin.set_fact:
|
||||
value:
|
||||
- 192.24.2.1
|
||||
- host.fqdn
|
||||
- ::1
|
||||
- 192.168.32.0/24
|
||||
- fe80::100/10
|
||||
- "42540766412265424405338506004571095040/64"
|
||||
- True
|
||||
|
||||
- name: ipv4 filter
|
||||
ansible.builtin.set_fact:
|
||||
result1: "{{ value|ansible.utils.ipv4 }}"
|
||||
|
||||
- name: Assert result for ipv4.
|
||||
assert:
|
||||
that: "{{ result1 == result1_val }}"
|
||||
|
||||
- name: convert ipv4 to ipv6 filter
|
||||
ansible.builtin.set_fact:
|
||||
result2: "{{ value|ansible.utils.ipv4('ipv6') }}"
|
||||
|
||||
- name: Assert result for ipv6.
|
||||
assert:
|
||||
that: "{{ result2 == result2_val }}"
|
||||
|
||||
- name: Ipv4 filter with address query
|
||||
ansible.builtin.set_fact:
|
||||
result3: "{{ value|ansible.utils.ipv4('address') }}"
|
||||
|
||||
- name: Assert result for ipv4 filter with address query.
|
||||
assert:
|
||||
that: "{{ result3 == result3_val }}"
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
- name: Recursively find all test files
|
||||
find:
|
||||
file_type: file
|
||||
paths: "{{ role_path }}/tasks"
|
||||
recurse: false
|
||||
use_regex: true
|
||||
patterns:
|
||||
- '^(?!_|main).+$'
|
||||
delegate_to: localhost
|
||||
register: found
|
||||
|
||||
- include: "{{ item.path }}"
|
||||
loop: "{{ found.files }}"
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
result1_val:
|
||||
- "192.24.2.1"
|
||||
- "192.168.32.0/24"
|
||||
|
||||
result2_val:
|
||||
- "::ffff:192.24.2.1/128"
|
||||
- "::ffff:192.168.32.0/120"
|
||||
|
||||
result3_val:
|
||||
- "192.24.2.1"
|
|
@ -0,0 +1,57 @@
|
|||
# -*- 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 ipwrap filter plugin
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import unittest
|
||||
from ansible_collections.ansible.utils.plugins.filter.ipv4 import _ipv4
|
||||
|
||||
|
||||
VALID_DATA = [
|
||||
"192.24.2.1",
|
||||
"host.fqdn",
|
||||
"::1",
|
||||
"",
|
||||
"192.168.32.0/24",
|
||||
"fe80::100/10",
|
||||
"42540766412265424405338506004571095040/64",
|
||||
True,
|
||||
]
|
||||
|
||||
|
||||
VALID_OUTPUT = ["192.24.2.1", "192.168.32.0/24"]
|
||||
|
||||
VALID_OUTPUT1 = ["::ffff:192.24.2.1/128", "::ffff:192.168.32.0/120"]
|
||||
|
||||
VALID_OUTPUT2 = ["192.24.2.1"]
|
||||
|
||||
|
||||
class TestIp4(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_ipv4_filter_empty_query(self):
|
||||
"""Check ipv4 filter empty query"""
|
||||
args = ["", VALID_DATA, ""]
|
||||
result = _ipv4(*args)
|
||||
self.assertEqual(result, VALID_OUTPUT)
|
||||
|
||||
def test_ipv4_ipv6_conversion(self):
|
||||
"""Check ipv4 to ipv6 conversion"""
|
||||
args = ["", VALID_DATA, "ipv6"]
|
||||
result = _ipv4(*args)
|
||||
self.assertEqual(result, VALID_OUTPUT1)
|
||||
|
||||
def test_ipv4_filter_address_query(self):
|
||||
"""Check ipv4 filter address query"""
|
||||
args = ["", VALID_DATA, "address"]
|
||||
result = _ipv4(*args)
|
||||
self.assertEqual(result, VALID_OUTPUT2)
|
Loading…
Reference in New Issue