Synthesized from bible-local reviews across bee, logpile, core, chart, PriceForge: - rules/patterns/hardware-ingest-json/contract.md — Reanimator JSON ingest schema v2.10 - rules/patterns/submodule-integration/contract.md — read-only submodule principle - go-database: add driver-level violation symptoms for cursor safety rule Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
294 lines
15 KiB
Markdown
294 lines
15 KiB
Markdown
# Contract: Hardware Ingest JSON
|
||
|
||
Version: 2.10
|
||
Source: `bee/bible-local/docs/hardware-ingest-contract.md` (canonical upstream)
|
||
|
||
Стандартный JSON-контракт для передачи данных об аппаратном обеспечении серверов в Reanimator.
|
||
Используется в `bee`, `logpile`, `core` и внешних интеграторах (Redfish-коллекторы, CMDB-экспортёры).
|
||
|
||
> Актуальная версия: https://git.mchus.pro/reanimator/core/src/branch/main/bible-local/docs/hardware-ingest-contract.md
|
||
|
||
## Принципы
|
||
|
||
1. **Snapshot** — JSON описывает состояние сервера на момент сбора. Может включать историю изменений статуса.
|
||
2. **Идемпотентность** — повторная отправка идентичного payload не создаёт дублей (дедупликация по хешу).
|
||
3. **Частичность** — можно передавать только те секции, данные по которым доступны. Пустой массив и отсутствие секции эквивалентны.
|
||
4. **Строгая схема** — endpoint использует строгий JSON-декодер; неизвестные поля приводят к `400 Bad Request`.
|
||
5. **Event-driven** — импорт создаёт события в timeline (LOG_COLLECTED, INSTALLED, REMOVED, FIRMWARE_CHANGED и др.).
|
||
6. **Без синтеза** — сборщик передаёт только фактически собранные значения. Запрещено придумывать `serial_number`, `component_ref`, `message`, `message_id` или иные идентификаторы, если источник их не предоставил.
|
||
|
||
## Endpoint
|
||
|
||
```
|
||
POST /ingest/hardware
|
||
Content-Type: application/json
|
||
```
|
||
|
||
Ответ `202 Accepted` с `job_id`. Результат: `GET /ingest/hardware/jobs/{job_id}`.
|
||
|
||
## Структура верхнего уровня
|
||
|
||
```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": { ... },
|
||
"firmware": [ ... ],
|
||
"cpus": [ ... ],
|
||
"memory": [ ... ],
|
||
"storage": [ ... ],
|
||
"pcie_devices": [ ... ],
|
||
"power_supplies": [ ... ],
|
||
"sensors": { ... },
|
||
"event_logs": [ ... ],
|
||
"platform_config": { ... }
|
||
}
|
||
}
|
||
```
|
||
|
||
| Поле | Тип | Обязательно | Описание |
|
||
|------|-----|-------------|----------|
|
||
| `collected_at` | string RFC3339 | **да** | Время сбора данных |
|
||
| `hardware` | object | **да** | Аппаратный снапшот |
|
||
| `hardware.board.serial_number` | string | **да** | Серийный номер платы/сервера |
|
||
| `target_host` | string | нет | IP или hostname |
|
||
| `source_type` | string | нет | `api`, `logfile`, `manual` |
|
||
| `protocol` | string | нет | `redfish`, `ipmi`, `snmp`, `ssh` |
|
||
| `filename` | string | нет | Идентификатор источника |
|
||
|
||
## Общие поля статуса компонентов
|
||
|
||
Применяются ко всем компонентным секциям (`cpus`, `memory`, `storage`, `pcie_devices`, `power_supplies`).
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| `status` | string | `OK`, `Warning`, `Critical`, `Unknown`, `Empty` |
|
||
| `status_checked_at` | string RFC3339 | Время последней проверки |
|
||
| `status_changed_at` | string RFC3339 | Время последнего изменения |
|
||
| `status_history` | array | История переходов (`status`, `changed_at` обязательны) |
|
||
| `error_description` | string | Текст ошибки/диагностики |
|
||
| `manufactured_year_week` | string | Дата производства `YYYY-Www`, например `2024-W07` |
|
||
|
||
Правила статуса:
|
||
- Не включайте записи `status_history` без `changed_at`.
|
||
- `status_history` сортировать по `changed_at` по возрастанию.
|
||
- Все даты — RFC3339, рекомендуется UTC (`Z`).
|
||
|
||
| Статус | Поведение |
|
||
|--------|-----------|
|
||
| `OK` | Нормальная обработка |
|
||
| `Warning` | Создаётся событие `COMPONENT_WARNING` |
|
||
| `Critical` | Создаётся событие `COMPONENT_FAILED` + запись в `failure_events` |
|
||
| `Unknown` | Компонент считается рабочим, создаётся событие `COMPONENT_UNKNOWN` |
|
||
| `Empty` | Компонент не создаётся/не обновляется |
|
||
|
||
## Секции hardware
|
||
|
||
### board (обязательная)
|
||
|
||
| Поле | Тип | Обязательно | Описание |
|
||
|------|-----|-------------|----------|
|
||
| `serial_number` | string | **да** | Серийный номер (ключ идентификации Asset) |
|
||
| `manufacturer` | string | нет | Производитель |
|
||
| `product_name` | string | нет | Модель |
|
||
| `part_number` | string | нет | Партномер |
|
||
| `uuid` | string | нет | UUID системы |
|
||
|
||
Значения `"NULL"` в строковых полях трактуются как отсутствие данных.
|
||
|
||
### firmware
|
||
|
||
| Поле | Тип | Обязательно |
|
||
|------|-----|-------------|
|
||
| `device_name` | string | **да** |
|
||
| `version` | string | **да** |
|
||
|
||
Записи с пустым `device_name` или `version` игнорируются. Изменение версии создаёт событие `FIRMWARE_CHANGED`.
|
||
|
||
### cpus
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| `socket` | int | **обязательно**; используется для генерации serial |
|
||
| `model` | string | Модель процессора |
|
||
| `manufacturer` | string | |
|
||
| `cores` / `threads` | int | |
|
||
| `frequency_mhz` / `max_frequency_mhz` | int | |
|
||
| `temperature_c` | float | Telemetry, °C |
|
||
| `power_w` | float | Telemetry, Вт |
|
||
| `throttled` | bool | Thermal/power throttling |
|
||
| `correctable_error_count` / `uncorrectable_error_count` | int | |
|
||
| `life_remaining_pct` / `life_used_pct` | float | Health/wear, % |
|
||
| `serial_number` | string | Если доступен |
|
||
| `firmware` | string | Версия микрокода (Microcode level — передавать как есть) |
|
||
| `present` | bool | По умолчанию `true` |
|
||
|
||
Генерация serial при отсутствии: `{board_serial}-CPU-{socket}`
|
||
|
||
### memory
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| `slot` | string | |
|
||
| `present` | bool | По умолчанию `true` |
|
||
| `serial_number` | string | Обязателен для создания записи |
|
||
| `part_number` | string | Используется как модель |
|
||
| `manufacturer` | string | |
|
||
| `size_mb` | int | |
|
||
| `type` | string | `DDR3`, `DDR4`, `DDR5` |
|
||
| `max_speed_mhz` / `current_speed_mhz` | int | |
|
||
| `temperature_c` | float | Telemetry |
|
||
| `correctable_ecc_error_count` / `uncorrectable_ecc_error_count` | int | |
|
||
| `life_remaining_pct` / `life_used_pct` / `spare_blocks_remaining_pct` | float | |
|
||
| `performance_degraded` / `data_loss_detected` | bool | |
|
||
|
||
Модуль без `serial_number`, с `present=false` или `status=Empty` игнорируется.
|
||
|
||
### storage
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| `slot` | string | BDF (`0000:18:00.0`) для PCIe-подключённых |
|
||
| `serial_number` | string | Обязателен для создания записи |
|
||
| `model` / `manufacturer` | string | |
|
||
| `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 | Физический размер блока |
|
||
| `metadata_bytes_per_block` | int64 | Metadata/protection bytes на блок, например `0` или `8` |
|
||
| `temperature_c` | float | Telemetry |
|
||
| `power_on_hours` / `power_cycles` / `unsafe_shutdowns` | int64 | |
|
||
| `media_errors` / `error_log_entries` | int64 | |
|
||
| `written_bytes` / `read_bytes` | int64 | |
|
||
| `life_used_pct` / `life_remaining_pct` / `available_spare_pct` | float | |
|
||
| `reallocated_sectors` / `current_pending_sectors` / `offline_uncorrectable` | int64 | |
|
||
| `firmware` | string | Изменение создаёт `FIRMWARE_CHANGED` |
|
||
| `present` | bool | По умолчанию `true` |
|
||
|
||
Формат `512+8` не передаётся строкой — только через `logical_block_size_bytes` + `metadata_bytes_per_block`.
|
||
Диск без `serial_number` игнорируется.
|
||
|
||
### pcie_devices
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| `slot` | string | Канонический адрес (BDF). `bdf` — deprecated alias, нормализуется при ingest |
|
||
| `vendor_id` / `device_id` | int | PCI ID (decimal) |
|
||
| `numa_node` | int | NUMA/CPU affinity |
|
||
| `device_class` | string | `MassStorageController`, `StorageController`, `NetworkController`, `EthernetController`, `FibreChannelController`, `VideoController`, `ProcessingAccelerator`, `DisplayController` (список открытый) |
|
||
| `manufacturer` / `model` / `serial_number` / `firmware` | string | |
|
||
| `link_width` / `max_link_width` | int | |
|
||
| `link_speed` / `max_link_speed` | string | `Gen3`, `Gen4`, `Gen5` |
|
||
| `mac_addresses` | string[] | MAC-адреса портов |
|
||
| `temperature_c` / `power_w` | float | Device-level telemetry |
|
||
| `life_remaining_pct` / `life_used_pct` | float | |
|
||
| `ecc_corrected_total` / `ecc_uncorrected_total` | int64 | |
|
||
| `hw_slowdown` | bool | |
|
||
| `battery_charge_pct` / `battery_health_pct` / `battery_temperature_c` / `battery_voltage_v` | float | |
|
||
| `battery_replace_required` | bool | |
|
||
| `sfp_temperature_c` / `sfp_tx_power_dbm` / `sfp_rx_power_dbm` / `sfp_voltage_v` / `sfp_bias_ma` | float | Optical telemetry |
|
||
| `present` | bool | По умолчанию `true` |
|
||
|
||
Генерация serial при отсутствии или `"N/A"`: `{board_serial}-PCIE-{slot}`
|
||
|
||
### power_supplies
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| `slot` | string | |
|
||
| `present` | bool | По умолчанию `true` |
|
||
| `serial_number` | string | Обязателен для создания записи |
|
||
| `part_number` / `model` / `vendor` | string | |
|
||
| `wattage_w` | int | |
|
||
| `firmware` | string | |
|
||
| `input_type` | string | Например `ACWideRange` |
|
||
| `input_voltage` / `input_power_w` / `output_power_w` / `temperature_c` | float | Telemetry |
|
||
| `life_remaining_pct` / `life_used_pct` | float | |
|
||
|
||
PSU без `serial_number` игнорируется.
|
||
|
||
### sensors (опционально)
|
||
|
||
Данные хранятся как last-known-value на уровне Asset. Идентификатор: `(sensor_type, name)`.
|
||
Поле `location` передавать не нужно — игнорируется. Сенсоры без `name` игнорируются.
|
||
|
||
```json
|
||
"sensors": {
|
||
"fans": [{ "name": "FAN1", "rpm": 4200, "status": "OK" }],
|
||
"power": [{ "name": "12V Rail", "voltage_v": 12.06, "status": "OK" }],
|
||
"temperatures": [{ "name": "CPU0 Temp", "celsius": 46.0, "threshold_warning_celsius": 80.0, "threshold_critical_celsius": 95.0, "status": "OK" }],
|
||
"other": [{ "name": "System Humidity", "value": 38.5, "unit": "%" }]
|
||
}
|
||
```
|
||
|
||
### event_logs (опционально)
|
||
|
||
Нормализованные операционные логи. Не попадают в history timeline. Дедуплицируются по `(asset, source, fingerprint)`.
|
||
|
||
| Поле | Тип | Обязательно | Описание |
|
||
|------|-----|-------------|----------|
|
||
| `source` | string | **да** | `host`, `bmc`, `redfish` |
|
||
| `message` | string | **да** | Нормализованный текст события |
|
||
| `event_time` | string RFC3339 | нет | |
|
||
| `severity` | string | нет | `OK`, `Info`, `Warning`, `Critical`, `Unknown` |
|
||
| `message_id` | string | нет | Код события источника |
|
||
| `component_ref` | string | нет | Ссылка на компонент/слот |
|
||
| `fingerprint` | string | нет | Внешний dedup-key; если нет — система вычисляет свой |
|
||
| `is_active` | bool | нет | Событие всё ещё активно |
|
||
| `raw_payload` | object | нет | Сырой vendor-specific payload |
|
||
|
||
Запрещено синтезировать `message`, `message_id`, `component_ref`, serial/device identifiers.
|
||
|
||
### platform_config (опционально)
|
||
|
||
Произвольный объект с настройками платформы (BIOS/Redfish/IPMI) как есть из источника.
|
||
При каждом импорте хранится latest-snapshot per machine.
|
||
|
||
## Обработка отсутствующих serial_number
|
||
|
||
Интегратор не подставляет вымышленные значения, хеши или placeholder-идентификаторы.
|
||
Разрешены только server-side fallback-правила:
|
||
|
||
| Тип | Поведение |
|
||
|-----|-----------|
|
||
| CPU | Генерируется: `{board_serial}-CPU-{socket}` |
|
||
| PCIe | Генерируется: `{board_serial}-PCIE-{slot}` |
|
||
| Memory | Компонент игнорируется |
|
||
| Storage | Компонент игнорируется |
|
||
| PSU | Компонент игнорируется |
|
||
|
||
Если `serial_number` не уникален внутри payload для того же `model`: первое вхождение — оригинальный serial, дубли получают `NO_SN-XXXXXXXX`.
|
||
|
||
## Минимальный валидный пример
|
||
|
||
```json
|
||
{
|
||
"collected_at": "2026-02-10T15:30:00Z",
|
||
"target_host": "192.168.1.100",
|
||
"hardware": {
|
||
"board": {
|
||
"serial_number": "SRV-001"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## Changelog
|
||
|
||
| Версия | Дата | Изменения |
|
||
|--------|------|-----------|
|
||
| 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` |
|
||
| 2.8 | 2026-03-15 | Поле `location` удалено из всех `sensors.*` |
|
||
| 2.7 | 2026-03-15 | Явно запрещён синтез данных в `event_logs` |
|
||
| 2.6 | 2026-03-15 | Добавлена секция `event_logs` |
|
||
| 2.5 | 2026-03-15 | Добавлено `manufactured_year_week` для всех компонентов |
|
||
| 2.4 | 2026-03-15 | Component telemetry: health/life поля для всех секций |
|
||
| 2.0 | 2026-02-01 | `status_history`, `status_changed_at`; async job response |
|
||
| 1.0 | 2026-01-01 | Начальная версия |
|