[PATCH 05/13] qemu: install under the controller data path

Daniel Gomez <[email protected]> Fri, 12 Jun 2026 14:36:43 +0200
Newsgroups dev.linux.lists.kdevops
Message-ID <[email protected]>
From: Daniel Gomez <[email protected]>

Build QEMU out of tree and install it under a qemu-destdir/ prefix in
the controller data path, which the invoking user owns, so the install
needs no root and the binary stays out of confined system locations.
The fetched tree, the out-of-tree build directory and the destdir now all sit
side by side under KDEVOPS_CONTROLLER_DATA_PATH.

The libvirt backend is the exception: its QEMU runs under libvirtd's
AppArmor and SELinux confinement, which only permits binaries under
/usr/local, so libvirt keeps the /usr/local prefix and the sudo install.
QEMU_INSTALL_DIR and QEMU_INSTALL_NEEDS_SUDO capture this per-backend,
and the install task becomes conditional on the latter. The role default
for qemu_install_needs_sudo is a plain false rather than a
self-reference, because a false bool is omitted from extra_vars and a
self-referential default would recurse; the libvirt backend emits true
and overrides it.

QEMU_BIN_PATH derives from QEMU_INSTALL_DIR so the configure prefix and
the path the qemu role verifies and qsu execs share one source of truth.

Generated-by: Claude AI
Signed-off-by: Daniel Gomez <[email protected]>
---
 kconfigs/Kconfig.qemu                  | 49 ++++++++++++++++++++++++++--------
 playbooks/roles/qemu/defaults/main.yml |  9 ++++---
 playbooks/roles/qemu/tasks/main.yml    | 27 ++++++++++++++-----
 3 files changed, 64 insertions(+), 21 deletions(-)

diff --git a/kconfigs/Kconfig.qemu b/kconfigs/Kconfig.qemu
index b19ef8c0..549b322b 100644
--- a/kconfigs/Kconfig.qemu
+++ b/kconfigs/Kconfig.qemu
@@ -58,11 +58,12 @@ config QEMU_GIT
 config QEMU_GIT_DATA_PATH
 	string "The destination directory where to fetch the QEMU git tree"
 	output yaml
-	default "{{local_dev_path}}/qemu"
+	default "{{ kdevops_controller_data_path }}/qemu"
 	help
 	  This is the target location of where to fetch the above git tree.
-	  Note that {{local_dev_path}} corresponds to the location set by the
-	  configuration option CONFIG_NEEDS_LOCAL_DEVELOPMENT_PATH.
+	  Defaults to a qemu/ directory under KDEVOPS_CONTROLLER_DATA_PATH,
+	  the controller-side data path, alongside the out-of-tree build
+	  directory (qemu-build/) and the install destdir (qemu-destdir/).
 
 config QEMU_GIT_VERSION
 	string "The version of QEMU to build"
@@ -89,18 +90,44 @@ config QEMU_TARGET
 	  QEMU --target-list value derived from the target architecture.
 	  Consumed by the qemu role to configure the build.
 
+config QEMU_INSTALL_DIR
+	string
+	output yaml
+	default "/usr/local" if LIBVIRT
+	default "{{ kdevops_controller_data_path }}/qemu-destdir"
+	help
+	  The install prefix passed to QEMU's configure (--prefix). The
+	  libvirt backend installs into /usr/local so the system QEMU under
+	  libvirtd's AppArmor/SELinux confinement can execute it. Every other
+	  backend installs into a qemu-destdir/ under the controller data
+	  path, which the invoking user owns, so the install needs no sudo
+	  and the binary stays out of confined system locations.
+
+config QEMU_INSTALL_NEEDS_SUDO
+	bool
+	output yaml
+	default y if LIBVIRT
+	default n
+	help
+	  Whether installing the built QEMU needs root. The /usr/local prefix
+	  used by the libvirt backend does; the per-user qemu-destdir prefix
+	  used by the other backends does not.
+
 endif # QEMU
 
 config QEMU_BIN_PATH
 	string
 	output yaml
+	default "{{ qemu_install_dir }}/bin/qemu-system-x86_64" if QEMU && TARGET_ARCH_X86_64
+	default "{{ qemu_install_dir }}/bin/qemu-system-aarch64" if QEMU && TARGET_ARCH_ARM64
+	default "{{ qemu_install_dir }}/bin/qemu-system-ppc64" if QEMU && TARGET_ARCH_PPC64LE
 	default QEMU_BIN_PATH_LIBVIRT if LIBVIRT
-	default "/usr/local/bin/qemu-system-aarch64" if TARGET_ARCH_ARM64
-	default "/usr/local/bin/qemu-system-ppc64" if TARGET_ARCH_PPC64LE
-	default "/usr/local/bin/qemu-system-x86_64"
+	default "/usr/bin/qemu-system-x86_64"
 	help
-	  Absolute path to the QEMU system emulator binary. The libvirt
-	  backend resolves this from QEMU_BIN_PATH_LIBVIRT. Every other
-	  backend uses the binary installed by the qemu role under its
-	  install prefix. This single source of truth feeds gen_nodes for
-	  the libvirt domain emulator and qsu for the systemd unit ExecStart.
+	  Absolute path to the QEMU system emulator binary. When QEMU=y this
+	  derives from QEMU_INSTALL_DIR so the install prefix and the consumed
+	  binary path share one source of truth; both Jinja2 references
+	  resolve at Ansible-use time, so the qemu role and qsu render a
+	  fully-expanded path. The plain-libvirt case with no QEMU build
+	  resolves it from QEMU_BIN_PATH_LIBVIRT for the gen_nodes domain
+	  emulator.
diff --git a/playbooks/roles/qemu/defaults/main.yml b/playbooks/roles/qemu/defaults/main.yml
index 806d5375..31671d78 100644
--- a/playbooks/roles/qemu/defaults/main.yml
+++ b/playbooks/roles/qemu/defaults/main.yml
@@ -1,11 +1,14 @@
 # SPDX-License-Identifier: copyleft-next-0.3.1
 ---
 
+qemu_install_dir: "{{ qemu_install_dir | default('/usr/local') }}"
+qemu_install_needs_sudo: false
+
 # Forces to build and install even if the binary is already present
 qemu_force_install_if_present: false
-qemu_bin_path: "{{ qemu_bin_path | default('/usr/local/bin/qemu-system-x86_64') }}"
-qemu_data: "{{ qemu_git_data_path | default(local_dev_path + '/qemu') }}"
+qemu_bin_path: "{{ qemu_bin_path | default(qemu_install_dir + '/bin/qemu-system-x86_64') }}"
+qemu_data: "{{ qemu_git_data_path | default(kdevops_controller_data_path + '/qemu') }}"
 qemu_git: "{{ qemu_git | default('https://github.com/qemu/qemu.git') }}"
 qemu_version: "{{ qemu_git_version | default('v7.2.0-rc4') }}"
-qemu_build_dir: "{{ qemu_data }}/build"
+qemu_build_dir: "{{ kdevops_controller_data_path }}/qemu-build"
 qemu_target: "{{ qemu_target | default('x86_64-softmmu') }}"
diff --git a/playbooks/roles/qemu/tasks/main.yml b/playbooks/roles/qemu/tasks/main.yml
index 005d8637..015c4d15 100644
--- a/playbooks/roles/qemu/tasks/main.yml
+++ b/playbooks/roles/qemu/tasks/main.yml
@@ -36,9 +36,9 @@
     - qemu_force_install_if_present|bool or not qemu_present.stat.exists
   tags: vars
 
-- name: Ensure the local development path exists
+- name: Ensure the controller data path exists
   ansible.builtin.file:
-    path: "{{ local_dev_path }}"
+    path: "{{ kdevops_controller_data_path }}"
     state: directory
     mode: "0755"
   tags: ["qemu", "build-deps"]
@@ -63,6 +63,15 @@
   when:
     - qemu_build_now|bool
 
+- name: Create the out-of-tree build directory
+  ansible.builtin.file:
+    path: "{{ qemu_build_dir }}"
+    state: directory
+    mode: "0755"
+  tags: ["qemu", "build-deps"]
+  when:
+    - qemu_build_now|bool
+
 - name: Pre-fetch QEMU subprojects so configure can disable downloads
   ansible.builtin.command: "meson subprojects download"
   changed_when: true
@@ -73,11 +82,15 @@
     - qemu_build_now|bool
 
 - name: Run configure for QEMU
-  ansible.builtin.command: "./configure --target-list={{ qemu_target }} --disable-download"
+  ansible.builtin.command: >-
+    {{ qemu_data }}/configure
+    --target-list={{ qemu_target }}
+    --prefix={{ qemu_install_dir }}
+    --disable-download
   changed_when: true
   tags: ["qemu", "configure"]
   args:
-    chdir: "{{ qemu_data }}"
+    chdir: "{{ qemu_build_dir }}"
   when:
     - qemu_build_now|bool
 
@@ -91,19 +104,19 @@
 
 - name: Build QEMU
   community.general.make:
-    chdir: "{{ qemu_data }}"
+    chdir: "{{ qemu_build_dir }}"
     jobs: "{{ qemu_nproc.stdout }}"
   tags: ["qemu", "build"]
   when:
     - qemu_build_now|bool
 
 - name: Install QEMU
-  become: true
+  become: "{{ qemu_install_needs_sudo | bool }}"
   become_method: ansible.builtin.sudo
   ansible.builtin.command: "{{ make }} install"
   changed_when: true
   args:
-    chdir: "{{ qemu_data }}"
+    chdir: "{{ qemu_build_dir }}"
   tags: ["qemu", "install"]
   when:
     - qemu_build_now|bool

-- 
2.54.0