1

I am very new to python and mako and I may be having trouble with basic concepts. I have working templates but my CSV input could be cleaned up considerably if I could use the netaddr module to work with IP addresses in the template. What I would like to do is pass an interface IP variable like: LAN_IP = '192.168.1.1/24' (I am doing this from a CSV) to the template and then somehow use the netaddr module to fill in the IP, subnet mask (in dotted decimal and inverse mask) and the network address so I can use that one CSV variable for bgp network statements, eigrp masks and ACLs, etc in the same configuration. I can do the following from a Python shell:

>>> from netaddr import *
>>> LAN_IP = '192.168.1.1/24'
>>> IP = IPNetwork(LAN_IP)
>>> print(IP.ip)
192.168.1.1
>>> print(IP.network)
192.168.1.0
>>> print(IP.netmask)
255.255.255.0
>>> print(IP.hostmask)
0.0.0.255

EDIT. I was told to try the ipaddress built in fuction. Trying this from a Python prompt shows that it can work:

MAKO_TEMPLATE_STRING = """\
<%def name="get_netmask(ip_string)"><%
    import ipaddress
    return ipaddress.IPv4Interface(ip_string).netmask
%></%def>
<%def name="get_address(ip_string)"><%
    import ipaddress
    return ipaddress.IPv4Interface(ip_string).ip
%></%def>
<%def name="get_subnet(ip_string)"><%
    import ipaddress
    return ipaddress.IPv4Interface(ip_string).network
%></%def>
<%def name="get_hostmask(ip_string)"><%
    import ipaddress
    return ipaddress.IPv4Interface(ip_string).hostmask
%></%def>
! Variable Input: ${data}
${get_address(data)} ${get_netmask(data)} ${get_subnet(data)} ${get_hostmask(data)}
"""
print(Template(MAKO_TEMPLATE_STRING).render(data="192.168.1.1/25"))

This gives me the following output: ! Variable Input: 192.168.1.1/25 192.168.1.1 255.255.255.128 192.168.1.0/25 0.0.0.127

Now, a new problem is that the ipaddress.IPv4Interface(ip_string).network definition returns the network PLUS the subnet "192.168.1.0/25" which netaddr did not do. I have not found any way to get ipaddress to return ONLY the subnet portion.

So for the template build I got a bit farther.... If I put ONLY this into the template, it validates:

<%def name="get_netmask(ip_string)"><%
    import ipaddress
    return ipaddress.IPv4Interface(ip_string).netmask
%></%def>
<%def name="get_address(ip_string)"><%
    import ipaddress
    return ipaddress.IPv4Interface(ip_string).ip
%></%def>
<%def name="get_network(ip_string)"><%
    import ipaddress
    return ipaddress.IPv4Interface(ip_string).network
%></%def>

But when I try any type of reference, I get an error.

! Variable Input: ${LAN_IP}  <--I tried with and without this line
${get_address(LAN_IP)}  <--The template does not seem to like these references.
${get_netmask(LAN_IP)}
${get_subnet(LAN_IP)}

It seems to me that I am missing something really simple here but maybe I am approaching the problem the wrong way. Any help would be greatly appreciated as the Mako docs and Google have turned up little to illustrate how to do something like this.

Daniel Flick
  • 93
  • 1
  • 10
  • 1
    Still working on this but nothing has helped so far. – Daniel Flick Oct 17 '17 at 15:56
  • 1
    I may have gotten a bit farther though. I tried this for a template: <%! ## python module-level code import ipaddress %> <%def name="get_address(ip_string)"> <% return ipaddress.IPv4Interface(ip_string).ip %> %def> ! Variable Input: ${LAN_IP} ${get_address(LAN_IP)} and now I get Invalid template, please correct the following error: Template Attribute error: Expected 4 octets in 'test' Since 'test' is no where in my template, I think this is a bug in the front end code. – Daniel Flick Oct 17 '17 at 20:46

1 Answers1

1

The problem was with the validation code. Within the python section of the template, the class IPv4Interface will throw an exception due to the invalid value during the validation process. Therefore, the server rejects the form data and the template is not created.

Solution: It would work if you add an error handling to the python section of the template.

<%!
    ## python module-level code
    import ipaddress
%>
<%def name="get_address(ip_string)">
    <%
    try:
        return ipaddress.IPv4Interface(ip_string).ip
    except Exception:
        return "FAIL_OR_EMPTY"
    %>
</%def>
! Variable Input: ${LAN_IP}
${get_address(LAN_IP)}

Explanation: During the server-side validation of the HTML form, the configuration template is rendered with a dummy parameter set to verify the syntax (see file app/forms.py, class ConfigTemplateForm). Your config template is validated with the following parameter set during the form validation:

{
    "hostname": "test",
    "LAN_IP": "test"
}
Daniel Flick
  • 93
  • 1
  • 10