Для удобной поддержки инфраструктуры ЛВС, оказывается не лишнем поле description на коммутаторах. Разумеется прописывать руками, при большом размере сети, — долго, хлопотно и можно ошибиться. Поэтому, резонно данный процесс доверить скрипту. Как это сделать для коммутаторов Cisco Catalyst на языке Python далее в заметке.
Описание
Для работы скрипта необходимо установить Python 3 и библиотеку telnetlib.
Скрипт имеет 2 режима работы, которые задаются аргументами при запуске скрипта:
- show — подключиться к коммутаторам и вывести то, что будет записано в поле информации;
- write — подключться к коммутаторам и установить в поле description имена подключеных к ним хостов. Дополнительно, можно указать аргумент save, чтобы сохранить результаты.
В скрипте описан класс cisco_telnet со следующими методами:
- Конструктор — инициализирует переменные. Принимает строковые параметры IP-адрес коммутатора, имя пользователя, пароль;
- connect — Выполняет подключение к коммутатору и отключает постраничный вывод;
- command — Выполняет команду на коммутаторе. Принимает строку с командой и ожидаемый по завершении символ (по умолчанию — #);
- set_description — Выполняет команды установки поля description. Принимает имя интерфейса и строку с описанием;
- save_config — Сохраняет конфигурацию.
В списке словарей switches хранится информация о коммутаторах:
- ip — IP-адрес устройства;
- user — имя пользователя;
- password — пароль;
- ignore_ports — порты, которым не нужно менять описание;
- has_arp — коммутатор имеет ARP-таблицу, которая будет использована для поиска;
- vlans — список VLAN, которые нас интересуют.
Скрипт работает так: в цикле подключается к коммутаторам из списка switches. На устройствах, у которых есть ключ has_arp, выполняется команда show arp, результаты которой, генератором словарей, записываются в общий словарь arp, содержащий соответствие MAC- и IP-адреса. Выполняется команда show mac address и составляется словарь switches[‘mac’] в который помещаются MAC-адрес, VLAN и имя порта.
Далее, для каждого коммутатора из списка делается сопоставление MAC-адреса на порте с ARP-таблицей.
После чего, выполняется разрешение IP-адреса в имя хоста, основываясь на результате запроса к DNS и имя записывается в поле description. Если для IP-адреса обратной записи в DNS нет, то запишется IP-адрес. При этом, в зависимости от аргументов командной строки, происходит запись поля description.
Ну и в конце сохранение конфигурации, если есть соответствующий флаг, и отключение от коммутаторов.
Скрипт
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2017-2019 Aleksey Rogov <alex@arogov.com> import telnetlib import sys import socket class cisco_telnet: def __init__(self, host, user, password): self.host = host self.user = user self.password = password self.connected = 0 def __del__(self): if self.connected: self.disconnect() self.connected = 0 def connect(self): try: self.telnetobj = telnetlib.Telnet(self.host, 23, 5) except: return 0 self.connected = 1 self.telnetobj.read_until(b'Username: ') self.telnetobj.write(self.user.encode('ascii') + b'\n') self.telnetobj.read_until(b'Password: ') self.telnetobj.write(self.password.encode('ascii') + b'\n') self.telnetobj.read_until(b'#') self.telnetobj.write(b"terminal length 0\n") self.telnetobj.read_until(b"#") return 1 def disconnect(self): if not self.connected: return 0 self.telnetobj.write(b'exit\n') self.telnetobj.read_until(b"\n") self.telnetobj.close() self.connected = 0 return 1 def command(self, command, until='#'): if not self.connected: return None self.telnetobj.write(command.encode('ascii') + b'\n') out = self.telnetobj.read_until(until.encode('ascii')) return out def set_description(self, interface, description): if not self.connected: return 0 self.telnetobj.write(b'conf t\n') self.telnetobj.read_until(b'(config)#') self.telnetobj.write(b'int ' + interface.encode('ascii') + b'\n') self.telnetobj.read_until(b'(config-if)#') self.telnetobj.write(b'desc ' + description.encode('ascii') + b'\n') self.telnetobj.read_until(b'(config-if)#') self.telnetobj.write(b'exit\n') self.telnetobj.read_until(b'#') self.telnetobj.write(b'exit\n') self.telnetobj.read_until(b'#') return 1 def save_config(self): if not self.connected: return 0 self.telnetobj.write(b'write\n') self.telnetobj.read_until(b'#') return 1 switches = [{'ip': '10.0.0.1', 'user': 'descrwriter', 'password': 'pa$$w0rd', 'ignore_ports': ['Gi0/1', 'Gi0/2', 'Gi0/3', 'Gi0/4', 'Gi0/5', 'Gi0/6', 'Gi0/7', 'Gi0/8', 'Gi0/9', 'Gi0/10', 'Gi0/11', 'Gi0/12', 'Gi0/13', 'Gi0/14', 'Gi0/15', 'Gi0/16', 'Gi0/17', 'Gi0/18', 'Gi0/49', 'Gi0/50'], 'has_arp': True, 'vlans': ['1', '4']}, {'ip': '10.0.0.2', 'user': 'descrwriter', 'password': 'pa$$w0rd', 'ignore_ports': ['Gi0/1', 'Gi0/2'], 'vlans': ['1', '4']}, {'ip': '10.0.0.3', 'user': 'descrwriter', 'password': 'pa$$w0rd', 'ignore_ports': ['Fa0/1', 'Fa0/2', 'Fa0/3', 'Fa0/4', 'Fa0/5', 'Fa0/6', 'Fa0/7', 'Fa0/8', 'Fa0/9', 'Fa0/10', 'Fa0/11', 'Fa0/12', 'Fa0/13', 'Fa0/14', 'Fa0/15', 'Fa0/16', 'Fa0/17', 'Fa0/18', 'Fa0/19', 'Fa0/20', 'Fa0/21', 'Fa0/22', 'Fa0/23', 'Gi0/1', 'Gi0/2'], 'vlans': ['1', '4']} ] if len(sys.argv) < 2: print('usage:\n\t' + sys.argv[0] + ' show | write [save]') exit() if sys.argv[1] == 'show': mode = 0 elif sys.argv[1] == 'write': mode = 1 if sys.argv[1] == 'save': mode = 2 else: print('unknown argument: ' + sys.argv[1]) exit() for switch in switches: switch['tobj'] = cisco_telnet(switch['ip'], switch['user'], switch['password']) if not switch['tobj'].connect(): print('Error while connecting to host ' + switch['ip']) exit() if switch.get('has_arp'): out = switch['tobj'].command('show arp') switch['arp'] = {i.split()[3]: [socket.getfqdn(i.split()[1]).replace('.main.burgaz.ru', ''), i.split()[5]] for i in out.decode('utf-8').split('\n') if 'Internet' in i and 'Incomplete' not in i} out = switch['tobj'].command('show mac add') switch['mac'] = {i.split()[1]: [i.split()[0], i.split()[3]] for i in out.decode('utf-8').split('\n') if 'DYNAMIC' in i} for switch in switches: print(switch['ip']) for k, v in switch['mac'].items(): if v[0] in switch['vlans'] and v[1] not in switch['ignore_ports']: print(v[1] + '\t|\t' + k + '\t|\t' + switches[0]['arp'].get(k,[''])[0]) if mode > 0: switch['tobj'].set_description(v[1],switches[0]['arp'].get(k,[''])[0]) if mode == 2: print('Saving configuration...', end='') switch['tobj'].save_config() print('OK') switch['tobj'].disconnect() |