diff --git a/.ansible/.lock b/.ansible/.lock new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index 3edfbf7..d94a839 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# role_modele +# role_proxmox -Modèle \ No newline at end of file +Manage Proxmox VE instances. diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..e631ec5 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,94 @@ +--- +# vars file for proxmox + +# Informations to connect to API +proxmox_api_host: "" +proxmox_api_user: "" +proxmox_api_token_id: "" +proxmox_api_token_secret: "" +proxmox_api_validate_certs: false + +# Proxmox VE node +## Node +proxmox_node_interfaces: [] + +## Cluster +proxmox_cluster_name: "" +# proxmox_cluster_link0: "" +# proxmox_cluster_link1: "" + +## Storage +proxmox_storage: [] + +# Images list to download +proxmox_images: [] +# Example: +# - url: "https://cloud.debian.org/images/cloud/bookworm-backports/latest/debian-12-backports-genericcloud-amd64.qcow2" +# dest: /mnt/pve/pumbaa/disk_image/bookworm.qcow2 + +# Proxmox VE instance +## Delegate tasks to Proxmox VE host (local API call !) +proxmox_delegate_to: "" + +## Type of instance (ct or vm) +proxmox_instance_type: "" + +## Instance configuration +proxmox_instance_node: "" +proxmox_instance_autostart: true +proxmox_instance_args: "" +proxmox_instance_cores: 1 +proxmox_instance_cpu: kvm64 +proxmox_instance_full: false +proxmox_instance_hotplug: + - cpu + - disk + - memory + - network + - usb +proxmox_instance_ipconfig: {} +proxmox_instance_memory: 1024 +proxmox_instance_migrate: true +proxmox_instance_net: {} +proxmox_instance_numa: true +proxmox_instance_onboot: true +proxmox_instance_protection: true +proxmox_instance_vmid: "" + +proxmox_instance_disks: [] +# Example: +# - disk: virtio0 +# storage: ssd120 +# size: 8 +# img: "{{ proxmox_images[0].dest }}" +# - disk: virtio1 +# backup: true +# mbps: 10 +# storage: nfs +# size: 10 + +proxmox_instance_cloudinit: true +proxmox_instance_cloudinit_cipassword: "" +proxmox_instance_cloudinit_ciuser: "" +proxmox_instance_cloudinit_nameservers: "" +proxmox_instance_cloudinit_searchdomains: null +proxmox_instance_cloudinit_sshkeys: "" + +## HA configuration +proxmox_instance_ha: + max_restart: 3 + max_relocate: 2 + state: started + +## Configuration to wait SSH +proxmox_instance_ssh_ip: "" +proxmox_instance_ssh_port: "" + +## Start instance after installation +proxmox_start_instance: true + +## Reboot instance when changed +proxmox_reboot_instance: true + +## Start qemu-guest-agent.service at the end +proxmox_start_agent: true diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..5c9159b --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,90 @@ +--- +# handlers file for proxmox + +- name: Configure cloud-init + community.proxmox.proxmox_kvm: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + agent: "enabled=1,fstrim_cloned_disks=1" + cipassword: "{{ proxmox_instance_cloudinit_cipassword }}" + ciuser: "{{ proxmox_instance_cloudinit_ciuser }}" + ciupgrade: false + ide: + ide2: "{{ proxmox_instance_disks[0].storage }}:cloudinit" + ipconfig: "{{ proxmox_instance_ipconfig }}" + name: "{{ inventory_hostname }}" + nameservers: "{{ proxmox_instance_cloudinit_nameservers }}" + node: "{{ proxmox_instance_node }}" + searchdomains: "{{ proxmox_instance_cloudinit_searchdomains }}" + sshkeys: "{{ proxmox_instance_cloudinit_sshkeys }}" + vmid: "{{ proxmox_instance_vmid }}" + update: true + update_unsafe: true + when: proxmox_instance_cloudinit + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + +- name: Start instance + community.proxmox.proxmox_kvm: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + name: "{{ inventory_hostname }}" + node: "{{ proxmox_instance_node }}" + state: started + when: + - proxmox_start_instance + - not rebooted.changed + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + +- name: Wait SSH port is open + ansible.builtin.wait_for: + host: "{{ proxmox_instance_ssh_ip }}" + port: "{{ proxmox_instance_ssh_port }}" + search_regex: OpenSSH + delay: 30 + when: proxmox_start_instance + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + +- name: Wait cloud-init + ansible.builtin.command: + cmd: cloud-init status + when: proxmox_instance_cloudinit + register: cloudinit_status + changed_when: cloudinit_status.stdout_lines | select("search", "done") | list | length > 0 + failed_when: cloudinit_status.rc == 1 + until: cloudinit_status.stdout_lines | select("search", "done") | list | length > 0 + retries: 100 + delay: 36 + ignore_errors: true + +- name: Disable maintenance mode on node + ansible.builtin.command: + argv: + - ha-manager + - crm-command + - node-maintenance + - disable + - "{{ inventory_hostname_short }}" + become: true + register: proxmox_maintenance_disabled + changed_when: proxmox_maintenance_disabled.rc == 0 + listen: Disable maintenance mode + +# TO DEBUG: +#- name: Wait until node quits maintenance mode +# ansible.builtin.command: +# argv: +# - ha-manager +# - status +# become: true +# register: proxmox_ha_manager_status +# changed_when: >- +# proxmox_ha_manager_status.stdout_lines | select("search", "lrm " + inventory_hostname_short +" (active") | list | length > 0 +# retries: 20 +# delay: 30 +# until: >- +# proxmox_ha_manager_status.stdout_lines | select("search", "lrm " + inventory_hostname_short + " (active") | list | length > 0 +# listen: Disable maintenance mode diff --git a/meta/main.yml b/meta/main.yml index c58bebf..91807c6 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -1,7 +1,7 @@ galaxy_info: namespace: ykn author: pulsar89.5 - description: Rôle modèle + description: Manage Proxmox VE instances license: GPL-3.0-or-later @@ -10,6 +10,6 @@ galaxy_info: platforms: - name: Debian versions: - - all + - bookworm dependencies: [] diff --git a/tasks/instance_vm_configuration.yml b/tasks/instance_vm_configuration.yml new file mode 100644 index 0000000..0aab83e --- /dev/null +++ b/tasks/instance_vm_configuration.yml @@ -0,0 +1,149 @@ +--- +# tasks file for proxmox + +- name: Build disks list + ansible.builtin.set_fact: + proxmox_instance_disks: "{{ proxmox_instance_disks + specific }}" + when: specific | length > 0 + loop: "{{ lookup('ansible.builtin.varnames', '^proxmox_instance_disks_.+', wantlist=True) }}" + vars: + specific: "{{ lookup('ansible.builtin.vars', item, default='') }}" + +- name: Configure disks + community.proxmox.proxmox_disk: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + aio: "{{ item.aio | default(omit) }}" + backup: "{{ item.backup | default(omit) }}" + cache: "{{ item.cache | default(omit) }}" + disk: "{{ item.disk }}" + iothread: "{{ item.iothread | default(omit) }}" + mbps: "{{ item.mbps | default(omit) }}" + name: "{{ inventory_hostname }}" + storage: "{{ item.storage }}" + size: "{{ item.size }}" + state: present + vmid: "{{ proxmox_instance_vmid }}" + loop: "{{ proxmox_instance_disks }}" + loop_control: + label: "{{ item.disk }}" + index_var: disk_number + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + +- name: Get instance informations + community.proxmox.proxmox_vm_info: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + config: current + name: "{{ inventory_hostname }}" + vmid: "{{ proxmox_instance_vmid }}" + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + register: instance_current_infos + +- name: Grow disk size + community.proxmox.proxmox_disk: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + aio: "{{ item.aio | default(omit) }}" + backup: "{{ item.backup | default(omit) }}" + cache: "{{ item.cache | default(omit) }}" + disk: "{{ item.disk }}" + iothread: "{{ item.iothread | default(omit) }}" + mbps: "{{ item.mbps | default(omit) }}" + name: "{{ inventory_hostname }}" + storage: "{{ item.storage }}" + size: "{{ item.size }}G" + state: resized + vmid: "{{ proxmox_instance_vmid }}" + when: + - proxmox_instance_disks | length > 0 + - instance_current_infos.proxmox_vms | length > 0 + - formated_size not in instance_current_infos.proxmox_vms[0].config[item.disk] + loop: "{{ proxmox_instance_disks }}" + loop_control: + label: "{{ item.disk }}" + index_var: disk_number + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + vars: + formated_size: "size={{ item.size }}G" + +- name: Reconfigure instance + community.proxmox.proxmox_kvm: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + agent: "enabled=1,fstrim_cloned_disks=1" + autostart: "{{ proxmox_instance_autostart }}" + cores: "{{ proxmox_instance_cores }}" + cpu: "{{ proxmox_instance_cpu }}" + hotplug: "{{ proxmox_instance_hotplug | join(',') }}" + ipconfig: "{{ proxmox_instance_ipconfig }}" + memory: "{{ proxmox_instance_memory }}" + name: "{{ inventory_hostname }}" + nameservers: "{{ proxmox_instance_cloudinit_nameservers }}" + node: "{{ proxmox_instance_node }}" + numa_enabled: "{{ proxmox_instance_numa }}" + onboot: "{{ proxmox_instance_onboot }}" + protection: "{{ proxmox_instance_protection }}" + tablet: false + vmid: "{{ proxmox_instance_vmid }}" + update: true + update_unsafe: true + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + +- name: Get changed information about the instance + community.proxmox.proxmox_vm_info: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + config: pending + name: "{{ inventory_hostname }}" + vmid: "{{ proxmox_instance_vmid }}" + node: "{{ proxmox_instance_node }}" + when: not create_instance.changed + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + register: instance_pending_infos + +- name: Reboot the instance + community.proxmox.proxmox_kvm: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + name: "{{ inventory_hostname }}" + node: "{{ proxmox_instance_node }}" + state: restarted + timeout: 300 + when: + - proxmox_reboot_instance + - not create_instance.changed + - instance_current_infos.proxmox_vms[0].config != instance_pending_infos.proxmox_vms[0].config + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + notify: Wait SSH port is open + register: rebooted + +- name: Flush handlers + ansible.builtin.meta: flush_handlers + +- name: Ensure instance is started + community.proxmox.proxmox_kvm: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + name: "{{ inventory_hostname }}" + node: "{{ proxmox_instance_node }}" + state: started + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + notify: Wait SSH port is open + +- name: Flush handlers + ansible.builtin.meta: flush_handlers diff --git a/tasks/instance_vm_ha.yml b/tasks/instance_vm_ha.yml new file mode 100644 index 0000000..648f580 --- /dev/null +++ b/tasks/instance_vm_ha.yml @@ -0,0 +1,44 @@ +--- +# tasks file for proxmox + +- name: Build HA resources (vm:) for node + set_fact: + ha_resources_for_node: >- + {{ ['vm:'] | product( + query('inventory_hostnames', 'type_proxmox_kvm:&zone_gaia') | + sort | + map('extract', hostvars) | + selectattr('proxmox_instance_node', 'equalto', proxmox_instance_node) | + map(attribute='proxmox_instance_vmid') + ) | map('join') | list }} + +- name: Ensure HA services exist for VMs + community.proxmox.proxmox_cluster_ha_resources: + api_host: "{{ proxmox_api_host }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + api_user: "{{ proxmox_api_user }}" + comment: "Managed by Ansible" + hastate: "{{ proxmox_instance_ha.state }}" + max_relocate: "{{ proxmox_instance_ha.max_relocate }}" + max_restart: "{{ proxmox_instance_ha.max_restart }}" + name: "vm:{{ proxmox_instance_vmid }}" + state: present + become: true + delegate_to: "{{ proxmox_delegate_to }}" + +- name: Create HA node affinity rule + community.proxmox.proxmox_cluster_ha_rules: + api_host: "{{ proxmox_api_host }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + api_user: "{{ proxmox_api_user }}" + comment: Managed by ansible + disable: false + name: "{{ proxmox_instance_node }}" + nodes: ["{{ proxmox_instance_node }}"] + resources: "{{ ha_resources_for_node }}" + state: present + type: node-affinity + become: true + delegate_to: "{{ proxmox_delegate_to }}" diff --git a/tasks/instance_vm_template.yml b/tasks/instance_vm_template.yml new file mode 100644 index 0000000..f878c9b --- /dev/null +++ b/tasks/instance_vm_template.yml @@ -0,0 +1,71 @@ +--- +# tasks file for proxmox + +- name: Create instance + community.proxmox.proxmox_kvm: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + agent: "enabled=1,fstrim_cloned_disks=1" + autostart: "{{ proxmox_instance_autostart }}" + cores: "{{ proxmox_instance_cores }}" + cpu: "{{ proxmox_instance_cpu }}" + hotplug: "{{ proxmox_instance_hotplug | join(',') }}" + ipconfig: "{{ proxmox_instance_ipconfig }}" + memory: "{{ proxmox_instance_memory }}" + name: "{{ inventory_hostname }}" + nameservers: "{{ proxmox_instance_cloudinit_nameservers }}" + net: "{{ proxmox_instance_net }}" + node: "{{ proxmox_instance_node }}" + numa_enabled: "{{ proxmox_instance_numa }}" + onboot: "{{ proxmox_instance_onboot }}" + scsihw: virtio-scsi-single + tablet: false + vmid: "{{ proxmox_instance_vmid }}" + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + register: create_instance + notify: + - Configure cloud-init + - Start instance + - Wait SSH port is open + - Wait cloud-init + +- name: Import virtual disk + ansible.builtin.command: + cmd: >- + qm set {{ proxmox_instance_vmid }} + --{{ proxmox_instance_disks[0].disk }} + {{ proxmox_instance_disks[0].storage }}:0,import-from={{ proxmox_instance_disks[0].img }} + chdir: "{{ proxmox_instance_disks[0].img | ansible.builtin.dirname }}" + when: create_instance.changed # noqa: no-handler no-changed-when + become: true + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + +- name: Workaround to add args to the VM + ansible.builtin.lineinfile: + path: /etc/pve/qemu-server/{{ proxmox_instance_vmid }}.conf + line: "args: {{ proxmox_instance_args }}" + state: present + when: proxmox_instance_args | length > 0 + become: true + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + +- name: Add configuration items + community.proxmox.proxmox_kvm: + api_host: "{{ proxmox_api_host }}" + api_user: "{{ proxmox_api_user }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + boot: order=virtio0 + name: "{{ inventory_hostname }}" + node: "{{ proxmox_instance_node }}" + serial: + serial0: socket + update: true + update_unsafe: true + vmid: "{{ proxmox_instance_vmid }}" + delegate_to: "{{ proxmox_delegate_to | default(omit) }}" + notify: + - Start instance + - Wait SSH port is open diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..4734590 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,26 @@ +--- +# tasks file for proxmox + +# Node +- name: Import Proxmox VE configuration tasks + ansible.builtin.include_tasks: + file: node_configuration.yml + when: proxmox_instance_type | length == 0 + +# Instance: vm +- name: Import instance creation tasks + ansible.builtin.include_tasks: + file: instance_vm_template.yml + when: proxmox_instance_type == "vm" + +- name: Import instance configuration tasks + ansible.builtin.include_tasks: + file: instance_vm_configuration.yml + when: proxmox_instance_type == "vm" + +- name: Import HA configuration tasks + ansible.builtin.include_tasks: + file: instance_vm_ha.yml + when: + - proxmox_instance_type == "vm" + - proxmox_instance_ha | length > 0 diff --git a/tasks/node_configuration.yml b/tasks/node_configuration.yml new file mode 100644 index 0000000..10c1a7a --- /dev/null +++ b/tasks/node_configuration.yml @@ -0,0 +1,120 @@ +--- +# tasks file for proxmox + +- name: Install prerequisite + ansible.builtin.apt: + name: python3-proxmoxer + update_cache: true + become: true + +- name: Manage Proxmox VE node bridge interface + community.proxmox.proxmox_node_network: + api_host: "{{ proxmox_api_host }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + api_user: "{{ proxmox_api_user }}" + autostart: "{{ item.autostart | default(true) }}" + cidr: "{{ item.cidr | default(omit) }}" + cidr6: "{{ item.cidr6 | default(omit) }}" + comments: "{{ item.comments | default(omit) }}" + gateway: "{{ item.gateway | default(omit) }}" + gateway6: "{{ item.gateway6 | default(omit) }}" + iface_type: bridge + iface: "{{ item.iface }}" + mtu: "{{ item.mtu | default(omit) }}" + node: "{{ inventory_hostname_short }}" + state: "{{ item.states | default('present') }}" + validate_certs: "{{ proxmox_api_validate_certs }}" + become: true + loop: "{{ proxmox_node_interfaces | selectattr('type', 'equalto', 'bridge') }}" + loop_control: + label: "{{ item.iface }}" + +- name: Manage Proxmox VE node eth interface + community.proxmox.proxmox_node_network: + api_host: "{{ proxmox_api_host }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + api_user: "{{ proxmox_api_user }}" + autostart: "{{ item.autostart | default(true) }}" + cidr: "{{ item.cidr | default(omit) }}" + cidr6: "{{ item.cidr6 | default(omit) }}" + comments: "{{ item.comments | default(omit) }}" + gateway: "{{ item.gateway | default(omit) }}" + gateway6: "{{ item.gateway6 | default(omit) }}" + iface_type: eth + iface: "{{ item.iface }}" + mtu: "{{ item.mtu | default(omit) }}" + node: "{{ inventory_hostname_short }}" + state: "{{ item.states | default('present') }}" + validate_certs: "{{ proxmox_api_validate_certs }}" + become: true + loop: "{{ proxmox_node_interfaces | selectattr('type', 'equalto', 'eth') }}" + loop_control: + label: "{{ item.iface }}" + +- name: Proxmox VE cluster management tasks + when: proxmox_cluster_name | length > 0 + block: + - name: List existing Proxmox VE cluster join information + community.proxmox.proxmox_cluster_join_info: + api_host: "{{ proxmox_api_host }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + api_user: "{{ proxmox_api_user }}" + validate_certs: "{{ proxmox_api_validate_certs }}" + become: true + register: proxmox_cluster_join + + - name: Define variables to join an existing Proxmox VE cluster + ansible.builtin.set_fact: + proxmox_cluster_name: "{{ proxmox_cluster_join.cluster_join.totem.cluster_name }}" + proxmox_cluster_master_ip: "{{ proxmox_cluster_join.cluster_join.nodelist[0].pve_addr }}" + proxmox_cluster_fingerprint: "{{ proxmox_cluster_join.cluster_join.nodelist[0].pve_fp }}" + when: proxmox_cluster_join.cluster_join.nodelist | length > 0 + + - name: Manage Proxmox VE Cluster + community.proxmox.proxmox_cluster: + api_host: "{{ proxmox_api_host }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + api_user: "{{ proxmox_api_user }}" + cluster_name: "{{ proxmox_cluster_name }}" + fingerprint: "{{ proxmox_cluster_fingerprint | default(omit) }}" + link0: "{{ proxmox_cluster_link0 | default(omit) }}" + link1: "{{ proxmox_cluster_link1 | default(omit) }}" + master_ip: "{{ proxmox_cluster_master_ip | default(omit) }}" + state: "{{ item.states | default('present') }}" + validate_certs: "{{ proxmox_api_validate_certs }}" + become: true + when: >- + proxmox_cluster_join.cluster_join.nodelist | length == 0 + or + proxmox_cluster_join.cluster_join.nodelist | selectattr('name', 'equalto', inventory_hostname_short) | list | length == 0 + +- name: Manage Proxmox VE storage + community.proxmox.proxmox_storage: + api_host: "{{ proxmox_api_host }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + api_user: "{{ proxmox_api_user }}" + content: "{{ item.content }}" + name: "{{ item.name }}" + nfs_options: "{{ item.nfs_options | default(omit) }}" + nodes: "{{ item.nodes }}" + state: "{{ item.states | default('present') }}" + type: "{{ item.type }}" + validate_certs: "{{ proxmox_api_validate_certs }}" + loop: "{{ proxmox_storage }}" + loop_control: + label: "{{ item.name }}" + +- name: Download images + ansible.builtin.get_url: + url: "{{ item.url }}" + dest: "{{ item.dest }}" + mode: u=rw,g=r,o=r + become: true + loop: "{{ proxmox_images }}" + loop_control: + label: "{{ item.dest | ansible.builtin.basename }}" diff --git a/tasks/node_update.yml b/tasks/node_update.yml new file mode 100644 index 0000000..f3aba5f --- /dev/null +++ b/tasks/node_update.yml @@ -0,0 +1,92 @@ +--- + +- name: Update apt cache + ansible.builtin.apt: + update_cache: true + become: true + +- name: List upgradable packages + ansible.builtin.command: + argv: + - apt + - list + - --upgradable + register: apt_upgradable + changed_when: apt_upgradable.stdout_lines | length > 1 + +- name: Upgrade tasks + when: apt_upgradable.stdout_lines | length > 1 + block: + - name: Enable maintenance node + ansible.builtin.command: + argv: + - ha-manager + - crm-command + - node-maintenance + - enable + - "{{ inventory_hostname_short }}" + become: true + register: proxmox_maintenance_enabled + changed_when: proxmox_maintenance_enabled.rc == 0 + notify: Disable maintenance mode + + - name: Wait for the instances to be migrated + community.proxmox.proxmox_vm_info: + api_host: "{{ proxmox_api_host }}" + api_token_id: "{{ proxmox_api_token_id }}" + api_token_secret: "{{ proxmox_api_token_secret }}" + api_user: "{{ proxmox_api_user }}" + node: "{{ inventory_hostname_short }}" + type: all + validate_certs: "{{ proxmox_api_validate_certs }}" + become: true + register: proxmox_current_instances + retries: 20 + delay: 30 + until: proxmox_current_instances.proxmox_vms | selectattr('hastate', 'defined') | selectattr('hastate', 'equalto', 'started') | list | length == 0 + + - name: Run the full-upgrade + ansible.builtin.apt: + upgrade: dist + become: true + + - name: Reboot + ansible.builtin.reboot: + become: true + + - name: Clean apt things + ansible.builtin.apt: + clean: true + become: true + + - name: List installed packages + ansible.builtin.command: # noqa: no-changed-when + argv: + - apt + - list + - --installed + register: apt_list + check_mode: false + + - name: Remove old kernels + ansible.builtin.apt: + name: "{{ installed_kernels[:-2] }}" + state: absent + when: installed_kernels | length > 2 + become: true + vars: + installed_kernels: + apt_list.stdout_lines | + select('search', '^proxmox-kernel') | + select('search', 'automatic') | + split('/') | first | list + check_mode: true + + - name: Autoremove apt packages + ansible.builtin.apt: + autoremove: true + purge: true + become: true + +- name: Flush handlers + ansible.builtin.meta: flush_handlers