Ищем порт на коммутаторах D-Link

от автора

Предисловие

Есть у нас сеть. Не вдаваясь в подробности, состоит она из нескольких десятков коммутаторов D-Link и Cisco. Последние выполняют роль агрегаторов трафика, а D-Link занимаются доступом. Соответственно много VLAN, много устройств. И очень часто, пользователи меняют свое расположение, а вместе с ними переезжают их компьютеры и МФУ. Всё это дело хоть и подключается к нумерованным розеткам, часто поиск порта на коммутаторе для замены VLAN довольно долог и не удобен. Кроме того, техники, которые занимаются непосредственно подключением оборудования, не имеют доступа на коммутаторы. Им приходится глазами искать кабель в стойке, а потом просить инженеров проверить VLAN. Хотелось как-то упростить процесс.

Я уже давно неспешно изучаю Python в качестве дополнительного образования. В основном для применения в автоматизации на работе и дома. Ну тут вот и сошлось: Python, проблема и статья на Хабре Python3. Автоматизация конфигурации мультивендорного сетевого оборудования / Хабр. Прямо брать код из ее не стал, такой функциональности не требуется. Да и хотелось самому разобраться, опираясь на идею.

Написание кода началось с запроса к GigaChat, заодно было интересно проверить насколько он хорошо пишет скрипты. Не буду утомлять приводя весь цикл нашего общения, остановлюсь кратко: сперва GigaChat выдал скрипт на основе библиотеки paramiko, после уточнения, что нужно для Telnet выдал и эту версию. После нескольких итераций, в том числе с асинхронными функциями, остановился на библиотеке telnetlib. В Python 3.12 есть ее замена telnetlib3, т.к. сама telnetlib вырезана в релизе — устарела. ИИ вполне можно использовать для написания небольших скриптов, как отработка идеи или небольших функций в проектах. Однако стоит проверять код, т.к. в части случаев он получается совсем не рабочий.

Разберем получившийся код, благо он небольшой:

import telnetlib import sys import argparse import re import ipaddress from time import sleep  # Параметры подключения username = 'логин'  # Имя пользователя password = 'пароль'  # Пароль администратора #mac_address = ''    #04-18-D6-9C-75-C4 

Подключение нужных библиотек и параметры подключений. Так же небольшая подсказка как должен выглядеть вводимый MAC- адрес.

def telnet_connect(ip, username, password):     try:    # отслеживаем ошибку соединения         tn = telnetlib.Telnet(ip,timeout=2)         tn.read_until(b":")  # Username         tn.write(username.encode('ascii') + b"\n")         tn.read_until(b":")  # Password         tn.write(password.encode('ascii') + b"\n")         sleep(1)  # Ждем немного, пока коммутатор авторизует пользователя         return tn     except ConnectionRefusedError:         print(f'Ошибка подключения: {ip}, соединение отклонено сервером.')         return '0'     except TimeoutError:         print(f'Ошибка таймаута: {ip}, истекло время ожидания подключения.')         return '0'     except OSError:         print(f'Общая ошибка ОС')         return '0' 

Функция подключения к коммутатору с обработчиком трех типов ошибок, вполне хватает чтобы понять, что коммутатор например отключен.

def find_mac(tn, mac_address):     # запрос таблицы адресов     tn.write(f'show fdb mac_address {mac_address}\n'.encode('ascii'))     response = tn.read_until(b'Total entries:', timeout=1).decode('utf-8')     # ищем нужный мас     lines = response.split('\n')     for line in lines:         parts = line.strip().split()         if len(parts) < 6: continue # пропуск коротких строк         if mac_address in parts[2]:  # Проверка совпадения MAC             port_number = int(parts[3]) # берем порт             if port_number in range(49,52): continue    # trunk нас не интересует             print(f"MAC-адрес {mac_address} найден на порте {port_number} VLAN {parts[1]}")             return True     # найден     return False    # не найден   

Функция поиска MAC-адреса. Собственно всё крутиться вокруг команды show fdb mac_address и парсинга её вывода. Так же исключаются порты в режиме trunk, так как там тоже может светиться нужный нам MAC, но это не то, что мы ищем.

def find_port(ipaddr,mac_address): # проходим по списку коммутаторов     if not ipaddr: # если не задан конкретный адрес загружаем весь список         # загружаем файл со списком коммутаторов         try:             with open('ListComm.txt') as f:                 list_comm = f.readlines()             f.close()         except FileNotFoundError:             print("Файл списка коммутаторов не найден или недоступен")             sys.exit()     else:         list_comm = [ipaddr]    # грузим в список указанный ip      for ip in list_comm:         ip = ip.strip()         print(f'Ищем на {ip}...')         tn = telnet_connect(ip, username, password)         if tn == '0':   # если выдана ошибка соединения продолжаем искать на следующем             continue         port_found = find_mac(tn, mac_address)         tn.close()         if port_found: sys.exit()

Функция поиска порта. Собственно тут и происходит перебор всех коммутаторов с вызовом предыдущих функций. Всё просто: список коммутаторов лежит в текстовом файле, он подгружается и идет перебор ip-адресов. Да, тут нет проверки корректности IP адреса, предполагается что список-то подготовить корректно можно. Но даже с некорректным просто выдается ошибка соединения.

def main():     # работа с аргументами     parser = argparse.ArgumentParser(description="Поиск порта и VLAN по MAC-адресу на коммутаторах DLink")     parser.add_argument('mac',type=str, help="MAC-адрес в формате XX-XX-XX-XX-XX-XX")     parser.add_argument('ip', type=str,  nargs='?', help="IP адрес коммутатора, не обязательно")     args = parser.parse_args()      mac_address = args.mac.upper()  # сразу перевод в заглавные буквы     # Регулярное выражение для проверки формата MAC-адреса     mac_pattern = r'^([0-9A-Fa-f]{2}[-]){5}([0-9A-Fa-f]{2})$'      if not re.match(mac_pattern, mac_address):         print('Не верный формат MAC-адреса')         sys.exit()      if args.ip: # если присутствует адрес коммутатора, считываем его как единственный для поиска         ipaddr = args.ip         try:    # проверяем корректность ip адреса             ipaddress.IPv4Address(ipaddr)         except ipaddress.AddressValueError:             print('Не верный формат IP-адреса')             sys.exit()         find_port(ipaddr, mac_address)     else:         find_port('',mac_address)  if __name__ == "__main__":     main()   

Основная функция. Здесь инструкции по запуску, аргументам, их считывание и проверка корректности. Есть варианты запуска: по-умолчанию, со списком IP-адресов из файла или прямым указанием одного IP-адреса с игнорированием списка.

Вот собственно и всё. Теперь поиск нужного порта занимает буквально пару минут при переборе всех коммутаторов. Самое главное: скрипт упаковывается в EXE файл через PyInstaller, тем самым скрывая логин и пароль от техников, которым запрещен вход на коммутаторы. Используя скрипт, они могут найти порт гораздо быстрее и сразу проверить правильный ли VLAN там стоит.

Да, я знаю, что PyInstaller делает просто упаковку скрипта и нужных библиотек в EXE файл, который при желании можно вскрыть. Но на сей момент ничего лучшего не знаю.


ссылка на оригинал статьи https://habr.com/ru/articles/937954/