PySnmp查询可达SNMP代理失败,但snmpget命令行执行成功求助
问题:PySnmp调用SNMP代理时报TypeError参数不匹配错误
我需要实现一个监控SNMP代理的服务端,基于Python编写了基础应用。在同一Docker网络中部署了polinux/snmpd镜像的SNMP代理(固定IP:10.5.0.5),在服务端容器中执行snmpget命令可成功获取目标OID,但通过PySnmp编程调用时失败,核心报错为TypeError: decodeMessageVersion.<locals>.<lambda>() takes 3 positional arguments but 4 were given。
Docker Compose配置
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
snmpget执行成功结果
$ 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#
Python代码片段
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
报错信息
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
问题原因与解决方案
原因
这个错误是PySnmp与PyASN1版本不兼容导致的:PyASN1 0.5.x版本对解码函数的API做了修改,增加了一个参数,但旧版本的PySnmp(如4.4.x系列)中的decodeMessageVersion函数仍使用旧的参数调用方式,从而引发参数不匹配的TypeError。
解决方案
有两种可行的修复方式:
- 固定PyASN1到兼容版本:在项目依赖中指定
pyasn1<0.5.0,比如在requirements.txt中添加:pyasn1>=0.4.8,<0.5.0 - 升级PySnmp到兼容版本:将PySnmp升级到支持PyASN1 0.5.x的版本,比如升级到
pysnmp>=4.4.12(需确认版本兼容性)。
验证
修改依赖版本后,重新构建Docker镜像并启动服务,再次测试PySnmp的SNMP调用,确认是否能正常获取OID数据。
内容的提问来源于stack exchange,提问作者Balint Laszlo
相关产品推荐
相关产品推荐

