@@ -19,20 +19,37 @@ import (
1919 "fmt"
2020 "os"
2121 "path/filepath"
22+ "regexp"
23+ "strconv"
2224
2325 "github.com/prometheus/procfs/internal/util"
2426)
2527
2628const nvmeClassPath = "class/nvme"
2729
30+ var nvmeNamespacePattern = regexp .MustCompile (`nvme\d+c\d+n(\d+)` )
31+
32+ // NVMeNamespace contains info from files in /sys/class/nvme/<device>/<namespace>.
33+ type NVMeNamespace struct {
34+ ID string // namespace ID extracted from directory name
35+ UsedBlocks uint64 // from nuse file (blocks used)
36+ SizeBlocks uint64 // from size file (total blocks)
37+ LogicalBlockSize uint64 // from queue/logical_block_size file
38+ ANAState string // from ana_state file
39+ UsedBytes uint64 // calculated: UsedBlocks * LogicalBlockSize
40+ SizeBytes uint64 // calculated: SizeBlocks * LogicalBlockSize
41+ CapacityBytes uint64 // calculated: SizeBlocks * LogicalBlockSize
42+ }
43+
2844// NVMeDevice contains info from files in /sys/class/nvme for a single NVMe device.
2945type NVMeDevice struct {
3046 Name string
31- Serial string // /sys/class/nvme/<Name>/serial
32- Model string // /sys/class/nvme/<Name>/model
33- State string // /sys/class/nvme/<Name>/state
34- FirmwareRevision string // /sys/class/nvme/<Name>/firmware_rev
35- ControllerID string // /sys/class/nvme/<Name>/cntlid
47+ Serial string // /sys/class/nvme/<Name>/serial
48+ Model string // /sys/class/nvme/<Name>/model
49+ State string // /sys/class/nvme/<Name>/state
50+ FirmwareRevision string // /sys/class/nvme/<Name>/firmware_rev
51+ ControllerID string // /sys/class/nvme/<Name>/cntlid
52+ Namespaces []NVMeNamespace // NVMe namespaces for this device
3653}
3754
3855// NVMeClass is a collection of every NVMe device in /sys/class/nvme.
@@ -67,6 +84,7 @@ func (fs FS) parseNVMeDevice(name string) (*NVMeDevice, error) {
6784 path := fs .sys .Path (nvmeClassPath , name )
6885 device := NVMeDevice {Name : name }
6986
87+ // Parse device-level attributes
7088 for _ , f := range [... ]string {"firmware_rev" , "model" , "serial" , "state" , "cntlid" } {
7189 name := filepath .Join (path , f )
7290 value , err := util .SysReadFile (name )
@@ -88,5 +106,68 @@ func (fs FS) parseNVMeDevice(name string) (*NVMeDevice, error) {
88106 }
89107 }
90108
109+ // Parse namespaces - read directory and filter using regex
110+ dirs , err := os .ReadDir (path )
111+ if err != nil {
112+ return nil , fmt .Errorf ("failed to list NVMe namespaces at %q: %w" , path , err )
113+ }
114+
115+ var namespaces []NVMeNamespace
116+
117+ for _ , d := range dirs {
118+ // Use regex to identify namespace directories and extract namespace ID
119+ match := nvmeNamespacePattern .FindStringSubmatch (d .Name ())
120+ if len (match ) < 2 {
121+ // Skip if not a namespace directory
122+ continue
123+ }
124+ nsid := match [1 ]
125+ namespacePath := filepath .Join (path , d .Name ())
126+
127+ namespace := NVMeNamespace {
128+ ID : nsid ,
129+ ANAState : "unknown" , // Default value
130+ }
131+
132+ // Parse namespace attributes using the same approach as device attributes
133+ for _ , f := range [... ]string {"nuse" , "size" , "queue/logical_block_size" , "ana_state" } {
134+ filePath := filepath .Join (namespacePath , f )
135+ value , err := util .SysReadFile (filePath )
136+ if err != nil {
137+ if f == "ana_state" {
138+ // ana_state may not exist, skip silently
139+ continue
140+ }
141+ return nil , fmt .Errorf ("failed to read file %q: %w" , filePath , err )
142+ }
143+
144+ switch f {
145+ case "nuse" :
146+ if val , parseErr := strconv .ParseUint (value , 10 , 64 ); parseErr == nil {
147+ namespace .UsedBlocks = val
148+ }
149+ case "size" :
150+ if val , parseErr := strconv .ParseUint (value , 10 , 64 ); parseErr == nil {
151+ namespace .SizeBlocks = val
152+ }
153+ case "queue/logical_block_size" :
154+ if val , parseErr := strconv .ParseUint (value , 10 , 64 ); parseErr == nil {
155+ namespace .LogicalBlockSize = val
156+ }
157+ case "ana_state" :
158+ namespace .ANAState = value
159+ }
160+ }
161+
162+ // Calculate derived values
163+ namespace .UsedBytes = namespace .UsedBlocks * namespace .LogicalBlockSize
164+ namespace .SizeBytes = namespace .SizeBlocks * namespace .LogicalBlockSize
165+ namespace .CapacityBytes = namespace .SizeBlocks * namespace .LogicalBlockSize
166+
167+ namespaces = append (namespaces , namespace )
168+ }
169+
170+ device .Namespaces = namespaces
171+
91172 return & device , nil
92173}
0 commit comments