「Python 网络自动化」Nornir—— Inventory(主机清单)介绍
Nornir 是一个非常好用的网络自动化的框架,最近我输出了一份 Nornir 中文手册,欢迎大家阅读指正。
主机清单
主机清单(Inventory) 是 nornir 最重要的部分,它由 hosts、groups、defaults 三部分组成。它还支持多种插件,默认情况下使用 SimpleInventory
插件。在之前的版本中,nornir 还支持 Ansible、Netbox 等主机格式的插件,3.0 版本之后,除了最核心的功能外,其他的功能都需要手动导入插件来使用。
在本教程中使用 SimpleInventory
插件来了解主机清单相关的内容。
可以在 nornir.tech 中获取当前已经公开发布的插件。
在 SimpleInventory
插件中,需要 hosts、groups、defaults 三个文件来存储信息,其中 groups、defaults 文件不是必需的。
主机相关的文件都使用 YAML 格式来保存数据,YAML 是一种可读性较好的标记语言,有关 YAML 的内容,可以查看 YAML 入门教程或者 YAML 官方手册。
现在来看一个 hosts 的示例文件:
# %load files/inventory/hosts.yaml
---
host01.bj:hostname: 127.0.0.1port: 2201username: netdevopspassword: netdevopsplatform: linuxgroups:- bjdata:site: bjrole: hosttype: hostnested_data:a_dict:a: 1b: 2a_list: [1, 2]a_string: "this is a web server"spine00.bj:hostname: 127.0.0.1username: netdevopspassword: netdevopsport: 12444platform: iosgroups:- bjdata:site: bjrole: spinetype: network_devicespine01.bj:hostname: 127.0.0.1username: netdevopspassword: ""platform: junosport: 12204groups:- bjdata:site: bjrole: spinetype: network_deviceleaf00.bj:hostname: 127.0.0.1username: netdevopspassword: netdevopsport: 12443platform: hp_comwaregroups:- bjdata:site: bjrole: leaftype: network_deviceasn: 65100leaf01.bj:hostname: 127.0.0.1username: netdevopspassword: ""port: 12203platform: huaweigroups:- bjdata:site: bjrole: leaftype: network_deviceasn: 65101host01.gz:groups:- gzplatform: linuxdata:site: gzrole: hosttype: hostspine01.gz:hostname: 127.0.0.1username: netdevopspassword: netdevopsport: 12444platform: eosgroups:- gzdata:site: gzrole: spinetype: network_deviceleaf01.gz:hostname: 127.0.0.1username: netdevopspassword: netdevopsport: 12443platform: eosgroups:- gzdata:site: gzrole: leaftype: network_devicehost00:groups:- gz- bjhost01:groups:- bj- gz
主机文件是由键值对组成的映射表,其中最外层的是主机名,第二层是主机的一些基本信息,第三层、第四层是主机的其他相关信息。可以通过以下代码来查看一个主机对象的数据模型:
from nornir.core.inventory import Host
import json
print(json.dumps(Host.schema(), indent=4))
{"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"
}
通过这段代码可以看到一个主机对象可以包含的所有信息。
如果需要登录设备,那么 connection_options
里面的 5 个参数 hostname、port、username、password、platform 是必须包含的(注:默认情况下,connection_options
会从第二层进行取值,如果设备的登录地址和资产管理地址不一样,可以在该选项里面单独指定),如果有额外的连接参数需要传递(如 enable password 、指定连接方式等),则需要在 extras
里面进行添加;其他字段都是可以选的,其中用户可以将所需的任意信息定义到 data
字段中。
当然,如果主机信息只做资产管理的作用,没有登录设备的需求,除了最外层的主机名以外,其他字段都是可选的。
groups 文件和 hosts 文件一样,也是由键值对映射组成,来看一个示例:
# %load files/inventory/groups.yaml
---
global:data:domain: global.localasn: 1north:data:asn: 65100bj:groups:- north- globalgz:data:asn: 65000vlans:100: wired200: wireless
最后,defaults 文件与之前描述的 Host 对象架构一样,但是它只有 data
字段,没有其他外层键值对。
# %load files/inventory/defaults.yaml
---
data:domain: netdevops.local
访问主机清单
可以通过 nornir 对象的 inventory
属性来访问主机清单。
from nornir import InitNornir
nr = InitNornir(config_file="files/config.yaml")
主机清单有两个类字典(dict-like)的属性:hosts
和 groups
,通过访问该属性,可以获取到当前有哪些主机和组。
查看加载的配置文件中包含哪些主机:
nr.inventory.hosts
{'host01.bj': Host: host01.bj,'spine00.bj': Host: spine00.bj,'spine01.bj': Host: spine01.bj,'leaf00.bj': Host: leaf00.bj,'leaf01.bj': Host: leaf01.bj,'host01.gz': Host: host01.gz,'spine01.gz': Host: spine01.gz,'leaf01.gz': Host: leaf01.gz,'host00': Host: host00,'host01': Host: host01}
查看加载的配置文件中包含哪些组:
nr.inventory.groups
{'global': Group: global,'north': Group: north,'bj': Group: bj,'gz': Group: gz}
主机和组都是类字典(dict-like)形式的对象,可以通过 [$values]
来访问它们的属性,以主机 host01.bj
为例,来查看一下这个主包含哪些属性:
host = nr.inventory.hosts["host01.bj"]
host.keys()
dict_keys(['site', 'role', 'type', 'nested_data', 'asn', 'domain'])
查看这个主机位于哪个站点:
host["site"]
'bj'
继承模型
Nornir 中,hosts、groups、defaults 数据之间有继承关系,下面来看一下继承是如何工作的。
# %load files/inventory/groups.yaml
---
global:data:domain: global.localasn: 1north:data:asn: 65100bj:groups:- north- globalgz:data:asn: 65000vlans:100: wired200: wireless
在 hosts.yaml
中,可以看到 host01.bj
属于 bj
组,bj
组又属于 north
和 global
组;主机 host01.gz
属于 gz
组。
在这里,nornir 的数据解析方式是:递归遍历所属的父组,并查看任意父组中是否包含相应的数据。
host01_bj = nr.inventory.hosts["host01.bj"]
host01_bj["domain"] # 继承自 `global` 组
'global.local'
host01_bj["asn"] # 继承自 `north` 组
65100
如果主机有数据,那么优先使用主机具有的数据,而不是从父组继承:
leaf01_bj = nr.inventory.hosts["leaf01.bj"]
leaf01_bj["asn"] # 主机的 asn 为 65101,父组 `bj` 的 asn 为 65100
65101
如果主机、父组都没有数据,那么会从 defaults
中继承:
host01_gz = nr.inventory.hosts["host01.gz"]
host01_gz["domain"] # 从 `defaults` 中继承数据
'netdevops.local'
如果 nornir 遍历了所有的父组,而且 defaults
中也没有数据,则会返回 KeyError
:
try:host01_gz["non_existent"]
except KeyError as e:print(f"无法找到数据:{e}")
无法找到数据:'non_existent'
如果不想遍历父组的话,可以直接使用主机的 data
属性来访问。例如从上面的示例中 host01_bj
的 asn 是继承自父组 north
,直接通过 data
来访问这个属性的话,不会遍历父组,而是返回 KeyError
的错误。
父组之间数据的优先级关系
Nornir 通过遍历所有父组来查找数据,那么如果多个父组里面有相同的数据,会如何取值?通过一个不恰当的例子来看一下,host00
和 host01
都属于 bj
和 gz
组,但是配置文件中的顺序有所差异:
host00 = nr.inventory.hosts["host00"]
print(host00.groups) # `gz` 的 asn 为 65000
host00["asn"]
[Group: gz, Group: bj]65000
host01 = nr.inventory.hosts["host01"]
print(host01.groups) # `bj` 的 asn 为 65100,继承自 `north`
host01["asn"]
[Group: bj, Group: gz]65100
可以看到如果主机属于多个组,数据解析是按照列表的先后顺序进行迭代,源码实现中是对数据的 key
做了判断,如果遍历已经找到了对应的 key
,之后不会再更新数据。
主机清单的过滤方法
到目前已经看到 nr.inventory.hosts
和 nr.inventory.groups
是类字典(dict-like)的对象,可以使用它们来遍历所有主机和组或直接访问任何特定的主机和组。现在来看看如何进行一些更高级的过滤:根据主机的属性对来对一组主机进行操作。
过滤主机最简单的方法是通过 filter
传入键值对(<key,value>)参数,例如筛选站点是 bj
的机器:
nr.filter(site='bj').inventory.hosts
{'host01.bj': Host: host01.bj,'spine00.bj': Host: spine00.bj,'spine01.bj': Host: spine01.bj,'leaf00.bj': Host: leaf00.bj,'leaf01.bj': Host: leaf01.bj}
也可以使用多个键值对来进行过滤,例如筛选站点是 bj
而且角色为 spine
的设备:
nr.filter(site='bj', role='spine').inventory.hosts
{'spine00.bj': Host: spine00.bj, 'spine01.bj': Host: spine01.bj}
filter
方法也可以进行叠加使用:
nr.filter(site='bj').filter(role='spine').inventory.hosts
{'spine00.bj': Host: spine00.bj, 'spine01.bj': Host: spine01.bj}
或者赋值给对象,进行再次过滤:
bj = nr.filter(site='bj')
bj.filter(role='spine').inventory.hosts
{'spine00.bj': Host: spine00.bj, 'spine01.bj': Host: spine01.bj}
bj.filter(role='leaf').inventory.hosts
{'leaf00.bj': Host: leaf00.bj, 'leaf01.bj': Host: leaf01.bj}
还可以根据组进行过滤,例如查找所有属于 bj
组的主机:
nr.inventory.children_of_group('bj')
{Host: host00,Host: host01,Host: host01.bj,Host: leaf00.bj,Host: leaf01.bj,Host: spine00.bj,Host: spine01.bj}
高级过滤方法
有时候使用键值对无法满足过滤需求,还可以使用更高级的过滤方式:
- 过滤函数(filter function)
- 过滤对象(filter object)
过滤函数(filter functions)
Filter 方法里面的 filter_func
参数可以通过传入自定义代码来进行主机过滤。过滤函数的格式应该是 my_func(host)
,其中参数是一个主机对象(Host)并且返回值必须是 True
或 False
来确定过滤结果是否是需要的主机。
# 过滤名字主机名长度为 10 的主机
def has_long_name(host):return len(host.name) == 10nr.filter(filter_func=has_long_name).inventory.hosts
{'spine00.bj': Host: spine00.bj,'spine01.bj': Host: spine01.bj,'spine01.gz': Host: spine01.gz}
# 或者使用 lambda 函数
nr.filter(filter_func=lambda h: len(h.name)==6).inventory.hosts
{'host00': Host: host00, 'host01': Host: host01}
过滤对象(filter object)
使用过滤对象 F
来叠加创建复杂查询对象。
F
对象作为 filter
方法的参数,也接受键值对传参,可以使用叠加的双下划线来访问到任意数据(类似于字典的 []
取值),也可以使用 __contains
来检查一个元素中是否包含指定字符。同时还支持将多个 F
对象进行位运算(&
、|
、~
)来返回查询对象。
注:
__contains__
一般情况下是 Python 容器对象的方法,在 nornir 中,groups 是一个列表,所以对组进行过滤时,应该使用__contains
。
来看几个例子:
# 首先引入 F 对象
from nornir.core.filter import F
# 查看属于 `bj` 组的设备
bj = nr.filter(F(groups__contains='bj'))
bj.inventory.hosts
{'host01.bj': Host: host01.bj,'spine00.bj': Host: spine00.bj,'spine01.bj': Host: spine01.bj,'leaf00.bj': Host: leaf00.bj,'leaf01.bj': Host: leaf01.bj,'host00': Host: host00,'host01': Host: host01}
# 查看 `bj` 组中,系统是 `linux` 的设备
bj_linux = nr.filter(F(groups__contains='bj') & F(platform='linux'))
bj_linux.inventory.hosts
{'host01.bj': Host: host01.bj}
# 查看系统是 `ios` 或者 `eos` 的设备
ios_or_eos = nr.filter(F(platform='ios') | F(platform='eos'))
ios_or_eos.inventory.hosts
{'spine00.bj': Host: spine00.bj,'spine01.gz': Host: spine01.gz,'leaf01.gz': Host: leaf01.gz}
# 查看 `gz` 组中,角色不是 `spine` 的设备
gz_not_spine = nr.filter(F(groups__contains='gz') & ~F(role='spine'))
gz_not_spine.inventory.hosts
{'host01.gz': Host: host01.gz,'leaf01.gz': Host: leaf01.gz,'host00': Host: host00,'host01': Host: host01}
# 使用 `__` 来查看用户自定义的数据,并检查 dicts/lists/strings 是否包含元素
nested_dict = nr.filter(F(nested_data__a_dict__a=1))
nested_dict.inventory.hosts
{'host01.bj': Host: host01.bj}
nested_list = nr.filter(F(nested_data__a_list__contains=1))
nested_list.inventory.hosts
{'host01.bj': Host: host01.bj}
nested_string = nr.filter(F(nested_data__a_string__contains='web'))
nested_string.inventory.hosts
{'host01.bj': Host: host01.bj}
# 也可以对键值对的数据进行 `__contains` 查找
host_os = nr.filter(F(platform__contains='os'))
host_os.inventory.hosts
{'spine00.bj': Host: spine00.bj,'spine01.bj': Host: spine01.bj,'spine01.gz': Host: spine01.gz,'leaf01.gz': Host: leaf01.gz}
「Python 网络自动化」Nornir—— Inventory(主机清单)介绍相关推荐
- 「Python 网络自动化」目录汇总
目录 Netmiko NETCONF Nornir Paramiko Napalm NetBox TextFSM 其他 关于文章 关于我 Netmiko 「Python 网络自动化」Netmiko - ...
- 网络服务器没运行,「dns网络服务器」未响应应该如何解决呢
「dns网络服务器」未响应怎么解决呢?我们日常上网的时候,有时候会遇到网络连接异常,显示DNS服务器未响应的状况.下面我们介绍下解决方法. 首先如果你的电脑上安装了360或者腾讯等其他的杀毒软件,可以 ...
- python断点调试_「Python调试器」,快速定位各种疑难杂症!!!
在很多的编辑器其实都带着「调试程序」的功能,比如写 c/c++ 的 codeblocks,写 Python 的 pycharm,这种图形界面的使用和显示都相当友好,简单方便易学,这个不是我这篇文章要讲 ...
- python程序如何执行死刑_「Python基础知识」Python生成器函数
原标题:「Python基础知识」Python生成器函数 对于程序而言,内存也是很重要的,因为程序中很多数据都是保存在内存中的,如果内存中存储的数据过多,那么系统就会崩溃,这是人们不希望发生的. 可以采 ...
- Ansible中的inventory主机清单(预祝你我有数不尽的鲜花和浪漫)
文章目录 前言 一.inventory 主机清单 1.1 inventory 中的变量 1.2 主机变量 1.3 组变量 1.4 组嵌套 前言 本篇博客主要解释Ansible主机清单的相关配置知识 一 ...
- Python网络编程(06)----MySQL8.0介绍--01(使用command命令创建数据库以及数据表)
学习python网络编程最重要的是学会用数据库,数据库的基础知识这里不作介绍,主要讲解使用command命令创建数据库,并往数据库里面插入数据表并查看.然后再用python调用pymysql访问以及操 ...
- 「腾讯视频」微信小程序插件介绍
上期,我们在<从原理到应用,一文带你了解小程序插件能力>一文中介绍了小程序插件的意义.作用以及应用.今天开始,我们会每期与大家分享一款优秀的小程序插件,从使用场景到使用方法,都将作出详细的 ...
- Ansible 系列之 Inventory 资源清单介绍
http://www.cnblogs.com/hanyifeng/p/6137905.html 一.Inventory 库存清单文件 1.Inventory 作用 Ansible 可以在同一时间针对多 ...
- 「Python|输入输出」如何进行用户输入、文件输入和输出
本文主要介绍Python中如何让用户进行内容输入,如何从文件中读取数据作为输入以及如何将数据保存到文件中 文章目录 如何让用户进行内容输入 如何从文件中获取数据到程序(内存)中 如何将程序数据存储到文 ...
最新文章
- js获取asp.net服务器端控件Label,TextBox,RadioButtonList,DropDownList的值
- 综合评价模型的缺点_浅谈交通影响评价中不同交通预测方法的特性
- 老婆从怀孕到产子的所有细节
- c mssql mysql_mssql与mysql 数据迁移
- 通过MULE集成服务的几种方式
- Linux查看修改时间、时区
- 使用log4j2免费分配日志记录
- C++经典面试题汇总
- android 条码扫描控件,Android Zxing条码扫描自定义控件(附代码)
- 硅谷卖场里看家庭监控设备:Dropcam难撼传统DVR系统
- 【二维码识别】基于matlab GUI 灰度+二值化+校正QR二维码识别与生成【含Matlab源码 600期】
- python与机械教育初探_《Python与机器人程序设计》
- 微信信息轰炸【简易版】
- Ring buffer streaming in general - how to imple...
- 关于双硬盘安装双系统
- 金仓数据库 KingbaseES插件参考手册 F
- 给Android手机设置的壁纸应该是多大尺寸
- python中字符串转数组、数组转字符串
- 又闹分裂?Node.js 被 fork 出一个项目 Ayo.js
- python微信聊天机器人源码_Python的微信二次开发!实战微信智能聊天机器人!