diff --git a/mdstat.go b/mdstat.go index dd2b8988..67a9d2b4 100644 --- a/mdstat.go +++ b/mdstat.go @@ -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]`) @@ -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) @@ -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) @@ -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) } @@ -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, @@ -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 { diff --git a/mdstat_test.go b/mdstat_test.go index 871e95ae..adeb2bb1 100644 --- a/mdstat_test.go +++ b/mdstat_test.go @@ -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 {