[PATCH 2/8] nixos: consume nixos-qemu modules via a per-node default.nix

Daniel Gomez <[email protected]> Thu, 23 Apr 2026 00:48:40 +0200
Newsgroups dev.linux.lists.kdevops
Message-ID <20260423-kdevops-series-b-nixos-qemu-v1-2-209154ae54f8@samsung.com>
From: Daniel Gomez <[email protected]>

Collapse every kdevops-owned jinja template that carried inline
NixOS configuration into a single per-node default.nix.j2 that
imports modules from scripts/nixos-qemu. The library becomes the
single source of truth for what a NixOS guest looks like;
kdevops only renders per-node overrides (hostname, SSH keys,
workflow enablement, cache mirror, optional 9p share). The
Generate NixOS configurations play creates one directory per
guest, copies the subtree's libvirt template flake, rewrites its
nixos-qemu.url to resolve at the checked-in subtree, and renders
default.nix.j2 alongside.

Generated-by: Claude AI
Signed-off-by: Daniel Gomez <[email protected]>
(cherry picked from commit eb20b00dd5161197aea3490803cd97a06e8fb81d)
---
 kconfigs/Kconfig.nixos                             |   7 +-
 playbooks/nixos.yml                                |  61 +++++----
 playbooks/templates/nixos/configuration.nix.j2     | 138 ---------------------
 playbooks/templates/nixos/default.nix.j2           |  69 +++++++++++
 playbooks/templates/nixos/flake.nix.j2             |  38 ------
 .../templates/nixos/hardware-configuration.nix.j2  |  42 -------
 playbooks/templates/nixos/vms.nix.j2               |  45 -------
 playbooks/templates/nixos/workflow-deps.nix.j2     | 127 -------------------
 8 files changed, 100 insertions(+), 427 deletions(-)

diff --git a/kconfigs/Kconfig.nixos b/kconfigs/Kconfig.nixos
index 196bdafd..622b4492 100644
--- a/kconfigs/Kconfig.nixos
+++ b/kconfigs/Kconfig.nixos
@@ -10,12 +10,7 @@ config NIXOS_STORAGE_DIR
 config NIXOS_CONFIG_DIR
 	string
 	output yaml
-	default "{{ topdir_path }}/nixos"
-
-config NIXOS_GENERATION_DIR
-	string
-	output yaml
-	default "{{ nixos_config_dir }}/generated"
+	default "{{ topdir_path }}/scripts/nixos-qemu/configurations"
 
 config NIXOS_USE_FLAKES
 	bool "Use Nix flakes for configuration"
diff --git a/playbooks/nixos.yml b/playbooks/nixos.yml
index d6aa2283..007c9033 100644
--- a/playbooks/nixos.yml
+++ b/playbooks/nixos.yml
@@ -57,16 +57,22 @@
     - "{{ playbook_dir }}/../extra_vars.yaml"
   tags: generate-configs
   tasks:
-    - name: Create NixOS directories
+    - name: Create top-level NixOS directories
       ansible.builtin.file:
         path: "{{ item }}"
         state: directory
         mode: '0755'
       loop:
         - "{{ nixos_config_dir }}"
-        - "{{ nixos_generation_dir }}"
         - "{{ nixos_storage_dir }}"
 
+    - name: Create per-node configuration directories
+      ansible.builtin.file:
+        path: "{{ nixos_config_dir }}/{{ item }}"
+        state: directory
+        mode: '0755'
+      loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}"
+
     - name: Ensure SSH key exists for configuration
       block:
         - name: Determine SSH key path based on directory
@@ -110,41 +116,33 @@
         - detected_mirror_url.stdout is defined
         - detected_mirror_url.stdout | trim != ""
 
-    - name: Template base NixOS configuration
-      ansible.builtin.template:
-        src: nixos/configuration.nix.j2
-        dest: "{{ nixos_generation_dir }}/configuration.nix"
-        mode: '0644'
-
-    - name: Template hardware configuration
-      ansible.builtin.template:
-        src: nixos/hardware-configuration.nix.j2
-        dest: "{{ nixos_generation_dir }}/hardware-configuration.nix"
-        mode: '0644'
-
-    - name: Generate workflow dependencies configuration
-      ansible.builtin.template:
-        src: nixos/workflow-deps.nix.j2
-        dest: "{{ nixos_generation_dir }}/workflow-deps.nix"
-        mode: '0644'
-      when: nixos_enable_workflow_deps | bool
-
     - name: Debug SSH key path
       ansible.builtin.debug:
         msg: "Using SSH key: {{ hostvars['localhost']['nixos_ssh_key_path'] | default('NOT SET') }}"
 
-    - name: Generate VM definitions
-      ansible.builtin.template:
-        src: nixos/vms.nix.j2
-        dest: "{{ nixos_generation_dir }}/vms.nix"
+    - name: Copy nixos-qemu libvirt template flake to each per-node directory
+      ansible.builtin.copy:
+        src: "{{ topdir_path }}/scripts/nixos-qemu/templates/libvirt/flake.nix"
+        dest: "{{ nixos_config_dir }}/{{ item }}/flake.nix"
+        remote_src: true
         mode: '0644'
+      loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}"
 
-    - name: Generate flake.nix if enabled
+    - name: Point nixos-qemu input at the local subtree in each per-node flake
+      ansible.builtin.replace:
+        path: "{{ nixos_config_dir }}/{{ item }}/flake.nix"
+        regexp: '^(\s*)nixos-qemu\.url = "path:/path/to/nixos-qemu";'
+        replace: '\1nixos-qemu.url = "path:{{ topdir_path }}/scripts/nixos-qemu";'
+      loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}"
+
+    - name: Render per-node default.nix
       ansible.builtin.template:
-        src: nixos/flake.nix.j2
-        dest: "{{ nixos_config_dir }}/flake.nix"
+        src: nixos/default.nix.j2
+        dest: "{{ nixos_config_dir }}/{{ item }}/default.nix"
         mode: '0644'
-      when: nixos_use_flakes | bool
+      loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}"
+      vars:
+        vm_name: "{{ item }}"
 
 # The setup phase is integrated into generate-configs to ensure SSH keys are available
 
@@ -528,10 +526,11 @@
         path: "{{ nixos_storage_dir }}/extra-drives"
         state: absent
 
-    - name: Clean up generated NixOS configuration
+    - name: Clean up per-node generated NixOS configurations
       ansible.builtin.file:
-        path: "{{ nixos_generation_dir }}"
+        path: "{{ nixos_config_dir }}/{{ item }}"
         state: absent
+      loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}"
 
     - name: Garbage collect cached NixOS disk images from Nix store
       ansible.builtin.shell: |
diff --git a/playbooks/templates/nixos/configuration.nix.j2 b/playbooks/templates/nixos/configuration.nix.j2
deleted file mode 100644
index d5c00fc3..00000000
--- a/playbooks/templates/nixos/configuration.nix.j2
+++ /dev/null
@@ -1,138 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-{
-  imports = [
-    ./hardware-configuration.nix
-{% if nixos_enable_workflow_deps %}
-    ./workflow-deps.nix
-{% endif %}
-{% if nixos_custom_config_path != "" %}
-    {{ nixos_custom_config_path }}
-{% endif %}
-  ];
-
-  # Nix configuration
-{% if nixos_use_local_mirror is defined and nixos_use_local_mirror and nixos_mirror_url is defined and nixos_mirror_url != "" %}
-  nix.settings = {
-    substituters = [
-      "{{ nixos_mirror_url }}"
-      "https://cache.nixos.org"
-    ];
-    trusted-substituters = [
-      "{{ nixos_mirror_url }}"
-      "https://cache.nixos.org"
-    ];
-    # Prefer local mirror
-    extra-substituters = [ "{{ nixos_mirror_url }}" ];
-  };
-{% endif %}
-
-  # Boot configuration
-  boot.loader.grub.enable = true;
-  boot.loader.grub.device = "/dev/vda";
-  boot.loader.timeout = 1;
-
-  # Kernel
-  boot.kernelPackages = pkgs.linuxPackages_latest;
-
-  # Enable 9p support if configured
-{% if bootlinux_9p is defined and bootlinux_9p %}
-  boot.kernelModules = [ "9p" "9pnet_virtio" ];
-  boot.initrd.kernelModules = [ "9p" "9pnet_virtio" ];
-{% endif %}
-
-  # Networking
-  networking.useDHCP = lib.mkDefault true;
-
-  # Enable SSH
-  services.openssh = {
-    enable = true;
-    settings = {
-      PermitRootLogin = "yes";
-      PasswordAuthentication = false;
-      PubkeyAuthentication = true;
-    };
-  };
-
-  # Users
-  users.users.root = {
-    openssh.authorizedKeys.keys = [
-{% if nixos_ssh_authorized_key is defined %}
-      "{{ nixos_ssh_authorized_key }}"
-{% else %}
-      # SSH key will be generated during provisioning
-{% endif %}
-    ];
-  };
-
-  users.users.kdevops = {
-    isNormalUser = true;
-    extraGroups = [ "wheel" "libvirt" "kvm" ];
-    openssh.authorizedKeys.keys = [
-{% if nixos_ssh_authorized_key is defined %}
-      "{{ nixos_ssh_authorized_key }}"
-{% else %}
-      # SSH key will be generated during provisioning
-{% endif %}
-    ];
-  };
-
-  # Sudo without password for kdevops user
-  security.sudo.wheelNeedsPassword = false;
-
-  # Basic packages
-  environment.systemPackages = with pkgs; [
-    vim
-    git
-    tmux
-    htop
-    wget
-    curl
-    rsync
-    python3
-    gcc
-    gnumake
-    binutils
-    coreutils
-    findutils
-    procps
-    util-linux
-  ];
-
-  # Enable libvirt for nested virtualization if needed
-  virtualisation.libvirtd.enable = false;
-
-  # Filesystems
-  fileSystems."/" = {
-    device = "/dev/vda1";
-    fsType = "ext4";
-  };
-
-{% if bootlinux_9p is defined and bootlinux_9p %}
-  # 9P mount for shared kernel source
-  fileSystems."/mnt/linux" = {
-    device = "linux_source";
-    fsType = "9p";
-    options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
-  };
-{% endif %}
-
-  # Time zone
-  time.timeZone = "UTC";
-
-  # Locale
-  i18n.defaultLocale = "en_US.UTF-8";
-
-  # State version
-  system.stateVersion = "24.05";
-
-  # Enable nix flakes
-  nix.settings.experimental-features = [ "nix-command" "flakes" ];
-
-  # Optimize storage
-  nix.gc = {
-    automatic = true;
-    dates = "weekly";
-    options = "--delete-older-than 7d";
-  };
-}
diff --git a/playbooks/templates/nixos/default.nix.j2 b/playbooks/templates/nixos/default.nix.j2
new file mode 100644
index 00000000..64c8871c
--- /dev/null
+++ b/playbooks/templates/nixos/default.nix.j2
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: copyleft-next-0.3.1
+#
+# Per-node overrides for {{ vm_name }}. Generated by kdevops.
+#
+# The flake that imports this module (copied from
+# scripts/nixos-qemu/templates/libvirt/flake.nix) passes `nixos-qemu`
+# and the flake inputs through specialArgs, so workflow modules are
+# imported directly and mirror substituters attach to nixos-qemu's
+# overlay through plain nixpkgs config.
+{ config, lib, pkgs, nixos-qemu, inputs, ... }: {
+  imports = [
+    nixos-qemu.nixosModules.devel
+    nixos-qemu.nixosModules.build-tools
+{% if kdevops_workflow_enable_blktests is defined and kdevops_workflow_enable_blktests %}
+    nixos-qemu.nixosModules.workflows.blktests
+{% endif %}
+{% if kdevops_workflow_enable_fstests is defined and kdevops_workflow_enable_fstests %}
+    nixos-qemu.nixosModules.workflows.fstests
+{% endif %}
+{% if kdevops_workflow_enable_gitr is defined and kdevops_workflow_enable_gitr %}
+    nixos-qemu.nixosModules.workflows.gitr
+{% endif %}
+{% if kdevops_workflow_enable_ltp is defined and kdevops_workflow_enable_ltp %}
+    nixos-qemu.nixosModules.workflows.ltp
+{% endif %}
+{% if kdevops_workflow_enable_mmtests is defined and kdevops_workflow_enable_mmtests %}
+    nixos-qemu.nixosModules.workflows.mmtests
+{% endif %}
+{% if kdevops_workflow_enable_pynfs is defined and kdevops_workflow_enable_pynfs %}
+    nixos-qemu.nixosModules.workflows.pynfs
+{% endif %}
+{% if kdevops_workflow_enable_selftests is defined and kdevops_workflow_enable_selftests %}
+    nixos-qemu.nixosModules.workflows.selftests
+{% endif %}
+{% if kdevops_workflow_enable_sysbench is defined and kdevops_workflow_enable_sysbench %}
+    nixos-qemu.nixosModules.workflows.sysbench
+{% endif %}
+  ];
+
+  networking.hostName = "{{ vm_name }}";
+
+  nixos-qemu.user.name = "kdevops";
+
+{% if nixos_ssh_authorized_key is defined %}
+  users.users.root.openssh.authorizedKeys.keys = [ "{{ nixos_ssh_authorized_key }}" ];
+  users.users.kdevops.openssh.authorizedKeys.keys = [ "{{ nixos_ssh_authorized_key }}" ];
+{% endif %}
+
+{% if nixos_use_local_mirror is defined and nixos_use_local_mirror and nixos_mirror_url is defined and nixos_mirror_url != "" %}
+  nix.settings = {
+    substituters = [
+      "{{ nixos_mirror_url }}"
+      "https://cache.nixos.org"
+    ];
+    extra-substituters = [ "{{ nixos_mirror_url }}" ];
+  };
+{% endif %}
+
+{% if bootlinux_9p is defined and bootlinux_9p %}
+  # 9P mount for shared kernel source
+  boot.kernelModules = [ "9p" "9pnet_virtio" ];
+  boot.initrd.kernelModules = [ "9p" "9pnet_virtio" ];
+  fileSystems."/mnt/linux" = {
+    device = "linux_source";
+    fsType = "9p";
+    options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
+  };
+{% endif %}
+}
diff --git a/playbooks/templates/nixos/flake.nix.j2 b/playbooks/templates/nixos/flake.nix.j2
deleted file mode 100644
index 52b5b680..00000000
--- a/playbooks/templates/nixos/flake.nix.j2
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  description = "kdevops NixOS VMs";
-
-  inputs = {
-    nixpkgs.url = "github:NixOS/nixpkgs/{{ nixos_channel }}";
-  };
-
-  outputs = { self, nixpkgs }: {
-    nixosConfigurations = {
-{% for node in groups['all'] if node != 'localhost' %}
-      "{{ node }}" = nixpkgs.lib.nixosSystem {
-        system = "x86_64-linux";
-        modules = [
-          ./generated/configuration.nix
-          ./generated/hardware-configuration.nix
-{% if nixos_enable_workflow_deps %}
-          ./generated/workflow-deps.nix
-{% endif %}
-          ({ ... }: {
-            networking.hostName = "{{ node }}";
-          })
-        ];
-      };
-{% endfor %}
-    };
-
-    # Build all VMs
-    defaultPackage.x86_64-linux =
-      nixpkgs.legacyPackages.x86_64-linux.writeShellScriptBin "build-vms" ''
-        echo "Building NixOS VMs..."
-{% for node in groups['all'] if node != 'localhost' %}
-        echo "Building {{ node }}..."
-        nix build .#nixosConfigurations.{{ node }}.config.system.build.vm
-{% endfor %}
-        echo "All VMs built successfully!"
-      '';
-  };
-}
diff --git a/playbooks/templates/nixos/hardware-configuration.nix.j2 b/playbooks/templates/nixos/hardware-configuration.nix.j2
deleted file mode 100644
index bb91bba4..00000000
--- a/playbooks/templates/nixos/hardware-configuration.nix.j2
+++ /dev/null
@@ -1,42 +0,0 @@
-{ config, lib, pkgs, modulesPath, ... }:
-
-{
-  imports = [
-    (modulesPath + "/profiles/qemu-guest.nix")
-  ];
-
-  boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "sr_mod" "virtio_blk" ];
-  boot.initrd.kernelModules = [ ];
-  boot.kernelModules = [ "kvm-intel" "kvm-amd" ];
-  boot.extraModulePackages = [ ];
-
-  # Root filesystem
-  fileSystems."/" = {
-    device = "/dev/disk/by-label/nixos";
-    fsType = "ext4";
-  };
-
-  # Boot partition (if UEFI is enabled)
-{% if guestfs_requires_uefi is defined and guestfs_requires_uefi %}
-  fileSystems."/boot" = {
-    device = "/dev/disk/by-label/boot";
-    fsType = "vfat";
-  };
-{% endif %}
-
-  # Swap
-  swapDevices = [ ];
-
-  # Networking
-  networking.useDHCP = lib.mkDefault true;
-  networking.interfaces.eth0.useDHCP = lib.mkDefault true;
-
-  # Hardware configuration
-  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
-  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
-  hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
-
-  # Virtualization features
-  virtualisation.hypervGuest.enable = false;
-  virtualisation.vmware.guest.enable = false;
-}
diff --git a/playbooks/templates/nixos/vms.nix.j2 b/playbooks/templates/nixos/vms.nix.j2
deleted file mode 100644
index f2af2b96..00000000
--- a/playbooks/templates/nixos/vms.nix.j2
+++ /dev/null
@@ -1,45 +0,0 @@
-# NixOS VM configurations for kdevops
-{ config, pkgs, ... }:
-
-{
-  # VM-specific configurations
-  virtualisation.memorySize = {{ nixos_vm_memory_mb }};
-  virtualisation.diskSize = {{ nixos_vm_disk_size_gb * 1024 }};  # Convert GB to MB
-  virtualisation.cores = {{ nixos_vm_vcpus }};
-
-  # Enable virtio for better performance
-  virtualisation.qemu.options = [
-    "-machine q35,accel=kvm"
-    "-cpu host"
-    "-smp {{ nixos_vm_vcpus }}"
-    "-m {{ nixos_vm_memory_mb }}M"
-  ];
-
-  # Network configuration for VMs
-  networking.useDHCP = true;
-  networking.nameservers = [ "8.8.8.8" "8.8.4.4" ];
-
-  # VM-specific services
-  services.qemuGuest.enable = true;
-
-  # Enable console access
-  services.getty.autologinUser = "kdevops";
-  boot.kernelParams = [ "console=ttyS0,115200n8" "console=tty0" ];
-
-  # Additional VM optimizations
-  boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_blk" "virtio_scsi" "9p" "9pnet_virtio" ];
-  boot.kernelModules = [ "virtio_balloon" "virtio_rng" ];
-
-{% if bootlinux_9p is defined and bootlinux_9p %}
-  # 9P filesystem support for kernel development
-  fileSystems."/mnt/linux" = {
-    device = "linux_source";
-    fsType = "9p";
-    options = [ "trans=virtio" "version=9p2000.L" "rw" ];
-  };
-{% endif %}
-
-  # Ensure VM can be reached via SSH
-  services.openssh.permitRootLogin = "yes";
-  services.openssh.passwordAuthentication = false;
-}
diff --git a/playbooks/templates/nixos/workflow-deps.nix.j2 b/playbooks/templates/nixos/workflow-deps.nix.j2
deleted file mode 100644
index d01aac08..00000000
--- a/playbooks/templates/nixos/workflow-deps.nix.j2
+++ /dev/null
@@ -1,127 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-{
-  # Workflow-specific dependencies based on enabled kdevops workflows
-  environment.systemPackages = with pkgs; [
-{% if kdevops_workflow_enable_fstests is defined and kdevops_workflow_enable_fstests %}
-    # fstests dependencies
-    xfsprogs
-    btrfs-progs
-    e2fsprogs
-    f2fs-tools
-    fio
-    dbench
-    stress-ng
-    attr
-    acl
-    quota
-    nfs-utils
-    cifs-utils
-{% endif %}
-
-{% if kdevops_workflow_enable_blktests is defined and kdevops_workflow_enable_blktests %}
-    # blktests dependencies
-    nvme-cli
-    sg3_utils
-    targetcli
-    multipath-tools
-    dmraid
-    lvm2
-    mdadm
-{% endif %}
-
-{% if kdevops_workflow_enable_selftests is defined and kdevops_workflow_enable_selftests %}
-    # selftests dependencies
-    perf-tools
-    numactl
-    libcap
-    libseccomp
-    keyutils
-    iproute2
-    ethtool
-    tc
-{% endif %}
-
-{% if kdevops_workflow_enable_mmtests is defined and kdevops_workflow_enable_mmtests %}
-    # mmtests dependencies
-    gnuplot
-    perl
-    cpupower
-    dmidecode
-    sysstat
-    iotop
-    powertop
-{% endif %}
-
-{% if kdevops_workflow_enable_pynfs is defined and kdevops_workflow_enable_pynfs %}
-    # pynfs dependencies
-    python3
-    python3Packages.ply
-    nfs-utils
-{% endif %}
-
-{% if kdevops_workflow_enable_ltp is defined and kdevops_workflow_enable_ltp %}
-    # LTP dependencies
-    autoconf
-    automake
-    m4
-    libtool
-    pkg-config
-    flex
-    bison
-    libacl
-    libcap
-    libaio
-    libnuma
-    libsepol
-    libselinux
-    libssl
-{% endif %}
-
-{% if kdevops_workflow_enable_sysbench is defined and kdevops_workflow_enable_sysbench %}
-    # sysbench dependencies
-    sysbench
-    mysql
-    postgresql
-{% endif %}
-
-{% if kdevops_workflow_enable_gitr is defined and kdevops_workflow_enable_gitr %}
-    # git regression testing dependencies
-    git
-    gitFull
-    perl
-    subversion
-    mercurial
-{% endif %}
-
-    # Common build tools often needed
-    autoconf
-    automake
-    libtool
-    pkg-config
-    flex
-    bison
-    bc
-    openssl
-    elfutils
-    libelf
-  ];
-
-{% if kdevops_workflow_enable_fstests is defined and kdevops_workflow_enable_fstests %}
-  # Enable required services for fstests
-  services.nfs.server.enable = true;
-  services.rpcbind.enable = true;
-{% endif %}
-
-{% if kdevops_workflow_enable_sysbench is defined and kdevops_workflow_enable_sysbench %}
-  # Database services for sysbench
-  services.mysql = {
-    enable = true;
-    package = pkgs.mariadb;
-  };
-
-  services.postgresql = {
-    enable = true;
-  };
-{% endif %}
-}

-- 
2.53.0