Skip to content

Commit

Permalink
Parse recovery line to be synced blocks (#637)
Browse files Browse the repository at this point in the history
Capture the blocks to be synced from the recvery line as it can differ
from the total blocks.
* Reformat test struct to make it easier to read.

Fixes: #636

Signed-off-by: SuperQ <superq@gmail.com>
  • Loading branch information
SuperQ committed May 13, 2024
1 parent 1adce6b commit 5a801c6
Show file tree
Hide file tree
Showing 2 changed files with 281 additions and 33 deletions.
42 changes: 26 additions & 16 deletions mdstat.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (

var (
statusLineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[([U_]+)\]`)
recoveryLineBlocksRE = regexp.MustCompile(`\((\d+)/\d+\)`)
recoveryLineBlocksRE = regexp.MustCompile(`\((\d+/\d+)\)`)
recoveryLinePctRE = regexp.MustCompile(`= (.+)%`)
recoveryLineFinishRE = regexp.MustCompile(`finish=(.+)min`)
recoveryLineSpeedRE = regexp.MustCompile(`speed=(.+)[A-Z]`)
Expand All @@ -50,6 +50,8 @@ type MDStat struct {
BlocksTotal int64
// Number of blocks on the device that are in sync.
BlocksSynced int64
// Number of blocks on the device that need to be synced.
BlocksToBeSynced int64
// progress percentage of current sync
BlocksSyncedPct float64
// estimated finishing time for current sync (in minutes)
Expand Down Expand Up @@ -115,7 +117,8 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {

// If device is syncing at the moment, get the number of currently
// synced bytes, otherwise that number equals the size of the device.
syncedBlocks := size
blocksSynced := size
blocksToBeSynced := size
speed := float64(0)
finish := float64(0)
pct := float64(0)
Expand All @@ -136,9 +139,9 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
// Handle case when resync=PENDING or resync=DELAYED.
if strings.Contains(lines[syncLineIdx], "PENDING") ||
strings.Contains(lines[syncLineIdx], "DELAYED") {
syncedBlocks = 0
blocksSynced = 0
} else {
syncedBlocks, pct, finish, speed, err = evalRecoveryLine(lines[syncLineIdx])
blocksSynced, blocksToBeSynced, pct, finish, speed, err = evalRecoveryLine(lines[syncLineIdx])
if err != nil {
return nil, fmt.Errorf("%w: Cannot parse sync line in md device: %q: %w", ErrFileParse, mdName, err)
}
Expand All @@ -154,7 +157,8 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
DisksSpare: spare,
DisksTotal: total,
BlocksTotal: size,
BlocksSynced: syncedBlocks,
BlocksSynced: blocksSynced,
BlocksToBeSynced: blocksToBeSynced,
BlocksSyncedPct: pct,
BlocksSyncedFinishTime: finish,
BlocksSyncedSpeed: speed,
Expand Down Expand Up @@ -206,48 +210,54 @@ func evalStatusLine(deviceLine, statusLine string) (active, total, down, size in
return active, total, down, size, nil
}

func evalRecoveryLine(recoveryLine string) (syncedBlocks int64, pct float64, finish float64, speed float64, err error) {
func evalRecoveryLine(recoveryLine string) (blocksSynced int64, blocksToBeSynced int64, pct float64, finish float64, speed float64, err error) {
matches := recoveryLineBlocksRE.FindStringSubmatch(recoveryLine)
if len(matches) != 2 {
return 0, 0, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine %s: %w", ErrFileParse, recoveryLine, err)
return 0, 0, 0, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine blocks %s: %w", ErrFileParse, recoveryLine, err)
}

syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64)
blocks := strings.Split(matches[1], "/")
blocksSynced, err = strconv.ParseInt(blocks[0], 10, 64)
if err != nil {
return 0, 0, 0, 0, fmt.Errorf("%w: Unexpected parsing of recoveryLine %q: %w", ErrFileParse, recoveryLine, err)
return 0, 0, 0, 0, 0, fmt.Errorf("%w: Unable to parse recovery blocks synced %q: %w", ErrFileParse, matches[1], err)
}

blocksToBeSynced, err = strconv.ParseInt(blocks[1], 10, 64)
if err != nil {
return blocksSynced, 0, 0, 0, 0, fmt.Errorf("%w: Unable to parse recovery to be synced blocks %q: %w", ErrFileParse, matches[2], err)
}

// Get percentage complete
matches = recoveryLinePctRE.FindStringSubmatch(recoveryLine)
if len(matches) != 2 {
return syncedBlocks, 0, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine matching percentage %s", ErrFileParse, recoveryLine)
return blocksSynced, blocksToBeSynced, 0, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine matching percentage %s", ErrFileParse, recoveryLine)
}
pct, err = strconv.ParseFloat(strings.TrimSpace(matches[1]), 64)
if err != nil {
return syncedBlocks, 0, 0, 0, fmt.Errorf("%w: Error parsing float from recoveryLine %q", ErrFileParse, recoveryLine)
return blocksSynced, blocksToBeSynced, 0, 0, 0, fmt.Errorf("%w: Error parsing float from recoveryLine %q", ErrFileParse, recoveryLine)
}

// Get time expected left to complete
matches = recoveryLineFinishRE.FindStringSubmatch(recoveryLine)
if len(matches) != 2 {
return syncedBlocks, pct, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine matching est. finish time: %s", ErrFileParse, recoveryLine)
return blocksSynced, blocksToBeSynced, pct, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine matching est. finish time: %s", ErrFileParse, recoveryLine)
}
finish, err = strconv.ParseFloat(matches[1], 64)
if err != nil {
return syncedBlocks, pct, 0, 0, fmt.Errorf("%w: Unable to parse float from recoveryLine: %q", ErrFileParse, recoveryLine)
return blocksSynced, blocksToBeSynced, pct, 0, 0, fmt.Errorf("%w: Unable to parse float from recoveryLine: %q", ErrFileParse, recoveryLine)
}

// Get recovery speed
matches = recoveryLineSpeedRE.FindStringSubmatch(recoveryLine)
if len(matches) != 2 {
return syncedBlocks, pct, finish, 0, fmt.Errorf("%w: Unexpected recoveryLine value: %s", ErrFileParse, recoveryLine)
return blocksSynced, blocksToBeSynced, pct, finish, 0, fmt.Errorf("%w: Unexpected recoveryLine value: %s", ErrFileParse, recoveryLine)
}
speed, err = strconv.ParseFloat(matches[1], 64)
if err != nil {
return syncedBlocks, pct, finish, 0, fmt.Errorf("%w: Error parsing float from recoveryLine: %q: %w", ErrFileParse, recoveryLine, err)
return blocksSynced, blocksToBeSynced, pct, finish, 0, fmt.Errorf("%w: Error parsing float from recoveryLine: %q: %w", ErrFileParse, recoveryLine, err)
}

return syncedBlocks, pct, finish, speed, nil
return blocksSynced, blocksToBeSynced, pct, finish, speed, nil
}

func evalComponentDevices(deviceFields []string) []string {
Expand Down
272 changes: 255 additions & 17 deletions mdstat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,261 @@ func TestFS_MDStat(t *testing.T) {
}

refs := map[string]MDStat{
"md127": {Name: "md127", 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"}},
"md0": {Name: "md0", 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"}},
"md4": {Name: "md4", 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"}},
"md6": {Name: "md6", 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"}},
"md3": {Name: "md3", 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"}},
"md8": {Name: "md8", 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"}},
"md7": {Name: "md7", 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"}},
"md9": {Name: "md9", 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"}},
"md10": {Name: "md10", 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"}},
"md11": {Name: "md11", 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"}},
"md12": {Name: "md12", 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"}},
"md120": {Name: "md120", 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"}},
"md126": {Name: "md126", 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"}},
"md219": {Name: "md219", 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"}},
"md00": {Name: "md00", 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"}},
"md101": {Name: "md101", 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"}},
"md201": {Name: "md201", 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"}},
"md127": {
Name: "md127",
ActivityState: "active",
DisksActive: 2,
DisksTotal: 2,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 0,
BlocksTotal: 312319552,
BlocksSynced: 312319552,
BlocksToBeSynced: 312319552,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sdi2", "sdj2"}},
"md0": {
Name: "md0",
ActivityState: "active",
DisksActive: 2,
DisksTotal: 2,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 0,
BlocksTotal: 248896,
BlocksSynced: 248896,
BlocksToBeSynced: 248896,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sdi1", "sdj1"}},
"md4": {
Name: "md4",
ActivityState: "inactive",
DisksActive: 0,
DisksTotal: 0,
DisksFailed: 1,
DisksDown: 0,
DisksSpare: 1,
BlocksTotal: 4883648,
BlocksSynced: 4883648,
BlocksToBeSynced: 4883648,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sda3", "sdb3"}},
"md6": {
Name: "md6",
ActivityState: "recovering",
DisksActive: 1,
DisksTotal: 2,
DisksFailed: 1,
DisksDown: 1,
DisksSpare: 1,
BlocksTotal: 195310144,
BlocksSynced: 16775552,
BlocksToBeSynced: 195310144,
BlocksSyncedPct: 8.5,
BlocksSyncedFinishTime: 17,
BlocksSyncedSpeed: 259783,
Devices: []string{"sdb2", "sdc", "sda2"}},
"md3": {
Name: "md3",
ActivityState: "active",
DisksActive: 8,
DisksTotal: 8,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 2,
BlocksTotal: 5853468288,
BlocksSynced: 5853468288,
BlocksToBeSynced: 5853468288,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sda1", "sdh1", "sdg1", "sdf1", "sde1", "sdd1", "sdc1", "sdb1", "sdd1", "sdd2"}},
"md8": {
Name: "md8",
ActivityState: "resyncing",
DisksActive: 2,
DisksTotal: 2,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 2,
BlocksTotal: 195310144,
BlocksSynced: 16775552,
BlocksToBeSynced: 195310144,
BlocksSyncedPct: 8.5,
BlocksSyncedFinishTime: 17,
BlocksSyncedSpeed: 259783,
Devices: []string{"sdb1", "sda1", "sdc", "sde"}},
"md7": {
Name: "md7",
ActivityState: "active",
DisksActive: 3,
DisksTotal: 4,
DisksFailed: 1,
DisksDown: 1,
DisksSpare: 0,
BlocksTotal: 7813735424,
BlocksSynced: 7813735424,
BlocksToBeSynced: 7813735424,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sdb1", "sde1", "sdd1", "sdc1"}},
"md9": {
Name: "md9",
ActivityState: "resyncing",
DisksActive: 4,
DisksTotal: 4,
DisksSpare: 1,
DisksDown: 0,
DisksFailed: 2,
BlocksTotal: 523968,
BlocksSynced: 0,
BlocksToBeSynced: 523968,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sdc2", "sdd2", "sdb2", "sda2", "sde", "sdf", "sdg"}},
"md10": {
Name: "md10",
ActivityState: "active",
DisksActive: 2,
DisksTotal: 2,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 0,
BlocksTotal: 314159265,
BlocksSynced: 314159265,
BlocksToBeSynced: 314159265,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sda1", "sdb1"}},
"md11": {
Name: "md11",
ActivityState: "resyncing",
DisksActive: 2,
DisksTotal: 2,
DisksFailed: 1,
DisksDown: 0,
DisksSpare: 2,
BlocksTotal: 4190208,
BlocksSynced: 0,
BlocksToBeSynced: 4190208,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sdb2", "sdc2", "sdc3", "hda", "ssdc2"}},
"md12": {
Name: "md12",
ActivityState: "active",
DisksActive: 2,
DisksTotal: 2,
DisksSpare: 0,
DisksDown: 0,
DisksFailed: 0,
BlocksTotal: 3886394368,
BlocksSynced: 3886394368,
BlocksToBeSynced: 3886394368,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sdc2", "sdd2"}},
"md120": {
Name: "md120",
ActivityState: "active",
DisksActive: 2,
DisksTotal: 2,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 0,
BlocksTotal: 2095104,
BlocksSynced: 2095104,
BlocksToBeSynced: 2095104,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sda1", "sdb1"}},
"md126": {
Name: "md126",
ActivityState: "active",
DisksActive: 2,
DisksTotal: 2,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 0,
BlocksTotal: 1855870976,
BlocksSynced: 1855870976,
BlocksToBeSynced: 1855870976,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sdb", "sdc"}},
"md219": {
Name: "md219",
ActivityState: "inactive",
DisksTotal: 0,
DisksFailed: 0,
DisksActive: 0,
DisksDown: 0,
DisksSpare: 3,
BlocksTotal: 7932,
BlocksSynced: 7932,
BlocksToBeSynced: 7932,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sdc", "sda"}},
"md00": {
Name: "md00",
ActivityState: "active",
DisksActive: 1,
DisksTotal: 1,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 0,
BlocksTotal: 4186624,
BlocksSynced: 4186624,
BlocksToBeSynced: 4186624,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"xvdb"}},
"md101": {
Name: "md101",
ActivityState: "active",
DisksActive: 3,
DisksTotal: 3,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 0,
BlocksTotal: 322560,
BlocksSynced: 322560,
BlocksToBeSynced: 322560,
BlocksSyncedPct: 0,
BlocksSyncedFinishTime: 0,
BlocksSyncedSpeed: 0,
Devices: []string{"sdb", "sdd", "sdc"}},
"md201": {
Name: "md201",
ActivityState: "checking",
DisksActive: 2,
DisksTotal: 2,
DisksFailed: 0,
DisksDown: 0,
DisksSpare: 0,
BlocksTotal: 1993728,
BlocksSynced: 114176,
BlocksToBeSynced: 1993728,
BlocksSyncedPct: 5.7,
BlocksSyncedFinishTime: 0.2,
BlocksSyncedSpeed: 114176,
Devices: []string{"sda3", "sdb3"}},
}

if want, have := len(refs), len(mdStats); want != have {
Expand Down

0 comments on commit 5a801c6

Please sign in to comment.