Files
bible/rules/patterns/hardware-ingest-json/contract.md
Michael Chus 1977730d93 Add hardware-ingest-json and submodule-integration contracts; expand go-database cursor safety
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>
2026-06-12 10:17:50 +03:00

294 lines
15 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.
# 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 | Начальная версия |