Skip to the content.

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:

  1. 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.
  2. 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.
  3. Moving the connection log lines from warn to info made them completely invisible, because the default root logger is set to warn.

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.