Книга: PyNEng
Назад: List, dict, set comprehensions
Дальше: Дополнительные материалы

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

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

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

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

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

В этом примере будет разбираться вывод команды 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'}} 
Назад: List, dict, set comprehensions
Дальше: Дополнительные материалы