async def test_bulkwalk(self): request_ids = [1001613222, 1001613223, 1001613224] with patch('puresnmp.aio.api.pythonic.raw', new_callable=AsyncGenMock) as mck: mck.multiwalk.return_value = [ VarBind('1.3.6.1.2.1.2.2.1.1.1', Integer(1)), VarBind('1.3.6.1.2.1.2.2.1.1.10', Integer(10)), VarBind('1.3.6.1.2.1.2.2.1.2.1', OctetString(b"lo")), VarBind('1.3.6.1.2.1.2.2.1.22.10', ObjectIdentifier(0, 0)) ] result = [] async for x in bulkwalk('127.0.0.1', 'private', ['1.3.6.1.2.1.2.2'], bulk_size=20): result.append(x) expected = [ VarBind('1.3.6.1.2.1.2.2.1.1.1', 1), VarBind('1.3.6.1.2.1.2.2.1.1.10', 10), VarBind('1.3.6.1.2.1.2.2.1.2.1', b"lo"), VarBind('1.3.6.1.2.1.2.2.1.22.10', '0.0'), ] assert result == expected
async def test_table(self): with patch('puresnmp.aio.api.pythonic.raw') as mck: oid = ObjectIdentifier.from_string mck.bulktable.return_value = [ { '0': '1', '1': Integer(1) }, { '0': '2', '1': Integer(2) }, ] aio_result = bulktable('1.2.3.4', 'private', '1.2') result = [] async for row in aio_result: result.append(row) expected = [ { '0': '1', '1': 1 }, { '0': '2', '1': 2 }, ] assert result == expected
def test_bulktable(self): with patch('puresnmp.api.pythonic.raw') as mck: oid = ObjectIdentifier.from_string mck.bulktable.return_value = [ { '0': '1', '1': Integer(1) }, { '0': '2', '1': Integer(2) }, ] result = list(bulktable('1.2.3.4', 'private', '1.2')) expected = [ { '0': '1', '1': 1 }, { '0': '2', '1': 2 }, ] six.assertCountEqual(self, result, expected)
def test_traps(self): data_generator = readbytes_multiple("trap_requests.hex") def socket_response_generator(): for blob in data_generator: yield SocketResponse(blob, SocketInfo("192.0.2.1", 64001)) expected = [ VarBind(ObjectIdentifier((1, 3, 6, 1, 2, 1, 1, 3, 0)), TimeTicks(794602)), VarBind(ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0)), ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 0, 1))), VarBind(ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 2, 1)), Integer(123456)), VarBind(ObjectIdentifier((1, 3, 6, 1, 2, 1, 1, 3, 0)), TimeTicks(795345)), VarBind(ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0)), ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 0, 1))), VarBind(ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 2, 1)), Integer(123457)), VarBind(ObjectIdentifier((1, 3, 6, 1, 2, 1, 1, 3, 0)), TimeTicks(795538)), VarBind(ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0)), ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 0, 1))), VarBind(ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 2, 1)), Integer(123459)), ] result = [] with patch('puresnmp.api.raw.Transport') as mck: mck().listen.return_value = socket_response_generator() for trap in traps(): result.extend(trap.varbinds) self.assertEqual(result, expected)
def test_bulkget(self): data = readbytes('bulk_get_response.hex') expected = BulkResult( { '1.3.6.1.2.1.1.1.0': OctetString( b'Linux 7e68e60fe303 4.4.0-28-generic ' b'#47-Ubuntu SMP Fri Jun 24 10:09:13 UTC 2016 x86_64') }, { '1.3.6.1.2.1.3.1.1.1.10.1.172.17.0.1': Integer(10), '1.3.6.1.2.1.3.1.1.2.10.1.172.17.0.1': OctetString(b'\x02B\xe2\xc5\x8d\t'), '1.3.6.1.2.1.3.1.1.3.10.1.172.17.0.1': IpAddress(b'\xac\x11\x00\x01'), '1.3.6.1.2.1.4.1.0': Integer(1), '1.3.6.1.2.1.4.3.0': Counter(57) }) with patch('puresnmp.api.raw.send') as mck: mck.return_value = data result = bulkget('::1', 'public', ['1.3.6.1.2.1.1.1'], ['1.3.6.1.2.1.3.1'], max_list_size=5) self.assertEqual(result, expected)
async def test_multi_walk(self): response_1 = readbytes('multiwalk_response_1.hex') response_2 = readbytes('multiwalk_response_2.hex') response_3 = readbytes('multiwalk_response_3.hex') expected = [VarBind( ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.1.1'), Integer(1) ), VarBind( ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.2.1'), OctetString(b'lo') ), VarBind( ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.1.78'), Integer(78) ), VarBind( ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.2.78'), OctetString(b'eth0') )] with patch('puresnmp.aio.api.raw.Transport') as mck: mck().send = AsyncMock() mck().get_request_id.return_value = 0 mck().send.side_effect = [response_1, response_2, response_3] result = [] async for x in multiwalk('::1', 'public', [ '1.3.6.1.2.1.2.2.1.1', '1.3.6.1.2.1.2.2.1.2' ]): result.append(x) # TODO (advanced): should order matter in the following result? assert len(result) == len(expected)
async def test_bulkget(self): expected = BulkResult( {'1.3.6.1.2.1.1.1.0': b'Linux 7e68e60fe303 4.4.0-28-generic ' b'#47-Ubuntu SMP Fri Jun 24 10:09:13 UTC 2016 x86_64'}, {'1.3.6.1.2.1.3.1.1.1.10.1.172.17.0.1': 10, '1.3.6.1.2.1.3.1.1.2.10.1.172.17.0.1': b'\x02B\xe2\xc5\x8d\t', '1.3.6.1.2.1.3.1.1.3.10.1.172.17.0.1': b'\xac\x11\x00\x01', '1.3.6.1.2.1.4.1.0': 1, '1.3.6.1.2.1.4.3.0': 57}) with patch('puresnmp.aio.api.pythonic.raw', new_callable=AsyncMock) as mck: mck.bulkget.return_value = BulkResult({ '1.3.6.1.2.1.1.1.0': OctetString( b'Linux 7e68e60fe303 4.4.0-28-generic ' b'#47-Ubuntu SMP Fri Jun 24 10:09:13 UTC 2016 x86_64') }, { '1.3.6.1.2.1.3.1.1.1.10.1.172.17.0.1': Integer(10), '1.3.6.1.2.1.3.1.1.2.10.1.172.17.0.1': OctetString( b'\x02B\xe2\xc5\x8d\t'), '1.3.6.1.2.1.3.1.1.3.10.1.172.17.0.1': IpAddress( b'\xac\x11\x00\x01'), '1.3.6.1.2.1.4.1.0': Integer(1), '1.3.6.1.2.1.4.3.0': Counter(57) }) result = await bulkget('::1', 'public', ['1.3.6.1.2.1.1.1'], ['1.3.6.1.2.1.3.1'], max_list_size=5) assert result == expected
def test_traps(self): data_generator = readbytes_multiple('trap_requests.hex') expected = [ VarBind(ObjectIdentifier((1, 3, 6, 1, 2, 1, 1, 3, 0)), TimeTicks(794602)), VarBind(ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0)), ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 0, 1))), VarBind(ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 2, 1)), Integer(123456)), VarBind(ObjectIdentifier((1, 3, 6, 1, 2, 1, 1, 3, 0)), TimeTicks(795345)), VarBind(ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0)), ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 0, 1))), VarBind(ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 2, 1)), Integer(123457)), VarBind(ObjectIdentifier((1, 3, 6, 1, 2, 1, 1, 3, 0)), TimeTicks(795538)), VarBind(ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0)), ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 0, 1))), VarBind(ObjectIdentifier((1, 3, 6, 1, 4, 1, 8072, 2, 3, 2, 1)), Integer(123459)), ] result = [] with patch('puresnmp.api.raw.Transport') as mck: mck().listen.return_value = data_generator for trap in traps(): result.extend(trap.varbinds) self.assertEqual(result, expected)
def test_bulktable(self, mck_transport): responses = readbytes_multiple('bulktable_response.hex') mck_transport().send.side_effect = responses request_ids = [1, 2, 3, 4, 5] mck_transport().get_request_id.side_effect = request_ids result = list(bulktable('127.0.0.1', 'private', '1.3.6.1.2.1.2.2')) expected = [{ '0': '1', '1': Integer(1), '2': OctetString(b'lo'), '3': Integer(24), '4': Integer(65536), '5': Gauge(10000000), '6': OctetString(b''), '7': Integer(1), '8': Integer(1), '9': TimeTicks(0), '10': Counter(172), '11': Counter(2), '12': Counter(0), '13': Counter(0), '14': Counter(0), '15': Counter(0), '16': Counter(172), '17': Counter(2), '18': Counter(0), '19': Counter(0), '20': Counter(0), '21': Gauge(0), '22': ObjectIdentifier(0, 0), }, { '0': '4', '1': Integer(4), '2': OctetString(b'eth0'), '3': Integer(6), '4': Integer(1500), '5': Gauge(4294967295), '6': OctetString(b'\x02B\xac\x11\x00\x02'), '7': Integer(1), '8': Integer(1), '9': TimeTicks(0), '10': Counter(548760), '11': Counter(3888), '12': Counter(0), '13': Counter(0), '14': Counter(0), '15': Counter(0), '16': Counter(186660), '17': Counter(1875), '18': Counter(0), '19': Counter(0), '20': Counter(0), '21': Gauge(0), '22': ObjectIdentifier(0, 0), }] self.assertEqual(sorted(result, key=lambda x: x['0']), expected)
def test_getnext_increasing_oid_strict(self): ''' When running "getnext" we expect a different OID than the one we passed in. If not, this can cause endless-loops in the worst case. Faulty SNMP implementations may behave this way! ''' requested_oid = ObjectIdentifier(1, 2, 3, 4) response_object = Sequence( Integer(1), OctetString(b'public'), GetResponse(234, [VarBind(requested_oid, Integer(123))])) response_bytes = to_bytes(response_object) with patch('puresnmp.api.raw.send') as mck: mck.return_value = response_bytes with self.assertRaises(FaultySNMPImplementation): getnext('::1', 'private', '1.2.3.4')
def test_getnext(self): data = readbytes('getnext_response.hex') expected = VarBind('1.3.6.1.6.3.1.1.6.1.0', Integer(354522558)) with patch('puresnmp.api.raw.send') as mck: mck.return_value = data result = getnext('::1', 'private', '1.3.6.1.5') self.assertEqual(result, expected)
async def test_getnext(self): data = readbytes('getnext_response.hex') expected = VarBind('1.3.6.1.6.3.1.1.6.1.0', Integer(354522558)) with patch('puresnmp.aio.api.raw.send', new_callable=AsyncMock) as mck: mck.return_value = data result = await getnext('::1', 'private', '1.3.6.1.5') assert result == expected
async def test_getnext(self): expected = VarBind('1.3.6.1.6.3.1.1.6.1.0', 354522558) with patch('puresnmp.aio.api.pythonic.raw', new_callable=AsyncMock) as mck: mck.multigetnext.return_value = [ VarBind('1.3.6.1.6.3.1.1.6.1.0', Integer(354522558)) ] result = await getnext('::1', 'private', '1.3.6.1.5') assert result == expected
def test_getnext(self): expected = VarBind('1.3.6.1.6.3.1.1.6.1.0', 354522558) with patch('puresnmp.api.pythonic.raw') as mck: mck.multigetnext.return_value = [ VarBind('1.3.6.1.6.3.1.1.6.1.0', Integer(354522558)) ] result = getnext('::1', 'private', '1.3.6.1.5') self.assertEqual(result, expected)
def test_get_call_args(self): data = readbytes('dummy.hex') # any dump would do packet = Sequence(Integer(Version.V2C), OctetString('public'), GetNextRequest(0, ObjectIdentifier(1, 2, 3))) with patch('puresnmp.api.raw.send') as mck, \ patch('puresnmp.api.raw.get_request_id') as mck2: mck2.return_value = 0 mck.return_value = data getnext('::1', 'public', '1.2.3') mck.assert_called_with('::1', 161, to_bytes(packet), timeout=2)
async def test_getnext(self): data = readbytes('getnext_response.hex') expected = VarBind('1.3.6.1.6.3.1.1.6.1.0', Integer(354522558)) with patch('puresnmp.aio.api.raw.Transport') as mck: mck().send = AsyncMock() mck().get_request_id.return_value = 0 mck().send.return_value = data result = await getnext('::1', 'private', '1.3.6.1.5') assert result == expected
async def test_get_call_args(self): data = readbytes('dummy.hex') # any dump would do packet = Sequence(Integer(Version.V2C), OctetString('public'), GetNextRequest(0, ObjectIdentifier(1, 2, 3))) with patch('puresnmp.aio.api.raw.Transport') as mck: mck().send = AsyncMock() mck().get_request_id.return_value = 0 mck().send.return_value = data await getnext('::1', 'public', '1.2.3') mck.assert_called_with(timeout=6) mck().send.assert_called_with('::1', 161, to_bytes(packet))
def test_get_call_args_issue_22(self): data = readbytes('dummy.hex') # any dump would do packet = Sequence(Integer(Version.V2C), OctetString('public'), BulkGetRequest(0, 0, 2, ObjectIdentifier(1, 2, 3))) with patch('puresnmp.api.raw.Transport') as mck: mck().send.return_value = data mck().get_request_id.return_value = 0 with six.assertRaisesRegex(self, TypeError, 'OIDS.*list'): # we need to wrap this in a list to consume the generator. list(bulkwalk('::1', 'public', '1.2.3', bulk_size=2))
def test_walk_endless_loop(self): ''' In rare cases, some devices fall into an endless loop by returning the requested OID on a "getnext" call during a "walk" operation. A SNMP client behaving according to the SNMP spec will fall into an endless loop. This test fakes such a case and revents the loop. ''' response_binds = [ VarBind(ObjectIdentifier(1, 2, 3), Integer(123)), VarBind(ObjectIdentifier(1, 2, 4), Integer(124)), VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), # same OID VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), # same OID VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), # same OID ] response_packets = [ Sequence(Integer(1), OctetString(b'public'), GetResponse(234, [bind])) for bind in response_binds ] response_bytes = [to_bytes(packet) for packet in response_packets] handler = CapturingHandler() logger = getLogger('puresnmp') logger.addHandler(handler) with patch('puresnmp.api.raw.Transport') as mck: mck().send.side_effect = response_bytes mck().get_request_id.return_value = 0 result = list(walk('::1', 'private', '1.2', errors='warn')) logger.removeHandler(handler) # The last OID in the mocked responses is decreasing so we want to read # just up to that point. expected = [ VarBind(ObjectIdentifier(1, 2, 3), Integer(123)), VarBind(ObjectIdentifier(1, 2, 4), Integer(124)), VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), ] self.assertEqual(result, expected) # We also want to make sure that we have a proper warning about this handler.assertContains(WARNING, r'.*1.2.5.*')
def test_get_call_args(self): data = readbytes('dummy.hex') # any dump would do packet = Sequence(Integer(Version.V2C), OctetString('public'), BulkGetRequest(0, 0, 2, ObjectIdentifier(1, 2, 3))) with patch('puresnmp.api.raw.send') as mck, \ patch('puresnmp.api.raw.get_request_id') as mck2: mck2.return_value = 0 mck.return_value = data # we need to wrap this in a list to consume the generator. list(bulkwalk('::1', 'public', ['1.2.3'], bulk_size=2)) mck.assert_called_with('::1', 161, to_bytes(packet), timeout=2)
def test_get_call_args(self): data = readbytes('dummy.hex') # any dump would do packet = Sequence( Integer(Version.V2C), OctetString('public'), BulkGetRequest(0, 1, 2, ObjectIdentifier(1, 2, 3), ObjectIdentifier(1, 2, 4))) with patch('puresnmp.send') as mck, \ patch('puresnmp.get_request_id') as mck2: mck2.return_value = 0 mck.return_value = data bulkget('::1', 'public', ['1.2.3'], ['1.2.4'], max_list_size=2) mck.assert_called_with('::1', 161, bytes(packet))
def test_get_call_args(self): data = readbytes('dummy.hex') # any dump would do packet = Sequence( Integer(Version.V2C), OctetString('public'), BulkGetRequest(0, 1, 2, ObjectIdentifier(1, 2, 3), ObjectIdentifier(1, 2, 4))) with patch('puresnmp.api.raw.Transport') as mck: mck().send.return_value = data mck().get_request_id.return_value = 0 bulkget('::1', 'public', ['1.2.3'], ['1.2.4'], max_list_size=2) mck.assert_called_with(timeout=6) mck().send.assert_called_with('::1', 161, to_bytes(packet))
def test_54_endofmibview(): data = readbytes('gh-issues/54-endofmibview.hex') with patch('puresnmp.api.raw.Transport') as ptch: ptch().send.return_value = data ptch().get_request_id.return_value = 123 result = bulkget('192.0.2.1', 'private', [], ['1.2.3'], max_list_size=10) assert result.scalars == {} expected_lists = { '1.3.6.1.4.1.12325.1.303.1.9.1.3.1.0': OctetString(b'\x00' * 404), '1.3.6.1.4.1.12325.1.303.1.9.1.3.2.0': OctetString(b'\x00' * 404), '1.3.6.1.6.3.1.1.6.1.0': Integer(1103779487), '1.3.6.1.6.3.10.2.1.1.0': OctetString(b'\x80\x00\x08\xbe\x803740773355'), '1.3.6.1.6.3.10.2.1.2.0': Integer(1), '1.3.6.1.6.3.10.2.1.3.0': Integer(632), '1.3.6.1.6.3.10.2.1.4.0': Integer(1500), } assert result.listing == expected_lists
async def test_get_call_args_issue_22(self): data = readbytes('dummy.hex') # any dump would do packet = Sequence(Integer(Version.V2C), OctetString('public'), BulkGetRequest(0, 0, 2, ObjectIdentifier(1, 2, 3))) with patch('puresnmp.aio.api.raw.send', new_callable=AsyncMock) as mck, \ patch('puresnmp.aio.api.raw.get_request_id') as mck2: mck2.return_value = 0 mck.return_value = data with pytest.raises(TypeError, match=r'OIDS.*list'): # we need to wrap this in a list to consume the generator. async for x in bulkwalk('::1', 'public', '1.2.3', bulk_size=2): pass
def test_get_call_args(self): """ Test the call arguments of "get" """ data = readbytes('dummy.hex') # any dump would do packet = Sequence(Integer(Version.V2C), OctetString('public'), GetRequest(0, ObjectIdentifier(1, 2, 3))) with patch('puresnmp.send') as mck, \ patch('puresnmp.get_request_id') as mck2: mck2.return_value = 0 mck.return_value = data get('::1', 'public', '1.2.3') mck.assert_called_with('::1', 161, bytes(packet))
def test_multi_walk(self): response_1 = readbytes('multiwalk_response_1.hex') response_2 = readbytes('multiwalk_response_2.hex') response_3 = readbytes('multiwalk_response_3.hex') expected = [ VarBind(ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.1.1'), Integer(1)), VarBind(ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.2.1'), OctetString(b'lo')), VarBind(ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.1.78'), Integer(78)), VarBind(ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.2.78'), OctetString(b'eth0')) ] with patch('puresnmp.api.raw.send') as mck: mck.side_effect = [response_1, response_2, response_3] result = list( multiwalk('::1', 'public', ['1.3.6.1.2.1.2.2.1.1', '1.3.6.1.2.1.2.2.1.2'])) self.assertEqual(result, expected)
def test_eom(self): ''' Test a bulg-get operation which runs into the "endOfMibView" marker. ''' data = readbytes('x690/bulk_get_eom_response.hex') with patch('puresnmp.api.raw.Transport') as mck: mck().send.return_value = data mck().get_request_id.return_value = 0 result = bulkget('::1', 'public', [], ['1.2.4'], max_list_size=10) expected_scalars = {} self.assertEqual(result.scalars, expected_scalars) root = '1.3.6.1.6.3.16.1.5.2.1.6.6.95.110.111.110.101.95.1.' expected_listing = { root + '0': Integer(1), root + '1': Integer(1), root + '2': Integer(1), } self.assertEqual(result.listing, expected_listing)
def test_bulkwalk(self): request_ids = [1001613222, 1001613223, 1001613224] with patch('puresnmp.api.pythonic.raw') as mck: mck.multiwalk.return_value = [ VarBind('1.3.6.1.2.1.2.2.1.1.1', Integer(1)), VarBind('1.3.6.1.2.1.2.2.1.1.10', Integer(10)), VarBind('1.3.6.1.2.1.2.2.1.2.1', OctetString(b"lo")), VarBind('1.3.6.1.2.1.2.2.1.22.10', ObjectIdentifier(0, 0)) ] result = list( bulkwalk('127.0.0.1', 'private', ['1.3.6.1.2.1.2.2'], bulk_size=20)) expected = [ VarBind('1.3.6.1.2.1.2.2.1.1.1', 1), VarBind('1.3.6.1.2.1.2.2.1.1.10', 10), VarBind('1.3.6.1.2.1.2.2.1.2.1', b"lo"), VarBind('1.3.6.1.2.1.2.2.1.22.10', '0.0'), ] self.assertEqual(result, expected)
async def test_get_call_args(self): data = readbytes('dummy.hex') # any dump would do packet = Sequence(Integer(Version.V2C), OctetString('public'), BulkGetRequest(0, 0, 2, ObjectIdentifier(1, 2, 3))) with patch('puresnmp.aio.api.raw.Transport') as mck: mck().send = AsyncMock() mck().get_request_id.return_value = 0 mck().send.return_value = data # we need to wrap this in a list to consume the generator. async for x in bulkwalk('::1', 'public', ['1.2.3'], bulk_size=2): pass mck.assert_called_with(timeout=6) mck().send.assert_called_with('::1', 161, to_bytes(packet))
def test_walk(self): expected = [ VarBind('1.3.6.1.2.1.2.2.1.5.1', 10000000), VarBind('1.3.6.1.2.1.2.2.1.5.13', 4294967295) ] with patch('puresnmp.api.pythonic.raw') as mck: mck.walk.return_value = [ VarBind(ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.5.1'), Gauge(10000000)), VarBind(ObjectIdentifier.from_string('1.3.6.1.2.1.2.2.1.5.13'), Integer(4294967295)) ] result = list(walk('::1', 'public', '1.3.6.1.2.1.2.2.1.5')) self.assertEqual(result, expected)