Модуль Netmiko для Python

Модуль Netmiko для Python — очень простой и полезный модуль для начала базовой автоматизации сетевых устройств.

Опираемся на результаты статьи Как установить Python.


Модуль Netmiko

Открываем промпт конды Пуск > Anaconda Prompt (miniconda3) и устанавливаем модуль:

(base) C:\Users\Andy> pip install netmiko
Модуль Netmiko для Pytho

Подробнее про модуль Netmiko и его возможности написано здесь. Запускаем Jupyter, в скрипте первое это импорт модуля. Функция ConnectHandler, создающая подключение к устройству, имеет следующую структуру:

import netmiko

sshCli = ConnectHandler(
 device_type = 'cisco_ios',
 host = '10.10.20.48',
 port = 22,
 username = 'developer',
 password = 'C1sco12345'
 )

Выбираем с какой операционной системой будем работать cisco_ios.

Конфигурирование

Далее задаём список команд так, как мы их обычно вводим в режиме глобальной конфигурации:

config_commands = [
 'router ospf 1',
 'network 0.0.0.0 255.255.255.255 area 0'
 ]

Не нарушаем синтаксис языка, элементы списка должны быть отделены запятой. Это наверное одна из самых частых ошибок при работе с модулем.

Затем к объекту sshCli, это наше установленное соединение, применяем метод send_config_set(config_commands):

sshCli.send_config_set(config_commands)

И присваиваем результат выполнения переменной sentConfig, а затем выводим эту переменную:

sentConfig = sshCli.send_config_set(config_commands)

print(sentConfig)

Как видно на рисунке, не нужно вводить команды configure terminal в начале и end в конце, они вводятся автоматически. Теперь сессию нужно закрыть, если этого не сделать она так и останется висеть, занимая линию VTY. Несколько скриптов без завершения сесии и к устройству нельзя будет подключиться, так как нет свободных линий.

sshCli.disconnect()
Сохранение конфигруации

Если нужно сохранить конфигурацию, то это проще сделать так:

config_commands = [
 'router ospf 1',
 'network 0.0.0.0 255.255.255.255 area 0',
 'do wr'
 ]

Результат выполнения:

R2# configure terminal
R2(config)# router ospf 1
R2(config-router)# network 0.0.0.0 255.255.255.255 area 0
R2(config-router)# do wr
Building configuration...
[OK]
R2(config-router)#end
R2#
Просмотр конфигурации

Для этого используем метод send_command:

output = sshCli.send_command("sh run | se router")
print(output)

Ну и всё вместе:

Можно было импортировать не весь модуль:

from netmiko import ConnectHandler

Этого бы хватило. Закругляюсь, придумаю если ещё пример, тогда дополню.


Парсинг

Хорошо, получили вывод команд при просмотре конфы. Что с этим делать? Нужно парсить этот вывод, иcпользуем проект NTC Templates. Установка:

(base) C:\Users\Andy> pip install ntc_templates

В начало скрипта подписываем:

from ntc_templates.parse import parse_output

Формат использования:

command_parsed = parse_output(platform = "cisco_ios", command = "show vlan", data = vlan_output)
  • parse_output это как раз функция, выполняющая парсинг;
  • cisco_ios платформа (есть разные, не только циска);
  • command команда, результат выполнения которой мы хотим распарсить (нужно сначала посмотреть есть ли темплейт для парсинга данной команды);
  • data сам результат выполнения команды command на устройстве, получен ранее;
  • command_parsed результат парсинга.

На примере команды show ip interface brief:

output = sshCli.send_command("sh ip int br") - получили с устройства результат команды в "сыром" виде
command_parsed = parse_output(platform = "cisco_ios", command = "show ip interface brief", data = output) - распарсили

print(command_parsed)

Допустим тут напечаталось следующее:

[{'intf': 'GigabitEthernet0/0', 'ipaddr': 'unassigned', 'status': 'up', 'proto': 'up'}, 
 {'intf': 'GigabitEthernet0/1', 'ipaddr': 'unassigned', 'status': 'up', 'proto': 'up'}, 
 {'intf': 'GigabitEthernet0/2', 'ipaddr': '192.168.79.163', 'status': 'up', 'proto': 'up'},
 {'intf': 'GigabitEthernet0/3', 'ipaddr': 'unassigned', 'status': 'down', 'proto': 'down'}]

Результатом является список, каждый его элемент это словарь. Ключи словаря можно посмотреть в файле темплейта на сайте проекта. И теперь если нам нужно получить какое-то значение, допустим ip address для GigabitEthernet0/2, то это будет:

ip_adr_Gi02 = command_parsed[2]['ipaddr']

То есть третий элемент (нумерация с нуля) и ключ ipaddr.


Пример скрипта

Задача: Нужно получить для хелпдеска список неиспользуемых портов на коммутаторах.

У сотрудников NOC такая "задача" бывает несколько раз в неделю. По какому критерию определять неиспользуемый порт? Если он погашен административно, это раз, если на нём не было трафика, это два. То есть признак administratively down и признак Last input в значении never.

Да, это можно организовать в пару команд:

S1# sh int | i administratively down
S1# sh int | i line protocol | Last input

А если стек и таких стеков несколько штук? С первой командой вопросов нет. На второй глаза не замылятся? Скриптом удобнее, получился вот такой скриптик:

from netmiko import ConnectHandler
from ntc_templates.parse import parse_output

hosts = {'192.168.79.163','192.168.79.164','192.168.79.165'}
inetface_count = 0

for host_ip in hosts:
    sshCli = ConnectHandler(
    device_type = 'cisco_ios',
    host = host_ip,
    port = 22,
    username = 'admin',
    password = 'cisco'
    )
    
    output = sshCli.send_command("sh ip int br")
    command_parsed = parse_output(platform = "cisco_ios", command = "show ip interface brief", data = output)
    
    for elem_command_parsed in command_parsed:
        
        if elem_command_parsed['status'] == 'down':            
            output = sshCli.send_command("sh int " + elem_command_parsed['intf'])
            interface_parsed = parse_output(platform = "cisco_ios", command = "show interfaces", data = output)
            
            if interface_parsed[0]['last_input'] == 'never':
                inetface_count += 1
                print(host_ip, elem_command_parsed['intf'], 'down')
                
        elif elem_command_parsed['status'] == 'administratively down':
            inetface_count += 1
            print(host_ip, elem_command_parsed['intf'], 'administratively down')
    
    sshCli.disconnect()
    
print("ready! total: ", inetface_count)

Результат работы в тестовой среде:

Что насчёт портов, которые реально в down? Во-первых, это порты где оконечное устройство в данный момент выключено (например, пользовательский PC). Во-вторых, нескоммутированные порты. Поэтому признак down ни о чём не говорит и не может быть использован.

Если всё понятно, супер, нет — предлагаю немного поучить Python.

Leave a Comment

Scroll to Top