From 53848dde0675c44f024bdf485579cf833f6f812d Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Wed, 9 Jul 2025 11:46:35 +0530 Subject: [PATCH 01/14] adding nvme namespace parameters Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-64k-page-output.txt | 2 +- collector/fixtures/e2e-output.txt | 2 +- collector/nvme_linux.go | 148 +++++++++++++++++++-- 3 files changed, 139 insertions(+), 13 deletions(-) diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index 107d713b72..66b321a587 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -2825,7 +2825,7 @@ node_nfsd_server_rpcs_total 18628 node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge -node_nvme_info{device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 +node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 45938a2b98..ce555fc804 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2847,7 +2847,7 @@ node_nfsd_server_rpcs_total 18628 node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge -node_nvme_info{device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 +node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 diff --git a/collector/nvme_linux.go b/collector/nvme_linux.go index d1a9a87b55..88218e5ce0 100644 --- a/collector/nvme_linux.go +++ b/collector/nvme_linux.go @@ -21,14 +21,23 @@ import ( "fmt" "log/slog" "os" + "path/filepath" + "regexp" + "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) type nvmeCollector struct { - fs sysfs.FS - logger *slog.Logger + fs sysfs.FS + logger *slog.Logger + namespaceInfo *prometheus.Desc + namespaceCapacityBytes *prometheus.Desc + namespaceSizeBytes *prometheus.Desc + namespaceUsedBytes *prometheus.Desc + namespaceLogicalBlockSizeBytes *prometheus.Desc + info *prometheus.Desc } func init() { @@ -42,9 +51,51 @@ func NewNVMeCollector(logger *slog.Logger) (Collector, error) { return nil, fmt.Errorf("failed to open sysfs: %w", err) } + info := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "info"), + "Non-numeric data from /sys/class/nvme/, value is always 1.", + []string{"device", "firmware_revision", "model", "serial", "state", "cntlid"}, + nil, + ) + namespaceInfo := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_info"), + "Information about NVMe namespaces. Value is always 1", + []string{"device", "nsid", "ana_state"}, nil, + ) + + namespaceCapacityBytes := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_capacity_bytes"), + "Capacity of the NVMe namespace in bytes. Computed as namespace_size * namespace_logical_block_size", + []string{"device", "nsid"}, nil, + ) + + namespaceSizeBytes := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_size_bytes"), + "Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size", + []string{"device", "nsid"}, nil, + ) + + namespaceUsedBytes := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_used_bytes"), + "Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse", + []string{"device", "nsid"}, nil, + ) + + namespaceLogicalBlockSizeBytes := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_logical_block_size_bytes"), + "Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size", + []string{"device", "nsid"}, nil, + ) + return &nvmeCollector{ - fs: fs, - logger: logger, + fs: fs, + logger: logger, + namespaceInfo: namespaceInfo, + namespaceCapacityBytes: namespaceCapacityBytes, + namespaceSizeBytes: namespaceSizeBytes, + namespaceUsedBytes: namespaceUsedBytes, + namespaceLogicalBlockSizeBytes: namespaceLogicalBlockSizeBytes, + info: info, }, nil } @@ -59,15 +110,90 @@ func (c *nvmeCollector) Update(ch chan<- prometheus.Metric) error { } for _, device := range devices { - infoDesc := prometheus.NewDesc( - prometheus.BuildFQName(namespace, "nvme", "info"), - "Non-numeric data from /sys/class/nvme/, value is always 1.", - []string{"device", "firmware_revision", "model", "serial", "state"}, - nil, - ) infoValue := 1.0 - ch <- prometheus.MustNewConstMetric(infoDesc, prometheus.GaugeValue, infoValue, device.Name, device.FirmwareRevision, device.Model, device.Serial, device.State) + + devicePath := filepath.Join(*sysPath, "class/nvme", device.Name) + ch <- prometheus.MustNewConstMetric(c.info, prometheus.GaugeValue, infoValue, device.Name, device.FirmwareRevision, device.Model, device.Serial, device.State, device.ControllerID) + // Find namespace directories. + namespacePaths, err := filepath.Glob(filepath.Join(devicePath, "nvme[0-9]*c[0-9]*n[0-9]*")) + if err != nil { + c.logger.Error("failed to list NVMe namespaces", "device", device.Name, "err", err) + continue + } + re := regexp.MustCompile(`nvme[0-9]+c[0-9]+n([0-9]+)`) + + for _, namespacePath := range namespacePaths { + + // Read namespace data. + match := re.FindStringSubmatch(filepath.Base(namespacePath)) + if len(match) == 0 { + continue + } + nsid := match[1] + nuse, err := readUintFromFile(filepath.Join(namespacePath, "nuse")) + if err != nil { + c.logger.Debug("failed to read nuse", "device", device.Name, "namespace", match[0], "err", err) + } + nsze, err := readUintFromFile(filepath.Join(namespacePath, "size")) + if err != nil { + c.logger.Debug("failed to read size", "device", device.Name, "namespace", match[0], "err", err) + } + lbaSize, err := readUintFromFile(filepath.Join(namespacePath, "queue", "logical_block_size")) + if err != nil { + c.logger.Debug("failed to read queue/logical_block_size", "device", device.Name, "namespace", match[0], "err", err) + } + ncap := nsze * lbaSize + anaState := "unknown" + anaStateSysfs, err := os.ReadFile(filepath.Join(namespacePath, "ana_state")) + if err == nil { + anaState = strings.TrimSpace(string(anaStateSysfs)) + } else { + c.logger.Debug("failed to read ana_state", "device", device.Name, "namespace", match[0], "err", err) + } + + ch <- prometheus.MustNewConstMetric( + c.namespaceInfo, + prometheus.GaugeValue, + 1.0, + device.Name, + nsid, + anaState, + ) + + ch <- prometheus.MustNewConstMetric( + c.namespaceCapacityBytes, + prometheus.GaugeValue, + float64(ncap), + device.Name, + nsid, + ) + + ch <- prometheus.MustNewConstMetric( + c.namespaceSizeBytes, + prometheus.GaugeValue, + float64(nsze), + device.Name, + nsid, + ) + + ch <- prometheus.MustNewConstMetric( + c.namespaceUsedBytes, + prometheus.GaugeValue, + float64(nuse*lbaSize), + device.Name, + nsid, + ) + + ch <- prometheus.MustNewConstMetric( + c.namespaceLogicalBlockSizeBytes, + prometheus.GaugeValue, + float64(lbaSize), + device.Name, + nsid, + ) + } } return nil } + From e252d97c6f2534ed24f7e9e871764245d137124b Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Wed, 9 Jul 2025 11:53:27 +0530 Subject: [PATCH 02/14] linting the code Signed-off-by: Shashwat Hiregoudar --- collector/nvme_linux.go | 1 - 1 file changed, 1 deletion(-) diff --git a/collector/nvme_linux.go b/collector/nvme_linux.go index 88218e5ce0..04af7276bc 100644 --- a/collector/nvme_linux.go +++ b/collector/nvme_linux.go @@ -196,4 +196,3 @@ func (c *nvmeCollector) Update(ch chan<- prometheus.Metric) error { return nil } - From be1ad0084e0e3dd12dd04aa9dfa17244bff326e8 Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Tue, 30 Sep 2025 19:11:56 +0530 Subject: [PATCH 03/14] adding the namespace tests too Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-64k-page-output.txt | 15 +++++++++++++ collector/fixtures/e2e-output.txt | 15 +++++++++++++ collector/fixtures/sys.ttar | 26 ++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index 66b321a587..4571f84dbb 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -2826,6 +2826,21 @@ node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 +# HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes. Computed as namespace_size * namespace_logical_block_size +# TYPE node_nvme_namespace_capacity_bytes gauge +node_nvme_namespace_capacity_bytes{device="nvme0",nsid="0"} 1.6e+13 +# HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 +# TYPE node_nvme_namespace_info gauge +node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 +# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size +# TYPE node_nvme_namespace_logical_block_size_bytes gauge +node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 +# HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size +# TYPE node_nvme_namespace_size_bytes gauge +node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3.90625e+09 +# HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse +# TYPE node_nvme_namespace_used_bytes gauge +node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 2e+12 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index ce555fc804..49957b71e7 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2848,6 +2848,21 @@ node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 +# HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 +# TYPE node_nvme_namespace_info gauge +node_nvme_namespace_info{device="nvme0",nsid="0",ana_state="optimized"} 1 +# HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes.Computed as namespace_size * namespace_logical_block_size +# TYPE node_nvme_namespace_capacity_bytes gauge +node_nvme_namespace_capacity_bytes{device="nvme0",nsid="0"} 1.6e+13 +# HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes.Available in /sys/class/nvme///size +# TYPE node_nvme_namespace_size_bytes gauge +node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3906250000 +# HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes.Available in /sys/class/nvme///nuse +# TYPE node_nvme_namespace_used_bytes gauge +node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 488281250 +# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes.Usually 4Kb.Available in /sys/class/nvme///queue/logical_block_size +# TYPE node_nvme_namespace_logical_block_size_bytes gauge +node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 diff --git a/collector/fixtures/sys.ttar b/collector/fixtures/sys.ttar index 4e20c2a324..c30ba125c5 100644 --- a/collector/fixtures/sys.ttar +++ b/collector/fixtures/sys.ttar @@ -2199,6 +2199,32 @@ Lines: 1 live Mode: 444 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/nvme/nvme0/nvme0c0n0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/nvme/nvme0/nvme0c0n0/ana_state +Lines: 1 +optimized +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/nvme/nvme0/nvme0c0n0/size +Lines: 1 +3906250000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/nvme/nvme0/nvme0c0n0/nuse +Lines: 1 +488281250 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/nvme/nvme0/nvme0c0n0/queue +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/nvme/nvme0/nvme0c0n0/queue/logical_block_size +Lines: 1 +4096 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: sys/class/power_supply Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 1213dcc3237dac4bddd8ae6d517322676d43b942 Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Wed, 1 Oct 2025 09:45:41 +0530 Subject: [PATCH 04/14] correcting the formatting Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-output.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 49957b71e7..fdcb1a5e5b 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2850,17 +2850,17 @@ node_nfsd_server_threads 8 node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 # HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 # TYPE node_nvme_namespace_info gauge -node_nvme_namespace_info{device="nvme0",nsid="0",ana_state="optimized"} 1 -# HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes.Computed as namespace_size * namespace_logical_block_size +node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 +# HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes. Computed as namespace_size * namespace_logical_block_size # TYPE node_nvme_namespace_capacity_bytes gauge node_nvme_namespace_capacity_bytes{device="nvme0",nsid="0"} 1.6e+13 -# HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes.Available in /sys/class/nvme///size +# HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size # TYPE node_nvme_namespace_size_bytes gauge -node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3906250000 -# HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes.Available in /sys/class/nvme///nuse +node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3.90625e+09 +# HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse # TYPE node_nvme_namespace_used_bytes gauge -node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 488281250 -# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes.Usually 4Kb.Available in /sys/class/nvme///queue/logical_block_size +node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 2e+12 +# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size # TYPE node_nvme_namespace_logical_block_size_bytes gauge node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. From d99435f32dfb0211821daf71c7ae1a557abe68ae Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Wed, 1 Oct 2025 09:54:12 +0530 Subject: [PATCH 05/14] correcting the order too Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-output.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index fdcb1a5e5b..9a151823f2 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2848,21 +2848,21 @@ node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 -# HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 -# TYPE node_nvme_namespace_info gauge -node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 # HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes. Computed as namespace_size * namespace_logical_block_size # TYPE node_nvme_namespace_capacity_bytes gauge node_nvme_namespace_capacity_bytes{device="nvme0",nsid="0"} 1.6e+13 +# HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 +# TYPE node_nvme_namespace_info gauge +node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 +# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size +# TYPE node_nvme_namespace_logical_block_size_bytes gauge +node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size # TYPE node_nvme_namespace_size_bytes gauge node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3.90625e+09 # HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse # TYPE node_nvme_namespace_used_bytes gauge node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 2e+12 -# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size -# TYPE node_nvme_namespace_logical_block_size_bytes gauge -node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 From 98d6cf7fbb5c3d4e693ee0376cebf99be023d55b Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Sat, 28 Feb 2026 23:03:24 +0530 Subject: [PATCH 06/14] refactor: uprated procfs refactor: use updated library to collect NVMe namespace metrics directly from structured data instead of manual sysfs parsing. Signed-off-by: Shashwat Hiregoudar --- collector/nvme_linux.go | 84 +++++++++++++++-------------------------- go.mod | 2 +- go.sum | 2 + 3 files changed, 33 insertions(+), 55 deletions(-) diff --git a/collector/nvme_linux.go b/collector/nvme_linux.go index 356462ea33..9cf5a803de 100644 --- a/collector/nvme_linux.go +++ b/collector/nvme_linux.go @@ -20,9 +20,6 @@ import ( "fmt" "log/slog" "os" - "path/filepath" - "regexp" - "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" @@ -109,86 +106,65 @@ func (c *nvmeCollector) Update(ch chan<- prometheus.Metric) error { } for _, device := range devices { - infoValue := 1.0 - - devicePath := filepath.Join(*sysPath, "class/nvme", device.Name) - ch <- prometheus.MustNewConstMetric(c.info, prometheus.GaugeValue, infoValue, device.Name, device.FirmwareRevision, device.Model, device.Serial, device.State, device.ControllerID) - // Find namespace directories. - namespacePaths, err := filepath.Glob(filepath.Join(devicePath, "nvme[0-9]*c[0-9]*n[0-9]*")) - if err != nil { - c.logger.Error("failed to list NVMe namespaces", "device", device.Name, "err", err) - continue - } - re := regexp.MustCompile(`nvme[0-9]+c[0-9]+n([0-9]+)`) - - for _, namespacePath := range namespacePaths { - - // Read namespace data. - match := re.FindStringSubmatch(filepath.Base(namespacePath)) - if len(match) == 0 { - continue - } - nsid := match[1] - nuse, err := readUintFromFile(filepath.Join(namespacePath, "nuse")) - if err != nil { - c.logger.Debug("failed to read nuse", "device", device.Name, "namespace", match[0], "err", err) - } - nsze, err := readUintFromFile(filepath.Join(namespacePath, "size")) - if err != nil { - c.logger.Debug("failed to read size", "device", device.Name, "namespace", match[0], "err", err) - } - lbaSize, err := readUintFromFile(filepath.Join(namespacePath, "queue", "logical_block_size")) - if err != nil { - c.logger.Debug("failed to read queue/logical_block_size", "device", device.Name, "namespace", match[0], "err", err) - } - ncap := nsze * lbaSize - anaState := "unknown" - anaStateSysfs, err := os.ReadFile(filepath.Join(namespacePath, "ana_state")) - if err == nil { - anaState = strings.TrimSpace(string(anaStateSysfs)) - } else { - c.logger.Debug("failed to read ana_state", "device", device.Name, "namespace", match[0], "err", err) - } - + // Export device-level metrics + ch <- prometheus.MustNewConstMetric( + c.info, + prometheus.GaugeValue, + 1.0, + device.Name, + device.FirmwareRevision, + device.Model, + device.Serial, + device.State, + device.ControllerID, + ) + + // Export namespace-level metrics + for _, namespace := range device.Namespaces { + // Namespace info metric ch <- prometheus.MustNewConstMetric( c.namespaceInfo, prometheus.GaugeValue, 1.0, device.Name, - nsid, - anaState, + namespace.ID, + namespace.ANAState, ) + // Namespace capacity in bytes ch <- prometheus.MustNewConstMetric( c.namespaceCapacityBytes, prometheus.GaugeValue, - float64(ncap), + float64(namespace.CapacityBytes), device.Name, - nsid, + namespace.ID, ) + // Namespace size in bytes ch <- prometheus.MustNewConstMetric( c.namespaceSizeBytes, prometheus.GaugeValue, - float64(nsze), + float64(namespace.SizeBytes), device.Name, - nsid, + namespace.ID, ) + // Namespace used space in bytes ch <- prometheus.MustNewConstMetric( c.namespaceUsedBytes, prometheus.GaugeValue, - float64(nuse*lbaSize), + float64(namespace.UsedBytes), device.Name, - nsid, + namespace.ID, ) + // Namespace logical block size in bytes ch <- prometheus.MustNewConstMetric( c.namespaceLogicalBlockSizeBytes, prometheus.GaugeValue, - float64(lbaSize), + float64(namespace.LogicalBlockSize), device.Name, - nsid, + namespace.ID, ) } } diff --git a/go.mod b/go.mod index 2d7db779ed..9dcdecff02 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.67.5 github.com/prometheus/exporter-toolkit v0.15.1 - github.com/prometheus/procfs v0.20.0 + github.com/prometheus/procfs v0.20.1-0.20260225193243-56add94167b8 github.com/safchain/ethtool v0.7.0 golang.org/x/sys v0.41.0 howett.net/plist v1.0.1 diff --git a/go.sum b/go.sum index 7fb5e88b7d..cbf0305763 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/prometheus/exporter-toolkit v0.15.1 h1:XrGGr/qWl8Gd+pqJqTkNLww9eG8vR/ github.com/prometheus/exporter-toolkit v0.15.1/go.mod h1:P/NR9qFRGbCFgpklyhix9F6v6fFr/VQB/CVsrMDGKo4= github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q= github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +github.com/prometheus/procfs v0.20.1-0.20260225193243-56add94167b8 h1:24u5p7M4Pp5KHMIc6D8bxiCNeIZwZf3XHe2INOGPF9s= +github.com/prometheus/procfs v0.20.1-0.20260225193243-56add94167b8/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/safchain/ethtool v0.7.0 h1:rlJzfDetsVvT61uz8x1YIcFn12akMfuPulHtZjtb7Is= From d07acf9bdca3cb90e24e9cda5bdea85a873eeebb Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Sat, 28 Feb 2026 23:08:03 +0530 Subject: [PATCH 07/14] go mod tidy go mod tidy Signed-off-by: Shashwat Hiregoudar --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index cbf0305763..560b3a3df7 100644 --- a/go.sum +++ b/go.sum @@ -88,8 +88,6 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/exporter-toolkit v0.15.1 h1:XrGGr/qWl8Gd+pqJqTkNLww9eG8vR/CoRk0FubOKfLE= github.com/prometheus/exporter-toolkit v0.15.1/go.mod h1:P/NR9qFRGbCFgpklyhix9F6v6fFr/VQB/CVsrMDGKo4= -github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q= -github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/prometheus/procfs v0.20.1-0.20260225193243-56add94167b8 h1:24u5p7M4Pp5KHMIc6D8bxiCNeIZwZf3XHe2INOGPF9s= github.com/prometheus/procfs v0.20.1-0.20260225193243-56add94167b8/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= From ea12a368a7e37d6afe09322f3e4557ce4e452a42 Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Mon, 2 Mar 2026 18:20:46 +0530 Subject: [PATCH 08/14] Update github.com/prometheus/procfs version Updated the version of github.com/prometheus/procfs to v0.20.1. Signed-off-by: Shashwat Hiregoudar --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9dcdecff02..55b63d72dd 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.67.5 github.com/prometheus/exporter-toolkit v0.15.1 - github.com/prometheus/procfs v0.20.1-0.20260225193243-56add94167b8 + github.com/prometheus/procfs v0.20.1 github.com/safchain/ethtool v0.7.0 golang.org/x/sys v0.41.0 howett.net/plist v1.0.1 From 4720bcc683476476a48a53f358b808f724a5914c Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Mon, 2 Mar 2026 18:21:41 +0530 Subject: [PATCH 09/14] Update procfs dependency version in go.sum Signed-off-by: Shashwat Hiregoudar --- go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index 560b3a3df7..835b9d93cb 100644 --- a/go.sum +++ b/go.sum @@ -88,8 +88,8 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/exporter-toolkit v0.15.1 h1:XrGGr/qWl8Gd+pqJqTkNLww9eG8vR/CoRk0FubOKfLE= github.com/prometheus/exporter-toolkit v0.15.1/go.mod h1:P/NR9qFRGbCFgpklyhix9F6v6fFr/VQB/CVsrMDGKo4= -github.com/prometheus/procfs v0.20.1-0.20260225193243-56add94167b8 h1:24u5p7M4Pp5KHMIc6D8bxiCNeIZwZf3XHe2INOGPF9s= -github.com/prometheus/procfs v0.20.1-0.20260225193243-56add94167b8/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/safchain/ethtool v0.7.0 h1:rlJzfDetsVvT61uz8x1YIcFn12akMfuPulHtZjtb7Is= From 2a166b3e7f4ee19851af2acb8ae2e0ebd3cc94e1 Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Mon, 2 Mar 2026 18:30:55 +0530 Subject: [PATCH 10/14] Update Go version from 1.25.0 to 1.26 Signed-off-by: Shashwat Hiregoudar --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 55b63d72dd..102060c69b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/prometheus/node_exporter -go 1.25.0 +go 1.26 require ( github.com/alecthomas/kingpin/v2 v2.4.0 From 2fe75e1824dfd3bee0eb1ac99fb26cf6d6eeaf6b Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Tue, 3 Mar 2026 13:55:16 +0530 Subject: [PATCH 11/14] Update NVMe namespace size metric value Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-output.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 6c390fb816..7ac06c0f87 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2869,7 +2869,7 @@ node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size # TYPE node_nvme_namespace_size_bytes gauge -node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3.90625e+09 +node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 1.6e+13 # HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse # TYPE node_nvme_namespace_used_bytes gauge node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 2e+12 From 731838685d99e60eb25c8e85d8e49540748e9713 Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Tue, 3 Mar 2026 13:57:02 +0530 Subject: [PATCH 12/14] Update e2e-64k-page-output.txt Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-64k-page-output.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index 023702c4cf..7ad6200b1e 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -2837,7 +2837,7 @@ node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size # TYPE node_nvme_namespace_size_bytes gauge -node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3.90625e+09 +node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 1.6+e13 # HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse # TYPE node_nvme_namespace_used_bytes gauge node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 2e+12 From 4a8edfc6871dce569034129fb703bc0347743bd9 Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Tue, 3 Mar 2026 14:03:56 +0530 Subject: [PATCH 13/14] Fix formatting of node_nvme_namespace_size_bytes metric Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-64k-page-output.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index 7ad6200b1e..b860becad2 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -2837,7 +2837,7 @@ node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size # TYPE node_nvme_namespace_size_bytes gauge -node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 1.6+e13 +node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 1.6e+13 # HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse # TYPE node_nvme_namespace_used_bytes gauge node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 2e+12 From baf365d0c7c089b154def996a4e817553cf94315 Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Tue, 3 Mar 2026 14:22:43 +0530 Subject: [PATCH 14/14] Downgrade Go version from 1.26 to 1.25.0 Signed-off-by: Shashwat Hiregoudar --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 102060c69b..55b63d72dd 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/prometheus/node_exporter -go 1.26 +go 1.25.0 require ( github.com/alecthomas/kingpin/v2 v2.4.0