При обработке вывода команд или конфигурации часто надо будет записать итоговые данные в словарь.
Не всегда очевидно как обрабатывать вывод команд и каким образом в целом подходить к разбору вывода на части. В этом подразделе рассматриваются несколько примеров, с возрастающим уровнем сложности.
В этом примере будет разбираться вывод команды sh ip int br. Из вывода команды нам надо получить соответствия имя интерфейса - IP-адрес. То есть имя интерфейса - это ключ словаря, а IP-адрес - значение. При этом, соответствие надо делать только для тех интерфейсов, у которых назначен IP-адрес.
Пример вывода команды sh ip int br (файл sh_ip_int_br.txt):
R1#show ip interface brief Interface IP-Address OK? Method Status Protocol FastEthernet0/0 15.0.15.1 YES manual up up FastEthernet0/1 10.0.12.1 YES manual up up FastEthernet0/2 10.0.13.1 YES manual up up FastEthernet0/3 unassigned YES unset up down Loopback0 10.1.1.1 YES manual up up Loopback100 100.0.0.1 YES manual up up
Файл working_with_dict_example_1.py:
result = {} with open('sh_ip_int_br.txt') as f: for line in f: line = line.split() if line and line[1][0].isdigit(): interface, address, *other = line result[interface] = address print(result)
Команда sh ip int br отображает вывод столбцами. Значит нужные поля находятся в одной строке. Скрипт обрабатывает вывод построчно и каждую строку разбивает с помощью метода split.
Полученный в итоге список содержит столбцы вывода. Так как из всего вывода нужны только интерфейсы на которых настроен IP-адрес, выполняется проверка первого символа второго столбца: если первый символ число, значит на интерфейсе назначен адрес и эту строку надо обрабатывать.
В строке interface, address, *other = line
выполняется распаковка переменных. В переменную interface попадет имя интерфейса, в address попадет IP-адрес, а в other все остальные поля.
Так как для каждой строки есть пара ключ и значение, они присваиваются в словарь: result[interface] = address
.
Результатом выполнения скрипта будет такой словарь (тут он разбит на пары ключ-значение для удобства, в реальном выводе скрипта словарь будет отображаться в одну строку):
{'FastEthernet0/0': '15.0.15.1', 'FastEthernet0/1': '10.0.12.1', 'FastEthernet0/2': '10.0.13.1', 'Loopback0': '10.1.1.1', 'Loopback100': '100.0.0.1'}
Очень часто вывод команд выглядит таким образом, что ключ и значение находятся в разных строках. И надо придумать каким образом обрабатывать вывод, чтобы получить нужное соответствие.
Например, из вывода команды sh ip interface надо получить соответствие имя интерфейса - MTU (файл sh_ip_interface.txt):
Ethernet0/0 is up, line protocol is up Internet address is 192.168.100.1/24 Broadcast address is 255.255.255.255 Address determined by non-volatile memory MTU is 1500 bytes Helper address is not set ... Ethernet0/1 is up, line protocol is up Internet address is 192.168.200.1/24 Broadcast address is 255.255.255.255 Address determined by non-volatile memory MTU is 1500 bytes Helper address is not set ... Ethernet0/2 is up, line protocol is up Internet address is 19.1.1.1/24 Broadcast address is 255.255.255.255 Address determined by non-volatile memory MTU is 1500 bytes Helper address is not set ...
Имя интерфейса находится в строке вида Ethernet0/0 is up, line protocol is up
, а MTU в строке вида MTU is 1500 bytes
.
Например, попробуем запоминать каждый раз интерфейс и выводить его значение, когда встречается MTU, вместе со значением MTU:
In [2]: with open('sh_ip_interface.txt') as f: ...: for line in f: ...: if 'line protocol' in line: ...: interface = line.split()[0] ...: elif 'MTU is' in line: ...: mtu = line.split()[-2] ...: print('{:15}{}'.format(interface, mtu)) ...: Ethernet0/0 1500 Ethernet0/1 1500 Ethernet0/2 1500 Ethernet0/3 1500 Loopback0 1514
Вывод организован таким образом, что всегда сначала идет строка с интерфейсом, а затем через несоколько строк - строка с MTU. Если запоминать имя интерфейса каждый раз, когда оно встречается, то на момент когда встретится строка с MTU, последний запомненный интерфейс - это тот к которому относится MTU.
Теперь, если необходимо создать словарь с соответствием интерфейс - MTU, достаточно записать значения на момент, когда был найден MTU.
Файл working_with_dict_example_2.py:
result = {} with open('sh_ip_interface.txt') as f: for line in f: if 'line protocol' in line: interface = line.split()[0] elif 'MTU is' in line: mtu = line.split()[-2] result[interface] = mtu print(result)
Результатом выполнения скрипта будет такой словарь (тут он разбит на пары ключ-значение для удобства, в реальном выводе скрипта словарь будет отображаться в одну строку):
{'Ethernet0/0': '1500', 'Ethernet0/1': '1500', 'Ethernet0/2': '1500', 'Ethernet0/3': '1500', 'Loopback0': '1514'}
Этот прием будет достаточно часто полезен, так как вывод команд, в целом, организован очень похожим образом.
Если из вывода команды надо получить несколько параметров, очень удобно использовать словарь с вложенным словарем.
Например, из вывода sh ip interface надо получить два параметра: IP-адрес и MTU. Для начала, вывод информации:
In [2]: with open('sh_ip_interface.txt') as f: ...: for line in f: ...: if 'line protocol' in line: ...: interface = line.split()[0] ...: elif 'Internet address' in line: ...: ip_address = line.split()[-1] ...: elif 'MTU' in line: ...: mtu = line.split()[-2] ...: print('{:15}{:17}{}'.format(interface, ip_address, mtu)) ...: Ethernet0/0 192.168.100.1/24 1500 Ethernet0/1 192.168.200.1/24 1500 Ethernet0/2 19.1.1.1/24 1500 Ethernet0/3 192.168.230.1/24 1500 Loopback0 4.4.4.4/32 1514
Тут используется такой же прием, как в предыдущем примере, но добавляется еще одна вложенность словаря:
result = {} with open('sh_ip_interface.txt') as f: for line in f: if 'line protocol' in line: interface = line.split()[0] result[interface] = {} elif 'Internet address' in line: ip_address = line.split()[-1] result[interface]['ip'] = ip_address elif 'MTU' in line: mtu = line.split()[-2] result[interface]['mtu'] = mtu print(result)
Каждый раз, когда встречается интерфейс, в словаре result создается ключ с именем интерфейса, которому соответствует пустой словарь. Эта заготовка нужна для того, чтобы на момент когда встретится IP-адрес или MTU можно было записать параметр во вложенный словарь соответствующего интерфейса.
Результатом выполнения скрипта будет такой словарь (тут он разбит на пары ключ-значение для удобства, в реальном выводе скрипта словарь будет отображаться в одну строку):
{'Ethernet0/0': {'ip': '192.168.100.1/24', 'mtu': '1500'}, 'Ethernet0/1': {'ip': '192.168.200.1/24', 'mtu': '1500'}, 'Ethernet0/2': {'ip': '19.1.1.1/24', 'mtu': '1500'}, 'Ethernet0/3': {'ip': '192.168.230.1/24', 'mtu': '1500'}, 'Loopback0': {'ip': '4.4.4.4/32', 'mtu': '1514'}}
Иногда, в выводе будут попадаться секции с пустыми значениями. Например, в случае с выводом sh ip interface, могут попадаться интерфейс, которые выглядят так:
Ethernet0/1 is up, line protocol is up Internet protocol processing disabled Ethernet0/2 is administratively down, line protocol is down Internet protocol processing disabled Ethernet0/3 is administratively down, line protocol is down Internet protocol processing disabled
Соответственно тут нет MTU или IP-адреса.
И, если выполнить предыдущий скрипт для файла с такими интерфейсами, результат будет таким (вывод для файла sh_ip_interface2.txt):
{'Ethernet0/0': {'ip': '192.168.100.2/24', 'mtu': '1500'}, 'Ethernet0/1': {}, 'Ethernet0/2': {}, 'Ethernet0/3': {}, 'Loopback0': {'ip': '2.2.2.2/32', 'mtu': '1514'}}
Если необходимо добавлять интерфейсы в словарь только, когда на интерфейсе назначен IP-адрес, надо перенести создание ключа с именем интерфейса на момент, когда встречается строка с IP-адресом (файл working_with_dict_example_4.py):
result = {} with open('sh_ip_interface2.txt') as f: for line in f: if 'line protocol' in line: interface = line.split()[0] elif 'Internet address' in line: ip_address = line.split()[-1] result[interface] = {} result[interface]['ip'] = ip_address elif 'MTU' in line: mtu = line.split()[-2] result[interface]['mtu'] = mtu print(result)
В этом случае, результатом будет такой словарь:
{'Ethernet0/0': {'ip': '192.168.100.2/24', 'mtu': '1500'}, 'Loopback0': {'ip': '2.2.2.2/32', 'mtu': '1514'}}