Actualiser synology_upgrade_ssh.yml

This commit is contained in:
l.covela 2026-01-07 12:31:53 +01:00
parent 6d2c2e86fd
commit e13d89b123
1 changed files with 80 additions and 50 deletions

View File

@ -1,12 +1,12 @@
--- ---
- name: Synology DSM - télécharger puis installer une mise à jour via SSH (robuste Semaphore) - name: Synology DSM - télécharger puis installer mise à jour via SSH (robuste Semaphore)
hosts: synology hosts: synology
gather_facts: false gather_facts: false
vars: vars:
download_timeout: 3600 download_timeout: 7200 # 2h max pour download
reboot_timeout: 1800 reboot_timeout: 2400 # 40 min max reboot/upgrade
poll_delay: 20 poll_delay: 30 # intervalle de polling
synoupgrade_candidates: synoupgrade_candidates:
- "/usr/syno/sbin/synoupgrade" - "/usr/syno/sbin/synoupgrade"
@ -26,9 +26,9 @@
- "--install" - "--install"
tasks: tasks:
- name: Détecter synoupgrade (sans [ ] ; fallback PATH) - name: Détecter synoupgrade (sans [ ])
ansible.builtin.raw: | ansible.builtin.shell: |
set -e set -eu
FOUND="" FOUND=""
for p in {{ synoupgrade_candidates | join(' ') }}; do for p in {{ synoupgrade_candidates | join(' ') }}; do
if test -x "$p"; then if test -x "$p"; then
@ -45,122 +45,148 @@
fi fi
fi fi
echo "FOUND=$FOUND" echo "$FOUND"
args:
executable: /bin/sh
register: detect register: detect
changed_when: false changed_when: false
- name: Enregistrer le chemin synoupgrade détecté - name: Enregistrer le binaire synoupgrade
ansible.builtin.set_fact: ansible.builtin.set_fact:
synoupgrade_bin: "{{ (detect.stdout | regex_search('FOUND=(.*)', '\\1')) | default('') | trim }}" synoupgrade_bin: "{{ detect.stdout | trim }}"
changed_when: false changed_when: false
- name: Fail si synoupgrade introuvable - name: Fail si synoupgrade introuvable
ansible.builtin.fail: ansible.builtin.fail:
msg: >- msg: "synoupgrade introuvable. stdout='{{ detect.stdout }}' stderr='{{ detect.stderr }}'"
synoupgrade introuvable. Sortie détection:
{{ detect.stdout | default('') }}
when: synoupgrade_bin == "" when: synoupgrade_bin == ""
- name: Afficher le binaire trouvé - name: Afficher le binaire retenu
ansible.builtin.debug: ansible.builtin.debug:
msg: "synoupgrade utilisé: {{ synoupgrade_bin }}" msg: "synoupgrade utilisé: {{ synoupgrade_bin }}"
# 1) Check update # 1) Check update
- name: Check mise à jour disponible - name: Check mise à jour disponible
ansible.builtin.raw: "{{ synoupgrade_bin }} --check || true" ansible.builtin.shell: |
set -eu
{{ synoupgrade_bin }} --check || true
args:
executable: /bin/sh
register: check_out register: check_out
changed_when: false changed_when: false
- name: Afficher le résultat du check - name: Afficher stdout/stderr du check
ansible.builtin.debug: ansible.builtin.debug:
var: check_out.stdout_lines msg:
- "CHECK STDOUT: {{ check_out.stdout | default('') }}"
- "CHECK STDERR: {{ check_out.stderr | default('') }}"
# Si check ne renvoie rien, on NE STOP pas automatiquement, car certains DSM sont muets. # Heuristique: stop uniquement si on voit clairement "up to date"
# On stop seulement si on voit des motifs "no update".
- name: Stop si aucune mise à jour détectée (heuristique) - name: Stop si aucune mise à jour détectée (heuristique)
ansible.builtin.meta: end_play ansible.builtin.meta: end_play
when: > when: >
(check_out.stdout | lower) is search('no update') (check_out.stdout | lower) is search('up to date')
or (check_out.stdout | lower) is search('no newer') or (check_out.stdout | lower) is search('no update')
or (check_out.stdout | lower) is search('already') or (check_out.stdout | lower) is search('already')
or (check_out.stdout | lower) is search('up to date') or (check_out.stdout | lower) is search('no newer')
# 2) Download # 2) Download
- name: Lancer le téléchargement - name: Lancer le téléchargement
ansible.builtin.raw: "{{ synoupgrade_bin }} --download" ansible.builtin.shell: |
set -eu
{{ synoupgrade_bin }} --download
args:
executable: /bin/sh
register: download_start register: download_start
changed_when: true changed_when: true
- name: Afficher retour download start - name: Afficher stdout/stderr du download start
ansible.builtin.debug: ansible.builtin.debug:
var: download_start.stdout_lines msg:
- "DL START STDOUT: {{ download_start.stdout | default('') }}"
- "DL START STDERR: {{ download_start.stderr | default('') }}"
# 3) Poll download # 3) Poll download until "ready"
- name: Attendre fin du téléchargement (status/log) - name: Attendre fin du téléchargement (status ou logs)
ansible.builtin.raw: | ansible.builtin.shell: |
set -e set -eu
if {{ synoupgrade_bin }} --status >/dev/null 2>&1; then if {{ synoupgrade_bin }} --status >/dev/null 2>&1; then
{{ synoupgrade_bin }} --status || true {{ synoupgrade_bin }} --status || true
else else
(tail -n 80 /var/log/synoupgrade.log 2>/dev/null || true) (tail -n 120 /var/log/synoupgrade.log 2>/dev/null || true)
(tail -n 120 /var/log/messages 2>/dev/null | grep -i upgrad || true)
fi fi
args:
executable: /bin/sh
register: dl_poll register: dl_poll
changed_when: false changed_when: false
until: > until: >
(dl_poll.stdout | lower) is search('downloaded') (dl_poll.stdout | lower) is search('downloaded')
or (dl_poll.stdout | lower) is search('ready') or (dl_poll.stdout | lower) is search('ready')
or (dl_poll.stdout | lower) is search('finish') or (dl_poll.stdout | lower) is search('complete')
or (dl_poll.stdout | lower) is search('completed') or (dl_poll.stdout | lower) is search('completed')
or (dl_poll.stdout | lower) is search('finish')
or (dl_poll.stdout | lower) is search('done') or (dl_poll.stdout | lower) is search('done')
retries: "{{ (download_timeout // poll_delay) | int }}" retries: "{{ (download_timeout // poll_delay) | int }}"
delay: "{{ poll_delay }}" delay: "{{ poll_delay }}"
- name: Afficher statut fin téléchargement - name: Afficher statut fin téléchargement
ansible.builtin.debug: ansible.builtin.debug:
var: dl_poll.stdout_lines msg: "{{ dl_poll.stdout_lines | default([]) }}"
# 4) Start install (best effort) # 4) Start install (best effort)
- name: Tenter de démarrer l'installation (plusieurs sous-commandes) - name: Tenter de démarrer l'installation (plusieurs méthodes)
ansible.builtin.raw: "{{ synoupgrade_bin }} {{ item }} || true" ansible.builtin.shell: |
set -eu
{{ synoupgrade_bin }} {{ item }} || true
args:
executable: /bin/sh
loop: "{{ start_subcommands }}" loop: "{{ start_subcommands }}"
register: start_attempts register: start_attempts
changed_when: true changed_when: true
failed_when: false failed_when: false
- name: Choisir la première tentative OK (pas de mots d'erreur) - name: Afficher résultats des tentatives start
ansible.builtin.debug:
msg: >
{{ start_attempts.results
| map(attribute='item')
| zip(start_attempts.results | map(attribute='stdout'))
| list }}
- name: Choisir la première tentative qui ne contient pas "invalid/unknown/usage/error"
ansible.builtin.set_fact: ansible.builtin.set_fact:
start_ok: >- start_ok: >-
{{ {{
(start_attempts.results (start_attempts.results
| rejectattr('stdout', 'search', '(?i)(invalid|unknown|usage|not found|permission|error|failed)') | rejectattr('stdout', 'search', '(?i)(invalid|unknown|usage|error|failed)')
| rejectattr('stderr', 'search', '(?i)(invalid|unknown|usage|not found|permission|error|failed)') | rejectattr('stderr', 'search', '(?i)(invalid|unknown|usage|error|failed)')
| list | list
| first) | default({}) | first) | default({})
}} }}
changed_when: false
- name: Fail si aucune commande start ne marche - name: Fail si aucune tentative start ne marche
ansible.builtin.fail: ansible.builtin.fail:
msg: >- msg: >
Impossible de démarrer l'installation via synoupgrade. Impossible de démarrer l'installation.
stdout: {{ start_attempts.results | map(attribute='stdout') | list }} Détails stdout/stderr:
stderr: {{ start_attempts.results | map(attribute='stderr') | list }} {{ start_attempts.results | to_nice_json }}
when: start_ok | length == 0 when: start_ok | length == 0
- name: Afficher la commande start retenue - name: Afficher la commande start retenue
ansible.builtin.debug: ansible.builtin.debug:
msg: msg:
- "Commande retenue: {{ start_ok.item }}" - "Start retenu: {{ start_ok.item }}"
- "stdout: {{ start_ok.stdout | default('') }}" - "stdout: {{ start_ok.stdout | default('') }}"
- "stderr: {{ start_ok.stderr | default('') }}" - "stderr: {{ start_ok.stderr | default('') }}"
# 5) Wait reboot (SSH down/up) # 5) Attendre reboot (SSH down/up)
- name: Attendre que le NAS coupe SSH (reboot probable) - name: Attendre que SSH tombe (reboot probable)
ansible.builtin.wait_for: ansible.builtin.wait_for:
host: "{{ inventory_hostname }}" host: "{{ inventory_hostname }}"
port: 22 port: 22
state: drained state: drained
timeout: 300 timeout: 600
delegate_to: localhost delegate_to: localhost
- name: Attendre le retour SSH - name: Attendre le retour SSH
@ -172,11 +198,15 @@
delegate_to: localhost delegate_to: localhost
# 6) Post-check # 6) Post-check
- name: Post-check version / statut - name: Post-check version DSM
ansible.builtin.raw: | ansible.builtin.shell: |
uname -a || true set -eu
echo "=== /etc/VERSION ==="
cat /etc/VERSION 2>/dev/null || true cat /etc/VERSION 2>/dev/null || true
echo "=== synoupgrade --status ==="
{{ synoupgrade_bin }} --status 2>/dev/null || true {{ synoupgrade_bin }} --status 2>/dev/null || true
args:
executable: /bin/sh
register: post register: post
changed_when: false changed_when: false