Files
core/docs/plan/Plan #1.md

115 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Реализация JSON hardware-ingest по INTEGRATION_GUIDE.md
## Summary
Добавляем новый инструмент импорта hardware snapshot: `POST /ingest/hardware` + форму в ingest UI, без ломки текущего `POST /ingest/logbundle`.
Импорт должен:
1. находить/создавать `asset` по `hardware.board.serial_number`,
2. автоматически назначать новые asset в служебный customer/project (`Без заказчика` / `Без проекта`),
3. создавать/обновлять компоненты + LOT,
4. писать `observations` с `details JSON`,
5. синхронизировать `installations`,
6. генерировать timeline events (`LOG_COLLECTED`, `INSTALLED`, `REMOVED`, `FIRMWARE_CHANGED`, `COMPONENT_WARNING`, `COMPONENT_FAILED`),
7. создавать `failure_events` при `Critical`.
## Подтвержденные решения
1. Scope: `API + UI`.
2. Endpoint: основной `POST /ingest/hardware`.
3. Новые asset по умолчанию идут в служебный customer/project:
- customer: `Без заказчика`
- project: `Без проекта`
4. Статусы:
- `Critical` -> `failure_event` + `COMPONENT_FAILED`
- `Warning` -> `COMPONENT_WARNING`
5. Расширенные observation-поля храним в `observations.details JSON`.
6. Ответ endpoint: как в guide (`status`, `bundle_id`, `asset_id`, `collected_at`, `duplicate`, `summary|message`).
## Публичные API / контракты
1. Новый маршрут в `internal/api/ingest.go`:
- `POST /ingest/hardware`
2. Request JSON:
- `collected_at` (required, RFC3339)
- `target_host` (required)
- `hardware` (required) с `board.serial_number` (required)
- опционально: `filename`, `source_type`, `protocol`, секции `cpus/memory/storage/pcie_devices/power_supplies/firmware`
3. Response success (201):
- `status: "success"`
- `bundle_id`, `asset_id`, `collected_at`, `duplicate: false`
- `summary` с детализированными счетчиками
4. Response duplicate (200):
- те же поля + `duplicate: true`, `message`
5. Validation error (400):
- `status: "error"`, `error: "validation_failed"`, `details.field`, `details.message`
## Изменения по файлам (implementation design)
1. `migrations/0007_hardware_ingest/up.sql` и `down.sql`
- `ALTER TABLE observations ADD COLUMN details JSON NULL;`
2. `internal/api/ingest.go`
- новый handler `handleHardware`
- строгая decode/validate
- вызов `IngestHardware(...)`
3. `internal/ingest/service.go`
- `HardwareInput`, `HardwareResult`, summary counters
- transactional pipeline
- `ensureServiceProject()`:
- customer `Без заказчика` (create if missing)
- project `Без проекта` (create if missing)
- `ensureAssetByBoardSerial()`:
- lookup по `assets.vendor_serial = board.serial_number`
- create если нет (`name=target_host`, `project_id=service_project_id`, `vendor/model` по board)
- update vendor/model при валидных значениях
4. `internal/ingest/parser_hardware.go`
- parse + normalize + flatten snapshot
- генерация serial:
- CPU: `{board_serial}-CPU-{socket}`
- PCIe: `{board_serial}-PCIE-{slot}` если пусто/N/A
- фильтрация `present=false`, `status=Empty`
5. `internal/ingest/lot_classifier.go`
- LOT rules для CPU/DIMM/Storage/PSU/PCIe
- `ensureLotByCode()`
6. `internal/ingest/service.go` (status/failure/events)
- `observations.details` хранит: `component_type`, `slot`, `status`, `present`, `telemetry`, raw attrs
- `Critical`:
- `COMPONENT_FAILED` в timeline
- `failure_events` (`source=hardware_ingest`, deterministic `external_id`)
- `Warning`:
- `COMPONENT_WARNING` в timeline
- system firmware (`hardware.firmware`):
- сравнение с последним значением и `FIRMWARE_CHANGED` для asset
7. UI:
- `internal/api/ui_ingest.tmpl`
- `internal/api/ui.go`
- добавить секцию “Hardware Ingest” с примером payload на основе `docs/import-example-full.json`
## Идемпотентность
1. Хеш считается от канонически marshaled hardware request.
2. Если hash уже есть:
- без side effects
- `200 duplicate=true`
3. Если hash новый:
- полный pipeline
- `201`
## Тесты и acceptance
1. `TestIngestHardwareIdempotent`: 201 -> 200 duplicate, без дублей в `log_bundles/observations/installations`.
2. Тест авто-создания служебного customer/project и auto-create asset.
3. Тест замены компонента: `REMOVED` + `INSTALLED`.
4. Тест firmware change: `FIRMWARE_CHANGED`.
5. Тест статусов:
- Warning -> `COMPONENT_WARNING`
- Critical -> `COMPONENT_FAILED` + `failure_events`
6. Validation tests:
- missing required fields
- invalid RFC3339
- unknown field -> 400
7. UI smoke: `/ui/ingest` содержит форму hardware ingest.
## Assumptions and defaults
1. Служебные записи:
- customer.name = `Без заказчика`
- project.name = `Без проекта`
2. При дублях по имени используется запись с минимальным `id`.
3. `manufacturer/product_name == "NULL"` -> `NULL` в БД.
4. Новые `event_type` (`COMPONENT_WARNING`, `COMPONENT_FAILED`) хранятся как строки без enum-миграции.
5. `/ingest/logbundle` остается полностью совместимым.