from __future__ import annotations """ 与真实 H3C 设备联调的“活体”测试用例。 说明: - 连接参数参考 exp/yangcli/run_yangcli.sh: - server=127.0.0.1 - ncport=8830 - user=netconf_user - password='...' - 为避免在仓库中硬编码明文密码,本测试通过环境变量注入密码: - H3C_NETCONF_PASSWORD - 可选:H3C_NETCONF_HOST / H3C_NETCONF_PORT / H3C_NETCONF_USER 默认行为: - 若未设置 H3C_NETCONF_PASSWORD,或无法建立到指定 host:port 的 TCP 连接, 则使用 pytest.skip() 自动跳过,不影响普通单元测试/CI。 - 仅在本地联调时、显式设置上述环境变量后,此测试才会真正访问设备。 """ import os import re import socket from pathlib import Path import pytest from ncclient import manager from exporter.netconf_client import build_transceiver_filter, parse_netconf_response H3C_HOST = os.getenv("H3C_NETCONF_HOST", "127.0.0.1") H3C_PORT = int(os.getenv("H3C_NETCONF_PORT", "8830")) H3C_USER = os.getenv("H3C_NETCONF_USER", "netconf_user") def _load_password_from_script() -> str: """ 尝试从 exp/yangcli 下的脚本中解析 --password='...'. 这样可以重用你已经验证过的 yangcli 参数,而不在测试里硬编码密码。 若脚本不存在或未找到 password,则返回空字符串。 """ root = Path(__file__).resolve().parents[1] candidates = [ root / "exp" / "yangcli" / "run_yangcli_h3c.sh", root / "exp" / "yangcli" / "run_yangcli.sh", ] pattern = re.compile(r"--password='([^']*)'") for path in candidates: if not path.exists(): continue text = path.read_text(encoding="utf-8") match = pattern.search(text) if match: return match.group(1) return "" H3C_PASSWORD = os.getenv("H3C_NETCONF_PASSWORD") or _load_password_from_script() def _can_connect(host: str, port: int, timeout: float = 2.0) -> bool: """快速探测 host:port 是否可连,用于决定是否跳过 live 测试。""" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.settimeout(timeout) sock.connect((host, port)) return True except OSError: return False finally: sock.close() @pytest.mark.h3c_live def test_h3c_live_transceiver_rpc_and_parse() -> None: """ 使用真实 H3C 设备验证: - ncclient 能与设备建立 NETCONF 会话; - build_transceiver_filter() 构造的 subtree filter 在设备上可用; - parse_netconf_response() 能正确解析设备返回的 XML,并得到非空结果。 """ if not _can_connect(H3C_HOST, H3C_PORT): pytest.skip(f"H3C NETCONF {H3C_HOST}:{H3C_PORT} 不可达,跳过 live 测试") flt = build_transceiver_filter() with manager.connect( host=H3C_HOST, port=H3C_PORT, username=H3C_USER, password=H3C_PASSWORD, hostkey_verify=False, timeout=30, allow_agent=False, look_for_keys=False, ) as m: reply = m.get(filter=("subtree", flt)) xml_str = str(reply) transceivers, channels = parse_netconf_response(xml_str, device_name=f"h3c-{H3C_HOST}") # 只要返回非空结果,就说明“连接 + filter + 解析”在真实设备上可以工作 assert transceivers or channels, "H3C 设备未返回任何 transceiver/channel 数据"