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 } } }