Каков наиболее эффективный способ очистки нескольких одиночных и ветвей OID?

Я всегда нахожусь в войне между максимальной скоростью и минимальным потреблением ресурсов, поэтому моя цель — найти наилучшее сочетание скорости и потребления ресурсов.
Для каждого устройства snmp я хотел бы сканировать oids под несколькими ветками.
В каждой ветке есть динамическое количество oids, поэтому я не знаю, какие именно oids мне нужны, я просто знаю, что мне нужны все oids в ветке.
У меня есть некоторые устройства, которые поддерживают только SNMPv1, поэтому для этих устройств я написать код, совместимый с SNMPv1.
Для остальных устройств я использую SNMPv2.

SNMPv1

Допустим, у меня есть два OID, которые я хотел бы пройти по их branch('1.3.6.1.2.1.4' and '1.3.6.1.2.1.6').
Под OID ветки я подразумеваю все OID в ветке.
У меня есть следующий код:

cmdGen = cmdgen.AsynCommandGenerator()
cmdGen.asyncNextCmd(
  cmdgen.CommunityData('public', mpModel=1),
  cmdgen.UdpTransportTarget(('192.168.0.101', 161)),
  (str('1.3.6.1.2.1.4'),str('1.3.6.1.2.1.6'),),
  (__cbFun_Walk, (cmdgen.CommunityData('public', mpModel=1), cmdgen.UdpTransportTarget(('192.168.0.101', 161)))))
cmdGen.snmpEngine.transportDispatcher.runDispatcher()

Это работает хорошо, но единственная проблема заключается в том, что я могу остановить все обходы сразу, поэтому я не могу остановить каждый обход по отдельности, поэтому все обходы завершатся после окончания самого длинного обхода.
Очевидно, это неэффективно.
Я мог бы также написать 2 asyncNextCmd для OID ветки:

cmdGen = cmdgen.AsynCommandGenerator()
cmdGen.asyncNextCmd(
        cmdgen.CommunityData('public', mpModel=1),
        cmdgen.UdpTransportTarget(('192.168.0.101', 161)),
        (str('1.3.6.1.2.1.4'),),
        (__cbFun_Walk, (cmdgen.CommunityData('public', mpModel=1),
        cmdgen.UdpTransportTarget(('192.168.0.101', 161))))) 
cmdGen.asyncNextCmd(
        cmdgen.CommunityData('public', mpModel=1),
        cmdgen.UdpTransportTarget(('192.168.0.101', 161)),
        (str('1.3.6.1.2.1.6'),),
        (__cbFun_Walk, (cmdgen.CommunityData('public', mpModel=1),
        cmdgen.UdpTransportTarget(('192.168.0.101', 161)))))
cmdGen.snmpEngine.transportDispatcher.runDispatcher()

Я не слишком хорошо знаю SNMP, но предполагаю, что второй код имеет некоторые недостатки.
Например, на некоторых клиентах у меня есть сотни SNMP-устройств, поэтому я открыл asyncCmd для каждого сетевого устройства одновременно.
Это привело к большому количеству не отвечающих устройств и слишком высокой загрузке ЦП.

SNMPv2

Я также хотел бы попытаться понять, как работает объем и могу ли я использовать его, чтобы сделать мой код более эффективным.
Допустим, у меня есть 2 ветки, которые я хотел бы пройти.
1.3.6.1.2.1. 4.20 с 5 oids и 1.3.6.1.2.1.4.21 с 39 oids.
Я получаю все значения в обеих ветвях, но я также получаю больше значений, чем мне нужно.
Количество значений, которые я get всегда является ветвью с наибольшим количеством oids, умноженным на количество ветвей, которые у меня есть.
Например, ветка с наибольшим количеством oids имеет 39 oids, а количество ветвей равно 2, поэтому 39*2=78, это означает, что getBulk вернет 78 oids.
Я хочу, чтобы getBulk возвращал все oids ветвей для каждой ветки и ничего больше, поэтому в моем случае я хочу 44 oids (39+5 = 44).
Это мой код:

cmdGen = cmdgen.CommandGenerator()

errorIndication, errorStatus, errorIndex, varBindTable = cmdGen.bulkCmd(
  cmdgen.CommunityData('public'),
  cmdgen.UdpTransportTarget(('192.168.0.101', 161)),
  0, 1,
  '1.3.6.1.2.1.4.21', '1.3.6.1.2.1.4.20'
)

if errorIndication:
  print errorIndication
elif errorStatus:
  print '%s at %s\n' % (
    errorStatus.prettyPrint(),
    errorIndex and varBindTable[-1][int(errorIndex)-1] or '?'
  )
else:
  for varBindTableRow in varBindTable:
    for name, val in varBindTableRow:
      print str(name.prettyPrint()) + ' = ' + str(val.prettyPrint())

Итак, каков наиболее эффективный способ очистки OID нескольких ветвей как для SNMPv1, так и для SNMPv2?


person yuval    schedule 20.10.2015    source источник


Ответы (2)


Первое, что нужно понять, это то, что вы не можете указать SNMP-агенту вернуть вам именно «ветку». Такая операция не поддерживается в SNMP (на уровне протокола). Менеджер контролирует, какие OID запрашивать и когда останавливаться. Агент может вернуть точный один OID (GET) или следующий OID (GETNEXT/GETBULK) или приблизительное количество следующих OID одновременно (GETBULK).

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

Сказав это, я думаю, вы должны попытаться использовать GETBULK везде, где это возможно. Нацельтесь на максимально ожидаемое количество OID, чтобы получить их за одну операцию GETBULK. Но проверьте в своем коде, что вы действительно получаете все (агент может не возвращать запрошенное количество OID сразу).

Что касается API pysnmp, который вы используете, чтобы остановить его итерацию GETBULK/GETNEXT, вы должны вернуть True из своей функции обратного вызова. Вы можете проанализировать, какие OID вы получаете от агента, и вернуть True, когда они выходят за пределы интересующей вас ветки. Это прекратит поиск одной ветки, остальные продолжат работу.

Схема, в которой вы отправляете несколько параллельных запросов одному и тому же SNMP-агенту для разных OID, очевидно, намного менее эффективна.

person Ilya Etingof    schedule 20.10.2015
comment
Можете ли вы объяснить или показать, что вы подразумеваете под «Вы можете добавлять и удалять OID из запроса PDU на лету». Например, когда один OID исчерпан, вы просто вытаскиваете его из запроса PDU и продолжаете работу с остальными OID. - person yuval; 21.10.2015

Вы хотите bulkCmd, как описано в

http://pysnmp.sourceforge.net/examples/current/v3arch/oneliner/manager/cmdgen/getbulk-v2c.html

Он будет получать данные с сетевого устройства меньшими и большими порциями.

person k1eran    schedule 20.10.2015