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