2025-11-28 14:35:21 +08:00

288 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NETCONF Transceiver Exporter
基于 Python 的 Prometheus Exporter通过 NETCONF 周期性轮询 H3C 交换机采集光模块transceiver及其物理通道指标并通过 HTTP `/metrics` 暴露给 Prometheus。
本项目已按设计文档 `specs/transceiver-exporter-design-v9.md` 与 TDD 文档 `specs/transcrive-exporter-tdd-design-v2.md` 实现,并带有完整测试集。
下面以本仓库为路径 `/home/yuyr/dev/switch_lab/netconf_exporter` 为例,说明从零开始的操作步骤。
---
## 1. 创建虚拟环境 & 安装依赖
### 1.1 创建虚拟环境
```bash
cd /home/yuyr/dev/switch_lab/netconf_exporter
python3 -m venv .venv
source .venv/bin/activate
```
之后所有命令都建议在虚拟环境中执行(终端提示符前通常会有 `(.venv)`)。
### 1.2 安装依赖
```bash
cd /home/yuyr/dev/switch_lab/netconf_exporter
pip install --upgrade pip
pip install -r requirements.txt
```
`requirements.txt` 包含:
- 运行依赖:`fastapi`, `uvicorn`, `ncclient`, `cryptography`, `prometheus_client`, `PyYAML` 等;
- 开发/测试依赖:`pytest`, `pytest-asyncio`, `pytest-cov`, `httpx` 等。
---
## 2. 运行测试
项目所有源码都在 `src/exporter/` 下,测试在 `tests/` 目录中。
推荐使用如下命令运行所有测试:
```bash
cd /home/yuyr/dev/switch_lab/netconf_exporter
PYTHONPATH=src .venv/bin/pytest -q
```
- 会自动加载 `tests/conftest.py` 中定义的配置(包括从 `.env` 加载 H3C NETCONF 参数);
- 默认会运行:
- 单元/组件测试;
- 集成测试(包括 HTTP E2E
- H3C NETCONF live 测试(如果 `.env` 中配置了可用的 H3C 连接参数)。
如果只想运行 H3C 相关测试,可以使用 mark 过滤,例如:
```bash
PYTHONPATH=src .venv/bin/pytest -q -m "h3c_live"
```
只跑 HTTP 端到端测试:
```bash
PYTHONPATH=src .venv/bin/pytest -q -m "http_e2e"
```
---
## 3. 配置 H3C NETCONF 访问参数(.env
为了方便本地联调 H3C 设备,本项目支持从 `.env` 文件中加载 H3C 连接参数。`tests/conftest.py` 会在 pytest 启动时自动读取 `.env`
在项目根目录创建或编辑 `.env`
```env
H3C_NETCONF_HOST=127.0.0.1
H3C_NETCONF_PORT=8830
H3C_NETCONF_USER=netconf_user
H3C_NETCONF_PASSWORD='NASPLab123!'
```
说明:
- 上述示例假设已经在本机将 127.0.0.1:8830 转发到真实 H3C 设备的 NETCONF 端口;
- `.env` 支持行尾注释(以 `#` 开头的行会被忽略),支持被单/双引号包裹的值;
- 若不想把密码写入 `.env`,可以在 shell 中 `export H3C_NETCONF_PASSWORD=...`,环境变量优先生效。
---
## 4. 编辑配置文件 config.yaml
Exporter 在启动时从 `config.yaml` 中加载全局配置和静态设备列表。典型最小配置示例如下(仅含全局配置,设备通过 HTTP API 注册):
```yaml
global:
http_listen: "0.0.0.0:19100" # HTTP 监听地址
scrape_interval_seconds: 5 # 采集周期(秒)
netconf_port: 830 # 默认 NETCONF 端口(可被 runtime 设备覆盖)
connect_timeout_seconds: 5 # SSH 连接超时
rpc_timeout_seconds: 30 # RPC 调用超时
max_workers: 5
api_token: "changeme" # HTTP API tokencurl/客户端需携带
runtime_db_path: "./devices.db" # SQLite 路径(保存 runtime 设备列表)
# Fernet 密钥32 字节 URL-safe Base64长度 44
# 生成方式:
# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
password_secret: "在这里替换为你的FernetKey"
ssh_keepalive_seconds: 30
failure_threshold: 3 # 连续失败多少次开始退避
max_backoff_factor: 8 # 最大退避倍数
shutdown_timeout_seconds: 60 # 关停时等待 scraper 的超时时间
log_level: INFO
log_to_stdout: true
log_file: "" # 若非空,则写入指定文件
devices: [] # 静态设备先留空,通过 API 动态注册
```
注意:
- `global.password_secret` 必须是合法的 Fernet key否则程序会在启动时抛异常
- 若将 `shutdown_timeout_seconds` 配得太小,相比 `scrape_interval_seconds + rpc_timeout_seconds`,会在启动时打印一个 warning来自配置校验提示关停可能会在 RPC 尚未结束时终止 scraper。
---
## 5. 启动 Exporter HTTP Server
虚拟环境里,使用如下命令启动服务:
```bash
cd /home/yuyr/dev/switch_lab/netconf_exporter
PYTHONPATH=src .venv/bin/python -m exporter.main --config config.yaml
```
行为说明:
- 启动时:
- 加载 `config.yaml`
- 初始化日志系统(包含 `device=...` 字段);
- 初始化 SQLite `devices.db`
- 加载静态设备(若有);
- 启动 Scraper 线程,按全局配置定期轮询设备;
- 启动 Uvicorn HTTP server默认监听 `http_listen`(例如 `0.0.0.0:19100`)。
- Ctrl+C 时:
- Uvicorn 会优雅关停 HTTP
- Scraper 会收到 stop 信号并在 `shutdown_timeout_seconds` 内退出;
- 所有 NETCONF 连接和 SQLite 资源会被关闭;
- `KeyboardInterrupt` 会被捕获,退出不会打印 traceback。
启动成功后,可以在另一个终端通过:
```bash
curl -s http://127.0.0.1:19100/healthz
```
查看基本健康状态(`{"status":"ok", ...}`)。
---
## 6. 通过 curl 注册 H3C 设备runtime device
假设已经准备好 H3C 的 NETCONF 代理:
- H3C 可通过 127.0.0.1:8830 被访问;
- 用户名/密码为 `.env` 中配置的 `H3C_NETCONF_USER` / `H3C_NETCONF_PASSWORD`
启动 exporter 后,在另一个终端中执行(注意 token 要与 config.yaml 中一致):
```bash
cd /home/yuyr/dev/switch_lab/netconf_exporter
curl -s -X POST \
-H "Content-Type: application/json" \
-H "X-API-Token: changeme" \
-d '{
"name": "h3c-live-1",
"host": "127.0.0.1",
"port": 8830,
"username": "netconf_user",
"password": "NASPLab123!",
"enabled": true,
"supports_xpath": false
}' \
http://127.0.0.1:19100/api/v1/devices
```
预期返回(示例):
```json
{
"name": "h3c-live-1",
"host": "127.0.0.1",
"port": 8830,
"enabled": true,
"scrape_interval_seconds": null,
"supports_xpath": false,
"source": "runtime"
}
```
可以再通过:
```bash
curl -s -H "X-API-Token: changeme" http://127.0.0.1:19100/api/v1/devices
```
确认设备已注册(包含 `source: "runtime"`)。
---
## 7. 通过 curl 获取 Prometheus 指标
Scraper 线程会按 `global.scrape_interval_seconds` 周期性访问所有启用的设备,通过 NETCONF `<get>` 拉取 transceiver/channel 数据,并写入内存缓存。
等待一到两个采集周期(例如配置为 5 秒,则等待 10 秒左右)后,可用 curl 获取指标:
```bash
curl -s http://127.0.0.1:19100/metrics | head -n 40
```
或聚焦某些关键指标:
```bash
# 健康状态
curl -s http://127.0.0.1:19100/metrics | grep netconf_scrape
# Transceiver 级指标
curl -s http://127.0.0.1:19100/metrics | grep '^transceiver_'
# Channel 级指标
curl -s http://127.0.0.1:19100/metrics | grep '^transceiver_channel_'
```
典型输出示例(部分):
```text
netconf_scrape_success{device="h3c-live-1"} 1
netconf_scrape_duration_seconds{device="h3c-live-1"} 0.532
transceiver_channel_rx_power_dbm{device="h3c-live-1",port="1/0/1",channel="1/0/1:1",component_name="63.TwoHundredGigE1/0/1:1"} -3.53
transceiver_channel_tx_power_dbm{device="h3c-live-1",port="1/0/1",channel="1/0/1:1",component_name="63.TwoHundredGigE1/0/1:1"} 2.09
...
```
如果 `netconf_scrape_success=0``netconf_scrape_errors_total{error_type="TimeoutError"}` > 0说明采集超时或失败可通过 exporter 日志进一步排查(`exporter.scraper` logger 会输出具体异常堆栈)。
---
## 8. 删除 runtime 设备
若需删除通过 API 注册的 H3C 设备:
```bash
curl -s -X DELETE \
-H "X-API-Token: changeme" \
http://127.0.0.1:19100/api/v1/devices/h3c-live-1
```
再次列出设备即不再看到 `h3c-live-1`。静态设备(来自 config.yaml `devices:`)无法通过 API 删除。
---
## 9. 关停 Exporter
在运行 `exporter.main` 的终端中按 `Ctrl+C`
- Uvicorn 会打印 `Shutting down` / `Application shutdown complete`
- Scraper 线程会收到终止信号,尝试在 `shutdown_timeout_seconds` 内退出;
- 所有 NETCONF 会话会发送 `<close-session>` 并关闭;
- `KeyboardInterrupt` 会被捕获,退出不会打印 Python traceback。
Exporter 本身不持久化运行时状态,只有:
- `config.yaml`:静态全局配置 + 静态设备;
- `devices.db`:运行时注册的设备列表(已加密的密码)。
因此重启 Exporter 不会影响 H3C 设备,只会重新加载配置并恢复运行时设备列表。