9.6 KiB
Raw Permalink Blame History

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 创建虚拟环境

cd /home/yuyr/dev/switch_lab/netconf_exporter

python3 -m venv .venv
source .venv/bin/activate

之后所有命令都建议在虚拟环境中执行(终端提示符前通常会有 (.venv))。

1.2 安装依赖

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/ 目录中。

推荐使用如下命令运行所有测试:

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 过滤,例如:

PYTHONPATH=src .venv/bin/pytest -q -m "h3c_live"

只跑 HTTP 端到端测试:

PYTHONPATH=src .venv/bin/pytest -q -m "http_e2e"


3. 测试覆盖率统计

项目已集成 pytest-cov,并在根目录提供了 .coveragerc(仅统计 src/exporter/ 下源码,忽略 tests/.venv/)。

获取覆盖率统计的推荐命令:

cd /home/yuyr/dev/switch_lab/netconf_exporter

PYTHONPATH=src .venv/bin/pytest \
  --cov=exporter \
  --cov-report=term-missing \
  -q

说明:

  • --cov=exporter:统计 exporter 包(即 src/exporter/)的覆盖率;
  • --cov-report=term-missing:在终端打印每个文件未覆盖的行,方便补测试;
  • .coveragerc 中已开启 branch = True,统计分支覆盖率。

若需要 HTML 报告,便于在浏览器中查看:

PYTHONPATH=src .venv/bin/pytest \
  --cov=exporter \
  --cov-report=html \
  -q

# 生成的报告在 htmlcov/index.html

用浏览器打开 htmlcov/index.html 即可查看详细覆盖率情况。


4. 配置 H3C NETCONF 访问参数(.env

为了方便本地联调 H3C 设备,本项目支持从 .env 文件中加载 H3C 连接参数。tests/conftest.py 会在 pytest 启动时自动读取 .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=...,环境变量优先生效。

5. 编辑配置文件 config.yaml

Exporter 在启动时从 config.yaml 中加载全局配置和静态设备列表。典型最小配置示例如下(仅含全局配置,设备通过 HTTP API 注册):

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。

6. 启动 Exporter HTTP Server

虚拟环境里,使用如下命令启动服务:

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。

启动成功后,可以在另一个终端通过:

curl -s http://127.0.0.1:19100/healthz

查看基本健康状态({"status":"ok", ...})。


7. 通过 curl 注册 H3C 设备runtime device

假设已经准备好 H3C 的 NETCONF 代理:

  • H3C 可通过 127.0.0.1:8830 被访问;
  • 用户名/密码为 .env 中配置的 H3C_NETCONF_USER / H3C_NETCONF_PASSWORD

启动 exporter 后,在另一个终端中执行(注意 token 要与 config.yaml 中一致):

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

预期返回(示例):

{
  "name": "h3c-live-1",
  "host": "127.0.0.1",
  "port": 8830,
  "enabled": true,
  "scrape_interval_seconds": null,
  "supports_xpath": false,
  "source": "runtime"
}

可以再通过:

curl -s -H "X-API-Token: changeme" http://127.0.0.1:19100/api/v1/devices

确认设备已注册(包含 source: "runtime")。


8. 通过 curl 获取 Prometheus 指标

Scraper 线程会按 global.scrape_interval_seconds 周期性访问所有启用的设备,通过 NETCONF <get> 拉取 transceiver/channel 数据,并写入内存缓存。

等待一到两个采集周期(例如配置为 5 秒,则等待 10 秒左右)后,可用 curl 获取指标:

curl -s http://127.0.0.1:19100/metrics | head -n 40

或聚焦某些关键指标:

# 健康状态
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_'

典型输出示例(部分):

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=0netconf_scrape_errors_total{error_type="TimeoutError"} > 0说明采集超时或失败可通过 exporter 日志进一步排查(exporter.scraper logger 会输出具体异常堆栈)。


9. 删除 runtime 设备

若需删除通过 API 注册的 H3C 设备:

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 删除。


10. 关停 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 设备,只会重新加载配置并恢复运行时设备列表。