Составить план модернизации интерфей
This commit is contained in:
253
CLAUDE.md
253
CLAUDE.md
@@ -1,199 +1,124 @@
|
||||
# LOGPile - Инструкции для Claude Code
|
||||
|
||||
## Описание проекта
|
||||
## Что это за проект
|
||||
|
||||
Приложение для анализа диагностической информации с BMC серверов (IPMI).
|
||||
Представляет собой standalone Go-бинарник со встроенным веб-интерфейсом.
|
||||
LOGPile - standalone Go-приложение для анализа BMC/IPMI диагностических архивов с веб-интерфейсом.
|
||||
Приложение запускает локальный HTTP-сервер, парсит архив, автоматически выбирает подходящий parser по vendor и показывает результат в UI + экспортирует данные.
|
||||
|
||||
### Функциональность
|
||||
## Актуальная архитектура
|
||||
|
||||
**Входные данные:**
|
||||
- Архив (tar.gz/zip) с диагностическими данными IPMI сервера
|
||||
- Язык: Go 1.22+
|
||||
- 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/
|
||||
├── cmd/logpile/main.go # Точка входа
|
||||
├── cmd/logpile/main.go
|
||||
├── internal/
|
||||
│ ├── parser/ # Парсинг архивов и IPMI данных
|
||||
│ ├── models/ # Модели данных
|
||||
│ ├── analyzer/ # Логика анализа
|
||||
│ ├── exporter/ # Экспорт данных
|
||||
│ └── server/ # HTTP сервер и handlers
|
||||
├── web/ # Embedded веб-интерфейс
|
||||
│ ├── static/ # CSS, JS, изображения
|
||||
│ └── templates/ # HTML шаблоны
|
||||
├── testdata/ # Примеры архивов для тестов
|
||||
├── go.mod
|
||||
│ ├── analyzer/
|
||||
│ ├── exporter/
|
||||
│ ├── models/
|
||||
│ ├── parser/
|
||||
│ │ └── vendors/
|
||||
│ │ ├── generic/
|
||||
│ │ ├── inspur/
|
||||
│ │ ├── nvidia/
|
||||
│ │ ├── nvidia_bug_report/
|
||||
│ │ └── supermicro/
|
||||
│ └── server/
|
||||
├── web/
|
||||
│ ├── static/
|
||||
│ └── templates/
|
||||
├── Makefile
|
||||
└── README.md
|
||||
└── go.mod
|
||||
```
|
||||
|
||||
## Технический стек
|
||||
|
||||
### 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)
|
||||
|
||||
## Примеры использования
|
||||
## CLI и запуск (актуально)
|
||||
|
||||
```bash
|
||||
# Сборка
|
||||
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)
|
||||
```
|
||||
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
|
||||
```
|
||||
## Основной runtime-flow
|
||||
|
||||
### FRU (Field Replaceable Unit)
|
||||
```
|
||||
FRU Device Description : Builtin FRU Device (ID 0)
|
||||
Board Mfg Date : Mon Jan 1 00:00:00 1996
|
||||
Board Mfg : Supermicro
|
||||
Board Product : X11DPH-T
|
||||
Board Serial : WM194S001234
|
||||
Board Part Number : X11DPH-TQ
|
||||
```
|
||||
1. `main.go` регистрирует embedded web FS и запускает сервер.
|
||||
2. `POST /api/upload` принимает архив и передаёт его в `parser.BMCParser`.
|
||||
3. `DetectFormat()` выбирает parser с максимальным confidence.
|
||||
4. Результат сохраняется в памяти (`Server.result`) и отдаётся через API.
|
||||
5. UI строит вкладки: конфигурация, прошивки, сенсоры, серийники, события.
|
||||
|
||||
## 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 # Загрузить архив
|
||||
GET /api/status # Получить статус парсинга
|
||||
GET /api/parsers # Получить список доступных парсеров
|
||||
GET /api/events # Получить список событий
|
||||
GET /api/sensors # Получить показания сенсоров
|
||||
GET /api/config # Получить конфигурацию
|
||||
GET /api/serials # Получить серийные номера
|
||||
GET /api/firmware # Получить версии прошивок
|
||||
GET /api/export/csv # Экспорт в CSV
|
||||
GET /api/export/json # Экспорт в JSON
|
||||
GET /api/export/txt # Экспорт текстового отчета
|
||||
DELETE /api/clear # Очистить загруженные данные
|
||||
POST /api/shutdown # Завершить работу приложения
|
||||
POST /api/upload
|
||||
GET /api/status
|
||||
GET /api/parsers
|
||||
GET /api/events
|
||||
GET /api/sensors
|
||||
GET /api/config
|
||||
GET /api/serials
|
||||
GET /api/firmware
|
||||
GET /api/export/csv
|
||||
GET /api/export/json
|
||||
GET /api/export/txt
|
||||
DELETE /api/clear
|
||||
POST /api/shutdown
|
||||
```
|
||||
|
||||
## Поддерживаемые производители
|
||||
## Форматы данных и экспорт
|
||||
|
||||
- Supermicro
|
||||
- Inspur/Kaytus
|
||||
- Nvidia
|
||||
- Generic (fallback)
|
||||
- `AnalysisResult` агрегирует: events, sensors, FRU, hardware.
|
||||
- Экспорт реализован в `internal/exporter/exporter.go`:
|
||||
- CSV: серийные номера компонентов
|
||||
- JSON: полный `AnalysisResult`
|
||||
- TXT: человекочитаемый отчёт
|
||||
|
||||
## Следующие шаги
|
||||
## Важные текущие ограничения (чтобы не ошибаться в задачах)
|
||||
|
||||
1. Добавление поддержки других производителей BMC (Dell iDRAC, HP iLO, Lenovo XCC)
|
||||
2. Расширение функционала экспорта данных
|
||||
3. Добавление фильтрации и сортировки данных в UI
|
||||
4. Улучшение производительности парсинга больших архивов
|
||||
5. Добавление поддержки других форматов IPMI данных
|
||||
- Upload через `/api/upload` использует `ParseFromReader()`, где сейчас поддержаны `.tar`, `.tar.gz`, `.tgz`.
|
||||
- Код распаковки `.zip` есть, но в текущем upload-пути `zip` не обрабатывается.
|
||||
- Флаг `--file` присутствует в CLI-конфиге, но preload в `Server.Run()` сейчас не выполняется.
|
||||
- Данные хранятся только в памяти процесса; перезапуск очищает состояние.
|
||||
|
||||
## Примечания
|
||||
## Практические рекомендации для доработок
|
||||
|
||||
- Все файлы веб-интерфейса должны быть embedded в бинарник через `//go:embed`
|
||||
- Приоритет на простоту и минимум зависимостей
|
||||
- Безопасность: валидация загружаемых архивов (размер, типы файлов)
|
||||
- UI должен быть простым и функциональным, не перегруженным
|
||||
- Поддержка русского языка в интерфейсе
|
||||
- Если меняется parser-логика, обновляй `Version()` соответствующего parser-модуля.
|
||||
- Новые vendor-парсеры регистрируй через import в `internal/parser/vendors/vendors.go`.
|
||||
- Для API/контрактов проверяй согласованность `handlers.go` и `web/static/js/app.js`.
|
||||
- Для UI-изменений не забывай, что ассеты embedded через `web/embed.go`.
|
||||
|
||||
## Приоритетные следующие шаги
|
||||
|
||||
1. Довести поддержку `zip` в upload path (`ParseFromReader`).
|
||||
2. Реализовать preload из `--file`.
|
||||
3. Добавить/актуализировать автотесты для parser и HTTP handlers.
|
||||
4. Расширить vendor coverage (Dell/HPE/Lenovo) по реальным дампам.
|
||||
|
||||
32
quick_test.go
Normal file
32
quick_test.go
Normal 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
BIN
test_nvidia_full
Executable file
Binary file not shown.
96
test_nvidia_full.go
Normal file
96
test_nvidia_full.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user