Adds an ipmath filter (#41985)

pull/42647/head
Fran Fitzpatrick 6 years ago committed by John R Barker
parent 79e366e7ba
commit 5928ec04ca

@ -379,6 +379,28 @@ be automatically converted to a router address (with ``::1/48`` host address)::
.. _6to4: https://en.wikipedia.org/wiki/6to4 .. _6to4: https://en.wikipedia.org/wiki/6to4
IP Math
^^^^^^^
.. versionadded:: 2.7
``ipmath()`` filter can be used to do simple IP math/arithmetic.
Here are a few simple examples::
# {{ '192.168.1.5' | ipmath(5) }}
192.168.1.10
# {{ '192.168.0.5' | ipmath(-10) }}
192.167.255.251
# {{ '2001::1' | ipmath(10) }}
2001::b
# {{ '2001::5' | ipmath(-10) }}
2000:ffff:ffff:ffff:ffff:ffff:ffff:fffb
Subnet manipulation Subnet manipulation
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
@ -527,5 +549,3 @@ convert it between various formats. Examples::
Have a question? Stop by the google group! Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_ `irc.freenode.net <http://irc.freenode.net>`_
#ansible IRC chat channel #ansible IRC chat channel

@ -670,6 +670,23 @@ def ipaddr(value, query='', version=False, alias='ipaddr'):
return False return False
def ipmath(value, amount):
try:
ip = netaddr.IPAddress(value)
except netaddr.AddrFormatError:
msg = 'You must pass a valid IP address; {0} is invalid'.format(value)
raise errors.AnsibleFilterError(msg)
if not isinstance(amount, int):
msg = (
'You must pass an integer for arithmetic; '
'{0} is not a valid integer'
).format(amount)
raise errors.AnsibleFilterError(msg)
return str(ip + amount)
def ipwrap(value, query=''): def ipwrap(value, query=''):
try: try:
if isinstance(value, (list, tuple, types.GeneratorType)): if isinstance(value, (list, tuple, types.GeneratorType)):
@ -1060,6 +1077,7 @@ class FilterModule(object):
# IP addresses and networks # IP addresses and networks
'cidr_merge': cidr_merge, 'cidr_merge': cidr_merge,
'ipaddr': ipaddr, 'ipaddr': ipaddr,
'ipmath': ipmath,
'ipwrap': ipwrap, 'ipwrap': ipwrap,
'ip4_hex': ip4_hex, 'ip4_hex': ip4_hex,
'ipv4': ipv4, 'ipv4': ipv4,

@ -20,8 +20,10 @@ __metaclass__ = type
import pytest import pytest
from ansible.compat.tests import unittest 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,
previous_nth_usable, network_in_usable, network_in_network, cidr_merge) previous_nth_usable, network_in_usable, network_in_network,
cidr_merge, ipmath)
netaddr = pytest.importorskip('netaddr') netaddr = pytest.importorskip('netaddr')
@ -473,3 +475,30 @@ class TestIpFilter(unittest.TestCase):
subnets = ['1.12.1.1', '1.12.1.255'] 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), ['1.12.1.1/32', '1.12.1.255/32'])
self.assertEqual(cidr_merge(subnets, 'span'), '1.12.1.0/24') self.assertEqual(cidr_merge(subnets, 'span'), '1.12.1.0/24')
def test_ipmath(self):
self.assertEqual(ipmath('192.168.1.5', 5), '192.168.1.10')
self.assertEqual(ipmath('192.168.1.5', -5), '192.168.1.0')
self.assertEqual(ipmath('192.168.0.5', -10), '192.167.255.251')
self.assertEqual(ipmath('2001::1', 8), '2001::9')
self.assertEqual(ipmath('2001::1', 9), '2001::a')
self.assertEqual(ipmath('2001::1', 10), '2001::b')
self.assertEqual(ipmath('2001::5', -3), '2001::2')
self.assertEqual(
ipmath('2001::5', -10),
'2000:ffff:ffff:ffff:ffff:ffff:ffff:fffb'
)
expected = 'You must pass a valid IP address; invalid_ip is invalid'
with self.assertRaises(AnsibleFilterError) as exc:
ipmath('invalid_ip', 8)
self.assertEqual(exc.exception.message, expected)
expected = (
'You must pass an integer for arithmetic; '
'some_number is not a valid integer'
)
with self.assertRaises(AnsibleFilterError) as exc:
ipmath('1.2.3.4', 'some_number')
self.assertEqual(exc.exception.message, expected)

Loading…
Cancel
Save