SNMP でネットワーク機器を定期的にポーリングし、UniFi の Web UI のような画面で 可視化するモニタリングアプリケーションです。Python + FastAPI で動き、 ブラウザだけで参照できます。
- インターフェースのトラフィック — ifHCInOctets / ifHCOutOctets (64bit) から bps を算出。 64bit カウンタ非対応の機器では自動的に 32bit カウンタへフォールバックします
- ポートマップ — 物理ポート / SFP / LAG / VLAN / スタックポート / 仮想インターフェースを 区分ごとに分けて表示。物理ポートは実機の並びに合わせて奇数を上段・偶数を下段に配置し、 スタック構成はユニットごとにパネルを分けます
- システムリソース — CPU / メモリ / ディスクを HOST-RESOURCES-MIB から取得。 非対応機器では UCD-SNMP-MIB にフォールバックします
- 死活監視とイベント — 応答なし・復旧・リンクアップ/ダウン・機器の再起動を検出して記録
- SNMP v2c / v3 の両対応 — v3 は noAuthNoPriv / authNoPriv / authPriv すべてに対応
- 履歴の保存 — SQLite に保存し、古いデータは 5 分平均・1 時間平均へ自動的に集約 (ダウンサンプリング)
- リアルタイム更新 — WebSocket でポーリング結果を即座に画面へ反映
- 外部 CDN 非依存 — フロントエンドは依存ライブラリなしの素の JavaScript。 インターネットに出られない閉じたネットワークでもそのまま動きます
- Python 3.11 以上
- 監視対象機器で SNMP が有効になっていること
git clone <このリポジトリ>
cd snmp_monitor
python3 -m venv .venv
source .venv/bin/activate
pip install -e .実機がなくても、ダミーのネットワーク機器 4 台をシミュレートして画面を確認できます。 設定ファイルは不要です。
snmp-monitor --demo
# もしくは: python -m snmp_monitor --demoブラウザで http://127.0.0.1:8080 を開いてください。
cp config.example.yaml config.yaml
# config.yaml を編集してから
snmp-monitor -c config.yamlserver:
host: 0.0.0.0 # 他の PC からも見る場合
port: 8080
polling:
interval_seconds: 30
storage:
path: data/snmp_monitor.db
raw_retention_hours: 48
devices:
# SNMP v2c
- id: gateway
name: 拠点ルータ
host: 192.168.1.1
tags: [gateway]
snmp:
version: "2c"
community: ${SNMP_COMMUNITY} # 環境変数から読み込む
# SNMP v3 (authPriv)
- id: switch-1f
name: 1F スイッチ
host: 192.168.1.10
snmp:
version: "3"
username: monitor
auth_protocol: sha # none / md5 / sha / sha224 / sha256 / sha384 / sha512
auth_key: ${SNMP_AUTH_KEY}
priv_protocol: aes # none / des / 3des / aes / aes128 / aes192 / aes256
priv_key: ${SNMP_PRIV_KEY}- 値の中で
${VAR}と書くと環境変数に置き換わります (${VAR:-既定値}も使えます)。 コミュニティ文字列や v3 のパスワードを設定ファイルに直接書かずに済みます。 - 監視するポートを絞りたい場合は
interface_include/interface_excludeに 正規表現を指定してください (例:interface_exclude: "^(lo|docker|veth)")。 - 一時的に監視を止めたいデバイスは
enabled: falseにします。
| オプション | 説明 |
|---|---|
-c, --config |
設定ファイルのパス (既定: config.yaml) |
--host, --port |
待ち受けアドレス / ポート (設定ファイルより優先) |
--demo |
実機を使わずダミーデータで起動 |
--log-level |
debug / info / warning / error |
デバイス詳細のポート表示は、インターフェースを区分ごとに分けて描画します。
| 区分 | 内容 |
|---|---|
| 物理ポート / アップリンク・SFP | 実機のフェイスプレートに見立てたパネル。奇数が上段、偶数が下段 |
| 管理ポート | OOB 管理用。パネルの左端に MGMT 区画として置く (Gi0/0、mgmt0、fxp0 など) |
| LAG / ポートチャネル | Port-channel1、bond0、ae0 など |
| VLAN インターフェース | Vlan100、irb、Gi0/1.100 などのサブインターフェース |
| スタックポート | StackPort1、vcp-0/0/0 など |
| 仮想インターフェース | lo、Loopback0、tun0、docker0 など |
区分と配置は ifType と ifName から推定します
(GigabitEthernet1/0/24 → ユニット 1・ポート 24)。次の命名に対応しています。
- Cisco —
Gi1/0/24、TenGigabitEthernet1/1/1、Port-channel1、Vlan100、StackPort1 - Juniper —
ge-0/0/12、xe-0/1/0、ae0、vcp-0/0/0 - Arista / Nexus —
Ethernet1/24 - Linux —
eth0、bond0、eth0.100、docker0 - 汎用 —
Port 12
スタック構成やモジュラー機は先頭の番号 (Gi**2**/0/1) でパネルを分けます。
Catalyst の GigabitEthernet0/0 のように、名前の階層では本体と別シャーシに
なってしまう管理ポートは、「本体と呼べる大きさのパネルがあり、そこから外れた
ポートが数本だけ」という形から判断して MGMT 区画にまとめます
(独立した「ユニット 0」パネルを作らないため)。
番号が飛んでいるポートは、実機と対応が取れるよう空きスロットとして残ります。
アップリンクは名前 (sfp / Te / xe- など) のほか、
「その機器の中で最速かつ少数派の速度」であれば SFP 区画に分けます
(全ポートが 10G の機器を誤判定しないため)。
機種によっては実機のパネルと並びが異なることがあります。その場合は
config.yaml の port_layout で上書きしてください。
devices:
- id: switch-2f
host: 192.168.1.20
snmp: { version: "2c", community: public }
port_layout:
rows: 2 # 物理パネルの段数
order: odd-top # odd-top: 奇数を上段 / sequential: 並び順どおり
groups: # 区画分け (未指定なら自動推定)
- name: 本体
ports: "1-24"
- name: SFP
ports: "25-28"
rows: 1
categories: # 区分の強制指定 (ifIndex または ifName)
"49": stack
Gi0/0: physical # 管理ポートではなくデータポートだった場合指定できる区分は physical / uplink / mgmt / lag / vlan / stack /
virtual / other です。
Linux サーバを net-snmp で監視する場合の /etc/snmp/snmpd.conf の例です。
# --- SNMP v2c ---
rocommunity your-community 192.168.1.0/24
# --- SNMP v3 (authPriv) ---
createUser monitor SHA "auth-password" AES "priv-password"
rouser monitor priv
# HOST-RESOURCES-MIB を有効にしておくと CPU / メモリ / ディスクが取得できます
view systemonly included .1.3.6.1.2.1ネットワークスイッチの場合は、SNMP を有効にしたうえで
IF-MIB (必須) と HOST-RESOURCES-MIB (任意) が読める権限を与えてください。
snmp_monitor/
├── config.py 設定ファイルの読み込みとバリデーション
├── models.py ポーリング結果のデータ構造
├── metrics.py カウンタ差分から bps を計算 (wrap / 再起動に対応)
├── poller.py 定期ポーリングとイベント検出
├── api.py FastAPI の REST / WebSocket
├── simulator.py デモモード用の仮想デバイス
├── snmp/
│ ├── oids.py 使用する OID の定義
│ └── client.py pysnmp の asyncio ラッパ (v2c / v3)
├── storage/
│ ├── db.py SQLite 接続とスキーマ
│ └── timeseries.py 保存・集約・削除・参照
└── web/ フロントエンド (依存ライブラリなし)
データの流れは次のとおりです。
SNMP エージェント → Poller → SQLite (raw → 5m → 1h)
↓
WebSocket → ブラウザ
| メソッド | パス | 説明 |
|---|---|---|
| GET | /api/summary |
全体のサマリとデバイス一覧 |
| GET | /api/devices |
デバイス一覧 |
| GET | /api/devices/{id} |
デバイス詳細 (ポート・ストレージを含む) |
| GET | /api/devices/{id}/interfaces |
ポート一覧 |
| GET | /api/devices/{id}/portmap |
区分と物理配置の推定結果 |
| GET | /api/devices/{id}/history?range=1h |
CPU/メモリと合計トラフィックの履歴 |
| GET | /api/devices/{id}/interfaces/{ifIndex}/history?range=1h |
ポート単位の履歴 |
| GET | /api/sparklines?range=15m |
全デバイスの短い時系列 (一覧表示用) |
| GET | /api/events?limit=100 |
イベント履歴 |
| WS | /ws |
ポーリング結果のリアルタイム配信 |
range に指定できる値: 15m / 1h / 6h / 24h / 7d / 30d
表示期間に応じて解像度が自動的に切り替わります (6 時間以内は生データ、 7 日以内は 5 分平均、それ以上は 1 時間平均)。
pip install -e ".[dev]"
pytest # テスト (ローカルに実際の SNMP エージェントを立てる結合テストを含む)
ruff check . # 静的解析テストは以下をカバーしています。
- 設定ファイルの解析とバリデーション
- ポートの区分判定と物理配置の推定 (Cisco / Juniper / Arista / Linux の命名)
- カウンタの折り返し・機器再起動を含むレート計算
- SQLite への保存、集約 (ロールアップ)、保持期間による削除
- イベント検出 (リンクダウン、応答なし、再起動)
- REST / WebSocket の各エンドポイント
- 実際の SNMP 通信 — pysnmp のコマンドレスポンダをローカルに立て、 v2c と v3 の各セキュリティレベルで GET / GETBULK が通ることを確認
デバイスが「応答なし」のままになる
- 機器側で SNMP が有効か、監視サーバの IP からのアクセスが許可されているか確認してください
snmpwalk -v2c -c <community> <host> systemで手動確認できます- ファイアウォールで UDP/161 が塞がれていないか確認してください
SNMPv3 で Ciphering services not available と出る
暗号ライブラリが入っていません。pip install cryptography を実行してください
(通常は依存として自動的に入ります)。
CPU やメモリが表示されない
機器が HOST-RESOURCES-MIB にも UCD-SNMP-MIB にも対応していない可能性があります。 トラフィックとポート状態は IF-MIB だけで取得できるため、そちらは表示されます。
トラフィックの値が出るまで時間がかかる
bps は 2 回のポーリングの差分から求めるため、最初の 1 回目では表示されません。
interval_seconds の分だけお待ちください。
MIT

