ci / test (push) Successful in 29s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
197 lines
7.1 KiB
YAML
197 lines
7.1 KiB
YAML
- name: Site-Datei laden
|
||
ansible.builtin.set_fact:
|
||
knaster_pve: "{{ lookup('ansible.builtin.file', pve_site_file) | from_yaml }}"
|
||
run_once: true
|
||
delegate_to: localhost
|
||
|
||
- name: Bootloader erkennen (systemd-boot bei ZFS-Root, sonst GRUB)
|
||
ansible.builtin.stat:
|
||
path: /etc/kernel/cmdline
|
||
register: kernel_cmdline
|
||
|
||
- name: IOMMU aktivieren (systemd-boot)
|
||
ansible.builtin.replace:
|
||
path: /etc/kernel/cmdline
|
||
regexp: '^((?!.*intel_iommu=on).*)$'
|
||
replace: '\1 {{ pve_iommu_cmdline }}'
|
||
when: kernel_cmdline.stat.exists
|
||
notify: refresh boot
|
||
|
||
- name: IOMMU aktivieren (GRUB)
|
||
ansible.builtin.lineinfile:
|
||
path: /etc/default/grub
|
||
regexp: '^GRUB_CMDLINE_LINUX_DEFAULT="((?!.*intel_iommu=on).*)"$'
|
||
line: 'GRUB_CMDLINE_LINUX_DEFAULT="\1 {{ pve_iommu_cmdline }}"'
|
||
backrefs: true
|
||
when: not kernel_cmdline.stat.exists
|
||
notify: refresh boot
|
||
|
||
- name: VFIO-Module laden
|
||
ansible.builtin.copy:
|
||
dest: /etc/modules-load.d/vfio.conf
|
||
content: "{{ pve_vfio_modules | join('\n') }}\n"
|
||
mode: "0644"
|
||
notify: update initramfs
|
||
|
||
# IDs auf dem Knoten lesen, nicht aus dem Gedächtnis: Grafik- und Audio-Funktion jeder Karte
|
||
- name: NVIDIA-Funktionen des Knotens lesen
|
||
ansible.builtin.shell: |
|
||
set -o pipefail
|
||
lspci -n -d 10de: | awk '{print $3}' | sort -u | paste -sd,
|
||
args:
|
||
executable: /bin/bash
|
||
register: nvidia_ids
|
||
check_mode: false
|
||
changed_when: false
|
||
failed_when: nvidia_ids.rc != 0 or nvidia_ids.stdout == ''
|
||
|
||
- name: Karten an vfio-pci binden, bevor ein Grafik- oder Audiotreiber zugreift
|
||
ansible.builtin.copy:
|
||
dest: /etc/modprobe.d/vfio.conf
|
||
content: |
|
||
options vfio-pci ids={{ nvidia_ids.stdout }}
|
||
softdep nouveau pre: vfio-pci
|
||
softdep nvidia pre: vfio-pci
|
||
softdep nvidiafb pre: vfio-pci
|
||
softdep snd_hda_intel pre: vfio-pci
|
||
mode: "0644"
|
||
notify: update initramfs
|
||
|
||
- name: NVIDIA-Treiber auf dem Host sperren (GPU gehört der VM)
|
||
ansible.builtin.copy:
|
||
dest: /etc/modprobe.d/blacklist-nvidia.conf
|
||
content: |
|
||
blacklist nouveau
|
||
blacklist nvidia
|
||
blacklist nvidiafb
|
||
mode: "0644"
|
||
notify: update initramfs
|
||
|
||
# ZFS-ARC begrenzen: Standard wären 10 % des RAM (3,2 GB), bei Pool-Anlage 4 GB gesetzt. Die GPU-VMs liegen auf
|
||
# local-lvm, tank trägt nur Replikate und die kleinen Infra-Gäste – 2 GB reichen, der Rest geht an die VMs.
|
||
- name: ZFS-ARC-Obergrenze (modprobe, gilt nach initramfs beim nächsten Start)
|
||
ansible.builtin.copy:
|
||
dest: /etc/modprobe.d/zfs.conf
|
||
content: "options zfs zfs_arc_max={{ knaster_pve.cluster.zfs_arc_max_mb * 1024 * 1024 }}
|
||
"
|
||
mode: "0644"
|
||
when: knaster_pve.cluster.zfs_arc_max_mb is defined
|
||
notify: update initramfs
|
||
|
||
- name: ZFS-ARC-Obergrenze sofort setzen
|
||
ansible.builtin.shell: >
|
||
[ -w /sys/module/zfs/parameters/zfs_arc_max ] || exit 0;
|
||
v={{ knaster_pve.cluster.zfs_arc_max_mb * 1024 * 1024 }};
|
||
[ "$(cat /sys/module/zfs/parameters/zfs_arc_max)" = "$v" ] && exit 0;
|
||
echo "$v" > /sys/module/zfs/parameters/zfs_arc_max && echo gesetzt
|
||
register: arc
|
||
changed_when: "'gesetzt' in arc.stdout"
|
||
when: knaster_pve.cluster.zfs_arc_max_mb is defined
|
||
|
||
- name: Speicher local darf Snippets enthalten (clusterweite Einstellung, einmal)
|
||
ansible.builtin.shell: |
|
||
set -euo pipefail
|
||
c=$(pvesh get /storage/local --output-format json | python3 -c 'import json,sys; print(json.load(sys.stdin)["content"])')
|
||
case ",$c," in
|
||
*,snippets,*) echo unverändert ;;
|
||
*) pvesm set local --content "$c,snippets"; echo geändert ;;
|
||
esac
|
||
args:
|
||
executable: /bin/bash
|
||
register: snippets
|
||
changed_when: "'geändert' in snippets.stdout"
|
||
run_once: true
|
||
|
||
- name: Snippet-Verzeichnis
|
||
ansible.builtin.file:
|
||
path: "{{ pve_vendor_snippet | dirname }}"
|
||
state: directory
|
||
mode: "0755"
|
||
|
||
- name: cloud-init-Vendor-Daten für GPU-VMs (qemu-guest-agent meldet tofu die DHCP-Adresse)
|
||
ansible.builtin.copy:
|
||
dest: "{{ pve_vendor_snippet }}"
|
||
content: |
|
||
#cloud-config
|
||
packages: [qemu-guest-agent]
|
||
runcmd:
|
||
- [systemctl, enable, --now, qemu-guest-agent]
|
||
mode: "0644"
|
||
|
||
- name: Container auf diesem Knoten lesen
|
||
ansible.builtin.command: ls /etc/pve/lxc/
|
||
register: local_cts
|
||
check_mode: false
|
||
changed_when: false
|
||
|
||
- name: VMs auf diesem Knoten lesen
|
||
ansible.builtin.command: ls /etc/pve/qemu-server/
|
||
register: local_vms
|
||
check_mode: false
|
||
changed_when: false
|
||
|
||
# Infra-Gäste = Container (ct:) plus Dienst-VMs ohne GPU (vm:, D6); beide liegen auf tank mit HA
|
||
- name: Infra-Gäste zusammenstellen
|
||
ansible.builtin.set_fact:
|
||
pve_infra_guests: >-
|
||
{{ (knaster_pve.containers | dict2items | map('combine', {'kind': 'ct'}) | list)
|
||
+ (knaster_pve.service_vms | default({}) | dict2items | map('combine', {'kind': 'vm'}) | list) }}
|
||
pve_local_guest_confs: "{{ local_cts.stdout_lines + local_vms.stdout_lines }}"
|
||
|
||
- name: Bestehende Replikationsjobs lesen
|
||
ansible.builtin.command: pvesr list
|
||
register: pvesr_jobs
|
||
check_mode: false
|
||
changed_when: false
|
||
|
||
- name: ZFS-Replikation für Infra-Gäste (erst wenn der Container auf diesem Knoten existiert)
|
||
ansible.builtin.command: >
|
||
pvesr create-local-job {{ item.0.value.vmid }}-{{ idx }} {{ item.1 }}
|
||
--schedule '{{ knaster_pve.replication_schedule }}' --comment 'knaster {{ item.0.key }}'
|
||
loop: "{{ pve_infra_guests | subelements('value.replicate_to', skip_missing=True) }}"
|
||
vars:
|
||
idx: "{{ item.0.value.replicate_to.index(item.1) }}"
|
||
when:
|
||
- item.0.value.node == inventory_hostname
|
||
- (item.0.value.vmid | string) + '.conf' in pve_local_guest_confs
|
||
- (item.0.value.vmid | string) + '-' + (idx | string) not in pvesr_jobs.stdout
|
||
changed_when: true
|
||
|
||
- name: HA-Konfiguration lesen
|
||
ansible.builtin.command: ha-manager config
|
||
register: ha_config
|
||
check_mode: false
|
||
changed_when: false
|
||
|
||
- name: Infra-Gäste unter HA stellen (erst wenn der Container auf diesem Knoten existiert)
|
||
ansible.builtin.command: >
|
||
ha-manager add {{ item.kind }}:{{ item.value.vmid }} --state started --max_restart 1 --max_relocate 1
|
||
--comment 'knaster {{ item.key }}'
|
||
loop: "{{ pve_infra_guests }}"
|
||
when:
|
||
- item.value.ha | default(false)
|
||
- item.value.node == inventory_hostname
|
||
- (item.value.vmid | string) + '.conf' in pve_local_guest_confs
|
||
- (item.kind + ':' + (item.value.vmid | string)) not in ha_config.stdout
|
||
changed_when: true
|
||
|
||
- name: HA-Regeln lesen
|
||
ansible.builtin.command: ha-manager rules config
|
||
register: ha_rules
|
||
check_mode: false
|
||
changed_when: false
|
||
|
||
# Strikt: Die Gäste liegen auf tank, den gibt es nur auf dem Heimatknoten und den Replikationszielen
|
||
- name: Node-Affinity für Infra-Gäste (Heimatknoten bevorzugt, sonst Replikationsziele)
|
||
ansible.builtin.command: >
|
||
ha-manager rules add node-affinity knaster-{{ item.key }} --resources {{ item.kind }}:{{ item.value.vmid }}
|
||
--nodes {{ ([item.value.node ~ ':2'] + (item.value.replicate_to | map('regex_replace', '$', ':1') | list)) | join(',') }}
|
||
--strict 1 --comment 'knaster {{ item.key }}'
|
||
loop: "{{ pve_infra_guests }}"
|
||
when:
|
||
- item.value.ha | default(false)
|
||
- item.value.node == inventory_hostname
|
||
- (item.value.vmid | string) + '.conf' in pve_local_guest_confs
|
||
- ('knaster-' + item.key) not in ha_rules.stdout
|
||
changed_when: true
|