我正在使用Nornir自动化网络设备的配置。在我的Jinja2模板中,我想调用Python函数为我做一个子网计算。我似乎无法弄清楚如何使Jinja2具有以Nornir作为驱动程序调用Python函数的功能。
错误输出:
**** Nornir Playbook to generate site configurations ***************************
device_config*******************************************************************
* router01 ** changed : False **************************************************
vvvv device_config ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ERROR
Subtask: <function template_file at 0x7f547c2418c8> (failed)
---- Device Configuration ** changed : False ----------------------------------- ERROR
Traceback (most recent call last):
File "/home/user1234/venv/lib/python3.6/site-packages/nornir/core/task.py", line 85, in start
r = self.task(self, **self.params)
File "/home/user1234/venv/lib/python3.6/site-packages/nornir/plugins/tasks/text/template_file.py",
line 35, in template_file
**kwargs
File "/home/user1234/venv/lib/python3.6/site-packages/nornir/core/helpers/jinja_helper.py", line
18, in render_from_file
return t.render(**kwargs)
File "/home/user1234/venv/lib/python3.6/site-packages/jinja2/asyncsupport.py", line 76, in render
return original_render(self, *args, **kwargs)
File "/home/user1234/venv/lib/python3.6/site-packages/jinja2/environment.py", line 1008, in render
return self.environment.handle_exception(exc_info, True)
File "/home/user1234/venv/lib/python3.6/site-packages/jinja2/environment.py", line 780, in
handle_exception
reraise(exc_type, exc_value, tb)
File "/home/user1234/venv/lib/python3.6/site-packages/jinja2/_compat.py", line 37, in reraise
raise value.with_traceback(tb)
File "./templates/router_master_config.j2", line 52, in top-level template code
{% include 'router01_interfaces.j2' %}
File "./templates/router01_interfaces.j2", line 17, in top-level template code
description VLAN 3000 - WAN Interlink {{ calculate_subnet() }}
jinja2.exceptions.UndefinedError: 'calculate_subnet' is undefined
^^^^ END device_config ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Process finished with exit code 0
Jinja2模板部分:
interface Port-channel10.1000
description VLAN 1000 - WAN {{ calculate_subnet(host.interfaces[2]['ipv4_addr'], host.interfaces[2]['ipv4_mask'] ) }}
encapsulation dot1Q 1000
ip address {{ host.interfaces[2]['ipv4_addr'] }} {{ host.interfaces[2]['ipv4_mask']}}
ip nbar protocol-discovery
bfd interval 750 min_rx 750 multiplier 4
no shutdown
Python脚本:
from nornir import InitNornir
from nornir.plugins.tasks import networking, text
from nornir.plugins.tasks.networking import napalm_configure
from nornir.plugins.functions.text import print_title, print_result
def device_config(task):
# Transform inventory data to configuration via a template file
var = task.run(task=text.template_file,
name="Device Configuration",
template="router_master_config.j2",
path=f"./templates/"
)
# Save the compiled configuration into a host variable
task.host["config"] = var.result
def calculate_subnet(ip,mask):
# Hard coding subnet for testing
subnet = "10.10.10.0/24"
# Calculation will happen here
return subnet
def main():
nr = InitNornir(config_file="config.yaml", dry_run=False)
print_title("Nornir Playbook to generate site configurations")
routers = nr.filter(device_type="router")
task = routers.run(task=device_config)
print_result(task)
if __name__ == "__main__":
main()
如果跟踪nornir.core.task.run
的代码,您会发现它只是将关键字参数传递给插件,最终传递给t.render
中的nornir.core.helpers.jinja_helper.render_from_file
。
因此,您可以简单地将函数对象作为附加关键字参数传递给task.run
,以使其在模板的名称空间中可用:
var = task.run(task=text.template_file,
name="Device Configuration",
template="router_master_config.j2",
path=f"./templates/",
calculate_subnet=calculate_subnet
)