Hands-on labs are course-buyer only

These 5 hands-on labs are bonus content for students of the O-RAN Course. Enroll once (₹1,799 lifetime) to unlock all labs forever.

Get the course — ₹1,799 Already enrolled? Sign in
O
O-RAN Labs
All labs
LAB 02 · INTERMEDIATE

Write your first xApp in Python

~45 minutes Python 3.9+ Requires Lab 01
What you'll do. Clone the O-RAN SC Python xApp framework, write a tiny hello-xapp that subscribes to E2 indications from a simulated E2 Node, package it into a container, and onboard it via dms_cli to the Near-RT RIC you stood up in Lab 01.

0Prerequisites

1Clone the xApp framework

git clone "https://gerrit.o-ran-sc.org/r/ric-plt/xapp-frame-py" xapp-frame-py
cd xapp-frame-py
git checkout bronze
pip install -e .

2Write hello-xapp.py

Create a new directory and write the xApp:

mkdir hello-xapp && cd hello-xapp
cat > hello_xapp.py <<'EOF'
from ricxappframe.xapp_frame import Xapp, RMRXapp, rmr

class HelloXapp(Xapp):
    def __init__(self):
        super().__init__(entrypoint=self.entry, rmr_port=4560, use_fake_sdl=False)
        self.logger.info("hello-xapp instantiated")

    def entry(self, sdl):
        self.logger.info("xApp main loop started")
        while True:
            for msg in self.rmr_get_messages():
                self.logger.info(f"got message: mtype={msg.mtype} len={len(msg.payload)}")
                msg.payload = b"ACK from hello-xapp"
                self.rmr_send(msg)
            sleep(1)

if __name__ == "__main__":
    HelloXapp().run()
EOF

3Add the xApp descriptor

Every xApp needs a JSON descriptor that tells the RIC platform what RMR messages it handles and what config it needs. Create config-file.json:

cat > config-file.json <<'EOF'
{
  "xapp_name": "hello-xapp",
  "version": "1.0.0",
  "containers": [{
    "name": "hello-xapp",
    "image": {"registry":"docker.io","name":"hello-xapp","tag":"1.0.0"}
  }],
  "messaging": {
    "ports": [{
      "name": "rmr-data",
      "container": "hello-xapp",
      "port": 4560,
      "rxMessages": ["RIC_SUB_RESP","RIC_INDICATION"],
      "txMessages": ["RIC_SUB_REQ","RIC_CONTROL_REQ"],
      "policies": [],
      "description": "RMR data port for xApp"
    }]
  },
  "metrics": [],
  "rmr": {"protPort":"tcp:4560","maxSize":2072,"numWorkers":1,"txMessages":["RIC_SUB_REQ"],"rxMessages":["RIC_INDICATION"]}
}
EOF

4Containerize

cat > Dockerfile <<'EOF'
FROM python:3.10-slim
WORKDIR /app
RUN pip install ricxappframe
COPY hello_xapp.py config-file.json /app/
ENV CONFIG_FILE=/app/config-file.json
CMD ["python3","-u","hello_xapp.py"]
EOF

docker build -t hello-xapp:1.0.0 .
kind load docker-image hello-xapp:1.0.0 --name oran-ric

The kind load step makes the image available inside the kind cluster.

5Onboard via dms_cli

The xApp Onboarder service (in the RIC) accepts xApp packages via its REST API. Use dms_cli:

pip install xapp-dms-cli
kubectl -n ricplt port-forward svc/service-ricplt-xapp-onboarder-http 8888:8888 &
dms_cli onboard --config_file_path=./config-file.json --shcema_file_path=./schema.json

If onboarding succeeds, the xApp appears in the catalog:

dms_cli get_charts_list

6Deploy

dms_cli install --xapp_chart_name=hello-xapp --version=1.0.0 --namespace=ricxapp

Then verify it's running:

kubectl -n ricxapp get pods

Expected: deployment-ricxapp-hello-xapp-... 1/1 Running

7Watch the xApp logs

kubectl -n ricxapp logs -f deployment/deployment-ricxapp-hello-xapp

You'll see hello-xapp instantiated and xApp main loop started. Since there's no E2 Node attached yet, no messages flow. We'll attach one in Lab 03.

Checkpoint

Real-world noteProduction xApps subscribe to a specific RAN Function ID via E2SM-KPM or E2SM-RC. Our hello-xapp skips subscription — it just listens. Lab 03 shows how to inspect the actual E2AP exchange.
Lab 03 · Up next

Capture E2AP traffic in Wireshark

Install dissector · attach a simulated E2 Node · walk through E2 Setup + Subscription messages

Continue