Skip to main content

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 in o1.alarms, one per raise, change or clear, and the view o1.current_alarms lists 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:

  1. o1-scaffold copies the DU profile's scaffold out of the NETCONF server image: the 3GPP NRM structure with every mandatory field.
  2. o1-seed turns 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 the racora-default performance job that streams every metric to the collector.
  3. netconf, the O1 NETCONF server, starts from the seed and serves the chart-generated account over SSH on the element's Service, port 830.
  4. 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).
  5. 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/Drifted with the leaves named, and a Warning Event O1ConfigDrifted. 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/InSync and 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.