DEV Community

Nikita Vakula
Nikita Vakula

Posted on

QEMU QAPI Client for Go — Native Code-Gen Straight from QEMU

Ever glued together socat, raw JSON, and a prayer just to talk to QEMU from Go?

I did—until one missing comma killed an overnight CI run. So I bolted a Go backend onto QEMU’s own QAPI generator, and now every VM call is type-safe, fully async, and always in sync with upstream. ➡️ Check out the project on GitHub

Why I Built This

QEMU already ships a rich JSON interface (QAPI), yet Go developers still end up:

  • Writing raw JSON by hand – easy to mistype, hard to test.
  • Chasing partial wrappers – most cover only “common” commands.
  • Fixing silent schema drift.

What You Get

  • Fully typed bindings for every command, struct, and enum in qapi-schema.json.
  • Async events & requests via goroutines and channels—no polling loops.
  • Transport helpers for QMP, QGA, serial pipes, TCP, stdio… the lot.
  • Schema-sync guarantee – regenerate on each QEMU bump; drift becomes impossible.

The generated Go code is structured per schema module and works out of the box with a reusable core client.

🚀 Example

  • Generate Go bindigs:
./generate.sh --schema qemu/qapi/qapi-schema.json --out-dir generated --package qapi
Enter fullscreen mode Exit fullscreen mode
  • Use the generated client in Go:
monitor, monitorErr := client.NewMonitor()
if monitorErr != nil {
    return monitorErr
}

defer monitor.Close()
msgCh := monitor.Start()

monitor.Add("example instance", socketPath)
if req, reqErr := qapi.PrepareQmpCapabilitiesRequest(qapi.QObjQmpCapabilitiesArg{}); reqErr == nil {
    if ch, chErr := monitor.Execute("example instance", client.Request(*req)); chErr == nil {
        res := <-ch // capabilities negotiated
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Listen to events:
for msg := range msgCh {
    log.Printf("event: %+v\n", msg)
}
Enter fullscreen mode Exit fullscreen mode

Because every struct and enum comes straight from the schema, your IDE autocompletes everything, and compile-time checks catch mismatched fields long before QEMU ever sees the request.

A runnable sample lives in /example.

Bonus: Custom QEMU Builds Welcome

Running a patched QEMU with extra QAPI entries? No problem—point the generator at your fork’s qapi-schema.json and commit the result. Add one GitHub Actions step:

- name: Regenerate QAPI bindings
  run: |
    ./generate.sh \
      --schema ./qemu-fork/qapi/qapi-schema.json \
      --out-dir ./generated \
      --package qapi
  shell: bash
Enter fullscreen mode Exit fullscreen mode

If the schema changes, the action yields a clean diff and your pull request instantly shows whether anything downstream breaks.

Wrapping Up

Stop juggling JSON and second-guessing upgrades—let QEMU’s own generator keep your Go client perfectly aligned. Star the repo, open an issue, or submit a PR if you spot something missing. Your VMs (and future, better-rested self) will thank you.

Top comments (0)