Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions blockdevice/stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestBlockDevice(t *testing.T) {
if err != nil {
t.Fatal(err)
}
expectedNumOfDevices := 14
expectedNumOfDevices := 16
if len(devices) != expectedNumOfDevices {
t.Fatalf(failMsgFormat, "Incorrect number of devices", expectedNumOfDevices, len(devices))
}
Expand All @@ -95,18 +95,18 @@ func TestBlockDevice(t *testing.T) {
if device0stats.WeightedIOTicks != 6088971 {
t.Errorf(failMsgFormat, "Incorrect time in queue", 6088971, device0stats.WeightedIOTicks)
}
device9stats, count, err := blockdevice.SysBlockDeviceStat(devices[9])
device11stats, count, err := blockdevice.SysBlockDeviceStat(devices[11])
if count != 15 {
t.Errorf(failMsgFormat, "Incorrect number of stats read", 15, count)
}
if err != nil {
t.Fatal(err)
}
if device9stats.WriteSectors != 286915323 {
t.Errorf(failMsgFormat, "Incorrect write merges", 286915323, device9stats.WriteSectors)
if device11stats.WriteSectors != 286915323 {
t.Errorf(failMsgFormat, "Incorrect write merges", 286915323, device11stats.WriteSectors)
}
if device9stats.DiscardTicks != 12 {
t.Errorf(failMsgFormat, "Incorrect discard ticks", 12, device9stats.DiscardTicks)
if device11stats.DiscardTicks != 12 {
t.Errorf(failMsgFormat, "Incorrect discard ticks", 12, device11stats.DiscardTicks)
}
blockQueueStatExpected := BlockQueueStats{
AddRandom: 1,
Expand Down Expand Up @@ -147,7 +147,7 @@ func TestBlockDevice(t *testing.T) {
WriteZeroesMaxBytes: 0,
}

blockQueueStat, err := blockdevice.SysBlockDeviceQueueStats(devices[9])
blockQueueStat, err := blockdevice.SysBlockDeviceQueueStats(devices[11])
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -181,7 +181,7 @@ func TestBlockDmInfo(t *testing.T) {
t.Fatalf("unexpected BlockQueueStat (-want +got):\n%s", diff)
}

dm1Info, err := blockdevice.SysBlockDeviceMapperInfo(devices[9])
dm1Info, err := blockdevice.SysBlockDeviceMapperInfo(devices[11])
if err != nil {
var pErr *os.PathError
if errors.As(err, &pErr) {
Expand Down Expand Up @@ -232,13 +232,13 @@ func TestSysBlockDeviceSize(t *testing.T) {
if err != nil {
t.Fatal(err)
}
size9, err := blockdevice.SysBlockDeviceSize(devices[9])
size11, err := blockdevice.SysBlockDeviceSize(devices[11])
if err != nil {
t.Fatal(err)
}
size9Expected := uint64(1920383410176)
if size9 != size9Expected {
t.Errorf("Incorrect BlockDeviceSize, expected: \n%+v, got: \n%+v", size9Expected, size9)
size11Expected := uint64(1920383410176)
if size11 != size11Expected {
t.Errorf("Incorrect BlockDeviceSize, expected: \n%+v, got: \n%+v", size11Expected, size11)
}
}

Expand All @@ -252,13 +252,13 @@ func TestSysBlockDeviceRotational(t *testing.T) {
t.Fatal(err)
}

// devices[9] is "sda" in the fixtures — a rotational HDD (value: 1).
rotational9, err := blockdevice.SysBlockDeviceRotational(devices[9])
// devices[11] is "sda" in the fixtures — a rotational HDD (value: 1).
rotational11, err := blockdevice.SysBlockDeviceRotational(devices[11])
if err != nil {
t.Fatal(err)
}
if rotational9 != 1 {
t.Errorf("Incorrect rotational value for %s: expected 1, got %d", devices[9], rotational9)
if rotational11 != 1 {
t.Errorf("Incorrect rotational value for %s: expected 1, got %d", devices[11], rotational11)
}

// devices[0] is "dm-0" — no queue/rotational file; expect an error.
Expand Down
42 changes: 36 additions & 6 deletions sysfs/mdraid.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ package sysfs

import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/prometheus/procfs/internal/util"
Expand All @@ -26,10 +28,10 @@ import (
// Mdraid holds info parsed from relevant files in the /sys/block/md*/md directory.
type Mdraid struct {
Device string // Kernel device name of array.
Level string // mdraid level.
Level string // mdraid level. Empty if level file is unreadable.
ArrayState string // State of the array.
MetadataVersion string // mdraid metadata version.
Disks uint64 // Number of devices in a fully functional array.
Disks *uint64 // Number of devices in a fully functional array. Nil when unavailable (e.g. container/imsm/ddf, empty level, or empty raid_disks).
Components []MdraidComponent // mdraid component devices.
UUID string // UUID of the array.

Expand Down Expand Up @@ -81,10 +83,38 @@ func (fs FS) Mdraids() ([]Mdraid, error) {
return mdraids, err
}

if val, err := util.ReadUintFromFile(filepath.Join(path, "raid_disks")); err == nil {
md.Disks = val
} else {
return mdraids, err
// Parse raid_disks:
// - Container types (container/imsm/ddf) have no physical disks assigned.
// - Empty level file means we can't determine the array type, skip parsing.
// - During reshape/grow, raid_disks may contain "current (previous)" (e.g. "11 (10)").
// - Normal case is a plain integer.
switch md.Level {
case "container", "imsm", "ddf":
Comment thread
dongjiang1989 marked this conversation as resolved.
// No disks for container-type arrays, Disks remains nil.
case "":
// Empty level file, cannot determine disk count, Disks remains nil.
default:
data, err := os.ReadFile(filepath.Join(path, "raid_disks"))
if err != nil {
return mdraids, fmt.Errorf("reading raid_disks for %s: %w", md.Device, err)
}
content := strings.TrimSpace(string(data))
if content == "" {
// Empty file, Disks remains nil.
break
}

// Try normal integer parse first.
if val, parseErr := strconv.ParseUint(content, 10, 64); parseErr == nil {
md.Disks = &val
} else if idx := strings.Index(content, " "); idx > 0 {
// Reshape format: "current (previous)", extract the first number.
if val, parseErr := strconv.ParseUint(content[:idx], 10, 64); parseErr == nil {
md.Disks = &val
}
// If parsing fails, Disks remains nil (reshape in progress, count may be inaccurate).
}
// If neither format matches, Disks remains nil.
}

if val, err := util.SysReadFile(filepath.Join(path, "uuid")); err == nil {
Expand Down
36 changes: 30 additions & 6 deletions sysfs/mdraid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ func TestMdraidStats(t *testing.T) {
t.Fatal(err)
}

uint64ptr := func(v uint64) *uint64 { return &v }

want := []Mdraid{
{
Device: "md0",
Level: "raid0",
ArrayState: "clean",
MetadataVersion: "1.2",
Disks: 2,
Disks: uint64ptr(2),
Components: []MdraidComponent{
{Device: "sdg", State: "in_sync"},
{Device: "sdh", State: "in_sync"},
Expand All @@ -51,7 +53,7 @@ func TestMdraidStats(t *testing.T) {
Level: "raid1",
ArrayState: "clean",
MetadataVersion: "1.2",
Disks: 2,
Disks: uint64ptr(2),
Components: []MdraidComponent{
{Device: "sdi", State: "in_sync"},
{Device: "sdj", State: "in_sync"},
Expand All @@ -64,7 +66,7 @@ func TestMdraidStats(t *testing.T) {
Level: "raid10",
ArrayState: "clean",
MetadataVersion: "1.2",
Disks: 4,
Disks: uint64ptr(4),
Components: []MdraidComponent{
{Device: "sdu", State: "in_sync"},
{Device: "sdv", State: "in_sync"},
Expand All @@ -80,7 +82,7 @@ func TestMdraidStats(t *testing.T) {
Level: "raid4",
ArrayState: "clean",
MetadataVersion: "1.2",
Disks: 3,
Disks: uint64ptr(3),
Components: []MdraidComponent{
{Device: "sdk", State: "in_sync"},
{Device: "sdl", State: "in_sync"},
Expand All @@ -95,7 +97,7 @@ func TestMdraidStats(t *testing.T) {
Level: "raid5",
ArrayState: "clean",
MetadataVersion: "1.2",
Disks: 3,
Disks: uint64ptr(3),
Components: []MdraidComponent{
{Device: "sdaa", State: "spare"},
{Device: "sdn", State: "in_sync"},
Expand All @@ -112,7 +114,7 @@ func TestMdraidStats(t *testing.T) {
Level: "raid6",
ArrayState: "active",
MetadataVersion: "1.2",
Disks: 4,
Disks: uint64ptr(4),
Components: []MdraidComponent{
{Device: "sdq", State: "in_sync"},
{Device: "sdr", State: "in_sync"},
Expand All @@ -125,6 +127,28 @@ func TestMdraidStats(t *testing.T) {
SyncAction: "recover",
SyncCompleted: 0.7500458659491194,
},
{
Device: "md7",
Level: "container",
ArrayState: "inactive",
MetadataVersion: "1.2",
Disks: nil, // container type, no disks assigned
UUID: "0e51f6d1-b357-2712-8eaa-31f6f597be6b",
},
{
Device: "md8",
Level: "raid5",
ArrayState: "active",
MetadataVersion: "1.2",
Disks: uint64ptr(11), // reshaping from 10 to 11, current value is 11
Components: []MdraidComponent{
{Device: "sdy", State: "in_sync"},
{Device: "sdz", State: "in_sync"},
},
UUID: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
ChunkSize: 524288,
SyncAction: "reshape",
},
}

if diff := cmp.Diff(want, got); diff != "" {
Expand Down
160 changes: 129 additions & 31 deletions testdata/fixtures.ttar

Large diffs are not rendered by default.