mdstat.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Copyright 2018 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package procfs
  14. import (
  15. "fmt"
  16. "io/ioutil"
  17. "regexp"
  18. "strconv"
  19. "strings"
  20. )
  21. var (
  22. statuslineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`)
  23. buildlineRE = regexp.MustCompile(`\((\d+)/\d+\)`)
  24. )
  25. // MDStat holds info parsed from /proc/mdstat.
  26. type MDStat struct {
  27. // Name of the device.
  28. Name string
  29. // activity-state of the device.
  30. ActivityState string
  31. // Number of active disks.
  32. DisksActive int64
  33. // Total number of disks the device consists of.
  34. DisksTotal int64
  35. // Number of blocks the device holds.
  36. BlocksTotal int64
  37. // Number of blocks on the device that are in sync.
  38. BlocksSynced int64
  39. }
  40. // ParseMDStat parses an mdstat-file and returns a struct with the relevant infos.
  41. func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
  42. mdStatusFilePath := fs.Path("mdstat")
  43. content, err := ioutil.ReadFile(mdStatusFilePath)
  44. if err != nil {
  45. return []MDStat{}, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
  46. }
  47. mdStates := []MDStat{}
  48. lines := strings.Split(string(content), "\n")
  49. for i, l := range lines {
  50. if l == "" {
  51. continue
  52. }
  53. if l[0] == ' ' {
  54. continue
  55. }
  56. if strings.HasPrefix(l, "Personalities") || strings.HasPrefix(l, "unused") {
  57. continue
  58. }
  59. mainLine := strings.Split(l, " ")
  60. if len(mainLine) < 3 {
  61. return mdStates, fmt.Errorf("error parsing mdline: %s", l)
  62. }
  63. mdName := mainLine[0]
  64. activityState := mainLine[2]
  65. if len(lines) <= i+3 {
  66. return mdStates, fmt.Errorf(
  67. "error parsing %s: too few lines for md device %s",
  68. mdStatusFilePath,
  69. mdName,
  70. )
  71. }
  72. active, total, size, err := evalStatusline(lines[i+1])
  73. if err != nil {
  74. return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
  75. }
  76. // j is the line number of the syncing-line.
  77. j := i + 2
  78. if strings.Contains(lines[i+2], "bitmap") { // skip bitmap line
  79. j = i + 3
  80. }
  81. // If device is syncing at the moment, get the number of currently
  82. // synced bytes, otherwise that number equals the size of the device.
  83. syncedBlocks := size
  84. if strings.Contains(lines[j], "recovery") || strings.Contains(lines[j], "resync") {
  85. syncedBlocks, err = evalBuildline(lines[j])
  86. if err != nil {
  87. return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
  88. }
  89. }
  90. mdStates = append(mdStates, MDStat{
  91. Name: mdName,
  92. ActivityState: activityState,
  93. DisksActive: active,
  94. DisksTotal: total,
  95. BlocksTotal: size,
  96. BlocksSynced: syncedBlocks,
  97. })
  98. }
  99. return mdStates, nil
  100. }
  101. func evalStatusline(statusline string) (active, total, size int64, err error) {
  102. matches := statuslineRE.FindStringSubmatch(statusline)
  103. if len(matches) != 4 {
  104. return 0, 0, 0, fmt.Errorf("unexpected statusline: %s", statusline)
  105. }
  106. size, err = strconv.ParseInt(matches[1], 10, 64)
  107. if err != nil {
  108. return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
  109. }
  110. total, err = strconv.ParseInt(matches[2], 10, 64)
  111. if err != nil {
  112. return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
  113. }
  114. active, err = strconv.ParseInt(matches[3], 10, 64)
  115. if err != nil {
  116. return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
  117. }
  118. return active, total, size, nil
  119. }
  120. func evalBuildline(buildline string) (syncedBlocks int64, err error) {
  121. matches := buildlineRE.FindStringSubmatch(buildline)
  122. if len(matches) != 2 {
  123. return 0, fmt.Errorf("unexpected buildline: %s", buildline)
  124. }
  125. syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64)
  126. if err != nil {
  127. return 0, fmt.Errorf("%s in buildline: %s", err, buildline)
  128. }
  129. return syncedBlocks, nil
  130. }