diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index db384df620..b860becad2 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -2825,7 +2825,22 @@ 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_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"} 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 # 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 9d59cab31e..7ac06c0f87 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2857,7 +2857,22 @@ 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_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"} 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 # 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 345cbcd2e3..f8a453fe37 100644 --- a/collector/fixtures/sys.ttar +++ b/collector/fixtures/sys.ttar @@ -2229,6 +2229,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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/collector/nvme_linux.go b/collector/nvme_linux.go index 6de1b9368f..9cf5a803de 100644 --- a/collector/nvme_linux.go +++ b/collector/nvme_linux.go @@ -26,8 +26,14 @@ import ( ) 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() { @@ -41,9 +47,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 } @@ -58,14 +106,67 @@ 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, + // 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, ) - infoValue := 1.0 - ch <- prometheus.MustNewConstMetric(infoDesc, prometheus.GaugeValue, infoValue, device.Name, device.FirmwareRevision, device.Model, device.Serial, device.State) + + // Export namespace-level metrics + for _, namespace := range device.Namespaces { + // Namespace info metric + ch <- prometheus.MustNewConstMetric( + c.namespaceInfo, + prometheus.GaugeValue, + 1.0, + device.Name, + namespace.ID, + namespace.ANAState, + ) + + // Namespace capacity in bytes + ch <- prometheus.MustNewConstMetric( + c.namespaceCapacityBytes, + prometheus.GaugeValue, + float64(namespace.CapacityBytes), + device.Name, + namespace.ID, + ) + + // Namespace size in bytes + ch <- prometheus.MustNewConstMetric( + c.namespaceSizeBytes, + prometheus.GaugeValue, + float64(namespace.SizeBytes), + device.Name, + namespace.ID, + ) + + // Namespace used space in bytes + ch <- prometheus.MustNewConstMetric( + c.namespaceUsedBytes, + prometheus.GaugeValue, + float64(namespace.UsedBytes), + device.Name, + namespace.ID, + ) + + // Namespace logical block size in bytes + ch <- prometheus.MustNewConstMetric( + c.namespaceLogicalBlockSizeBytes, + prometheus.GaugeValue, + float64(namespace.LogicalBlockSize), + device.Name, + namespace.ID, + ) + } } return nil diff --git a/go.mod b/go.mod index 2d7db779ed..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.0 + 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 diff --git a/go.sum b/go.sum index 7fb5e88b7d..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.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q= -github.com/prometheus/procfs v0.20.0/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=