关于python:NAPALM介绍与实操

[toc]

NAPALM概述

官网链接

其余drive链接(国人)

NAPALM全称为Network Automation and Programmability Abstraction Layer with Multivendor support,翻译起来就是反对”多厂商”网络自动化和可编程的形象层.

NAPALM是一个python开源的第三方模块,截至2021.11月,反对的厂商如下所示:

尽管反对厂商不是很多,临时没有反对国内厂商,但性能都是很弱小的。

Cisco IOS
Cisco NX-OS
Cisco IOS-XR
Juniper JUNOS
Arista EOS

NX-API support on the Nexus 5k, 6k and 7k families was introduced in version 7.2

  • 依赖netmiko,本机装置即可.

反对的设施

通用反对模型

_ EOS Junos IOS-XR (NETCONF) IOS-XR (XML-Agent) NX-OS NX-OS SSH IOS
Driver Name eos junos iosxr_netconf iosxr nxos nxos_ssh ios
Structured data Yes Yes Yes No Yes No No
Minimum version 4.15.0F 12.1 7.0 5.1.0 6.1 [1] 12.4(20)T 6.3.2
Backend library pyeapi junos-eznc ncclient pyIOSXR pynxos netmiko netmiko
Caveats EOS IOS-XR (NETCONF) NXOS NXOS IOS

阐明:

  • driver name:前面咱们写代码须要填写的,如get_network_driver(ios)
  • structured data:反对的结构化数据
  • Minimum version:这里还有最低的版本要求;

配置反对模型

反对配置替换、合并、提交、比照、原子配置、回滚等。

_ EOS Junos IOS-XR (NETCONF) IOS-XR (XML-Agent) NX-OS IOS
Config. replace Yes Yes Yes Yes Yes Yes
Config. merge Yes Yes Yes Yes Yes Yes
Commit Confirm Yes Yes No No No No
Compare config Yes Yes Yes Yes [2] Yes [4] Yes
Atomic Changes Yes Yes Yes Yes Yes/No [5] Yes/No [5]
Rollback Yes [3] Yes Yes Yes Yes/No [5] Yes

[2] Hand-crafted by the API as the device doesn’t support the feature.
[3] Not supported but emulated. Check caveats.
[4] For merges, the diff is very simplistic. See caveats.
[5] (1, 2, 3) No for merges. See caveats.
[6] NAPALM requires Junos OS >= 14.1 for commit-confirm functionality.

Getters反对的模型

该表官网会不定期自动更新。

EOS IOS IOSXR IOSXR_NETCONF JUNOS NXOS NXOS_SSH
get_arp_table
get_bgp_config
get_bgp_neighbors
get_bgp_neighbors_detail
get_config
get_environment
get_facts
get_firewall_policies
get_interfaces
get_interfaces_counters
get_interfaces_ip
get_ipv6_neighbors_table
get_lldp_neighbors
get_lldp_neighbors_detail
get_mac_address_table
get_network_instances
get_ntp_peers
get_ntp_servers
get_ntp_stats
get_optics
get_probes_config
get_probes_results
get_route_to
get_snmp_information
get_users
get_vlans
is_alive
ping
traceroute
  • ✅ – supported
  • ❌ – not supported
  • ☠ – broken

通过Getters类,比方get_config就能够失去设施的配置(running和startup)、get_facts获取设施的根本信息、get_interfaces获取接口信息等等,也是十分不便的。

其余办法

_ EOS Junos IOS-XR (NETCONF) IOS-XR NX-OS IOS
load_template
ping
traceroute

可用的配置模板

  • set_hostname (JunOS, IOS-XR, IOS) – Configures the hostname of the device.
  • set_ntp_peers (JunOS, IOS-XR, EOS, NXOS, IOS) – Configures NTP peers of the device.
  • delete_ntp_peers (JunOS, IOS-XR, EOS, NXOS, IOS): Removes NTP peers from device’s configuration.
  • set_probes (JunOS, IOS-XR): Configures RPM/SLA probes.
  • schedule_probes (IOS-XR): On Cisco devices, after defining the SLA probes, it is mandatory to schedule them. Defined also for JunOS as empty template, for consistency reasons.
  • delete_probes (JunOS, IOS-XR): Removes RPM/SLA probes.

以上办法,大家能够在试验环境测试下。

如何装置

须要确保netmiko曾经被装置,依赖与netmiko的。

  • 通过pip3装置
python -m pip install napalm

备注:从版本3.0.0之后,NAPALM仅反对Python 3.6+.

如何降级

pip install napalm -U

备注:如果napalm有降级了,能够通过该办法进行降级.

实操示例

视频中演示的代码,都在这里了.

根本的设施连贯

from napalm import get_network_driver
import pprint

pp = pprint.PrettyPrinter(indent=2)

# drive是一个类class,传入drive_name
drive = get_network_driver('ios')
# 类实例化
conn = drive(hostname='192.168.0.20',
             username='cisco',
             password='cisco',
             optional_args={'port': 22})
# 建设连贯会话
conn.open()
# 应用其中一个办法,获取接口ip地址
ouput = conn.get_interfaces_ip()
# 打印后果
pp.pprint(ouput)

# 敞开连贯
conn.close()

执行脚本后,后果如下所示:

备份配置

from napalm import get_network_driver

import pprint
import os

pp = pprint.PrettyPrinter(indent=2)

host = "192.168.0.20"

# drive是一个类class
drive = get_network_driver('ios')
# 类实例化
conn = drive(hostname= host,
             username= 'cisco',
             password= 'cisco',
             optional_args= {'port': 22})
# 建设连贯会话
conn.open()
# 获取设施配置信息
output = conn.get_config()
# 打印后果
pp.pprint(output)
running_config = output['running']
# 写入文件
with open(os.path.join('LOG', f'{host}-running.conf'), 'w+') as f:
    f.writelines(running_config)

# 敞开连贯
conn.close()

执行完脚本,后果如下所示:

默认蕴含了3个备份配置:

  • running配置:就是以后运行配置.
  • startup配置:就是启动配置了.
  • candidate配置,这个个别为空.

这里,我保留了running.config配置文件.

获取设施根底信息

# drive是一个类class
drive = get_network_driver('ios')
# 类实例化
conn = drive(hostname='192.168.0.20',
             username='cisco',
             password='cisco',
             optional_args={'port': 22})
# 建设连贯会话
conn.open()
# 获取接口ip地址
output = conn.get_facts()
# 打印后果
pp.pprint(ouput)
# 敞开连贯
conn.close()

执行脚本后,后果如下所示:

get_facts()办法采集了如下字段:

  • 域名、主机名称、接口列表、型号、版本号、序列号、运行工夫、厂商.

发送命令

# drive是一个类class
drive = get_network_driver('ios')
# 类实例化
conn = drive(hostname='192.168.0.20',
             username='cisco',
             password='cisco',
             optional_args={'port': 22, 'secret': 'cisco'})
# 建设连贯会话
conn.open()
# 发送命令
output = conn.cli(['show ip int brief', 'show arp'])
pp.pprint(output)

# 敞开连贯
conn.close()

执行脚本后,后果如下所示:

阐明:这里要注意下如果用户权限不够或者须要进入enable的,须要optional_args减少secret参数,表明须要进入特权模式了.

如果执行的命令权限不容许,就会报如下谬误:

ValueError: Failed to enter enable mode. Please ensure you pass the 'secret' argument to ConnectHandler.

配置类

通过scp协定下发配置,它不须要enable明码.

前提条件:

  • 设施要开启scp服务, ip scp server enable .
  • 开启archive服务,备份配置到本地flash上;

    archive
     path flash:R1.conf
     write-memory

合并配置

阐明:

  • load_merge_candidate():加载配置文件.
  • commit_config():提交配置.
  • revert_in=30:如果平台反对,能够设置工夫挂起配置提交,超时没commit就复原配置。

    根底玩法

    模板文件门路:templates\ios_logging_config.cfg

    logging host 10.1.1.2
from napalm import get_network_driver
import pprint

pp = pprint.PrettyPrinter(indent=2)

# drive是一个类class
drive = get_network_driver('ios')
# 类实例化
conn = drive(hostname='192.168.0.20',
             username='cisco',
             password='cisco',
             optional_args={'port': 22})
# 建设连贯会话
conn.open()
try:
    conn.load_merge_candidate(filename='templates/ios_logging_config.cfg')
    conn.commit_config()
except Exception as e:
    print(e)    

如果设施上没开启scp服务就会报错:

SCP file transfers are not enabled. Configure 'ip scp server enable' on the device.

高级一点玩法:

模板文件门路:templates\ios_logging_config.cfg

def merge_config(vendor, devices, template_file):
    try:
        # drive是一个类class
        drive = get_network_driver(vendor)
        # 类实例化
        conn = drive(**devices)
        # 建设连贯会话
        conn.open()
    except Exception as e:
        print("连贯谬误:{}".format(e))
        return

    try:
        conn.load_merge_candidate(filename=template_file)
        new_config = conn.compare_config()
        if new_config:
            print('预推送的配置如下:')
            print('='*80)
            print(new_config)
            print('=' * 80)

            choice = input("你要开始推送这些配置嘛, [Y/N]: ")
            if choice.lower() == 'y':
                print('开始提交配置,请稍后...')
                conn.commit_config()
                rollback = input("是否须要回滚配置,输出Y:回滚, 输出N,不回滚. [Y/N]")
                if rollback.lower() == 'y':
                    print('开始回滚配置,请稍后...')
                    conn.rollback()
                    print('配置已回滚,请查看配置.')
                else:
                    print('配置曾经下发胜利.')
            else:
                conn.discard_config()
                print('本次预配置没有推送.')
        else:
            print('无需反复配置.')

    except Exception as e:
        print(e)
    finally:
        conn.close()

if __name__ == '__main__':
    vendor = 'ios'
    devices = {'hostname': '192.168.0.20',
               'username': 'cisco',
               'password': 'cisco',
               'optional_args':{'port': 22}
               }
    tmp = 'templates/ios_logging_config.cfg'

    merge_config(vendor, devices, template_file=tmp)

配置替换

办法:load_replace_candidate()

def replace_config(vendor, devices, template_file):
    try:
        # drive是一个类class
        drive = get_network_driver(vendor)
        # 类实例化
        conn = drive(**devices)
        # 建设连贯会话
        conn.open()
    except Exception as e:
        print("连贯谬误:{}".format(e))
        return

    try:
        if not (os.path.exists(template_file) and os.path.isfile(template_file)):
            msg = '文件不存在或文件类型不可用.'
            raise ValueError(msg)

        print("开始加载候选替换配置...")
        # 这个还未加载到设施上的
        conn.load_replace_candidate(template_file)
        # 配置比拟
        print("\n预览配置比照:")
        print(">"*80)
        compare_result = conn.compare_config()
        print(compare_result)
        print(">" * 80)

        # You can commit or discard the candidate changes.
        if compare_result:
            try:
                choice = input("\nWould you like to commit these changes? [yN]: ").lower()
            except NameError:
                choice = input("\nWould you like to commit these changes? [yN]: ").lower()
            if choice == "y":
                print("Committing ...")
                conn.commit_config()
            else:
                print("Discarding ...")
                conn.discard_config()
        else:
            print('没有新的配置.')
            conn.discard_config()

        conn.close()

        print("Done...")

    except Exception as e:
        print(e)

if __name__ == '__main__':
    vendor = 'ios'
    devices = {'hostname': '192.168.0.20',
               'username': 'cisco',
               'password': 'cisco',
               'optional_args':{'port': 22}
               }
    tmp = 'LOG/192.168.0.20-running.conf'

    replace_config(vendor, devices, template_file=tmp)

阐明:这里我用思科的设施,留神的是,如果running的配置里应用了banner,我是都提前把它删除了,不然会报错,这块我临时没测试进去,如果哪位小伙伴试出来了,请告知我,谢谢。

Jinja2模板

  • 装置jinja2

    python -m pip install jinja2
  • jiaja2模板配置
    模板文件门路:templates/ssh-acl.tpl

    {% for host in hosts -%}
        access-list 20 permit host {{ host }}
    {% endfor -%}
    line vty 0 4
        access-class 20 in
  • 下发配置

    from napalm import get_network_driver
    from jinja2 import FileSystemLoader, Environment
    
    import pprint
    import logging
    
    pp = pprint.PrettyPrinter(indent=2)
    
    def connect_device(vendor, devices, hosts):
        try:
            # drive是一个类class
            drive = get_network_driver(vendor)
            # 类实例化
            conn = drive(**devices)
            # 建设连贯会话
            conn.open()
    
        except Exception as e:
            print("连贯谬误:{}".format(e))
            return
    
        try:
            loader = FileSystemLoader('templates')
            env = Environment(loader=loader)
            tpl = env.get_template('ssh-acl.tpl')
            config_tpl = tpl.render({'hosts': hosts})
            # print(config_tpl)
            conn.load_merge_candidate(config=config_tpl)
            conn.commit_config()
    
        except Exception as e:
            print(e)
        finally:
            conn.close()
    
    if __name__ == '__main__':
        vendor = 'ios'
        devices = {'hostname': '192.168.0.20',
                   'username': 'cisco',
                   'password': 'cisco',
                   'optional_args':{'secret': 'cisco', 'port': 22}  # 如果enable须要明码,退出'secret'参数
                   }
        hosts = ['192.168.0.1', '192.168.0.254']
        connect_device(vendor, devices, hosts)

    这里能够打印下config_tpl,看看下发的配置是啥:

    # print(config_tpl)的输入后果如下所示:
    access-list 20 permit host 192.168.0.1
    access-list 20 permit host 192.168.0.254
    line vty 0 4
        access-class 20 in

配置更改回滚

conn.rollback()

备注:见后面章节演示.

验证部署

验证设施的状态是否是你冀望的.

  • 定义一个yaml文件,定义预期想要的状态

    模板文件门路:templates/napalm_t.yaml

    ---
    - get_facts:
        'serial_number': '99CBK1M35C217V5Z7ABWQ'
    - get_interfaces_ip:
        GigabitEthernet0/0:
          ipv4: 192.168.0.20
  • compliance_report办法

    import pprint
    from napalm import get_network_driver
    
    pp = pprint.PrettyPrinter(indent=2)
    
    def connect_device(vendor, devices, hosts):
        try:
            # drive是一个类class
            drive = get_network_driver(vendor)
            # 类实例化
            conn = drive(**devices)
            # 建设连贯会话
            conn.open()
    
        except Exception as e:
            print("连贯谬误:{}".format(e))
            return
    
        try:
            out = conn.compliance_report('templates/napalm_t.yaml')
            pp.pprint(out)
    
        except Exception as e:
            print(e)
        finally:
            conn.close()
    
    if __name__ == '__main__':
        vendor = 'ios'
        devices = {'hostname': '192.168.0.20',
                   'username': 'cisco',
                   'password': 'cisco',
                   'optional_args':{'port': 22}
                   }
        hosts = ['192.168.0.1', '192.168.0.254']
        connect_device(vendor, devices, hosts)

    执行后果如下所示:

    { 'complies': True,
      'get_facts': { 'complies': True,
                     'extra': [],
                     'missing': [],
                     'present': { 'serial_number': { 'complies': True,
                                                     'nested': False}}},
      'get_interfaces_ip': { 'complies': True,
                             'extra': [],
                             'missing': [],
                             'present': { 'GigabitEthernet0/0': { 'complies': True,
                                                                  'nested': True}}},
      'skipped': []}

    阐明:complies的value值为True阐明失常,如果False示意和预期构想不太统一。

反对上下文治理

通过上下文治理就不须要调用open()和close()办法了.

# 类实例化
drive = get_network_driver('ios')
# with上下文治理
with drive(hostname='192.168.0.20',username='cisco',password='cisco',optional_args={'port': 22}) as conn:
    output = conn.get_facts()
    # 打印后果
    pp.pprint(ouput)

ping办法

默认格局:ping x.x.x.x timeout 2 size 100 repeat 5

#!/usr/bin/env python3
#-*- coding:UTF-8 -*-

from napalm import get_network_driver
import pprint
import logging

# logging.basicConfig(filename='debug.log', level=logging.DEBUG)
# logger = logging.getLogger('napalm')

pp = pprint.PrettyPrinter(indent=2)

def connect_device(vendor, devices, dst_ip):
    try:
        # drive是一个类class
        drive = get_network_driver(vendor)
        # 类实例化
        conn = drive(**devices)
        # 建设连贯会话
        conn.open()

    except Exception as e:
        print("连贯谬误:{}".format(e))
        return

    try:
        for ip in dst_ip:
            output = conn.ping(ip)
            pp.pprint(output)

    except Exception as e:
        print(e)
    finally:
        conn.close()

if __name__ == '__main__':
    vendor = 'ios'
    devices = {'hostname': '192.168.0.20',
               'username': 'cisco',
               'password': 'cisco',
               'optional_args':{'port': 22}
               }
    dst_ip = ['192.168.0.19', '192.168.0.20']
    connect_device(vendor, devices, dst_ip)

回显的后果:

{ 'success': { 'packet_loss': 0,
               'probes_sent': 5,
               'results': [ {'ip_address': '192.168.0.19', 'rtt': 0.0},
                            {'ip_address': '192.168.0.19', 'rtt': 0.0},
                            {'ip_address': '192.168.0.19', 'rtt': 0.0},
                            {'ip_address': '192.168.0.19', 'rtt': 0.0},
                            {'ip_address': '192.168.0.19', 'rtt': 0.0}],
               'rtt_avg': 2.0,
               'rtt_max': 3.0,
               'rtt_min': 2.0,
               'rtt_stddev': 0.0}}
{ 'success': { 'packet_loss': 0,
               'probes_sent': 5,
               'results': [ {'ip_address': '192.168.0.20', 'rtt': 0.0},
                            {'ip_address': '192.168.0.20', 'rtt': 0.0},
                            {'ip_address': '192.168.0.20', 'rtt': 0.0},
                            {'ip_address': '192.168.0.20', 'rtt': 0.0},
                            {'ip_address': '192.168.0.20', 'rtt': 0.0}],
               'rtt_avg': 1.0,
               'rtt_max': 2.0,
               'rtt_min': 1.0,
               'rtt_stddev': 0.0}}    

好了,文章的解说就到此结束了,各个性能曾经给大家演示了,各位小伙伴如实操一遍,置信就能把握了并依据需要进行改写了。

如果须要更具体的信息,请挪动官网进行查阅。

别忘了,动动手分享下,点击、珍藏、三连击! 哈哈…

如果喜爱的我的文章,欢送关注我的公众号:点滴技术,扫码关注,不定期分享

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理