Skip to content

Commit

Permalink
Merge pull request #76 from itdependsnetworks/ip_jinja
Browse files Browse the repository at this point in the history
Expose ipaddress for jinja filters
  • Loading branch information
itdependsnetworks authored Nov 25, 2021
2 parents 6607a3e + 37b3eab commit cf6541a
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 6 deletions.
54 changes: 53 additions & 1 deletion docs/source/netutils/utilities/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Netutils Utilities
Netutils to Jinja2 Filters
============================

In an effort to simplify the process of adding netutils' functions to jinja2 as filters we have created a convenience function. One use case.
In an effort to simplify the process of adding netutils' functions to jinja2 as filters we have created a convenience function. Let's go through how you could add the filters to your jinja2 environment.
Here is the current folder structure.

.. code-block:: python
Expand Down Expand Up @@ -62,3 +62,55 @@ The below list shows what jinja2 filters are added when you add them using the p
json_obj = json.dumps(data, sort_keys=True, indent=4)
json_obj = json_obj[:-1] + " ]"
print(f".. code-block:: JavaScript\n\n {json_obj}\n\n")


ipaddress Convenience Functions
---------------------------------

When adding the netutils functions to your jinja2 environment, you also gain access to the built-in ipaddress python library using these three jinja2 filters.

.. code-block:: python
"ipaddress_address": "ip.ipaddress_address",
"ipaddress_interface": "ip.ipaddress_interface",
"ipaddress_network": "ip.ipaddress_network",
When using these filters, you must specify an attribute of that given class. Here is an example of how you would use the `version` if the `ipaddress_interface` filter.

.. code-block:: python
.
├── jinja2_environment.py
└── templates
└── test.j2
Below is the code in the `test.j2` file.

.. code-block:: jinja
The version of 192.168.0.1/24 is IPv{{ "192.168.0.1/24" | ipaddress_interface("version") }}.
Below is a code in the `jinja2_environment.py` folder.

.. code-block:: python
from jinja2.loaders import FileSystemLoader, PackageLoader
from jinja2 import Environment, PackageLoader, select_autoescape
from netutils.utils import jinja2_convenience_function
env = Environment(
loader=FileSystemLoader("templates"),
autoescape=select_autoescape()
)
env.filters.update(jinja2_convenience_function())
template = env.get_template("test.j2")
result = template.render()
print(result)
When you run `jinja2_environment.py` the output will be:

.. code-block:: python
The version of 192.168.0.1/24 is IPv4.
77 changes: 77 additions & 0 deletions netutils/ip.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,85 @@
"""Functions for working with IP addresses."""
import ipaddress
from operator import attrgetter
from netutils.constants import IPV4_MASKS, IPV6_MASKS


def ipaddress_address(ip, attr):
"""Convenience function primarily built to expose ipaddress.ip_address to Jinja.
Args:
ip_addr (str): IP Address str compliant with ipaddress.ip_address inputs.
attr (atr): An attribute in string dotted format.
Returns:
str: Returns the value provided by the ipaddress.ip_address attribute provided.
Example:
>>> from netutils.ip import ipaddress_address
>>> ipaddress_address('10.1.1.1', 'version')
4
>>> ipaddress_address('10.1.1.1', '__int__')
167837953
>>> ipaddress_address('10.1.1.1', 'is_loopback')
False
>>>
"""
retriever = attrgetter(attr)
retrieved_method = retriever(ipaddress.ip_address(ip))
if callable(retrieved_method):
return retrieved_method()
return retrieved_method


def ipaddress_interface(ip, attr):
"""Convenience function primarily built to expose ipaddress.ip_interface to Jinja.
Args:
ip_interface (str): IP interface str compliant with ipaddress.ip_interface inputs.
attr (atr): An attribute in string dotted format.
Returns:
str: Returns the value provided by the ipaddress.ip_interface attribute provided.
Example:
>>> from netutils.ip import ipaddress_interface
>>> ipaddress_interface('10.1.1.1/24', 'version')
4
>>> ipaddress_interface('10.1.1.1/24', '__int__')
167837953
"""
retriever = attrgetter(attr)
retrieved_method = retriever(ipaddress.ip_interface(ip))
if callable(retrieved_method):
return retrieved_method()
return retrieved_method


def ipaddress_network(ip, attr):
"""Convenience function primarily built to expose ipaddress.ip_network to Jinja.
Args:
ip_network (str): IP network str compliant with ipaddress.ip_network inputs.
attr (atr): An attribute in string dotted format.
Returns:
str: Returns the value provided by the ipaddress.ip_network attribute provided.
Example:
>>> from netutils.ip import ipaddress_network
>>> ipaddress_network('10.1.1.0/24', 'version')
4
>>> ipaddress_network('10.1.1.0/24', '__str__')
'10.1.1.0/24'
>>>
"""
retriever = attrgetter(attr)
retrieved_method = retriever(ipaddress.ip_network(ip))
if callable(retrieved_method):
return retrieved_method()
return retrieved_method


def ip_to_hex(ip):
"""Converts an IP address in string format to a hex string.
Expand Down
13 changes: 8 additions & 5 deletions netutils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@

_JINJA2_FUNCTION_MAPPINGS = {
"asn_to_int": "asn.asn_to_int",
"name_to_bits": "bandwidth.name_to_bits",
"name_to_bytes": "bandwidth.name_to_bytes",
"bits_to_name": "bandwidth.bits_to_name",
"bytes_to_name": "bandwidth.bytes_to_name",
"name_to_name": "bandwidth.name_to_name",
"clean_config": "config.clean.clean_config",
"sanitize_config": "config.clean.sanitize_config",
"config_compliance": "config.compliance.compliance",
Expand Down Expand Up @@ -36,6 +41,9 @@
"get_first_usable": "ip.get_first_usable",
"get_peer_ip": "ip.get_peer_ip",
"get_usable_range": "ip.get_usable_range",
"ipaddress_address": "ip.ipaddress_address",
"ipaddress_interface": "ip.ipaddress_interface",
"ipaddress_network": "ip.ipaddress_network",
"is_valid_mac": "mac.is_valid_mac",
"mac_to_format": "mac.mac_to_format",
"mac_to_int": "mac.mac_to_int",
Expand All @@ -51,11 +59,6 @@
"longest_prefix_match": "route.longest_prefix_match",
"vlanlist_to_config": "vlan.vlanlist_to_config",
"vlanconfig_to_list": "vlan.vlanconfig_to_list",
"name_to_bits": "bandwidth.name_to_bits",
"name_to_bytes": "bandwidth.name_to_bytes",
"bits_to_name": "bandwidth.bits_to_name",
"bytes_to_name": "bandwidth.bytes_to_name",
"name_to_name": "bandwidth.name_to_name",
}


Expand Down
68 changes: 68 additions & 0 deletions tests/unit/test_ip.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,59 @@

from netutils import ip

IP_ADDRESS = [
{
"sent": {"ip": "10.1.1.1", "attr": "is_loopback"},
"received": False,
},
{
"sent": {"ip": "10.1.1.1", "attr": "__int__"},
"received": 167837953,
},
{
"sent": {"ip": "10.1.1.1", "attr": "is_private"},
"received": True,
},
{
"sent": {"ip": "10.1.1.1", "attr": "__str__"},
"received": "10.1.1.1",
},
]

IP_INTERFACE = [
{
"sent": {"ip": "10.1.1.1/24", "attr": "is_loopback"},
"received": False,
},
{
"sent": {"ip": "10.1.1.1/24", "attr": "__int__"},
"received": 167837953,
},
{
"sent": {"ip": "10.1.1.1/24", "attr": "network.__str__"},
"received": "10.1.1.0/24",
},
{
"sent": {"ip": "10.1.1.1/24", "attr": "netmask.__str__"},
"received": "255.255.255.0",
},
]

IP_NETWORK = [
{
"sent": {"ip": "10.1.1.0/24", "attr": "hostmask.__str__"},
"received": "0.0.0.255",
},
{
"sent": {"ip": "10.1.1.0/24", "attr": "network_address.__int__"},
"received": 167837952,
},
{
"sent": {"ip": "10.1.1.0/24", "attr": "version"},
"received": 4,
},
]

IP_TO_HEX = [
{
"sent": {"ip": "10.1.1.1"},
Expand Down Expand Up @@ -365,3 +418,18 @@ def test_get_peer_ip_fail_subnet(data):
def test_get_peer_ip_fail_ip(data):
with pytest.raises(ValueError, match=r".*usable range.*"):
ip.get_peer_ip(**data["sent"])


@pytest.mark.parametrize("data", IP_ADDRESS)
def test_ipaddress_address(data):
assert ip.ipaddress_address(**data["sent"]) == data["received"]


@pytest.mark.parametrize("data", IP_INTERFACE)
def test_ipaddress_interface(data):
assert ip.ipaddress_interface(**data["sent"]) == data["received"]


@pytest.mark.parametrize("data", IP_NETWORK)
def test_ipaddress_network(data):
assert ip.ipaddress_network(**data["sent"]) == data["received"]

0 comments on commit cf6541a

Please sign in to comment.