Skip to content

Commit 1ad0a36

Browse files
committed
feat: expose MD raid component devices
Expose what component devices are part of a MD raid device, as well as the most common flags per-component. This will enable a future node_exporter metric showing which component of a RAID had failed. Signed-off-by: Robin H. Johnson <[email protected]> Signed-off-by: Robin H. Johnson <[email protected]>
1 parent 2246fee commit 1ad0a36

File tree

2 files changed

+112
-41
lines changed

2 files changed

+112
-41
lines changed

mdstat.go

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,28 @@ var (
2727
recoveryLinePctRE = regexp.MustCompile(`= (.+)%`)
2828
recoveryLineFinishRE = regexp.MustCompile(`finish=(.+)min`)
2929
recoveryLineSpeedRE = regexp.MustCompile(`speed=(.+)[A-Z]`)
30-
componentDeviceRE = regexp.MustCompile(`(.*)\[\d+\]`)
30+
componentDeviceRE = regexp.MustCompile(`(.*)\[(\d+)\](\([SF]+\))?`)
31+
personalitiesPrefix = "Personalities : "
3132
)
3233

34+
type MDStatComponent struct {
35+
// Name of the component device.
36+
Name string
37+
// DescriptorIndex number of component device, e.g. the order in the superblock.
38+
DescriptorIndex int32
39+
// Flags per Linux drivers/md/md.[ch] as of v6.12-rc1
40+
// Subset that are exposed in mdstat
41+
WriteMostly bool
42+
Journal bool
43+
Faulty bool // "Faulty" is what kernel source uses for "(F)"
44+
Spare bool
45+
Replacement bool
46+
// Some additional flags that are NOT exposed in procfs today; they may
47+
// be available via sysfs.
48+
// In_sync, Bitmap_sync, Blocked, WriteErrorSeen, FaultRecorded,
49+
// BlockedBadBlocks, WantReplacement, Candidate, ...
50+
}
51+
3352
// MDStat holds info parsed from /proc/mdstat.
3453
type MDStat struct {
3554
// Name of the device.
@@ -58,8 +77,8 @@ type MDStat struct {
5877
BlocksSyncedFinishTime float64
5978
// current sync speed (in Kilobytes/sec)
6079
BlocksSyncedSpeed float64
61-
// Name of md component devices
62-
Devices []string
80+
// component devices
81+
Devices []MDStatComponent
6382
}
6483

6584
// MDStat parses an mdstat-file (/proc/mdstat) and returns a slice of
@@ -80,38 +99,52 @@ func (fs FS) MDStat() ([]MDStat, error) {
8099
// parseMDStat parses data from mdstat file (/proc/mdstat) and returns a slice of
81100
// structs containing the relevant info.
82101
func parseMDStat(mdStatData []byte) ([]MDStat, error) {
102+
// TODO:
103+
// - parse global hotspares from the "unused devices" line.
83104
mdStats := []MDStat{}
84105
lines := strings.Split(string(mdStatData), "\n")
106+
knownRaidTypes := make(map[string]bool)
85107

86108
for i, line := range lines {
87109
if strings.TrimSpace(line) == "" || line[0] == ' ' ||
88-
strings.HasPrefix(line, "Personalities") ||
89110
strings.HasPrefix(line, "unused") {
90111
continue
91112
}
113+
// Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
114+
if len(knownRaidTypes) == 0 && strings.HasPrefix(line, personalitiesPrefix) {
115+
personalities := strings.Fields(line[len(personalitiesPrefix):])
116+
for _, word := range personalities {
117+
word := word[1 : len(word)-1]
118+
knownRaidTypes[word] = true
119+
}
120+
continue
121+
}
92122

93123
deviceFields := strings.Fields(line)
94124
if len(deviceFields) < 3 {
95125
return nil, fmt.Errorf("not enough fields in mdline (expected at least 3): %s", line)
96126
}
97127
mdName := deviceFields[0] // mdx
98-
state := deviceFields[2] // active or inactive
128+
state := deviceFields[2] // active, inactive, broken
99129

100-
mdType := "unknown" // raid1, raid5, etc.
130+
mdType := "unknown" // raid1, raid5, etc.
131+
var deviceStartIndex int
101132
if len(deviceFields) > 3 { // mdType may be in the 3rd or 4th field
102-
if isRaidType(deviceFields[3]) {
133+
if isRaidType(deviceFields[3], knownRaidTypes) {
103134
mdType = deviceFields[3]
104-
} else if len(deviceFields) > 4 && isRaidType(deviceFields[4]) {
135+
deviceStartIndex = 4
136+
} else if len(deviceFields) > 4 && isRaidType(deviceFields[4], knownRaidTypes) {
105137
// if the 3rd field is (...), the 4th field is the mdType
106138
mdType = deviceFields[4]
139+
deviceStartIndex = 5
107140
}
108141
}
109142

110143
if len(lines) <= i+3 {
111144
return nil, fmt.Errorf("error parsing %q: too few lines for md device", mdName)
112145
}
113146

114-
// Failed disks have the suffix (F) & Spare disks have the suffix (S).
147+
// Failed (Faulty) disks have the suffix (F) & Spare disks have the suffix (S).
115148
fail := int64(strings.Count(line, "(F)"))
116149
spare := int64(strings.Count(line, "(S)"))
117150
active, total, down, size, err := evalStatusLine(lines[i], lines[i+1])
@@ -157,6 +190,11 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
157190
}
158191
}
159192

193+
devices, err := evalComponentDevices(deviceFields[deviceStartIndex:])
194+
if err != nil {
195+
return nil, fmt.Errorf("error parsing components in md device %q: %w", mdName, err)
196+
}
197+
160198
mdStats = append(mdStats, MDStat{
161199
Name: mdName,
162200
Type: mdType,
@@ -171,7 +209,7 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
171209
BlocksSyncedPct: pct,
172210
BlocksSyncedFinishTime: finish,
173211
BlocksSyncedSpeed: speed,
174-
Devices: evalComponentDevices(deviceFields),
212+
Devices: devices,
175213
})
176214
}
177215

@@ -181,11 +219,13 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
181219
// check if a string's format is like the mdType
182220
// Rule 1: mdType should not be like (...)
183221
// Rule 2: mdType should not be like sda[0]
184-
func isRaidType(mdType string) bool {
185-
return !strings.ContainsAny(mdType, "([")
222+
func isRaidType(mdType string, knownRaidTypes map[string]bool) bool {
223+
_, ok := knownRaidTypes[mdType]
224+
return !strings.ContainsAny(mdType, "([") && ok
186225
}
187226

188227
func evalStatusLine(deviceLine, statusLine string) (active, total, down, size int64, err error) {
228+
// e.g. 523968 blocks super 1.2 [4/4] [UUUU]
189229
statusFields := strings.Fields(statusLine)
190230
if len(statusFields) < 1 {
191231
return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q", statusLine)
@@ -270,17 +310,29 @@ func evalRecoveryLine(recoveryLine string) (syncedBlocks int64, pct float64, fin
270310
return syncedBlocks, pct, finish, speed, nil
271311
}
272312

273-
func evalComponentDevices(deviceFields []string) []string {
274-
mdComponentDevices := make([]string, 0)
275-
if len(deviceFields) > 3 {
276-
for _, field := range deviceFields[4:] {
277-
match := componentDeviceRE.FindStringSubmatch(field)
278-
if match == nil {
279-
continue
280-
}
281-
mdComponentDevices = append(mdComponentDevices, match[1])
313+
func evalComponentDevices(deviceFields []string) ([]MDStatComponent, error) {
314+
mdComponentDevices := make([]MDStatComponent, 0)
315+
for _, field := range deviceFields {
316+
match := componentDeviceRE.FindStringSubmatch(field)
317+
if match == nil {
318+
continue
282319
}
320+
descriptorIndex, err := strconv.ParseInt(match[2], 10, 32)
321+
if err != nil {
322+
return mdComponentDevices, fmt.Errorf("error parsing int from device %q: %w", match[2], err)
323+
}
324+
mdComponentDevices = append(mdComponentDevices, MDStatComponent{
325+
Name: match[1],
326+
DescriptorIndex: int32(descriptorIndex),
327+
// match may contain one or more of these
328+
// https://github.com/torvalds/linux/blob/7ec462100ef9142344ddbf86f2c3008b97acddbe/drivers/md/md.c#L8376-L8392
329+
Faulty: strings.Contains(match[3], "(F)"),
330+
Spare: strings.Contains(match[3], "(S)"),
331+
Journal: strings.Contains(match[3], "(J)"),
332+
Replacement: strings.Contains(match[3], "(R)"),
333+
WriteMostly: strings.Contains(match[3], "(W)"),
334+
})
283335
}
284336

285-
return mdComponentDevices
337+
return mdComponentDevices, nil
286338
}

mdstat_test.go

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,30 @@ func TestFS_MDStat(t *testing.T) {
2323
if err != nil {
2424
t.Fatalf("parsing of reference-file failed entirely: %s", err)
2525
}
26+
// TODO: Test cases to capture in future:
27+
// WriteMostly devices
28+
// Journal devices
29+
// Replacement devices
30+
// Global hotspares
2631

2732
refs := map[string]MDStat{
28-
"md127": {Name: "md127", Type: "raid1", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 312319552, BlocksSynced: 312319552, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sdi2", "sdj2"}},
29-
"md0": {Name: "md0", Type: "raid1", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 248896, BlocksSynced: 248896, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sdi1", "sdj1"}},
30-
"md4": {Name: "md4", Type: "raid1", ActivityState: "inactive", DisksActive: 0, DisksTotal: 0, DisksFailed: 1, DisksDown: 0, DisksSpare: 1, BlocksTotal: 4883648, BlocksSynced: 4883648, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sda3", "sdb3"}},
31-
"md6": {Name: "md6", Type: "raid1", ActivityState: "recovering", DisksActive: 1, DisksTotal: 2, DisksFailed: 1, DisksDown: 1, DisksSpare: 1, BlocksTotal: 195310144, BlocksSynced: 16775552, BlocksSyncedPct: 8.5, BlocksSyncedFinishTime: 17, BlocksSyncedSpeed: 259783, Devices: []string{"sdb2", "sdc", "sda2"}},
32-
"md3": {Name: "md3", Type: "raid6", ActivityState: "active", DisksActive: 8, DisksTotal: 8, DisksFailed: 0, DisksDown: 0, DisksSpare: 2, BlocksTotal: 5853468288, BlocksSynced: 5853468288, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sda1", "sdh1", "sdg1", "sdf1", "sde1", "sdd1", "sdc1", "sdb1", "sdd1", "sdd2"}},
33-
"md8": {Name: "md8", Type: "raid1", ActivityState: "resyncing", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 2, BlocksTotal: 195310144, BlocksSynced: 16775552, BlocksSyncedPct: 8.5, BlocksSyncedFinishTime: 17, BlocksSyncedSpeed: 259783, Devices: []string{"sdb1", "sda1", "sdc", "sde"}},
34-
"md7": {Name: "md7", Type: "raid6", ActivityState: "active", DisksActive: 3, DisksTotal: 4, DisksFailed: 1, DisksDown: 1, DisksSpare: 0, BlocksTotal: 7813735424, BlocksSynced: 7813735424, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sdb1", "sde1", "sdd1", "sdc1"}},
35-
"md9": {Name: "md9", Type: "raid1", ActivityState: "resyncing", DisksActive: 4, DisksTotal: 4, DisksSpare: 1, DisksDown: 0, DisksFailed: 2, BlocksTotal: 523968, BlocksSynced: 0, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sdc2", "sdd2", "sdb2", "sda2", "sde", "sdf", "sdg"}},
36-
"md10": {Name: "md10", Type: "raid0", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 314159265, BlocksSynced: 314159265, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sda1", "sdb1"}},
37-
"md11": {Name: "md11", Type: "raid1", ActivityState: "resyncing", DisksActive: 2, DisksTotal: 2, DisksFailed: 1, DisksDown: 0, DisksSpare: 2, BlocksTotal: 4190208, BlocksSynced: 0, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sdb2", "sdc2", "sdc3", "hda", "ssdc2"}},
38-
"md12": {Name: "md12", Type: "raid0", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksSpare: 0, DisksDown: 0, DisksFailed: 0, BlocksTotal: 3886394368, BlocksSynced: 3886394368, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sdc2", "sdd2"}},
39-
"md120": {Name: "md120", Type: "linear", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 2095104, BlocksSynced: 2095104, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sda1", "sdb1"}},
40-
"md126": {Name: "md126", Type: "raid0", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 1855870976, BlocksSynced: 1855870976, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sdb", "sdc"}},
41-
"md219": {Name: "md219", Type: "unknown", ActivityState: "inactive", DisksTotal: 0, DisksFailed: 0, DisksActive: 0, DisksDown: 0, DisksSpare: 3, BlocksTotal: 7932, BlocksSynced: 7932, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sdc", "sda"}},
42-
"md00": {Name: "md00", Type: "raid0", ActivityState: "active", DisksActive: 1, DisksTotal: 1, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 4186624, BlocksSynced: 4186624, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"xvdb"}},
43-
"md101": {Name: "md101", Type: "raid0", ActivityState: "active", DisksActive: 3, DisksTotal: 3, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 322560, BlocksSynced: 322560, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []string{"sdb", "sdd", "sdc"}},
44-
"md201": {Name: "md201", Type: "raid1", ActivityState: "checking", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 1993728, BlocksSynced: 114176, BlocksSyncedPct: 5.7, BlocksSyncedFinishTime: 0.2, BlocksSyncedSpeed: 114176, Devices: []string{"sda3", "sdb3"}},
33+
"md127": {Name: "md127", Type: "raid1", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 312319552, BlocksSynced: 312319552, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sdi2", DescriptorIndex: 0}, {Name: "sdj2", DescriptorIndex: 1}}},
34+
"md0": {Name: "md0", Type: "raid1", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 248896, BlocksSynced: 248896, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sdi1", DescriptorIndex: 0}, {Name: "sdj1", DescriptorIndex: 1}}},
35+
"md4": {Name: "md4", Type: "raid1", ActivityState: "inactive", DisksActive: 0, DisksTotal: 0, DisksFailed: 1, DisksDown: 0, DisksSpare: 1, BlocksTotal: 4883648, BlocksSynced: 4883648, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sda3", Faulty: true, DescriptorIndex: 0}, {Name: "sdb3", Spare: true, DescriptorIndex: 1}}},
36+
"md6": {Name: "md6", Type: "raid1", ActivityState: "recovering", DisksActive: 1, DisksTotal: 2, DisksFailed: 1, DisksDown: 1, DisksSpare: 1, BlocksTotal: 195310144, BlocksSynced: 16775552, BlocksSyncedPct: 8.5, BlocksSyncedFinishTime: 17, BlocksSyncedSpeed: 259783, Devices: []MDStatComponent{{Name: "sdb2", DescriptorIndex: 2, Faulty: true}, {Name: "sdc", DescriptorIndex: 1, Spare: true}, {Name: "sda2", DescriptorIndex: 0}}},
37+
"md3": {Name: "md3", Type: "raid6", ActivityState: "active", DisksActive: 8, DisksTotal: 8, DisksFailed: 0, DisksDown: 0, DisksSpare: 2, BlocksTotal: 5853468288, BlocksSynced: 5853468288, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sda1", DescriptorIndex: 8}, {Name: "sdh1", DescriptorIndex: 7}, {Name: "sdg1", DescriptorIndex: 6}, {Name: "sdf1", DescriptorIndex: 5}, {Name: "sde1", DescriptorIndex: 11}, {Name: "sdd1", DescriptorIndex: 3}, {Name: "sdc1", DescriptorIndex: 10}, {Name: "sdb1", DescriptorIndex: 9}, {Name: "sdd1", DescriptorIndex: 10, Spare: true}, {Name: "sdd2", DescriptorIndex: 11, Spare: true}}},
38+
"md8": {Name: "md8", Type: "raid1", ActivityState: "resyncing", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 2, BlocksTotal: 195310144, BlocksSynced: 16775552, BlocksSyncedPct: 8.5, BlocksSyncedFinishTime: 17, BlocksSyncedSpeed: 259783, Devices: []MDStatComponent{{Name: "sdb1", DescriptorIndex: 1}, {Name: "sda1", DescriptorIndex: 0}, {Name: "sdc", DescriptorIndex: 2, Spare: true}, {Name: "sde", DescriptorIndex: 3, Spare: true}}},
39+
"md7": {Name: "md7", Type: "raid6", ActivityState: "active", DisksActive: 3, DisksTotal: 4, DisksFailed: 1, DisksDown: 1, DisksSpare: 0, BlocksTotal: 7813735424, BlocksSynced: 7813735424, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sdb1", DescriptorIndex: 0}, {Name: "sde1", DescriptorIndex: 3}, {Name: "sdd1", DescriptorIndex: 2}, {Name: "sdc1", DescriptorIndex: 1, Faulty: true}}},
40+
"md9": {Name: "md9", Type: "raid1", ActivityState: "resyncing", DisksActive: 4, DisksTotal: 4, DisksSpare: 1, DisksDown: 0, DisksFailed: 2, BlocksTotal: 523968, BlocksSynced: 0, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sdc2", DescriptorIndex: 2}, {Name: "sdd2", DescriptorIndex: 3}, {Name: "sdb2", DescriptorIndex: 1}, {Name: "sda2", DescriptorIndex: 0}, {Name: "sde", DescriptorIndex: 4, Faulty: true}, {Name: "sdf", DescriptorIndex: 5, Faulty: true}, {Name: "sdg", DescriptorIndex: 6, Spare: true}}},
41+
"md10": {Name: "md10", Type: "raid0", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 314159265, BlocksSynced: 314159265, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sda1", DescriptorIndex: 0}, {Name: "sdb1", DescriptorIndex: 1}}},
42+
"md11": {Name: "md11", Type: "raid1", ActivityState: "resyncing", DisksActive: 2, DisksTotal: 2, DisksFailed: 1, DisksDown: 0, DisksSpare: 2, BlocksTotal: 4190208, BlocksSynced: 0, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sdb2", DescriptorIndex: 0}, {Name: "sdc2", DescriptorIndex: 1}, {Name: "sdc3", DescriptorIndex: 2, Faulty: true}, {Name: "hda", DescriptorIndex: 4, Spare: true}, {Name: "ssdc2", DescriptorIndex: 3, Spare: true}}},
43+
"md12": {Name: "md12", Type: "raid0", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksSpare: 0, DisksDown: 0, DisksFailed: 0, BlocksTotal: 3886394368, BlocksSynced: 3886394368, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sdc2", DescriptorIndex: 0}, {Name: "sdd2", DescriptorIndex: 1}}},
44+
"md120": {Name: "md120", Type: "linear", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 2095104, BlocksSynced: 2095104, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sda1", DescriptorIndex: 1}, {Name: "sdb1", DescriptorIndex: 0}}},
45+
"md126": {Name: "md126", Type: "raid0", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 1855870976, BlocksSynced: 1855870976, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sdb", DescriptorIndex: 1}, {Name: "sdc", DescriptorIndex: 0}}},
46+
"md219": {Name: "md219", Type: "unknown", ActivityState: "inactive", DisksTotal: 0, DisksFailed: 0, DisksActive: 0, DisksDown: 0, DisksSpare: 3, BlocksTotal: 7932, BlocksSynced: 7932, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sdb", DescriptorIndex: 2, Spare: true}, {Name: "sdc", DescriptorIndex: 1, Spare: true}, {Name: "sda", DescriptorIndex: 0, Spare: true}}},
47+
"md00": {Name: "md00", Type: "raid0", ActivityState: "active", DisksActive: 1, DisksTotal: 1, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 4186624, BlocksSynced: 4186624, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "xvdb", DescriptorIndex: 0}}},
48+
"md101": {Name: "md101", Type: "raid0", ActivityState: "active", DisksActive: 3, DisksTotal: 3, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 322560, BlocksSynced: 322560, BlocksSyncedPct: 0, BlocksSyncedFinishTime: 0, BlocksSyncedSpeed: 0, Devices: []MDStatComponent{{Name: "sdb", DescriptorIndex: 2}, {Name: "sdd", DescriptorIndex: 1}, {Name: "sdc", DescriptorIndex: 0}}},
49+
"md201": {Name: "md201", Type: "raid1", ActivityState: "checking", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksDown: 0, DisksSpare: 0, BlocksTotal: 1993728, BlocksSynced: 114176, BlocksSyncedPct: 5.7, BlocksSyncedFinishTime: 0.2, BlocksSyncedSpeed: 114176, Devices: []MDStatComponent{{Name: "sda3", DescriptorIndex: 0}, {Name: "sdb3", DescriptorIndex: 1}}},
4550
}
4651

4752
if want, have := len(refs), len(mdStats); want != have {
@@ -56,18 +61,32 @@ func TestFS_MDStat(t *testing.T) {
5661
}
5762

5863
func TestInvalidMdstat(t *testing.T) {
59-
invalidMount := [][]byte{[]byte(`
64+
invalidMount := [][]byte{
65+
// Test invalid Personality and format
66+
[]byte(`
6067
Personalities : [invalid]
6168
md3 : invalid
6269
314159265 blocks 64k chunks
6370
6471
unused devices: <none>
6572
`),
73+
// Test extra blank line
6674
[]byte(`
6775
md12 : active raid0 sdc2[0] sdd2[1]
6876
6977
3886394368 blocks super 1.2 512k chunks
70-
`)}
78+
`),
79+
// test for impossible component state
80+
[]byte(`
81+
md127 : active raid1 sdi2[0] sdj2[1](Z)
82+
312319552 blocks [2/2] [UU]
83+
`),
84+
// test for malformed component state
85+
[]byte(`
86+
md127 : active raid1 sdi2[0] sdj2[X]
87+
312319552 blocks [2/2] [UU]
88+
`),
89+
}
7190

7291
for _, invalid := range invalidMount {
7392
_, err := parseMDStat(invalid)

0 commit comments

Comments
 (0)