您好,欢迎访问这里是深圳市硕远科技有限公司!
戴尔服务器价格_IBM联想配置_浪潮代理-深圳市硕远科技有限公司
联系我们
戴尔服务器价格_IBM联想配置_浪潮代理-深圳市硕远科技有限公司
邮箱:2324898850@qq.com
电话:400-080-6079
地址:深圳市龙华区河背工业区108创业园A301
当前位置:主页 > 新闻动态 > 企业新闻 >

企业新闻

网络工程师的Python之路——Netmiko终极指南

发布时间:2022-09-15 17:03:08浏览次数:

作为Paramiko最为成功的衍生模块,Netmiko成为了很多学习Python网络运维自动化技术的网工日常工作中最常用的模块之一。相较于Paramiko,Netmiko将很多细节优化和简化,比如不需要导入time模块做休眠,输入每条命令不需要在后面加换行符\n,不需要执行config term,exit,end等命令,提取、打印回显内容更方便,可以配合Jinja2模块调用配置模板,以及配合TextFSM、pyATS、Genie等模块将回显内容以有序的JSON格式输出,方便我们过滤和提取出所需的数据等等,并且在Netmiko的基础上也诞生出了napalm, pyntc,netdev等扩展模块甚至Nornir这样成功的网络运维自动化框架。

在我的《网络工程师的Python之路》一书里,我刻意减少了Netmiko的相关内容,重点讲解了Paramiko,因为道理很简单:Netmiko将太多功能简化的做法其实并不利于初学者学习。Netmiko和Paramiko两者就像自动挡汽车和手动挡汽车的区别。会驾车的都知道,一开始就学手动挡车的人100%会开自动挡的车,而从一开始就学自动挡车的人,除非额外加课,否则是100%不会开手动挡的车的。Paramiko虽然复杂、繁琐一些,但是就像手动挡车一样,整体“操控感”更强,运维脚本中的所有细节和各种参数都在我们自己的掌控之中,更利于我们从整体来把握进而写出自己需要的脚本,并且无需像Netmiko那样担心对各种设备各种OS的支持的问题。

随着越来越多的网工读者们逐渐上手和适应Paramiko,为了弥补我书里“重Paramiko,轻Netmiko”的遗憾,特此补上一篇《Netmiko终极指南》,本文和之前我连载的Nornir3.0.0的教程类似,将Netmiko的各个知识点直接以实验形式演示、讲解,总计8个实验,由简到难涵盖Netmiko的各个知识点。

实验平台:

本文承接《网络工程师的Python之路》过往的拓扑,还是老规矩:一台CentOS 8主机(192.168.2.1)上跑Netmiko,下面连上5台试验用的虚拟三层思科交换机IP从192.168.2.11到192.168.2.15。

实验准备:

通过pip安装netmiko后在Python解释器里用import验证,截至2021年4月27号,目前最新的Netmiko版本为3.4.0。

实验1: 通过Netmiko登录一台交换机

要通过netmiko来登录一台设备需要用到它的核心对象ConnectHandler。ConnetHandler()包含几个必要的参数和可选参数,必要参数包括device_type,ip(也可以为host),username和password,可选参数包括port, secret,use_keys,key_file,conn_timeout等等,可选参数的作用和用法在后面的实验中会陆续讲到。

首先创建第一个脚本netmiko1.py,然后写入下面代码:

from netmiko import ConnectHandlersw1 = {'device_type': 'cisco_ios','ip': '192.168.2.11','username': 'python','password': '123'}with ConnectHandler(**sw1) as connect:print ("已经成功登陆交换机" + sw1['ip'])

代码分段讲解:

  • 首先通过 import 语句从Netmiko导入它的核心对象ConnectHandler()。
from netmiko import ConnectHandler
  • 创建一个名为SW1的字典,该字典包含'device_type'、'ip'、'username'和'password'4个必选的键。SW1字典里的四组键值对等会会以关键字参数的形式(**kwargs)在ConnectHandler()里打开,被用作前面讲到的ConnectHandler()的4个必选参数(device_type,ip,username和password)。其中ip,username,password3个键的意思很好理解(在实际工作中,username和password建议通过input()和getpass模块来输入,这里因为只是实验演示使用,我就偷懒直接把username和password明文写进脚本里了),这里主要说下'device_type'。截至2021年4月,Netmiko 支持 Arista、Cisco、HP、Juniper、Alcatel、Huawei、Extreme和Palo Alto等绝大多数主流厂商的设备。除此之外,Netmiko同样支持拥有多种不同 OS 类型的厂商的设备,比如针对思科的设备,Netmiko能同时支持 Cisco ASA、Cisco IOS、Cisco IOS-XE、Cisco IOS-XR、Cisco NX-OS 和 Cisco SG300共 6 种不同 OS 类型的设备。由于不同厂商的设备登录 SSH 后命令行界面和特性不尽相同,因此我们必须通过'device_type'来指定需要登录的设备的类型。因为实验里我们用到的是思科 IOS设备,因此'deivce_type'的键值为'cisco_ios'。
sw1 = {'device_type': 'cisco_ios','ip': '192.168.2.11','username': 'python','password': '123'}
  • 最后调用ConnetHandler(),以关键字参数的形式将sw1这个字典里的键值对提取出来,作为自己的ConnetHandler()本身的参数使用,最后打印出“已经成功登录交换机xx.xx.xx.xx"来提示我们SSH登录成功,这里注意我用了context manager(上下文管理器,也就是with语句)来调用ConnetHandler,它的好处是可以在脚本运行完毕后自动帮助我们关闭SSH会话(如果你不想使用context manager,而是写成connect = ConnectHandler(**sw1)当然也是可以的,不影响Netmiko登录设备):
with ConnectHandler(**sw1) as connect:print ("已经成功登陆交换机" + sw1['ip'])
  • 这里额外提一点:如果脚本只需要单独登录一台设备的话,这里也可以不创建sw1这个字典,而是直接在ConnetHandler()里面放入'device_type'、'ip'、'username'和'password'四个参数,如下:
from netmiko import ConnectHandlerwith ConnectHandler(device_type='cisco_ios', ip='192.168.2.11', username='python', password='123') as connect:        print ("已经成功登陆交换机192.168.2.11")

运行脚本看效果:

运行脚本前先在S1上开启debug ip ssh,以便我们验证脚本是否真正SSH登录了交换机。

运行脚本:

然后回到S1上,如果看到如下debug日志,则说明netmiko登录交换机成功:

实验2:通过Netmiko向设备做配置

Netmiko主要有四种函数向设备做配置:send_command(),send_config_set()以及send_config_from_file(),除此之外还有一个不太常用的send_command_timing(),它们的用法和区别如下:

send_command():只支持向设备发送一条命令,通常是show/display之类的查询、排错命令或者wr mem这样保存配置的命令。发出命令后,默认情况下这个函数会一直等待,直到接收到设备的完整回显内容为止(以收到设备提示符为准,比如说要一直等到读取到“S1#"为止),如果想要指定netmiko从回显内容中读到我们需要的内容,则需要用到expect_string参数(expect_string默认值为None),如果send_command()从回显内容中读到了expect_string参数指定的内容,则send_command()依然返回完整的回显内容,如果没读到expect_string参数指定的内容,则netmiko会返回一个OSError: Search pattern never detected in send_command: xxxxx的异常,关于expect_string参数的用法会在稍后的实验里演示。

send_config_set(): 向设备发送一条或多条配置命令,注意是配置命令,不是show/display之类的查询命令,因为send_config_set()本身会自动替我们加上一个config terminal命令进入配置模式(以及在命令末尾自动替我们加上一个end命令),在config terminal下除非在show命令前面加上一个do,比如do show ip int brief,否则show命令无效(以上以思科IOS设备为例)。send_config_set()一般配合列表使用。

send_config_from_file(): 在配置命令数量较多的时候,将所有配置命令写入列表显然是比较笨拙的,因为会造成代码过长,不方便阅读,并且在部分厂商的设备上(比如华为)还会出现超时报错的情况。我们可以先将所有的配置命令写入一个配置文件中,然后使用send_config_from_file()去读取该文件的内容帮助我们完成配置。和send_config_set()一样,send_config_from_file()也会自动帮我们添加config terminal和end两个命令,所以在我们的配置文件里无需加入这两个命令。

send_command_timing(): 和send_command()一样,只支持向设备发送一条show/display命令。区别是在用send_command()输入一条show命令后,send_command()会一直等待,直到接收到设备的完整回显内容为止(以收到设备提示符为准)。send_command_timing()则会自己去“猜”什么时候停止运行,它的原理是如果没有从设备收到更多新的回显内容后,它会继续等待2秒钟,然后才停止运行,send_command_timing()里有一个叫做delay_factor的参数,默认为1,如果将它修改为2,则send_command_timing()会等待4秒钟,修改为3,则等待6秒,修改为4,等待8秒。。以此类推。有时候在输入show tech-support或者show log(logging buffer特别大,日志特别长的那种)这种回显内容巨多的命令时,即使输入了term len 0来取消分屏显示,设备在返回回显内容时依然会有停顿,有时停顿时间会大于2秒钟,这样就会导致截屏不完整,必须手动去调整delay_factor这个参数。

接下来做实验,首先创建一个config.txt文件,然后写入下列配置命令,这里我们将gi0/0端口的description改为Netmiko2.py

int gi0/0description Netmiko2.py

然后创建第二个实验脚本netmiko.py2(确保实验脚本和config.txt配置命令位于同一个文件夹下),在脚本中放入下列代码:

from netmiko import ConnectHandlersw1 = {        'device_type': 'cisco_ios',        'ip': '192.168.2.11',        'username': 'python',        'password': '123'}commands = ['interface gi0/1', 'description Nornir2.py']with ConnectHandler(**sw1) as connect:        print ("已经成功登陆交换机" + sw1['ip'])        output = connect.send_command('show interface description')        print(output)        output = connect.send_config_set(commands)        print(output)        output = connect.send_config_from_file('config.txt')        print(output)        output = connect.send_command('show interface description')        print(output)        output = connect.send_command('wr mem')        print(output)

代码分段讲解:

  • 首先通过 import 语句从Netmiko导入它的核心对象ConnectHandler(),创建字典sw1用作ConnectHandler()登陆交换机的参数:
from netmiko import ConnectHandlersw1 = {        'device_type': 'cisco_ios',        'ip': '192.168.2.11',        'username': 'python',        'password': '123'}
  • 然后创建一个列表commands,放入要对gi0/1端口修改description的配置,该commands列表等会儿会被send_config_set()函数调用:
commands = ['interface gi0/1', 'description Nornir2.py']
  • 通过ConnectHandler()连入交换机S1后,首先使用send_command('show interface description')查询配置前Gi0/0和Gi0/1两个端口当前的description,然后通过send_config_set(commands)调用commands这个列表对gi0/1做配置,然后再使用send_config_from_file('config.txt')读取之前我们创建的配置文件里的命令对gi0/0做配置,最后再用send_command('show interface description')查询配置完毕后两个端口的description,并send_command('wr mem')保存配置:
with ConnectHandler(**sw1) as connect:        print ("已经成功登陆交换机" + sw1['ip'])        output = connect.send_command('show interface description')        print(output)        output = connect.send_config_set(commands)        print(output)        output = connect.send_config_from_file('config.txt')        print(output)        output = connect.send_command('show interface description')        print(output)        output = connect.send_command('wr mem')        print(output)

运行脚本看效果:

实验3:用Netmiko配合Textfsm或者Genie将回显内容以JSON格式输出,以pprint模块打印出来

在我的书中曾重点介绍过什么是Textfsm以及它的用法。除了Textfsm之外,思科也出过类似的工具pyATS和Genie来帮助我们将网络设备无序的回显内容以有序的JSON格式输出(不过只限于思科设备)。在Netmiko的send_command()和send_command_timing()中已经内置了可以直接调用Textfsm和Genie的参数,非常方便。实验3就来分别介绍一下它们的用法:

首先通过pip install来下载TextFSM,pyATS和Genie,这里要注意的是pyATS和Genie只支持Linux不支持Windows(Windows下运行pip install pyats genie会报错),如果你是Windows用户的话需要使用WLS2或者通过虚拟机运行Linux再来安装pyATS和Genie。另外并且强烈建议先通过pip install --upgrade pip将pip升级到最新版本,否则下载的pyATS和Genie会碰到各种奇怪的问题。

pip install --upgrade pippip install textfsmpip install pyatspip install genie

我们以show interfaces命令为例,首先创建一个test.py来看下不使用textfsm或genie,输入该命令后的回显内容:

test.py代码如下:

from netmiko import ConnectHandlerimport pprintconnection_info = {        'device_type': 'cisco_ios',        'host': '192.168.2.11',        'username': 'python',        'password': '123'}with ConnectHandler(**connection_info) as conn: out = conn.send_command("show interfaces")    print(out)

运行脚本后的效果:

接下来创建脚本netmiko3_1.py,写入下面代码:

from netmiko import ConnectHandlerconnection_info = {        'device_type': 'cisco_ios',        'host': '192.168.2.11',        'username': 'python',        'password': '123'}with ConnectHandler(**connection_info) as conn:    out = conn.send_command("show interfaces", use_textfsm=True)    print(out)

可以看到和Nornir一样,要在Netmiko里调用textfsm很简单,只需要在send_command()或者send_command_timing()里直接添加一个参数use_textfsm=True即可(默认为False)。接下来运行脚本看效果:

这里可以看到虽然textfsm输出的内容为JSON格式,但是依然不具备可读性。为了将内容更美观地打印出来,这里我们可以用到Python另外一个很强大的内置库:pprint,pprint全称是pretty printer,它的功能是将各种数据结构更美观地输出。这里我们将netmiko3_1.py的代码稍作修改,在第二行加上from pprint import pprint,在最后一行将print(out)改为pprint(out)即可,如下:

from netmiko import ConnectHandlerfrom pprint import pprintconnection_info = {        'device_type': 'cisco_ios',        'host': '192.168.2.11',        'username': 'python',        'password': '123'}with ConnectHandler(**connection_info) as conn:    out = conn.send_command("show interfaces", use_textfsm=True)    pprint(out)

运行代码看效果,输出内容是不是更美观,可读性更强了?

和textfsm一样,在Netmiko中使用genie也很简单,只需要在send_command()或者send_command_timing()里直接添加一个参数use_genie=True即可(默认为False),当然前提是你之前有通过pip安装了pyATS和Genie(虽然参数只是use_genie,但是pyATS也必须下载安装,缺一不可),接下来我们再创建netmiko3_2.py,看下用genie配合pprint将数据内容输出后是什么效果,代码如下:

from netmiko import ConnectHandlerfrom pprint import pprintconnection_info = {        'device_type': 'cisco_ios',        'host': '192.168.2.11',        'username': 'python',        'password': '123'}with ConnectHandler(**connection_info) as conn:    out = conn.send_command("show interfaces", use_genie=True)    pprint(out)

运行代码看效果,可以看到Netmiko使用genie配合pprint后输出的内容比Textfsm+pprint更美观,层次更分明:

那么我们可以参照paramiko的方法,创建一个ip_list.txt文件,将所有设备的管理IP地址都写进去,如下,这里我将SW1-SW5总共5个交换机的IP地址都写入了ip_list.txt文件里:

from netmiko import ConnectHandlerimport pprintconnection_info = {        'device_type': 'cisco_ios',        'host': '192.168.2.11',        'username': 'python',        'password': '123'}with ConnectHandler(**connection_info) as conn:    out = conn.send_command("show interfaces", use_genie=True)    for name, details in out.items():        print(f"{name}")        print(f"- Status: {details.get('enabled', None)}")        print(f"- Physical address: {details.get('phys_address', None)}")        print(f"- Duplex mode: {details.get('duplex_mode', None)}")    for counter, count in details.get('counters', {}).items():            if isinstance(count, int):                if count > 0:                    print(f"- {counter}: {count}")            elif isinstance(count, dict):                for sub_counter, sub_count in count.items():                    if sub_count > 0:                        print(f"- {counter}::{sub_counter}: {sub_count}")

脚本运行后的效果如下:

实验4:通过Netmiko连接多台交换机

前面三个实验我们都是通过Netmiko登录一台设备,实验4里我们来看下怎么通过Netmiko登录多台设备,根据情况不同主要有两种方法,分两个脚本讲解。

第一种情况:生产网络里所有设备的username, password, port这些参数都一样

那么我们可以参照paramiko的方法,创建一个ip_list.txt文件,将所有设备的管理IP地址都写进去,如下,这里我将SW1-SW5总共5个交换机的IP地址都写入了ip_list.txt文件里:

然后创建脚本netmiko4_1.py,写入下列代码:

from netmiko import ConnectHandlerwith open('ip_list.txt') as f:    for ips in f.readlines():        ip = ips.strip()        connection_info = {                'device_type': 'cisco_ios',                'ip': ip,                'username': 'python',                'password': '123',        }        with ConnectHandler(**connection_info) as conn:            print (f'已经成功登陆交换机{ip}')            output = conn.send_command('show run | i hostname')            print(output)

代码分段讲解:

  • 这里我们用context manager (with语句)打开保存5台交换机管理IP的ip_list.txt文件,然后用for循环配合readlines()遍历里面的每一个ip,因为readlines()返回的列表里的每个元素后面都会接一个换行符\n,所以我们用strip()函数将其拿掉然后赋值给变量ip,这个变量ip则作为字典connection_info里'ip'这个键的值放入字典,这里因为我所有的交换机用的SSH端口号都为默认的22,所以没有必要在字典里明文给出来。
from netmiko import ConnectHandlerwith open('ip_list.txt') as f:    for ips in f.readlines():        ip = ips.strip()        connection_info = {                'device_type': 'cisco_ios',                'ip': ip,                'username': 'python',                'password': '123',        }
  • 然后通过ConnetHandler(**connection_info)依次登陆每台交换机,这里除了打印“已经成功登陆交换机xxx.xxx.xxx.xxx“之外我还额外向每台交换机发送了一个show run |ihostname并将回显内容(即交换机各自的hostname)打印出来,目的是验证我们确实通过Netmiko登录了每台交换机。
        with ConnectHandler(**connection_info) as conn:            print (f'已经成功登陆交换机{ip}')            output = conn.send_command('show run | i hostname')            print(output)

运行脚本看效果:

第二种情况:生产网络里设备的username, password, port这些参数不尽相同

这种情况下就不能再用ip_list.txt的方法来做了,必须给每一个参数不同的交换机创建一个字典,因为每个字典一般都要占据5,6排代码的空间,设备一多的时候脚本代码量会非常恐怖。我们可以将这些字典写入一个额外的文件类型为json的文件,将其取名为switches.json,在Python脚本里可以导入Python内置的json模块,利用json.load()这个函数来加载switches.json文件里的内容,因为json.load()会返回一个列表类型的JSON数据,我们可以使用for循环来遍历该列表里的JSON数据来达到依次登录多台设备的目的。具体方法如下:

首先创建switches.json文件,将包含交换机S1和S2的数据以字典形式写入:

[  {    "name": "SW1",    "connection": {       "device_type": "cisco_ios",          "host": "192.168.2.11",          "username": "python",          "password": "123"    }  },  {    "name": "SW2",    "connection": {       "device_type": "cisco_ios",          "host": "192.168.2.12",          "username": "python",          "password": "123"    }  }]

然后创建实验4的第二个脚本netmiko4_2.py

import jsonfrom netmiko import ConnectHandlerwith open("switches.json") as f:    devices = json.load(f)for device in devices:    with ConnectHandler(**device['connection']) as conn:        hostname = device['name']        print (f'已经成功登陆交换机{hostname}')        output = conn.send_command('show run | i hostname')        print(output)

代码分段讲解:

  • 导入Python内置的json模块,然后调用它的load()函数将我们之前创建的switches.json里面的SW1和SW2的登录信息导出并赋值给变量devices,注意json.load()返回值的数据类型为为列表。
import jsonfrom netmiko import ConnectHandlerwith open("switches.json") as f:    devices = json.load(f)
  • 随后用for循环遍历devices,并对每台交换机执行命令show run | i hostname打印出它们的hostname:
for device in devices:    with ConnectHandler(**device['connection']) as conn:        hostname = device['name']        print (f'已经成功登陆交换机{hostname}')        output = conn.send_command('show run | i hostname')        print(output)

运行脚本看效果:

实验5:Netmiko配合Jinja2配置模板给设备做配置

Jinja2是Python的内置模块,无需通过pip下载安装。Jinja2在我的专栏里已经多次提到过,这里就不介绍它的基本语法和用法了,这里直接给出它在Netmiko中的用法。

这里我们将使用netmiko配合jinja2给SW1的loopback 1端口配置一个inbound方向的ACL 1,该ACL 1会分别允许和拒绝一些IP地址。首先创建一个文件夹,取名templates,进入该文件夹后创建我们的jinja2模板文件,将其取名为acl.conf.tpl,然后放入下面的jinja2语句:

interface {{ interface }}ip access-group 1 in{% for host in disallow_ip %}access-list 1 deny host {{ host }}{% endfor %}{% for host in allow_ip %}access-list 1 permit host {{ host }}{% endfor %}
from netmiko import ConnectHandlerfrom jinja2 import Environment, FileSystemLoadersw1 = {        'device_type': 'cisco_ios',        'ip': '192.168.2.11',        'username': 'python',        'password': '123'}loader = FileSystemLoader('templates')environment = Environment(loader=loader)tpl = environment.get_template('acl.conf.tpl')allow_ip = ['10.1.1.1', '10.1.1.2']disallow_ip = ['10.1.1.3', '10.1.1.4']out = tpl.render(allow_ip=allow_ip, disallow_ip=disallow_ip, interface='loopback 1')with open("configuration.conf", "w") as f:       f.write(out)with ConnectHandler(**sw1) as conn:        print ("已经成功登陆交换机" + sw1['ip'])        output = conn.send_config_from_file("configuration.conf")        print (output)
  • 这里我们导入Jinja2的Enviroment和FileSystemLoader两个子类,用来读取templates文件夹下的Jinja2模板文件acl.conf.tpl,然后赋值给tpl这个函数:
from netmiko import ConnectHandlerfrom jinja2 import Environment, FileSystemLoadersw1 = {        'device_type': 'cisco_ios',        'ip': '192.168.2.11',        'username': 'python',        'password': '123'}loader = FileSystemLoader('templates')environment = Environment(loader=loader)tpl = environment.get_template('acl.conf.tpl')
  • 然后我们将允许和拒绝的IP写入两个列表,分别取名allow_ip和disallow_ip,然后通过render()这个函数对模板文件做渲染(注意这里render()里的三个参数allow_ip,disallow_ip和interface就是模板文件里的变量)将渲染后的模板文件赋值给变量out:
allow_ip = ['10.1.1.1', '10.1.1.2']disallow_ip = ['10.1.1.3', '10.1.1.4']out = tpl.render(allow_ip=allow_ip, disallow_ip=disallow_ip, interface='loopback 1')
  • 有了渲染后的模板文件后,我们需要将它写入一个扩展名为conf的文件,这里我们创建一个configuration.conf文件,将模板文件内容全部写入进去:
with open("configuration.conf", "w") as f:       f.write(out)
  • 最后通过ConnectHandler()登录SW1,然后使用send_config_from_file()读取configuration.conf文件里的内容对SW1做配置:
with ConnectHandler(**sw1) as conn:        out = conn.send_config_from_file("configuration.conf")

运行脚本看效果:

运行脚本前首先确认SW1里没有ACL 1,并且loopback 1下也没有放入任何ACL:

脚本运行完毕后再次返回SW1验证:

实验6:在Netmiko中使用enable密码进入设备特权模式

在我之前的所有专栏文章以及书里我都是将SSH用户名的特权级别设为15,跳过了输入enable密码这一步。鉴于部分读者的生产网络中仍然要求使用enable密码来进入设备的特权模式,这里就来看下如何在Netmiko中使用enable密码。

开始实验之前,首先将SW1上的用户python的特权级别从之前的15改为1:

然后使用用户名python手动SSH登录SW1,可以发现输入SSH密码后,来到了用户模式(>提示符),必须再次输入enable密码才能进入特权模式:

验证完毕后,开始创建实验6的脚本netmiko6.py,写入下列代码:

from netmiko import ConnectHandlersw1 = {        'device_type': 'cisco_ios',        'ip': '192.168.2.11',        'username': 'python',        'password': '123',        'secret': '123'}with ConnectHandler(**sw1) as connect:    connect.enable()    print ("已经成功登陆交换机" + sw1['ip'])    output = connect.send_command('show run')    print (output)

代码分段讲解:

  • 要在Netmiko中使用enable密码,需要在设备登录信息的字典里加入secret这个键,该键对应的值就是设备的enable密码,这里我用的enable密码是123:
from netmiko import ConnectHandlersw1 = {        'device_type': 'cisco_ios',        'ip': '192.168.2.11',        'username': 'python',        'password': '123',        'secret': '123'}
  • 之后在使用ConnectHandler()登录交换机时这里要多一个步骤:调用enable()这个方法,这样Netmiko才能配合之前的secret键,将它的值作为enable密码输入后登陆设备的特权模式。注意这里我特意用send_command()输入了show run这个只能在特权模式下使用,用户模式下不支持的命来验证Netmiko确实帮我们输入了enable密码,进入了特权模式:
with ConnectHandler(**sw1) as connect:    connect.enable()    print ("已经成功登陆交换机" + sw1['ip'])    output = connect.send_command('show run')    print (output)

运行脚本看效果:

实验7: 使用Netmiko向设备传送文件。

开始实验7之前不要忘记先把用户python的特权级别从1改回15。

平常的网络运维工作中少不了要向设备传送文件,比如新的OS的镜像文件。实验7将演示如何使用Netmiko配合scp协议来向设备(SW1)传送文件。

首先在SW1上开启scp:

然后创建一个名为test.txt的文本文件,在其中写入test这个单词。

然后创建脚本netmiko7.py,写入下列代码:

from netmiko import ConnectHandler, file_transfersw1 = {        'device_type': 'cisco_ios',        'ip': '192.168.2.11',        'username': 'python',        'password': '123'}with ConnectHandler(**sw1) as connect:     print ("已经成功登陆交换机" + sw1['ip'])     output = file_transfer(connect,                source_file="test.txt",                dest_file="test.txt",                file_system="flash:",                direction="put")     print (output)

代码分段讲解:

  • 这里我们除ConnectHandler之外,额外从netmiko引入了file_transfer这个子类用来向设备传送文件:
from netmiko import ConnectHandler, file_transfersw1 = {        'device_type': 'cisco_ios',        'ip': '192.168.2.11',        'username': 'python',        'password': '123'}
  • 通过ConnectHandler()登录SW1后, 调用file_transfer(),里面的各个参数应该不难理解,这里提一下因为我用的SW1是思科的IOS设备,所以file_system参数后面放的是flash:,如果是思科NX-OS的话,需要放bootflash:,如果是Arista设备的话,需要放/mnt/flash,如果是Junos设备的话,则要放/var/tmp。大家可以根据自己的情况来做改变。最后的direction这个参数,根据Netmiko官网API资料只能放“put”这一个值:
with ConnectHandler(**sw1) as connect:     print ("已经成功登陆交换机" + sw1['ip'])     output = file_transfer(connect,                source_file="test.txt",                dest_file="test.txt",                file_system="flash:",                direction="put")     print (output)

运行脚本看效果:

首先查看SW1的文件系统flash0,确认里面没有test.txt文件

运行脚本:

注意这里print(output)返回的内容为一个字典,如果文件传输失败的话,该字典的file_transferred这个键的值将为False。

最后回到SW1上验证,可以看到test.txt文件已经成功上传到SW1的flash0::

实验8:用Netmiko处理设备提示命令。

在网络设备中输入某些命令后系统会返回一个提示命令,询问你是继续执行命令还是撤销命令,比如我们在实验7中向SW1传入了test.txt这个文件,如果这时我们想将它删除,则需要输入命令del flash0:/test.txt,输入该命令后,系统会询问你是否confirm,如下:

这时如果你输入字母y或者敲下回车键则系统会执行del命令将该文件删除,如果你输入字母n,则系统会中断该命令,该文件不会被删除。

在Netmiko中我们可以调用send_command()函数中的command_string,expect_string,strip_prompt,strip_command四个参数来应对这种情况。

首先创建实验8的脚本netmiko8.py,写入下列代码:

from netmiko import ConnectHandlersw1 = {        'device_type': 'cisco_ios',        'ip': '192.168.2.11',        'username': 'python',        'password': '123'}with ConnectHandler(**sw1) as connect:    print ("已经成功登陆交换机" + sw1['ip'])    output = connect.send_command(command_string="del flash0:/test.txt",                  expect_string=r"Delete flash0:/test.txt?",                  strip_prompt=False,                  strip_command=False)    output += connect.send_command(command_string="y",                  expect_string=r"#",                  strip_prompt=False,                  strip_command=False)print(output)
    output = connect.send_command(command_string="del flash0:/test.txt",

代码分段讲解:

  • 前面这段代码没什么可讲的,正常创建字典用以登录交换机SW1
from netmiko import ConnectHandler sw1 ={      'device_type':'cisco_ios',      'ip':'192.168.2.11',      'username':'python',      'password':'123'      }
from netmiko import ConnectHandler 
  • 通过ConnectHandler()登录SW1后,我们前后两次调用send_command()函数,我们在第一个send_command()函数中通过command_string这个参数向SW1输入命令del flash0:/test.txt,然后在expect_string这个参数里告知Netmiko去SW1的回显内容里查找“Delete flash0:/test.txt?”这段系统返回的提示命令,如果查到了的话,则继续输入命令y(第二个send_command())让脚本删除test.txt这个文件,之后如果接收到命令提示符"#",则继续执行脚本后面的代码。strip_prompt和strip_command两个参数这里放Fasle就行,目的是让代码最后的print(output)输出的内容的格式更好看一点:
with ConnectHandler(**sw1) as connect:    print ("已经成功登陆交换机" + sw1['ip'])    output = connect.send_command(command_string="del flash0:/test.txt",                  expect_string=r"Delete flash0:/test.txt?",                  strip_prompt=False,                  strip_command=False)    output += connect.send_command(command_string="y",                  expect_string=r"#",                  strip_prompt=False,                  strip_command=False)print(output)
    output = connect.send_command(command_string="del flash0:/test.txt",

运行脚本看效果:

首先确认目前 SW1的flash0下还有test.txt文件:

运行脚本netmiko8.py:

可以看到Netmiko在遇到提示命令“Delete flash0:/test.txt?”帮我们自动输入了命令y,删除了test.txt文件。

回到SW1上验证,test.txt已被删除:

前面说了,“strip_prompt和strip_command两个参数这里放Fasle就行,目的是让代码最后的print(output)输出的内容的格式更好看一点”, 如果把它们改成True会怎么样呢?来看效果:

显然不如放False的时候美观,并且看不到Netmiko帮我们输入的y这个命令。

个命令。

代码最后的print(output)输出的内容的格式更好看一点”, 如果把它们改成True会怎么样呢?来看效果:

显然不如放False的时候美观,并且看不到Netmiko帮我们输入的y这个命令。

个命令。

果:

显然不如放False的时候美观,并且看不到Netmiko帮我们输入的y这个命令。

个命令。

400-080-6079