sync.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package raid
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "strings"
  8. )
  9. type SyncState struct {
  10. DeviceName string //e.g, md0
  11. ResyncPercent float64 //e.g, 0.5
  12. CompletedBlocks int64
  13. TotalBlocks int64
  14. ExpectedTime string //e.g, 1h23m
  15. Speed string //e.g, 1234K/s
  16. }
  17. // GetSyncStateByPath retrieves the synchronization state of a RAID device by its device path.
  18. // devpath can be either a full path (e.g., /dev/md0) or just the device name (e.g., md0).
  19. func (m *Manager) GetSyncStateByPath(devpath string) (*SyncState, error) {
  20. devpath = strings.TrimPrefix(devpath, "/dev/")
  21. syncStates, err := m.GetSyncStates()
  22. if err != nil {
  23. return nil, err
  24. }
  25. for _, syncState := range syncStates {
  26. if syncState.DeviceName == devpath {
  27. return &syncState, nil
  28. }
  29. }
  30. return nil, errors.New("device not found")
  31. }
  32. // GetSyncStates retrieves the synchronization states of RAID arrays from /proc/mdstat.
  33. func (m *Manager) GetSyncStates() ([]SyncState, error) {
  34. file, err := os.Open("/proc/mdstat")
  35. if err != nil {
  36. return nil, err
  37. }
  38. defer file.Close()
  39. var syncStates []SyncState
  40. var lastDeviceName string = ""
  41. scanner := bufio.NewScanner(file)
  42. for scanner.Scan() {
  43. line := scanner.Text()
  44. if strings.Contains(line, "resync =") || strings.Contains(line, "recovery =") {
  45. parts := strings.Fields(line)
  46. var syncState SyncState
  47. for i, part := range parts {
  48. if part == "resync" || part == "recovery" {
  49. // Extract percentage
  50. if i+2 < len(parts) && strings.HasSuffix(parts[i+2], "%") {
  51. fmt.Sscanf(parts[i+2], "%f%%", &syncState.ResyncPercent)
  52. }
  53. // Extract completed and total blocks
  54. if i+3 < len(parts) && strings.HasPrefix(parts[i+3], "(") && strings.Contains(parts[i+3], "/") {
  55. var completed, total int64
  56. fmt.Sscanf(parts[i+3], "(%d/%d)", &completed, &total)
  57. syncState.CompletedBlocks = completed
  58. syncState.TotalBlocks = total
  59. }
  60. // Extract expected time
  61. if i+4 < len(parts) && strings.HasPrefix(parts[i+4], "finish=") {
  62. syncState.ExpectedTime = strings.TrimPrefix(parts[i+4], "finish=")
  63. }
  64. // Extract speed
  65. if i+5 < len(parts) && strings.HasPrefix(parts[i+5], "speed=") {
  66. syncState.Speed = strings.TrimPrefix(parts[i+5], "speed=")
  67. }
  68. }
  69. }
  70. syncState.DeviceName = lastDeviceName
  71. if syncState.DeviceName == "" {
  72. return nil, errors.New("device name not found")
  73. }
  74. // Add the sync state to the list
  75. syncStates = append(syncStates, syncState)
  76. } else if strings.HasPrefix(line, "md") {
  77. // Extract device name
  78. parts := strings.Fields(line)
  79. if len(parts) > 0 {
  80. lastDeviceName = strings.TrimPrefix(parts[0], "/dev/")
  81. }
  82. }
  83. }
  84. if err := scanner.Err(); err != nil {
  85. return nil, err
  86. }
  87. return syncStates, nil
  88. }