Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / prometheus / procfs / mdstat.go
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 }