parseMDStat parses data from mdstat file (/proc/mdstat) and returns a slice of structs containing the relevant info.
(mdStatData []byte)
| 101 | // parseMDStat parses data from mdstat file (/proc/mdstat) and returns a slice of |
| 102 | // structs containing the relevant info. |
| 103 | func parseMDStat(mdStatData []byte) ([]MDStat, error) { |
| 104 | // TODO: |
| 105 | // - parse global hotspares from the "unused devices" line. |
| 106 | mdStats := []MDStat{} |
| 107 | lines := strings.Split(string(mdStatData), "\n") |
| 108 | knownRaidTypes := make(map[string]bool) |
| 109 | |
| 110 | for i, line := range lines { |
| 111 | if strings.TrimSpace(line) == "" || line[0] == ' ' || |
| 112 | strings.HasPrefix(line, "unused") { |
| 113 | continue |
| 114 | } |
| 115 | // Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10] |
| 116 | if len(knownRaidTypes) == 0 && strings.HasPrefix(line, personalitiesPrefix) { |
| 117 | personalities := strings.Fields(line[len(personalitiesPrefix):]) |
| 118 | for _, word := range personalities { |
| 119 | word := word[1 : len(word)-1] |
| 120 | knownRaidTypes[word] = true |
| 121 | } |
| 122 | continue |
| 123 | } |
| 124 | |
| 125 | deviceFields := strings.Fields(line) |
| 126 | if len(deviceFields) < 3 { |
| 127 | return nil, fmt.Errorf("%w: Expected 3+ lines, got %q", ErrFileParse, line) |
| 128 | } |
| 129 | mdName := deviceFields[0] // mdx |
| 130 | state := deviceFields[2] // active, inactive, broken |
| 131 | |
| 132 | mdType := "unknown" // raid1, raid5, etc. |
| 133 | var deviceStartIndex int |
| 134 | if len(deviceFields) > 3 { // mdType may be in the 3rd or 4th field |
| 135 | if isRaidType(deviceFields[3], knownRaidTypes) { |
| 136 | mdType = deviceFields[3] |
| 137 | deviceStartIndex = 4 |
| 138 | } else if len(deviceFields) > 4 && isRaidType(deviceFields[4], knownRaidTypes) { |
| 139 | // if the 3rd field is (...), the 4th field is the mdType |
| 140 | mdType = deviceFields[4] |
| 141 | deviceStartIndex = 5 |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | if len(lines) <= i+3 { |
| 146 | return nil, fmt.Errorf("%w: Too few lines for md device: %q", ErrFileParse, mdName) |
| 147 | } |
| 148 | |
| 149 | // Failed (Faulty) disks have the suffix (F) & Spare disks have the suffix (S). |
| 150 | fail := int64(strings.Count(line, "(F)")) |
| 151 | spare := int64(strings.Count(line, "(S)")) |
| 152 | active, total, down, size, err := evalStatusLine(lines[i], lines[i+1]) |
| 153 | |
| 154 | if err != nil { |
| 155 | return nil, fmt.Errorf("%w: Cannot parse md device lines: %v: %w", ErrFileParse, active, err) |
| 156 | } |
| 157 | |
| 158 | syncLineIdx := i + 2 |
| 159 | if strings.Contains(lines[i+2], "bitmap") { // skip bitmap line |
| 160 | syncLineIdx++ |
searching dependent graphs…