SNMP reading from an OID with three libraries gives different execution times

2.2k Views Asked by At

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?

1

There are 1 best solutions below

5
Ilya Etingof On

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())