Files
logpile/internal/parser/vendors/inspur/storage_serial_fallback.go

149 lines
3.0 KiB
Go

package inspur
import (
"regexp"
"sort"
"strings"
"git.mchus.pro/mchus/logpile/internal/models"
"git.mchus.pro/mchus/logpile/internal/parser"
)
var bpHDDSerialTokenRegex = regexp.MustCompile(`[A-Za-z0-9]{8,32}`)
func enrichStorageFromSerialFallbackFiles(files []parser.ExtractedFile, hw *models.HardwareConfig) {
if hw == nil {
return
}
f := parser.FindFileByName(files, "BpHDDSerialNumber.info")
if f == nil {
return
}
serials := extractBPHDDSerials(f.Content)
if len(serials) == 0 {
return
}
applyStorageSerialFallback(hw, serials)
}
func extractBPHDDSerials(content []byte) []string {
if len(content) == 0 {
return nil
}
matches := bpHDDSerialTokenRegex.FindAllString(string(content), -1)
if len(matches) == 0 {
return nil
}
out := make([]string, 0, len(matches))
seen := make(map[string]struct{}, len(matches))
for _, m := range matches {
v := normalizeRedisValue(m)
if !looksLikeStorageSerial(v) {
continue
}
key := strings.ToLower(v)
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
out = append(out, v)
}
return out
}
func looksLikeStorageSerial(v string) bool {
if len(v) < 8 {
return false
}
hasLetter := false
hasDigit := false
for _, r := range v {
switch {
case r >= 'A' && r <= 'Z':
hasLetter = true
case r >= 'a' && r <= 'z':
hasLetter = true
case r >= '0' && r <= '9':
hasDigit = true
default:
return false
}
}
return hasLetter && hasDigit
}
func applyStorageSerialFallback(hw *models.HardwareConfig, serials []string) {
if hw == nil || len(hw.Storage) == 0 || len(serials) == 0 {
return
}
existing := make(map[string]struct{}, len(hw.Storage))
for _, dev := range hw.Storage {
if sn := normalizeRedisValue(dev.SerialNumber); sn != "" {
existing[strings.ToLower(sn)] = struct{}{}
}
}
filtered := make([]string, 0, len(serials))
for _, sn := range serials {
key := strings.ToLower(sn)
if _, ok := existing[key]; ok {
continue
}
filtered = append(filtered, sn)
}
if len(filtered) == 0 {
return
}
type target struct {
index int
rank int
slot string
}
targets := make([]target, 0, len(hw.Storage))
for i := range hw.Storage {
dev := hw.Storage[i]
if normalizeRedisValue(dev.SerialNumber) != "" {
continue
}
if !dev.Present && strings.TrimSpace(dev.Slot) == "" {
continue
}
rank := 0
if !dev.Present {
rank += 10
}
if strings.EqualFold(strings.TrimSpace(dev.Type), "NVMe") {
rank += 5
}
if strings.TrimSpace(dev.Slot) == "" {
rank += 4
}
targets = append(targets, target{
index: i,
rank: rank,
slot: strings.ToLower(strings.TrimSpace(dev.Slot)),
})
}
if len(targets) == 0 {
return
}
sort.Slice(targets, func(i, j int) bool {
if targets[i].rank != targets[j].rank {
return targets[i].rank < targets[j].rank
}
return targets[i].slot < targets[j].slot
})
for i := 0; i < len(targets) && i < len(filtered); i++ {
dev := &hw.Storage[targets[i].index]
dev.SerialNumber = filtered[i]
if !dev.Present {
dev.Present = true
}
}
}