pysnmp icon indicating copy to clipboard operation
pysnmp copied to clipboard

walkCmd not returning from loaded custom MIB

Open conradmcha opened this issue 7 months ago • 1 comments

Expected behavior

Code Block to Walk SNMP MIB

Consider the following block of code:

import asyncio
from pysnmp.hlapi.asyncio import *
from pysnmp.smi import builder, view, compiler, rfc1902

# Initialize the MIB builder
mib_builder = builder.MibBuilder()

# Add MIB sources, including the directory containing the converted MIB file
custom_mib_path = '/home/conrad/.pysnmp/mibs'
mib_sources = mib_builder.getMibSources() + (builder.DirMibSource(custom_mib_path),)
mib_builder.setMibSources(*mib_sources)

# Load custom MIB files
compiler.addMibCompiler(mib_builder, sources=['file://' + custom_mib_path])
mib_builder.loadModules('ELTEK-BC2000-DC-POWER-MIB-V2-12')

# Create MIB viewer
mib_view_controller = view.MibViewController(mib_builder)

async def snmp_walk_custom_mib(oid, ip, community='public'):
    result = []
    try:
        identity = ObjectIdentity('ELTEK-BC2000-DC-POWER-MIB-V2-12', oid).loadMibs('ELTEK-BC2000-DC-POWER-MIB-V2-12')
        identity = identity.resolveWithMib(mib_view_controller)
    except Exception as e:
        print(f"Error resolving OID {oid}: {e}")
        return []

    iterator = walkCmd(
        SnmpEngine(),
        CommunityData(community),
        UdpTransportTarget((ip, 161)),
        ContextData(),
        ObjectType(identity),
        lexicographicMode=False
    )
    async for error_indication, error_status, error_index, var_binds in iterator:
        if error_indication:
            print(f"Error indication: {error_indication}")
            break
        elif error_status:
            print(f"Error status: {error_status.prettyPrint()} at {error_index}")
            break
        else:
            for var_bind in var_binds:
                oid, value = var_bind
                try:
                    oid_label = oid.resolveWithMib(mib_view_controller)
                    oid_number = oid.getOid()._value
                    oid_label = mib_view_controller.getNodeName((oid_number))
                except Exception as e:
                    oid_label = 'Unknown'
                    print(f"Error getting label for OID {oid}: {e}")
                result.append(f'{oid.prettyPrint()} = {value.prettyPrint()} ({oid_label})')
    return result

async def main():
    value1 = await snmp_walk_custom_mib('vpwrPanelModuleSerNum', '10.115.0.10')
    print("\n".join(value1))

# Run the main function
asyncio.run(main())

Expected Output

The code should return the correct MIB from ELTEK-BC2000-DC-POWER-MIB-V2-12: ELTEK-BC2000-DC-POWER-MIB-V2-12::vpwrPanelModuleSerNum.0 = 183886010176

Actual Output

However, it returns: SNMPv2-SMI::enterprises.13858.2.4.2.1.3.5.1 = 183886010176

Actual behavior

Issue

The returned object from the walkCmd doesn't use the right MIB but defaults to the SNMPv2-SMI MIB file:

SNMPv2-SMI::enterprises.13858.2.4.2.1.3.5.1 = 183886010176

I believe this is a similar issue than this bug from "etingof" repo: https://github.com/etingof/pysnmp/issues/273

Detailed steps

Issue

From the debug log, we can see that my ELTEK-BC2000-DC-POWER-MIB-V2-12 mib file is loaded properly. Also the mib_view_controller object contains my mib file as well.

Note

This issue only happens for the walkCmd, it works as intended with the getCmd.

The workaround that I've found is to reinstantiate the object based on the returned OID number with the correct MIB:

  for device_result in completed_tasks:
       task_results = []
       for oid_result in device_result:
           if oid_result is not None:
               for varBind in oid_result:
                   if varBind[0]._ObjectIdentity__modName == "ELTEK-BC2000-DC-POWER-MIB-V2-12":
                       full_oid = varBind[0].prettyPrint()
                       key = full_oid.split('::')[-1]
                       value = varBind[1].prettyPrint()                                    
                       task_results.append({key: value})
                       all_keys.add(key)
                   else:
                       full_oid = varBind[0].getOid()._value
                       resolved_varBind = mibViewController.getNodeName((full_oid))
                       last_value = resolved_varBind[1][-1]
                       resolved_varBind_2_str = '.' + '.'.join(map(str, resolved_varBind[2]))
                       key = last_value + resolved_varBind_2_str
                       value = varBind[1]._value
                       if isinstance(value, bytes):
                           decoded_value = value.decode('utf-8')
                       else:
                           decoded_value = value
                       task_results.append({key: decoded_value})
                       all_keys.add(key)

Partial log

...

2024-07-25 16:44:27,548 DEBUG exportSymbols: symbol ELTEK-BC2000-DC-POWER-MIB-V2-12::vpwrDcPowerUnkModule 2024-07-25 16:44:27,548 DEBUG exportSymbols: symbol ELTEK-BC2000-DC-POWER-MIB-V2-12::thirdPartyProduct 2024-07-25 16:44:27,548 DEBUG loadModule: loaded /home/conrad/.pyenv/versions/3.12.0/lib/python3.12/site-packages/pysnmp/smi/mibs/ELTEK-BC2000-DC-POWER-MIB-V2-12/home/conrad/.pyenv/versions/3.12.0/lib/python3.12/site-packages/pysnmp/smi/mibs/ELTEK-BC2000-DC-POWER-MIB-V2-12.py 2024-07-25 16:44:27,548 DEBUG MIB loaded successfully ...

Python package information

pysnmp-lextudio 6.2.1

Operating system information

Ubuntu 22.04.4 LTS

Python information

3.12.0

(Optional) Contents of your test script

No response

Relevant log output

No response

conradmcha avatar Jul 26 '24 21:07 conradmcha