Skip to content

Commit

Permalink
Parse recovery line to be synced blocks
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 148ef37
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 148ef37

Please sign in to comment.