Python скрипт заполнения поля Description порта на коммутаторах Catalyst

By | 2019-04-09

Для удобной поддержки инфраструктуры ЛВС, оказывается не лишнем поле 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.
Ну и в конце сохранение конфигурации, если есть соответствующий флаг, и отключение от коммутаторов.

Скрипт

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright (C) 2017-2019 Aleksey Rogov 

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()

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

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