diff --git a/bible-local/docs/hardware-ingest-contract.md b/bible-local/docs/hardware-ingest-contract.md index 37fff09..b99654a 100644 --- a/bible-local/docs/hardware-ingest-contract.md +++ b/bible-local/docs/hardware-ingest-contract.md @@ -1,7 +1,7 @@ --- title: Hardware Ingest JSON Contract -version: "2.7" -updated: "2026-03-15" +version: "2.11" +updated: "2026-06-19" maintainer: Reanimator Core audience: external-integrators, ai-agents language: ru @@ -9,7 +9,7 @@ language: ru # Интеграция с Reanimator: контракт JSON-импорта аппаратного обеспечения -Версия: **2.7** · Дата: **2026-03-15** +Версия: **2.11** · Дата: **2026-06-19** Документ описывает формат JSON для передачи данных об аппаратном обеспечении серверов в систему **Reanimator** (управление жизненным циклом аппаратного обеспечения). Предназначен для разработчиков смежных систем (Redfish-коллекторов, агентов мониторинга, CMDB-экспортёров) и может быть включён в документацию интегрируемых проектов. @@ -22,6 +22,10 @@ language: ru | Версия | Дата | Изменения | |--------|------|-----------| +| 2.11 | 2026-06-19 | В `pcie_devices[]` добавлен необязательный массив `sfp_modules[]` с идентификацией и DOM telemetry SFP/QSFP-модулей. Скалярные поля `sfp_temperature_c` / `sfp_tx_power_dbm` / `sfp_rx_power_dbm` / `sfp_voltage_v` / `sfp_bias_ma` помечены как deprecated (принимаются, но `sfp_modules[]` имеет приоритет) | +| 2.10 | 2026-04-29 | Для `hardware.storage[]` добавлены необязательные числовые поля `logical_block_size_bytes`, `physical_block_size_bytes`, `metadata_bytes_per_block` для нормализованного описания формата блока накопителя | +| 2.9 | 2026-03-19 | Добавлена необязательная секция `hardware.platform_config` — произвольный объект с настройками платформы (BIOS/Redfish); хранится как latest-snapshot per machine | +| 2.8 | 2026-03-15 | Поле `location` удалено из всех `sensors.*`; сенсоры передаются только по `name` и измеренным значениям | | 2.7 | 2026-03-15 | Явно запрещён синтез данных в `event_logs`; интеграторы не должны придумывать серийные номера компонентов, если источник их не отдал | | 2.6 | 2026-03-15 | Добавлена необязательная секция `event_logs` для dedup/upsert логов `host` / `bmc` / `redfish` вне history timeline | | 2.5 | 2026-03-15 | Добавлено общее необязательное поле `manufactured_year_week` для компонентных секций (`YYYY-Www`) | @@ -131,8 +135,9 @@ GET /ingest/hardware/jobs/{job_id} "storage": [ ... ], "pcie_devices": [ ... ], "power_supplies": [ ... ], - "sensors": { ... }, - "event_logs": [ ... ] + "sensors": { ... }, + "event_logs": [ ... ], + "platform_config": { ... } } } ``` @@ -343,6 +348,9 @@ GET /ingest/hardware/jobs/{job_id} | `type` | string | нет | Тип: `NVMe`, `SSD`, `HDD` | | `interface` | string | нет | Интерфейс: `NVMe`, `SATA`, `SAS` | | `size_gb` | int | нет | Размер в ГБ | +| `logical_block_size_bytes` | int64 | нет | Логический размер пользовательского блока данных, например `512` или `4096` | +| `physical_block_size_bytes` | int64 | нет | Физический размер блока, если известен, например `4096` | +| `metadata_bytes_per_block` | int64 | нет | Metadata / protection bytes на логический блок, например `0` или `8` | | `temperature_c` | float | нет | Температура накопителя, °C (telemetry) | | `power_on_hours` | int64 | нет | Время работы, часы | | `power_cycles` | int64 | нет | Количество циклов питания | @@ -363,6 +371,11 @@ GET /ingest/hardware/jobs/{job_id} Диск без `serial_number` игнорируется. Изменение `firmware` создаёт событие `FIRMWARE_CHANGED`. +Формат вида `512+8` в контракт не добавляется отдельным строковым полем. Если источник знает такую форму, он должен передавать её как: +- `logical_block_size_bytes = 512` +- `metadata_bytes_per_block = 8` +- `physical_block_size_bytes = 512` или `4096`, если известен физический размер блока + ```json "storage": [ { @@ -370,6 +383,9 @@ GET /ingest/hardware/jobs/{job_id} "type": "NVMe", "model": "INTEL SSDPF2KX076T1", "size_gb": 7680, + "logical_block_size_bytes": 512, + "physical_block_size_bytes": 4096, + "metadata_bytes_per_block": 8, "temperature_c": 38.5, "power_on_hours": 12450, "unsafe_shutdowns": 3, @@ -407,11 +423,12 @@ GET /ingest/hardware/jobs/{job_id} | `battery_temperature_c` | float | нет | Температура батареи / supercap, °C | | `battery_voltage_v` | float | нет | Напряжение батареи / supercap, В | | `battery_replace_required` | bool | нет | Требуется замена батареи / supercap | -| `sfp_temperature_c` | float | нет | Температура SFP/optic, °C | -| `sfp_tx_power_dbm` | float | нет | TX optical power, dBm | -| `sfp_rx_power_dbm` | float | нет | RX optical power, dBm | -| `sfp_voltage_v` | float | нет | Напряжение SFP, В | -| `sfp_bias_ma` | float | нет | Bias current SFP, мА | +| `sfp_temperature_c` | float | нет | Температура SFP/optic, °C *(deprecated since 2.11)* | +| `sfp_tx_power_dbm` | float | нет | TX optical power, dBm *(deprecated since 2.11)* | +| `sfp_rx_power_dbm` | float | нет | RX optical power, dBm *(deprecated since 2.11)* | +| `sfp_voltage_v` | float | нет | Напряжение SFP, В *(deprecated since 2.11)* | +| `sfp_bias_ma` | float | нет | Bias current SFP, мА *(deprecated since 2.11)* | +| `sfp_modules` | array | нет | Установленные SFP/QSFP-модули по портам (см. sfp_modules[]) | | `bdf` | string | нет | Deprecated alias для `slot`; при наличии ingest нормализует его в `slot` | | `device_class` | string | нет | Класс устройства (см. список ниже) | | `manufacturer` | string | нет | Производитель | @@ -429,10 +446,43 @@ GET /ingest/hardware/jobs/{job_id} `numa_node` передавайте для NIC / InfiniBand / RAID / GPU, когда источник знает CPU/NUMA affinity. Поле сохраняется в snapshot-атрибутах PCIe-компонента и дублируется в telemetry для topology use cases. Поля `temperature_c` и `power_w` используйте для device-level telemetry GPU / accelerator / smart PCIe devices. Они не влияют на идентификацию компонента. +**Deprecated поля sfp_\*:** Скалярные поля `sfp_temperature_c`, `sfp_tx_power_dbm`, `sfp_rx_power_dbm`, `sfp_voltage_v`, `sfp_bias_ma` продолжают приниматься, но помечены как deprecated since 2.11. Если в payload одновременно присутствуют `sfp_modules[]` и deprecated sfp_-скаляры — приоритет у `sfp_modules[]`, скаляры игнорируются. Deprecated поля будут удалены в версии 3.0. + **Генерация serial_number при отсутствии или `"N/A"`:** `{board_serial}-PCIE-{slot}`, где `slot` для PCIe равен BDF. `slot` — единственный канонический адрес компонента. Для PCIe в `slot` передавайте BDF. Поле `bdf` сохраняется только как переходный alias на входе и не должно использоваться как отдельная координата рядом со `slot`. +#### pcie_devices[].sfp_modules[] + +Необязательный массив установленных SFP/QSFP-модулей для данного PCIe-устройства. Один элемент — один порт. Используйте для многопортовых NIC (ConnectX-6 Dx, Intel X710, Mellanox HDR и др.). + +| Поле | Тип | Обязательно | Описание | +|------|-----|-------------|----------| +| `port` | int | **да** | Номер порта на NIC (0-based). Ключ дедупликации внутри устройства | +| `identifier` | string | нет | Тип модуля: `SFP`, `SFP+`, `SFP28`, `QSFP+`, `QSFP28`, `QSFP-DD`, `DAC` | +| `connector` | string | нет | Тип разъёма: `LC`, `MPO`, `RJ45`, `DAC`, `AOC`, `No separable connector` | +| `vendor` | string | нет | Производитель модуля из EEPROM | +| `part_number` | string | нет | Партномер из EEPROM | +| `serial_number` | string | нет | Серийный номер из EEPROM | +| `revision` | string | нет | Ревизия из EEPROM | +| `wavelength_nm` | int | нет | Длина волны, нм (0 для DAC/медных кабелей) | +| `transceiver_type` | string | нет | `10GBase-SR`, `10GBase-LR`, `25GBase-SR`, `100GBase-SR4`, `DAC`, … | +| `temperature_c` | float | нет | Температура модуля, °C (DOM telemetry) | +| `voltage_v` | float | нет | Напряжение питания, В (DOM telemetry) | +| `tx_power_dbm` | float | нет | TX оптическая мощность, dBm (DOM telemetry) | +| `rx_power_dbm` | float | нет | RX оптическая мощность, dBm (DOM telemetry) | +| `bias_ma` | float | нет | Bias current, мА (DOM telemetry) | + +**Ключ дедупликации:** `(pcie_devices[].slot, sfp_modules[].port)`. + +**Правила ingest:** +- При каждом импорте — полная замена `sfp_modules[]` для данного `pcie_devices[].slot` (upsert всего массива целиком). +- Если `sfp_modules` отсутствует или `null` — существующие данные SFP не трогать. +- Если `sfp_modules: []` (пустой массив) — трактовать как «модули не обнаружены», очистить сохранённые данные. +- Дубли по `port` внутри одного `pcie_devices[]` — невалидны, endpoint возвращает `400` с описанием поля. +- Модули без `serial_number` допустимы (многие DAC-кабели не имеют SN); сохраняются по ключу `(slot, port)`. +- Изменение `serial_number` или `part_number` модуля на порту создаёт событие `COMPONENT_CHANGED` для PCIe-устройства с описанием «SFP module replaced on port N». + **Значения `device_class`:** | Значение | Назначение | @@ -457,16 +507,47 @@ GET /ingest/hardware/jobs/{job_id} "numa_node": 0, "temperature_c": 48.5, "power_w": 18.2, - "sfp_temperature_c": 36.2, - "sfp_tx_power_dbm": -1.8, - "sfp_rx_power_dbm": -2.1, "device_class": "EthernetController", - "manufacturer": "Intel", - "model": "X710 10GbE", - "serial_number": "K65472-003", - "firmware": "9.20 0x8000d4ae", + "manufacturer": "Mellanox", + "model": "ConnectX-6 Dx", + "serial_number": "MT2012X12345", + "firmware": "22.35.2010", "mac_addresses": ["3c:fd:fe:aa:bb:cc", "3c:fd:fe:aa:bb:cd"], - "status": "OK" + "status": "OK", + "sfp_modules": [ + { + "port": 0, + "identifier": "QSFP28", + "connector": "LC", + "vendor": "Mellanox", + "part_number": "MFA1A00-C003", + "serial_number": "MT2124VS09999", + "revision": "A", + "wavelength_nm": 850, + "transceiver_type": "100GBase-SR4", + "temperature_c": 36.4, + "voltage_v": 3.29, + "tx_power_dbm": -1.8, + "rx_power_dbm": -2.1, + "bias_ma": 7.2 + }, + { + "port": 1, + "identifier": "QSFP28", + "connector": "LC", + "vendor": "Mellanox", + "part_number": "MFA1A00-C003", + "serial_number": "MT2124VS09998", + "revision": "A", + "wavelength_nm": 850, + "transceiver_type": "100GBase-SR4", + "temperature_c": 35.9, + "voltage_v": 3.28, + "tx_power_dbm": -1.9, + "rx_power_dbm": -2.3, + "bias_ma": 7.1 + } + ] } ] ``` @@ -592,7 +673,6 @@ PSU без `serial_number` игнорируется. | Поле | Тип | Обязательно | Описание | |------|-----|-------------|----------| | `name` | string | **да** | Уникальное имя сенсора в рамках секции | -| `location` | string | нет | Физическое расположение | | `rpm` | int | нет | Обороты, RPM | | `status` | string | нет | Статус: `OK`, `Warning`, `Critical`, `Unknown` | @@ -601,7 +681,6 @@ PSU без `serial_number` игнорируется. | Поле | Тип | Обязательно | Описание | |------|-----|-------------|----------| | `name` | string | **да** | Уникальное имя сенсора | -| `location` | string | нет | Физическое расположение | | `voltage_v` | float | нет | Напряжение, В | | `current_a` | float | нет | Ток, А | | `power_w` | float | нет | Мощность, Вт | @@ -612,7 +691,6 @@ PSU без `serial_number` игнорируется. | Поле | Тип | Обязательно | Описание | |------|-----|-------------|----------| | `name` | string | **да** | Уникальное имя сенсора | -| `location` | string | нет | Физическое расположение | | `celsius` | float | нет | Температура, °C | | `threshold_warning_celsius` | float | нет | Порог Warning, °C | | `threshold_critical_celsius` | float | нет | Порог Critical, °C | @@ -623,38 +701,63 @@ PSU без `serial_number` игнорируется. | Поле | Тип | Обязательно | Описание | |------|-----|-------------|----------| | `name` | string | **да** | Уникальное имя сенсора | -| `location` | string | нет | Физическое расположение | | `value` | float | нет | Значение | | `unit` | string | нет | Единица измерения | | `status` | string | нет | Статус | **Правила sensors:** - Идентификатор сенсора: пара `(sensor_type, name)`. Дубли в одном payload — берётся первое вхождение. +- `location` для сенсоров передавать не нужно и не следует: в Reanimator location/slot используется только для проверки перемещения и установки компонентов, а не для last-known-value sensor ingest. - Сенсоры без `name` игнорируются. - При каждом импорте значения перезаписываются (upsert по ключу). ```json "sensors": { "fans": [ - { "name": "FAN1", "location": "Front", "rpm": 4200, "status": "OK" }, - { "name": "FAN_CPU0", "location": "CPU0", "rpm": 5600, "status": "OK" } + { "name": "FAN1", "rpm": 4200, "status": "OK" }, + { "name": "FAN_CPU0", "rpm": 5600, "status": "OK" } ], "power": [ - { "name": "12V Rail", "location": "Mainboard", "voltage_v": 12.06, "status": "OK" }, - { "name": "PSU0 Input", "location": "PSU0", "voltage_v": 215.25, "current_a": 0.64, "power_w": 137.0, "status": "OK" } + { "name": "12V Rail", "voltage_v": 12.06, "status": "OK" }, + { "name": "PSU0 Input", "voltage_v": 215.25, "current_a": 0.64, "power_w": 137.0, "status": "OK" } ], "temperatures": [ - { "name": "CPU0 Temp", "location": "CPU0", "celsius": 46.0, "threshold_warning_celsius": 80.0, "threshold_critical_celsius": 95.0, "status": "OK" }, - { "name": "Inlet Temp", "location": "Front", "celsius": 22.0, "threshold_warning_celsius": 40.0, "threshold_critical_celsius": 50.0, "status": "OK" } + { "name": "CPU0 Temp", "celsius": 46.0, "threshold_warning_celsius": 80.0, "threshold_critical_celsius": 95.0, "status": "OK" }, + { "name": "Inlet Temp", "celsius": 22.0, "threshold_warning_celsius": 40.0, "threshold_critical_celsius": 50.0, "status": "OK" } ], "other": [ - { "name": "System Humidity", "value": 38.5, "unit": "%" , "status": "OK" } + { "name": "System Humidity", "value": 38.5, "unit": "%", "status": "OK" } ] } ``` --- +## Секция platform_config + +Необязательный объект с произвольными ключами — настройки платформы как есть из источника (BIOS, Redfish, IPMI). + +| Поле | Тип | Обязательно | Описание | +|------|-----|-------------|----------| +| `platform_config` | object | нет | Произвольный объект: ключи — строки, значения — строки, числа, булевы | + +**Правила platform_config:** +- Содержимое объекта не валидируется: передавайте параметры как есть. +- При каждом импорте хранится latest-snapshot per machine; история изменений по каждому ключу накапливается отдельно. +- Если секция отсутствует или равна `null` — данные платформы не обновляются. + +```json +"platform_config": { + "SecureBoot": "Enabled", + "BiosVersion": "06.08.05", + "TpmEnabled": true, + "NumaEnabled": false, + "HyperThreading": "Enabled" +} +``` + +--- + ## Обработка статусов компонентов | Статус | Поведение | @@ -756,7 +859,24 @@ PSU без `serial_number` игнорируется. "model": "X710 10GbE", "serial_number": "K65472-003", "mac_addresses": ["3c:fd:fe:aa:bb:cc", "3c:fd:fe:aa:bb:cd"], - "status": "OK" + "status": "OK", + "sfp_modules": [ + { + "port": 0, + "identifier": "SFP+", + "connector": "LC", + "vendor": "Intel", + "part_number": "FTLX8574D3BCV-IT", + "serial_number": "FNS123456789", + "wavelength_nm": 850, + "transceiver_type": "10GBase-SR", + "temperature_c": 34.1, + "voltage_v": 3.30, + "tx_power_dbm": -2.5, + "rx_power_dbm": -3.0, + "bias_ma": 6.8 + } + ] } ], "power_supplies": [ @@ -787,6 +907,12 @@ PSU без `serial_number` игнорируется. "other": [ { "name": "System Humidity", "value": 38.5, "unit": "%" } ] + }, + "platform_config": { + "SecureBoot": "Enabled", + "BiosVersion": "06.08.05", + "TpmEnabled": true, + "HyperThreading": "Enabled" } } }