# Руководство по интеграции 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, опционально) - дата/время, когда был проверен статус работоспособности компонента - `error_description` (string, опционально) - текст ошибки/диагностики для статуса компонента (например при `Warning`/`Critical`) --- ## Секция 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": "Critical", "error_description": "Error Code on GPU 0 [18:00.0] (S/N 1653925025827) = 020000190097 (unexpected device interrupts)" }, { "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 спецификация