Практическое руководство по Netmiko и TextFSM
Rick Donato, вольный перевод Emil GaripovВведение
В этой статье мы рассмотрим два инструмента, используемых в мире сетевой автоматизации - Netmiko и TextFSM.
Наши примеры будут основаны на небольшой топологии, состоящей из 3 устройств Arista, Cisco NXOS9K и маршрутизатора Cisco IOS. Ниже показана топология

Netmiko
Netmiko - это библиотека SSH Python с поддержкой нескольких вендоров, которая упрощает процесс подключения к сетевым устройствам через SSH. Эта библиотека добавляет специфическую логику производителя к paramiko, которая является де-факто SSH библиотекой в Python[1].
Установка
Netmiko может быть установлен через менеджер пакетов Python - pip. Вот так:
$ pip3 install netmiko
Подключение
Для подключения к устройству мы используем ConnectHandler, которому передаем свои данные подключения. Следующим образом:
from netmiko import ConnectHandler
arista = {
'device_type': 'arista_eos',
'host': '172.29.133.1',
'username': 'rick',
'password': 'abc123',
}
cisco = {
'device_type': 'cisco_ios',
'host': '172.29.133.2',
'username': 'rick',
'password': 'abc123',
}
nxos = {
'device_type': 'cisco_nxos',
'host': '172.29.133.3',
'username': 'rick',
'password': 'abc123',
}
net_connect_arista = ConnectHandler(**arista)
net_connect_cisco = ConnectHandler(**cisco)
net_connect_nxos = ConnectHandler(**nxos)
Вывод на экран
Теперь, когда у нас есть соединение, установленное с нашими устройствами, мы можем начать выполнение некоторых задачи. Небольшая задача, с которой мы можем начать, это отображение диалогового окна для каждого устройства. Следующим образом:
>>> for device in [net_connect_arista, net_connect_cisco, net_connect_nxos]: ... device.find_prompt() ... 'veos>' 'viosl3#' 'nxos#'
Отправка команды
Давайте отправим команду на каждое из устройств. Поскольку каждое устройство использует OSPF, давайте проверим таблицу соседей OSPF. Следующим образом:
>>> for device in [net_connect_arista, net_connect_cisco, net_connect_nxos]:
... output = device.send_command('show ip ospf neighbor')
... print("----{}----".format(device))
... print(output)
... print("\n")
----<netmiko.arista.arista.AristaSSH object at 0x7f9713239278>----
Neighbor ID VRF Pri State Dead Time Address Interface
2.2.2.2 default 1 FULL/DR 00:00:35 10.1.1.2 Ethernet3
----<netmiko.cisco.cisco_ios.CiscoIosSSH object at 0x7f9713233978>----
Neighbor ID Pri State Dead Time Address Interface
3.3.3.3 1 FULL/BDR 0:00:36 10.2.1.1 GigabitEthernet0/2
1.1.1.1 1 FULL/BDR 0:00:37 10.1.1.1 GigabitEthernet0/1
--<netmiko.cisco.cisco_nxos_ssh.CiscoNxosSSH object at 0x7f97132334a8>-
OSPF Process ID 1 VRF default
Total number of neighbors: 1
Neighbor ID Pri State UP Time Address Interface
2.2.2.2 1 FULL/DR 04:25:36 10.2.1.2 GigabitEthernet0/2
Обновление конфигурации
Допустим, мы хотим отредактировать/внести изменения в некоторые настройки. Это достигается с помощью метода send_config_set. Этот метод принимает список() команд, которые будут отправлены на устройство.
# pre config
>>> print(net_connect_cisco.send_command('show run | sec ospf'))
router ospf 1
router-id 2.2.2.2
redistribute connected subnets
network 10.1.1.0 0.0.0.255 area 0
network 10.2.1.0 0.0.0.255 area 0
# update config
>>> output = net_connect_cisco.send_config_set(['router ospf 1', 'no redistribute connected subnets'])
>>> print(output)
config term
Enter configuration commands, one per line. End with CNTL/Z.
viosl3(config)#router ospf 1
viosl3(config-router)#no redistribute connected subnets
viosl3(config-router)#end
viosl3#
# post config
>>> print(net_connect_cisco.send_command('show run | sec ospf'))
router ospf 1
router-id 2.2.2.2
network 10.1.1.0 0.0.0.255 area 0
network 10.2.1.0 0.0.0.255 area 0
TextFSM
TextFSM - это модуль Python, который позволяет брать неструктурированные данные и преобразовывать их в структурированные с помощью наборов шаблонов на основе regex.
Замечательно, что Netmiko обеспечивает прямую интеграцию с TextFSM, которую мы скоро покажем.
Шаблоны TextFSM
Чтобы подготовиться к работе, мы извлечем некоторые предварительно созданные шаблоны из репозитория github:
https://github.com/networktocode/ntc-templates.
root@dekstop:~# git clone https://github.com/networktocode/ntc-templates Cloning into 'ntc-templates'... remote: Enumerating objects: 14, done. remote: Counting objects: 100% (14/14), done. remote: Compressing objects: 100% (12/12), done. remote: Total 4990 (delta 2), reused 6 (delta 1), pack-reused 4976 Receiving objects: 100% (4990/4990), 1.19 MiB | 2.29 MiB/s, done. Resolving deltas: 100% (2749/2749), done. Checking connectivity... done.
Применение TextFSM в Netmiko
Чтобы использовать TextFSM и ранее загруженные шаблоны, мы, как и прежде, просто используем команду send_command, установив атрибут use_textfsm равным True:
>>> from netmiko import ConnectHandler
>>> arista = {
... 'device_type': 'arista_eos',
... 'host': '172.29.133.1',
... 'username': 'rick',
... 'password': 'abc123',
... }
>>> net_connect_arista = ConnectHandler(**arista)
>>> output = net_connect_arista.send_command('sh ver', use_textfsm=True)
Если мы сейчас посмотрим на наш вывод, то увидим, что он теперь структурирован.
>>> import pprint
>>> pprint.pprint(output)
[{'free_memory': '121936',
'hw_version': '',
'image': '4.15.10M',
'model': 'vEOS',
'serial_number': '',
'sys_mac': '5000.00d7.ee0b',
'total_memory': '1897596'}]
>>> output[0]['model']
'vEOS'
Индексный файл
Пример хороший, но откуда Netmiko знает, какой шаблон использовать?
Это достигается с помощью индексного файла, который содержит привязку команд к шаблонам (фрагмент, показанный ниже). По умолчанию Netmiko настроен на автоматический поиск индексного файла в ~/ntc-template/templates/index. Однако его можно изменить на другой путь с помощью переменной окружения NET_TEXTFSM.
... arista_eos_show_ip_route.template, .*, arista_eos, sh[[ow]] i[[p]] rou[[te]] arista_eos_show_version.template, .*, arista_eos, sh[[ow]] ver[[sion]] arista_eos_show_ip_arp.template, .*, arista_eos, sh[[ow]] i[[p]] ar[[p]] arista_eos_show_ip_bgp.template, .*, arista_eos, sh[[ow]] i[[p]] bg[[p]] arista_eos_show_module.template, .*, arista_eos, sh[[ow]] modu[[le]] ...
Создание пользовательских шаблонов
Для создания собственного шаблона необходимо выполнить 2 шага - создание шаблона и обновление индекса. Чтобы продемонстрировать эти шаги, мы создадим шаблон для команды EOS show uptime.
Создание шаблона
Прежде всего, мы создадим новый файл шаблона. Название должно быть основано на следующей схеме:{{ vendor_name }}_{{show_command}}.template. Например, в нашем случае это будет шаблон arista_eos_show_uptime.template. Этот файл будет помещен в ntc-шаблоны/шаблоны.
Ниже показано содержимое шаблона. Различные опции написания шаблона TextFSM выходят за рамки этой статьи. Однако, в двух словах, мы разбиваем наш текст через regex, показанный внизу, используя Values для определения текста, который мы извлечем.
Value TIME (\S+)
Value UPTIME (\S+)
Value USERS (\d+)
Value LOAD_AVERAGE (\S+)
Start
^\s${TIME}\s+up\s+${UPTIME}.*${USERS}\s+.*load average.\s+${LOAD_AVERAGE} -> Record
Обновление индекса
Затем файл индекса будет обновлен, чтобы сопоставить наш шаблон с командой (см. ниже). Примечание: Это также позволит сократить время работы команды, например, sh uptime или sh up.
... arista_eos_show_uptime.template, .*, arista_eos, sh[[ow]] up[[time]]
Тест
Быстрый тест показывает нам вывод команды в исходном состоянии и структурированный с помощью нашего недавно созданного шаблона.
>>> net_connect_arista.send_command('show uptime')
' 19:54:17 up 23 min, 1 user, load average: 0.15, 0.15, 0.21'
>>> net_connect_arista.send_command('show uptime', use_textfsm=True)
[{'load_average': '0.15,', 'time': '19:54:20', 'users': '1', 'uptime': '23'}]
Подключайтесь к каналу - https://t.me/automate_net