117 lines
6.1 KiB
YAML
117 lines
6.1 KiB
YAML
---
|
|
# =============================================================================
|
|
# proxmox_upgrade — migrate_guest.yml
|
|
# Handles migration of a single VM or LXC
|
|
# Called with loop_var: guest
|
|
# guest = { vmid, name, type, status, needs_fallback, fallback_reason }
|
|
# =============================================================================
|
|
|
|
- name: "Migrate | {{ guest.type | upper }} {{ guest.vmid }} ({{ guest.name }}) — skip check"
|
|
ansible.builtin.debug:
|
|
msg: "SKIPPING {{ guest.type | upper }} {{ guest.vmid }} ({{ guest.name }}) — live_migrate_fallback=skip, will go down during reboot"
|
|
when: guest.needs_fallback and live_migrate_fallback == 'skip'
|
|
delegate_to: localhost
|
|
|
|
- name: "Migrate | {{ guest.type | upper }} {{ guest.vmid }} ({{ guest.name }})"
|
|
when: not (guest.needs_fallback and live_migrate_fallback == 'skip')
|
|
block:
|
|
# ── Cold migration: shutdown first ───────────────────────────────────────
|
|
- name: "Migrate | {{ guest.vmid }} | Shutdown for cold migration"
|
|
ansible.builtin.uri:
|
|
url: "https://{{ api_host }}:{{ api_port }}/api2/json/nodes/{{ current_node }}/{{ 'qemu' if guest.type == 'qemu' else 'lxc' }}/{{ guest.vmid }}/status/shutdown"
|
|
method: POST
|
|
headers:
|
|
Authorization: "PVEAPIToken={{ api_token_id }}={{ api_token_secret }}"
|
|
body_format: json
|
|
body:
|
|
timeout: "{{ vm_shutdown_timeout }}"
|
|
forceStop: 1
|
|
validate_certs: false
|
|
when: guest.needs_fallback and live_migrate_fallback == 'shutdown' and guest.status == 'running'
|
|
delegate_to: localhost
|
|
|
|
- name: "Migrate | {{ guest.vmid }} | Wait for shutdown"
|
|
ansible.builtin.uri:
|
|
url: "https://{{ api_host }}:{{ api_port }}/api2/json/nodes/{{ current_node }}/{{ 'qemu' if guest.type == 'qemu' else 'lxc' }}/{{ guest.vmid }}/status/current"
|
|
method: GET
|
|
headers:
|
|
Authorization: "PVEAPIToken={{ api_token_id }}={{ api_token_secret }}"
|
|
validate_certs: false
|
|
register: vm_status
|
|
until: vm_status.json.data.status == 'stopped'
|
|
retries: "{{ (vm_shutdown_timeout | int / 5) | int }}"
|
|
delay: 5
|
|
when: guest.needs_fallback and live_migrate_fallback == 'shutdown' and guest.status == 'running'
|
|
delegate_to: localhost
|
|
|
|
# ── Trigger migration ─────────────────────────────────────────────────────
|
|
- name: "Migrate | {{ guest.vmid }} | Trigger migration to {{ migration_targets | first }}"
|
|
ansible.builtin.uri:
|
|
url: "https://{{ api_host }}:{{ api_port }}/api2/json/nodes/{{ current_node }}/{{ 'qemu' if guest.type == 'qemu' else 'lxc' }}/{{ guest.vmid }}/migrate"
|
|
method: POST
|
|
headers:
|
|
Authorization: "PVEAPIToken={{ api_token_id }}={{ api_token_secret }}"
|
|
body_format: json
|
|
body:
|
|
target: "{{ migration_targets | first }}"
|
|
online: "{{ 0 if (guest.needs_fallback and live_migrate_fallback == 'shutdown') else 1 }}"
|
|
validate_certs: false
|
|
register: migration_task
|
|
delegate_to: localhost
|
|
|
|
# ── Wait for migration to complete ────────────────────────────────────────
|
|
- name: "Migrate | {{ guest.vmid }} | Wait for migration task to complete"
|
|
ansible.builtin.uri:
|
|
url: "https://{{ api_host }}:{{ api_port }}/api2/json/nodes/{{ current_node }}/tasks/{{ migration_task.json.data }}/status"
|
|
method: GET
|
|
headers:
|
|
Authorization: "PVEAPIToken={{ api_token_id }}={{ api_token_secret }}"
|
|
validate_certs: false
|
|
register: task_status
|
|
until: task_status.json.data.status == 'stopped'
|
|
retries: 60
|
|
delay: 10
|
|
delegate_to: localhost
|
|
|
|
- name: "Migrate | {{ guest.vmid }} | Verify migration succeeded"
|
|
ansible.builtin.fail:
|
|
msg: "Migration of {{ guest.type | upper }} {{ guest.vmid }} ({{ guest.name }}) failed — {{ task_status.json.data.exitstatus }}"
|
|
when: task_status.json.data.exitstatus != 'OK'
|
|
delegate_to: localhost
|
|
|
|
# ── Cold migration: restart on target ────────────────────────────────────
|
|
- name: "Migrate | {{ guest.vmid }} | Start on target node after cold migration"
|
|
ansible.builtin.uri:
|
|
url: "https://{{ api_host }}:{{ api_port }}/api2/json/nodes/{{ migration_targets | first }}/{{ 'qemu' if guest.type == 'qemu' else 'lxc' }}/{{ guest.vmid }}/status/start"
|
|
method: POST
|
|
headers:
|
|
Authorization: "PVEAPIToken={{ api_token_id }}={{ api_token_secret }}"
|
|
validate_certs: false
|
|
when: guest.needs_fallback and live_migrate_fallback == 'shutdown' and guest.status == 'running'
|
|
delegate_to: localhost
|
|
|
|
- name: "Migrate | {{ guest.vmid }} | Wait for VM to start on target"
|
|
ansible.builtin.uri:
|
|
url: "https://{{ api_host }}:{{ api_port }}/api2/json/nodes/{{ migration_targets | first }}/{{ 'qemu' if guest.type == 'qemu' else 'lxc' }}/{{ guest.vmid }}/status/current"
|
|
method: GET
|
|
headers:
|
|
Authorization: "PVEAPIToken={{ api_token_id }}={{ api_token_secret }}"
|
|
validate_certs: false
|
|
register: vm_start_status
|
|
until: vm_start_status.json.data.status == 'running'
|
|
retries: "{{ (vm_start_timeout | int / 5) | int }}"
|
|
delay: 5
|
|
when: guest.needs_fallback and live_migrate_fallback == 'shutdown' and guest.status == 'running'
|
|
delegate_to: localhost
|
|
|
|
- name: "Migrate | {{ guest.vmid }} ({{ guest.name }}) | Migration complete"
|
|
ansible.builtin.debug:
|
|
msg: >-
|
|
{{ guest.type | upper }} {{ guest.vmid }} ({{ guest.name }})
|
|
{% if guest.needs_fallback and live_migrate_fallback == 'shutdown' %}
|
|
cold migrated to {{ migration_targets | first }} and restarted
|
|
{% else %}
|
|
live migrated to {{ migration_targets | first }}
|
|
{% endif %}
|
|
delegate_to: localhost
|