Add integration guide, example generator, and built binary

This commit is contained in:
2026-02-15 20:08:46 +03:00
parent 8715fcace4
commit 0af3cee9b6
3 changed files with 1156 additions and 0 deletions

992
docs/INTEGRATION_GUIDE.md Normal file
View File

@@ -0,0 +1,992 @@
# Руководство по интеграции Reanimator
## Импорт серверов через JSON (Redfish/API)
Данное руководство описывает формат JSON для импорта аппаратной информации о серверах, собранной через Redfish API или другие источники мониторинга.
---
## Принципы импорта
1. **Snapshot данных** - JSON содержит состояние сервера на момент сбора, без исторической информации
2. **Автоматическое определение LOT** - классификация компонентов определяется приложением на основе vendor/model/type
3. **Статус компонентов** - каждый компонент имеет статус работоспособности (OK, Warning, Critical, Unknown)
4. **Идемпотентность** - повторный импорт с тем же snapshot не создает дубликаты
5. **Event-driven обновления** - импорт создает события в timeline (LOG_COLLECTED, INSTALLED, REMOVED, FIRMWARE_CHANGED)
---
## Формат JSON для импорта
> Важно: endpoint использует строгий JSON-декодер (`DisallowUnknownFields`).
> Любое неизвестное поле (включая вложенные объекты) приведет к `400 Bad Request`.
> Используйте только `snake_case` ключи из этого руководства.
### Структура верхнего уровня
```json
{
"filename": "redfish://10.10.10.103",
"source_type": "api",
"protocol": "redfish",
"target_host": "10.10.10.103",
"collected_at": "2026-02-10T15:30:00Z",
"hardware": {
"board": {...},
"cpus": [...],
"memory": [...],
"storage": [...],
"pcie_devices": [...],
"power_supplies": [...],
"firmware": [...]
}
}
```
### Обязательные поля верхнего уровня
- `collected_at` (string RFC3339, обязательно) - время сбора информации
- `target_host` (string, опционально) - IP или hostname сервера
- `hardware.board.serial_number` (string, обязательно) - серийный номер сервера/платы
- `source_type` (string, опционально) - тип источника: `api`, `logfile`, `manual`
- `protocol` (string, опционально) - протокол сбора: `redfish`, `ipmi`, `snmp`, `ssh`
- `filename` (string, опционально) - идентификатор источника данных
- `hardware` (object, обязательно) - структура с аппаратными компонентами
---
## Секция hardware
### 1. Board (Материнская плата / Server)
**Назначение:** Основная информация о сервере/материнской плате. Эта информация используется для создания/обновления Asset.
```json
{
"board": {
"manufacturer": "Supermicro",
"product_name": "X12DPG-QT6",
"serial_number": "21D634101",
"part_number": "X12DPG-QT6-REV1.01",
"uuid": "d7ef2fe5-2fd0-11f0-910a-346f11040868"
}
}
```
**Поля:**
- `serial_number` (string, обязательно) - серийный номер материнской платы/сервера (используется как `vendor_serial` для Asset)
- `manufacturer` (string, опционально) - производитель (используется как `vendor` для Asset)
- `product_name` (string, опционально) - модель (используется как `model` для Asset)
- `part_number` (string, опционально) - партномер
- `uuid` (string, опционально) - UUID системы
**Примечание:** Если `manufacturer` или `product_name` = "NULL", они интерпретируются как отсутствующие.
---
### 2. CPUs (Процессоры)
**Назначение:** Информация о установленных процессорах.
```json
{
"cpus": [
{
"socket": 0,
"model": "INTEL(R) XEON(R) GOLD 6530",
"cores": 32,
"threads": 64,
"frequency_mhz": 2100,
"max_frequency_mhz": 4000,
"manufacturer": "Intel",
"status": "OK"
},
{
"socket": 1,
"model": "INTEL(R) XEON(R) GOLD 6530",
"cores": 32,
"threads": 64,
"frequency_mhz": 2100,
"max_frequency_mhz": 4000,
"manufacturer": "Intel",
"status": "OK"
}
]
}
```
**Поля:**
- `socket` (int, обязательно) - номер сокета (используется для формирования уникального идентификатора)
- `model` (string, обязательно) - модель процессора
- `cores` (int, опционально) - количество ядер
- `threads` (int, опционально) - количество потоков
- `frequency_mhz` (int, опционально) - текущая частота в МГц
- `max_frequency_mhz` (int, опционально) - максимальная частота в МГц
- `manufacturer` (string, опционально) - производитель (Intel, AMD, etc.)
- `status` (string, опционально) - статус: `OK`, `Warning`, `Critical`, `Unknown`
**Генерация serial_number:**
- Формат: `{board_serial}-CPU-{socket}`
- Пример: `21D634101-CPU-0`, `21D634101-CPU-1`
**LOT автоопределение:**
- Формат: `CPU_{NORMALIZED_MODEL}`
- Пример: `CPU_XEON_GOLD_6530`, `CPU_EPYC_7763`
---
### 3. Memory (Модули памяти)
**Назначение:** Информация о модулях памяти (DIMM).
```json
{
"memory": [
{
"slot": "CPU0_C0D0",
"location": "CPU0_C0D0",
"present": true,
"size_mb": 32768,
"type": "DDR5",
"max_speed_mhz": 4800,
"current_speed_mhz": 4800,
"manufacturer": "Hynix",
"serial_number": "80AD032419E17CEEC1",
"part_number": "HMCG88AGBRA191N",
"status": "OK"
},
{
"slot": "CPU0_C1D0",
"location": "CPU0_C1D0",
"present": false,
"size_mb": 0,
"type": null,
"manufacturer": null,
"serial_number": null,
"part_number": null,
"status": "Empty"
}
]
}
```
**Поля:**
- `slot` (string, обязательно) - идентификатор слота
- `location` (string, опционально) - физическое расположение
- `present` (bool, обязательно) - наличие модуля в слоте
- `size_mb` (int, опционально) - размер в МБ
- `type` (string, опционально) - тип памяти: `DDR4`, `DDR5`, `DDR3`, etc.
- `max_speed_mhz` (int, опционально) - максимальная частота
- `current_speed_mhz` (int, опционально) - текущая частота
- `manufacturer` (string, опционально) - производитель
- `serial_number` (string, условно обязательно если present=true) - серийный номер
- `part_number` (string, опционально) - партномер
- `status` (string, опционально) - статус: `OK`, `Warning`, `Critical`, `Unknown`, `Empty`
**Обработка:**
- Если `present = false` или `status = "Empty"`, компонент не создается/не обновляется
- Если модуль был в предыдущем snapshot, но отсутствует в текущем - создается событие REMOVED
**LOT автоопределение:**
- Формат: `DIMM_{TYPE}_{SIZE_GB}GB`
- Пример: `DIMM_DDR5_32GB`, `DIMM_DDR4_64GB`
---
### 4. Storage (Диски)
**Назначение:** Информация о дисках (SSD, HDD, NVMe).
```json
{
"storage": [
{
"slot": "OB01",
"type": "NVMe",
"model": "INTEL SSDPF2KX076T1",
"size_gb": 7680,
"serial_number": "BTAX41900GF87P6DGN",
"manufacturer": "Intel",
"firmware": "9CV10510",
"interface": "NVMe",
"present": true,
"status": "OK"
},
{
"slot": "FP00HDD00",
"type": "HDD",
"model": "ST12000NM0008",
"size_gb": 12000,
"serial_number": "ZJV01234",
"manufacturer": "Seagate",
"firmware": "SN03",
"interface": "SATA",
"present": true,
"status": "OK"
}
]
}
```
**Поля:**
- `slot` (string, обязательно) - идентификатор слота
- `type` (string, опционально) - тип: `NVMe`, `SSD`, `HDD`
- `model` (string, обязательно) - модель диска
- `size_gb` (int, опционально) - размер в ГБ
- `serial_number` (string, обязательно) - серийный номер
- `manufacturer` (string, опционально) - производитель (может быть VID в hex формате типа "8086")
- `firmware` (string, опционально) - версия прошивки
- `interface` (string, опционально) - интерфейс: `NVMe`, `SATA`, `SAS`
- `present` (bool, обязательно) - наличие диска в слоте
- `status` (string, опционально) - статус: `OK`, `Warning`, `Critical`, `Unknown`
**Обработка firmware:**
- Если версия firmware изменилась относительно предыдущего observation - создается событие FIRMWARE_CHANGED
**LOT автоопределение:**
- Формат: `{TYPE}_{INTERFACE}_{SIZE_TB}TB` или `{TYPE}_{INTERFACE}_{SIZE_GB}GB`
- Пример: `SSD_NVME_07.68TB`, `HDD_SATA_12TB`
---
### 5. Power Supplies (Блоки питания)
**Назначение:** Информация о блоках питания.
```json
{
"power_supplies": [
{
"slot": "0",
"present": true,
"model": "GW-CRPS3000LW",
"vendor": "Great Wall",
"wattage_w": 3000,
"serial_number": "2P06C102610",
"part_number": "V0310C9000000000",
"firmware": "00.03.05",
"status": "OK",
"input_type": "ACWideRange",
"input_power_w": 137,
"output_power_w": 104,
"input_voltage": 215.25
}
]
}
```
**Поля:**
- `slot` (string, обязательно) - идентификатор слота
- `present` (bool, обязательно) - наличие PSU
- `model` (string, опционально) - модель
- `vendor` (string, опционально) - производитель
- `wattage_w` (int, опционально) - мощность в ваттах
- `serial_number` (string, условно обязательно если present=true) - серийный номер
- `part_number` (string, опционально) - партномер
- `firmware` (string, опционально) - версия прошивки
- `status` (string, опционально) - статус: `OK`, `Warning`, `Critical`, `Unknown`
- `input_type` (string, опционально) - тип входа
- `input_power_w` (int, опционально) - входная мощность (telemetry)
- `output_power_w` (int, опционально) - выходная мощность (telemetry)
- `input_voltage` (float, опционально) - входное напряжение (telemetry)
**Примечание:** Telemetry поля (input_power_w, output_power_w, input_voltage) сохраняются в observation, но не влияют на Component.
**LOT автоопределение:**
- Формат: `PSU_{WATTAGE}W`
- Пример: `PSU_3000W`, `PSU_1600W`
---
### 6. PCIe Devices (PCIe устройства)
**Назначение:** Информация о PCIe устройствах (NIC, RAID контроллеры, GPU, etc.).
```json
{
"pcie_devices": [
{
"slot": "PCIeCard1",
"vendor_id": 32902,
"device_id": 2912,
"bdf": "0000:18:00.0",
"device_class": "MassStorageController",
"manufacturer": "Intel",
"model": "RAID Controller RSP3DD080F",
"link_width": 8,
"link_speed": "Gen3",
"max_link_width": 8,
"max_link_speed": "Gen3",
"serial_number": "RAID-001-12345",
"firmware": "50.9.1-4296",
"status": "OK"
},
{
"slot": "PCIeCard2",
"vendor_id": 5555,
"device_id": 4401,
"bdf": "",
"device_class": "NetworkController",
"manufacturer": "Mellanox",
"model": "ConnectX-5",
"link_width": 16,
"link_speed": "Gen3",
"max_link_width": 16,
"max_link_speed": "Gen3",
"serial_number": "MT2892012345",
"status": "OK"
}
]
}
```
**Поля:**
- `slot` (string, обязательно) - идентификатор слота
- `vendor_id` (int, опционально) - PCI Vendor ID (hex в decimal)
- `device_id` (int, опционально) - PCI Device ID (hex в decimal)
- `bdf` (string, опционально) - Bus:Device.Function (например "0000:18:00.0")
- `device_class` (string, опционально) - класс устройства: `NetworkController`, `MassStorageController`, `DisplayController`, etc.
- `manufacturer` (string, опционально) - производитель
- `model` (string, опционально) - модель устройства
- `link_width` (int, опционально) - текущая ширина линка (x1, x4, x8, x16)
- `link_speed` (string, опционально) - текущая скорость линка (Gen3, Gen4, Gen5)
- `max_link_width` (int, опционально) - максимальная ширина линка
- `max_link_speed` (string, опционально) - максимальная скорость линка
- `serial_number` (string, опционально) - серийный номер (если доступен, иначе генерируется)
- `firmware` (string, опционально) - версия прошивки
- `status` (string, опционально) - статус: `OK`, `Warning`, `Critical`, `Unknown`
**Генерация serial_number (если отсутствует):**
- Формат: `{board_serial}-PCIE-{slot}`
- Пример: `21D634101-PCIE-PCIeCard1`
**LOT автоопределение:**
- Формат: `PCIE_{DEVICE_CLASS}_{NORMALIZED_MODEL}` или `PCIE_{DEVICE_CLASS}_{VENDOR_ID}_{DEVICE_ID}`
- Пример: `PCIE_NETWORK_CONNECTX5`, `PCIE_STORAGE_32902_2912`
---
### 7. Firmware (Прошивки системных компонентов)
**Назначение:** Информация о версиях прошивок системных компонентов (BIOS, BMC, etc.).
```json
{
"firmware": [
{
"device_name": "BIOS",
"version": "06.08.05 (2025-05-15 18:39:00)"
},
{
"device_name": "BMC",
"version": "5.17.00 (2025-04-22 12:06:31)"
}
]
}
```
**Поля:**
- `device_name` (string, обязательно) - название устройства: `BIOS`, `BMC`, `CPLD`, etc.
- `version` (string, обязательно) - версия прошивки
**Обработка:**
- Firmware данные сохраняются в observation
- Изменения версий firmware системных компонентов создают события FIRMWARE_CHANGED для Asset
---
## Полный пример JSON
```json
{
"filename": "redfish://10.10.10.103",
"source_type": "api",
"protocol": "redfish",
"target_host": "10.10.10.103",
"collected_at": "2026-02-10T15:30:00Z",
"hardware": {
"board": {
"manufacturer": "Supermicro",
"product_name": "X12DPG-QT6",
"serial_number": "21D634101",
"part_number": "X12DPG-QT6-REV1.01",
"uuid": "d7ef2fe5-2fd0-11f0-910a-346f11040868"
},
"firmware": [
{
"device_name": "BIOS",
"version": "06.08.05"
},
{
"device_name": "BMC",
"version": "5.17.00"
}
],
"cpus": [
{
"socket": 0,
"model": "INTEL(R) XEON(R) GOLD 6530",
"cores": 32,
"threads": 64,
"frequency_mhz": 2100,
"max_frequency_mhz": 4000,
"manufacturer": "Intel",
"status": "OK"
},
{
"socket": 1,
"model": "INTEL(R) XEON(R) GOLD 6530",
"cores": 32,
"threads": 64,
"frequency_mhz": 2100,
"max_frequency_mhz": 4000,
"manufacturer": "Intel",
"status": "OK"
}
],
"memory": [
{
"slot": "CPU0_C0D0",
"location": "CPU0_C0D0",
"present": true,
"size_mb": 32768,
"type": "DDR5",
"max_speed_mhz": 4800,
"current_speed_mhz": 4800,
"manufacturer": "Hynix",
"serial_number": "80AD032419E17CEEC1",
"part_number": "HMCG88AGBRA191N",
"status": "OK"
},
{
"slot": "CPU1_C0D0",
"location": "CPU1_C0D0",
"present": true,
"size_mb": 32768,
"type": "DDR5",
"max_speed_mhz": 4800,
"current_speed_mhz": 4800,
"manufacturer": "Hynix",
"serial_number": "80AD032419E17D6FBA",
"part_number": "HMCG88AGBRA191N",
"status": "OK"
}
],
"storage": [
{
"slot": "OB01",
"type": "NVMe",
"model": "INTEL SSDPF2KX076T1",
"size_gb": 7680,
"serial_number": "BTAX41900GF87P6DGN",
"manufacturer": "Intel",
"firmware": "9CV10510",
"interface": "NVMe",
"present": true,
"status": "OK"
},
{
"slot": "OB02",
"type": "NVMe",
"model": "INTEL SSDPF2KX076T1",
"size_gb": 7680,
"serial_number": "BTAX41900BEG7P6DGN",
"manufacturer": "Intel",
"firmware": "9CV10510",
"interface": "NVMe",
"present": true,
"status": "OK"
}
],
"pcie_devices": [
{
"slot": "PCIeCard1",
"vendor_id": 32902,
"device_id": 2912,
"bdf": "0000:18:00.0",
"device_class": "MassStorageController",
"manufacturer": "Intel",
"model": "RAID Controller",
"serial_number": "RAID-001-12345",
"status": "OK"
}
],
"power_supplies": [
{
"slot": "0",
"present": true,
"model": "GW-CRPS3000LW",
"vendor": "Great Wall",
"wattage_w": 3000,
"serial_number": "2P06C102610",
"part_number": "V0310C9000000000",
"firmware": "00.03.05",
"status": "OK",
"input_power_w": 137,
"output_power_w": 104,
"input_voltage": 215.25
}
]
}
}
```
---
## Процесс обработки импорта
### 1. Валидация входных данных
- Проверка наличия обязательных полей: `collected_at`, `hardware.board.serial_number`
- Проверка формата `collected_at` (RFC3339)
- Проверка наличия `hardware.board.serial_number`
### 2. Поиск или создание Asset
**Поиск:** по `hardware.board.serial_number` (= `vendor_serial` в таблице assets)
**Создание/Обновление:**
```
vendor_serial = board.serial_number
vendor = board.manufacturer (если != "NULL")
model = board.product_name (если != "NULL")
name = target_host (если передан), иначе `hardware.board.serial_number`
```
**Примечание:** Asset должен быть связан с Project. Если project_id не задан, используется default project для данного импорта.
### 3. Обработка компонентов
Для каждого типа компонентов (cpus, memory, storage, pcie_devices, power_supplies):
#### 3.1. Фильтрация
- Игнорировать компоненты с `present = false` (кроме создания REMOVED events)
- Игнорировать компоненты без serial_number (после генерации, если применимо)
#### 3.2. Определение LOT
- Автоматически определить LOT на основе vendor/model/type/size
- Создать LOT если не существует
- Связать component с lot_id
#### 3.3. Поиск или создание Component
**Поиск:** по `vendor_serial`
**Создание/Обновление:**
```
vendor_serial = {serial_number или generated}
vendor = {manufacturer}
model = {model}
lot_id = {auto-determined lot}
```
#### 3.4. Создание Observation
Для каждого компонента создается observation запись:
```
log_bundle_id = {created bundle}
asset_id = {asset.id}
component_id = {component.id}
observed_at = collected_at
```
Дополнительные данные сохраняются в JSON поле observation (slot, status, firmware, telemetry, etc.)
#### 3.5. Обновление Installations
**Логика:**
- Если компонент уже установлен в этот asset (`installations.removed_at IS NULL`) - ничего не делать
- Если компонент был в другом asset - закрыть старую installation (установить `removed_at`)
- Создать новую installation:
```
asset_id = {asset.id}
component_id = {component.id}
installed_at = collected_at (или first_seen_at если компонент новый)
removed_at = NULL
```
#### 3.6. Определение removed компонентов
Сравнить текущий snapshot с предыдущим observation для этого asset:
- Если компонент был в предыдущем observation, но отсутствует в текущем - закрыть installation (`removed_at = collected_at`)
### 4. Создание Timeline Events
События создаются автоматически на основе изменений:
**LOG_COLLECTED:**
```
subject_type = "asset"
subject_id = asset.id
event_type = "LOG_COLLECTED"
event_time = collected_at
```
**INSTALLED:** (при создании новой installation)
```
subject_type = "component"
subject_id = component.id
event_type = "INSTALLED"
event_time = installed_at
asset_id = asset.id
component_id = component.id
```
**REMOVED:** (при закрытии installation)
```
subject_type = "component"
subject_id = component.id
event_type = "REMOVED"
event_time = removed_at
asset_id = asset.id
component_id = component.id
```
**FIRMWARE_CHANGED:** (при изменении firmware версии)
```
subject_type = "component" (или "asset" для BIOS/BMC)
subject_id = {id}
event_type = "FIRMWARE_CHANGED"
event_time = collected_at
firmware_version = {new version}
```
### 5. Обработка статусов компонентов
Статусы (`OK`, `Warning`, `Critical`, `Unknown`) сохраняются в observation и могут быть использованы для:
- Анализа трендов деградации
- Автоматического создания failure events (если `status = "Critical"`)
- Dashboard отображения текущего состояния
---
## API Endpoint для импорта
```http
POST /ingest/hardware
Content-Type: application/json
{
"collected_at": "2026-02-10T15:30:00Z",
"target_host": "10.10.10.103",
"hardware": {...}
}
```
### Ответ при успехе (201 Created)
```json
{
"status": "success",
"bundle_id": "lb_01J...",
"asset_id": "mach_01J...",
"collected_at": "2026-02-10T15:30:00Z",
"duplicate": false,
"summary": {
"parts_observed": 15,
"parts_created": 2,
"parts_updated": 13,
"installations_created": 2,
"installations_closed": 1,
"timeline_events_created": 9,
"failure_events_created": 1
}
}
```
### Ответ при дубликате (200 OK)
```json
{
"status": "success",
"bundle_id": "lb_01J...",
"asset_id": "mach_01J...",
"collected_at": "2026-02-10T15:30:00Z",
"duplicate": true,
"message": "LogBundle with this content hash already exists"
}
```
### Ответ при ошибке (400 Bad Request)
```json
{
"status": "error",
"error": "validation_failed",
"details": {
"field": "hardware.board.serial_number",
"message": "serial_number is required"
}
}
```
### Частые причины `400 Bad Request`
- Лишние поля в JSON (даже глубоко во вложенных объектах).
- Неверное имя ключа (например, `targetHost` вместо `target_host`).
- Неверный формат даты (`collected_at` должен быть RFC3339).
- Пустой `hardware.board.serial_number`.
---
## Правила автоопределения LOT
### CPU
```
Формат: CPU_{VENDOR}_{MODEL_NORMALIZED}
Пример:
"INTEL(R) XEON(R) GOLD 6530" -> "CPU_INTEL_XEON_GOLD_6530"
"AMD EPYC 7763" -> "CPU_AMD_EPYC_7763"
```
### Memory (DIMM)
```
Формат: DIMM_{TYPE}_{SIZE_GB}GB
Пример:
DDR5 32GB -> "DIMM_DDR5_32GB"
DDR4 64GB -> "DIMM_DDR4_64GB"
```
### Storage
```
Формат: {TYPE}_{INTERFACE}_{SIZE_TB}TB (или GB для маленьких дисков)
Пример:
NVMe 7.68TB -> "SSD_NVME_07.68TB"
HDD 12TB -> "HDD_SATA_12TB"
SSD 960GB -> "SSD_SATA_0.96TB" или "SSD_SATA_960GB"
```
### Power Supply
```
Формат: PSU_{WATTAGE}W_{VENDOR_NORMALIZED}
Пример:
3000W Great Wall -> "PSU_3000W_GREAT_WALL"
1600W Delta -> "PSU_1600W_DELTA"
```
### PCIe Device
```
Формат: PCIE_{DEVICE_CLASS}_{MODEL_NORMALIZED} или PCIE_{DEVICE_CLASS}_{VENDOR_ID}_{DEVICE_ID}
Пример:
Network Mellanox ConnectX-5 -> "PCIE_NETWORK_CONNECTX5"
Storage Intel RAID -> "PCIE_STORAGE_INTEL_RAID"
Unknown 32902:2912 -> "PCIE_STORAGE_32902_2912"
```
### Правила нормализации
1. **Удаление специальных символов:** `(`, `)`, `-`, `®`, ``, пробелы заменяются на `_`
2. **Uppercase:** все символы в верхнем регистре
3. **Множественные подчеркивания:** заменяются на одно
4. **Префиксы:** убираются общие префиксы типа `MODEL:`, `PN:`, etc.
---
## Статусы компонентов
### Возможные значения
- `OK` - компонент работает нормально
- `Warning` - есть предупреждения (degraded, warning threshold)
- `Critical` - критическое состояние (failed, error)
- `Unknown` - статус неизвестен или не поддерживается
- `Empty` - слот пустой (только для memory, pcie_devices)
### Обработка статусов
**OK:**
- Нормальная обработка, никаких дополнительных действий
**Warning:**
- Создать observation с флагом warning
- Опционально: создать timeline event `COMPONENT_WARNING`
**Critical:**
- Создать observation с флагом critical
- **Автоматически создать failure_event** для этого компонента
- Создать timeline event `COMPONENT_FAILED`
**Unknown:**
- Сохранить как есть, считать компонент рабочим
**Empty:**
- Не создавать component/observation для этого слота
---
## Обработка отсутствующих полей
### serial_number
**CPU:** генерируется как `{board_serial}-CPU-{socket}`
**PCIe Device:** генерируется как `{board_serial}-PCIE-{slot}` (если serial_number = "N/A" или пустой)
**Другие компоненты:** если serial_number отсутствует и не может быть сгенерирован - компонент игнорируется
### manufacturer
**Если vendor_id присутствует (PCIe):** используется lookup таблица PCI vendors
- `8086` -> `Intel`
- `10de` -> `NVIDIA`
- `15b3` -> `Mellanox`
- etc.
**Если "NULL" или пустой:** сохраняется как NULL в БД
### status
**Если отсутствует:** считается `Unknown`
### firmware
**Если отсутствует:** не создается FIRMWARE_CHANGED event
---
## Примеры использования
### Пример 1: Минимальный snapshot
```json
{
"collected_at": "2026-02-10T15:30:00Z",
"target_host": "192.168.1.100",
"hardware": {
"board": {
"serial_number": "TEST-SERVER-001"
},
"cpus": [
{
"socket": 0,
"model": "Intel Xeon Gold 6530"
}
],
"memory": [],
"storage": [],
"pcie_devices": [],
"power_supplies": []
}
}
```
### Пример 2: Server с отказавшим диском
```json
{
"collected_at": "2026-02-10T15:30:00Z",
"target_host": "prod-db-01",
"hardware": {
"board": {
"manufacturer": "Dell",
"product_name": "PowerEdge R740",
"serial_number": "CN7475162Q0123"
},
"storage": [
{
"slot": "Disk.Bay.0",
"type": "SSD",
"model": "Samsung PM1733",
"serial_number": "S5GUNG0N123456",
"firmware": "9CV10510",
"interface": "NVMe",
"present": true,
"status": "Critical"
},
{
"slot": "Disk.Bay.1",
"type": "SSD",
"model": "Samsung PM1733",
"serial_number": "S5GUNG0N123457",
"firmware": "9CV10510",
"interface": "NVMe",
"present": true,
"status": "OK"
}
]
}
}
```
**Обработка:**
- Disk.Bay.0 получит статус Critical
- Автоматически создастся failure_event для компонента S5GUNG0N123456
- Timeline event COMPONENT_FAILED
### Пример 3: Замена памяти
**Snapshot 1 (до замены):**
```json
{
"collected_at": "2026-02-09T10:00:00Z",
"target_host": "web-01",
"hardware": {
"board": {"serial_number": "SRV001"},
"memory": [
{
"slot": "DIMM_A1",
"serial_number": "OLD-DIMM-001",
"present": true,
"status": "OK"
}
]
}
}
```
**Snapshot 2 (после замены):**
```json
{
"collected_at": "2026-02-10T14:00:00Z",
"target_host": "web-01",
"hardware": {
"board": {"serial_number": "SRV001"},
"memory": [
{
"slot": "DIMM_A1",
"serial_number": "NEW-DIMM-002",
"present": true,
"status": "OK"
}
]
}
}
```
**Обработка:**
- Компонент OLD-DIMM-001: закрывается installation (`removed_at = 2026-02-10T14:00:00Z`), создается REMOVED event
- Компонент NEW-DIMM-002: создается новый component, создается installation, создается INSTALLED event
---
## Интеграция с существующим кодом
Текущий эндпоинт `/ingest/logbundle` уже реализует часть этой логики. Новый формат должен:
1. **Быть обратно совместимым** - старый формат продолжает работать
2. **Использовать ту же инфраструктуру** - LogBundle, Observations, Installations
3. **Расширить observation JSON** - добавить поля status, slot, telemetry
4. **Добавить LOT автоопределение** - новая функция в `internal/ingest`
5. **Добавить обработку статусов** - автоматическое создание failure events
### Предлагаемые изменения
**Новый endpoint:** `POST /ingest/hardware` (принимает новый формат)
**Старый endpoint:** `POST /ingest/logbundle` (остается без изменений)
**Общая логика:** оба endpoint используют общий `ingest.Service` с расширенной обработкой
---
## Следующие шаги
1. **Реализовать парсер нового формата** - `internal/ingest/parser_hardware.go`
2. **Добавить LOT автоопределение** - `internal/ingest/lot_classifier.go`
3. **Расширить observation модель** - добавить JSON поля для status, slot, etc.
4. **Реализовать обработку статусов** - автосоздание failure events
5. **Добавить endpoint** - `POST /ingest/hardware`
6. **Написать тесты** - unit + integration тесты для нового формата
7. **Документировать API** - OpenAPI спецификация