Модуль Netmiko для Python — очень простой и полезный модуль для начала базовой автоматизации сетевых устройств.
Опираемся на результаты статьи Как установить Python.
Модуль Netmiko
Открываем промпт конды Пуск > Anaconda Prompt (miniconda3) и устанавливаем модуль:
(base) C:\Users\Andy> pip install netmiko

Подробнее про модуль 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.