EasySNMP: различные выходные данные, конвертирующие OCTETSTR в Hex
Когда я использую функцию encode
чтобы преобразовать OCTET в шестнадцатеричный код, некоторые символы добавляются, когда это не должно
Пример:
Linux: snmpwalk -t 5 -v2c -c public 192.168.10.150 iso.3.6.1.4.1.25355.3.2.6.4.2.5.1.7.1.1.1
Выход SNMPWALK: iso.3.6.1.4.1.25355.3.2.6.4.2.5.1.7.1.1.1.144 = Hex-STRING: AC 84 C6 5F 95 EF B0 4E 26 8B 1C C5 C0 4A 00 AE
Код:
session = Session(hostname='192.168.10.150', community='public', version=2)
description = session.walk('iso.3.6.1.4.1.25355.3.2.6.4.2.5.1.7.1.1.1')
for item in description:
print '{oid}.{oid_index} {snmp_type} = {value}'.format(
oid=item.oid,
oid_index=item.oid_index,
snmp_type=item.snmp_type,
value=item.value.decode("hex"))
Выход EasySNMP: iso.3.6.1.4.1.25355.3.2.6.4.2.5.1.7.1.1.1.144. OCTETSTR = c2acc284c3865fc295c3afc2b04e26c28b1cc385c3804a00c2ae59c293c2b04e26c28b4ec2ad
Используются некоторые OID, но результат отличается от того, что я ожидал. Это правильный способ использовать easysnmp?
3 ответа
Хорошо. Мы можем сказать следующее:
- Запросы идентичны от обоих менеджеров
- Ответы на оба запроса идентичны
Это означает, что либо выход Net-SNMP "искажен"/ преобразован, либо выход EasySNMP является.
К сожалению, захваты пакетов не показывают взаимодействия, которые были описаны в оригинальной версии поста, поэтому мы не можем сразу сказать, какой менеджер виноват. Тем не менее, можно сделать вывод на основе значений, которые мы видим.
Вывод вашего Python-скрипта - почти расширенный вывод snmpwalk:
Net-SNMP:
- AC 84 C6 5F 95 EF B0 4E 26 8B 1C C5 C0 4A 00 AE
Скрипт Python:
- C2 AC C2 84 C3 86 5F C2 95 C3 AF C2 B0 4E 26 C2 8B 1C C3 85 C3 80 4A 00 C2 AE 59 C2 93 C2 B0 4E 26 C2 8B 4E C2 AD
Итак, почему были добавлены дополнительные байты, и почему некоторые байты были потеряны? Это пахнет как перекодировка исходных данных, верно?
Мы можем наблюдать, что байт C2 много всплывает. Что это такое? Это "расширенный ASCII" (на самом деле такого нет, но во многих кодовых страницах) для персонажа. Ага, красный флаг. Почему это красный флаг? Потому что это обычно свидетельствует о неверной интерпретации UTF-8. (Я мог бы объяснить более подробно, почему это так, но я позволю вам исследовать кодировки Unicode отдельно, если хотите.)
Итак, используя онлайн-инструмент или два, давайте расшифруем этот второй поток байтов как UTF-8 и посмотрим, какие логические кодовые точки мы получаем:
U + AC U + 84 U + C6 U + 5F U + 95 U + EF U + B0 U + 4E U + 26 U + 8B U + 1C U + C5 U + C0 U + 4A U + AE U + 59 U + 93 U + B0 U + 4E U + 26 U + 8B U + 4E U + AD
Эй, это выглядит знакомо! (Опять же, я выделил биты, которые соответствуют выходу Net-SNMP.) U+00 отсутствует (предположительно потому, что это "нулевой" байт, как в ASCII), и в конце все еще есть куча шума (если Вы заинтересованы, они отображаются так: "Y" °N&‹N"), но теперь мы можем хотя бы увидеть, что происходит: ваш исходный поток байтов был перекодирован в виде строки UTF-8. Действительно, кодировка Python 3 по умолчанию - UTF-8.
Причина, по которой байты типа C6 полностью исчезли, заключается в том, что они выходят за пределы диапазона ASCII, поэтому отображение "нечисто" в Unicode. Оказывается, ASCII C6 - это U + 00C6, который представлен в UTF-8 C3 86, так что теперь мы знаем, откуда берутся несжатые байты.
Итак, EasySNMP обрабатывает ваш результат обхода как строку, а не как непрозрачную последовательность байтов, и в результате Python искажает его. Затем, когда вы написали .encode("hex")
Вы получили шестнадцатеричное представление этой новой фальсифицированной строки UTF-8.
Это, вероятно, не должно происходить. Ответ SNMP четко обозначен как "OctetString", а спецификация говорит нам, что "The OCTET STRING
type представляет собой произвольные двоичные или текстовые данные ". MIB (который вы, по-видимому, не используете или, по крайней мере, не предоставил) для агента, с которым вы общаетесь, может предоставить дополнительную информацию о кодировании; при отсутствии этой информации существует нет способа узнать наверняка, как OCTET STRING
должен отображаться. Например, эта ошибка Net-SNMP обсуждает буквальное предположение, когда это применимо.
Во всяком случае, все очень интересно, но что мы можем с этим поделать?
Документы EasySNMP довольно тонкие, но мы можем немного покопаться в исходном коде (и в процессе обнаружим, что EasySNMP на самом деле является просто оболочкой Python вокруг Net-SNMP!) И в списке проблем EasySNMP, куда он превращается. кто-то жаловался на это раньше.
Опять же, что мы можем с этим поделать?
Хм, я не уверен, что мы можем с этим многое сделать. В настоящее время это недостаток EasySNMP. Вещи Unicodised (либо самим EasySNMP, путем преобразования в строки Python, либо модулем Compat Net-SNMP, описанным ранее), даже если они не должны быть.
Тем не менее, этот глава предложил обходной путь, который вы можете попробовать:
session = Session(
hostname='192.168.10.150',
community='public',
version=2,
use_sprint_value=False
)
Этот новый последний аргумент должен отключить преобразование значения. Тем не менее, я не уверен, потому что согласно документам это уже False
по умолчанию.
В любом случае, я думаю, что помимо попыток сделать это лучше всего добавить свой вес в соответствующий отчет (ы) о проблемах и оказать давление на разработчика, чтобы он предложил исправление. Сожалею.
Я полагаю, что у вас та же проблема, что и у меня. Я также недавно рассказывал об этом на Github . Compat.py в библиотеке кодирует данные как latin-1 вместо utf-8.
Несмотря на это, на основе вашего исходного примера кода должно работать следующее:
session = Session(hostname='192.168.10.150', community='public', version=2)
description = session.walk('iso.3.6.1.4.1.25355.3.2.6.4.2.5.1.7.1.1.1')
for item in description:
print '{oid}.{oid_index} {snmp_type} = {value}'.format(
oid=item.oid,
oid_index=item.oid_index,
snmp_type=item.snmp_type,
value=item.value.encode("latin-1").hex())
Это даст вам шестнадцатеричный код в виде непрерывной строки. Если вы предпочитаете, чтобы это был байтовый объект, просто оставьте «.hex()» в значении.
Вы также можете сделать несколько сравнений, если хотите убедиться, что знаете шестнадцатеричный код, например:
from easysnmp import snmp_get
OID = iso.0.8802.1.1.2.1.4.1.1.7.2520.9.1
foo = snmp_get(OID, hostname='192.0.2.1', community='public', version=2).value.encode('latin-1')
foo == bytes.fromhex("312f3300")
В этом примере мы предполагаем, что OID возвращает шестнадцатеричное значение 312F 3300 для сравнения.
Чтобы получить правильное шестнадцатеричное значение, я использую это.
def toRaw(s):
x = [ord(i) for i in list(s)]
return bytearray(x)
print(toRaw(item.value).hex().upper())