A small page hosted on the Raspberry Pi that shows what usbip devices are exported and lets an operator flush a stale export (unbind → rebind) without physically reaching the bus.
It exists to supply the one operation the DRA driver can't: recovering a device
left wedged in exported/used state after a node client dies uncleanly (the
Device busy (exported) / FailedPrepareDynamicResources crash loop). Flush
the device on the Pi and the driver's own retry loop reattaches it — the cluster
heals itself.
The Pi can only see mechanical state, never cluster intent, so the page is deliberately honest about its blind spot:
- available (status 1) — in the pool. Unambiguous.
- in use (status 2, no extra evidence) — could be a healthy attachment or a stale ghost; the status value alone can't tell them apart. Flushable, but the UI asks you to confirm a node is actually crash-looping first.
- likely stale (amber) — status 2 plus either a surplus (more devices in
use than there are live connections, so at least one is provably a ghost) or
repeated attach failures naming that busid in
usbipd's own journal.
The operator bridges Pi state and cluster intent — read the failing claim / ArgoCD error for the busid, then flush. The page never touches Kubernetes.
System pip is absent and Python is externally-managed, so use a venv:
sudo apt install -y python3-venv # if venv creation complains
git clone <this> /home/local/usbip-web # or copy the files there
cd /home/local/usbip-web
python3 -m venv venv
./venv/bin/pip install fastapi uvicornRun it directly to try it:
./venv/bin/uvicorn app:app --host 0.0.0.0 --port 8080Then visit http://<pi-ip>:8080/.
sudo cp usbip-web.service /etc/systemd/system/
# EDIT the file first: pin --host to the Pi's beamline IP, fix paths/user.
sudo systemctl daemon-reload
sudo systemctl enable --now usbip-webFakeBackend serves a representative device set (the real ambiguous beamline
case plus a nested extender hub) and mutates on flush, so the whole UI works on
a laptop:
./venv/bin/python app.py --fake # or USBIP_WEB_FAKE=1 uvicorn app:app ...python3 test_usbip.pyAll logic runs against FakeBackend, so no hardware is needed. The only thing
to verify on a real Pi is that RealBackend's five primitives read the box
correctly (sysfs status, ss connection count, journalctl lines, device
descriptors, and the usbip flush).
No authentication — being on the beamline network is the gate. That is only safe because the only privileged action exposed is flushing a device the Pi currently sees. The real boundary is in the code, not a login page:
- the busid is validated against the live device set before any
usbipcommand runs (a busid the Pi doesn't currently see is refused); - commands are always argv lists, never a shell — no user string reaches a shell.
This matters because the service account has passwordless-root sudo, so input
validation is what keeps "no auth" safe. Hardening worth doing: narrow that
sudo to just the two usbip commands rather than ALL, e.g.
local ALL=(root) NOPASSWD: /usr/sbin/usbip unbind -b *, /usr/sbin/usbip bind -b *
so even a bug in the app can't run arbitrary root commands.
backend.py— the system seam:UsbipBackend+RealBackend+FakeBackendusbip.py— pure logic: topology tree, classification, log correlationapp.py— FastAPI: the page + three endpointsindex.html— the operator pagetest_usbip.py— unit testsusbip-web.service— systemd unit