Files
logpile/docs/INTEGRATION_GUIDE.md

1047 lines
37 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.
# Руководство по интеграции 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, обязательно) - структура с аппаратными компонентами
### Общее поле статуса для компонентных секций
Для секций `cpus`, `memory`, `storage`, `pcie_devices`, `power_supplies` поддерживается дополнительное поле:
- `status_checked_at` (string RFC3339, опционально) - дата/время, когда был проверен статус работоспособности компонента
- `status_changed_at` (string RFC3339, опционально) - дата/время последнего изменения статуса компонента
- `status_at_collection` (object, опционально) - зафиксированный статус на момент сбора логов:
- `status` (string) - статус в момент сбора (`OK`, `Warning`, `Critical`, `Unknown`, `Empty`)
- `at` (string RFC3339) - дата/время, к которому относится этот статус
- `status_history` (array, опционально) - история статусов компонента:
- `status` (string) - статус (`OK`, `Warning`, `Critical`, `Unknown`, `Empty`)
- `changed_at` (string RFC3339) - дата/время смены статуса
- `details` (string, опционально) - пояснение к переходу статуса
- `error_description` (string, опционально) - текст ошибки/диагностики для статуса компонента (например при `Warning`/`Critical`)
### Правила экспорта JSON для внешнего проекта
Используйте эти правила, если JSON формируется внешним сервисом/экспортером:
1. Всегда передавайте `status` как текущее состояние компонента в snapshot.
2. Если есть точное время последней смены, передавайте `status_changed_at` (RFC3339, UTC).
3. Если источник умеет фиксировать состояние именно на момент сбора, передавайте `status_at_collection` c полями `status` и `at`.
4. Если источник хранит историю (например Windows Event Log), передавайте `status_history` отсортированным по `changed_at` по возрастанию.
5. В `status_history` не отправляйте записи без `changed_at`; такие записи игнорируются.
6. Для совместимости допускается передавать только старые поля (`status`, `status_checked_at`) без истории.
7. Все даты/время в исторических полях должны быть RFC3339; рекомендуется использовать UTC (`Z`).
---
## Секция 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",
"status_checked_at": "2026-02-10T15:28:00Z"
},
{
"socket": 1,
"model": "INTEL(R) XEON(R) GOLD 6530",
"cores": 32,
"threads": 64,
"frequency_mhz": 2100,
"max_frequency_mhz": 4000,
"manufacturer": "Intel",
"status": "OK",
"status_checked_at": "2026-02-10T15:28:00Z"
}
]
}
```
**Поля:**
- `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`
- `status_checked_at` (string RFC3339, опционально) - дата/время проверки статуса
**Генерация 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",
"status_checked_at": "2026-02-10T15:28:00Z"
},
{
"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`
- `status_checked_at` (string RFC3339, опционально) - дата/время проверки статуса
**Обработка:**
- Если `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`
- `status_checked_at` (string RFC3339, опционально) - дата/время проверки статуса
**Обработка 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",
"status_checked_at": "2026-02-10T15:28:00Z",
"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`
- `status_checked_at` (string RFC3339, опционально) - дата/время проверки статуса
- `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",
"status_checked_at": "2026-02-10T15:28:00Z"
},
{
"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`
- `status_checked_at` (string RFC3339, опционально) - дата/время проверки статуса
**Генерация 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": "OK",
"status_changed_at": "2026-02-10T15:22:00Z",
"status_at_collection": {
"status": "OK",
"at": "2026-02-10T15:30:00Z"
},
"status_history": [
{
"status": "Critical",
"changed_at": "2026-02-10T15:10:00Z",
"details": "I/O timeout on NVMe queue 3"
},
{
"status": "OK",
"changed_at": "2026-02-10T15:22:00Z",
"details": "Recovered after controller reset"
}
]
},
{
"slot": "Disk.Bay.1",
"type": "SSD",
"model": "Samsung PM1733",
"serial_number": "S5GUNG0N123457",
"firmware": "9CV10510",
"interface": "NVMe",
"present": true,
"status": "OK"
}
]
}
}
```
**Обработка:**
- Disk.Bay.0 получит текущий статус `OK`
- История статусов сохранится в `observations.details.status_history`
- Автоматический `failure_event` не создается, так как текущий статус snapshot не `Critical`
### Пример 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 спецификация