本文基于nornir3.1.1进行代码测试和执行。
其实和nornir官方文档没什么大差别,写出来方便自己查阅和使用。
nornir,100%基于python3构成的自动化框架,易于NetDevOps进行二次开发和脚本编写,操作容易。
相比于ansible,操作更为简单容易,且只需使用python3即可完成配置和脚本编写。
加上支持使用netmiko,使得一些较为冷门的交换机如ruijie、h3c等无需二次开发插件即可进行脚本编写。
目前版本为3.1.1,与之前的版本差异较大,看同类文章时需注意版本。
 安装
nornir本体和常用的插件。
| 1
 | pip install nornir nornir_utils nornir_netmiko
 | 
 使用
 文件类型
nornir分为三类文件:
- 配置文件yaml
- 机器管理文件yaml
- 用户脚本py
 配置文件
首先看配置文件yaml,一般使用config.yaml,可自定义命名。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | ---
 
 inventory:
 
 plugin: SimpleInventory
 options:
 
 host_file: "inventory/hosts.yaml"
 group_file: "inventory/groups.yaml"
 defaults_file: "inventory/defaults.yaml"
 
 runner:
 
 plugin: threaded
 options:
 
 num_workers: 64
 
 | 
也可以直接在python文件中使用变量指定。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | from nornir import InitNornirnr = InitNornir(
 runner={
 "plugin": "threaded",
 "options": {
 "num_workers": 100,
 },
 },
 inventory={
 "plugin": "SimpleInventory",
 "options": {
 "host_file": "inventory/hosts.yaml",
 "group_file": "inventory/groups.yaml"
 },
 },
 )
 
 | 
或两类混用指定配置。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | from nornir import InitNornirnr = InitNornir(
 config_file="config.yaml",
 runner={
 "plugin": "threaded",
 "options": {
 "num_workers": 50,
 },
 },
 )
 
 | 
接着可以使用python文件进行测试,无异常则配置测试通过。
| 12
 
 | from nornir import InitNornirnr = InitNornir(config_file="config.yaml")
 
 | 
 机器管理文件
目前来看,例如host_file不支持使用多个文件,无法像ansible使用多个文件进行分类管理。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | ---
 
 cisco_3850:
 hostname: 192.168.1.1
 port: 22
 username: cisco
 password: cisco_password
 
 platform: cisco_ios
 
 groups:
 - access_switch
 
 data:
 role: access_switch
 
 cisco_3548:
 hostname: 192.168.1.0
 port: 22
 username: cisco
 password: cisco_password
 platform: cisco_nxos
 groups:
 - core_switch
 data:
 role: core_switch
 
 server_1:
 hostname: 192.168.1.2
 username: root
 password: root_password
 
 | 
下面为nornir默认的key/value参数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | {"name": "str",
 "connection_options": {
 "$connection_type": {
 "extras": {
 "$key": "$value"
 },
 "hostname": "str",
 "port": "int",
 "username": "str",
 "password": "str",
 "platform": "str"
 }
 },
 "groups": [
 "$group_name"
 ],
 "data": {
 "$key": "$value"
 },
 "hostname": "str",
 "port": "int",
 "username": "str",
 "password": "str",
 "platform": "str"
 }
 
 | 
分组信息文件示例如下。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | ---
 access_switch:
 groups:
 - switch
 data:
 os_type: ios
 
 core_switch:
 data:
 os_type: nxos
 groups:
 - switch
 
 switch:
 data:
 type: switch
 test:
 test_dict:
 a: 1
 b: 2
 c: abcdef
 
 linux:
 data:
 type: server
 
 | 
默认配置文件示例如下
| 12
 3
 4
 5
 
 | ---
 platform: linux
 groups:
 - linux
 
 | 
 用户脚本
接下来使用脚本来查看配置文件内的机器参数。
| 12
 3
 4
 5
 6
 7
 
 | from nornir import InitNornirnr = InitNornir(config_file="config.yaml")
 
 print(nr.inventory.hosts)
 print(nr.inventory.groups)
 print(nr.inventory.hosts['server_1'].keys())
 print(nr.inventory.hosts['cisco_3850'].keys())
 
 | 
| 12
 3
 4
 
 | {'cisco_3850': Host: cisco_3850, 'cisco_3548': Host: cisco_3548, 'server_1': Host: server_1}{'access_switch': Group: access_switch, 'core_switch': Group: core_switch, 'switch': Group: switch, 'linux': Group: linux}
 dict_keys([])
 dict_keys(['role', 'os_type', 'type', 'test'])
 
 | 
输出1可以看到所有机器名称和它的hostname。
输出2可以看到所有组。
输出3可以看到server_1的data所有key。
输出4可以看到cisco_3850的data所有key。
并且这些输出都是用标准的python数据格式,字典输出而成,无需经过二次处理即可使用。这里面玩法有多少想必大家都很清楚了。
 过滤器
 filter
没错,对于inventory,nornir自带了一个过滤器,可以用来区分设备。
| 1
 | print(nr.filter(os_type="nxos").inventory.hosts.keys())
 | 
| 1
 | dict_keys(['cisco_3548'])
 | 
filter还可以循环使用,多个kv使用。
| 12
 
 | print(nr.filter(os_type="nxos").filter(role="core_switch").inventory.hosts.keys())print(nr.filter(os_type="nxos", role="core_switch").inventory.hosts.keys())
 
 | 
也可以作为变量,中转使用。
| 12
 
 | filter_device=nr.filter(os_type="nxos")print(filter_device.filter(role="core_switch"))
 
 | 
甚至可以塞函数进去。
| 12
 3
 4
 
 | def has_long_name(host):return len(host.name) == 11
 
 nr.filter(filter_func=has_long_name).inventory.hosts.keys()
 
 | 
 children_of_group
filter主要使用data里面的key-value进行过滤,如果根据组别拉出来则使用。
| 1
 | print(nr.inventory.children_of_group("access_switch"))
 | 
 F
nornir提供了过滤器对象F,可以使用它进行更加复杂的组合判断。__contains可以判断是否符合指定字符串。
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | from nornir.core.filter import Fdevice = nr.filter(F(groups__contains="access_switch"))
 print(device.inventory.hosts.keys())
 device = nr.filter(F(groups__contains="access_switch") | F(platform="linux"))
 print(device.inventory.hosts.keys())
 device = nr.filter(F(groups__contains="access_switch") & F(platform="linux"))
 print(device.inventory.hosts.keys())
 device = nr.filter(~F(platform="linux"))
 print(device.inventory.hosts.keys())
 
 | 
| 12
 3
 4
 
 | dict_keys(['cisco_3850'])dict_keys(['cisco_3548', 'server_1'])
 dict_keys([])
 dict_keys(['cisco_3850', 'cisco_3548'])
 
 | 
F甚至可以使用__进行data内的字段直接访问,这时候加上__contains可以判断是否为子字符串。
| 12
 
 | device = nr.filter(F(test__test_dict__c__contains="def"))print(device.inventory.hosts.keys())
 
 | 
| 1
 | dict_keys(['cisco_3850', 'cisco_3548'])
 |