From 6a0a4755b6e9b74d20c38e5f0dd02c54d3c4225d Mon Sep 17 00:00:00 2001 From: yuyr Date: Mon, 1 Dec 2025 16:11:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/curl_metric_sample.md | 208 ++++++++++++++++++ .../netconf_client.cpython-312.pyc | Bin 7428 -> 7579 bytes src/exporter/netconf_client.py | 5 +- ..._live_netconf.cpython-312-pytest-9.0.1.pyc | Bin 5321 -> 12355 bytes ...etconf_parser.cpython-312-pytest-9.0.1.pyc | Bin 22673 -> 32534 bytes tests/test_h3c_live_netconf.py | 182 ++++++++++++++- tests/test_netconf_parser.py | 97 ++++++++ 7 files changed, 490 insertions(+), 2 deletions(-) create mode 100644 docs/curl_metric_sample.md diff --git a/docs/curl_metric_sample.md b/docs/curl_metric_sample.md new file mode 100644 index 0000000..bc4aad1 --- /dev/null +++ b/docs/curl_metric_sample.md @@ -0,0 +1,208 @@ + curl -s http://127.0.0.1:19100/metrics +# HELP netconf_scrape_duration_seconds Duration of last NETCONF scrape per device +# TYPE netconf_scrape_duration_seconds gauge +netconf_scrape_duration_seconds{device="h3c-live-1"} 17.09970760345459 +# HELP netconf_scrape_success Whether last NETCONF scrape succeeded (1) or failed (0) +# TYPE netconf_scrape_success gauge +netconf_scrape_success{device="h3c-live-1"} 1.0 +# HELP netconf_last_scrape_timestamp_seconds Timestamp of last NETCONF scrape per device +# TYPE netconf_last_scrape_timestamp_seconds gauge +netconf_last_scrape_timestamp_seconds{device="h3c-live-1"} 1.7643102845284216e+09 +# HELP transceiver_data_staleness_seconds Age of last successful transceiver scrape per device +# TYPE transceiver_data_staleness_seconds gauge +# HELP netconf_scrape_errors_total Total number of NETCONF scrape errors by device and type +# TYPE netconf_scrape_errors_total counter +# HELP transceiver_temperature_celsius Transceiver temperature in degrees Celsius +# TYPE transceiver_temperature_celsius gauge +transceiver_temperature_celsius{component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 41.0 +transceiver_temperature_celsius{component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 41.0 +transceiver_temperature_celsius{component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/2"} 42.0 +transceiver_temperature_celsius{component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/2"} 42.0 +transceiver_temperature_celsius{component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/3"} 42.0 +transceiver_temperature_celsius{component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/3"} 42.0 +transceiver_temperature_celsius{component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/4"} 41.0 +transceiver_temperature_celsius{component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/4"} 41.0 +transceiver_temperature_celsius{component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 41.0 +transceiver_temperature_celsius{component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/66"} 41.0 +transceiver_temperature_celsius{component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 42.0 +transceiver_temperature_celsius{component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 41.0 +# HELP transceiver_supply_voltage_volts Transceiver supply voltage +# TYPE transceiver_supply_voltage_volts gauge +transceiver_supply_voltage_volts{component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 3.34 +transceiver_supply_voltage_volts{component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 3.34 +transceiver_supply_voltage_volts{component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/2"} 3.34 +transceiver_supply_voltage_volts{component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/2"} 3.34 +transceiver_supply_voltage_volts{component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/3"} 3.28 +transceiver_supply_voltage_volts{component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/3"} 3.28 +transceiver_supply_voltage_volts{component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/4"} 3.3 +transceiver_supply_voltage_volts{component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/4"} 3.3 +transceiver_supply_voltage_volts{component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 3.33 +transceiver_supply_voltage_volts{component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/66"} 3.32 +transceiver_supply_voltage_volts{component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 3.3 +transceiver_supply_voltage_volts{component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 3.3 +# HELP transceiver_present Transceiver present state (1 present, 0 otherwise) +# TYPE transceiver_present gauge +transceiver_present{component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 1.0 +transceiver_present{component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 1.0 +transceiver_present{component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/2"} 1.0 +transceiver_present{component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/2"} 1.0 +transceiver_present{component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/3"} 1.0 +transceiver_present{component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/3"} 1.0 +transceiver_present{component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/4"} 1.0 +transceiver_present{component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/4"} 1.0 +transceiver_present{component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 1.0 +transceiver_present{component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/66"} 1.0 +transceiver_present{component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 1.0 +transceiver_present{component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 1.0 +# HELP transceiver_channel_rx_power_dbm Receive optical power per channel (dBm) +# TYPE transceiver_channel_rx_power_dbm gauge +transceiver_channel_rx_power_dbm{channel="1/0/1:1",component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} -3.57 +transceiver_channel_rx_power_dbm{channel="1/0/1:2",component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 1.07 +transceiver_channel_rx_power_dbm{channel="1/0/1:1",component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 1.06 +transceiver_channel_rx_power_dbm{channel="1/0/1:2",component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 1.07 +transceiver_channel_rx_power_dbm{channel="1/0/2:1",component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/2"} 1.53 +transceiver_channel_rx_power_dbm{channel="1/0/1:2",component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/1"} 1.07 +transceiver_channel_rx_power_dbm{channel="1/0/2:1",component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/2"} 1.94 +transceiver_channel_rx_power_dbm{channel="1/0/1:2",component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/1"} 1.85 +transceiver_channel_rx_power_dbm{channel="1/0/3:1",component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/3"} 1.74 +transceiver_channel_rx_power_dbm{channel="1/0/2:2",component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/2"} 1.85 +transceiver_channel_rx_power_dbm{channel="1/0/3:1",component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/3"} 1.94 +transceiver_channel_rx_power_dbm{channel="1/0/2:2",component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/2"} 1.85 +transceiver_channel_rx_power_dbm{channel="1/0/4:1",component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/4"} 1.23 +transceiver_channel_rx_power_dbm{channel="1/0/2:2",component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/2"} 1.85 +transceiver_channel_rx_power_dbm{channel="1/0/4:1",component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/4"} 1.76 +transceiver_channel_rx_power_dbm{channel="1/0/2:2",component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/2"} 1.63 +transceiver_channel_rx_power_dbm{channel="1/0/65:1",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 0.59 +transceiver_channel_rx_power_dbm{channel="1/0/4:2",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/4"} 1.63 +transceiver_channel_rx_power_dbm{channel="1/0/65:3",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 0.72 +transceiver_channel_rx_power_dbm{channel="1/0/65:4",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 0.63 +transceiver_channel_rx_power_dbm{channel="1/0/66:1",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/66"} 0.48 +transceiver_channel_rx_power_dbm{channel="1/0/4:2",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/4"} 0.67 +transceiver_channel_rx_power_dbm{channel="1/0/65:3",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/65"} 0.72 +transceiver_channel_rx_power_dbm{channel="1/0/65:4",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/65"} 0.66 +transceiver_channel_rx_power_dbm{channel="1/0/67:1",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 0.53 +transceiver_channel_rx_power_dbm{channel="1/0/67:2",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 0.67 +transceiver_channel_rx_power_dbm{channel="1/0/65:3",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/65"} 1.09 +transceiver_channel_rx_power_dbm{channel="1/0/67:4",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 0.66 +transceiver_channel_rx_power_dbm{channel="1/0/68:1",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 1.43 +transceiver_channel_rx_power_dbm{channel="1/0/67:2",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/67"} 1.18 +transceiver_channel_rx_power_dbm{channel="1/0/68:3",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 1.09 +transceiver_channel_rx_power_dbm{channel="1/0/67:4",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/67"} 0.92 +# HELP transceiver_channel_tx_power_dbm Transmit optical power per channel (dBm) +# TYPE transceiver_channel_tx_power_dbm gauge +transceiver_channel_tx_power_dbm{channel="1/0/1:1",component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 2.03 +transceiver_channel_tx_power_dbm{channel="1/0/1:2",component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 2.11 +transceiver_channel_tx_power_dbm{channel="1/0/1:1",component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 2.05 +transceiver_channel_tx_power_dbm{channel="1/0/1:2",component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 2.11 +transceiver_channel_tx_power_dbm{channel="1/0/2:1",component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/2"} 1.41 +transceiver_channel_tx_power_dbm{channel="1/0/1:2",component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/1"} 2.11 +transceiver_channel_tx_power_dbm{channel="1/0/2:1",component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/2"} 1.66 +transceiver_channel_tx_power_dbm{channel="1/0/1:2",component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/1"} 1.74 +transceiver_channel_tx_power_dbm{channel="1/0/3:1",component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/3"} 1.9 +transceiver_channel_tx_power_dbm{channel="1/0/2:2",component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/2"} 1.74 +transceiver_channel_tx_power_dbm{channel="1/0/3:1",component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/3"} 2.15 +transceiver_channel_tx_power_dbm{channel="1/0/2:2",component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/2"} 1.74 +transceiver_channel_tx_power_dbm{channel="1/0/4:1",component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/4"} 1.88 +transceiver_channel_tx_power_dbm{channel="1/0/2:2",component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/2"} 1.74 +transceiver_channel_tx_power_dbm{channel="1/0/4:1",component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/4"} 1.91 +transceiver_channel_tx_power_dbm{channel="1/0/2:2",component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/2"} 1.82 +transceiver_channel_tx_power_dbm{channel="1/0/65:1",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 1.75 +transceiver_channel_tx_power_dbm{channel="1/0/4:2",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/4"} 1.82 +transceiver_channel_tx_power_dbm{channel="1/0/65:3",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 1.98 +transceiver_channel_tx_power_dbm{channel="1/0/65:4",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 1.94 +transceiver_channel_tx_power_dbm{channel="1/0/66:1",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/66"} 2.17 +transceiver_channel_tx_power_dbm{channel="1/0/4:2",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/4"} 2.09 +transceiver_channel_tx_power_dbm{channel="1/0/65:3",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/65"} 1.98 +transceiver_channel_tx_power_dbm{channel="1/0/65:4",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/65"} 2.03 +transceiver_channel_tx_power_dbm{channel="1/0/67:1",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 1.75 +transceiver_channel_tx_power_dbm{channel="1/0/67:2",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 2.09 +transceiver_channel_tx_power_dbm{channel="1/0/65:3",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/65"} 1.88 +transceiver_channel_tx_power_dbm{channel="1/0/67:4",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 2.03 +transceiver_channel_tx_power_dbm{channel="1/0/68:1",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 2.05 +transceiver_channel_tx_power_dbm{channel="1/0/67:2",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/67"} 2.23 +transceiver_channel_tx_power_dbm{channel="1/0/68:3",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 1.88 +transceiver_channel_tx_power_dbm{channel="1/0/67:4",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/67"} 2.09 +# HELP transceiver_channel_bias_current_ma Laser bias current per channel (mA) +# TYPE transceiver_channel_bias_current_ma gauge +transceiver_channel_bias_current_ma{channel="1/0/1:1",component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 8.72 +transceiver_channel_bias_current_ma{channel="1/0/1:2",component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 8.63 +transceiver_channel_bias_current_ma{channel="1/0/1:1",component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 8.65 +transceiver_channel_bias_current_ma{channel="1/0/1:2",component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 8.63 +transceiver_channel_bias_current_ma{channel="1/0/2:1",component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/2"} 8.91 +transceiver_channel_bias_current_ma{channel="1/0/1:2",component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/1"} 8.63 +transceiver_channel_bias_current_ma{channel="1/0/2:1",component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/2"} 8.68 +transceiver_channel_bias_current_ma{channel="1/0/1:2",component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/1"} 8.82 +transceiver_channel_bias_current_ma{channel="1/0/3:1",component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/3"} 8.65 +transceiver_channel_bias_current_ma{channel="1/0/2:2",component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/2"} 8.82 +transceiver_channel_bias_current_ma{channel="1/0/3:1",component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/3"} 8.51 +transceiver_channel_bias_current_ma{channel="1/0/2:2",component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/2"} 8.82 +transceiver_channel_bias_current_ma{channel="1/0/4:1",component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/4"} 8.58 +transceiver_channel_bias_current_ma{channel="1/0/2:2",component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/2"} 8.82 +transceiver_channel_bias_current_ma{channel="1/0/4:1",component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/4"} 8.89 +transceiver_channel_bias_current_ma{channel="1/0/2:2",component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/2"} 8.65 +transceiver_channel_bias_current_ma{channel="1/0/65:1",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 8.84 +transceiver_channel_bias_current_ma{channel="1/0/4:2",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/4"} 8.65 +transceiver_channel_bias_current_ma{channel="1/0/65:3",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 8.84 +transceiver_channel_bias_current_ma{channel="1/0/65:4",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 8.63 +transceiver_channel_bias_current_ma{channel="1/0/66:1",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/66"} 8.8 +transceiver_channel_bias_current_ma{channel="1/0/4:2",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/4"} 8.67 +transceiver_channel_bias_current_ma{channel="1/0/65:3",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/65"} 8.84 +transceiver_channel_bias_current_ma{channel="1/0/65:4",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/65"} 8.65 +transceiver_channel_bias_current_ma{channel="1/0/67:1",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 8.77 +transceiver_channel_bias_current_ma{channel="1/0/67:2",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 8.67 +transceiver_channel_bias_current_ma{channel="1/0/65:3",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/65"} 8.7 +transceiver_channel_bias_current_ma{channel="1/0/67:4",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 8.65 +transceiver_channel_bias_current_ma{channel="1/0/68:1",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 8.89 +transceiver_channel_bias_current_ma{channel="1/0/67:2",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/67"} 8.63 +transceiver_channel_bias_current_ma{channel="1/0/68:3",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 8.7 +transceiver_channel_bias_current_ma{channel="1/0/67:4",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/67"} 8.91 +# HELP transceiver_channel_laser_temperature_celsius Laser temperature per channel (Celsius) +# TYPE transceiver_channel_laser_temperature_celsius gauge +# HELP transceiver_info_info Transceiver static information +# TYPE transceiver_info_info gauge +transceiver_info_info{component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/1",serial="G80231AM995701HH",vendor="H3C"} 1.0 +transceiver_info_info{component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/1",serial="G80231AM995701HH",vendor="H3C"} 1.0 +transceiver_info_info{component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/2",serial="G80231AM995701ML",vendor="H3C"} 1.0 +transceiver_info_info{component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/2",serial="G80231AM995701ML",vendor="H3C"} 1.0 +transceiver_info_info{component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/3",serial="G80231AM995701MQ",vendor="H3C"} 1.0 +transceiver_info_info{component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/3",serial="G80231AM995701MQ",vendor="H3C"} 1.0 +transceiver_info_info{component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/4",serial="G80231AM995701JW",vendor="H3C"} 1.0 +transceiver_info_info{component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/4",serial="G80231AM995701JW",vendor="H3C"} 1.0 +transceiver_info_info{component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/65",serial="G80231AM995701MK",vendor="H3C"} 1.0 +transceiver_info_info{component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/66",serial="G80231AM995701JX",vendor="H3C"} 1.0 +transceiver_info_info{component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/67",serial="G80231AM995701JQ",vendor="H3C"} 1.0 +transceiver_info_info{component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",form_factor="",hardware_rev="",part_number="EQ854HG01M3-H3C",port="1/0/68",serial="G80231AM995701SK",vendor="H3C"} 1.0 +# HELP transceiver_channel_info_info Transceiver channel info +# TYPE transceiver_channel_info_info gauge +transceiver_channel_info_info{channel="1/0/1:1",channel_index="1",component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 1.0 +transceiver_channel_info_info{channel="1/0/1:2",channel_index="2",component_name="63.TwoHundredGigE1/0/1:1",device="h3c-live-1",port="1/0/1"} 1.0 +transceiver_channel_info_info{channel="1/0/1:1",channel_index="1",component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 1.0 +transceiver_channel_info_info{channel="1/0/1:2",channel_index="2",component_name="64.TwoHundredGigE1/0/1:2",device="h3c-live-1",port="1/0/1"} 1.0 +transceiver_channel_info_info{channel="1/0/2:1",channel_index="1",component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/2"} 1.0 +transceiver_channel_info_info{channel="1/0/1:2",channel_index="2",component_name="67.TwoHundredGigE1/0/2:1",device="h3c-live-1",port="1/0/1"} 1.0 +transceiver_channel_info_info{channel="1/0/2:1",channel_index="1",component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/2"} 1.0 +transceiver_channel_info_info{channel="1/0/1:2",channel_index="2",component_name="68.TwoHundredGigE1/0/2:2",device="h3c-live-1",port="1/0/1"} 1.0 +transceiver_channel_info_info{channel="1/0/3:1",channel_index="1",component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/3"} 1.0 +transceiver_channel_info_info{channel="1/0/2:2",channel_index="2",component_name="71.TwoHundredGigE1/0/3:1",device="h3c-live-1",port="1/0/2"} 1.0 +transceiver_channel_info_info{channel="1/0/3:1",channel_index="1",component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/3"} 1.0 +transceiver_channel_info_info{channel="1/0/2:2",channel_index="2",component_name="72.TwoHundredGigE1/0/3:2",device="h3c-live-1",port="1/0/2"} 1.0 +transceiver_channel_info_info{channel="1/0/4:1",channel_index="1",component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/4"} 1.0 +transceiver_channel_info_info{channel="1/0/2:2",channel_index="2",component_name="75.TwoHundredGigE1/0/4:1",device="h3c-live-1",port="1/0/2"} 1.0 +transceiver_channel_info_info{channel="1/0/4:1",channel_index="1",component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/4"} 1.0 +transceiver_channel_info_info{channel="1/0/2:2",channel_index="2",component_name="76.TwoHundredGigE1/0/4:2",device="h3c-live-1",port="1/0/2"} 1.0 +transceiver_channel_info_info{channel="1/0/65:1",channel_index="1",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 1.0 +transceiver_channel_info_info{channel="1/0/4:2",channel_index="2",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/4"} 1.0 +transceiver_channel_info_info{channel="1/0/65:3",channel_index="3",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 1.0 +transceiver_channel_info_info{channel="1/0/65:4",channel_index="4",component_name="319.FourHundredGigE1/0/65",device="h3c-live-1",port="1/0/65"} 1.0 +transceiver_channel_info_info{channel="1/0/66:1",channel_index="1",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/66"} 1.0 +transceiver_channel_info_info{channel="1/0/4:2",channel_index="2",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/4"} 1.0 +transceiver_channel_info_info{channel="1/0/65:3",channel_index="3",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/65"} 1.0 +transceiver_channel_info_info{channel="1/0/65:4",channel_index="4",component_name="323.FourHundredGigE1/0/66",device="h3c-live-1",port="1/0/65"} 1.0 +transceiver_channel_info_info{channel="1/0/67:1",channel_index="1",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 1.0 +transceiver_channel_info_info{channel="1/0/67:2",channel_index="2",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 1.0 +transceiver_channel_info_info{channel="1/0/65:3",channel_index="3",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/65"} 1.0 +transceiver_channel_info_info{channel="1/0/67:4",channel_index="4",component_name="327.FourHundredGigE1/0/67",device="h3c-live-1",port="1/0/67"} 1.0 +transceiver_channel_info_info{channel="1/0/68:1",channel_index="1",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 1.0 +transceiver_channel_info_info{channel="1/0/67:2",channel_index="2",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/67"} 1.0 +transceiver_channel_info_info{channel="1/0/68:3",channel_index="3",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/68"} 1.0 +transceiver_channel_info_info{channel="1/0/67:4",channel_index="4",component_name="331.FourHundredGigE1/0/68",device="h3c-live-1",port="1/0/67"} 1.0 \ No newline at end of file diff --git a/src/exporter/__pycache__/netconf_client.cpython-312.pyc b/src/exporter/__pycache__/netconf_client.cpython-312.pyc index da6c850cf39661810548a5ae9b2fb98d70e4c7bd..f6e3cbfd78b5f51a15a89eb196a0946af6e137be 100644 GIT binary patch delta 406 zcmZp%nr+Q@nwOW0fq{WxTZCrjdHIce^_+}fCiih>vFb4})Ur*E=Mmp5!sW!w9>~B@ zD_J8sIgv+nauIL2OpRy{KH<`pNWW|pNEStaM^rDdj1 z))1E2EG+z}Smwea$ytmEvGh4`O0ey1^^( eff>YjASm*I6~y?!#=z#n_=O$BF5+ciU;qGi7j4P_ delta 337 zcmbPj-D1UenwOW0fq{V`NlG*Go!mygdQQf5llwTcSh*P(YS|{o^N4R2;c{YT*JEI) zm8_ARoX8_Oxrn!XvOZtvWGOz8$us!WCb#ejPd>}dJNX`;+T?wF3X@g%)h4s>D@@Mf zSDWm`uP}K7zlpfiYEF#g&OcMD7y< zBd_cYX_?6!AD9_=Wq%b5F)%QkVCVN`WjV{r?JL55R+!OOWOI%*2a~!#6Qj}%UV#tH cAjShhkq@jO#s@YAHW$V(>>zd#F9QPu0J)i0+yDRo diff --git a/src/exporter/netconf_client.py b/src/exporter/netconf_client.py index 1197418..e911568 100644 --- a/src/exporter/netconf_client.py +++ b/src/exporter/netconf_client.py @@ -160,7 +160,10 @@ def parse_netconf_response( except ValueError: ch_index = 0 - desc_elem = ch.find("oc-transceiver:state/oc-transceiver:description", NS) + # 优先从 config 读取 description,fallback 到 state + desc_elem = ch.find("oc-transceiver:config/oc-transceiver:description", NS) + if desc_elem is None or not _get_text(desc_elem): + desc_elem = ch.find("oc-transceiver:state/oc-transceiver:description", NS) description = _get_text(desc_elem) logical_port, logical_channel = parse_port_and_channel( description, component_name, ch_index diff --git a/tests/__pycache__/test_h3c_live_netconf.cpython-312-pytest-9.0.1.pyc b/tests/__pycache__/test_h3c_live_netconf.cpython-312-pytest-9.0.1.pyc index c258c399dd0862af1a12069a20d14c7fff1de8ff..f1f3acc73c27cd3154943dd74373c6800c7eac7d 100644 GIT binary patch delta 6701 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 J1Q{WL0RVWWq$>ab delta 313 zcmX?{a8i@+G%qg~0|Nttqm*W5KJP?63C5C%>R%WICd)9|G7F{%O^#tS6c$e5Z(&&t zQpUi*5G7m5q$#rb1>*xAp(-8^V`l|F*AQoaKR1P6j8>(SZ}Dj|YEEY1k7U%`9LK+t zk!{|17 zuVFZ2{A5w1&-Dom3=CDA$@#ejhE~ZL2AZt57>Yn*MY0SG5Eeq#P?Pl+yP>{;zM+*N zSkX%+kZxA6P*D;C1A`{(FA?|5^u(mh64#Q9)S|r95{M+o$d}9@BU3Th{&3J#XOF&V6(AB2RCE#<~`2K8I@NufnDTRR02{_#=yW(46lAnCp#zS6dNA?#Zw0l$cUPz($Kw(`bH>N4P2&Yq@J#YdEr` zCQsBBX68x}1KT7{O`90GCr8;T;|LxJc<>}LN)Q_?C~oJ>l7PBh66|*LP@;wnGEf_& zz&1#ug%0I5aAb*rY+&W8;e@c*S!%dySh6)HYuT$yvea;=F{a3XM2JaPyj-gpp@|RE zWesMWOewOH^{j*I5ea~$l3P=*bU6b91DAq=g2J<{-7nTpeY$tDLUMjyT4uV!(@8xF z#U+U)sS3|$wJ4;d7AF^F7L;V>=P5kd(feda!;AJU&!%sCzo+NPo*7SDJD%*_|9r{{ zsOHBFO<+S`?4S8;#`-5ac0JkE_G0s~IJ0W&SJgk^Fc+Y&J|bG?xAe2>z+l%muW_sn!xP^n}znJ;ki z{A1b86X$cmxU@_D)HJ&`1GidT}4n;}A;WHtl~DyV1X6_l3f7UY+w7TMWk<`tJD=9SnP>KT~Z=)-s**5p;3 zt4*u8GK`aTQ&P(^lT(Y5K_!nIh)@6#iXfs0L@0v@H4vfBz`)=)nbS4OyQmh#$6Z8L z*_5Ofm&6worKY78#Y6lTj})5m`DLj^@!&X|e9={y?+(Z~cxgJB(>2x`p_Gjh|mTRI-r^ZT>ltZ85TJ+FfgQ7i>z~hSoOnRld;NVGNZcm zk0^Tg=HBMpaPtM(OgcuLF0;eS|BOEvkqIM@M{0QZ84mf~~CrRHG(i zRoG-kb>+#CQhbwx-Q-0I@{3BiYKxjcRR(_%sKhQ(oLpvott=@nB>S z)?6XHBImM@_2k9wlNg^&HuI2Y{lLb+!$0|yM^(M{0?i97+84Ox7HD4N*1paHLRX;z z5FVVo$fDh04B=fyFt30WLxeA}XfM!&Fp&nUhwuYXZq{{oBt1#amJ^7-XB zyNqC70jq{9f-o+yfGhxOzl>mll*)s&LlnWuD=hj&QVa|X$qWpWQ}l(I3RvN7A8@q` zZTr+PrSMFy^OY6mOyR9%t>LU;&DLOGsO75RDqcHTz+ax5rG_hwF@+B#Gdae7^<-W< z&dJAoSXa7v^IK$|_>DS|0NHQdl<4-dH6vq(n-Y!PP#V=WiBZ33<&)|x`fS|^ZpY-Oz}w5-+SK`Qwqir|HwCT|gH31>RlNKe8~3*>=#n3v;A z@*%ZKe0fG{9<1WY%`7g?%u6qt%D}+z4pg@FGlI)D4pDH~rUNP3GA;|5gNnBL2b_Wv z6g#CGoE~uV_S<*bUtm$4k$!ntF26~c#b;N(RX)ea*F?=pgU1*8@t zdI@9}go#XEW>LMt&3m0&_9C}z{RI}S1(Fw7v@USVE|9#)t#zFRgswsbAUrsEkwpt+ zBS^(%1oH}5F+|ZNkZ}+uGI@nX3!JeuWo~gKm1gFYWabqY@q@ZI3gHQifs-40BYqG*H0FWC=~nT!YIvX^~uJX O(cp6pqXMHg*eU>e>|O={ delta 487 zcmbRCk8$EgM!wU$yj%m|_GP-Zh)Y#0*=sNkFq0(e~J!{6O&DDDQnHYU1TN~Oi`cJMfRAzLUJjYOn z(RK1k!*Is9$%;mwz2g}e7^*mv^K%Oft&%egG+A#k6oJHwWEmJBEQG9~ChISDLwy5% zLn}kDqL)k{-K=1tqQuGZ#xmR>y)T(TdXqP|8GEq^q=VR5AR-M!WKMoy9>P2kALM3Cnj8?E%Iy!51N*;j@`2#{jINUxgj6tgZI%pWVq(&in%r2M z&loWIW34l{J4j^|h=`kPUnkC;4`Mcfh_1=mby8f@85kHCLH;S3++SD7{n>?4n$fCA Iih+Rv05w{EZ2$lO diff --git a/tests/test_h3c_live_netconf.py b/tests/test_h3c_live_netconf.py index c588879..31dfa28 100644 --- a/tests/test_h3c_live_netconf.py +++ b/tests/test_h3c_live_netconf.py @@ -102,5 +102,185 @@ def test_h3c_live_transceiver_rpc_and_parse() -> None: transceivers, channels = parse_netconf_response(xml_str, device_name=f"h3c-{H3C_HOST}") - # 只要返回非空结果,就说明“连接 + filter + 解析”在真实设备上可以工作 + # 只要返回非空结果,就说明"连接 + filter + 解析"在真实设备上可以工作 assert transceivers or channels, "H3C 设备未返回任何 transceiver/channel 数据" + + +@pytest.mark.h3c_live +def test_h3c_config_description_consistency() -> None: + """ + 验证 H3C 设备上所有 transceiver 的 physical channel 的 config/description 是否正确。 + + 检查规则: + - component 名称格式如: "67.TwoHundredGigE1/0/2:1" + - 从中提取物理端口: "1/0/2" + - channel config/description 应该是: "1/0/2:1", "1/0/2:2", 等 + - 即:config/description 的端口部分应与 component 名称中的端口部分一致 + + 目的:发现 H3C 设备上 config/description 配置错误的情况 + """ + 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) + + # 解析 XML 提取详细的 config 和 state 信息 + import xml.etree.ElementTree as ET + + NS = { + "oc-platform": "http://openconfig.net/yang/platform", + "oc-transceiver": "http://openconfig.net/yang/platform/transceiver", + } + + root = ET.fromstring(xml_str) + components_path = ".//oc-platform:components/oc-platform:component" + + inconsistencies = [] + total_channels_checked = 0 + + for comp in root.findall(components_path, NS): + # 获取 component 名称 + name_elem = comp.find("oc-platform:name", NS) + if name_elem is None or not name_elem.text: + continue + component_name = name_elem.text.strip() + + # 只检查 TRANSCEIVER 类型 + type_elem = comp.find("oc-platform:state/oc-platform:type", NS) + if type_elem is None or "TRANSCEIVER" not in type_elem.text: + continue + + # 从 component 名称中提取端口 + # 格式: "67.TwoHundredGigE1/0/2:1" -> 提取 "1/0/2" + expected_port = _extract_port_from_component_name(component_name) + if not expected_port: + # 无法从 component 名称中提取端口,跳过 + continue + + # 遍历所有 physical channels + channels_path = ( + "oc-transceiver:transceiver/oc-transceiver:physical-channels/" + "oc-transceiver:channel" + ) + for ch in comp.findall(channels_path, NS): + total_channels_checked += 1 + + # 获取 channel index + idx_elem = ch.find("oc-transceiver:index", NS) + channel_index = idx_elem.text if idx_elem is not None else "?" + + # 获取 config/description + config_desc_elem = ch.find("oc-transceiver:config/oc-transceiver:description", NS) + config_desc = config_desc_elem.text.strip() if config_desc_elem is not None and config_desc_elem.text else None + + # 获取 state/description + state_desc_elem = ch.find("oc-transceiver:state/oc-transceiver:description", NS) + state_desc = state_desc_elem.text.strip() if state_desc_elem is not None and state_desc_elem.text else None + + # 检查 config/description + if config_desc: + # config/description 应该是 "端口:通道号" 格式,如 "1/0/2:1" + if ":" in config_desc: + config_port = config_desc.split(":", 1)[0] + if config_port != expected_port: + inconsistencies.append({ + "component": component_name, + "channel_index": channel_index, + "expected_port": expected_port, + "config_desc": config_desc, + "config_port": config_port, + "issue": "config/description 端口部分与 component 名称不一致" + }) + else: + # config/description 缺失 + inconsistencies.append({ + "component": component_name, + "channel_index": channel_index, + "expected_port": expected_port, + "config_desc": None, + "issue": "config/description 缺失" + }) + + # 同时检查 state/description(仅作信息收集,不作为测试失败条件) + # 但我们会记录这些差异,以便了解 state 数据质量 + pass # state 检查移至单独的报告 + + # 生成报告 + print(f"\n=== H3C Config/Description 一致性检查报告 ===") + print(f"检查的 channel 总数: {total_channels_checked}") + print(f"发现不一致的 channel 数: {len(inconsistencies)}") + + if inconsistencies: + print("\n发现的不一致情况:") + for item in inconsistencies: + print(f" Component: {item['component']}") + print(f" Channel Index: {item['channel_index']}") + print(f" 期望端口: {item['expected_port']}") + print(f" 实际 config/description: {item.get('config_desc', 'N/A')}") + if 'config_port' in item: + print(f" config 中的端口: {item['config_port']}") + print(f" 问题: {item['issue']}") + print() + + # 测试失败,config/description 应该是准确的 + pytest.fail( + f"发现 {len(inconsistencies)} 个 channel 的 config/description 与 component 名称不一致," + "详见上方输出" + ) + else: + print("\n✅ 所有 channel 的 config/description 都与 component 名称一致") + + +def _extract_port_from_component_name(component_name: str) -> str | None: + """ + 从 component 名称中提取物理端口。 + + 例如: + - "67.TwoHundredGigE1/0/2:1" -> "1/0/2" + - "63.TwoHundredGigE1/0/1:1" -> "1/0/1" + - "323.FourHundredGigE1/0/66" -> "1/0/66" + + 返回: 端口字符串,如 "1/0/2",若无法提取则返回 None + """ + import re + + # 匹配 "数字/数字/数字" 格式的端口 + # 可能在 GigE 或 FourHundredGigE 等后面,可能带或不带 ":通道号" + pattern = r"(\d+/\d+/\d+)" + match = re.search(pattern, component_name) + if match: + return match.group(1) + return None + + +def test_extract_port_from_component_name(): + """测试从 component 名称中提取端口的辅助函数(不依赖真实设备)""" + # 测试各种可能的命名格式 + test_cases = [ + ("67.TwoHundredGigE1/0/2:1", "1/0/2"), + ("63.TwoHundredGigE1/0/1:1", "1/0/1"), + ("323.FourHundredGigE1/0/66", "1/0/66"), + ("64.TwoHundredGigE10/20/30:2", "10/20/30"), + ("SomePrefix99/88/77", "99/88/77"), + ("no_port_here", None), + ("", None), + ] + for component_name, expected_port in test_cases: + result = _extract_port_from_component_name(component_name) + assert result == expected_port, ( + f"从 '{component_name}' 提取端口失败: " + f"期望 '{expected_port}', 实际 '{result}'" + ) diff --git a/tests/test_netconf_parser.py b/tests/test_netconf_parser.py index 39680da..a58f936 100644 --- a/tests/test_netconf_parser.py +++ b/tests/test_netconf_parser.py @@ -172,3 +172,100 @@ def test_h3c_multi_component_same_serial(): assert txs[1].serial == "SN001" assert txs[0].component_name != txs[1].component_name + +def test_prefer_config_description_over_state(): + """ + 测试当 config 和 state 的 description 不一致时,优先使用 config。 + 这是为了解决 H3C 设备 state/description 有时错误的问题。 + """ + xml = """\ + + + + + 67.TwoHundredGigE1/0/2:1 + TRANSCEIVER + + + + 1 + + 1/0/2:1 + + + 1/0/2:1 + 1.53 + + + + 2 + + 1/0/2:2 + + + 1/0/1:2 + 1.07 + + + + + + + + +""" + _, channels = parse_netconf_response(xml, "h3c-device") + assert len(channels) == 2 + + ch1 = next(c for c in channels if c.channel_index == 1) + ch2 = next(c for c in channels if c.channel_index == 2) + + # 验证 channel 1: config 和 state 一致,结果应该是 1/0/2:1 + assert ch1.logical_port == "1/0/2" + assert ch1.logical_channel == "1/0/2:1" + assert ch1.tx_power_dbm == 1.53 + + # 验证 channel 2: config 是 1/0/2:2,state 是错误的 1/0/1:2 + # 应该优先使用 config 的值 + assert ch2.logical_port == "1/0/2" # 从 config 的 1/0/2:2 解析 + assert ch2.logical_channel == "1/0/2:2" # 使用 config 的值 + assert ch2.rx_power_dbm == 1.07 + + # 验证两个 channel 的 port 一致(都是 1/0/2) + assert ch1.logical_port == ch2.logical_port + + +def test_fallback_to_state_when_config_missing(): + """ + 测试当 config/description 不存在时,fallback 到 state/description。 + """ + xml = """\ + + + + + test-component + TRANSCEIVER + + + + 1 + + 1/0/5:1 + + + + + + + + +""" + _, channels = parse_netconf_response(xml, "device") + assert len(channels) == 1 + + ch = channels[0] + # 没有 config,应该 fallback 到 state + assert ch.logical_port == "1/0/5" + assert ch.logical_channel == "1/0/5:1" +