semaphore/synology_dsm_upgrade_api.yml

227 lines
8.4 KiB
YAML

---
- name: Synology DSM - upgrade via DSM API (Semaphore/legacy compatible)
hosts: synology
gather_facts: false
vars:
syno_scheme: "https"
syno_port: 5001
syno_verify_ssl: false
syno_user: "{{ vault_syno_user }}"
syno_pass: "{{ vault_syno_pass }}"
syno_session: "DSM"
uri_timeout: 60
refuse_if_no_upgrade_api: true
# Méthodes candidates (DSM varie selon versions)
upgrade_status_methods:
- "status"
- "get"
- "check"
# Méthode "start" pour déclencher l'upgrade
upgrade_start_method: "start"
# Attente reboot (sec)
reboot_wait_timeout: 1800 # 30 min
reboot_down_timeout: 300 # 5 min
reboot_up_timeout: 1800 # 30 min
tasks:
- name: Construire base_url
ansible.builtin.set_fact:
base_url: "{{ syno_scheme }}://{{ inventory_hostname }}:{{ syno_port }}"
- name: Discover SYNO.API.Auth & SYNO.Core.Upgrade via SYNO.API.Info
ansible.builtin.uri:
url: "{{ base_url }}/webapi/entry.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth,SYNO.Core.Upgrade"
method: GET
return_content: true
validate_certs: "{{ syno_verify_ssl }}"
status_code: 200
timeout: "{{ uri_timeout }}"
register: api_info
failed_when: api_info.json.success is not defined or api_info.json.success != true
- name: Extraire info Auth/Upgrade
ansible.builtin.set_fact:
auth_info: "{{ api_info.json.data['SYNO.API.Auth'] | default({}) }}"
upgrade_info: "{{ api_info.json.data['SYNO.Core.Upgrade'] | default({}) }}"
- name: Fail si SYNO.Core.Upgrade absent (optionnel)
ansible.builtin.fail:
msg: >-
L'API SYNO.Core.Upgrade n'est pas exposée sur ce NAS via /webapi.
Solution de repli: SSH (synoupgrade) ou mise à jour manuelle DSM.
when:
- refuse_if_no_upgrade_api | bool
- (upgrade_info | length) == 0
- name: Définir chemins et versions
ansible.builtin.set_fact:
auth_path: "{{ auth_info.path | default('auth.cgi') }}"
upgrade_path: "{{ upgrade_info.path | default('entry.cgi') }}"
upgrade_ver: "{{ upgrade_info.maxVersion | default(1) }}"
- name: Login DSM API (SYNO.API.Auth) - try v6 then v2
block:
- name: Login v6
ansible.builtin.uri:
url: "{{ base_url }}/webapi/{{ auth_path }}?api=SYNO.API.Auth&version=6&method=login&account={{ syno_user | urlencode }}&passwd={{ syno_pass | urlencode }}&session={{ syno_session | urlencode }}&format=sid"
method: GET
validate_certs: "{{ syno_verify_ssl }}"
return_content: true
status_code: 200
timeout: "{{ uri_timeout }}"
register: login_v6
failed_when: login_v6.json.success != true
- name: Set login result from v6
ansible.builtin.set_fact:
login: "{{ login_v6 }}"
rescue:
- name: Login v2 (fallback)
ansible.builtin.uri:
url: "{{ base_url }}/webapi/{{ auth_path }}?api=SYNO.API.Auth&version=2&method=login&account={{ syno_user | urlencode }}&passwd={{ syno_pass | urlencode }}&session={{ syno_session | urlencode }}&format=sid"
method: GET
validate_certs: "{{ syno_verify_ssl }}"
return_content: true
status_code: 200
timeout: "{{ uri_timeout }}"
register: login_v2
failed_when: login_v2.json.success != true
- name: Set login result from v2
ansible.builtin.set_fact:
login: "{{ login_v2 }}"
- name: Enregistrer SID
ansible.builtin.set_fact:
sid: "{{ login.json.data.sid }}"
# ---- STATUS (on teste plusieurs méthodes) ----
- name: Interroger statut upgrade (essais multiples)
ansible.builtin.uri:
url: "{{ base_url }}/webapi/{{ upgrade_path }}?api=SYNO.Core.Upgrade&version={{ upgrade_ver }}&method={{ item }}&_sid={{ sid }}"
method: GET
validate_certs: "{{ syno_verify_ssl }}"
return_content: true
status_code: 200
timeout: "{{ uri_timeout }}"
loop: "{{ upgrade_status_methods }}"
register: upgrade_status_attempts
failed_when: false
- name: Choisir le premier statut qui répond success=true
ansible.builtin.set_fact:
upgrade_status: >-
{{
(upgrade_status_attempts.results
| selectattr('json', 'defined')
| selectattr('json.success', 'defined')
| selectattr('json.success', 'equalto', true)
| list
| first) | default({})
}}
- name: Fail si aucune méthode status n'a fonctionné
ansible.builtin.fail:
msg: >-
Impossible de récupérer un statut upgrade via SYNO.Core.Upgrade.
Vérifie les méthodes supportées (status/get/check) sur ce DSM.
when: upgrade_status | length == 0
- name: Debug - statut retenu
ansible.builtin.debug:
var: upgrade_status.json
# ---- READY CHECK (inclut ton cas "ready_upgrade") ----
- name: Déterminer si une mise à jour est prête
ansible.builtin.set_fact:
upgrade_ready: >-
{{
(
(upgrade_status.json.data is defined)
and (
(upgrade_status.json.data.allow_upgrade is defined and upgrade_status.json.data.allow_upgrade | bool)
and (
(upgrade_status.json.data.status is defined and (upgrade_status.json.data.status|string) in
['ready','downloaded','prepared','ready_upgrade']
)
or (upgrade_status.json.data.ready is defined and upgrade_status.json.data.ready | bool)
)
)
) | default(false)
}}
- name: Stop si pas prête (évite code 103) + message clair
ansible.builtin.fail:
msg: >-
DSM n'est pas prêt à lancer l'upgrade via API (souvent code 103 si on force).
Vérifie sur DSM: Panneau de config > Mise à jour DSM :
- téléchargement terminé
- EULA/consentement accepté
- aucune upgrade en cours
Statut API (data): {{ upgrade_status.json.data | default({}) | to_nice_json }}
when: not upgrade_ready
# ---- START (POST form-urlencoded, plus fiable) ----
- name: Start DSM upgrade (SYNO.Core.Upgrade start)
ansible.builtin.uri:
url: "{{ base_url }}/webapi/{{ upgrade_path }}"
method: POST
validate_certs: "{{ syno_verify_ssl }}"
return_content: true
status_code: 200
timeout: "{{ uri_timeout }}"
body_format: form-urlencoded
body:
api: SYNO.Core.Upgrade
version: "{{ upgrade_ver }}"
method: "{{ upgrade_start_method }}"
_sid: "{{ sid }}"
register: upgrade_start
- name: Fail si start a échoué (message lisible)
ansible.builtin.fail:
msg: >-
Echec du démarrage upgrade DSM via API.
Réponse: {{ upgrade_start.json | default(upgrade_start.content) }}
when: upgrade_start.json is not defined or upgrade_start.json.success is not defined or upgrade_start.json.success != true
- name: Afficher résultat start
ansible.builtin.debug:
var: upgrade_start.json
# ---- WAIT REBOOT (si DSM indique reboot:true, on attend la chute + le retour) ----
- name: Attendre que DSM tombe (reboot en cours)
ansible.builtin.wait_for:
host: "{{ inventory_hostname }}"
port: "{{ syno_port }}"
state: stopped
timeout: "{{ reboot_down_timeout }}"
delegate_to: localhost
when: upgrade_status.json.data.reboot is defined and upgrade_status.json.data.reboot | bool
- name: Attendre le retour de DSM (port 5001)
ansible.builtin.wait_for:
host: "{{ inventory_hostname }}"
port: "{{ syno_port }}"
state: started
delay: 10
timeout: "{{ reboot_up_timeout }}"
delegate_to: localhost
when: upgrade_status.json.data.reboot is defined and upgrade_status.json.data.reboot | bool
- name: Logout DSM API
ansible.builtin.uri:
url: "{{ base_url }}/webapi/{{ auth_path }}?api=SYNO.API.Auth&version=2&method=logout&session={{ syno_session | urlencode }}&_sid={{ sid }}"
method: GET
validate_certs: "{{ syno_verify_ssl }}"
return_content: true
status_code: 200
timeout: "{{ uri_timeout }}"
register: logout
failed_when: false