diff --git a/docs/docsite/rst/user_guide/playbooks_filters_ipaddr.rst b/docs/docsite/rst/user_guide/playbooks_filters_ipaddr.rst index 5b75f64623d..e910e1102f1 100644 --- a/docs/docsite/rst/user_guide/playbooks_filters_ipaddr.rst +++ b/docs/docsite/rst/user_guide/playbooks_filters_ipaddr.rst @@ -434,6 +434,13 @@ To find the next nth usable IP address within a range, use ``next_nth_usable``:: In this example, ``next_nth_usable`` returns the second usable IP address for the given IP range. +To find the peer IP address for a point to point link, use ``peer``:: + + # {{ '192.168.122.1/31' | ipaddr('peer') }} + 192.168.122.0 + # {{ '192.168.122.1/30' | ipaddr('peer') }} + 192.168.122.2 + IP Math ^^^^^^^ diff --git a/lib/ansible/plugins/filter/ipaddr.py b/lib/ansible/plugins/filter/ipaddr.py index f2961b825e9..ed00fef6986 100644 --- a/lib/ansible/plugins/filter/ipaddr.py +++ b/lib/ansible/plugins/filter/ipaddr.py @@ -277,6 +277,21 @@ def _next_usable_query(v, vtype): return str(netaddr.IPAddress(int(v.ip) + 1)) +def _peer_query(v, vtype): + if vtype == 'address': + raise errors.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 errors.AnsibleFilterError("Network address of /30 has no peer") + if int(v.ip) % 4 == 3: + raise errors.AnsibleFilterError("Broadcast address of /30 has no peer") + return str(netaddr.IPAddress(int(v.ip) ^ 3)) + raise errors.AnsibleFilterError("Not a point-to-point network") + + def _prefix_query(v): return int(v.prefixlen) @@ -463,6 +478,7 @@ def ipaddr(value, query='', version=False, alias='ipaddr'): 'lo': ('value',), 'multicast': ('value',), 'next_usable': ('vtype',), + 'peer': ('vtype',), 'previous_usable': ('vtype',), 'private': ('value',), 'public': ('value',), @@ -507,6 +523,7 @@ def ipaddr(value, query='', version=False, alias='ipaddr'): '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, diff --git a/test/units/plugins/filter/test_ipaddr.py b/test/units/plugins/filter/test_ipaddr.py index 6b7d7039543..bdc024b06e4 100644 --- a/test/units/plugins/filter/test_ipaddr.py +++ b/test/units/plugins/filter/test_ipaddr.py @@ -336,6 +336,31 @@ class TestIpFilter(unittest.TestCase): address = '1.12.1.254/24' self.assertEqual(ipaddr(address, 'next_usable'), None) + def test_peer(self): + address = '1.12.1.0/31' + self.assertEqual(ipaddr(address, 'peer'), '1.12.1.1') + address = '1.12.1.1/31' + self.assertEqual(ipaddr(address, 'peer'), '1.12.1.0') + address = '1.12.1.1/30' + self.assertEqual(ipaddr(address, 'peer'), '1.12.1.2') + address = '1.12.1.2/30' + self.assertEqual(ipaddr(address, 'peer'), '1.12.1.1') + with self.assertRaises(AnsibleFilterError): + address = '1.12.1.34' + ipaddr(address, 'peer') + with self.assertRaises(AnsibleFilterError): + address = '1.12.1.33/29' + ipaddr(address, 'peer') + with self.assertRaises(AnsibleFilterError): + address = '1.12.1.32/30' + ipaddr(address, 'peer') + with self.assertRaises(AnsibleFilterError): + address = '1.12.1.35/30' + ipaddr(address, 'peer') + with self.assertRaises(AnsibleFilterError): + address = '1.12.1.34/32' + ipaddr(address, 'peer') + def test_previous_usable(self): address = '1.12.1.0/24' self.assertEqual(ipaddr(address, 'previous_usable'), None)