Python的网络设备信息采集利器:Netmiko

  • A+
所属分类:神兵利刃

本文是本人在2020年的一篇文章,随着认知的不断深入以及堵着门的相关,本人又编写了上下两篇三万字的最强Netmiko攻略(不接受反驳,手动狗头~)

————————————————————分割线—————————————————————

采集网络设备有多种方法,常见的有SSH、Telnet、SNMP、Netconf、设备或控制器的Restful API。

之前的分享讲解了Netconf的采集,Netconf的前提是设备支持此协议,如果设备不支持或者是工程师一时难以接受,我们就来分享一个基于CLI的网络设备信息采集工具Netmiko(也是Python的第三方包,非常优秀的网络工具包)。

使用ssh登录设备输入命令回显输出。python常用的包有pexpect、paramiko、netmiko。个人推荐使用netmiko,封装的比较好,支持的厂商和型号(系列)比较多,也可以对Linux进行操作。

pexpect像是Linux的expect,实际使用中需要二次开发或者定制回显的expect字符串(比如每敲一段show version,当我们看到“设备名#“就代表回显结束,这个“设备名#”就是expect的字符串)。

paramiko相比pexpect进行过一些封装,可以比较好的自动判断回显,但是遇到分页还是需要自己处理,程序无法处理。而网络设备在执行命令的时候经常有分页,或者一些特殊模式等等。所以paramiko对网络设备支持还是有限。

那Netmiko是基于paramiko的二次封装,从名字上就可以看出来它有两部分,net代表网络,miko代表的是paramiko。它能非常好的适配网络设备,处理好各厂商的分页、特殊模式,同时针对网络的一些特点加入了config、save、enable、文件传输(多用于备份)等网络所特有的一些功能。

安装netmiko包

请安装python(建议Anaconda,简单粗暴)后。记得是python3.7及以上版本

执行"pip install netmiko"命令即可。

使用netmiko

from netmiko import ConnectHandler
'''
关于netmiko的使用,建议查看官网的说明,

简单的show命令的话,用以下代码即可,注意的是device_type

内置常用的有有以下 请留意左侧的key值,是支持的设备系列。

CLASS_MAPPER_BASE = {
    'a10': A10SSH,
    'accedian': AccedianSSH,
    'alcatel_aos': AlcatelAosSSH,
    'alcatel_sros': AlcatelSrosSSH,
    'apresia_aeos': ApresiaAeosSSH,
    'arista_eos': AristaSSH,
    'aruba_os': ArubaSSH,
    'avaya_ers': AvayaErsSSH,
    'avaya_vsp': AvayaVspSSH,
    'brocade_fastiron': RuckusFastironSSH,
    'brocade_netiron': BrocadeNetironSSH,
    'brocade_nos': BrocadeNosSSH,
    'brocade_vdx': BrocadeNosSSH,
    'brocade_vyos': VyOSSSH,
    'checkpoint_gaia': CheckPointGaiaSSH,
    'calix_b6': CalixB6SSH,
    'ciena_saos': CienaSaosSSH,
    'cisco_asa': CiscoAsaSSH,
    'cisco_ios': CiscoIosSSH,
    'cisco_nxos': CiscoNxosSSH,
    'cisco_s300': CiscoS300SSH,
    'cisco_tp': CiscoTpTcCeSSH,
    'cisco_wlc': CiscoWlcSSH,
    'cisco_xe': CiscoIosSSH,
    'cisco_xr': CiscoXrSSH,
    'coriant': CoriantSSH,
    'dell_force10': DellForce10SSH,
    'dell_os10': DellOS10SSH,
    'dell_powerconnect': DellPowerConnectSSH,
    'dell_isilon': DellIsilonSSH,
    'eltex': EltexSSH,
    'enterasys': EnterasysSSH,
    'extreme': ExtremeSSH,
    'extreme_wing': ExtremeWingSSH,
    'f5_ltm': F5LtmSSH,
    'fortinet': FortinetSSH,
    'generic_termserver': TerminalServerSSH,
    'hp_comware': HPComwareSSH,
    'hp_procurve': HPProcurveSSH,
    'huawei': HuaweiSSH,
    'huawei_vrpv8': HuaweiVrpv8SSH,
    'juniper': JuniperSSH,
    'juniper_junos': JuniperSSH,
    'linux': LinuxSSH,
    'mellanox': MellanoxSSH,
    'mrv_optiswitch': MrvOptiswitchSSH,
    'netapp_cdot': NetAppcDotSSH,
    'netscaler': NetscalerSSH,
    'ovs_linux': OvsLinuxSSH,
    'paloalto_panos': PaloAltoPanosSSH,
    'pluribus': PluribusSSH,
    'quanta_mesh': QuantaMeshSSH,
    'ruckus_fastiron': RuckusFastironSSH,
    'ubiquiti_edge': UbiquitiEdgeSSH,
    'ubiquiti_edgeswitch': UbiquitiEdgeSSH,
    'vyatta_vyos': VyOSSSH,
    'vyos': VyOSSSH,
}
'''

def ssh_dev_exc(dev_type, ip, commands, port=22, 

                 username='readonly', password='readonly'):

    dev_info = {
        'device_type': dev_type,
        'ip': ip,
        'port': port,
        'username': username,
        'password': password
    }

    with ConnectHandler(**dev_info) as dev_connection:
        outputs = []
        for c in commands:
            output = dev_connection.send_command(c)
            outputs.append(output)

        return outputs


if __name__ == '__main__':
    some_dev_ssh_info = {
        'dev_type': 'cisco_nxos',
        'ip': '192.168.1.23',
        'port': 22,
        'username': 'user',
        'password': 'passowrd',
        'commands': ['show int']
    }
    outputs = ssh_dev_exc(**some_dev_ssh_info)
    print(outputs)

看到满满的支持的设备类型,笔者是在一个设备类型众多的数据中心工作,我反正是心动了,尤其是华为华三等国产厂商的支持,happy

悄悄说一句 netmiko的封装太优秀,cisco_nxos可以直接但是不完全驱动非常多的网络设备。其他很多驱动都是基于cisco的一个底层通用驱动类进行小改的。

这段代码我们往前往后都可以去拓展开,我给大家抛砖:

1、读取csv,遍历设备,执行此函数,并将命令结果保存到txt,这不就是备份脚本了吗?

2、按照上面的,解析文本内容,输出到Excel或者word文档,这不就是巡检脚本了吗?

3、读取csv,遍历设备,执行函数,进入config模式(下面会讲config),执行命令,这不就是批量下发了吗?

4、定时登录设备采集解析某指标入库,再可视化出来数据,这不就是一个小小的监控了吗?很多SNMP采集不到的信息,不就是通过命令行来采集解析的吗?

当然这都是一些比较raw的想法,实际中,还是需要处理一些特殊情况,比如认证如果是动态的需要处理,比如库表设计。向上向下都可以加很多封装抽象。

进行设备的配置

设备最开始是普通状态,无法写操作(视具体网络设备而言,大部分遵循此原则),通过命令行后才可以进行写操作。netmiko为了安全起见,也将平时的show 和config区别了开,你通过命令行输入config,大部分情况下是无效的。

from netmiko import SSHDetect, Netmiko
from getpass import getpass

device = {'device_type': 'cisco_nxos',
          'host': 'XXX',
          'port': 'XXX',
          'username': 'admin',
          'password': 'XXX!', }
connection = Netmiko(**device)
in_config_mode = connection.check_config_mode()
print('config mode is :{}'.format(in_config_mode))
# 方法1 进入config model 输入命令,确定expect的字符,可以是正则,保存
connection.config_mode()
connection.send_command('interface eth1/50',expect_string='.*#')
connection.send_command('description configed by netmiko',expect_string='.*#')
connection.save_config()

方法一

是进入config模式,调用config_mode'函数即可,函数会根据device_type,自动输入对应的命令比如config或者是system-view等。

用send_command发送命令,我们实际操作中,config后操作提示符会不断变化(比如配置一个端口的时候提示符会变成“dev_name(config-if)#“),所以写一个通配的正则匹配这种,用expect_string保证脚本确认动作执行完成。实际效果如下。(普通模式下发送命令不会修改提示符)

最后用save_config函数来保存配置,这个也是netmiko帮我们自动完成,根据设备自动输入对应的命令行保存配置。

Python的网络设备信息采集利器:Netmiko

方法二

实际上,有更简单的方法,方法2 用send_config_set函数发送命令集。无需进入config_mode。但是需要save_config.

connection.send_config_set(config_commands=['interface eth1/50','description configed by netmiko in method 2'])

Python的网络设备信息采集利器:Netmiko

方法三

从文件中读取

无需进入config_mode。但是需要save_config

# 方法3 通过文本文件进行配置
connection.send_config_from_file(config_file='config.txt')

Python的网络设备信息采集利器:Netmiko

自动判断设备的device_type

有时候我们不太清楚用哪个device_type,没关系,netmiko有一个自动检测的函数。上代码:

from netmiko import SSHDetect, Netmiko

device = {'device_type': 'autodetect',
          'host': 'XXX',
          'port': 'XXX',
          'username': 'root',
          'password': 'XXX', }
guesser = SSHDetect(**device)

best_match = guesser.autodetect()

print('best_match is:{}'.format(best_match))
print('all guessers  is:{}'.format(guesser.potential_matches))

device['device_type'] = best_match
connection = Netmiko(**device)
# 获取回显的前缀,提示符,prompt。
print('prompt is :{}'.format(connection.find_prompt())

这个在简单show的时候可以使用,常见设备准确率还可以,比如笔者去用nxos和linux检测的时候很准。但是有时候推荐的并不是可用的,我也遇到过。

Python的网络设备信息采集利器:Netmiko

find_prompt可以获取提示符,从而获取设备名,划重点,脑洞大的同学会不会有一些别的想法呢?我们从prompt中获取提示符,用正则提取出设备名,是不是可以与CMDB里的信息进行比对呢?我觉得这是一个很实在的运维场景,可以在自己内部扩展成一个简单的小工具了。

这个操作本质是用遍历内置的一些适配,执行命令看看是否符合预期,会给一个准确度的值。由于是遍历所以有时候比较耗时。

Python的网络设备信息采集利器:Netmiko

我们去看卡这个SSH_MAPPER_BASE

Python的网络设备信息采集利器:Netmiko

就是去执行命令,根据回显的文本去用正则匹配,匹配到了就返回device_type,在这里是这个字典的Key值。通过优先级返回best_match。其实我们可以重写这个netmiko的这个函数,基于这个字典丰富,按我们的所需去发现device_type。

我们初步用一个工具的时候,怎么用都可以,但是当我们用的越深入要求越多,会发现有时候需要做一些必要的二次开发。所以后期我也花了很多时间去看源代码,并结合自己的实际需求继续封装:

  • 一方面是满足自己的奇葩需求,比如有新的适配,或者自动检测不准做调整。
  • 一方面是加速开发的速度,以前写十行,封装后可能就3行了。

所以我的分享有时候会深入到源代码层面去。大家愿意看就看看,了解即可。如果一些大厂有特殊需求,我们也可以探讨一下,互通有无。比如我们基于netmiko和一些web框架,就可以实现like SDN的网络设备了,通过RESTful API来调用设备信息,作为一个微服务。传统网络设备也可以像Nexus交换机一样有RESTful API了。大家在传统网络向SDN网络过渡的过程中,肯定是敏态稳态并存,新老设备和谐相处的,那我们可以通过这个方法,去改造传统网络,使之具备一些SDN网络才有的功能,拉平两种网络,我个人觉得会比ansible更加实用(后续我们也会从国内的网络视角分享ansible的长与短,先挖坑)。

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin