From 90444c0c567406ac4406cdd1c3332b2fd86c606a Mon Sep 17 00:00:00 2001 From: Admin MPCZ Date: Thu, 7 May 2026 11:41:05 +0200 Subject: [PATCH] feat(patching): particularites par serveur (notes wiki SANEF) + skip_first_reboot + reboot_delay cluster MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration migrate_patching_notes_20260507.sql: - servers.skip_first_reboot boolean (TPV1: vptraatpf1/2 a true) - servers.patching_notes text (markdown — meme operateur) - server_clusters.reboot_delay_min_minutes int default 0 - Backfill patching_notes pour 22 cas particuliers du wiki SANEF: ASM Oracle (~50 hosts kernel*), TPV1, HAproxy FL, Covoiturage, SI Patrimoine (vrpatbsip1 avant vrpataels1, kibana, certs), Talend, Scoop (Debian apt-mark hold + CentOS containers Docker), DATI (pm2/tomcat post-reboot), COMMVAULT (mode maintenance), Masterparc (kmeihm pm2), Splunk (RPM special), Site institutionnel (HAproxy backend rotation, no *node*), Centreon (1 par 1 + check centengine), Sextan (10min reboot delay), OCTAN, PAIPOR (site maintenance), Gaspar, Postgres, Oracle OEM, SMTP relay - Cluster Sextan cree (10 min entre reboots) + 10 serveurs Sextan rattaches UI iexec: - Banner cumule en haut: '⚠ Particularites pour N serveur(s)' si au moins 1 note - Badges sur la cellule asset_name: ⚠ note (modal markdown au clic), ⏭ skip 1st reboot, ⏱ Xmin (cluster reboot delay) - Modal patching_notes avec rendu pre/markdown, fermeture Escape UI fiche serveur (server_detail.html): - Ligne 'Skip 1er reboot' dans bloc Patching - Bandeau orange particularites avec contenu patching_notes si renseigne Pas encore implemente cote logique d'execution (Phase 2): - skip_first_reboot logic dans le step reboot - enforcement reboot_delay_min_minutes entre membres cluster - Pour l'instant: notes affichees en mode 'memo operateur' uniquement --- app/routers/planning_import.py | 5 +- app/templates/partials/server_detail.html | 8 + app/templates/patching_iexec.html | 48 +++- migrate_patching_notes_20260507.sql | 317 ++++++++++++++++++++++ 4 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 migrate_patching_notes_20260507.sql diff --git a/app/routers/planning_import.py b/app/routers/planning_import.py index 0b58cf3..7c438a2 100644 --- a/app/routers/planning_import.py +++ b/app/routers/planning_import.py @@ -619,10 +619,13 @@ async def iexec_page(request: Request, db=Depends(get_db), r.intervenant, r.is_eligible, r.server_id, r.pct_required, r.pct_confirmed_at, - s.hostname, vs.effective_excludes + s.hostname, vs.effective_excludes, + s.skip_first_reboot, s.patching_notes, + sc.name AS cluster_name, sc.reboot_delay_min_minutes FROM patch_planning_import_rows r LEFT JOIN servers s ON s.id = r.server_id LEFT JOIN v_servers vs ON vs.id = r.server_id + LEFT JOIN server_clusters sc ON sc.id = s.cluster_id WHERE r.id IN ({placeholders}) AND r.is_eligible = true """)).fetchall() diff --git a/app/templates/partials/server_detail.html b/app/templates/partials/server_detail.html index 8364a41..be66fb1 100644 --- a/app/templates/partials/server_detail.html +++ b/app/templates/partials/server_detail.html @@ -73,12 +73,20 @@
Frequence{{ s.patch_frequency }}
Podman{{ 'Oui' if s.is_podman else 'Non' }}
Prevenance PCT{{ 'Oui' if s.pct_required else 'Non' }}
+
Skip 1er reboot{{ 'Oui' if s.skip_first_reboot else 'Non' }}
Jour préféré{{ s.pref_patch_jour or 'indifférent' }}
Heure préférée{{ s.pref_patch_heure or 'indifférent' }}
Satellite{% if s.satellite_url %}{% if 'sat1' in s.satellite_url %}SAT1 (DMZ){% elif 'sat2' in s.satellite_url %}SAT2 (LAN){% else %}{{ s.satellite_url }}{% endif %}{% else %}N/A{% endif %}
+ {% if s.patching_notes %} +
+

⚠ Particularités de patching

+
{{ s.patching_notes }}
+
+ {% endif %} +

Responsables

diff --git a/app/templates/patching_iexec.html b/app/templates/patching_iexec.html index c05726d..a49dd45 100644 --- a/app/templates/patching_iexec.html +++ b/app/templates/patching_iexec.html @@ -28,6 +28,15 @@ border-color:#f59e0b; animation: pulseIt 1.2s infinite; } @keyframes pulseIt { 0%,100% { opacity:1 } 50% { opacity:.55 } } +{# Banner cumulé des particularités (servers avec patching_notes) #} +{% set has_notes = rows|selectattr('patching_notes')|list %} +{% if has_notes %} +
+

⚠ Particularités de patching pour {{ has_notes|length }} serveur(s)

+

Clique sur le badge ⚠ note à côté du nom du serveur pour voir la procédure spécifique.

+
+{% endif %} +
1 Vérif @@ -86,12 +95,24 @@ {% for r in rows %} + data-pct-confirmed="{{ '1' if r.pct_confirmed_at else '0' }}" + data-skip-first-reboot="{{ '1' if r.skip_first_reboot else '0' }}" + data-cluster-name="{{ r.cluster_name or '' }}" + data-reboot-delay-min="{{ r.reboot_delay_min_minutes or 0 }}"> {{ r.asset_name }}{% if r.pct_required %} {% if r.pct_confirmed_at %}✅ PCT ok{% else %}⚠ Prév PCT à faire{% endif %} + {% endif %}{% if r.skip_first_reboot %} + ⏭ skip 1st reboot + {% endif %}{% if r.patching_notes %} + + {% endif %}{% if r.cluster_name and r.reboot_delay_min_minutes and r.reboot_delay_min_minutes > 0 %} + ⏱ {{ r.reboot_delay_min_minutes }}min {% endif %} {{ r.hostname or '–' }} {{ r.environnement or '' }} @@ -833,4 +854,29 @@ function toggleDetails(){ }); })(); + +{# Modal de patching_notes (affiché au clic sur badge ⚠ note) #} + + + {% endblock %} diff --git a/migrate_patching_notes_20260507.sql b/migrate_patching_notes_20260507.sql new file mode 100644 index 0000000..80422ef --- /dev/null +++ b/migrate_patching_notes_20260507.sql @@ -0,0 +1,317 @@ +-- Migration : particularités patching extraites du wiki SANEF "Procédures cas spécifiques" +-- - servers.skip_first_reboot : boolean (default false) — saute le 1er reboot post-patch +-- - servers.patching_notes : text — mémo opérateur (markdown), affiché dans iexec +-- - server_clusters.reboot_delay_min_minutes : int default 0 — délai minimum entre reboots de membres +-- Idempotent. +-- Source : DokuWiki SANEF "Procédures de mises à jour Linux cas spécifiques" + +-- ─── 1) Nouvelles colonnes ──────────────────────────────────── + +ALTER TABLE public.servers + ADD COLUMN IF NOT EXISTS skip_first_reboot boolean NOT NULL DEFAULT false, + ADD COLUMN IF NOT EXISTS patching_notes text; + +ALTER TABLE public.server_clusters + ADD COLUMN IF NOT EXISTS reboot_delay_min_minutes integer NOT NULL DEFAULT 0; + +-- ─── 2) skip_first_reboot — TPV1 (info JFCRABS) ────────────── + +UPDATE public.servers SET skip_first_reboot = true + WHERE LOWER(hostname::text) IN ('vptraatpf1', 'vptraatpf2'); + +-- ─── 3) Backfill patching_notes par groupe applicatif ──────── + +-- ASM stockage : exclude kernel* déjà fait, on ajoute un mémo sur la procédure grubby +UPDATE public.servers SET patching_notes = +$mn$**ASM Oracle stockage** — kernel* exclu (kmod-redhat-oracleasm doit suivre la version d'ASM). + +Post-MAJ : vérifier le kernel par défaut avec `sudo grubby --info=ALL`. +Si le nouveau kernel a été installé, remettre l'ancien comme kernel de boot par défaut : +`sudo grubby --set-default /boot/vmlinuz-` + +Si apparition de `kmod-redhat-oracleasm-kernel_` : exclure le kernel à la prochaine MAJ.$mn$ +WHERE LOWER(hostname::text) IN ( + 'lamarrac1','lamarrac2','lamarrac3','lamarrac4', + 'srdsibora1','srdsibora2','lptrabgas1', + 'lragtbpla1','lpaiigrid1', + 'lamaprac1','lamaprac2','lamaprac3','lamaprac4', + 'lampadp1','lampadp2','lampadv1','lampasu1','lampasu2', + 'lampcrm1','lampdec1','lampfin1','lampfin3','lampoct1','lamppea1', + 'lamppla1','lamppla2','lamrsip1','lamrsip2','lamtinf1','lamtpfe1', + 'lpagtbpla1','lpemvbaemv1','lpemvbpemv1','lpemvbpemv2', + 'lpsimbpfe1','lraiibora1','lremvbremv1','lremvbremv2', + 'rmila150','rmilgmo3','rmilsas1', + 'rsmiged1','rsmigmo1','rsmipat1','rsmisar2', + 'srtrabgas1','vpemvgrid1','vpnapamed1' +); + +-- TPV1 — pas de 1er reboot +UPDATE public.servers SET patching_notes = +$mn$**TPV1 — pas de premier reboot pour les pf** (info JFCRABS). + +`skip_first_reboot=true` est positionné, le workflow iexec saute automatiquement le 1er reboot.$mn$ +WHERE LOWER(hostname::text) IN ('vptraatpf1', 'vptraatpf2'); + +-- HAproxy FL +UPDATE public.servers SET patching_notes = +$mn$**HAproxy Flux Libre** — exclusions du domaine Flux Libre s'appliquent +(`*podman* run* *container* *sdcss-kmod*` uniquement).$mn$ +WHERE LOWER(hostname::text) IN ('vrpeahbst1','vppeahbst1','vipeahbst1'); + +-- Covoiturage +UPDATE public.servers SET patching_notes = +$mn$**Covoiturage** — services métier ne se relancent pas automatiquement. + +Post-reboot : +```bash +systemctl restart covoit* +systemctl start covoit-load # si non démarré +```$mn$ +WHERE LOWER(hostname::text) = 'vppeaaphov1'; + +-- SI Patrimoine 1.0 - sipat (vrpatbsip1) +UPDATE public.servers SET patching_notes = +$mn$**SI Patrimoine** — patcher CE SERVEUR AVANT vrpataels1 (sipat doit être arrêté). + +Pre-patch : +```bash +sudo su - sipat -c "/applis/sipat/stop.bash" +# Commenter dans crontab user sipat : +# * * * * * /applis/sipat/start.bash sipat &>/dev/null +``` + +Post-patch : redémarrer, vérifier que sipat n'est PAS lancé, puis patcher vrpataels1. + +Après vrpataels1 OK : décommenter crontab + redémarrer.$mn$ +WHERE LOWER(hostname::text) = 'vrpatbsip1'; + +UPDATE public.servers SET patching_notes = +$mn$**SI Patrimoine — Elasticsearch** — patcher APRÈS vrpatbsip1 (sipat arrêté). + +Post-patch : vérifier que Elasticsearch est toujours fonctionnel. + +Vérifier présence des certificats CA recette dans `/usr/lib/jvm/java/lib/security/cacerts` : +```bash +keytool -list -keystore /usr/lib/jvm/java/lib/security/cacerts | grep -i recette +``` +Doit afficher `acdistributionrecette` et `acracinerecette`. Sinon importer via `/etc/pki/ca-trust/source/anchors/recette-ac.pem` puis `sudo update-ca-trust`.$mn$ +WHERE LOWER(hostname::text) = 'vrpataels1'; + +UPDATE public.servers SET patching_notes = +$mn$**SI Patrimoine — Kibana** — *kibana* déjà dans la base d'exclusions globale, rien à faire.$mn$ +WHERE LOWER(hostname::text) = 'vppatakib1'; + +-- Talend +UPDATE public.servers SET patching_notes = +$mn$**Talend** — relancer Talend depuis l'utilisateur Talend en passant par le compte root.$mn$ +WHERE LOWER(hostname::text) = 'vdechatal1'; + +-- Scoop (Debian — hors yum) +UPDATE public.servers SET patching_notes = +$mn$**Scoop Debian** — HORS workflow yum standard. Utilise `apt-mark hold` : + +```bash +sudo apt-mark hold *mongodb* *sql* *postgres* *mariadb* *oracle* *centreon* *pgdg* *php* *java* *containers* *docker* *qwserver* *ansible* *node* *tomcat* *jupyter* +sudo apt-get update && sudo apt-get upgrade +sudo apt-mark unhold *mongodb* *sql* *postgres* *mariadb* *oracle* *centreon* *pgdg* *php* *java* *containers* *docker* *qwserver* *ansible* *node* *tomcat* *jupyter* +``` + +vrscobpf1 : post-reboot, java/scoop-ptf nécessite connexion compte `scoop` : +```bash +sudo su -l scoop +sudo systemctl status scoop-ptf +```$mn$ +WHERE LOWER(hostname::text) IN ('vrscobpf1','vrscouevs1'); + +-- Scoop CentOS (vpscoav2x1) +UPDATE public.servers SET patching_notes = +$mn$**Scoop CentOS** — arrêter les containers Docker AVANT le patch : + +```bash +sudo docker container ls # lister +sudo docker container stop ... # arrêter +# patch yum normal (avec exclude *containers*) +sudo docker container start ... # redémarrer +``` + +Pour vrscoav2x1 : laisser IP forwarding activé (`net.ipv4.ip_forward = 1`) sinon métier KO.$mn$ +WHERE LOWER(hostname::text) = 'vpscoav2x1'; + +-- DATI - composants pm2/tomcat +UPDATE public.servers SET patching_notes = +$mn$**DATI — composant FrontalIP (node/pm2)** — métier non auto post-reboot : +```bash +sudo su - node -c "pm2 start /applis/frontal-ip/frontal.js -i 2" +```$mn$ +WHERE LOWER(hostname::text) IN ('vrexpadat3','vpexpadat3'); + +UPDATE public.servers SET patching_notes = +$mn$**DATI — Tomcat** — métier non auto post-reboot : +```bash +sudo su - tomcat -c "/applis/tomcat/bin/catalina.sh start" +```$mn$ +WHERE LOWER(hostname::text) IN ('vpexpadat1','vpexpadat2'); + +-- COMMVAULT +UPDATE public.servers SET patching_notes = +$mn$**COMMVAULT** — pré-patch obligatoire : + +1. Mettre le serveur en mode maintenance dans la console Commvault. +2. Suspendre les jobs qui utilisent le media agent à patcher. + +Vérifier post-patch que les jobs reprennent correctement avant de désactiver le mode maintenance.$mn$ +WHERE LOWER(hostname::text) IN ( + 'spbckamag1','spbckamag2','spbckamag3','spbckamag4', + 'spbckamag5','spbckamag6','spbckamag7','spbckamag8', + 'spbckmmag1','spbckmmag2' +); + +-- Masterparc +UPDATE public.servers SET patching_notes = +$mn$**Masterparc — kmeihm via pm2** — relance manuelle si non auto : +```bash +sudo su - root -c "/usr/local/bin/pm2 list" +sudo su - root -c "/usr/local/bin/pm2 start 'cd /applis/kme/kmeihm ; node ./app.js' --name kmeihm" +sudo su - root -c "/usr/local/bin/pm2 show kmeihm" +```$mn$ +WHERE LOWER(hostname::text) = 'vrtrabkme1'; + +-- SPLUNK +UPDATE public.servers SET patching_notes = +$mn$**Splunk Enterprise** — procédure RPM spéciale, NE PAS patcher avec workflow yum standard. + +Cf wiki SANEF section "SPLUNK" : +1. `sudo /applis/splunk/bin/splunk stop` +2. `sudo cp -r /applis/splunk/ /applis/splunk-avant-maj` (rollback) +3. `sudo rpm -U --prefix=/applis /root/splunk-X.Y.Z.x86_64.rpm` +4. `sudo /applis/splunk/bin/splunk start` + accepter licence + migration config$mn$ +WHERE LOWER(hostname::text) IN ('spsecalog1','spemvalog1'); + +-- Site institutionnel HAproxy + backends +UPDATE public.servers SET patching_notes = +$mn$**Site institutionnel — HAproxy + backends www.sanef.com** + +Pré-patch obligatoire : rotation des backends dans `/etc/haproxy/haproxy.cfg` sur vpintaprx2. + +Avant de patcher vpintaweb2 : commenter `app1 192.168.20.36:80` (vpintaweb2) +Avant de patcher vpintaweb3 : commenter `app2 192.168.20.37:80` (vpintaweb3) + +Test config + reload après chaque modif : +```bash +sudo haproxy -f /etc/haproxy/haproxy.cfg -c +sudo systemctl restart haproxy.service +``` + +Post-patch : faire valider le métier par RA Arnaud Meillon / Bertrand Letendard avant de réactiver les 2 backends. + +Ne pas MAJ les paquets `*node*` (déjà dans la base d'exclusions).$mn$ +WHERE LOWER(hostname::text) IN ('vpintaprx2','vpintaweb2','vpintaweb3'); + +-- Centreon polleurs +UPDATE public.servers SET patching_notes = +$mn$**Centreon — patcher 1 serveur à la fois et valider avant le suivant.** + +Post-reboot : vérifier services : +```bash +sudo systemctl status centengine +sudo systemctl status centreontrapd # seulement s'il existe +``` +Démarrer manuellement si non actifs.$mn$ +WHERE LOWER(hostname::text) IN ( + 'vpaiiapol1','vpaiiapol2','vpaiiapol3','vpaiiapol4','vpaiiapol5', + 'vpaiiapol6','vpaiiapol7','vpaiiapol9','vpaiiapol10' +); + +-- Sextan +UPDATE public.servers SET patching_notes = +$mn$**Sextan** — vérifier IP forwarding (`sysctl net.ipv4.ip_forward` doit être 1). + +Si à 0 : éditer `/etc/sysctl.conf`, mettre `net.ipv4.ip_forward=1` puis `sudo sysctl -p /etc/sysctl.conf`. + +⚠ ATTENTION : espacer les reboots de minimum 10 min entre chaque serveur Sextan (sinon perte de données). Le cluster Sextan a `reboot_delay_min_minutes=10`.$mn$ +WHERE LOWER(hostname::text) IN ( + 'vdameasxt1','vdameasxt2','vdameasxt3','vdameasxt4', + 'vrameahtp1','vrameahtp2', + 'vrameasxt1','vrameasxt2','vrameasxt3','vrameasxt4' +); + +-- OCTAN - vrameaoct1 +UPDATE public.servers SET patching_notes = +$mn$**OCTAN — applis ne se relancent pas auto post-reboot** : +```bash +sudo su - tomcat -c 'pm2 start /applis/decisionnelpeage/backend/index.js --name backend' +sudo /bin/bash /applis/base-recette/octan-edition-4.8/bin/catalina.sh start & +sudo /bin/bash /applis/base-recette/octan-services/bin/control.sh start & +sudo /bin/bash /applis/base-recette/octan-services-a150/bin/run.sh start & +sudo /bin/bash /applis/base-qualif/octan-edition-4.8/bin/catalina.sh start & +sudo /bin/bash /applis/base-qualif/octan-services/bin/control.sh start & +```$mn$ +WHERE LOWER(hostname::text) = 'vrameaoct1'; + +-- PAIPOR +UPDATE public.servers SET patching_notes = +$mn$**PAIPOR** — pré-patch : mettre site en maintenance. + +Sur vpcliabosp1, éditer `/applis/sanef/forsAccess.override.json` : +```json +"sanef": { "maintenanceEspaceClient": true, ... } +``` +Post-patch : remettre `false`. + +Ne pas MAJ `python2-certbot` ni `*certbot*` (déjà dans base exclusions).$mn$ +WHERE LOWER(hostname::text) = 'vpsimapai1'; + +-- Gaspar +UPDATE public.servers SET patching_notes = +$mn$**Gaspar** — applis Java non auto post-reboot. Relance manuelle Tomcat (cf wiki).$mn$ +WHERE LOWER(hostname::text) = 'vrtraagas1'; + +-- Postgres BDD +UPDATE public.servers SET patching_notes = +$mn$**Postgres** — service ne se lance pas automatiquement post-reboot : +```bash +sudo systemctl start postgres* +``` +Pour vraiibpgs3 (réplication de vraiibpgs2) : si problème, demander à Nadine BENARD de redémarrer le métier.$mn$ +WHERE LOWER(hostname::text) IN ('vraiibpgs1','vraiibpgs2','vraiibpgs3'); + +-- Oracle OEM +UPDATE public.servers SET patching_notes = +$mn$**Oracle OEM** — relancer la couche OMS manuellement post-reboot, en utilisateur `oracle` : +```bash +/applis/oracle/oem/middleware134/bin/emctl start oms +```$mn$ +WHERE LOWER(hostname::text) = 'lpaiigrid1'; + +-- SMTP Relay +UPDATE public.servers SET patching_notes = +$mn$**SMTP Relay** — post-reboot, vérifier services : +```bash +systemctl status postfix +systemctl status keepalived +``` + +Lancer le check_smtp et confirmer la réception du mail à Joel CAVE : +```bash +cd /usr/local/bin && ./check_smtp.sh +```$mn$ +WHERE LOWER(hostname::text) IN ('vpdsismtp1','vpdsismtp2'); + +-- ─── 4) reboot_delay_min_minutes sur cluster Sextan ────────── + +-- Création du cluster Sextan s'il n'existe pas (idempotent via ON CONFLICT) +INSERT INTO public.server_clusters (name, description, reboot_strategy, reboot_delay_min_minutes) +VALUES ('Sextan', 'Cluster Sextan — espacer les reboots de 10 min minimum (perte données sinon)', + 'sequential', 10) +ON CONFLICT (name) DO UPDATE + SET reboot_delay_min_minutes = 10, + description = EXCLUDED.description; + +-- Rattache les serveurs Sextan au cluster +UPDATE public.servers s + SET cluster_id = (SELECT id FROM server_clusters WHERE name='Sextan') + WHERE LOWER(s.hostname::text) IN ( + 'vdameasxt1','vdameasxt2','vdameasxt3','vdameasxt4', + 'vrameahtp1','vrameahtp2', + 'vrameasxt1','vrameasxt2','vrameasxt3','vrameasxt4' + );