nornir基本使用教程

本文基于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,可自定义命名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#config.yaml
---
#仓库配置
inventory:
#仓库管理插件,一般使用这个,也可自己编写插件。
plugin: SimpleInventory
options:
#三类机器管理文件yaml,可自定义位置。
host_file: "inventory/hosts.yaml"
group_file: "inventory/groups.yaml"
defaults_file: "inventory/defaults.yaml"
#执行器配置
runner:
#多线程插件
plugin: threaded
options:
#线程数
num_workers: 64

也可以直接在python文件中使用变量指定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from nornir import InitNornir
nr = InitNornir(
runner={
"plugin": "threaded",
"options": {
"num_workers": 100,
},
},
inventory={
"plugin": "SimpleInventory",
"options": {
"host_file": "inventory/hosts.yaml",
"group_file": "inventory/groups.yaml"
},
},
)

或两类混用指定配置。

1
2
3
4
5
6
7
8
9
10
from nornir import InitNornir
nr = InitNornir(
config_file="config.yaml",
runner={
"plugin": "threaded",
"options": {
"num_workers": 50,
},
},
)

接着可以使用python文件进行测试,无异常则配置测试通过。

1
2
from nornir import InitNornir
nr = InitNornir(config_file="config.yaml")

机器管理文件

目前来看,例如host_file不支持使用多个文件,无法像ansible使用多个文件进行分类管理。

1
2
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
#hosts.yaml
---
#设备名称,可自定义,即nornir认为的机器名称。
cisco_3850:
hostname: 192.168.1.1
port: 22
username: cisco
password: cisco_password
#nornir自带的platform支持数量过少,这里直接使用netmiko支持的platform
platform: cisco_ios
#分组,会在groups.yaml内体现
groups:
- access_switch
#自定义字段,key和value可随意制定,与设备绑定。
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参数

1
2
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"
}

分组信息文件示例如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#groups.yaml
---
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

默认配置文件示例如下

1
2
3
4
5
#defaults.yaml
---
platform: linux
groups:
- linux

用户脚本

接下来使用脚本来查看配置文件内的机器参数。

1
2
3
4
5
6
7
from nornir import InitNornir
nr = 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())
1
2
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_1data所有key。

输出4可以看到cisco_3850data所有key。

并且这些输出都是用标准的python数据格式,字典输出而成,无需经过二次处理即可使用。这里面玩法有多少想必大家都很清楚了。

过滤器

filter

没错,对于inventory,nornir自带了一个过滤器,可以用来区分设备。

1
print(nr.filter(os_type="nxos").inventory.hosts.keys())
1
dict_keys(['cisco_3548'])

filter还可以循环使用,多个kv使用。

1
2
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())

也可以作为变量,中转使用。

1
2
filter_device=nr.filter(os_type="nxos")
print(filter_device.filter(role="core_switch"))

甚至可以塞函数进去。

1
2
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"))
1
{Host: cisco_3850}

F

nornir提供了过滤器对象F,可以使用它进行更加复杂的组合判断。__contains可以判断是否符合指定字符串。

1
2
3
4
5
6
7
8
9
from nornir.core.filter import F
device = 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())
1
2
3
4
dict_keys(['cisco_3850'])
dict_keys(['cisco_3548', 'server_1'])
dict_keys([])
dict_keys(['cisco_3850', 'cisco_3548'])

F甚至可以使用__进行data内的字段直接访问,这时候加上__contains可以判断是否为子字符串。

1
2
device = nr.filter(F(test__test_dict__c__contains="def"))
print(device.inventory.hosts.keys())
1
dict_keys(['cisco_3850', 'cisco_3548'])