additional metrics for pcidevice and id to name conversion (#3425)

* * Rebase from upstream

* add sriov, power info support and pci id name resolution

Signed-off-by: Jain Johny <jj@asama.ai>

* fix/remove debug lines

Signed-off-by: Jain Johny <jj@asama.ai>

---------

Signed-off-by: Jain Johny <jj@asama.ai>

* add numa_node and missing test output file (#2)

* add numa_node and missing test output file

Signed-off-by: Jain Johny <jj@asama.ai>

* use c.logger for debug line

Signed-off-by: Jain Johny <jj@asama.ai>

* point to procfs master

Signed-off-by: Jain Johny <jj@asama.ai>

* include device_id in e2e-output pecidevice output

Signed-off-by: Jain Johny <jj@asama.ai>

* fix typo in e2e-output.txt

Signed-off-by: Jain Johny <jj@asama.ai>

* update test cases in e2e-64k-page-output.txt

Signed-off-by: Jain Johny <jj@asama.ai>

* fixing the rebase

Signed-off-by: Jain Johny <jj@asama.ai>

* move power_state to enum metric type, dont emit unavailable metrics

Signed-off-by: Jain Johny <jj@asama.ai>

* change test fixtures for power state change

Signed-off-by: Jain Johny <jj@asama.ai>

* change test fixtures for numa_node change

Signed-off-by: Jain Johny <jj@asama.ai>

---------

Signed-off-by: Jain Johny <jj@asama.ai>
pull/3422/merge
Jain Johny 3 months ago committed by GitHub
parent 4f4ff38d9b
commit 2f8f920951
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 60
      collector/fixtures/e2e-64k-page-output.txt
  2. 60
      collector/fixtures/e2e-output.txt
  3. 26
      collector/fixtures/pci.ids
  4. 95
      collector/fixtures/pcidevice-names-output.txt
  5. 2183
      collector/fixtures/sys.ttar
  6. 535
      collector/pcidevice_linux.go
  7. 89
      collector/pcidevice_linux_test.go

@ -2836,22 +2836,78 @@ node_os_version{id="ubuntu",id_like="debian",name="Ubuntu"} 20.04
# TYPE node_pcidevice_current_link_transfers_per_second gauge
node_pcidevice_current_link_transfers_per_second{bus="00",device="02",function="1",segment="0000"} 8e+09
node_pcidevice_current_link_transfers_per_second{bus="01",device="00",function="0",segment="0000"} 8e+09
node_pcidevice_current_link_transfers_per_second{bus="45",device="00",function="0",segment="0000"} 5e+09
# HELP node_pcidevice_current_link_width Value of current link's width (number of lanes)
# TYPE node_pcidevice_current_link_width gauge
node_pcidevice_current_link_width{bus="00",device="02",function="1",segment="0000"} 4
node_pcidevice_current_link_width{bus="01",device="00",function="0",segment="0000"} 4
node_pcidevice_current_link_width{bus="45",device="00",function="0",segment="0000"} 4
# HELP node_pcidevice_d3cold_allowed Whether the PCIe device supports D3cold power state (0/1).
# TYPE node_pcidevice_d3cold_allowed gauge
node_pcidevice_d3cold_allowed{bus="00",device="02",function="1",segment="0000"} 1
node_pcidevice_d3cold_allowed{bus="01",device="00",function="0",segment="0000"} 1
node_pcidevice_d3cold_allowed{bus="45",device="00",function="0",segment="0000"} 1
# HELP node_pcidevice_info Non-numeric data from /sys/bus/pci/devices/<location>, value is always 1.
# TYPE node_pcidevice_info gauge
node_pcidevice_info{bus="00",class_id="0x060400",device="02",function="1",parent_bus="*",parent_device="*",parent_function="*",parent_segment="*",revision="0x00",segment="0000",subsystem_device_id="0x5095",subsystem_vendor_id="0x17aa",vendor_id="0x1634"} 1
node_pcidevice_info{bus="01",class_id="0x010802",device="00",function="0",parent_bus="00",parent_device="02",parent_function="1",parent_segment="0000",revision="0x01",segment="0000",subsystem_device_id="0x5021",subsystem_vendor_id="0xc0a9",vendor_id="0x540a"} 1
node_pcidevice_info{bus="00",class_id="0x060400",device="02",device_id="0x1634",function="1",parent_bus="*",parent_device="*",parent_function="*",parent_segment="*",revision="0x00",segment="0000",subsystem_device_id="0x5095",subsystem_vendor_id="0x17aa",vendor_id="0x1022"} 1
node_pcidevice_info{bus="01",class_id="0x010802",device="00",device_id="0x540a",function="0",parent_bus="00",parent_device="02",parent_function="1",parent_segment="0000",revision="0x01",segment="0000",subsystem_device_id="0x5021",subsystem_vendor_id="0xc0a9",vendor_id="0xc0a9"} 1
node_pcidevice_info{bus="45",class_id="0x020000",device="00",device_id="0x1521",function="0",parent_bus="40",parent_device="01",parent_function="3",parent_segment="0000",revision="0x01",segment="0000",subsystem_device_id="0x00a3",subsystem_vendor_id="0x8086",vendor_id="0x8086"} 1
# HELP node_pcidevice_max_link_transfers_per_second Value of maximum link's transfers per second (T/s)
# TYPE node_pcidevice_max_link_transfers_per_second gauge
node_pcidevice_max_link_transfers_per_second{bus="00",device="02",function="1",segment="0000"} 8e+09
node_pcidevice_max_link_transfers_per_second{bus="01",device="00",function="0",segment="0000"} 1.6e+10
node_pcidevice_max_link_transfers_per_second{bus="45",device="00",function="0",segment="0000"} 5e+09
# HELP node_pcidevice_max_link_width Value of maximum link's width (number of lanes)
# TYPE node_pcidevice_max_link_width gauge
node_pcidevice_max_link_width{bus="00",device="02",function="1",segment="0000"} 8
node_pcidevice_max_link_width{bus="01",device="00",function="0",segment="0000"} 4
node_pcidevice_max_link_width{bus="45",device="00",function="0",segment="0000"} 4
# HELP node_pcidevice_numa_node NUMA node number for the PCI device. -1 indicates unknown or not available.
# TYPE node_pcidevice_numa_node gauge
node_pcidevice_numa_node{bus="45",device="00",function="0",segment="0000"} 0
# HELP node_pcidevice_power_state PCIe device power state, one of: D0, D1, D2, D3hot, D3cold, unknown or error.
# TYPE node_pcidevice_power_state gauge
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D0"} 1
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D1"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D2"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D3cold"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D3hot"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="error"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="unknown"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D0"} 1
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D1"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D2"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D3cold"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D3hot"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="error"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="unknown"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D0"} 1
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D1"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D2"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D3cold"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D3hot"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="error"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="unknown"} 0
# HELP node_pcidevice_sriov_drivers_autoprobe Whether SR-IOV drivers autoprobe is enabled for the device (0/1).
# TYPE node_pcidevice_sriov_drivers_autoprobe gauge
node_pcidevice_sriov_drivers_autoprobe{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_drivers_autoprobe{bus="01",device="00",function="0",segment="0000"} 1
node_pcidevice_sriov_drivers_autoprobe{bus="45",device="00",function="0",segment="0000"} 1
# HELP node_pcidevice_sriov_numvfs Number of Virtual Functions (VFs) currently enabled for SR-IOV.
# TYPE node_pcidevice_sriov_numvfs gauge
node_pcidevice_sriov_numvfs{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_numvfs{bus="01",device="00",function="0",segment="0000"} 4
node_pcidevice_sriov_numvfs{bus="45",device="00",function="0",segment="0000"} 0
# HELP node_pcidevice_sriov_totalvfs Total number of Virtual Functions (VFs) supported by the device.
# TYPE node_pcidevice_sriov_totalvfs gauge
node_pcidevice_sriov_totalvfs{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_totalvfs{bus="01",device="00",function="0",segment="0000"} 8
node_pcidevice_sriov_totalvfs{bus="45",device="00",function="0",segment="0000"} 7
# HELP node_pcidevice_sriov_vf_total_msix Total number of MSI-X vectors for Virtual Functions.
# TYPE node_pcidevice_sriov_vf_total_msix gauge
node_pcidevice_sriov_vf_total_msix{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_vf_total_msix{bus="01",device="00",function="0",segment="0000"} 16
node_pcidevice_sriov_vf_total_msix{bus="45",device="00",function="0",segment="0000"} 0
# HELP node_power_supply_capacity capacity value of /sys/class/power_supply/<power_supply>.
# TYPE node_power_supply_capacity gauge
node_power_supply_capacity{power_supply="BAT0"} 81

@ -2858,22 +2858,78 @@ node_os_version{id="ubuntu",id_like="debian",name="Ubuntu"} 20.04
# TYPE node_pcidevice_current_link_transfers_per_second gauge
node_pcidevice_current_link_transfers_per_second{bus="00",device="02",function="1",segment="0000"} 8e+09
node_pcidevice_current_link_transfers_per_second{bus="01",device="00",function="0",segment="0000"} 8e+09
node_pcidevice_current_link_transfers_per_second{bus="45",device="00",function="0",segment="0000"} 5e+09
# HELP node_pcidevice_current_link_width Value of current link's width (number of lanes)
# TYPE node_pcidevice_current_link_width gauge
node_pcidevice_current_link_width{bus="00",device="02",function="1",segment="0000"} 4
node_pcidevice_current_link_width{bus="01",device="00",function="0",segment="0000"} 4
node_pcidevice_current_link_width{bus="45",device="00",function="0",segment="0000"} 4
# HELP node_pcidevice_d3cold_allowed Whether the PCIe device supports D3cold power state (0/1).
# TYPE node_pcidevice_d3cold_allowed gauge
node_pcidevice_d3cold_allowed{bus="00",device="02",function="1",segment="0000"} 1
node_pcidevice_d3cold_allowed{bus="01",device="00",function="0",segment="0000"} 1
node_pcidevice_d3cold_allowed{bus="45",device="00",function="0",segment="0000"} 1
# HELP node_pcidevice_info Non-numeric data from /sys/bus/pci/devices/<location>, value is always 1.
# TYPE node_pcidevice_info gauge
node_pcidevice_info{bus="00",class_id="0x060400",device="02",function="1",parent_bus="*",parent_device="*",parent_function="*",parent_segment="*",revision="0x00",segment="0000",subsystem_device_id="0x5095",subsystem_vendor_id="0x17aa",vendor_id="0x1634"} 1
node_pcidevice_info{bus="01",class_id="0x010802",device="00",function="0",parent_bus="00",parent_device="02",parent_function="1",parent_segment="0000",revision="0x01",segment="0000",subsystem_device_id="0x5021",subsystem_vendor_id="0xc0a9",vendor_id="0x540a"} 1
node_pcidevice_info{bus="00",class_id="0x060400",device="02",device_id="0x1634",function="1",parent_bus="*",parent_device="*",parent_function="*",parent_segment="*",revision="0x00",segment="0000",subsystem_device_id="0x5095",subsystem_vendor_id="0x17aa",vendor_id="0x1022"} 1
node_pcidevice_info{bus="01",class_id="0x010802",device="00",device_id="0x540a",function="0",parent_bus="00",parent_device="02",parent_function="1",parent_segment="0000",revision="0x01",segment="0000",subsystem_device_id="0x5021",subsystem_vendor_id="0xc0a9",vendor_id="0xc0a9"} 1
node_pcidevice_info{bus="45",class_id="0x020000",device="00",device_id="0x1521",function="0",parent_bus="40",parent_device="01",parent_function="3",parent_segment="0000",revision="0x01",segment="0000",subsystem_device_id="0x00a3",subsystem_vendor_id="0x8086",vendor_id="0x8086"} 1
# HELP node_pcidevice_max_link_transfers_per_second Value of maximum link's transfers per second (T/s)
# TYPE node_pcidevice_max_link_transfers_per_second gauge
node_pcidevice_max_link_transfers_per_second{bus="00",device="02",function="1",segment="0000"} 8e+09
node_pcidevice_max_link_transfers_per_second{bus="01",device="00",function="0",segment="0000"} 1.6e+10
node_pcidevice_max_link_transfers_per_second{bus="45",device="00",function="0",segment="0000"} 5e+09
# HELP node_pcidevice_max_link_width Value of maximum link's width (number of lanes)
# TYPE node_pcidevice_max_link_width gauge
node_pcidevice_max_link_width{bus="00",device="02",function="1",segment="0000"} 8
node_pcidevice_max_link_width{bus="01",device="00",function="0",segment="0000"} 4
node_pcidevice_max_link_width{bus="45",device="00",function="0",segment="0000"} 4
# HELP node_pcidevice_numa_node NUMA node number for the PCI device. -1 indicates unknown or not available.
# TYPE node_pcidevice_numa_node gauge
node_pcidevice_numa_node{bus="45",device="00",function="0",segment="0000"} 0
# HELP node_pcidevice_power_state PCIe device power state, one of: D0, D1, D2, D3hot, D3cold, unknown or error.
# TYPE node_pcidevice_power_state gauge
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D0"} 1
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D1"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D2"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D3cold"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D3hot"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="error"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="unknown"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D0"} 1
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D1"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D2"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D3cold"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D3hot"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="error"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="unknown"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D0"} 1
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D1"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D2"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D3cold"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D3hot"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="error"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="unknown"} 0
# HELP node_pcidevice_sriov_drivers_autoprobe Whether SR-IOV drivers autoprobe is enabled for the device (0/1).
# TYPE node_pcidevice_sriov_drivers_autoprobe gauge
node_pcidevice_sriov_drivers_autoprobe{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_drivers_autoprobe{bus="01",device="00",function="0",segment="0000"} 1
node_pcidevice_sriov_drivers_autoprobe{bus="45",device="00",function="0",segment="0000"} 1
# HELP node_pcidevice_sriov_numvfs Number of Virtual Functions (VFs) currently enabled for SR-IOV.
# TYPE node_pcidevice_sriov_numvfs gauge
node_pcidevice_sriov_numvfs{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_numvfs{bus="01",device="00",function="0",segment="0000"} 4
node_pcidevice_sriov_numvfs{bus="45",device="00",function="0",segment="0000"} 0
# HELP node_pcidevice_sriov_totalvfs Total number of Virtual Functions (VFs) supported by the device.
# TYPE node_pcidevice_sriov_totalvfs gauge
node_pcidevice_sriov_totalvfs{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_totalvfs{bus="01",device="00",function="0",segment="0000"} 8
node_pcidevice_sriov_totalvfs{bus="45",device="00",function="0",segment="0000"} 7
# HELP node_pcidevice_sriov_vf_total_msix Total number of MSI-X vectors for Virtual Functions.
# TYPE node_pcidevice_sriov_vf_total_msix gauge
node_pcidevice_sriov_vf_total_msix{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_vf_total_msix{bus="01",device="00",function="0",segment="0000"} 16
node_pcidevice_sriov_vf_total_msix{bus="45",device="00",function="0",segment="0000"} 0
# HELP node_power_supply_capacity capacity value of /sys/class/power_supply/<power_supply>.
# TYPE node_power_supply_capacity gauge
node_power_supply_capacity{power_supply="BAT0"} 81

@ -0,0 +1,26 @@
# Test PCI IDs file for node_exporter testing
# This file contains sample entries for testing PCI name resolution
# Classes
C 06 Bridge device
04 PCI bridge
C 01 Mass storage controller
08 Non-Volatile memory controller
02 NVM Express
C 02 Network controller
00 Ethernet controller
# Vendors
1022 Advanced Micro Devices, Inc. [AMD]
1634 Renoir/Cezanne PCIe GPP Bridge
17aa 5095 T540-5095 Unified Wire Ethernet Controller
c0a9 Micron/Crucial Technology
540a P2 [Nick P2] / P3 / P3 Plus NVMe PCIe SSD (DRAM-less)
c0a9 5021 PS5021-E21 PCIe4 NVMe Controller (DRAM-less)
8086 Intel Corporation
1521 I350 Gigabit Network Connection
8086 00a3 Ethernet Network Adapter I350-T4 for OCP NIC 3.0
17aa Lenovo

@ -0,0 +1,95 @@
# Test output for PCI device collector with name resolution enabled
# This file demonstrates the --collector.pcidevice.names=true functionality
# HELP node_pcidevice_current_link_transfers_per_second Value of current link's transfers per second (T/s)
# TYPE node_pcidevice_current_link_transfers_per_second gauge
node_pcidevice_current_link_transfers_per_second{bus="00",device="02",function="1",segment="0000"} 8e+09
node_pcidevice_current_link_transfers_per_second{bus="01",device="00",function="0",segment="0000"} 8e+09
node_pcidevice_current_link_transfers_per_second{bus="45",device="00",function="0",segment="0000"} 5e+09
# HELP node_pcidevice_current_link_width Value of current link's width (number of lanes)
# TYPE node_pcidevice_current_link_width gauge
node_pcidevice_current_link_width{bus="00",device="02",function="1",segment="0000"} 4
node_pcidevice_current_link_width{bus="01",device="00",function="0",segment="0000"} 4
node_pcidevice_current_link_width{bus="45",device="00",function="0",segment="0000"} 4
# HELP node_pcidevice_d3cold_allowed Whether the PCIe device supports D3cold power state (0/1).
# TYPE node_pcidevice_d3cold_allowed gauge
node_pcidevice_d3cold_allowed{bus="00",device="02",function="1",segment="0000"} 1
node_pcidevice_d3cold_allowed{bus="01",device="00",function="0",segment="0000"} 1
node_pcidevice_d3cold_allowed{bus="45",device="00",function="0",segment="0000"} 1
# HELP node_pcidevice_info Non-numeric data from /sys/bus/pci/devices/<location>, value is always 1.
# TYPE node_pcidevice_info gauge
# Example 1: AMD PCIe Bridge with Lenovo subsystem
node_pcidevice_info{bus="00",class_id="0x060400",class_name="PCI bridge",device="02",device_id="0x1634",device_name="Renoir/Cezanne PCIe GPP Bridge",function="1",parent_bus="*",parent_device="*",parent_function="*",parent_segment="*",revision="0x00",segment="0000",subsystem_device_id="0x5095",subsystem_device_name="T540-5095 Unified Wire Ethernet Controller",subsystem_vendor_id="0x17aa",subsystem_vendor_name="Lenovo",vendor_id="0x1022",vendor_name="Advanced Micro Devices, Inc. [AMD]"} 1
# Example 2: Micron/Crucial NVMe Controller
node_pcidevice_info{bus="01",class_id="0x010802",class_name="NVM Express",device="00",device_id="0x540a",device_name="P2 [Nick P2] / P3 / P3 Plus NVMe PCIe SSD (DRAM-less)",function="0",parent_bus="00",parent_device="02",parent_function="1",parent_segment="0000",revision="0x01",segment="0000",subsystem_device_id="0x5021",subsystem_device_name="PS5021-E21 PCIe4 NVMe Controller (DRAM-less)",subsystem_vendor_id="0xc0a9",subsystem_vendor_name="Micron/Crucial Technology",vendor_id="0xc0a9",vendor_name="Micron/Crucial Technology"} 1
# Example 3: Intel Network Controller
node_pcidevice_info{bus="45",class_id="0x020000",class_name="Ethernet controller",device="00",device_id="0x1521",device_name="I350 Gigabit Network Connection",function="0",parent_bus="40",parent_device="01",parent_function="3",parent_segment="0000",revision="0x01",segment="0000",subsystem_device_id="0x00a3",subsystem_device_name="Ethernet Network Adapter I350-T4 for OCP NIC 3.0",subsystem_vendor_id="0x8086",subsystem_vendor_name="Intel Corporation",vendor_id="0x8086",vendor_name="Intel Corporation"} 1
# HELP node_pcidevice_numa_node NUMA node number for the PCI device. -1 indicates unknown or not available.
# TYPE node_pcidevice_numa_node gauge
node_pcidevice_numa_node{bus="45",device="00",function="0",segment="0000"} 0
# HELP node_pcidevice_max_link_transfers_per_second Value of maximum link's transfers per second (T/s)
# TYPE node_pcidevice_max_link_transfers_per_second gauge
node_pcidevice_max_link_transfers_per_second{bus="00",device="02",function="1",segment="0000"} 8e+09
node_pcidevice_max_link_transfers_per_second{bus="01",device="00",function="0",segment="0000"} 1.6e+10
node_pcidevice_max_link_transfers_per_second{bus="45",device="00",function="0",segment="0000"} 5e+09
# HELP node_pcidevice_max_link_width Value of maximum link's width (number of lanes)
# TYPE node_pcidevice_max_link_width gauge
node_pcidevice_max_link_width{bus="00",device="02",function="1",segment="0000"} 8
node_pcidevice_max_link_width{bus="01",device="00",function="0",segment="0000"} 4
node_pcidevice_max_link_width{bus="45",device="00",function="0",segment="0000"} 4
# HELP node_pcidevice_power_state PCIe device power state, one of: D0, D1, D2, D3hot, D3cold, unknown or error.
# TYPE node_pcidevice_power_state gauge
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D0"} 1
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D1"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D2"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D3cold"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="D3hot"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="error"} 0
node_pcidevice_power_state{bus="00",device="02",function="1",segment="0000",state="unknown"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D0"} 1
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D1"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D2"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D3cold"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="D3hot"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="error"} 0
node_pcidevice_power_state{bus="01",device="00",function="0",segment="0000",state="unknown"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D0"} 1
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D1"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D2"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D3cold"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="D3hot"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="error"} 0
node_pcidevice_power_state{bus="45",device="00",function="0",segment="0000",state="unknown"} 0
# HELP node_pcidevice_sriov_drivers_autoprobe Whether SR-IOV drivers autoprobe is enabled for the device (0/1).
# TYPE node_pcidevice_sriov_drivers_autoprobe gauge
node_pcidevice_sriov_drivers_autoprobe{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_drivers_autoprobe{bus="01",device="00",function="0",segment="0000"} 1
node_pcidevice_sriov_drivers_autoprobe{bus="45",device="00",function="0",segment="0000"} 1
# HELP node_pcidevice_sriov_numvfs Number of Virtual Functions (VFs) currently enabled for SR-IOV.
# TYPE node_pcidevice_sriov_numvfs gauge
node_pcidevice_sriov_numvfs{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_numvfs{bus="01",device="00",function="0",segment="0000"} 4
node_pcidevice_sriov_numvfs{bus="45",device="00",function="0",segment="0000"} 0
# HELP node_pcidevice_sriov_totalvfs Total number of Virtual Functions (VFs) supported by the device.
# TYPE node_pcidevice_sriov_totalvfs gauge
node_pcidevice_sriov_totalvfs{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_totalvfs{bus="01",device="00",function="0",segment="0000"} 8
node_pcidevice_sriov_totalvfs{bus="45",device="00",function="0",segment="0000"} 7
# HELP node_pcidevice_sriov_vf_total_msix Total number of MSI-X vectors for Virtual Functions.
# TYPE node_pcidevice_sriov_vf_total_msix gauge
node_pcidevice_sriov_vf_total_msix{bus="00",device="02",function="1",segment="0000"} 0
node_pcidevice_sriov_vf_total_msix{bus="01",device="00",function="0",segment="0000"} 16
node_pcidevice_sriov_vf_total_msix{bus="45",device="00",function="0",segment="0000"} 0

File diff suppressed because it is too large Load Diff

@ -17,11 +17,14 @@
package collector
import (
"bufio"
"errors"
"fmt"
"log/slog"
"os"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs/sysfs"
)
@ -31,6 +34,13 @@ const (
)
var (
pciIdsPaths = []string{
"/usr/share/misc/pci.ids",
"/usr/share/hwdata/pci.ids",
}
pciIdsFile = kingpin.Flag("collector.pcidevice.idsfile", "Path to pci.ids file to use for PCI device identification.").String()
pciNames = kingpin.Flag("collector.pcidevice.names", "Enable PCI device name resolution (requires pci.ids file).").Default("false").Bool()
pcideviceLabelNames = []string{"segment", "bus", "device", "function"}
pcideviceMaxLinkTSDesc = prometheus.NewDesc(
@ -54,13 +64,62 @@ var (
"Value of current link's width (number of lanes)",
pcideviceLabelNames, nil,
)
pcidevicePowerStateDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, pcideviceSubsystem, "power_state"),
"PCIe device power state, one of: D0, D1, D2, D3hot, D3cold, unknown or error.",
append(pcideviceLabelNames, "state"), nil,
)
pcideviceD3coldAllowedDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, pcideviceSubsystem, "d3cold_allowed"),
"Whether the PCIe device supports D3cold power state (0/1).",
pcideviceLabelNames, nil,
)
pcideviceSriovDriversAutoprobeDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, pcideviceSubsystem, "sriov_drivers_autoprobe"),
"Whether SR-IOV drivers autoprobe is enabled for the device (0/1).",
pcideviceLabelNames, nil,
)
pcideviceSriovNumvfsDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, pcideviceSubsystem, "sriov_numvfs"),
"Number of Virtual Functions (VFs) currently enabled for SR-IOV.",
pcideviceLabelNames, nil,
)
pcideviceSriovTotalvfsDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, pcideviceSubsystem, "sriov_totalvfs"),
"Total number of Virtual Functions (VFs) supported by the device.",
pcideviceLabelNames, nil,
)
pcideviceSriovVfTotalMsixDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, pcideviceSubsystem, "sriov_vf_total_msix"),
"Total number of MSI-X vectors for Virtual Functions.",
pcideviceLabelNames, nil,
)
pcideviceNumaNodeDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, pcideviceSubsystem, "numa_node"),
"NUMA node number for the PCI device. -1 indicates unknown or not available.",
pcideviceLabelNames, nil,
)
)
type pcideviceCollector struct {
fs sysfs.FS
infoDesc typedDesc
descs []typedFactorDesc
logger *slog.Logger
fs sysfs.FS
infoDesc typedDesc
descs []typedFactorDesc
logger *slog.Logger
pciVendors map[string]string
pciDevices map[string]map[string]string
pciSubsystems map[string]map[string]string
pciClasses map[string]string
pciSubclasses map[string]string
pciProgIfs map[string]string
pciNames bool
}
func init() {
@ -74,29 +133,49 @@ func NewPcideviceCollector(logger *slog.Logger) (Collector, error) {
return nil, fmt.Errorf("failed to open sysfs: %w", err)
}
c := pcideviceCollector{
fs: fs,
logger: logger,
infoDesc: typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, pcideviceSubsystem, "info"),
"Non-numeric data from /sys/bus/pci/devices/<location>, value is always 1.",
append(pcideviceLabelNames,
[]string{"parent_segment", "parent_bus", "parent_device", "parent_function",
"class_id", "vendor_id", "subsystem_vendor_id", "subsystem_device_id", "revision"}...),
nil,
),
valueType: prometheus.GaugeValue,
},
descs: []typedFactorDesc{
{desc: pcideviceMaxLinkTSDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceMaxLinkWidthDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceCurrentLinkTSDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceCurrentLinkWidthDesc, valueType: prometheus.GaugeValue},
},
}
return &c, nil
// Initialize PCI ID maps
c := &pcideviceCollector{
fs: fs,
logger: logger,
pciNames: *pciNames,
}
// Build label names based on whether name resolution is enabled
labelNames := append(pcideviceLabelNames,
[]string{"parent_segment", "parent_bus", "parent_device", "parent_function",
"class_id", "vendor_id", "device_id", "subsystem_vendor_id", "subsystem_device_id", "revision"}...)
if c.pciNames {
c.loadPCIIds()
// Add name labels when name resolution is enabled
labelNames = append(labelNames, "vendor_name", "device_name", "subsystem_vendor_name", "subsystem_device_name", "class_name")
}
c.infoDesc = typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, pcideviceSubsystem, "info"),
"Non-numeric data from /sys/bus/pci/devices/<location>, value is always 1.",
labelNames,
nil,
),
valueType: prometheus.GaugeValue,
}
c.descs = []typedFactorDesc{
{desc: pcideviceMaxLinkTSDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceMaxLinkWidthDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceCurrentLinkTSDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceCurrentLinkWidthDesc, valueType: prometheus.GaugeValue},
{desc: pcidevicePowerStateDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceD3coldAllowedDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceSriovDriversAutoprobeDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceSriovNumvfsDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceSriovTotalvfsDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceSriovVfTotalMsixDesc, valueType: prometheus.GaugeValue},
{desc: pcideviceNumaNodeDesc, valueType: prometheus.GaugeValue},
}
return c, nil
}
func (c *pcideviceCollector) Update(ch chan<- prometheus.Metric) error {
@ -117,27 +196,405 @@ func (c *pcideviceCollector) Update(ch chan<- prometheus.Metric) error {
} else {
values = append(values, []string{"*", "*", "*", "*"}...)
}
values = append(values, fmt.Sprintf("0x%06x", device.Class))
values = append(values, fmt.Sprintf("0x%04x", device.Device))
values = append(values, fmt.Sprintf("0x%04x", device.SubsystemVendor))
values = append(values, fmt.Sprintf("0x%04x", device.SubsystemDevice))
values = append(values, fmt.Sprintf("0x%02x", device.Revision))
// Add basic device information
classID := fmt.Sprintf("0x%06x", device.Class)
vendorID := fmt.Sprintf("0x%04x", device.Vendor)
deviceID := fmt.Sprintf("0x%04x", device.Device)
subsysVendorID := fmt.Sprintf("0x%04x", device.SubsystemVendor)
subsysDeviceID := fmt.Sprintf("0x%04x", device.SubsystemDevice)
values = append(values, classID, vendorID, deviceID, subsysVendorID, subsysDeviceID, fmt.Sprintf("0x%02x", device.Revision))
// Add name values if name resolution is enabled
if c.pciNames {
vendorName := c.getPCIVendorName(vendorID)
deviceName := c.getPCIDeviceName(vendorID, deviceID)
subsysVendorName := c.getPCIVendorName(subsysVendorID)
subsysDeviceName := c.getPCISubsystemName(vendorID, deviceID, subsysVendorID, subsysDeviceID)
className := c.getPCIClassName(classID)
values = append(values, vendorName, deviceName, subsysVendorName, subsysDeviceName, className)
}
ch <- c.infoDesc.mustNewConstMetric(1.0, values...)
// MaxLinkSpeed and CurrentLinkSpeed are represented in GT/s
maxLinkSpeedTS := float64(int64(*device.MaxLinkSpeed * 1e9))
currentLinkSpeedTS := float64(int64(*device.CurrentLinkSpeed * 1e9))
var maxLinkSpeedTS float64
if device.MaxLinkSpeed != nil {
maxLinkSpeedTS = (*device.MaxLinkSpeed) * 1e9
} else {
maxLinkSpeedTS = -1
}
for i, val := range []float64{
var currentLinkSpeedTS float64
if device.CurrentLinkSpeed != nil {
currentLinkSpeedTS = (*device.CurrentLinkSpeed) * 1e9
} else {
currentLinkSpeedTS = -1
}
// Get power state information directly from device object
var currentPowerState string
var hasPowerState bool
if device.PowerState != nil {
currentPowerState = device.PowerState.String()
hasPowerState = true
}
var d3coldAllowed float64
if device.D3coldAllowed != nil {
if *device.D3coldAllowed {
d3coldAllowed = 1
} else {
d3coldAllowed = 0
}
}
// Get SR-IOV information directly from device object
var sriovDriversAutoprobe float64
if device.SriovDriversAutoprobe != nil {
if *device.SriovDriversAutoprobe {
sriovDriversAutoprobe = 1
} else {
sriovDriversAutoprobe = 0
}
}
var sriovNumvfs float64
if device.SriovNumvfs != nil {
sriovNumvfs = float64(*device.SriovNumvfs)
}
var sriovTotalvfs float64
if device.SriovTotalvfs != nil {
sriovTotalvfs = float64(*device.SriovTotalvfs)
}
var sriovVfTotalMsix float64
if device.SriovVfTotalMsix != nil {
sriovVfTotalMsix = float64(*device.SriovVfTotalMsix)
}
// Handle numa_node with nil safety
var numaNode float64
if device.NumaNode != nil {
numaNode = float64(*device.NumaNode)
} else {
numaNode = -1
}
// Handle link width fields with nil safety
var maxLinkWidth float64
if device.MaxLinkWidth != nil {
maxLinkWidth = float64(*device.MaxLinkWidth)
} else {
maxLinkWidth = -1
}
var currentLinkWidth float64
if device.CurrentLinkWidth != nil {
currentLinkWidth = float64(*device.CurrentLinkWidth)
} else {
currentLinkWidth = -1
}
// Emit metrics for all fields except numa_node and power_state
metrics := []float64{
maxLinkSpeedTS,
float64(*device.MaxLinkWidth),
maxLinkWidth,
currentLinkSpeedTS,
float64(*device.CurrentLinkWidth),
} {
ch <- c.descs[i].mustNewConstMetric(val, device.Location.Strings()...)
currentLinkWidth,
d3coldAllowed,
sriovDriversAutoprobe,
sriovNumvfs,
sriovTotalvfs,
sriovVfTotalMsix,
}
// Emit regular metrics (excluding power_state which is at index 4)
metricIndices := []int{0, 1, 2, 3, 5, 6, 7, 8, 9} // Skip power_state (4) and numa_node (10)
for i, val := range metrics {
ch <- c.descs[metricIndices[i]].mustNewConstMetric(val, device.Location.Strings()...)
}
// Emit power state metrics with state labels only if power state is available
if hasPowerState {
powerStates := []string{"D0", "D1", "D2", "D3hot", "D3cold", "unknown", "error"}
deviceLabels := device.Location.Strings()
for _, state := range powerStates {
var value float64
if state == currentPowerState {
value = 1
} else {
value = 0
}
stateLabels := append(deviceLabels, state)
ch <- c.descs[4].mustNewConstMetric(value, stateLabels...)
}
}
// Only emit numa_node metric if the value is available (not -1)
if numaNode != -1 {
ch <- c.descs[10].mustNewConstMetric(numaNode, device.Location.Strings()...)
}
}
return nil
}
// loadPCIIds loads PCI device information from pci.ids file
func (c *pcideviceCollector) loadPCIIds() {
var file *os.File
var err error
c.pciVendors = make(map[string]string)
c.pciDevices = make(map[string]map[string]string)
c.pciSubsystems = make(map[string]map[string]string)
c.pciClasses = make(map[string]string)
c.pciSubclasses = make(map[string]string)
c.pciProgIfs = make(map[string]string)
// Use custom pci.ids file if specified
if *pciIdsFile != "" {
file, err = os.Open(*pciIdsFile)
if err != nil {
c.logger.Debug("Failed to open PCI IDs file", "file", *pciIdsFile, "error", err)
return
}
c.logger.Debug("Loading PCI IDs from", "file", *pciIdsFile)
} else {
// Try each possible default path
for _, path := range pciIdsPaths {
file, err = os.Open(path)
if err == nil {
c.logger.Debug("Loading PCI IDs from default path", "path", path)
break
}
}
if err != nil {
c.logger.Debug("Failed to open any default PCI IDs file", "error", err)
return
}
}
defer file.Close()
scanner := bufio.NewScanner(file)
var currentVendor, currentDevice, currentBaseClass, currentSubclass string
var inClassContext bool
for scanner.Scan() {
line := scanner.Text()
if line == "" || strings.HasPrefix(line, "#") {
continue
}
// Handle class lines (starts with 'C')
if strings.HasPrefix(line, "C ") {
parts := strings.SplitN(line, " ", 2)
if len(parts) >= 2 {
classID := strings.TrimSpace(parts[0][1:]) // Remove 'C' prefix
className := strings.TrimSpace(parts[1])
c.pciClasses[classID] = className
currentBaseClass = classID
inClassContext = true
}
continue
}
// Handle subclass lines (single tab after class)
if strings.HasPrefix(line, "\t") && !strings.HasPrefix(line, "\t\t") && inClassContext {
line = strings.TrimPrefix(line, "\t")
parts := strings.SplitN(line, " ", 2)
if len(parts) >= 2 && currentBaseClass != "" {
subclassID := strings.TrimSpace(parts[0])
subclassName := strings.TrimSpace(parts[1])
// Store as base class + subclass (e.g., "0100" for SCSI storage controller)
fullClassID := currentBaseClass + subclassID
c.pciSubclasses[fullClassID] = subclassName
currentSubclass = fullClassID
}
continue
}
// Handle programming interface lines (double tab after subclass)
if strings.HasPrefix(line, "\t\t") && !strings.HasPrefix(line, "\t\t\t") && inClassContext {
line = strings.TrimPrefix(line, "\t\t")
parts := strings.SplitN(line, " ", 2)
if len(parts) >= 2 && currentSubclass != "" {
progIfID := strings.TrimSpace(parts[0])
progIfName := strings.TrimSpace(parts[1])
// Store as base class + subclass + programming interface (e.g., "010802" for NVM Express)
fullClassID := currentSubclass + progIfID
c.pciProgIfs[fullClassID] = progIfName
}
continue
}
// Handle vendor lines (no leading whitespace, not starting with 'C')
if !strings.HasPrefix(line, "\t") && !strings.HasPrefix(line, "C ") {
parts := strings.SplitN(line, " ", 2)
if len(parts) >= 2 {
currentVendor = strings.TrimSpace(parts[0])
c.pciVendors[currentVendor] = strings.TrimSpace(parts[1])
currentDevice = ""
inClassContext = false
}
continue
}
// Handle device lines (single tab)
if strings.HasPrefix(line, "\t") && !strings.HasPrefix(line, "\t\t") {
line = strings.TrimPrefix(line, "\t")
parts := strings.SplitN(line, " ", 2)
if len(parts) >= 2 && currentVendor != "" {
currentDevice = strings.TrimSpace(parts[0])
if c.pciDevices[currentVendor] == nil {
c.pciDevices[currentVendor] = make(map[string]string)
}
c.pciDevices[currentVendor][currentDevice] = strings.TrimSpace(parts[1])
}
continue
}
// Handle subsystem lines (double tab)
if strings.HasPrefix(line, "\t\t") {
line = strings.TrimPrefix(line, "\t\t")
parts := strings.SplitN(line, " ", 2)
if len(parts) >= 2 && currentVendor != "" && currentDevice != "" {
subsysID := strings.TrimSpace(parts[0])
subsysName := strings.TrimSpace(parts[1])
key := fmt.Sprintf("%s:%s", currentVendor, currentDevice)
if c.pciSubsystems[key] == nil {
c.pciSubsystems[key] = make(map[string]string)
}
// Convert subsystem ID from "vendor device" format to "vendor:device" format
subsysParts := strings.Fields(subsysID)
if len(subsysParts) == 2 {
subsysKey := fmt.Sprintf("%s:%s", subsysParts[0], subsysParts[1])
c.pciSubsystems[key][subsysKey] = subsysName
}
}
}
}
// Debug summary
totalDevices := 0
for _, devices := range c.pciDevices {
totalDevices += len(devices)
}
totalSubsystems := 0
for _, subsystems := range c.pciSubsystems {
totalSubsystems += len(subsystems)
}
c.logger.Debug("Loaded PCI device data",
"vendors", len(c.pciVendors),
"devices", totalDevices,
"subsystems", totalSubsystems,
"classes", len(c.pciClasses),
"subclasses", len(c.pciSubclasses),
"progIfs", len(c.pciProgIfs),
)
}
// getPCIVendorName converts PCI vendor ID to human-readable string using pci.ids
func (c *pcideviceCollector) getPCIVendorName(vendorID string) string {
// Return original ID if name resolution is disabled
if !c.pciNames {
return vendorID
}
// Remove "0x" prefix if present
vendorID = strings.TrimPrefix(vendorID, "0x")
vendorID = strings.ToLower(vendorID)
if name, ok := c.pciVendors[vendorID]; ok {
return name
}
return vendorID // Return ID if name not found
}
// getPCIDeviceName converts PCI device ID to human-readable string using pci.ids
func (c *pcideviceCollector) getPCIDeviceName(vendorID, deviceID string) string {
// Return original ID if name resolution is disabled
if !c.pciNames {
return deviceID
}
// Remove "0x" prefix if present
vendorID = strings.TrimPrefix(vendorID, "0x")
deviceID = strings.TrimPrefix(deviceID, "0x")
vendorID = strings.ToLower(vendorID)
deviceID = strings.ToLower(deviceID)
if devices, ok := c.pciDevices[vendorID]; ok {
if name, ok := devices[deviceID]; ok {
return name
}
}
return deviceID // Return ID if name not found
}
// getPCISubsystemName converts PCI subsystem ID to human-readable string using pci.ids
func (c *pcideviceCollector) getPCISubsystemName(vendorID, deviceID, subsysVendorID, subsysDeviceID string) string {
// Return original ID if name resolution is disabled
if !c.pciNames {
return subsysDeviceID
}
// Normalize all IDs
vendorID = strings.TrimPrefix(vendorID, "0x")
deviceID = strings.TrimPrefix(deviceID, "0x")
subsysVendorID = strings.TrimPrefix(subsysVendorID, "0x")
subsysDeviceID = strings.TrimPrefix(subsysDeviceID, "0x")
key := fmt.Sprintf("%s:%s", vendorID, deviceID)
subsysKey := fmt.Sprintf("%s:%s", subsysVendorID, subsysDeviceID)
if subsystems, ok := c.pciSubsystems[key]; ok {
if name, ok := subsystems[subsysKey]; ok {
return name
}
}
return subsysDeviceID
}
// getPCIClassName converts PCI class ID to human-readable string using pci.ids
func (c *pcideviceCollector) getPCIClassName(classID string) string {
// Return original ID if name resolution is disabled
if !c.pciNames {
return classID
}
// Remove "0x" prefix if present and normalize
classID = strings.TrimPrefix(classID, "0x")
classID = strings.ToLower(classID)
// Try to find the programming interface first (6 digits: base class + subclass + programming interface)
if len(classID) >= 6 {
progIf := classID[:6]
if className, exists := c.pciProgIfs[progIf]; exists {
return className
}
}
// Try to find the subclass (4 digits: base class + subclass)
if len(classID) >= 4 {
subclass := classID[:4]
if className, exists := c.pciSubclasses[subclass]; exists {
return className
}
}
// If not found, try with just the base class (first 2 digits)
if len(classID) >= 2 {
baseClass := classID[:2]
if className, exists := c.pciClasses[baseClass]; exists {
return className
}
}
// Return the original class ID if not found
return "Unknown class (" + classID + ")"
}

@ -0,0 +1,89 @@
// Copyright 2024 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !nopcidevice
// +build !nopcidevice
package collector
import (
"fmt"
"io"
"log/slog"
"os"
"strings"
"testing"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
)
func TestPCICollectorWithNameResolution(t *testing.T) {
// Test the PCI collector with name resolution enabled and compare against expected output
if _, err := kingpin.CommandLine.Parse([]string{
"--path.sysfs", "fixtures/sys",
"--path.procfs", "fixtures/proc",
"--path.rootfs", "fixtures",
"--collector.pcidevice",
"--collector.pcidevice.names",
// "--collector.pcidevice.idsfile", "/usr/share/misc/pci.ids",
"--collector.pcidevice.idsfile", "fixtures/pci.ids",
}); err != nil {
t.Fatal(err)
}
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
c, err := NewPcideviceCollector(logger)
if err != nil {
t.Fatal(err)
}
reg := prometheus.NewRegistry()
reg.MustRegister(&testPCICollector{pc: c})
// Read expected output from fixture file
expectedOutput, err := os.ReadFile("fixtures/pcidevice-names-output.txt")
if err != nil {
t.Fatal(err)
}
err = testutil.GatherAndCompare(reg, strings.NewReader(string(expectedOutput)))
if err != nil {
t.Fatal(err)
}
}
// testPCICollector wraps the PCI collector for testing
type testPCICollector struct {
pc Collector
}
func (tc *testPCICollector) Collect(ch chan<- prometheus.Metric) {
sink := make(chan prometheus.Metric)
go func() {
err := tc.pc.Update(sink)
if err != nil {
panic(fmt.Errorf("failed to update collector: %s", err))
}
close(sink)
}()
for m := range sink {
ch <- m
}
}
func (tc *testPCICollector) Describe(ch chan<- *prometheus.Desc) {
// No-op for testing
}
Loading…
Cancel
Save