[PATCH 6/8] nixos: collapse playbook into a tag-gated role entry
Daniel Gomez <[email protected]> Thu, 23 Apr 2026 00:48:44 +0200
| Newsgroups | dev.linux.lists.kdevops |
|---|---|
| Message-ID | <20260423-kdevops-series-b-nixos-qemu-v1-6-209154ae54f8@samsung.com> |
From: Daniel Gomez <[email protected]> Same shape as terraform.yml and guestfs.yml: one play, hosts: all with connection: local so every task executes on the controller regardless of the inventory host it is iterating over, role-level dispatch via tag-gated include_tasks in main.yml, and localhost-only phases wrapped in a single delegate_to + run_once block per task file so they execute exactly once even when the play runs across multiple hosts. libvirt_provision keeps its per-inventory-host iteration and sits behind a when-filter in main.yml so it skips the localhost iteration. The eight-play structure duplicated hosts and vars_files across phases and diverged from the rest of the codebase; collapsing it makes the invocation model consistent with every other bringup role. Generated-by: Claude AI Signed-off-by: Daniel Gomez <[email protected]> (cherry picked from commit a16cb8cf13b206b1bb059ee98af7797ffbe3476b) --- playbooks/nixos.yml | 102 +---------------- playbooks/roles/nixos/tasks/console.yml | 2 + playbooks/roles/nixos/tasks/destroy.yml | 138 ++++++++++++----------- playbooks/roles/nixos/tasks/generate_configs.yml | 108 +++++++++--------- playbooks/roles/nixos/tasks/install_deps.yml | 80 ++++++------- playbooks/roles/nixos/tasks/libvirt_build.yml | 80 ++++++------- playbooks/roles/nixos/tasks/libvirt_network.yml | 28 +++-- playbooks/roles/nixos/tasks/main.yml | 63 +++++++++++ playbooks/roles/nixos/tasks/ssh_access.yml | 90 ++++++++------- 9 files changed, 342 insertions(+), 349 deletions(-) diff --git a/playbooks/nixos.yml b/playbooks/nixos.yml index c1adebbf..a51cf345 100644 --- a/playbooks/nixos.yml +++ b/playbooks/nixos.yml @@ -1,102 +1,8 @@ --- -# SPDX-License-Identifier: copyleft-next-0.3.1 -# -# NixOS bringup playbook. Each play imports one task file from -# the nixos role, keeping the phases of bringup separate so a -# future imageless backend can slot in sibling imageless_*.yml -# files alongside today's libvirt_*.yml without touching this -# dispatcher beyond adding new plays. - -- name: Install NixOS dependencies on localhost - hosts: localhost - gather_facts: true - tags: install-deps - tasks: - - name: Run install_deps tasks - ansible.builtin.import_role: - name: nixos - tasks_from: install_deps - -- name: Generate NixOS configurations - hosts: localhost - gather_facts: true - vars_files: - - "{{ playbook_dir }}/../extra_vars.yaml" - tags: generate-configs - tasks: - - name: Run generate_configs tasks - ansible.builtin.import_role: - name: nixos - tasks_from: generate_configs - -- name: Build and deploy NixOS VMs - hosts: localhost - gather_facts: true - vars_files: - - "{{ playbook_dir }}/../extra_vars.yaml" - tags: build-vms - tasks: - - name: Run libvirt_build tasks - ansible.builtin.import_role: - name: nixos - tasks_from: libvirt_build - -- name: Ensure default libvirt network is available - hosts: localhost - gather_facts: true - vars_files: - - "{{ playbook_dir }}/../extra_vars.yaml" - tags: bringup - tasks: - - name: Run libvirt_network tasks - ansible.builtin.import_role: - name: nixos - tasks_from: libvirt_network - -- name: Provision NixOS VMs with libvirt - hosts: baseline,dev +- name: Manage NixOS VMs with libvirt + hosts: all gather_facts: false vars_files: - "{{ playbook_dir }}/../extra_vars.yaml" - tags: bringup - tasks: - - name: Run libvirt_provision tasks - ansible.builtin.import_role: - name: nixos - tasks_from: libvirt_provision - -- name: Setup SSH access for NixOS VMs - hosts: localhost - gather_facts: true - vars_files: - - "{{ playbook_dir }}/../extra_vars.yaml" - tags: bringup - tasks: - - name: Run ssh_access tasks - ansible.builtin.import_role: - name: nixos - tasks_from: ssh_access - -- name: Show VM access information - hosts: localhost - gather_facts: false - vars_files: - - "{{ playbook_dir }}/../extra_vars.yaml" - tags: console - tasks: - - name: Run console tasks - ansible.builtin.import_role: - name: nixos - tasks_from: console - -- name: Destroy NixOS VMs - hosts: localhost - gather_facts: true - vars_files: - - "{{ playbook_dir }}/../extra_vars.yaml" - tags: [destroy, never] - tasks: - - name: Run destroy tasks - ansible.builtin.import_role: - name: nixos - tasks_from: destroy + roles: + - role: nixos diff --git a/playbooks/roles/nixos/tasks/console.yml b/playbooks/roles/nixos/tasks/console.yml index 149bacd9..516028be 100644 --- a/playbooks/roles/nixos/tasks/console.yml +++ b/playbooks/roles/nixos/tasks/console.yml @@ -4,6 +4,8 @@ # Final status banner printed at the end of a successful bringup. - name: Display VM access information + delegate_to: localhost + run_once: true ansible.builtin.debug: msg: | NixOS VMs are running and accessible via libvirt. diff --git a/playbooks/roles/nixos/tasks/destroy.yml b/playbooks/roles/nixos/tasks/destroy.yml index 794f7bb5..8943e197 100644 --- a/playbooks/roles/nixos/tasks/destroy.yml +++ b/playbooks/roles/nixos/tasks/destroy.yml @@ -5,81 +5,85 @@ # config entries, disk images, per-node generated configurations, # and the cached NixOS disk images in the Nix store. -- name: Shut down VMs via libvirt - ansible.builtin.command: virsh destroy "{{ item }}" - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - failed_when: false - environment: - LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" +- name: Localhost destroy + delegate_to: localhost + run_once: true + block: + - name: Shut down VMs via libvirt + ansible.builtin.command: virsh destroy "{{ item }}" + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + failed_when: false + environment: + LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" -- name: Remove VM definitions from libvirt - ansible.builtin.command: virsh undefine "{{ item }}" - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - failed_when: false - environment: - LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" + - name: Remove VM definitions from libvirt + ansible.builtin.command: virsh undefine "{{ item }}" + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + failed_when: false + environment: + LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" -- name: Stop VMs using wrapper scripts - ansible.builtin.command: "{{ nixos_storage_dir }}/run-{{ item }}-wrapper.sh stop" - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - failed_when: false + - name: Stop VMs using wrapper scripts + ansible.builtin.command: "{{ nixos_storage_dir }}/run-{{ item }}-wrapper.sh stop" + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + failed_when: false -- name: Remove SSH config entries for NixOS VMs - ansible.builtin.command: | - python3 {{ playbook_dir }}/../scripts/update_ssh_config_nixos.py remove \ - {{ item }} \ - '' \ - '' \ - '' \ - {{ nixos_ssh_config_file | default(ansible_env.HOME + '/.ssh/config') }} \ - '' \ - 'NixOS VM' - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - when: nixos_update_ssh_config | default(true) | bool - failed_when: false + - name: Remove SSH config entries for NixOS VMs + ansible.builtin.command: | + python3 {{ playbook_dir }}/../scripts/update_ssh_config_nixos.py remove \ + {{ item }} \ + '' \ + '' \ + '' \ + {{ nixos_ssh_config_file | default(ansible_env.HOME + '/.ssh/config') }} \ + '' \ + 'NixOS VM' + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + when: nixos_update_ssh_config | default(true) | bool + failed_when: false -- name: Remove VM disk images - ansible.builtin.file: - path: "{{ nixos_storage_dir }}/{{ item }}.qcow2" - state: absent - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + - name: Remove VM disk images + ansible.builtin.file: + path: "{{ nixos_storage_dir }}/{{ item }}.qcow2" + state: absent + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" -- name: Remove VM wrapper scripts - ansible.builtin.file: - path: "{{ nixos_storage_dir }}/run-{{ item }}-wrapper.sh" - state: absent - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + - name: Remove VM wrapper scripts + ansible.builtin.file: + path: "{{ nixos_storage_dir }}/run-{{ item }}-wrapper.sh" + state: absent + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" -- name: Remove NixOS disk image symlink - ansible.builtin.file: - path: "{{ nixos_storage_dir }}/nixos-image-result" - state: absent + - name: Remove NixOS disk image symlink + ansible.builtin.file: + path: "{{ nixos_storage_dir }}/nixos-image-result" + state: absent -- name: Remove extra drive directories - ansible.builtin.file: - path: "{{ nixos_storage_dir }}/extra-drives" - state: absent + - name: Remove extra drive directories + ansible.builtin.file: + path: "{{ nixos_storage_dir }}/extra-drives" + state: absent -- name: Clean up per-node generated NixOS configurations - ansible.builtin.file: - path: "{{ nixos_config_dir }}/{{ item }}" - state: absent - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + - name: Clean up per-node generated NixOS configurations + ansible.builtin.file: + 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: | - if [ -f /nix/var/nix/profiles/default/etc/profile.d/nix.sh ]; then - . /nix/var/nix/profiles/default/etc/profile.d/nix.sh - fi + - name: Garbage collect cached NixOS disk images from Nix store + ansible.builtin.shell: | + if [ -f /nix/var/nix/profiles/default/etc/profile.d/nix.sh ]; then + . /nix/var/nix/profiles/default/etc/profile.d/nix.sh + fi - NIX_COLLECT_GARBAGE=$(which nix-collect-garbage 2>/dev/null || find /nix -name "nix-collect-garbage" -type f 2>/dev/null | head -1) + NIX_COLLECT_GARBAGE=$(which nix-collect-garbage 2>/dev/null || find /nix -name "nix-collect-garbage" -type f 2>/dev/null | head -1) - if [ -n "$NIX_COLLECT_GARBAGE" ]; then - echo "Running Nix garbage collection to remove cached disk images..." - sudo $NIX_COLLECT_GARBAGE -d 2>&1 | grep -E "(deleting|freed|store paths)" || true - else - echo "Warning: nix-collect-garbage not found, cached images may remain" - fi - register: gc_result - failed_when: false - changed_when: "'freed' in gc_result.stdout" + if [ -n "$NIX_COLLECT_GARBAGE" ]; then + echo "Running Nix garbage collection to remove cached disk images..." + sudo $NIX_COLLECT_GARBAGE -d 2>&1 | grep -E "(deleting|freed|store paths)" || true + else + echo "Warning: nix-collect-garbage not found, cached images may remain" + fi + register: gc_result + failed_when: false + changed_when: "'freed' in gc_result.stdout" diff --git a/playbooks/roles/nixos/tasks/generate_configs.yml b/playbooks/roles/nixos/tasks/generate_configs.yml index 9c6d49a0..22d7df3a 100644 --- a/playbooks/roles/nixos/tasks/generate_configs.yml +++ b/playbooks/roles/nixos/tasks/generate_configs.yml @@ -7,24 +7,26 @@ # phase 9 lands), rewrites the nixos-qemu.url input to resolve at # the checked-in subtree, and renders the per-node default.nix. -- name: Create top-level NixOS directories - ansible.builtin.file: - path: "{{ item }}" - state: directory - mode: '0755' - loop: - - "{{ nixos_config_dir }}" - - "{{ nixos_storage_dir }}" +- name: Localhost generate-configs + delegate_to: localhost + run_once: true + block: + - name: Create top-level NixOS directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: '0755' + loop: + - "{{ nixos_config_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: 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 ansible.builtin.command: python3 {{ playbook_dir }}/../scripts/nixos_ssh_key_name.py --path register: ssh_key_path_result @@ -51,45 +53,45 @@ ansible.builtin.set_fact: nixos_ssh_authorized_key: "{{ ssh_public_key['content'] | b64decode | trim }}" -- name: Detect local Nix cache mirror URL if enabled - ansible.builtin.shell: | - bash {{ playbook_dir }}/../scripts/check_nix_mirror.sh NIX_CACHE_MIRROR_URL - register: detected_mirror_url - when: nixos_use_local_mirror | default(false) | bool and (nixos_mirror_url is not defined or nixos_mirror_url == "") - changed_when: false + - name: Detect local Nix cache mirror URL if enabled + ansible.builtin.shell: | + bash {{ playbook_dir }}/../scripts/check_nix_mirror.sh NIX_CACHE_MIRROR_URL + register: detected_mirror_url + when: nixos_use_local_mirror | default(false) | bool and (nixos_mirror_url is not defined or nixos_mirror_url == "") + changed_when: false -- name: Set detected mirror URL - ansible.builtin.set_fact: - nixos_mirror_url: "{{ detected_mirror_url.stdout | trim }}" - when: - - detected_mirror_url is defined - - detected_mirror_url.stdout is defined - - detected_mirror_url.stdout | trim != "" + - name: Set detected mirror URL + ansible.builtin.set_fact: + nixos_mirror_url: "{{ detected_mirror_url.stdout | trim }}" + when: + - detected_mirror_url is defined + - detected_mirror_url.stdout is defined + - detected_mirror_url.stdout | trim != "" -- name: Debug SSH key path - ansible.builtin.debug: - msg: "Using SSH key: {{ hostvars['localhost']['nixos_ssh_key_path'] | default('NOT SET') }}" + - name: Debug SSH key path + ansible.builtin.debug: + msg: "Using SSH key: {{ nixos_ssh_key_path | default('NOT SET') }}" -- 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: 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: 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: 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: default.nix.j2 - dest: "{{ nixos_config_dir }}/{{ item }}/default.nix" - mode: '0644' - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - vars: - vm_name: "{{ item }}" + - name: Render per-node default.nix + ansible.builtin.template: + src: default.nix.j2 + dest: "{{ nixos_config_dir }}/{{ item }}/default.nix" + mode: '0644' + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + vars: + vm_name: "{{ item }}" diff --git a/playbooks/roles/nixos/tasks/install_deps.yml b/playbooks/roles/nixos/tasks/install_deps.yml index 2b8159aa..98a517ea 100644 --- a/playbooks/roles/nixos/tasks/install_deps.yml +++ b/playbooks/roles/nixos/tasks/install_deps.yml @@ -5,45 +5,49 @@ # and the libvirt client/daemon tooling. Runs under the # install-deps tag. -- name: Check if nix is installed - ansible.builtin.command: which nix - register: nix_check - failed_when: false - changed_when: false - -- name: Install nix package manager - become: true - when: nix_check.rc != 0 +- name: Localhost install-deps + delegate_to: localhost + run_once: true block: - - name: Download nix installer - ansible.builtin.get_url: - url: https://nixos.org/nix/install - dest: /tmp/install-nix.sh - mode: '0755' + - name: Check if nix is installed + ansible.builtin.command: which nix + register: nix_check + failed_when: false + changed_when: false + + - name: Install nix package manager + become: true + when: nix_check.rc != 0 + block: + - name: Download nix installer + ansible.builtin.get_url: + url: https://nixos.org/nix/install + dest: /tmp/install-nix.sh + mode: '0755' - - name: Install nix - ansible.builtin.shell: | - sh /tmp/install-nix.sh --daemon --yes - args: - creates: /nix + - name: Install nix + ansible.builtin.shell: | + sh /tmp/install-nix.sh --daemon --yes + args: + creates: /nix -- name: Ensure libvirt is installed - become: true - ansible.builtin.package: - name: - - libvirt0 - - qemu-kvm - - libvirt-daemon-system - - libvirt-clients - state: present - when: ansible_os_family == "Debian" + - name: Ensure libvirt is installed + become: true + ansible.builtin.package: + name: + - libvirt0 + - qemu-kvm + - libvirt-daemon-system + - libvirt-clients + state: present + when: ansible_os_family == "Debian" -- name: Ensure libvirt is installed (RedHat) - become: true - ansible.builtin.package: - name: - - libvirt - - qemu-kvm - - libvirt-daemon - state: present - when: ansible_os_family == "RedHat" + - name: Ensure libvirt is installed (RedHat) + become: true + ansible.builtin.package: + name: + - libvirt + - qemu-kvm + - libvirt-daemon + state: present + when: ansible_os_family == "RedHat" diff --git a/playbooks/roles/nixos/tasks/libvirt_build.yml b/playbooks/roles/nixos/tasks/libvirt_build.yml index f7f0ba00..23a7c317 100644 --- a/playbooks/roles/nixos/tasks/libvirt_build.yml +++ b/playbooks/roles/nixos/tasks/libvirt_build.yml @@ -6,43 +6,47 @@ # imageless backend will have a sibling imageless_build.yml that # produces a closure plus kernel and initrd instead of a qcow2. -- name: Build per-node NixOS disk image via the flake - ansible.builtin.command: - argv: - - nix - - build - - "path:{{ nixos_config_dir }}/{{ item }}#image" - - --out-link - - "{{ nixos_config_dir }}/{{ item }}/result" - creates: "{{ nixos_config_dir }}/{{ item }}/result" - environment: - PATH: "/nix/var/nix/profiles/default/bin:{{ ansible_env.PATH | default('/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin') }}" - NIX_CONFIG: "{{ ('substituters = ' ~ nixos_mirror_url ~ ' https://cache.nixos.org') if (nixos_use_local_mirror | default(false) | bool and (nixos_mirror_url | default('') | length > 0)) else omit }}" - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - loop_control: - label: "{{ item }}" +- name: Localhost libvirt build + delegate_to: localhost + run_once: true + block: + - name: Build per-node NixOS disk image via the flake + ansible.builtin.command: + argv: + - nix + - build + - "path:{{ nixos_config_dir }}/{{ item }}#image" + - --out-link + - "{{ nixos_config_dir }}/{{ item }}/result" + creates: "{{ nixos_config_dir }}/{{ item }}/result" + environment: + PATH: "/nix/var/nix/profiles/default/bin:{{ ansible_env.PATH | default('/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin') }}" + NIX_CONFIG: "{{ ('substituters = ' ~ nixos_mirror_url ~ ' https://cache.nixos.org') if (nixos_use_local_mirror | default(false) | bool and (nixos_mirror_url | default('') | length > 0)) else omit }}" + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + loop_control: + label: "{{ item }}" -- name: Copy per-node NixOS disk image into libvirt storage - ansible.builtin.shell: | - SRC="{{ nixos_config_dir }}/{{ item }}/result/nixos.qcow2" - DST="{{ nixos_storage_dir }}/{{ item }}.qcow2" - rm -f "$DST" - cp -- "$SRC" "$DST" - chmod u+w "$DST" - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - loop_control: - label: "{{ item }}" + - name: Copy per-node NixOS disk image into libvirt storage + ansible.builtin.shell: | + SRC="{{ nixos_config_dir }}/{{ item }}/result/nixos.qcow2" + DST="{{ nixos_storage_dir }}/{{ item }}.qcow2" + rm -f "$DST" + cp -- "$SRC" "$DST" + chmod u+w "$DST" + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + loop_control: + label: "{{ item }}" -- name: Generate VM wrapper scripts - ansible.builtin.template: - src: run-vm-wrapper.sh.j2 - dest: "{{ nixos_storage_dir }}/run-{{ item }}-wrapper.sh" - mode: '0755' - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - loop_control: - index_var: vm_idx - vars: - vm_name: "{{ item }}" - vm_index: "{{ vm_idx }}" - vm_memory: "{{ nixos_vm_memory_mb | default(4096) }}" - vm_vcpus: "{{ nixos_vm_vcpus | default(4) }}" + - name: Generate VM wrapper scripts + ansible.builtin.template: + src: run-vm-wrapper.sh.j2 + dest: "{{ nixos_storage_dir }}/run-{{ item }}-wrapper.sh" + mode: '0755' + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + loop_control: + index_var: vm_idx + vars: + vm_name: "{{ item }}" + vm_index: "{{ vm_idx }}" + vm_memory: "{{ nixos_vm_memory_mb | default(4096) }}" + vm_vcpus: "{{ nixos_vm_vcpus | default(4) }}" diff --git a/playbooks/roles/nixos/tasks/libvirt_network.yml b/playbooks/roles/nixos/tasks/libvirt_network.yml index d9262467..a354d8f6 100644 --- a/playbooks/roles/nixos/tasks/libvirt_network.yml +++ b/playbooks/roles/nixos/tasks/libvirt_network.yml @@ -4,16 +4,20 @@ # Ensure the libvirt default network is active before trying to # start any NixOS VM. Only used by the libvirt backend path. -- name: Check if default network exists and is active - ansible.builtin.shell: virsh net-info default - register: default_network_info - failed_when: false - environment: - LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" +- name: Localhost libvirt network + delegate_to: localhost + run_once: true + block: + - name: Check if default network exists and is active + ansible.builtin.shell: virsh net-info default + register: default_network_info + failed_when: false + environment: + LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" -- name: Start default network if not active - ansible.builtin.shell: virsh net-start default - when: default_network_info.rc != 0 or 'Active:' not in default_network_info.stdout or 'yes' not in default_network_info.stdout.split('Active:')[1].split('\n')[0] - failed_when: false - environment: - LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" + - name: Start default network if not active + ansible.builtin.shell: virsh net-start default + when: default_network_info.rc != 0 or 'Active:' not in default_network_info.stdout or 'yes' not in default_network_info.stdout.split('Active:')[1].split('\n')[0] + failed_when: false + environment: + LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" diff --git a/playbooks/roles/nixos/tasks/main.yml b/playbooks/roles/nixos/tasks/main.yml new file mode 100644 index 00000000..1ebf63e6 --- /dev/null +++ b/playbooks/roles/nixos/tasks/main.yml @@ -0,0 +1,63 @@ +--- +# nixos role entry point. Orchestrates install-deps, +# generate-configs, libvirt build, libvirt network, per-VM libvirt +# provision, ssh_access, console, and destroy via tag-gated +# include_tasks. Localhost-only phases run with delegate_to + +# run_once inside their task files so --limit localhost is not +# required from the caller. + +- name: Gather localhost facts + ansible.builtin.setup: + delegate_to: localhost + run_once: true + tags: always + +- name: Include install_deps tasks + ansible.builtin.import_tasks: + file: "{{ role_path }}/tasks/install_deps.yml" + tags: + - install-deps + +- name: Include generate_configs tasks + ansible.builtin.import_tasks: + file: "{{ role_path }}/tasks/generate_configs.yml" + tags: + - generate-configs + +- name: Include libvirt_build tasks + ansible.builtin.import_tasks: + file: "{{ role_path }}/tasks/libvirt_build.yml" + tags: + - build-vms + +- name: Include libvirt_network tasks + ansible.builtin.import_tasks: + file: "{{ role_path }}/tasks/libvirt_network.yml" + tags: + - bringup + +- name: Include libvirt_provision tasks + ansible.builtin.import_tasks: + file: "{{ role_path }}/tasks/libvirt_provision.yml" + tags: + - bringup + when: inventory_hostname != 'localhost' + +- name: Include ssh_access tasks + ansible.builtin.import_tasks: + file: "{{ role_path }}/tasks/ssh_access.yml" + tags: + - bringup + +- name: Include console tasks + ansible.builtin.import_tasks: + file: "{{ role_path }}/tasks/console.yml" + tags: + - console + +- name: Include destroy tasks + ansible.builtin.import_tasks: + file: "{{ role_path }}/tasks/destroy.yml" + tags: + - destroy + - never diff --git a/playbooks/roles/nixos/tasks/ssh_access.yml b/playbooks/roles/nixos/tasks/ssh_access.yml index 1f2fa67d..43cf3a37 100644 --- a/playbooks/roles/nixos/tasks/ssh_access.yml +++ b/playbooks/roles/nixos/tasks/ssh_access.yml @@ -7,50 +7,54 @@ # paths; qsu will populate nixos_vm_ips differently but the # downstream wait_for and ssh-config update are reusable. -- name: Wait for VMs to get IP addresses from DHCP - ansible.builtin.shell: | - for i in {1..90}; do - IP=$(virsh domifaddr {{ item }} --source lease 2>/dev/null | awk '/192\.168\.122\./ {print $4}' | cut -d'/' -f1) - if [ -n "$IP" ]; then - echo "$IP" - exit 0 - fi - sleep 3 - done - exit 1 - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - register: vm_ips - retries: 2 - delay: 10 - environment: - LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" +- name: Localhost ssh_access + delegate_to: localhost + run_once: true + block: + - name: Wait for VMs to get IP addresses from DHCP + ansible.builtin.shell: | + for i in {1..90}; do + IP=$(virsh domifaddr {{ item }} --source lease 2>/dev/null | awk '/192\.168\.122\./ {print $4}' | cut -d'/' -f1) + if [ -n "$IP" ]; then + echo "$IP" + exit 0 + fi + sleep 3 + done + exit 1 + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + register: vm_ips + retries: 2 + delay: 10 + environment: + LIBVIRT_DEFAULT_URI: "{{ libvirt_uri }}" -- name: Set VM IP facts - ansible.builtin.set_fact: - nixos_vm_ips: "{{ dict(groups['all'] | reject('equalto', 'localhost') | list | zip(vm_ips.results | map(attribute='stdout'))) }}" + - name: Set VM IP facts + ansible.builtin.set_fact: + nixos_vm_ips: "{{ dict(groups['all'] | reject('equalto', 'localhost') | list | zip(vm_ips.results | map(attribute='stdout'))) }}" -- name: Determine SSH key path for SSH config update - ansible.builtin.command: python3 {{ playbook_dir }}/../scripts/nixos_ssh_key_name.py --path - register: ssh_key_path_for_config - changed_when: false + - name: Determine SSH key path for SSH config update + ansible.builtin.command: python3 {{ playbook_dir }}/../scripts/nixos_ssh_key_name.py --path + register: ssh_key_path_for_config + changed_when: false -- name: Wait for SSH to be available on VMs - ansible.builtin.wait_for: - host: "{{ nixos_vm_ips[item] }}" - port: 22 - delay: 10 - timeout: 300 - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + - name: Wait for SSH to be available on VMs + ansible.builtin.wait_for: + host: "{{ nixos_vm_ips[item] }}" + port: 22 + delay: 10 + timeout: 300 + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" -- name: Update SSH config for NixOS VMs - ansible.builtin.command: | - python3 {{ playbook_dir }}/../scripts/update_ssh_config_nixos.py update \ - {{ item }} \ - {{ nixos_vm_ips[item] }} \ - {{ ansible_cfg_ssh_port }} \ - kdevops \ - {{ nixos_ssh_config_file | default(ansible_env.HOME + '/.ssh/config') }} \ - {{ ssh_key_path_for_config.stdout | trim }} \ - 'NixOS VM' - loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" - when: nixos_update_ssh_config | default(true) | bool + - name: Update SSH config for NixOS VMs + ansible.builtin.command: | + python3 {{ playbook_dir }}/../scripts/update_ssh_config_nixos.py update \ + {{ item }} \ + {{ nixos_vm_ips[item] }} \ + {{ ansible_cfg_ssh_port }} \ + kdevops \ + {{ nixos_ssh_config_file | default(ansible_env.HOME + '/.ssh/config') }} \ + {{ ssh_key_path_for_config.stdout | trim }} \ + 'NixOS VM' + loop: "{{ groups['all'] | reject('equalto', 'localhost') | list }}" + when: nixos_update_ssh_config | default(true) | bool -- 2.53.0