mdstat.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package procfs
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. )
  9. var (
  10. statuslineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`)
  11. buildlineRE = regexp.MustCompile(`\((\d+)/\d+\)`)
  12. )
  13. // MDStat holds info parsed from /proc/mdstat.
  14. type MDStat struct {
  15. // Name of the device.
  16. Name string
  17. // activity-state of the device.
  18. ActivityState string
  19. // Number of active disks.
  20. DisksActive int64
  21. // Total number of disks the device consists of.
  22. DisksTotal int64
  23. // Number of blocks the device holds.
  24. BlocksTotal int64
  25. // Number of blocks on the device that are in sync.
  26. BlocksSynced int64
  27. }
  28. // ParseMDStat parses an mdstat-file and returns a struct with the relevant infos.
  29. func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
  30. mdStatusFilePath := fs.Path("mdstat")
  31. content, err := ioutil.ReadFile(mdStatusFilePath)
  32. if err != nil {
  33. return []MDStat{}, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
  34. }
  35. mdStates := []MDStat{}
  36. lines := strings.Split(string(content), "\n")
  37. for i, l := range lines {
  38. if l == "" {
  39. continue
  40. }
  41. if l[0] == ' ' {
  42. continue
  43. }
  44. if strings.HasPrefix(l, "Personalities") || strings.HasPrefix(l, "unused") {
  45. continue
  46. }
  47. mainLine := strings.Split(l, " ")
  48. if len(mainLine) < 3 {
  49. return mdStates, fmt.Errorf("error parsing mdline: %s", l)
  50. }
  51. mdName := mainLine[0]
  52. activityState := mainLine[2]
  53. if len(lines) <= i+3 {
  54. return mdStates, fmt.Errorf(
  55. "error parsing %s: too few lines for md device %s",
  56. mdStatusFilePath,
  57. mdName,
  58. )
  59. }
  60. active, total, size, err := evalStatusline(lines[i+1])
  61. if err != nil {
  62. return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
  63. }
  64. // j is the line number of the syncing-line.
  65. j := i + 2
  66. if strings.Contains(lines[i+2], "bitmap") { // skip bitmap line
  67. j = i + 3
  68. }
  69. // If device is syncing at the moment, get the number of currently
  70. // synced bytes, otherwise that number equals the size of the device.
  71. syncedBlocks := size
  72. if strings.Contains(lines[j], "recovery") || strings.Contains(lines[j], "resync") {
  73. syncedBlocks, err = evalBuildline(lines[j])
  74. if err != nil {
  75. return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
  76. }
  77. }
  78. mdStates = append(mdStates, MDStat{
  79. Name: mdName,
  80. ActivityState: activityState,
  81. DisksActive: active,
  82. DisksTotal: total,
  83. BlocksTotal: size,
  84. BlocksSynced: syncedBlocks,
  85. })
  86. }
  87. return mdStates, nil
  88. }
  89. func evalStatusline(statusline string) (active, total, size int64, err error) {
  90. matches := statuslineRE.FindStringSubmatch(statusline)
  91. if len(matches) != 4 {
  92. return 0, 0, 0, fmt.Errorf("unexpected statusline: %s", statusline)
  93. }
  94. size, err = strconv.ParseInt(matches[1], 10, 64)
  95. if err != nil {
  96. return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
  97. }
  98. total, err = strconv.ParseInt(matches[2], 10, 64)
  99. if err != nil {
  100. return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
  101. }
  102. active, err = strconv.ParseInt(matches[3], 10, 64)
  103. if err != nil {
  104. return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
  105. }
  106. return active, total, size, nil
  107. }
  108. func evalBuildline(buildline string) (syncedBlocks int64, err error) {
  109. matches := buildlineRE.FindStringSubmatch(buildline)
  110. if len(matches) != 2 {
  111. return 0, fmt.Errorf("unexpected buildline: %s", buildline)
  112. }
  113. syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64)
  114. if err != nil {
  115. return 0, fmt.Errorf("%s in buildline: %s", err, buildline)
  116. }
  117. return syncedBlocks, nil
  118. }