refactor: proxmox_upgrade use inline Python for all API calls
This commit is contained in:
@@ -5,59 +5,77 @@
|
||||
# Only runs if migration_restore: true
|
||||
# =============================================================================
|
||||
|
||||
- name: Restore | Skip restore
|
||||
- name: "Restore | Skip — migration_restore=false"
|
||||
ansible.builtin.debug:
|
||||
msg: "migration_restore=false — leaving guests on their current nodes"
|
||||
when: not migration_restore | bool
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Restore | Migrate guests back to {{ current_node }}
|
||||
- name: "Restore | Migrate guests back to {{ current_node }}"
|
||||
when: migration_restore | bool
|
||||
block:
|
||||
- name: Restore | Get guests currently on other nodes that originated from {{ current_node }}
|
||||
- name: "Restore | Migrate all guests back to {{ current_node }}"
|
||||
ansible.builtin.shell: |
|
||||
python3 << 'PYEOF'
|
||||
import urllib.request, json, ssl, time
|
||||
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
api_base = "https://{{ api_host }}:{{ api_port }}/api2/json"
|
||||
headers = {"Authorization": "PVEAPIToken={{ api_token_id }}={{ api_token_secret }}"}
|
||||
node = "{{ current_node }}"
|
||||
source = "{{ migration_targets | first }}"
|
||||
plan = {{ migration_plan | to_json }}
|
||||
fallback = "{{ live_migrate_fallback }}"
|
||||
|
||||
def api_req(path, method="GET", body=None):
|
||||
url = f"{api_base}{path}"
|
||||
data = json.dumps(body).encode() if body else None
|
||||
hdrs = {**headers}
|
||||
if data:
|
||||
hdrs["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=hdrs, method=method)
|
||||
with urllib.request.urlopen(req, context=ctx) as r:
|
||||
return json.loads(r.read())["data"]
|
||||
|
||||
task_ids = []
|
||||
for guest in plan:
|
||||
if guest["needs_fallback"] and fallback == "skip":
|
||||
print(f"SKIP restore: {guest['type'].upper()} {guest['vmid']} ({guest['name']}) — was skipped during drain")
|
||||
continue
|
||||
gtype = guest["type"]
|
||||
online = 0 if (guest["needs_fallback"] and fallback == "shutdown") else 1
|
||||
print(f"Restoring {gtype.upper()} {guest['vmid']} ({guest['name']}) → {node} (online={online})...")
|
||||
task_id = api_req(f"/nodes/{source}/{gtype}/{guest['vmid']}/migrate", "POST",
|
||||
{"target": node, "online": online})
|
||||
task_ids.append({"vmid": guest["vmid"], "name": guest["name"], "task": task_id, "type": gtype})
|
||||
|
||||
failed = []
|
||||
for t in task_ids:
|
||||
for _ in range(60):
|
||||
status = api_req(f"/nodes/{source}/tasks/{t['task']}/status")
|
||||
if status["status"] == "stopped":
|
||||
if status.get("exitstatus") != "OK":
|
||||
failed.append(f"{t['name']} ({t['vmid']}): {status.get('exitstatus')}")
|
||||
else:
|
||||
print(f"OK: {t['name']} ({t['vmid']}) restored to {node}")
|
||||
break
|
||||
time.sleep(10)
|
||||
else:
|
||||
failed.append(f"{t['name']} ({t['vmid']}): timed out")
|
||||
|
||||
if failed:
|
||||
print("FAILED restores: " + ", ".join(failed))
|
||||
exit(1)
|
||||
print(f"All guests restored to {node}")
|
||||
PYEOF
|
||||
register: restore_result
|
||||
delegate_to: localhost
|
||||
changed_when: true
|
||||
|
||||
- name: "Restore | Log result"
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
Restoring {{ migration_plan | rejectattr('needs_fallback') | list | length +
|
||||
(migration_plan | selectattr('needs_fallback') | list | length if live_migrate_fallback != 'skip' else 0) }}
|
||||
guests back to {{ current_node }}
|
||||
|
||||
- name: Restore | Migrate each guest back
|
||||
ansible.builtin.uri:
|
||||
url: "https://{{ api_host }}:{{ api_port }}/api2/json/nodes/{{ migration_targets | first }}/{{ '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: "{{ current_node }}"
|
||||
online: "{{ 0 if (guest.needs_fallback and live_migrate_fallback == 'shutdown') else 1 }}"
|
||||
validate_certs: false
|
||||
register: restore_task
|
||||
loop: "{{ migration_plan | rejectattr('needs_fallback') | list }}"
|
||||
loop_control:
|
||||
loop_var: guest
|
||||
msg: "{{ restore_result.stdout_lines }}"
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Restore | Wait for all restore migrations to complete
|
||||
ansible.builtin.uri:
|
||||
url: "https://{{ api_host }}:{{ api_port }}/api2/json/nodes/{{ migration_targets | first }}/tasks/{{ item.json.data }}/status"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: "PVEAPIToken={{ api_token_id }}={{ api_token_secret }}"
|
||||
validate_certs: false
|
||||
register: restore_status
|
||||
until: restore_status.json.data.status == 'stopped'
|
||||
retries: 60
|
||||
delay: 10
|
||||
loop: "{{ restore_task.results }}"
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Restore | Check all restores succeeded
|
||||
ansible.builtin.fail:
|
||||
msg: "Restore migration failed — {{ item.json.data.exitstatus }}"
|
||||
loop: "{{ restore_status.results }}"
|
||||
when: item.json.data.exitstatus != 'OK'
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Restore | Complete
|
||||
ansible.builtin.debug:
|
||||
msg: "All guests restored to {{ current_node }}"
|
||||
|
||||
Reference in New Issue
Block a user