adding an option for the filter ipsubnet and testing (#40670)
* adding an option for the filter ipsubnet
This commit is contained in:
parent
bd866ed69a
commit
d11e078366
3 changed files with 85 additions and 3 deletions
|
@ -474,6 +474,28 @@ smaller subnets::
|
|||
# {{ address | ipsubnet(18, -5) }}
|
||||
192.168.144.0/27
|
||||
|
||||
By specifying an other subnet as a second argument, if the second subnet include
|
||||
the first you can have the rank of the first subnet in the second ::
|
||||
|
||||
# The rank of the ip in the subnet (the ip is the 36870nth /32 of the subnet)
|
||||
# {{ address | ipsubnet(subnet) }}
|
||||
36870
|
||||
|
||||
# The rank in the /24 that contain the address
|
||||
# {{ address | ipsubnet('192.168.144.0/24') }}
|
||||
6
|
||||
|
||||
# An IP with the subnet in the first /30 in a /24
|
||||
# {{ '192.168.144.1/30' | ipsubnet('192.168.144.0/24') }}
|
||||
1
|
||||
|
||||
# The fifth subnet /30 in a /24
|
||||
# {{ '192.168.144.16/30' | ipsubnet('192.168.144.0/24') }}
|
||||
5
|
||||
|
||||
If the secound subnet doesn't include the first it raise an error
|
||||
|
||||
|
||||
You can use ``ipsubnet()`` filter with ``ipaddr()`` filter to for example split
|
||||
given ``/48`` prefix into smaller, ``/64`` subnets::
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ __metaclass__ = type
|
|||
|
||||
from functools import partial
|
||||
import types
|
||||
from ansible.module_utils import six
|
||||
|
||||
try:
|
||||
import netaddr
|
||||
|
@ -736,6 +737,9 @@ def ipv6(value, query=''):
|
|||
#
|
||||
# - address | ipsubnet(cidr, index)
|
||||
# returns next indexed subnet which contains given address
|
||||
#
|
||||
# - address/prefix | ipsubnet(subnet/prefix)
|
||||
# return the index of the subnet in the subnet
|
||||
def ipsubnet(value, query='', index='x'):
|
||||
''' Manipulate IPv4/IPv6 subnets '''
|
||||
|
||||
|
@ -749,11 +753,11 @@ def ipsubnet(value, query='', index='x'):
|
|||
value = netaddr.IPNetwork(v)
|
||||
except:
|
||||
return False
|
||||
|
||||
query_string = str(query)
|
||||
if not query:
|
||||
return str(value)
|
||||
|
||||
elif str(query).isdigit():
|
||||
elif query_string.isdigit():
|
||||
vsize = ipaddr(v, 'size')
|
||||
query = int(query)
|
||||
|
||||
|
@ -786,6 +790,21 @@ def ipsubnet(value, query='', index='x'):
|
|||
except:
|
||||
return False
|
||||
|
||||
elif query_string:
|
||||
vtype = ipaddr(query, 'type')
|
||||
if vtype == 'address':
|
||||
v = ipaddr(query, 'cidr')
|
||||
elif vtype == 'network':
|
||||
v = ipaddr(query, 'subnet')
|
||||
else:
|
||||
msg = 'You must pass a valid subnet or IP address; {0} is invalid'.format(query_string)
|
||||
raise errors.AnsibleFilterError(msg)
|
||||
query = netaddr.IPNetwork(v)
|
||||
for i, subnet in enumerate(query.subnet(value.prefixlen), 1):
|
||||
if subnet == value:
|
||||
return str(i)
|
||||
msg = '{0} is not in the subnet {1}'.format(value.cidr, query.cidr)
|
||||
raise errors.AnsibleFilterError(msg)
|
||||
return False
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import pytest
|
|||
|
||||
from ansible.compat.tests import unittest
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.plugins.filter.ipaddr import (ipaddr, _netmask_query, nthhost, next_nth_usable,
|
||||
from ansible.plugins.filter.ipaddr import (ipaddr, _netmask_query, nthhost, next_nth_usable, ipsubnet,
|
||||
previous_nth_usable, network_in_usable, network_in_network,
|
||||
cidr_merge, ipmath)
|
||||
netaddr = pytest.importorskip('netaddr')
|
||||
|
@ -502,3 +502,44 @@ class TestIpFilter(unittest.TestCase):
|
|||
with self.assertRaises(AnsibleFilterError) as exc:
|
||||
ipmath('1.2.3.4', 'some_number')
|
||||
self.assertEqual(exc.exception.message, expected)
|
||||
|
||||
def test_ipsubnet(self):
|
||||
test_cases = (
|
||||
(('1.1.1.1/24', '30'), '64'),
|
||||
(('1.1.1.1/25', '24'), '0'),
|
||||
(('1.12.1.34/32', '1.12.1.34/24'), '35'),
|
||||
(('192.168.50.0/24', '192.168.0.0/16'), '51'),
|
||||
(('192.168.144.5', '192.168.0.0/16'), '36870'),
|
||||
(('192.168.144.5', '192.168.144.5/24'), '6'),
|
||||
(('192.168.144.5/32', '192.168.144.0/24'), '6'),
|
||||
(('192.168.144.16/30', '192.168.144.0/24'), '5'),
|
||||
(('192.168.144.5', ), '192.168.144.5/32'),
|
||||
(('192.168.0.0/16', ), '192.168.0.0/16'),
|
||||
(('192.168.144.5', ), '192.168.144.5/32'),
|
||||
(('192.168.0.0/16', '20'), '16'),
|
||||
(('192.168.0.0/16', '20', '0'), '192.168.0.0/20'),
|
||||
(('192.168.0.0/16', '20', '-1'), '192.168.240.0/20'),
|
||||
(('192.168.0.0/16', '20', '5'), '192.168.80.0/20'),
|
||||
(('192.168.0.0/16', '20', '-5'), '192.168.176.0/20'),
|
||||
(('192.168.144.5', '20'), '192.168.144.0/20'),
|
||||
(('192.168.144.5', '18', '0'), '192.168.128.0/18'),
|
||||
(('192.168.144.5', '18', '-1'), '192.168.144.4/31'),
|
||||
(('192.168.144.5', '18', '5'), '192.168.144.0/23'),
|
||||
(('192.168.144.5', '18', '-5'), '192.168.144.0/27'),
|
||||
(('span', 'test', 'error'), False),
|
||||
(('test', ), False),
|
||||
(('192.168.144.5', '500000', '-5'), False),
|
||||
(('192.168.144.5', '18', '500000'), False),
|
||||
(('200000', '18', '-5'), '0.3.13.64/27'),
|
||||
)
|
||||
for args, res in test_cases:
|
||||
self._test_ipsubnet(args, res)
|
||||
|
||||
def _test_ipsubnet(self, ipsubnet_args, expected_result):
|
||||
self.assertEqual(ipsubnet(*ipsubnet_args), expected_result)
|
||||
|
||||
with self.assertRaisesRegexp(AnsibleFilterError, 'You must pass a valid subnet or IP address; invalid_subnet is invalid'):
|
||||
ipsubnet('192.168.144.5', 'invalid_subnet')
|
||||
|
||||
with self.assertRaisesRegexp(AnsibleFilterError, '192.168.144.0/30 is not in the subnet 192.168.144.4/30'):
|
||||
ipsubnet('192.168.144.1/30', '192.168.144.5/30')
|
||||
|
|
Loading…
Reference in a new issue