0

I already used easysnmp to read SNMP OIDs, but I chose pysnmp library now because easysnmp did not support asyncio archtecture in Python3.

The considered issue is that,pysnmp is too slower than other libraries:

pysnmp:

from pysnmp.hlapi import *
import time

t = time.time()
iterator = getCmd(SnmpEngine(),
                  CommunityData('public'),
                  UdpTransportTarget(('192.168.1.120', 161)),
                  ContextData(),
                  ObjectType(ObjectIdentity("1.3.6.1.2.1.33.1.2.7.0")))

errorIndication, errorStatus, errorIndex, varBinds = next(iterator)

if errorIndication:  # SNMP engine errors
    print(errorIndication)
else:
    if errorStatus:  # SNMP agent errors
        print('%s at %s' % (
            errorStatus.prettyPrint(),
            varBinds[int(errorIndex)-1] if errorIndex else '?'))
    else:
        for varBind in varBinds:  # SNMP response contents
            print(' = '.join([x.prettyPrint() for x in varBind]))


print(time.time() - t, 's') 

Out:

SNMPv2-SMI::mib-2.33.1.2.7.0 = 21
0.15317177772521973 s

easysnmp

from easysnmp import snmp_get
import time

if __name__ == '__main__':
    t = time.time()
    response = snmp_get(
        '1.3.6.1.2.1.33.1.2.7.0', hostname='192.168.1.120',
        community='public', version=1
    )
    print(response.value)
    print(time.time() - t, 's')

Out:

21
0.0063724517822265625 s

gosnmp

func elapsed(what string) func() {
    start := time.Now()
    fmt.Println("start")
    return func() {
        fmt.Printf("%s took %v\n", what, time.Since(start))
    }
}

func snmpRead() {
    g.Default.Target = "192.168.1.120"
    err := g.Default.Connect()
    if err != nil {
        log.Fatalf("Connect() err: %v", err)
    }
    defer g.Default.Conn.Close()

    oids := []string{"1.3.6.1.2.1.33.1.2.7.0"}
    result, err2 := g.Default.Get(oids) // Get() accepts up to g.MAX_OIDS
    if err2 != nil {
        log.Fatalf("Get() err: %v", err2)
    }

    for i, variable := range result.Variables {
        fmt.Printf("%d: oid: %s ", i, variable.Name)

        switch variable.Type {
        case g.OctetString:
            fmt.Printf("string: %s\n", string(variable.Value.([]byte)))
        default:
            fmt.Printf("number: %d\n", g.ToBigInt(variable.Value))
        }
    }
}

func main() {
    defer elapsed("snmp")()
    snmpRead()
}

Out:

start
0: oid: .1.3.6.1.2.1.33.1.2.7.0 number: 21
snmp took 3.668148ms

30x faster than pysnmp


I need to the asynchronous performance which go-routine populated asyncio in Python3.

So, is this means that I should migrate from pysnmp to gosnmp?

Benyamin Jafari
  • 27,880
  • 26
  • 135
  • 150
  • Different libraries might have different implementations. It is too broad to discuss such here. – Lex Li Feb 25 '19 at 14:29
  • Consider the times for your test: 1) time to run the request code on your system, 2) transmitting the request over the network, 3) running the code to handle the request on the agent, 4) response on the network, 5) code on your system to handle the response. Then calculate the times of each, and you'll realize the amount of time for 1 and 5 are miniscule compared to everything else. Why the difference in the 3 tests? I would not trust anything run less than 1000 times, and then run it 3 times to remove the variation in 2), 3) and 4). Then re-phrase your requirements. – Gambit Support Feb 25 '19 at 17:49
  • Somebody deleted my answer, so maybe this comment will stay: the obvious difference is prettyPrint. Likely the MIB lookup is slowing test #1 down. – Gambit Support Feb 26 '19 at 14:45
  • @GambitSupport Thanks for the response, ok I'll check this benchmark without any print – Benyamin Jafari Feb 26 '19 at 18:26

1 Answers1

0

Keep in mind that first pysnmp call might take much more time relative to the subsequent calls. Because of the lazy imports, indexing, possible MIB compilation etc.

So if your use-case is to issue many SNMP queries from the same process, I'd advise to take that into account when measuring the time taken.

The other thing is that the latest (unreleased) pysnmp has introduced asyncio bindings over low-level SNMP routines i.e. excluding SNMP engine and all the heavy machinery it involves.

The pysnmp.hlapi.v1arch.asyncio API is intended to be very similar to pysnmp.hlapi.v3arch.asyncio from its signature viewpoint, however it should be faster at the expense of no SNMPv3 support. MIB support is still in place, if you need it.

When you import just pysnmp.hlapi.asyncio, you will effectively get pysnmp.hlapi.v3arch.asyncio, so to get on v1arch you need to import it explicitly.

For example the script below (running under GitHub master pysnmp) might be faster:

import asyncio
from pysnmp.hlapi.v1arch.asyncio import *


@asyncio.coroutine
def run():
    snmpDispatcher = SnmpDispatcher()

    iterator = getCmd(
        snmpDispatcher,
        CommunityData('public'),
        UdpTransportTarget(('192.168.1.120', 161)),
        ('1.3.6.1.2.1.33.1.2.7.0', None)
    )

    errorIndication, errorStatus, errorIndex, varBinds = yield from iterator

    if errorIndication:
        print(errorIndication)

    elif errorStatus:
        print('%s at %s' % (
            errorStatus.prettyPrint(),
            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'
        )
              )
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))

    snmpDispatcher.transportDispatcher.closeDispatcher()


asyncio.get_event_loop().run_until_complete(run())
Ilya Etingof
  • 5,440
  • 1
  • 17
  • 21