The gNB Contract
Racora acts on the gNB through interfaces, never through its internals. The controller renders its configuration, drives it over its command WebSocket, seeds its O1 datastore and reads its telemetry; the platform only runs the image. Any image built from OCUDU that holds the lines below works with Racora: upstream OCUDU, CNS OCUDU, or another distribution of it, whatever base image or transport it is built on.
A new image is brought in as a candidate: an intake run builds it from a source ref or takes a published tag, every integration suite installs it, and a pin change makes it the one releases ship (Release Process). The table is what that run checks.
| Interface | What Racora relies on | Checked by |
|---|---|---|
| The command WebSocket (port 8001 on every plane) | The CU-CP commands cell_lock, cell_unlock, cell_bar, cell_unbar, cell_status, mobility_cell_set, mobility_cell_remove, mobility_neighbor_add, mobility_neighbor_remove, mobility_report_config_set, mobility_report_config_remove, mobility_periodic_report_set, trigger_handover, trigger_conditional_handover; the DU command pci_set; metrics_subscribe; a reply of {"cmd", "result"} or {"error"} with the rejection strings of Runtime Commands | test/ws-contract/matrix.jsonl, run by test/ws-contract-integration.sh; controller/tests/test_cucp_ws_client.py |
| The configuration | The YAML the controller renders parses and boots the planes: a DU's gnb_id, gnb_id_bit_length, gnb_du_id, cell_cfg, ru_sdr / ru_dummy / ru_ofh, f1ap, f1u, log, pcap, metrics, remote_control; the CU-CP overlay's gnb_id, gnb_id_bit_length, ran_node_name, cu_cp.amf, cu_cp.mobility, cu_cp.cells, max_nof_dus | test/platform-integration.sh and test/telemetry-integration.sh (the planes come up on it in k3d); controller/tests/test_config_generator.py |
| F1, E1 and NG | A DU's F1 Setup is accepted with the identity the controller declares (its NCI decodes to the CU-CP's gNB-Id); the CU-UP's E1; NG Setup with the network identity | test/lib/ran-assertions.sh and test/lib/core-assertions.sh in the k3d suites; the cell's DUServed condition on a running network |
| O1 | The 3GPP NRM with the OCUDU extensions: the controller's configuration seeds it and the adapter renders it back unchanged, the leaves the models make mandatory (gNBId, gNBIdLength, gNBCUName) carrying the controller's values | test/check-o1-seed-roundtrip.py (CI job o1-seed-roundtrip); test/lib/o1-assertions.sh |
| Telemetry | RANN-P JSON lines (rann_p_v2); a cucp.<cmd>.execute span per command, parented by the WebSocket traceparent; OTLP logs carrying trace ids; the log markers the dashboards read; the O1 PM stream names | sidecars/rann-p-forwarder/tests/golden/rann_p_v2.jsonl (diffed against the gNB's copy by cross-repo-guards); test/telemetry-integration.sh; test/check-dashboard-markers.py; test/check-o1-dashboards.py |
| The image | gnb, ocucp, ocuup and odu on the path; file capabilities cap_sys_nice, cap_ipc_lock, cap_perfmon grantable; F1-C 38472, E1 38462, NG 38412, the WebSocket 8001, GTP-U 2152 and 2153; nothing beyond sh builtins (the probes walk /proc/net/sctp/eps) | test/helm-gates.sh; the readiness of every plane in the k3d suites; gnb --version in the intake build |
Changing the Contract
An interface changes the way everything else here does: the test first, the line in
this table in the same change, and a candidate run of the pinned gNB to see whether
the stable image still holds it. test/gnb-contract-gate.py keeps the table and the
tests in step: every path in the last column must exist, and every contract check
must be named here. Adding an interface adds a test and a line; retiring one removes
both, so nothing can still depend on it unseen.