tsnmp is a package-first SNMP runtime. Start with numeric OIDs and no bundle;
add compiled JSON only when you need symbolic resolution or better display metadata.
pip install trishul-snmp
For SNMPv3 auth/priv support (HMAC and AES-128-CFB encryption), install the [v3] extra:
pip install "trishul-snmp[v3]"
The base package always imports correctly without [v3]; only calling auth or priv methods
at runtime requires it.
For local development:
pip install -e ".[dev,v3]"
Requires Python >=3.10.
Current main-branch baseline:
get, get_next, get_bulk, walk, bulkwalkDeliberately out of scope:
trishul-smisetCurrent CLI coverage includes SNMPv2c plus SNMPv3 get, getnext, getbulk,
walk, bulkwalk, trap, inform, listen, and decode-notification via
explicit --snmp-version {2c,3} selection. SNMPv3 listen requires explicit
--local-engine-* values, and SNMPv3 decode-notification requires explicit
user/auth/priv inputs.
This works with no bundle loaded.
import asyncio
from trishul_snmp import V2cManager
async def main() -> None:
async with V2cManager(host="10.0.0.10", community="public") as manager:
response = await manager.get("1.3.6.1.2.1.1.3.0")
for varbind in response.varbinds:
print(varbind.oid_str, varbind.value_type, varbind.display_value)
asyncio.run(main())
Requires pip install "trishul-snmp[v3]" for auth/priv methods.
import asyncio
from trishul_snmp import AuthProtocol, UsmUser, V3Manager
user = UsmUser(
username="monitor",
auth_protocol=AuthProtocol.SHA256,
auth_key=b"authpass123",
)
async def main() -> None:
async with V3Manager(host="10.0.0.10", user=user) as manager:
response = await manager.get("1.3.6.1.2.1.1.3.0")
for varbind in response.varbinds:
print(varbind.oid_str, varbind.value_type, varbind.display_value)
asyncio.run(main())
V3Manager.open() runs engine discovery automatically before the first request.
If you already have compiled JSON artifacts, load them and use symbolic names.
import asyncio
from trishul_snmp import V2cManager, load_bundle
bundle = load_bundle("./IF-MIB.json")
async def main() -> None:
async with V2cManager(
host="10.0.0.10",
community="public",
bundle=bundle,
) as manager:
response = await manager.get("IF-MIB::ifDescr.1")
for varbind in response.varbinds:
print(varbind.display_name, "=", varbind.display_value)
asyncio.run(main())
import asyncio
from trishul_snmp import V2cManager, load_bundle
bundle = load_bundle("./mibs-json")
async def main() -> None:
async with V2cManager(
host="10.0.0.10",
community="public",
bundle=bundle,
) as manager:
rows = await manager.walk("IF-MIB::ifTable")
for row in rows:
print(row.display_name, "=", row.display_value)
asyncio.run(main())
import asyncio
from trishul_snmp import IntegerValue, V2cNotifier, load_bundle
bundle = load_bundle("./mibs-json")
async def main() -> None:
async with V2cNotifier(
host="10.0.0.20",
community="public",
bundle=bundle,
) as notifier:
await notifier.send_trap(
"IF-MIB::linkDown",
varbinds=[("IF-MIB::ifIndex.7", IntegerValue(7))],
uptime=123,
)
asyncio.run(main())
Requires pip install "trishul-snmp[v3]" for auth/priv methods.
import asyncio
from trishul_snmp import AuthProtocol, IntegerValue, UsmUser, V3Notifier, load_bundle
bundle = load_bundle("./mibs-json")
user = UsmUser(
username="notify",
auth_protocol=AuthProtocol.SHA256,
auth_key=b"authpass123",
)
async def main() -> None:
async with V3Notifier(
host="10.0.0.20",
user=user,
bundle=bundle,
) as notifier:
response = await notifier.send_inform(
"IF-MIB::linkDown",
varbinds=[("IF-MIB::ifIndex.7", IntegerValue(7))],
uptime=123,
)
print(response.error_status)
asyncio.run(main())
For SNMPv3 traps, supply explicit sender-authoritative engine state:
import asyncio
from trishul_snmp import (
IntegerValue,
UsmLocalEngine,
UsmUser,
V3Notifier,
load_bundle,
)
bundle = load_bundle("./mibs-json")
user = UsmUser(username="notify")
local_engine = UsmLocalEngine(
engine_id=bytes.fromhex("8000010203"),
engine_boots=7,
engine_time=99,
)
async def main() -> None:
async with V3Notifier(
host="10.0.0.20",
user=user,
bundle=bundle,
local_engine=local_engine,
) as notifier:
await notifier.send_trap(
"IF-MIB::linkDown",
varbinds=[("IF-MIB::ifIndex.7", IntegerValue(7))],
uptime=123,
)
asyncio.run(main())
V3Notifier.send_inform() discovers peer engine state automatically on first use.
V3Notifier.send_trap() does not use peer discovery; it requires explicit
UsmLocalEngine so the sender’s own authoritative engine state is carried on the wire.
import asyncio
from trishul_snmp import IntegerValue, OctetStringValue, V2cResponder, load_bundle
bundle = load_bundle("./mibs-json")
async def main() -> None:
async with V2cResponder(
host="127.0.0.1",
port=1161,
communities=["public"],
bundle=bundle,
) as responder:
responder.set_objects(
[
("IF-MIB::ifIndex.1", IntegerValue(1)),
("IF-MIB::ifDescr.1", OctetStringValue(b"eth0")),
]
)
await responder.serve_forever()
asyncio.run(main())
No SNMP traffic is involved here.
from trishul_snmp import load_bundle
bundle = load_bundle("./mibs-json")
print(bundle.translate("IF-MIB::ifDescr.7"))
print(bundle.translate("1.3.6.1.2.1.2.2.1.2.7"))
Offline translation:
tsmi or provide an equivalent supported JSON artifact.load_bundle().tsnmp validates and normalizes the bundle.Live numeric GET with no bundle:
manager.get() with numeric OIDs.display_value is still available without symbolic metadata.Live SNMPv3 GET:
UsmUser and open V3Manager(...).V3Manager.open() performs RFC 3414 engine discovery once.UsmModel wraps the request PDU into an SNMPv3 message, with auth/priv enabled as configured.Live symbolic walk with a bundle:
load_bundle().manager.walk("IF-MIB::ifTable").GETBULK by default or GETNEXT when requested.Live notification send:
V2cNotifier.send_trap() or V2cNotifier.send_inform() for SNMPv2c notifications.V3Notifier.send_inform() for SNMPv3 USM informs.V3Notifier.send_trap() for SNMPv3 traps only when you have explicit UsmLocalEngine state.tsnmp auto-populates sysUpTime.0 and snmpTrapOID.0 unless you override them.Responder simulation:
V2cResponder with either the default in-memory source or a callback-backed source.serve() or serve_forever() to answer GET, GET_NEXT, and GET_BULK.noSuchObject; end-of-tree lookups return endOfMibView.tsnmp/tsmi split, supported inputs, and current pairing status