Add cidr_merge filter (#36081)
This commit is contained in:
parent
bf53e441b1
commit
e2c1589201
3 changed files with 73 additions and 1 deletions
|
@ -462,6 +462,29 @@ Because of the size of IPv6 subnets, iteration over all of them to find the
|
|||
correct one may take some time on slower computers, depending on the size
|
||||
difference between subnets.
|
||||
|
||||
Subnet Merging
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
The `cidr_merge` filter can be used to merge subnets or individual addresses
|
||||
into their minimal representation, collapsing overlapping subnets and merging
|
||||
adjacent ones wherever possible::
|
||||
|
||||
{{ ['192.168.0.0/17', '192.168.128.0/17', '192.168.128.1' ] | cidr_merge }}
|
||||
# => ['192.168.0.0/16']
|
||||
|
||||
{{ ['192.168.0.0/24', '192.168.1.0/24', '192.168.3.0/24'] | cidr_merge }}
|
||||
# => ['192.168.0.0/23', '192.168.3.0/24']
|
||||
|
||||
Changing the action from 'merge' to 'span' will instead return the smallest
|
||||
subnet which contains all of the inputs::
|
||||
|
||||
{{ ['192.168.0.0/24', '192.168.3.0/24'] | cidr_merge('span') }}
|
||||
# => '192.168.0.0/22'
|
||||
|
||||
{{ ['192.168.1.42', '192.168.42.1'] | cidr_merge('span') }}
|
||||
# => '192.168.0.0/18'
|
||||
|
||||
MAC address filter
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -413,6 +413,38 @@ def _win_query(v):
|
|||
|
||||
|
||||
# ---- IP address and network filters ----
|
||||
|
||||
# Returns a minified list of subnets or a single subnet that spans all of
|
||||
# the inputs.
|
||||
def cidr_merge(value, action='merge'):
|
||||
if not hasattr(value, '__iter__'):
|
||||
raise errors.AnsibleFilterError('cidr_merge: expected iterable, got ' + repr(value))
|
||||
|
||||
if action == 'merge':
|
||||
try:
|
||||
return [str(ip) for ip in netaddr.cidr_merge(value)]
|
||||
except Exception as e:
|
||||
raise errors.AnsibleFilterError('cidr_merge: error in netaddr:\n%s' % e)
|
||||
|
||||
elif action == 'span':
|
||||
# spanning_cidr needs at least two values
|
||||
if len(value) == 0:
|
||||
return None
|
||||
elif len(value) == 1:
|
||||
try:
|
||||
return str(netaddr.IPNetwork(value[0]))
|
||||
except Exception as e:
|
||||
raise errors.AnsibleFilterError('cidr_merge: error in netaddr:\n%s' % e)
|
||||
else:
|
||||
try:
|
||||
return str(netaddr.spanning_cidr(value))
|
||||
except Exception as e:
|
||||
raise errors.AnsibleFilterError('cidr_merge: error in netaddr:\n%s' % e)
|
||||
|
||||
else:
|
||||
raise errors.AnsibleFilterError("cidr_merge: invalid action '%s'" % action)
|
||||
|
||||
|
||||
def ipaddr(value, query='', version=False, alias='ipaddr'):
|
||||
''' Check if string is an IP address or network and filter it '''
|
||||
|
||||
|
@ -1026,6 +1058,7 @@ class FilterModule(object):
|
|||
''' IP address and network manipulation filters '''
|
||||
filter_map = {
|
||||
# IP addresses and networks
|
||||
'cidr_merge': cidr_merge,
|
||||
'ipaddr': ipaddr,
|
||||
'ipwrap': ipwrap,
|
||||
'ip4_hex': ip4_hex,
|
||||
|
|
|
@ -21,7 +21,7 @@ import pytest
|
|||
|
||||
from ansible.compat.tests import unittest
|
||||
from ansible.plugins.filter.ipaddr import (ipaddr, _netmask_query, nthhost, next_nth_usable,
|
||||
previous_nth_usable, network_in_usable, network_in_network)
|
||||
previous_nth_usable, network_in_usable, network_in_network, cidr_merge)
|
||||
netaddr = pytest.importorskip('netaddr')
|
||||
|
||||
|
||||
|
@ -457,3 +457,19 @@ class TestIpFilter(unittest.TestCase):
|
|||
subnet = '1.12.1.0/24'
|
||||
address = '1.12.2.0'
|
||||
self.assertEqual(network_in_network(subnet, address), False)
|
||||
|
||||
def test_cidr_merge(self):
|
||||
self.assertEqual(cidr_merge([]), [])
|
||||
self.assertEqual(cidr_merge([], 'span'), None)
|
||||
subnets = ['1.12.1.0/24']
|
||||
self.assertEqual(cidr_merge(subnets), subnets)
|
||||
self.assertEqual(cidr_merge(subnets, 'span'), subnets[0])
|
||||
subnets = ['1.12.1.0/25', '1.12.1.128/25']
|
||||
self.assertEqual(cidr_merge(subnets), ['1.12.1.0/24'])
|
||||
self.assertEqual(cidr_merge(subnets, 'span'), '1.12.1.0/24')
|
||||
subnets = ['1.12.1.0/25', '1.12.1.128/25', '1.12.2.0/24']
|
||||
self.assertEqual(cidr_merge(subnets), ['1.12.1.0/24', '1.12.2.0/24'])
|
||||
self.assertEqual(cidr_merge(subnets, 'span'), '1.12.0.0/22')
|
||||
subnets = ['1.12.1.1', '1.12.1.255']
|
||||
self.assertEqual(cidr_merge(subnets), ['1.12.1.1/32', '1.12.1.255/32'])
|
||||
self.assertEqual(cidr_merge(subnets, 'span'), '1.12.1.0/24')
|
||||
|
|
Loading…
Reference in a new issue