# RFC: SFP Module Identity and Telemetry in Hardware Ingest Contract **Date:** 2026-06-19 **Status:** proposal **Target:** Reanimator Core — ingest schema, validation, and storage **Requested by:** bee (hardware audit LiveCD) **Contract version affected:** 2.10 → 2.11 --- ## Задача для агента Внести изменения в контракт ingest и реализовать их в Reanimator Core: 1. Добавить массив `sfp_modules[]` внутри `pcie_devices[]`. 2. Обновить JSON-схему и валидатор ingest endpoint. 3. Реализовать хранение/upsert данных SFP-модулей. 4. Пометить скалярные поля `sfp_*` на уровне `pcie_devices[]` как deprecated. 5. Обновить документ контракта (`hardware-ingest-contract.md`) до версии 2.11. Ниже — всё необходимое для принятия решений без дополнительных вопросов. --- ## Контекст: откуда берутся данные **Источник в bee:** `ethtool -m ` (читает EEPROM SFP/SFP+/QSFP28/QSFP-DD по стандарту MSA SFF-8472 / SFF-8636). **Связь с PCIe:** `ethtool -i ` возвращает `bus-info` = BDF (`0000:3b:00.0`), который совпадает с `pcie_devices[].slot`. Так bee связывает SFP-данные конкретного интерфейса с PCIe-устройством. **Один NIC — несколько модулей:** карта ConnectX-6 Dx (2 порта), Intel X710 (4 порта), Mellanox HDR (2 порта). Каждый порт — отдельный `ethtool -m`, отдельный SFP-модуль. Одного скаляра на устройство недостаточно. **QSFP28/QSFP-DD:** 4-канальные модули возвращают telemetry отдельно по каждому каналу (lane). В предложенной схеме lane-уровень не включён в первую версию — только агрегированные значения модуля в целом. Расширение до lane-уровня — отдельный RFC если понадобится. --- ## Проблема с текущим контрактом v2.10 В `pcie_devices[]` есть пять скалярных полей: ``` sfp_temperature_c float sfp_tx_power_dbm float sfp_rx_power_dbm float sfp_voltage_v float sfp_bias_ma float ``` Ограничения: - **Нет идентификации модуля** — vendor, part_number, serial_number, wavelength отсутствуют; модуль нельзя инвентаризировать как самостоятельный компонент. - **Только один набор значений на устройство** — невозможно описать 4-портовый NIC. - **Нет типа модуля** — SFP, QSFP28, DAC-кабель не различаются. - **Нет connector/transceiver_type** — невозможно понять, оптика это или медь. --- ## Предлагаемое изменение схемы ### Новая структура `sfp_modules[]` Добавляется как необязательное поле внутри каждого объекта `pcie_devices[]`. ```json "pcie_devices": [ { "slot": "0000:3b:00.0", "device_class": "EthernetController", "model": "ConnectX-6 Dx", "manufacturer": "Mellanox", "serial_number": "MT2012X12345", "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 } ] } ] ``` ### Поля `sfp_modules[]` | Поле | Тип | Обязательно | Описание | |---|---|---|---| | `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_device.slot, sfp_modules[].port)`. **Модули без серийного номера** — допустимы; многие DAC-кабели не имеют SN. Не игнорировать, сохранять по ключу `(slot, port)`. --- ## Deprecated поля Следующие поля на уровне `pcie_devices[]` помечаются как **deprecated** начиная с v2.11: ``` sfp_temperature_c sfp_tx_power_dbm sfp_rx_power_dbm sfp_voltage_v sfp_bias_ma ``` **Поведение при получении deprecated полей:** - Продолжать принимать и сохранять (не ломать существующих интеграторов). - Если одновременно присутствуют `sfp_modules[]` и deprecated скаляры — приоритет у `sfp_modules[]`; скаляры игнорируются. - В документации пометить как `deprecated since 2.11, will be removed in 3.0`. **Не удалять** deprecated поля из валидации в этом PR — только пометить в документации и changelog. --- ## Правила ingest для `sfp_modules[]` - `sfp_modules[]` хранится как snapshot-атрибут PCIe-компонента (аналогично `mac_addresses`). - При каждом импорте — полная замена `sfp_modules[]` для данного `pcie_devices[].slot` (upsert всего массива целиком, не merge по портам). - Если `sfp_modules` отсутствует или `null` — существующие данные SFP не трогать (не затирать). - Если `sfp_modules: []` (пустой массив) — трактовать как «модули не обнаружены», очистить сохранённые данные. - Изменение `serial_number` или `part_number` модуля на порту — создавать событие `COMPONENT_CHANGED` для PCIe-устройства с описанием «SFP module replaced on port N». --- ## Изменения в документе контракта Файл: `bible-local/docs/hardware-ingest-contract.md` 1. Заголовок версии: `2.10` → `2.11`, дата → `2026-06-19`. 2. Добавить в changelog: ``` | 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[] имеет приоритет). | ``` 3. В секции `pcie_devices` добавить строку в таблицу полей: ``` | `sfp_modules` | array | нет | Установленные SFP/QSFP-модули по портам (см. sfp_modules[]) | ``` 4. Добавить подсекцию `#### pcie_devices[].sfp_modules[]` с таблицей полей и примером JSON (из раздела выше). 5. Пометить deprecated поля в таблице: добавить суффикс `*(deprecated since 2.11)*`. 6. Обновить полный пример JSON — добавить `sfp_modules` к NIC-записи в `pcie_devices`. --- ## Что не нужно делать в этом PR - Не добавлять lane-level данные QSFP (tx_power_dbm_lane_0 и т.п.) — отдельный RFC. - Не удалять deprecated поля — только пометить. - Не создавать отдельную top-level секцию `network_ports` — данные остаются вложенными в `pcie_devices`. - Не менять логику идентификации PCIe-компонента — `serial_number` SFP-модуля не является ключом для самостоятельного компонента. --- ## Валидация Единственное обязательное поле в `sfp_modules[]` — `port` (int, >= 0). Все остальные поля опциональны. Дубли по `port` внутри одного `pcie_devices[]` — невалидны, возвращать `400` с описанием поля.