基于已收集的网络设备信息进行的结构化数据分析框架
Project description
前言
作为网络工程师,需要经常收集设备的设备信息,比如设备CPU利用率、内存利用率、软件版本、序列号、接口状态等,这些信息可以用来监控网络设备的运行状态,也可以用来做设备的资产管理。
在已经收集完成的情况下,我们需要将关键信息提取出来,然后将可用数据按需求格式输出。市面上目前没有针对多厂商的网络设备数据解析工具,厂家只会制作针对自己设备的解析工具。造成使用割裂,并且输出样式也无法做到自定义。
另外,网络工程师学习编程后大部分都是单兵作战,针对自己的使用场景,进行专门的脚本编写,但这就导致了脚本的可复用性差、性能优化不足、脚本设计考虑不够周全、扩展性不强、调试困难等。
通常情况下, 在获得设备回显信息后, 通过textFSM
或者正则表达式解析数据,最后将可用的数据按需求格式进行输出。
但是,这样的操作方式有很多不便,比如:
- 在通过
show
或者display
将信息收集后,如何分割命令的回显?哪些是无效命令? - 网管平台收集的信息格式不一样怎么处理?
- 如何识别设备对应的厂商?
- 如何调用对应的
textFSM
模板? - 如何搜索命令获取关键信息?
- 多厂商的情况下,如何做到统一的数据调用?
- 多厂商的情况下,如何做到统一的数据格式?
ntc-templates
大部分模板都是国外厂商,怎么办?- 如何在调用
ntc-templates
的同时调用自己写的textFSM
模板库? - 设计的脚本只针对单一情况,如何解决复用性的问题?
要解决以上问题,就需要用到net_inspect
这个框架。
它可以让使用者无需关心数据处理的各种细节以及各个厂家的差异。屏蔽了厂商差异,只需要在数据处理完成后通过统一的方式调用即可。
net_inspect框架介绍
net_inspect是一个基于python
的网络设备数据解析自动化框架
框架设计的初衷就是将各个环节解耦,使各个环节能够独立开发扩展,最终实现对多厂商设备的数据解析。
安装方法:
pip install net_inspect
一个简单的例子
├── log
│ ├── Switch_A.log
│ ├── Switch_B.log
│ ├── Switch_C.log
可用看到,在log
文件夹下有三台设备的命令的回显信息,这些文件是通过vty
或者console
连接到设备上作为记录日志保存下来。每个文件中都需要包含display version
这个命令来识别设备的厂商。
现在我们需要将这些设备的厂商、软件版本、管理IP,提取打印出来。
from net_inspect import NetInspect
net = NetInspect()
net.set_plugins(input_plugin='console')
net.run(input_path='log')
print('total devices:', len(net.cluster.devices))
for device in net.cluster.devices:
info = device.info
print(' | '.join([info.hostname, info.ip, info.vendor,
info.version, info.model, info.cpu_usage]))
output:
total devices: 3
Switch_A | 24.44.1.248 | Huawei | 5.170 | S12704 Terabit Routing Switch | 6%
Switch_B | 24.45.1.240 | H3C | 5.20 Release: 3342 | SR8800 | 5%
Switch_C | 24.45.254.254 | H3C | 5.20 Release: 1238P08 | S9508E-V | 1%
可以看到,仅仅通过简单的几行代码,就可以实现对多厂商设备的数据解析和调用。
console
代表的是vty
或者console
连接设备的回显信息格式。
可供使用的base_info属性信息在使用
net_inspect -b
查看 其中analysis
的分析结果均是Optional[bool]
类型
- 如果为
None
,则表示未分析出结果。- 如果为
True
,则表示有告警级别的信息。- 如果为
False
,则表示为正常或者只是关注级别的信息。
框架的组成
net_inspect
由以下几个部分组成:
input_plugin
输入插件,用于将设备的回显进行分割,将每个执行的命令和对应的回显作为字典储存。parse_plugin
解析插件,用于对命令进行解析,一般情况下不需要进行修改。analysis_plugin
分析插件,每一个分析插件对应一个状态分析,比如power_status
就是用来分析设备的电源状态的。output_plugin
输出插件,用于输出统一的格式报告。base_info
基础信息,用于存放设备的基础信息,比如厂商、软件版本、管理IP等。可以全厂商统一调用。
有时候我们收集设备信息的方式不止是通过console,或者自己的telnet/ssh。而是通过网管平台进行的,回显格式不同,这时候就需要自己写一个
input_plugin
,将网管平台的数据转换成net_inspect
需要的格式。
parse_plugin
使用的是ntc_templates_elinpf这个库,它是ntc_templates
的一个分支,主要是为了解决ntc_templates
没有对国内厂商支持的问题。
所以,需要先卸载
pip uninstall ntc_templates
然后安装pip install ntc_templates_elinpf
net_inspect
支持cli的命令行模式,可以通过net_inspect -h
查看帮助信息。
进阶教程
上面的简单例子中还有很多地方没有涉及到,比如analysis_plugin
的使用,base_info
的使用,output_plugin
的使用等等。
试想一个情况,我们需要获取设备的show clock
信息,在ntc_templates_elinpf
中没有,怎么办?
调用外部textFSM模板
要解决ntc_templates_elinpf
中没有模板的问题,我们可以自己写一个本地的textFSM模板仓库,然后调用。
├── local_templates
│ ├── huawei_vrp_display_clock.textfsm
│ ├── hp_comware_display_clock.textfsm
│ ├── cisco_ios_show_clock.textfsm
│ ├── index
可以看到上面的本地模板库中,包含了huawei_vrp
、hp_comware
、cisco_ios
三个厂商的display clock
模板。
另外还有一个index
文件,用于指定模板的调用命令,毕竟我们在敲命令的时候不会敲全,比如display clock
,我们可以敲dis clo
,这时候就需要index
文件来指定。
index
文件内容如下:
Template, Hostname, Platform, Command
huawei_vrp_display_clock.textfsm, .*, huawei_vrp, dis[[play]] clo[[ck]]
hp_comware_display_clock.textfsm, .*, hp_comware, di[[splay]] clo[[ck]]
cisco_ios_show_clock.textfsm, .*, cisco_ios, sh[[ow]] clo[[ck]]
注意每个厂商需要空格隔开, 并且开头必须要有Template, Hostname, Platform, Command
这行。
做好准备工作后,只需要在脚本中调用本地模板库即可。
from net_inspect import NetInspect, vendor
net = NetInspect()
net.set_plugins(input_plugin='console')
net.set_external_templates('local_templates') # 调用本地模板库
net.run(input_path='log')
print('total devices:', len(net.cluster.devices))
for device in net.cluster.devices:
info = device.info
clock = ''
if device.vendor == vendor.Huawei:
with device.search_cmd('display clock') as cmd:
if cmd.parse_result:
ps = cmd.parse_result[0]
clock = f"{ps['year']}-{ps['month']}-{ps['day']} {ps['time']}"
if device.vendor == vendor.H3C:
with device.search_cmd('display clock') as cmd:
if cmd.parse_result:
ps = cmd.parse_result[0]
clock = f"{ps['year']}-{ps['month']}-{ps['day']} {ps['time']}"
print(' | '.join([info.hostname, clock]))
output:
total devices: 3
Switch_A | 2021-03-19 10:23:08
Switch_B | 2021-03-19 10:24:17
Switch_C | 2021-03-19 10:32:17
可以看到,我们通过调用本地模板库,成功解析了display clock
命令。
然后搜索厂商对应的命令,将解析的结果格式化为我们想要的格式。最后再输出。
这样的写法是否有点麻烦,并且不利于复用呢?有没有更好的方式呢?
新增基础信息
我们可以在base_info
中新增一个clock
字段,然后只需要调用这个clock
属性就可以了,方法如下:
from net_inspect import NetInspect, EachVendorDeviceInfo, BaseInfo, Device
class AppendClock(BaseInfo):
clock: str = '' # 巡检时间
class EachVendorWithClock(EachVendorDeviceInfo):
base_info_class = AppendClock # 基本信息类
def do_huawei_vrp_baseinfo_2(self, device: Device, info: AppendClock):
# 添加do_<vendor_platform>_baseinfo_<something>方法,可以自动运行
with device.search_cmd('display clock') as cmd:
if cmd.parse_result:
row = cmd.parse_result[0]
info.clock = f'{row["year"]}-{row["month"]}-{row["day"]} {row["time"]}'
def do_hp_comware_baseinfo_2(self, device: Device, info: AppendClock):
with device.search_cmd('display clock') as cmd:
if cmd.parse_result:
row = cmd.parse_result[0]
info.clock = f'{row["year"]}-{row["month"]}-{row["day"]} {row["time"]}'
if __name__ == '__main__':
net = NetInspect()
net.set_plugins(input_plugin='console')
net.set_base_info_handler(EachVendorWithClock) # 设置获取设备基本信息的处理类
net.set_external_templates('local_templates')
net.run(input_path='log')
print('total devices:', len(net.cluster.devices))
for device in net.cluster.devices:
info = device.info # type: AppendClock
print(' | '.join([info.hostname, info.clock]))
output:
total devices: 3
Switch_A | 2021-03-19 10:23:08
Switch_B | 2021-03-19 10:24:17
Switch_C | 2021-03-19 10:32:17
输出的结果是一样的,可以看到,在base_info
中新增了一个clock
字段,然后只需要调用这个clock
属性就可以了。这样的做法复用性强,后续再想要获取设备的时间的时候,只需要调用clock
属性即可。
自定义分析信息
完成了数据的解析调用,下面还需要对设备进行分析,比如判断电源、风扇是否故障,cpu、memory利用率是否过高等。
当net_inspect
中没有需要的分析模块时,我们可以自定义分析模块。
比如我们写一个检查OSPF邻居状态的分析模块
from __future__ import annotations
from typing import TYPE_CHECKING
from net_inspect import NetInspect, vendor
from net_inspect.analysis_plugin import analysis, AnalysisPluginAbc
if TYPE_CHECKING:
from net_inspect.analysis_plugin import TemplateInfo
from net_inspect.domain import AnalysisResult
class AnalysisPluginWithOSPFStatus(AnalysisPluginAbc):
"""OSPF status 状态不能为Init"""
@analysis.vendor(vendor.Huawei)
@analysis.template_key('huawei_vrp_display_ospf_peer_brief.textfsm', ['neighbor', 'state'])
def huawei_vrp(template: TemplateInfo, result: AnalysisResult):
"""华为状态检查"""
for row in template['display ospf peer brief']:
if row['state'].lower() == 'init':
result.add_warning(f'{row["neighbor"]} is in init state')
if __name__ == '__main__':
net = NetInspect()
net.set_plugins(input_plugin='console')
net.run(input_path='log')
print('total devices:', len(net.cluster.devices))
for device in net.cluster.devices:
ospf_status = device.analysis_result.get('ospf status')
warning_list = []
for alarm in ospf_status:
if alarm.is_warning:
warning_list.append(alarm.message)
print(' | '.join([device.info.hostname, ', '.join(warning_list)]))
output:
total devices: 3
Switch_A | 24.42.254.20 is in init state
Switch_B |
Switch_C |
这样我们就得到了一个可重复利用的分析模块,后续在想要检查OSPF邻居状态的时候,只需要调用这个模块即可。
这里的案例只添加了Huawei
这一个厂家的分析方法,其他厂家的分析方法大家可以自行添加。
编写这个需要注意:
@analysis.template_key
中的templates
名称需要完整包含后缀名。- 类方法不需要
self
这个关键字。 - 结果加入到
result
中即可,不需要返回值。 - 整个过程不用单独设置,所有信息会直接写入
analysis
全局变量中。 - 类注释和方法注释必须要写,因为会用到这个注释作为分析结果的标题。
自定义输出模块
完成了数据的分析后,需要对结果进行调用,可以直接以写脚本的方式,也可以自定义输出模块。
自定义输出模块的方式方便二次调用,比如我们写一个输出table的例子:
from net_inspect import NetInspect, OutputPluginAbstract
from rich.table import Table
from rich.console import Console
class Output(OutputPluginAbstract):
def main(self):
self.check_args('title') # 检查是否提供title参数
console = Console()
table = Table(title=self.args.output_params.get(
'title'), show_lines=False)
columns = ['hostname', 'ip', 'model', 'version', 'power status']
for col in columns:
table.add_column(col, justify='center')
table.row_styles = ['green']
for device in self.args.devices:
info = device.info
table.add_row(
info.hostname,
info.ip,
info.model,
info.version,
'Abnormal' if info.analysis.power else 'Normal'
)
console.print(table)
if __name__ == '__main__':
net = NetInspect()
net.set_plugins(input_plugin='console', output_plugin=Output)
cluster = net.run(input_path='log', output_plugin_params={
'title': '设备信息'})
output:
可以看到,我们自定义了一个输出模块,可以直接调用,而不需要写脚本。
output_plugin_params
中的所有参数都会传递到Output
类中的output_params
变量中,可以直接使用。
手动添加设备
有时候我们需要手动添加设备,比如我们需要添加一个设备,但是这个设备没有日志,我们可以手动添加,并且指定设备的厂家,这样就可以使用对应厂家的分析模块。
from net_inspect import NetInspect, InputPluginResult, vendor
net = NetInspect()
d = InputPluginResult()
d.add_cmd('display clock', "2021-03-19 10:23:08+08:00")
d.hostname = 'Device'
d.vendor = vendor.Huawei
net.add_device(d)
net.run()
for device in net.cluster.devices:
print(device.info.hostname)
print(device.parse_result('dis clo'))
output:
Device
[{'time': '10:23:08', 'timezone': '', 'dayweek': '', 'year': '2021', 'month': '03', 'day': '19'}]
关于贡献
分析插件还在持续开发中,develop_script.py
脚本就是为高效开发提供的一个工具。
开发一个分析插件的流程,以开发检查风扇状态的fan_status
插件为例:
- 创建一个新的插件文件, 对应的文件初始状态会一并准备好
python ./develop_script.py -p fan_status -g
- 在对应的文件中实现插件对每个厂商分析的函数
class AnalysisPluginWithFanStatus(AnalysisPluginAbc):
"""
要求设备所有在位风扇模块运行在正常状态。
"""
@analysis.vendor(vendor.H3C)
@analysis.template_key('hp_comware_display_fan.textfsm', ['slot', 'id', 'status'])
def hp_comware(template: TemplateInfo, result: AnalysisResult):
"""模块状态不为Normal的时候告警"""
for row in template['display fan']:
if row['status'].lower() != 'normal':
result.add_warning(
f'Slot {row["slot"]} Fan {row["id"]} 状态异常' if row['slot'] else f'Fan {row["id"]} 状态异常')
其中@analysis
是用来记录插件的分析类型的,vendor
记录插件的厂商类型,template_key
记录分析模块所需要的textfsm
文件以及里面的哪些值。
这些值会在参数template: TemplateInfo
中给出。
result: AnalysisResult
用来记录分析结果。可以添加告警信息。
分析方法为类方法,不需要self
,不需要给出返回值。
插件中的类注释和方法注释都会被记录下来,方便后续调用。
- 创建对应的测试文件
当编写了对应的分析方法后,再次执行创建命令,工具会自动根据分析方法中需要的命令,生成对应的测试文件。
测试文件路径为tests/check_analysis_plugins/<plugin_name>/<funcation_name>.raw
python ./development_script.py -p fan_status -f hp_comware -g
- 在测试文件中添加测试用例
- 执行测试
python ./development_script.py -p fan_status -f hp_comware -t
- 完成测试,确认测试结果为正常后,生成yml文件作为参考文件。
python ./development_script.py -p fan_status -f hp_comware -y
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file net_inspect-1.5.3.tar.gz
.
File metadata
- Download URL: net_inspect-1.5.3.tar.gz
- Upload date:
- Size: 50.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.3.2 CPython/3.11.1 Linux/5.15.0-1031-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5868cf44419793df52bf6f069ebf73928421ed7b3ca4cb5eabbfa39066adf9ff |
|
MD5 | 967dace0b60797a07df320c84d4edc0b |
|
BLAKE2b-256 | 24f786f34420e5cc579131f8c31e003f987607172527e5c48165818c865647ce |
File details
Details for the file net_inspect-1.5.3-py3-none-any.whl
.
File metadata
- Download URL: net_inspect-1.5.3-py3-none-any.whl
- Upload date:
- Size: 55.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.3.2 CPython/3.11.1 Linux/5.15.0-1031-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c70e5542ca60d93de7188523c6118373aaa9162dfd2109d4bec4bc947ea3a2c6 |
|
MD5 | 518d335c551964cddc3d9c06373c82bc |
|
BLAKE2b-256 | 4a9d925325f8934abc6f97d69f3f1a40415fb2211d40dae7d23047ef098eb8d1 |