202 lines
5.8 KiB
Go
202 lines
5.8 KiB
Go
package xigmanas
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.mchus.pro/mchus/logpile/internal/parser"
|
|
)
|
|
|
|
func TestParserDetect(t *testing.T) {
|
|
p := &Parser{}
|
|
|
|
files := []parser.ExtractedFile{
|
|
{
|
|
Path: "xigmanas",
|
|
Content: []byte(`Version:
|
|
--------
|
|
14.3.0.5
|
|
loader_brand="XigmaNAS"`),
|
|
},
|
|
}
|
|
|
|
if got := p.Detect(files); got < 70 {
|
|
t.Fatalf("expected high confidence, got %d", got)
|
|
}
|
|
|
|
files2 := []parser.ExtractedFile{
|
|
{
|
|
Path: "random_file.txt",
|
|
Content: []byte("Some random content"),
|
|
},
|
|
}
|
|
|
|
if got := p.Detect(files2); got != 0 {
|
|
t.Fatalf("expected zero confidence, got %d", got)
|
|
}
|
|
}
|
|
|
|
func TestParserParseInlineDump(t *testing.T) {
|
|
p := &Parser{}
|
|
result, err := p.Parse([]parser.ExtractedFile{{Path: "diagnostic.txt", Content: []byte(`Version:
|
|
--------
|
|
14.3.0.5
|
|
smbios.bios.version="F.22"
|
|
smbios.system.maker="Acme"
|
|
smbios.system.product="NAS-1"
|
|
smbios.system.serial="SN-1"
|
|
smbios.system.uuid="uuid-1"
|
|
FreeBSD/SMP: 1 package(s) x 4 core(s)
|
|
CPU: AMD Ryzen 5 3600 (3600.00-MHz)
|
|
CPU: AMD Ryzen 5 3600 (3600.00-MHz)
|
|
real memory = 8589934592 (8192 MB)
|
|
10:10AM up 2 days, 3 users, load averages: 0.10, 0.20, 0.30
|
|
state: DEGRADED
|
|
<WDC WD10EZEX 1.01> at scbus0 target 0 lun 0 (da0,pass0)
|
|
S.M.A.R.T. [/dev/da0]:
|
|
--------
|
|
Device Model: WDC WD10EZEX
|
|
Serial Number: DISK-1
|
|
Firmware Version: 1.01
|
|
User Capacity: 1,000,000,000 bytes
|
|
SMART overall-health self-assessment test result: FAILED
|
|
194 Temperature_Celsius 0x0022 100 100 000 Old_age Always - 41
|
|
Last 275 System log entries:
|
|
--------
|
|
2026-01-02T03:04:05+00:00 host kernel: fatal disk error
|
|
Last 275 SMARTD log entries:
|
|
--------
|
|
Jan 2 03:04:05 host smartd: warning temperature
|
|
Last 275 Daemon log entries:
|
|
--------
|
|
plain informational line
|
|
`)}})
|
|
if err != nil {
|
|
t.Fatalf("Parse: %v", err)
|
|
}
|
|
if result.Hardware.BoardInfo.ProductName != "NAS-1" || len(result.Hardware.Firmware) != 2 {
|
|
t.Fatalf("unexpected board or firmware: %+v / %+v", result.Hardware.BoardInfo, result.Hardware.Firmware)
|
|
}
|
|
if len(result.Hardware.CPUs) != 1 || result.Hardware.CPUs[0].Cores != 4 {
|
|
t.Fatalf("unexpected CPUs: %+v", result.Hardware.CPUs)
|
|
}
|
|
if len(result.Hardware.Memory) != 1 || result.Hardware.Memory[0].SizeMB != 8192 {
|
|
t.Fatalf("unexpected memory: %+v", result.Hardware.Memory)
|
|
}
|
|
if len(result.Hardware.Storage) != 1 || result.Hardware.Storage[0].SizeGB != 1 || result.Hardware.Storage[0].SerialNumber != "DISK-1" {
|
|
t.Fatalf("unexpected storage: %+v", result.Hardware.Storage)
|
|
}
|
|
if len(result.Sensors) != 1 || result.Sensors[0].Status != "warning" {
|
|
t.Fatalf("unexpected sensors: %+v", result.Sensors)
|
|
}
|
|
if len(result.Events) != 6 {
|
|
t.Fatalf("expected uptime, ZFS, SMART, and three log events, got %+v", result.Events)
|
|
}
|
|
}
|
|
|
|
func TestXigmaNASHelpers(t *testing.T) {
|
|
if model, fw := splitModelAndFirmware("disk model 1.00"); model != "disk model" || fw != "1.00" {
|
|
t.Fatalf("splitModelAndFirmware = %q, %q", model, fw)
|
|
}
|
|
if got := guessStorageType("cd0"); got != "optical" {
|
|
t.Fatalf("optical type = %q", got)
|
|
}
|
|
if got := guessStorageType("ada0"); got != "hdd" {
|
|
t.Fatalf("HDD type = %q", got)
|
|
}
|
|
if got := parseCapacityBytes("invalid"); got != 0 {
|
|
t.Fatalf("invalid capacity = %d", got)
|
|
}
|
|
if got := classifyEventSeverity("all good"); got != "info" {
|
|
t.Fatalf("info severity = %q", got)
|
|
}
|
|
if got := classifyEventSeverity("kernel panic"); got != "critical" {
|
|
t.Fatalf("critical severity = %q", got)
|
|
}
|
|
if got := extractSyslogMessage("a: message"); got != "message" {
|
|
t.Fatalf("syslog message = %q", got)
|
|
}
|
|
}
|
|
|
|
func TestParserParseExample(t *testing.T) {
|
|
p := &Parser{}
|
|
|
|
examplePath := filepath.Join("..", "..", "..", "..", "example", "xigmanas.txt")
|
|
raw, err := os.ReadFile(examplePath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
t.Skipf("example file %s not present", examplePath)
|
|
}
|
|
t.Fatalf("read example file: %v", err)
|
|
}
|
|
|
|
files := []parser.ExtractedFile{
|
|
{Path: "xigmanas", Content: raw},
|
|
}
|
|
|
|
result, err := p.Parse(files)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if result == nil || result.Hardware == nil {
|
|
t.Fatal("expected non-nil result with hardware")
|
|
}
|
|
|
|
if len(result.Hardware.Firmware) == 0 {
|
|
t.Fatal("expected firmware data")
|
|
}
|
|
foundXigmaVersion := false
|
|
for _, fw := range result.Hardware.Firmware {
|
|
if fw.DeviceName == "XigmaNAS" && fw.Version == "14.3.0.5" {
|
|
foundXigmaVersion = true
|
|
}
|
|
}
|
|
if !foundXigmaVersion {
|
|
t.Fatalf("expected XigmaNAS firmware version 14.3.0.5, got %+v", result.Hardware.Firmware)
|
|
}
|
|
|
|
if result.Hardware.BoardInfo.Manufacturer != "HP" {
|
|
t.Fatalf("expected board manufacturer HP, got %q", result.Hardware.BoardInfo.Manufacturer)
|
|
}
|
|
if len(result.Hardware.CPUs) == 0 {
|
|
t.Fatal("expected at least one CPU")
|
|
}
|
|
if !strings.Contains(strings.ToLower(result.Hardware.CPUs[0].Model), "athlon") {
|
|
t.Fatalf("expected CPU model to contain athlon, got %q", result.Hardware.CPUs[0].Model)
|
|
}
|
|
|
|
if len(result.Hardware.Storage) < 4 {
|
|
t.Fatalf("expected at least 4 storage devices, got %d", len(result.Hardware.Storage))
|
|
}
|
|
if len(result.Sensors) == 0 {
|
|
t.Fatal("expected SMART temperature sensors")
|
|
}
|
|
if len(result.Events) == 0 {
|
|
t.Fatal("expected events from uptime/zfs sections")
|
|
}
|
|
|
|
var hasSystemLog, hasSmartdLog, hasDaemonLog, hasLoginFailure bool
|
|
for _, ev := range result.Events {
|
|
if ev.EventType == "System Log" {
|
|
hasSystemLog = true
|
|
}
|
|
if ev.EventType == "SMARTD Log" {
|
|
hasSmartdLog = true
|
|
}
|
|
if ev.EventType == "Daemon Log" {
|
|
hasDaemonLog = true
|
|
}
|
|
if strings.Contains(strings.ToLower(ev.Description), "login failure") {
|
|
hasLoginFailure = true
|
|
}
|
|
}
|
|
if !hasSystemLog || !hasSmartdLog || !hasDaemonLog {
|
|
t.Fatalf("expected events from System/SMARTD/Daemon sections, got system=%v smartd=%v daemon=%v", hasSystemLog, hasSmartdLog, hasDaemonLog)
|
|
}
|
|
if !hasLoginFailure {
|
|
t.Fatal("expected to parse login failure event from system log section")
|
|
}
|
|
}
|