O1 and the SMO Functions
O1 is the management interface of a RAN element: configuration over NETCONF and YANG, faults as VES notifications, performance data as a stream or as files. The system that consumes it is the SMO. Racora does not ship an SMO; it ships the smallest useful part of one, in its own namespace, and leaves the rest to a real SMO when one is attached.
What Racora Provides
The racora-smo namespace holds the SMO functions Racora runs itself. Today that
is the O1 collector: a receiver for what the elements' O1 adapters send north.
- Faults. Every VES event an adapter posts is kept verbatim in ClickHouse
(
o1.events). The 3GPP fault notifications among them become typed rows ino1.alarms, one per raise, change or clear, and the viewo1.current_alarmslists the alarms whose last notification is a raise. - Performance data. The adapters' PM stream lands one row per metric sample in
o1.pm: the element, the metric name, its value, the cell when the metric has one. Two Grafana dashboards read these tables: O1 KPIs and O1 Alarms (Read O1 KPIs and Alarms).
The collector is ready as soon as it listens. The adapters do not retry a failed
delivery, so the collector accepts first and inserts when ClickHouse answers, from
a bounded queue; /status on the collector reports what it accepted, stored and
dropped.
The controller is the other O1 client Racora runs: it reads every element's datastore, reports on each cell whether the datastore still holds what the cell declares, and carries a command line that reads and changes it (Change an Element's Configuration over O1).
What Racora does not provide: orchestration (the controller stays the only thing that deploys and changes the network, driven by the NRCell), an inventory, a graphical NETCONF client, and the file-based performance reporting a full SMO expects. The chart installs no SDN-R, no Kafka, no identity provider.
How an Element Is Managed
Every element pod, the DUs' and the CU planes', carries the element's O1 pair next to the gNB container, started in this order:
o1-scaffoldcopies the DU profile's scaffold out of the NETCONF server image: the 3GPP NRM structure with every mandatory field.o1-seedturns the DU config the controller rendered into the element's seed on that scaffold, with the adapter's own converter; the managed element takes the cell's name, and the seed carries theracora-defaultperformance job that streams every metric to the collector.netconf, the O1 NETCONF server, starts from the seed and serves the chart-generated account over SSH on the element's Service, port 830.o1, the adapter in the DU role, renders the gNB's configuration file from the datastore and keeps it in step: a parameter the gNB takes at runtime goes over the gNB's own websocket; any other change makes the adapter ask the gNB to restart, and the kubelet restarts the gNB container alone (the pod and the datastore stay).- The gNB reads the rendered file.
The NRCell stays the source of truth. The controller renders the same
configuration as before; the seed is made from it at every pod start, so a pod
restart returns the datastore to what the cell declares. Fields the NRCell does
not carry (what the controller passes outside the configuration: node facts,
telemetry endpoints) never enter the datastore. The lock and bar of a cell and
the mobility commands stay on the CU-CP's websocket (Runtime
Commands). racora-controller.o1.enabled: false renders the DU pod without the pair, the gNB reading the controller's
ConfigMap directly, racora-cu.o1.enabled: false the CU planes.
The seed carries exactly what the controller declares. The converter overlays
the controller's YAML onto the scaffold and prunes every leaf the YAML has no
key for, so a parameter the NRCell does not set never reaches the datastore and
the gNB applies its own default, as it does without O1. Two kinds of key need
more. A key the models have no leaf for (the one cell's band, two log flags)
rides the residual file the DU applies after the rendered file. A leaf the
models make mandatory stays from the scaffold whatever the YAML says, which is
why the controller declares the gNB identity itself: gnb_id and
gnb_id_bit_length (every cell's gnbId and the 22 bits its NCIs are built
on) are written into the CU-CP overlay and every DU configuration, so the CU-CP,
the DUs and the controller's NCIs agree whatever identity the scaffold carries;
the CU-CP's RAN node name toward the core is the element's, cu-cp.
The CU planes differ in one way. Their configuration is two files, the chart's base file and the controller's overlay (for the CU-CP: the AMF, the binds, the cell count, mobility, the logical cells). The seed is made from the two merged, and the plane starts on its base file, the rendered file and the overlay again, in that order, so what the controller owns stays the controller's whatever the datastore says; the scaffold's mobility never enters the seed. A DU does the same with what the datastore has no leaf for yet: its band comes back as the cell default after the rendered file.
Changes over O1
From the start of its pod on, the datastore is the element's configuration, and a NETCONF client may change it: the controller's command line or an external SMO. The adapter applies what changed. A performance job is applied at runtime; a change the gNB reads only at start makes the adapter restart the gNB container, the pod and its datastore staying.
The controller reads every element's datastore every
racora-controller.o1.checkSeconds (30 seconds by default) and keeps what it read in a
ConfigMap o1-snapshot-<element> in the element's namespace: the first reading
after the element's NETCONF server started, which is the seed, and the latest. It
tells two kinds of change apart:
- A leaf the seed carries that was changed or removed is drift. It is in
effect until the pod starts again; the new pod's seed is made from the NRCell, so
the declared value comes back. The cell reports
O1Config=False/Driftedwith the leaves named, and a Warning EventO1ConfigDrifted. To keep a value, change the NRCell. - A leaf the seed does not carry is an addition, a performance job an SMO
configures for instance. After a pod start the controller merges the additions
back into the new datastore, except where the new seed now carries the leaf: the
NRCell wins. The cell stays
O1Config=True/InSyncand its message counts them.
The additions carried over are those of the latest reading, so one made less than an
interval before the pod starts again is lost. O1Ready says whether the element's
server answers with its datastore at all (NRCell Status).
The CU planes have no NRCell: the controller reads, keeps and merges back their
datastores the same way, and logs their state when it changes.
Which Elements Send
Every DU's o1 adapter sends its faults and the racora-default job's
performance stream to the collector under the cell's name. An Open Fronthaul
cell's M-plane sidecar sends too (How a Radio Comes Up):
its own alarms (the session to the O-RU lost or degraded) and the O-RU's own
faults, under the same name.
How an Element Is Identified
The adapters send one fixed identity in the event body today. The name Racora
wants on every row is the element's: the cell's name for a DU and its O-RU,
cu-cp and cu-up for the CU planes. The controller passes that name to each
adapter as the VES basic-auth username, and the collector records it as the
element (racora-smo.collector.ves.auth: identity). When an event body carries an
element name of its own, the body wins. A PM envelope's element is its
?element= query parameter, else its nfInstanceId.
Rows whose element could not be identified are kept under unknown; the O1
Alarms dashboard counts them.
Attaching an External SMO
The collector's address is one umbrella value, global.o1.ves.url. Point it at
another VES collector and every adapter the distribution runs posts there instead,
with the element's name as the username; set smo.enabled: false
in the umbrella to drop Racora's collector. The adapters do not validate the
collector's certificate in this release, and they send no heartbeat; a collector
that is down loses the events sent meanwhile.
The elements' NETCONF endpoints are the element Services on port 830, inside
the cluster: du-<cell> in the DU namespace, cu-cp and cu-up in the CU
namespace, with the account in the o1-netconf-account Secret of each
namespace; an external client reaches them through a port-forward or an ingress
you add. A Service routes only to a ready pod, so an element's server answers there
once the whole pod is ready (for a DU, once its gNB runs); the controller dials the
pod's own address and reaches the server as soon as it starts. What it changes is a change over O1 like any other: drift until the pod
starts again, additions kept.