GSoC 2026: Event-Driven Automation for Uyuni via MQTT and Node-RED
Google Summer of Code 2026 · openSUSE Project
Contributor: Geetansh Goyal (@geetxnshgoyal) Mentors: Ondřej Holeček, Abid Mehmood Organisation: openSUSE · Uyuni
Deliverables
| What | Link | Status |
|---|---|---|
| RFC, the design and topic scheme | uyuni-rfc#119 | Open, under review |
| Implementation, publisher plus containers plus Node-RED nodes | uyuni#12385 | Open, under review |
Those two pull requests are the complete record of the work. The code PR has 13 commits touching the Java core, two container images and a Node-RED package.
The problem I set out to solve
Uyuni knows a lot about the machines it manages. It knows the moment a system registers, when a Salt job comes back, when states get applied, when an image is deployed. The catch is that none of it ever left the server.
If you wanted to automate on any of that, you had one option: poll the XML-RPC API on a timer and diff the results. That is wasteful at scale, it adds however much latency your polling interval happens to be, and it quietly misses anything that starts and finishes between two polls.
I wanted Uyuni to be able to say “this just happened” out loud, in a format other tools could understand without knowing anything about how Uyuni works inside.
What I built
An MQTT event publisher inside Uyuni’s Java core
The new com.suse.manager.reactor.mqtt package publishes nine event types
asynchronously over Eclipse Paho.
Five come from the Salt reactor, hooked in as a MessageAction next to the
handlers that were already there:
systems/registered · jobs/returned · states/applied · images/deployed · batches/started
The other four are published straight from the domain code:
orgs/created · users/created · clm/build-started · clm/build-completed
A few decisions worth calling out:
Topics carry the server’s FQDN, so they look like
uyuni/<fqdn>/<category>/<event>. Several Uyuni servers can then share one
broker, and a subscriber can still narrow down to one of them with uyuni/+/....
Every message uses the same envelope: an eventId, a UTC timestamp, the
topic, and a data payload. Whatever the event is, a consumer parses one shape.
Adding an event type means adding a class. The MqttEvent interface owns both
the topic suffix and the payload, so nobody has to extend a switch statement to
add the tenth event.
Nothing publishes until the transaction commits. An event announcing a change that then gets rolled back is worse than no event at all.
It is off unless you turn it on. uyuni.mqtt.enabled defaults to false, so a
server that does not want MQTT opens no connection and starts no threads.
The queue is bounded. If the broker goes away, the publisher drops the oldest queued message rather than growing until something falls over.
A Node-RED node package
node-red-contrib-uyuni gives administrators four nodes, so building automation
is a matter of dragging boxes around rather than writing a consumer from scratch.
| Node | What it does |
|---|---|
uyuni-events |
Subscribes to Uyuni events and unwraps the envelope onto msg |
uyuni-config |
Shared broker connection and credentials |
uyuni-action |
Acts back on Uyuni: apply highstate, reboot, install packages |
uyuni-query |
Reads from Uyuni: systems, errata, packages, channels |
There are 59 unit tests at 94.65% statement coverage, using
node-red-node-test-helper with the XML-RPC layer stubbed, so the suite runs
without a Uyuni server anywhere nearby.
Two container images
containers/mqtt-broker-image is a Mosquitto broker set up the way Uyuni wants
it: anonymous access off, and an ACL giving uyuni-publisher write-only and
uyuni-subscriber read-only access to uyuni/#.
containers/node-red-image is Node-RED with the Uyuni nodes already installed.
It refuses to start unless you supply a bcrypt admin password hash or explicitly
disable the editor. That is deliberate: the Node-RED admin API can deploy flows,
and a flow can run arbitrary code, so an unprotected editor is remote code
execution on the host.
Documentation
A deployment guide for the whole stack, an architecture README for the Java side,
and a reference for every uyuni.mqtt.* property.
What I verified
I did not want to hand in something that only worked on my laptop, so I deployed it onto a real Uyuni server.
Latency came out at 0.112 seconds, from a Salt job finishing to the event arriving at a subscriber.
Real events from a live Uyuni 2026.08 instance. Creating a user produced:
uyuni/server.local/users/created
{"eventId":"cd7a0eda-c4ba-4f5e-b0ad-3b62f9592cd1",
"data":{"userId":4,"orgId":1,"username":"gdgd"},
"timestamp":"2026-08-18T10:33:14.846Z"}
Node-RED picked those events up through the uyuni-events node and drove an
action off the back of them.
Deploying properly also turned up three bugs that code review had not:
- The FQDN lookup was returning the container’s internal hostname, which would have made every containerised Uyuni publish under the same topic prefix. The whole reason for putting the FQDN in the topic would have been lost.
- The branch needed a database schema newer than the released container image. The mismatch failed a route validator, which failed the entire web application, which put Tomcat into a restart loop.
- Moving the connection log lines from
warntoinfomade them completely invisible, because the default root logger is set towarn.
Not one of those was visible in a diff.
What is still left
Writing this down plainly, because whoever picks it up next deserves to know.
Review is still in progress. Both PRs are open. Every one of the 16 review comments so far has been addressed.
The broker container will not build from SLE_BCI yet, because bci-base has
no mosquitto package. Getting the OBS repository configured is being handled on
the openSUSE side.
The Node-RED image build inside OBS is an open question. It builds fine locally, but satisfying the npm dependency fetch in an offline build environment has not been worked out.
No Cucumber acceptance tests. The Java unit tests and the Node-RED suite cover the pieces, but there is nothing end to end in Uyuni’s acceptance suite.
Where the Node-RED package should be published is undecided: npm, an openSUSE package, or both.
TLS to the broker. Username and password auth is done. Client certificates were deliberately left for later and are noted in the RFC.
What I took away from it
The lesson that stuck was that being correct often comes down to ordering rather than logic. Abid pointed out in review that my events were going out before the database commit that produced them. My first fix, deferring publication until the transaction completed, looked right and was not enough, because Uyuni gives every handler its own transaction. The real fix was where the action got registered: it had to come after the handlers that actually write the row.
The other lesson was that passing tests and working software are not the same thing. Everything was green before those three deployment bugs turned up in the first couple of hours on a real server.
Thanks
To Ondřej and Abid, for reviews that went after real problems instead of stopping at style, and for being patient with someone working in a codebase this size for the first time. And to openSUSE generally, for making a first-year student feel like a contributor rather than a visitor.