def set(self, *args): """ Perform SNMP GET request :param oid: string or list of oids :returns: eigther result scalar or dict of name -> value """ @tornado.gen.coroutine def run(): try: self.result = yield snmp_set( address=self.script.credentials["address"], varbinds=varbinds, community=str(self.script.credentials["snmp_rw"]), tos=self.script.tos, ioloop=self.get_ioloop(), udp_socket=self.get_socket(), ) except SNMPError as e: if e.code == TIMED_OUT: raise self.TimeOutError() else: raise if len(args) == 1: varbinds = args elif len(args) == 2: varbinds = [(args[0], args[1])] else: raise ValueError("Invalid varbinds") if "snmp_ro" not in self.script.credentials: raise SNMPError(code=ERR_SNMP_BAD_COMMUNITY) self.get_ioloop().run_sync(run) r, self.result = self.result, None return r
def count(self, oid, filter=None, version=None): """ Iterate MIB subtree and count matching instances :param oid: OID :param filter: Callable accepting oid and value and returning boolean """ async def run(): try: r = await snmp_count( address=self.script.credentials["address"], oid=oid, community=str(self.script.credentials["snmp_ro"]), bulk=self.script.has_snmp_bulk(), filter=filter, tos=self.script.tos, udp_socket=self.get_socket(), version=version, rate_limit=self.rate_limit, ) return r except SNMPError as e: if e.code == TIMED_OUT: raise self.TimeOutError() else: raise if "snmp_ro" not in self.script.credentials: raise SNMPError(code=ERR_SNMP_BAD_COMMUNITY) version = self._get_snmp_version(version) return run_sync(run, close_all=False)
def set(self, *args): """ Perform SNMP GET request :param oid: string or list of oids :returns: eigther result scalar or dict of name -> value """ async def run(): try: r = await snmp_set( address=self.script.credentials["address"], varbinds=varbinds, community=str(self.script.credentials["snmp_rw"]), tos=self.script.tos, udp_socket=self.get_socket(), rate_limit=self.rate_limit, ) return r except SNMPError as e: if e.code == TIMED_OUT: raise self.TimeOutError() else: raise if len(args) == 1: varbinds = args elif len(args) == 2: varbinds = [(args[0], args[1])] else: raise ValueError("Invalid varbinds") if "snmp_ro" not in self.script.credentials: raise SNMPError(code=ERR_SNMP_BAD_COMMUNITY) return run_sync(run, close_all=False)
async def snmp_set( address, varbinds, port=161, community="public", version=SNMP_v2c, timeout=10, tos=None, udp_socket=None, rate_limit: Optional[AsyncRateLimit] = None, ): """ Perform SNMP set request and returns Future to be used inside async coroutine """ logger.debug("[%s] SNMP SET %s", address, varbinds) # Send GET PDU pdu = set_pdu(community=community, varbinds=varbinds, version=version) if rate_limit: await rate_limit.wait() # Wait for result with UDPSocketContext(udp_socket, tos=tos) as sock: try: data, addr = await asyncio.wait_for( sock.send_and_receive(pdu, (address, port)), timeout) except asyncio.TimeoutError: raise SNMPError(code=TIMED_OUT, oid=varbinds[0][0]) except socket.gaierror as e: logger.debug("[%s] Cannot resolve address: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=varbinds[0][0]) except OSError as e: logger.debug("[%s] Socket error: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=varbinds[0][0]) try: resp = parse_get_response(data) except ValueError: raise SNMPError(code=BER_ERROR, oid=varbinds[0][0]) if resp.error_status != NO_ERROR: oid = None if resp.error_index and resp.varbinds: oid = resp.varbinds[resp.error_index - 1][0] logger.debug("[%s] SNMP error: %s %s", address, oid, resp.error_status) raise SNMPError(code=resp.error_status, oid=oid) else: logger.debug("[%s] SET result: OK", address) return True
def getnext( self, oid, community_suffix=None, filter=None, cached=False, only_first=False, bulk=None, max_repetitions=None, version=None, max_retries=0, timeout=10, raw_varbinds=False, display_hints=None, ): async def run(): try: r = await snmp_getnext( address=self.script.credentials["address"], oid=oid, community=str(self.script.credentials["snmp_ro"]), bulk=self.script.has_snmp_bulk() if bulk is None else bulk, max_repetitions=max_repetitions, filter=filter, only_first=only_first, tos=self.script.tos, udp_socket=self.get_socket(), version=version, max_retries=max_retries, timeout=timeout, raw_varbinds=raw_varbinds, display_hints=display_hints, response_parser=self.script.profile. get_snmp_response_parser(self.script), rate_limit=self.rate_limit, ) return r except SNMPError as e: if e.code == TIMED_OUT: raise self.TimeOutError() else: raise if "snmp_ro" not in self.script.credentials: raise SNMPError(code=ERR_SNMP_BAD_COMMUNITY) if display_hints is None: display_hints = self._get_display_hints() version = self._get_snmp_version(version) return run_sync(run, close_all=False)
def get(self, oids, cached=False, version=None, raw_varbinds=False, display_hints=None): """ Perform SNMP GET request :param oid: string or list of oids :param cached: True if get results can be cached during session :param raw_varbinds: Return value in BER encoding :param display_hints: Dict of oid -> render_function. See BaseProfile.snmp_display_hints for details :returns: eigther result scalar or dict of name -> value """ async def run(): try: r = await snmp_get( address=self.script.credentials["address"], oids=oids, community=str(self.script.credentials["snmp_ro"]), tos=self.script.tos, udp_socket=self.get_socket(), version=version, raw_varbinds=raw_varbinds, display_hints=display_hints, response_parser=self.script.profile. get_snmp_response_parser(self.script), rate_limit=self.rate_limit, ) self.timeouts = self.timeouts_limit return r except SNMPError as e: if e.code == TIMED_OUT: if self.timeouts_limit: self.timeouts -= 1 if not self.timeouts: raise self.FatalTimeoutError() raise self.TimeOutError() else: raise if "snmp_ro" not in self.script.credentials: raise SNMPError(code=ERR_SNMP_BAD_COMMUNITY) if display_hints is None: display_hints = self._get_display_hints() version = self._get_snmp_version(version) return run_sync(run, close_all=False)
def getnext( self, oid, community_suffix=None, filter=None, cached=False, only_first=False, bulk=None, max_repetitions=None, version=None, max_retries=0, timeout=10, raw_varbinds=False, ): @tornado.gen.coroutine def run(): try: self.result = yield snmp_getnext( address=self.script.credentials["address"], oid=oid, community=str(self.script.credentials["snmp_ro"]), bulk=self.script.has_snmp_bulk() if bulk is None else bulk, max_repetitions=max_repetitions, filter=filter, only_first=only_first, tos=self.script.tos, ioloop=self.get_ioloop(), udp_socket=self.get_socket(), version=version, max_retries=max_retries, timeout=timeout, raw_varbinds=raw_varbinds, ) except SNMPError as e: if e.code == TIMED_OUT: raise self.TimeOutError() else: raise if "snmp_ro" not in self.script.credentials: raise SNMPError(code=ERR_SNMP_BAD_COMMUNITY) version = self._get_snmp_version(version) self.get_ioloop().run_sync(run) r, self.result = self.result, None return r
def get(self, oids, cached=False, version=None, raw_varbinds=False): """ Perform SNMP GET request :param oid: string or list of oids :param cached: True if get results can be cached during session :param raw_varbinds: Return value in BER encoding :returns: eigther result scalar or dict of name -> value """ @tornado.gen.coroutine def run(): try: self.result = yield snmp_get( address=self.script.credentials["address"], oids=oids, community=str(self.script.credentials["snmp_ro"]), tos=self.script.tos, ioloop=self.get_ioloop(), udp_socket=self.get_socket(), version=version, raw_varbinds=raw_varbinds, ) self.timeouts = self.timeouts_limit except SNMPError as e: if e.code == TIMED_OUT: if self.timeouts_limit: self.timeouts -= 1 if not self.timeouts: raise self.FatalTimeoutError() raise self.TimeOutError() else: raise if "snmp_ro" not in self.script.credentials: raise SNMPError(code=ERR_SNMP_BAD_COMMUNITY) version = self._get_snmp_version(version) self.get_ioloop().run_sync(run) r, self.result = self.result, None return r
async def snmp_get( address, oids, port=161, community="public", version=SNMP_v2c, timeout=10, tos=None, udp_socket: Optional[UDPSocket] = None, raw_varbinds=False, display_hints=None, response_parser: Optional[_ResponseParser] = None, rate_limit: Optional[AsyncRateLimit] = None, ): """ Perform SNMP get request and returns Future to be used inside async coroutine """ oid_map = {} if isinstance(oids, str): oids = [oids] elif isinstance(oids, dict): oid_map = {oids[k]: k for k in oids} oids = list(oids.values()) else: raise ValueError("oids must be either string or dict") logger.debug("[%s] SNMP GET %s", address, oids) parser = _get_parser(response_parser, raw_varbinds) # Send GET PDU pdu = get_pdu(community=community, oids=oids, version=version) if rate_limit: await rate_limit.wait() with UDPSocketContext(udp_socket, tos=tos) as sock: try: data, addr = await asyncio.wait_for( sock.send_and_receive(pdu, (address, port)), timeout) except asyncio.TimeoutError: raise SNMPError(code=TIMED_OUT, oid=oids[0]) except socket.gaierror as e: logger.debug("[%s] Cannot resolve address: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oids[0]) except OSError as e: logger.debug("[%s] Socket error: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oids[0]) try: resp = parser(data, display_hints) except ValueError: # Broken response raise SNMPError(code=BER_ERROR, oid=oids[0]) if resp.error_status == NO_ERROR: # Success if oid_map: result = {} for k, v in resp.varbinds: if k in oid_map: result[oid_map[k]] = v else: logger.error("[%s] Invalid oid %s returned in reply", address, k) else: result = resp.varbinds[0][1] logger.debug("[%s] GET result: %r", address, result) return result elif resp.error_status == NO_SUCH_NAME and len(oids) > 1: # One or more invalid oids b_idx = resp.error_index - 1 logger.debug("[%s] Invalid oid %s detected, trying to exclude", address, resp.varbinds[b_idx][0]) result = {} oid_parts = [] if b_idx: # Oids before b_idx are probable correct oid_parts += [[vb[0] for vb in resp.varbinds[:b_idx]]] if b_idx < len(resp.varbinds) - 1: # Some oids after b_idx may be correct oid_parts += [[vb[0] for vb in resp.varbinds[b_idx + 1:]]] for new_oids in oid_parts: try: new_result = await snmp_get( address=address, oids={k: k for k in new_oids}, port=port, community=community, version=version, timeout=timeout, tos=tos, udp_socket=sock, ) except SNMPError as e: if e.code == NO_SUCH_NAME and len(new_oids) == 1: # Ignore NO_SUCH_VALUE for last oid in list new_result = {} else: raise for k in new_result: if k in oid_map: result[oid_map[k]] = new_result[k] else: logger.info("[%s] Invalid oid %s returned in reply", address, k) if result: logger.debug("[%s] GET result: %r", address, result) return result else: # All oids excluded as broken logger.debug("[%s] All oids are broken", address) raise SNMPError(code=NO_SUCH_NAME, oid=oids[0]) else: oid = None if resp.error_index and resp.varbinds: if resp.error_index & 0x8000: # Some broken SNMP servers (i.e. Huawei) returns # negative error index. Try to negotiate silently oid = resp.varbinds[min(65536 - resp.error_index, len(resp.varbinds) - 1)][0] else: oid = resp.varbinds[resp.error_index - 1][0] logger.debug("[%s] SNMP error: %s %s", address, oid, resp.error_status) raise SNMPError(code=resp.error_status, oid=oid)
async def snmp_getnext( address: str, oid: str, port: int = 161, community: str = "public", version: int = SNMP_v2c, timeout: float = 10, bulk: bool = False, filter: Optional[Callable[[bytes, Any], bool]] = None, max_repetitions: int = BULK_MAX_REPETITIONS, only_first: bool = False, tos: Optional[int] = None, udp_socket: Optional[UDPSocket] = None, max_retries: int = 0, raw_varbinds: bool = False, display_hints: Optional[Dict[str, Optional[Callable[[str, bytes], Union[str, bytes]]]]] = None, response_parser: Optional[_ResponseParser] = None, rate_limit: Optional[AsyncRateLimit] = None, ): """ Perform SNMP GETNEXT/BULK request and returns Future to be used inside async coroutine """ def true(x, y): return True logger.debug("[%s] SNMP GETNEXT %s", address, oid) if not filter: filter = true poid = oid + "." result = [] parser = _get_parser(response_parser, raw_varbinds) with UDPSocketContext(udp_socket, tos=tos) as sock: first_oid = None last_oid = None while True: if rate_limit: await rate_limit.wait() # Get PDU if bulk: pdu = getbulk_pdu( community, oid, max_repetitions=max_repetitions or BULK_MAX_REPETITIONS, version=version, ) else: pdu = getnext_pdu(community, oid, version=version) # Send request and wait for response try: data, addr = await asyncio.wait_for( sock.send_and_receive(pdu, (address, port)), timeout) except asyncio.TimeoutError: if not max_retries: raise SNMPError(code=TIMED_OUT, oid=oid) max_retries -= 1 continue except socket.gaierror as e: logger.debug("[%s] Cannot resolve address: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oid) except OSError as e: logger.debug("[%s] Socket error: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oid) # Parse response try: resp = parser(data, display_hints) except ValueError: raise SNMPError(code=BER_ERROR, oid=oid) if resp.error_status == NO_SUCH_NAME: # NULL result break elif resp.error_status == END_OID_TREE: # End OID Tree return result elif resp.error_status != NO_ERROR: # Error raise SNMPError(code=resp.error_status, oid=oid) elif not raw_varbinds: # Success value for oid, v in resp.varbinds: if oid == first_oid: logger.warning("[%s] GETNEXT Oid wrap detected", address) return result elif oid.startswith(poid) and not ( only_first and result) and oid != last_oid: # Next value if filter(oid, v): result += [(oid, v)] last_oid = oid first_oid = first_oid or oid else: logger.debug("[%s] GETNEXT result: %s", address, result) return result else: # Raw varbinds for oid, v in resp.varbinds: s_oid = smart_text(oid) if s_oid.startswith(poid) and not ( only_first and result) and oid != last_oid: # Next value if filter(s_oid, v): result += [(oid, v)] last_oid = oid first_oid = first_oid or oid else: logger.debug("[%s] GETNEXT result: %s", address, result) return result
async def snmp_count( address, oid, port=161, community="public", version=SNMP_v2c, timeout=10, bulk=False, filter=None, max_repetitions=BULK_MAX_REPETITIONS, tos=None, udp_socket: Optional[UDPSocket] = None, rate_limit: Optional[AsyncRateLimit] = None, ): """ Perform SNMP get request and returns Future to be used inside async coroutine """ def true(x, y): return true logger.debug("[%s] SNMP COUNT %s", address, oid) if not filter: filter = true poid = oid + "." result = 0 with UDPSocketContext(udp_socket, tos=tos) as sock: while True: if rate_limit: await rate_limit.wait() # Get PDU if bulk: pdu = getbulk_pdu(community, oid, max_repetitions=max_repetitions, version=version) else: pdu = getnext_pdu(community, oid, version=version) # Send request and wait for response try: data, addr = await asyncio.wait_for( sock.send_and_receive(pdu, (address, port)), timeout) except asyncio.TimeoutError: raise SNMPError(code=TIMED_OUT, oid=oid) except socket.gaierror as e: logger.debug("[%s] Cannot resolve address: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oid) except OSError as e: logger.debug("[%s] Socket error: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oid) # Parse response try: resp = parse_get_response(data) except ValueError: raise SNMPError(code=BER_ERROR, oid=oid) if resp.error_status == NO_SUCH_NAME: # NULL result break elif resp.error_status != NO_ERROR: # Error raise SNMPError(code=resp.error_status, oid=oid) else: # Success value for oid, v in resp.varbinds: if oid.startswith(poid): # Next value if filter(oid, v): result += 1 else: logger.debug("[%s] COUNT result: %s", address, result) sock.close() return result
def snmp_get(address, oids, port=161, community="public", version=SNMP_v2c, timeout=10, tos=None, ioloop=None, udp_socket=None, raw_varbinds=False): """ Perform SNMP get request and returns Future to be used inside @tornado.gen.coroutine """ oid_map = {} if isinstance(oids, six.string_types): oids = [oids] elif isinstance(oids, dict): oid_map = dict((oids[k], k) for k in oids) oids = oids.values() else: raise ValueError("oids must be either string or dict") logger.debug("[%s] SNMP GET %s", address, oids) # Send GET PDU pdu = get_pdu(community=community, oids=oids, version=version) if udp_socket: sock = udp_socket prev_timeout = sock.get_timeout() else: sock = UDPSocket(ioloop=ioloop, tos=tos) sock.settimeout(timeout) # Wait for result try: yield sock.sendto(pdu, (address, port)) data, addr = yield sock.recvfrom(4096) except socket.timeout: raise SNMPError(code=TIMED_OUT, oid=oids[0]) except socket.gaierror as e: logger.debug("[%s] Cannot resolve address: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oids[0]) except socket.error as e: logger.debug("[%s] Socket error: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oids[0]) finally: if udp_socket: sock.settimeout(prev_timeout) else: sock.close() try: if raw_varbinds: resp = parse_get_response_raw(data) else: resp = parse_get_response(data) except ValueError: # Broken response raise SNMPError(code=BER_ERROR, oid=oids[0]) if resp.error_status == NO_ERROR: # Success if oid_map: result = {} for k, v in resp.varbinds: if k in oid_map: result[oid_map[k]] = v else: logger.error("[%s] Invalid oid %s returned in reply", address, k) else: result = resp.varbinds[0][1] logger.debug("[%s] GET result: %r", address, result) raise Return(result) elif resp.error_status == NO_SUCH_NAME and len(oids) > 1: # One or more invalid oids b_idx = resp.error_index - 1 logger.debug("[%s] Invalid oid %s detected, trying to exclude", address, resp.varbinds[b_idx][0]) result = {} oid_parts = [] if b_idx: # Oids before b_idx are probable correct oid_parts += [[vb[0] for vb in resp.varbinds[:b_idx]]] if b_idx < len(resp.varbinds) - 1: # Some oids after b_idx may be correct oid_parts += [[vb[0] for vb in resp.varbinds[b_idx + 1:]]] for new_oids in oid_parts: try: new_result = yield snmp_get(address=address, oids=dict( (k, k) for k in new_oids), port=port, community=community, version=version, timeout=timeout, tos=tos, ioloop=ioloop, udp_socket=sock) except SNMPError as e: if e.code == NO_SUCH_NAME and len(new_oids) == 1: # Ignore NO_SUCH_VALUE for last oid in list new_result = {} else: raise for k in new_result: if k in oid_map: result[oid_map[k]] = new_result[k] else: logger.info("[%s] Invalid oid %s returned in reply", address, k) if result: logger.debug("[%s] GET result: %r", address, result) raise Return(result) else: # All oids excluded as broken logger.debug("[%s] All oids are broken", address) raise SNMPError(code=NO_SUCH_NAME, oid=oids[0]) else: oid = None if resp.error_index and resp.varbinds: if resp.error_index & 0x8000: # Some broken SNMP servers (i.e. Huawei) returns # negative error index. Try to negotiate silently oid = resp.varbinds[65536 - resp.error_index][0] else: oid = resp.varbinds[resp.error_index - 1][0] logger.debug("[%s] SNMP error: %s %s", address, oid, resp.error_status) raise SNMPError(code=resp.error_status, oid=oid)
def snmp_getnext(address, oid, port=161, community="public", version=SNMP_v2c, timeout=10, bulk=False, filter=None, max_repetitions=BULK_MAX_REPETITIONS, only_first=False, tos=None, ioloop=None, udp_socket=None, max_retries=0, raw_varbinds=False): """ Perform SNMP GETNEXT/BULK request and returns Future to be used inside @tornado.gen.coroutine """ def true(x, y): return True def close_socket(): if udp_socket: sock.settimeout(prev_timeout) else: sock.close() logger.debug("[%s] SNMP GETNEXT %s", address, oid) if not filter: filter = true poid = oid + "." result = [] if udp_socket: sock = udp_socket prev_timeout = sock.get_timeout() else: sock = UDPSocket(ioloop=ioloop, tos=tos) sock.settimeout(timeout) last_oid = None while True: # Get PDU if bulk: pdu = getbulk_pdu(community, oid, max_repetitions=max_repetitions or BULK_MAX_REPETITIONS, version=version) else: pdu = getnext_pdu(community, oid, version=version) # Send request and wait for response try: yield sock.sendto(pdu, (address, port)) data, addr = yield sock.recvfrom(4096) except socket.timeout: if not max_retries: close_socket() raise SNMPError(code=TIMED_OUT, oid=oid) max_retries -= 1 continue except socket.gaierror as e: logger.debug("[%s] Cannot resolve address: %s", address, e) close_socket() raise SNMPError(code=UNREACHABLE, oid=oid) except socket.error as e: logger.debug("[%s] Socket error: %s", address, e) close_socket() raise SNMPError(code=UNREACHABLE, oid=oid) # Parse response try: if raw_varbinds: resp = parse_get_response_raw(data) else: resp = parse_get_response(data) except ValueError: raise SNMPError(code=BER_ERROR, oid=oid) if resp.error_status == NO_SUCH_NAME: # NULL result break elif resp.error_status != NO_ERROR: # Error close_socket() raise SNMPError(code=resp.error_status, oid=oid) else: # Success value for oid, v in resp.varbinds: if oid.startswith(poid) and not (only_first and result) and oid != last_oid: # Next value if filter(oid, v): result += [(oid, v)] last_oid = oid else: logger.debug("[%s] GETNEXT result: %s", address, result) close_socket() raise Return(result) close_socket()
def snmp_count(address, oid, port=161, community="public", version=SNMP_v2c, timeout=10, bulk=False, filter=None, max_repetitions=BULK_MAX_REPETITIONS, tos=None, ioloop=None, udp_socket=None): """ Perform SNMP get request and returns Future to be used inside @tornado.gen.coroutine """ def true(x, y): return true logger.debug("[%s] SNMP COUNT %s", address, oid) if not filter: filter = true poid = oid + "." result = 0 if udp_socket: sock = udp_socket prev_timeout = sock.get_timeout() else: sock = UDPSocket(ioloop=ioloop, tos=tos) sock.settimeout(timeout) while True: # Get PDU if bulk: pdu = getbulk_pdu(community, oid, max_repetitions=max_repetitions, version=version) else: pdu = getnext_pdu(community, oid, version=version) # Send request and wait for response try: yield sock.sendto(pdu, (address, port)) data, addr = yield sock.recvfrom(4096) except socket.timeout: raise SNMPError(code=TIMED_OUT, oid=oid) except socket.gaierror as e: logger.debug("[%s] Cannot resolve address: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oid) except socket.error as e: logger.debug("[%s] Socket error: %s", address, e) raise SNMPError(code=UNREACHABLE, oid=oid) finally: if udp_socket: sock.settimeout(prev_timeout) else: sock.close() # Parse response try: resp = parse_get_response(data) except ValueError: raise SNMPError(code=BER_ERROR, oid=oid) if resp.error_status == NO_SUCH_NAME: # NULL result break elif resp.error_status != NO_ERROR: # Error raise SNMPError(code=resp.error_status, oid=oid) else: # Success value for oid, v in resp.varbinds: if oid.startswith(poid): # Next value if filter(oid, v): result += 1 else: logger.debug("[%s] COUNT result: %s", address, result) sock.close() raise Return(result)