PySnmp query not working for reachable target but command line 'snmpget' succeeds

Question:

I need an SNMP server that can monitor an SNMP agent. For this purpose, I wrote a basic Python application, and I run an SNMP agent (polinux/snmpd image based), on the same network, the agent has a fixed IP address. When I run the SNMP get query from the server container, I get the desired OIB, but when I want to do the same programmatically using PySnmp, it just doesn’t work. I have tried in a lots of ways but without success.

Do you have any idea?

My docker compose file:

services:
  spm-health-service:
    build:
      context: .
      dockerfile: docker/Dockerfile
    restart: unless-stopped
    env_file:
      - .env
    ports:
      - "4500:4500"
    depends_on:
      - scs-server
      - network-device-identifier

  spm:
    image: polinux/snmpd
    restart: unless-stopped
    privileged: true
    ports:
      - "161:161/udp"
    volumes:
      - ./resources/snmpd-conf/snmpd.conf:/etc/snmp/snmpd.conf
      - ./resources/spm-mib/:/usr/local/share/snmp/mibs/
    networks:
      default:
        ipv4_address: 10.5.0.5

networks:
  default:
    driver: bridge
    ipam:
      config:
        - subnet: 10.5.0.0/16
          gateway: 10.5.0.1

My command and result when I enter bash shell of "spm-health-service":

$ winpty docker exec -it 627 bash
root@627d241920e3:/app# snmpget -v2c -t 10 -c public 10.5.0.5 .1.3.6.1.2.1.1.3.0
Created directory: /var/lib/snmp/cert_indexes
iso.3.6.1.2.1.1.3.0 = Timeticks: (21437) 0:03:34.37
root@627d241920e3:/app#

My Python code:

    def get_system_uptime(self) -> Union[str, None]:
        """Get system uptime"""
        try:
            result = snmp_get(self.ip, [SYS_UP_TIME], self.snmp_ro_credential)
        except SNMPError as e:
            logging.error(e.message)

            return None

        return result.get(SYS_UP_TIME, None)

- - - - - - -

def snmp_get(
    target: str,
    oids: list[str],
    credentials: CommunityData = SNMP_RO_CREDENTIAL,
    port: int = SNMP_DEFAULT_PORT,
) -> dict:
    """SNMP get"""
    handler = getCmd(
        SNMP_ENGINE,
        credentials,
        UdpTransportTarget((target, port), timeout=5, retries=10),
        SNMP_CONTEXT,
        *construct_object_types(oids),
    )
    return fetch(handler)[0]

- - - - - - -

def fetch(fetch_handler, count: int = -1) -> list[dict]:
    """Fetch"""

    def convert(_var_binds):
        """Convert"""
        return {str(var_bind[0]): var_bind[1] for var_bind in _var_binds}

    result = []
    while True:
        if count == 0:
            break

        count -= 1
        try:
            error_indication, error_status, error_index, var_binds = next(fetch_handler)
            if error_indication:
                raise SNMPError(f"SNMP error: {error_indication}")
            if error_status:
                raise SNMPError(
                    f"SNMP error: {error_status.prettyPrint()} at 
                        {error_index and var_binds[int(error_index) - 1][0] or '?'}",
                )

            items = convert(var_binds)
            result.append(items)
        except StopIteration:
            break

    return result

- - - - - - - - - - 

The exception I get:

spm-health-service-spm-health-service-1         | 2022-11-14 10:53:58,729 [INFO]: [get_spms_from_network_mapping] - line 32 | Query for 10.5.0.5
spm-health-service-spm-health-service-1         | 2022-11-14 10:53:58,792 [ERROR]: [get_spms_from_network_mapping] - line 36 | poll error: Traceback (most recent call last):
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pysnmp/carrier/asyncore/dispatch.py", line 45, in runDispatcher
spm-health-service-spm-health-service-1         |     loop(timeout or self.getTimerResolution(),
spm-health-service-spm-health-service-1         | ;  File "/usr/local/lib/python3.11/asyncore.py", line 212, in loop
spm-health-service-spm-health-service-1         |     poll_fun(timeout, map)
spm-health-service-spm-health-service-1         | ;  File "/usr/local/lib/python3.11/asyncore.py", line 193, in poll2
spm-health-service-spm-health-service-1         |     readwrite(obj, flags)
spm-health-service-spm-health-service-1         | ;  File "/usr/local/lib/python3.11/asyncore.py", line 128, in readwrite
spm-health-service-spm-health-service-1         |     obj.handle_error()
spm-health-service-spm-health-service-1         | ;  File "/usr/local/lib/python3.11/asyncore.py", line 113, in readwrite
spm-health-service-spm-health-service-1         |     obj.handle_read_event()
spm-health-service-spm-health-service-1         | ;  File "/usr/local/lib/python3.11/asyncore.py", line 425, in handle_read_event
spm-health-service-spm-health-service-1         |     self.handle_read()
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pysnmp/carrier/asyncore/dgram/base.py", line 170, in handle_read
spm-health-service-spm-health-service-1         |     self._cbFun(self, transportAddress, incomingMessage)
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pysnmp/carrier/base.py", line 84, in _cbFun
spm-health-service-spm-health-service-1         |     self.__recvCallables[recvId](
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pysnmp/entity/engine.py", line 151, in __receiveMessageCbFun
spm-health-service-spm-health-service-1         |     self.msgAndPduDsp.receiveMessage(
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pysnmp/proto/rfc3412.py", line 291, in receiveMessage
spm-health-service-spm-health-service-1         |     msgVersion = verdec.decodeMessageVersion(wholeMsg)
spm-health-service-spm-health-service-1         |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pysnmp/proto/api/verdec.py", line 15, in decodeMessageVersion
spm-health-service-spm-health-service-1         |     seq, wholeMsg = decoder.decode(
spm-health-service-spm-health-service-1         |                     ^^^^^^^^^^^^^^^
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pyasn1/codec/ber/decoder.py", line 2003, in __call__
spm-health-service-spm-health-service-1         |     for asn1Object in streamingDecoder:
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pyasn1/codec/ber/decoder.py", line 1918, in __iter__
spm-health-service-spm-health-service-1         |     for asn1Object in self._singleItemDecoder(
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pyasn1/codec/ber/decoder.py", line 1778, in __call__
spm-health-service-spm-health-service-1         |     for value in concreteDecoder.valueDecoder(
spm-health-service-spm-health-service-1         | ;  File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/pyasn1/codec/ber/decoder.py", line 654, in valueDecoder
spm-health-service-spm-health-service-1         |     for chunk in substrateFun(asn1Object, substrate, length, options):
spm-health-service-spm-health-service-1         |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
spm-health-service-spm-health-service-1         | ;TypeError: decodeMessageVersion.<locals>.<lambda>() takes 3 positional arguments but 4 were given
spm-health-service-spm-health-service-1         | caused by <class 'TypeError'>: decodeMessageVersion.<locals>.<lambda>() takes 3 positional arguments but 4 were given
Asked By: Balint Laszlo

||

Answers:

The issue was with Pipfile, the logs indicated some dependency issues and conflicts. I do not remember why but at some point the allow_prereleases flag was set to true in the file. When I set it to false and recreated the Pipfile.lock, the error gone away.

Answered By: Balint Laszlo