Мой друг Netmiko. Часть 2: Три улучшения Python-скрипта

от автора

Продолжаю ковырять автоматизацию рутины на сети из Huawei коммутаторов. На этот раз изыскания, которые позволили сократить код в 3 раза, а именно: хосты и команды перенесены в отдельные файлы, пароль и имя пользователя больше не хранятся в открытом тексте. Есть демонстрация запуска скрипта.

Я лучше воспринимаю вещи визуально, поэтому представил задачу по оптимизации скрипта в графическом виде (см. ниже) — а именно отразил три основные задачи:

1.     Применить inventory файл, в котором перечислить ip-адреса всех сетевых устройств, а не создавать словарь для каждого из них.

2.     Переместить конфигурационные команды в отдельный файл и так же, как и inventory файл вызывать его с помощью функции.

3.     Избавиться от хранения пароля и имени пользователя в скрипте.

Я также убрал команды верификации (Их, конечно, можно и оставить, и они будут работать из конфигурационного файла, но для настоящей задачи они не нужны).

В комментарии к предыдущему посту «Мой друг Netmiko» появился вопрос от пользователя anders_i о том, насколько можно масштабировать скрипт:

Тогда я использовал 4 виртуальных CloudEngine Huawei в eNSP. На этот раз я попробовал расширить их количество настолько, насколько хватит мощности Huawei MateBook X Pro 2021.

И расширить удалось до 8-ми. 9-ый запускаться отказался, оккупация 16GB ОЗУ составила 80%.

Обновленная топология выглядит так:

1.Создаю конфигурационный файл

Я предпочитаю редактор Nano (кто-то предпочитает Vim):

nano switch_file_config

В файле прописываю все команды из прошлого примера (команды могут быть любыми в зависимости от нужд) без пробелов/отступов. Важно указать команду return последней строкой. Как я понял, ее нужно прописывать вручную только при использовании конфигурационного файла – при прописывании команд в самом скрипке модуль Netmiko автоматически ее применяет и завершает SSH-соединение. Без return скрипт не сработает.

Список команд, перенесенные в отдельный файл:

Теперь весь предыдущий код:

    for n in range (300,302):         print ("Creating VLAN " + str(n))         config_commands = [                           'vlan ' + str(n),                           'desc NETMIKO_VLAN ' + str(n),                           'Commit'         ]         output = ssh_connect.send_config_set(config_commands)      output = ssh_connect.send_config_set(         [         'interface range GE 1/0/9 GE 1/0/10',         'port trunk allow-pass vlan 300 301',         'commit'         ]     )

Сокращаю до:

with open('switch_file_config') as f:     config_lines = f.read().splitlines() print (config_lines)

и меняю строчку:

    output = ssh_connect.send_config_set(config_lines)

Python функция open открывает указанный файл, читает его, разделяя на линии (форматируя) f.read().splitlines() и выводит на экран print(config_lines). Модуль Netmiko send_config_set теперь будет обращаться к этому файлу (config_lines), применяя команды из него.

 

2.Создаю inventory-файл с IP-адресами коммутаторов:

Начинаю с того же создания файла в редакторе Nano:

nano myswitches

Это файл будет содержать список (list) IP-адресов коммутаторов:

Здесь главное — убедиться, чтобы не осталось пробелов.

Сохраняю командой Ctr+X.

Для небольшого теста я создал python-файл, назвал его openfile.py.

Прописал в нем простой скрипт, который будет открывать файл с IP-адресами и выводить их на экран:

f = open ('myswitches')  for IP in f:     print (IP)

Запустил его:

Как видно, первая строчка осталась пустой, а значит для Python она будет выглядеть как [‘ ’,]. При попытке запуска такого файла Python выдаст ошибку, что не смог связаться с адресом назначения. Поэтому пустых строк лучше не допускать.

Я настроил дополнительные 4 CE-коммутатора в eNSP, дав им IP-адреса и настроив SSH-соединение:

Теперь сокращаю весь предыдущий код:

CE_1_BORDER = {     'device_type': 'huawei',     'ip':   '7.7.7.1',     'username': 'vasyo1',     'password': '@ghjcnjnF358986' }  CE_2 = {     'device_type': 'huawei',     'ip':   '7.7.7.2',     'username': 'vasyo1',     'password': '@ghjcnjnF358986' }  CE_3 = {     'device_type': 'huawei',     'ip':   '7.7.7.3',     'username': 'vasyo1',     'password': '@ghjcnjnF358986'         }  CE_4 = {     'device_type': 'huawei',     'ip':   '7.7.7.4',     'username': 'vasyo1',     'password': '@ghjcnjnF358986' }  all_devices = [CE_1_BORDER, CE_2, CE_3, CE_4]  for device in all_devices:     ssh_connect = ConnectHandler(**device)

До:

with open('myswitches') as f:     ip_lines = f.read().splitlines() print (ip_lines)  for device in ip_lines:     ip_address_of_device = device     CE = {         'device_type': 'huawei',         'ip':   ip_address_of_device,         'username': username,         'password': password     }      ssh_connect = ConnectHandler(**CE)     output = ssh_connect.send_config_set(config_lines)

С синтаксисом открытия файла все понятно.

В словаре Netmiko больше не прописываю IP-адрес. Вместо этого теперь код будем обращаться за IP-адресами к переменной ip_address_of_device, которая равна переменной device, которая является частью цикла (loop) for, который читает ip_lines, которая является переменной, содержащей IP-адреса устройств в файле «myswitches».

3.Удаляю пароль и имя пользователя из скрипта

Небезопасно хранить имя пользователя и пароль, что называется, clear text:

    CE = {         'device_type': 'huawei',         'ip':   ip_address_of_device,         'username': 'vasyo1',         'password': '@ghjcnjnF358986'     }

Вряд ли в производственной сети такое допустимо.

Поэтому я удалю их из скрипта, заменив переменными: переделаю скрипт таким образом, чтобы скрипт вывел на экран сообщение о необходимости ввода имени пользователя, а после введенные значения сохранил в переменной. Для этого использую функцию input(). Функция input() для версии Python 2.x называется raw_input().

Функцию  getpass() использую для ввода пароля.

username = input('Enter your SSH username: ') password = getpass()

В словаре имя пользователя и пароль заменяю на переменные:

    CE = {         'device_type': 'huawei',         'ip':   ip_address_of_device,         'username': username,         'password': password     }

В итоге я получил улучшенный код, который сократился с 63 строк до 28 и стал более адаптированным для применения в реальной производственной практике. Выглядит он следующий образом (запуск скрипта на видео можно посмотреть на странице Huawei Форума ICT Club):

from getpass import getpass from netmiko import ConnectHandler  username = input('Enter your SSH username: ') password = getpass()  with open('switch_file_config') as f:     config_lines = f.read().splitlines() print (config_lines)  with open('myswitches') as f:     ip_lines = f.read().splitlines() print (ip_lines)  for device in ip_lines:     ip_address_of_device = device     CE = {         'device_type': 'huawei',         'ip':   ip_address_of_device,         'username': username,         'password': password     }      ssh_connect = ConnectHandler(**CE)     output = ssh_connect.send_config_set(config_lines)     print(f"\n\n-------------- CE_{CE['ip']} --------------")     print(output)     print("-------------------- End -------------------")

Литература:

https://stackoverflow.com/questions/5563089/raw-input-function-in-python

https://pynet.twb-tech.com/blog/automation/netmiko.html

https://pyneng.readthedocs.io/en/latest/book/18_ssh_telnet/netmiko.html

https://github.com/ktbyers/netmiko

https://github.com/ktbyers/netmiko/blob/master/netmiko/ssh_dispatcher.py

Udemy.com — Python Network Programming for Network Engineers (Python 3) (David Bombal)

https://www.pythoncentral.io/pythons-range-function-explained


ссылка на оригинал статьи https://habr.com/ru/company/huawei/blog/648127/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *