Работа со словарями

При обработке вывода команд или конфигурации часто надо будет записать итоговые данные в словарь.

Не всегда очевидно как обрабатывать вывод команд и каким образом в целом подходить к разбору вывода на части. В этом подразделе рассматриваются несколько примеров, с возрастающим уровнем сложности.

Разбор вывода столбцами

В этом примере будет разбираться вывод команды 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'}}