diff --git a/src/cnaas_nms/tools/jinja_filters.py b/src/cnaas_nms/tools/jinja_filters.py index 4aa41a0b..f9c0a0eb 100644 --- a/src/cnaas_nms/tools/jinja_filters.py +++ b/src/cnaas_nms/tools/jinja_filters.py @@ -86,22 +86,27 @@ def isofy_ipv4(ip_string, prefix=''): @template_filter() def ipv4_to_ipv6( - v6_network: Union[str, ipaddress.IPv6Network], v4_address: Union[str, ipaddress.IPv4Interface] + v4_address: Union[str, ipaddress.IPv4Interface], + v6_network: Union[str, ipaddress.IPv6Network], + keep_octets: int = 1, ): - """Transforms an IPv4 address to an IPv6 interface address. This will combine an arbitrary - IPv6 network address with the 32 address bytes of an IPv4 address into a valid IPv6 address - + prefix length notation - the equivalent of dotted quad compatible notation. + """Transforms an IPv4 address intoto an IPv6 interface address that should be more-or-less + identifiable as the same device on a different protocol family. - E.g.: - >>> ipv6 = ipv4_to_ipv6("2001:700:dead:babe::/64", "127.0.0.1") - >>> ipv6 - IPv6Interface('2001:700:dead:babe::7f00:1/64') - >>> ipv6 == ipaddress.IPv6Interface('2001:700:dead:babe::127.0.0.1/64') - True + A selected number of octets, right-to-left, of the IPv4 address will be converted to + hexadecimal notation that can be read as the decimal IPv4 address. This will then be used as + a host address on the provided IPv6 network. + + Examples: + >>> ipv4_to_ipv6("10.1.0.42", "2001:700:dead:babe::/64") + IPv6Interface('2001:700:dead:babe::42/64') + >>> ipv4_to_ipv6("10.1.0.42", "2001:700:dead:babe::/64", keep_octets=4) + IPv6Interface('2001:700:dead:babe:10:1:0:42/64') Args: - v6_network: IPv6 network in prefix notation v4_address: IPv4 address + v6_network: IPv6 network in prefix notation + keep_octets: The number of octets to keep from the IPv4 address (from right-to-left) Returns: An IPv6Address object on the given network """ @@ -109,8 +114,11 @@ def ipv4_to_ipv6( v6_network = ipaddress.IPv6Network(v6_network) if isinstance(v4_address, str): v4_address = ipaddress.IPv4Address(v4_address) + assert keep_octets > 0 <= 4 - v6_address = v6_network[int(v4_address)] + octets = str(v4_address).split('.')[-keep_octets:] + v6ified = ipaddress.IPv6Address("::" + ":".join(octets)) + v6_address = v6_network[int(v6ified)] return ipaddress.IPv6Interface(f"{v6_address}/{v6_network.prefixlen}") diff --git a/src/cnaas_nms/tools/tests/test_jinja_filters.py b/src/cnaas_nms/tools/tests/test_jinja_filters.py index ce8bb77d..5519b879 100644 --- a/src/cnaas_nms/tools/tests/test_jinja_filters.py +++ b/src/cnaas_nms/tools/tests/test_jinja_filters.py @@ -68,23 +68,23 @@ class IPv6ToIPv4Tests(unittest.TestCase): def test_should_convert_short_network_properly(self): self.assertEqual( - ipv4_to_ipv6('2001:700::/64', '10.0.0.1'), - ipaddress.IPv6Interface('2001:700::10.0.0.1/64'), + ipv4_to_ipv6('10.0.0.1', '2001:700::/64'), + ipaddress.IPv6Interface('2001:700::1/64'), ) def test_should_convert_long_network_properly(self): self.assertEqual( - ipv4_to_ipv6('2001:700:dead:c0de:babe::/80', '10.0.0.1'), - ipaddress.IPv6Interface('2001:700:dead:c0de:babe::10.0.0.1/80'), + ipv4_to_ipv6('10.0.0.1', '2001:700:dead:c0de::/64', keep_octets=4), + ipaddress.IPv6Interface('2001:700:dead:c0de:10:0:0:1/64'), ) def test_should_raise_on_invalid_network(self): with self.assertRaises(ValueError): invalid_network = '2001:700:0:::/64' - ipv4_to_ipv6(invalid_network, '10.0.0.1') + ipv4_to_ipv6('10.0.0.1', invalid_network) def test_should_return_an_ipv6interface(self): - result = ipv4_to_ipv6('2001:700::/64', '10.0.0.1') + result = ipv4_to_ipv6('10.0.0.1', '2001:700::/64') self.assertIsInstance(result, ipaddress.IPv6Interface)