--- - 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