Составить план модернизации интерфей

This commit is contained in:
Mikhail Chusavitin
2026-02-04 09:20:30 +03:00
parent 241e4e3605
commit 8e99c36888
5 changed files with 217 additions and 164 deletions

253
CLAUDE.md
View File

@@ -1,199 +1,124 @@
# LOGPile - Инструкции для Claude Code # LOGPile - Инструкции для Claude Code
## Описание проекта ## Что это за проект
Приложение для анализа диагностической информации с BMC серверов (IPMI). LOGPile - standalone Go-приложение для анализа BMC/IPMI диагностических архивов с веб-интерфейсом.
Представляет собой standalone Go-бинарник со встроенным веб-интерфейсом. Приложение запускает локальный HTTP-сервер, парсит архив, автоматически выбирает подходящий parser по vendor и показывает результат в UI + экспортирует данные.
### Функциональность ## Актуальная архитектура
**Входные данные:** - Язык: Go 1.22+
- Архив (tar.gz/zip) с диагностическими данными IPMI сервера - HTTP: стандартный `net/http` + `http.ServeMux`
- UI: embedded (`//go:embed`) HTML/CSS/Vanilla JS
- Бинарник: один executable, без внешних зависимостей на runtime
- Порт по умолчанию: `8082` (а не 8080)
**Обработка:** ## Реальная структура репозитория
- Парсинг System Event Log (SEL) - журнал событий IPMI
- Парсинг FRU (Field Replaceable Unit) - серийные номера компонентов
- Парсинг конфигурации сервера (CPU, RAM, диски, и т.д.)
**Выходные данные:**
- Веб-интерфейс с человекочитаемой информацией
- Экспорт логов в TXT/JSON
- Экспорт конфигурации в JSON
- Экспорт серийных номеров в CSV
## Архитектура
- **Тип:** Standalone бинарник с embedded веб-сервером
- **Язык:** Go
- **UI:** Embedded HTML + CSS + Vanilla JS
- **Порт:** localhost:8080 (по умолчанию)
## Структура проекта
``` ```
logpile/ logpile/
├── cmd/logpile/main.go # Точка входа ├── cmd/logpile/main.go
├── internal/ ├── internal/
│ ├── parser/ # Парсинг архивов и IPMI данных │ ├── analyzer/
│ ├── models/ # Модели данных │ ├── exporter/
│ ├── analyzer/ # Логика анализа │ ├── models/
│ ├── exporter/ # Экспорт данных │ ├── parser/
│ └── server/ # HTTP сервер и handlers │ └── vendors/
├── web/ # Embedded веб-интерфейс │ │ ├── generic/
├── static/ # CSS, JS, изображения │ ├── inspur/
└── templates/ # HTML шаблоны │ ├── nvidia/
├── testdata/ # Примеры архивов для тестов ├── nvidia_bug_report/
├── go.mod │ │ └── supermicro/
│ └── server/
├── web/
│ ├── static/
│ └── templates/
├── Makefile ├── Makefile
└── README.md └── go.mod
``` ```
## Технический стек ## CLI и запуск (актуально)
### Backend
- Go 1.22+
- Стандартная библиотека (net/http, archive/tar, compress/gzip)
- embed для встраивания веб-ресурсов
### Frontend
- Vanilla JavaScript
- CSS (встроенный в бинарник)
- Без сборщиков - всё embedded в бинарник
### Парсинг IPMI
- SEL формат: текстовый вывод `ipmitool sel list` или бинарный
- FRU формат: вывод `ipmitool fru print`
- Конфигурация: различные текстовые файлы из архива
## Реализованные функции
### 1. Базовая структура
- [x] Создана структура директорий
- [x] go.mod инициализирован
- [x] Makefile создан
### 2. Парсер архивов
- [x] Распаковка tar.gz
- [x] Распаковка zip
- [x] Определение типов файлов внутри архива
### 3. Парсеры IPMI данных
- [x] SEL parser (System Event Log)
- [x] FRU parser (серийные номера)
- [x] Config parser (конфигурация сервера)
- [x] Поддержка нескольких производителей (Supermicro, Inspur, Nvidia, etc.)
### 4. Модели данных
- [x] Event (события из SEL)
- [x] Hardware (конфигурация)
- [x] SerialNumber (серийники компонентов)
### 5. Веб-сервер
- [x] HTTP сервер с embedded файлами
- [x] Upload handler для архивов
- [x] API endpoints для получения данных
- [x] Handlers для экспорта
### 6. Веб-интерфейс
- [x] Главная страница с upload формой
- [x] Отображение событий (timeline/таблица)
- [x] Отображение конфигурации
- [x] Таблица серийных номеров
- [x] Кнопки экспорта
### 7. Экспортеры
- [x] CSV экспорт (серийники)
- [x] JSON экспорт (конфиг, события)
- [x] TXT отчет (логи)
### 8. Тестирование и сборка
- [x] Unit тесты для парсеров
- [x] Интеграционные тесты
- [x] Cross-platform сборка (Linux, Windows, Mac)
## Примеры использования
```bash ```bash
# Сборка # Сборка
make build make build
# Запуск веб-сервера # Запуск (авто-открытие браузера включено)
./bin/logpile serve ./bin/logpile
# Открыть в браузере # Явный порт
open http://localhost:8080 ./bin/logpile --port 8082
# С указанием порта # Не открывать браузер автоматически
./bin/logpile serve --port 9000 ./bin/logpile --no-browser
# С предзагрузкой файла # Версия
./bin/logpile serve --file /path/to/bmc-archive.tar.gz ./bin/logpile --version
``` ```
## Формат данных IPMI Важно: сейчас **нет** subcommand `serve`, запуск идёт напрямую через флаги.
### SEL (System Event Log) ## Основной runtime-flow
```
SEL Record ID : 0001
Record Type : 02
Timestamp : 01/15/2025 14:23:45
Generator ID : 0020
EvM Revision : 04
Sensor Type : Temperature
Sensor Number : 01
Event Type : Threshold
Event Direction : Assertion Event
Event Data : 010000
Description : Upper Critical - going high
```
### FRU (Field Replaceable Unit) 1. `main.go` регистрирует embedded web FS и запускает сервер.
``` 2. `POST /api/upload` принимает архив и передаёт его в `parser.BMCParser`.
FRU Device Description : Builtin FRU Device (ID 0) 3. `DetectFormat()` выбирает parser с максимальным confidence.
Board Mfg Date : Mon Jan 1 00:00:00 1996 4. Результат сохраняется в памяти (`Server.result`) и отдаётся через API.
Board Mfg : Supermicro 5. UI строит вкладки: конфигурация, прошивки, сенсоры, серийники, события.
Board Product : X11DPH-T
Board Serial : WM194S001234
Board Part Number : X11DPH-TQ
```
## API Endpoints ## Поддерживаемые parser modules
- `supermicro` - Supermicro parser
- `inspur` - Inspur/Kaytus parser
- `nvidia` - NVIDIA Field Diagnostics parser
- `nvidia_bug_report` - parser для `nvidia-bug-report.sh`
- `generic` - fallback parser
Реестр parser-ов: `internal/parser/registry.go`, подключение модулей: `internal/parser/vendors/vendors.go`.
## API (фактически в коде)
``` ```
POST /api/upload # Загрузить архив POST /api/upload
GET /api/status # Получить статус парсинга GET /api/status
GET /api/parsers # Получить список доступных парсеров GET /api/parsers
GET /api/events # Получить список событий GET /api/events
GET /api/sensors # Получить показания сенсоров GET /api/sensors
GET /api/config # Получить конфигурацию GET /api/config
GET /api/serials # Получить серийные номера GET /api/serials
GET /api/firmware # Получить версии прошивок GET /api/firmware
GET /api/export/csv # Экспорт в CSV GET /api/export/csv
GET /api/export/json # Экспорт в JSON GET /api/export/json
GET /api/export/txt # Экспорт текстового отчета GET /api/export/txt
DELETE /api/clear # Очистить загруженные данные DELETE /api/clear
POST /api/shutdown # Завершить работу приложения POST /api/shutdown
``` ```
## Поддерживаемые производители ## Форматы данных и экспорт
- Supermicro - `AnalysisResult` агрегирует: events, sensors, FRU, hardware.
- Inspur/Kaytus - Экспорт реализован в `internal/exporter/exporter.go`:
- Nvidia - CSV: серийные номера компонентов
- Generic (fallback) - JSON: полный `AnalysisResult`
- TXT: человекочитаемый отчёт
## Следующие шаги ## Важные текущие ограничения (чтобы не ошибаться в задачах)
1. Добавление поддержки других производителей BMC (Dell iDRAC, HP iLO, Lenovo XCC) - Upload через `/api/upload` использует `ParseFromReader()`, где сейчас поддержаны `.tar`, `.tar.gz`, `.tgz`.
2. Расширение функционала экспорта данных - Код распаковки `.zip` есть, но в текущем upload-пути `zip` не обрабатывается.
3. Добавление фильтрации и сортировки данных в UI - Флаг `--file` присутствует в CLI-конфиге, но preload в `Server.Run()` сейчас не выполняется.
4. Улучшение производительности парсинга больших архивов - Данные хранятся только в памяти процесса; перезапуск очищает состояние.
5. Добавление поддержки других форматов IPMI данных
## Примечания ## Практические рекомендации для доработок
- Все файлы веб-интерфейса должны быть embedded в бинарник через `//go:embed` - Если меняется parser-логика, обновляй `Version()` соответствующего parser-модуля.
- Приоритет на простоту и минимум зависимостей - Новые vendor-парсеры регистрируй через import в `internal/parser/vendors/vendors.go`.
- Безопасность: валидация загружаемых архивов (размер, типы файлов) - Для API/контрактов проверяй согласованность `handlers.go` и `web/static/js/app.js`.
- UI должен быть простым и функциональным, не перегруженным - Для UI-изменений не забывай, что ассеты embedded через `web/embed.go`.
- Поддержка русского языка в интерфейсе
## Приоритетные следующие шаги
1. Довести поддержку `zip` в upload path (`ParseFromReader`).
2. Реализовать preload из `--file`.
3. Добавить/актуализировать автотесты для parser и HTTP handlers.
4. Расширить vendor coverage (Dell/HPE/Lenovo) по реальным дампам.

BIN
logpile

Binary file not shown.

32
quick_test.go Normal file
View File

@@ -0,0 +1,32 @@
package main
import (
"fmt"
"log"
"git.mchus.pro/mchus/logpile/internal/parser"
_ "git.mchus.pro/mchus/logpile/internal/parser/vendors"
)
func main() {
p := parser.NewBMCParser()
fmt.Println("Testing archive parsing...")
if err := p.ParseArchive("example/A514359X5A07900_logs-20260122-074208.tar"); err != nil {
log.Fatalf("ERROR: %v", err)
}
fmt.Println("✓ Archive parsed successfully!")
fmt.Printf("✓ Detected vendor: %s\n", p.DetectedVendor())
result := p.Result()
fmt.Printf("✓ GPUs found: %d\n", len(result.Hardware.GPUs))
fmt.Printf("✓ Events found: %d\n", len(result.Events))
fmt.Printf("✓ PCIe Devices found: %d\n", len(result.Hardware.PCIeDevices))
fmt.Println("\nBoard Info:")
fmt.Printf(" Manufacturer: %s\n", result.Hardware.BoardInfo.Manufacturer)
fmt.Printf(" Product Name: %s\n", result.Hardware.BoardInfo.ProductName)
fmt.Printf(" Serial Number: %s\n", result.Hardware.BoardInfo.SerialNumber)
fmt.Printf(" Part Number: %s\n", result.Hardware.BoardInfo.PartNumber)
}

BIN
test_nvidia_full Executable file

Binary file not shown.

96
test_nvidia_full.go Normal file
View File

@@ -0,0 +1,96 @@
package main
import (
"fmt"
"log"
"git.mchus.pro/mchus/logpile/internal/parser"
_ "git.mchus.pro/mchus/logpile/internal/parser/vendors"
)
func main() {
p := parser.NewBMCParser()
fmt.Println("Testing NVIDIA Bug Report parser (full)...")
if err := p.ParseArchive("/Users/mchusavitin/Downloads/nvidia-bug-report-2KD501412.log.gz"); err != nil {
log.Fatalf("ERROR: %v", err)
}
fmt.Println("✓ Archive parsed successfully!")
fmt.Printf("✓ Detected vendor: %s\n", p.DetectedVendor())
result := p.Result()
fmt.Printf("✓ CPUs: %d\n", len(result.Hardware.CPUs))
fmt.Printf("✓ Memory: %d modules\n", len(result.Hardware.Memory))
fmt.Printf("✓ Power Supplies: %d\n", len(result.Hardware.PowerSupply))
fmt.Printf("✓ GPUs: %d\n", len(result.Hardware.GPUs))
fmt.Printf("✓ Network Adapters: %d\n", len(result.Hardware.NetworkAdapters))
fmt.Println("\nSystem Information:")
if result.Hardware.BoardInfo.SerialNumber != "" {
fmt.Printf(" Serial Number: %s\n", result.Hardware.BoardInfo.SerialNumber)
}
if result.Hardware.BoardInfo.UUID != "" {
fmt.Printf(" UUID: %s\n", result.Hardware.BoardInfo.UUID)
}
if result.Hardware.BoardInfo.Manufacturer != "" {
fmt.Printf(" Manufacturer: %s\n", result.Hardware.BoardInfo.Manufacturer)
}
if result.Hardware.BoardInfo.ProductName != "" {
fmt.Printf(" Product: %s\n", result.Hardware.BoardInfo.ProductName)
}
if result.Hardware.BoardInfo.Version != "" {
fmt.Printf(" Version: %s\n", result.Hardware.BoardInfo.Version)
}
fmt.Println("\nCPU Information:")
for _, cpu := range result.Hardware.CPUs {
fmt.Printf(" Socket %d: %s\n", cpu.Socket, cpu.Model)
fmt.Printf(" S/N: %s, Cores: %d, Threads: %d\n", cpu.SerialNumber, cpu.Cores, cpu.Threads)
}
fmt.Println("\nPower Supplies:")
for _, psu := range result.Hardware.PowerSupply {
fmt.Printf(" %s: %s (%s)\n", psu.Slot, psu.Model, psu.Vendor)
fmt.Printf(" S/N: %s\n", psu.SerialNumber)
fmt.Printf(" Power: %d W, Revision: %s\n", psu.WattageW, psu.Firmware)
fmt.Printf(" Status: %s\n", psu.Status)
}
totalMemGB := 0
for _, mem := range result.Hardware.Memory {
totalMemGB += mem.SizeMB / 1024
}
fmt.Printf("\nMemory: %d modules, %d GB total\n", len(result.Hardware.Memory), totalMemGB)
fmt.Printf("\nNetwork Adapters: %d devices\n", len(result.Hardware.NetworkAdapters))
for _, nic := range result.Hardware.NetworkAdapters {
fmt.Printf(" %s: %s\n", nic.Location, nic.Model)
if nic.Slot != "" {
fmt.Printf(" Slot: %s\n", nic.Slot)
}
if nic.PartNumber != "" {
fmt.Printf(" P/N: %s\n", nic.PartNumber)
}
if nic.SerialNumber != "" {
fmt.Printf(" S/N: %s\n", nic.SerialNumber)
}
if nic.PortCount > 0 {
fmt.Printf(" Ports: %d x %s\n", nic.PortCount, nic.PortType)
}
}
fmt.Printf("\nGPUs: %d devices\n", len(result.Hardware.GPUs))
for _, gpu := range result.Hardware.GPUs {
fmt.Printf(" %s: %s\n", gpu.BDF, gpu.Model)
if gpu.UUID != "" {
fmt.Printf(" UUID: %s\n", gpu.UUID)
}
if gpu.VideoBIOS != "" {
fmt.Printf(" Video BIOS: %s\n", gpu.VideoBIOS)
}
if gpu.IRQ > 0 {
fmt.Printf(" IRQ: %d\n", gpu.IRQ)
}
}
}