monitor.snmp¶
otto SNMP manager — general SNMP v2c polling for performance monitoring.
This is the reusable core that lets otto monitor any SNMP speaker — network gear, a Linux box running net-snmp, or a Zephyr device running otto’s test-bed agent — over a separate channel from command execution. It is not embedded-only: a Unix host may also be monitored via SNMP.
Two layers, mirroring the shell-monitoring split in otto.monitor.parsers
(where MetricParser owns presentation and lab
data owns “what command to run”):
Acquisition —
SnmpClient: a thin async pysnmp v2c GET wrapper. Lab data (the host’ssnmpblock) supplies only connection params and the bare list of OIDs to poll. No presentation fields ever live in lab data.Presentation —
SnmpMetric+ the descriptor registry: maps each OID to how it is charted (label, chart group, unit, tab) and how its raw varbind is interpreted (scale). This is the SNMP analog ofMetricParser; graphing decisions live here. Built-in descriptors cover a standard OID set; private/device OIDs register a descriptor from an init module viaregister_snmp_metric(). An OID with no registered descriptor falls back to default styling (resolve_snmp_metric()) so a host can add a bare OID with zero code and still get a chart.
The pysnmp dependency is imported lazily inside SnmpClient.get() so this
module imports cleanly without it, and unit tests can mock at the get
boundary rather than against pysnmp internals.
-
class otto.monitor.snmp.SnmpMetric(oid, label, chart, y_title=
'', unit='', tab='metrics', tab_label='Metrics', scale=1.0)¶ Bases:
objectHow a single OID’s value is interpreted and charted.
Mirrors the presentation attributes
MetricParseralready exposes (chart/y_title/unit/tab/tab_label) plus ascalefactor that converts the raw integer varbind into a real value (e.g. sysUpTime is in hundredths of a second →scale=0.01for seconds; a CPU OID reported in centi-percent →scale=0.01for percent).These are deliberately not sourced from lab data — graphing stays in the monitor module.
- oid : --is-rst--:py:class:`str`¶
- label : --is-rst--:py:class:`str`¶
- chart : --is-rst--:py:class:`str`¶
- y_title : --is-rst--:py:class:`str`¶
- unit : --is-rst--:py:class:`str`¶
- tab : --is-rst--:py:class:`str`¶
- tab_label : --is-rst--:py:class:`str`¶
- scale : --is-rst--:py:class:`float`¶
- otto.monitor.snmp.register_snmp_metric(metric)¶
Register (or override) the descriptor for
metric.oid.Call from an init module listed in
.otto/settings.tomlto teach otto how to chart a private/device-specific OID — the same extension pattern asotto.monitor.parsers.register_host_parsers()andotto.host.command_frame.register_command_frame().- Return type:¶
None
- otto.monitor.snmp.get_snmp_metric(oid)¶
Return the registered descriptor for
oid, orNone.- Return type:¶
SnmpMetric|None
- otto.monitor.snmp.resolve_snmp_metric(oid)¶
Return the descriptor for
oid, or a default-styled fallback.The fallback charts the OID under its own label with no unit on the generic
metricstab, so a host can poll a bare OID it declared in lab data without anyone having registered a descriptor for it.- Return type:¶
- otto.monitor.snmp.points_from_values(values)¶
Map
{oid: raw_value}to(label, point, descriptor)triples.Each raw varbind is scaled by its descriptor and labelled for charting. OIDs with a
Nonevalue (no such instance / error) are skipped.- Return type:¶
list[tuple[str,MetricDataPoint,SnmpMetric]]
-
class otto.monitor.snmp.SnmpClient(address, port=
161, community='public', version='2c', timeout=2.0, retries=1)¶ Bases:
objectAsync SNMP v1/v2c GET client for a single endpoint.
The endpoint
(address, port)is whatever is reachable from the otto host — for a device behind a hop, that is the local end of a UDP forward / relay, not the device’s own address. Keeping the endpoint explicit means this client knows nothing about hop topology.- address : --is-rst--:py:class:`str`¶
- port : --is-rst--:py:class:`int`¶
- community : --is-rst--:py:class:`str`¶
- version : --is-rst--:py:data:`~typing.Literal`\ \[``'1'``, ``'2c'``]¶
- timeout : --is-rst--:py:class:`float`¶
- retries : --is-rst--:py:class:`int`¶
- async get(oids)¶
GET
oidsin one PDU; return{oid: numeric_value_or_None}.Non-numeric or errored varbinds map to
Noneso the caller can skip them. On a transport/PDU error the whole batch returnsNonevalues and the error is logged — a failed tick is non-fatal, matching the shell collector’s per-host error handling.- Return type:¶
dict[str,float|None]
- class otto.monitor.snmp.SnmpSource(client, oids)¶
Bases:
objectA
MonitorTarget’s SNMP collection mode.Pairs the
SnmpClient(where/how to reach the agent) with the bare list of OIDs to poll each tick. Presentation for those OIDs comes from the descriptor registry, not from here — this is acquisition only.- client : --is-rst--:py:class:`~otto.monitor.snmp.SnmpClient`¶
- oids : --is-rst--:py:class:`list`\ \[:py:class:`str`]¶