docs(cluster): incident pvemini backup SSD hang + thermal monitoring
Documenteaza incidentul Kingston SNV3S2000G hang la 2026-04-30 (Sensor 2
74°C → emergency mode + restart loop) si masurile aplicate: distantare
temporala backup-uri par/impar, mutare CT 101+110 pe pve1 backup-ssd,
nofail in fstab, hardware watchdog iTCO_wdt, monitoring CSV la 30 min.
Adauga scripturile /opt/scripts/kingston-thermal-{monitor,report}.sh
pentru tracking trend si alertare la depasirea pragurilor termale.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
406
proxmox/cluster/incidents/2026-04-30-pvemini-backup-ssd-hang.md
Normal file
406
proxmox/cluster/incidents/2026-04-30-pvemini-backup-ssd-hang.md
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
# Incident 2026-04-30 — pvemini emergency mode + restart loop (Kingston backup SSD hang)
|
||||||
|
|
||||||
|
**Severity:** Medium (efect amplu pe cluster, dar **fără pierdere de date** și **fără impact pe disponibilitatea producției** — datele live + DR sunt pe Samsung mirror, replicare ZFS independentă)
|
||||||
|
**Detected:** 2026-04-30 ~23:14 EEST (user — screenshot-uri ecran cu emergency mode)
|
||||||
|
**Resolved:** 2026-04-30 ~22:41 EEST (cold power cycle de la user → Kingston revine la enumerare; ulterior reboot curat la 23:37)
|
||||||
|
**Author:** Claude Code (mmarius28@gmail.com)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TL;DR — lanțul cauzal
|
||||||
|
|
||||||
|
1. **2026-04-30 22:13:01 EEST** — pvemini se oprește brusc după **10 zile uptime** (boot anterior din 2026-04-20 14:19, post-incident cluster outage). Cauza opririi nu e clară din log-uri — posibil hang controller Kingston blochează I/O kernel.
|
||||||
|
2. **22:15:35** — primul boot attempt. systemd așteaptă `/dev/nvme2n1p1` (Kingston SNV3S2000G, mount `/mnt/backup`) — **nu răspunde la enumerare PCIe**.
|
||||||
|
3. **22:17:05** — timeout 90s; `mnt-backup.mount`, `systemd-fsck@dev-nvme2n1p1.service` eșuează.
|
||||||
|
4. **22:17 → 22:40** — încă **2 cicluri de boot** (probabil user retries via reset). De fiecare dată: Kingston tăcut → timeout → emergency mode → "Give root password for maintenance".
|
||||||
|
5. **~22:40** — user face **cold power cycle complet** (oprește din buton, apasă din nou). Controllerul Kingston revine după pierdere completă de putere.
|
||||||
|
6. **22:41:37** — boot reușit. Kingston răspunde, fsck rulează (`recovering journal` → `clean, 102/122101760 files`), mount OK.
|
||||||
|
7. **23:18:22** — user face shutdown deliberat pentru investigație.
|
||||||
|
8. **23:37:09** — reboot final, totul stabil. Investigație + plan prevenție.
|
||||||
|
|
||||||
|
**Root cause likely:** thermal throttle / hang controller pe Kingston SNV3S2000G (model DRAM-less QLC, low-end). SMART confirmă **Temperature Sensor 2 = 74°C** la 1°C sub warning threshold (75°C, critical 80°C). În timpul activității de backup nocturn (job vzdump 02:00 zilnic + 22:00 zilnic, totalizând ~12 GB scriere zstd compressed peste ~1h), temperatura crește, controllerul intră în throttle/hang, partiția devine non-responsive, sistemul nu o mai vede chiar și la reboot — până la pierdere completă de alimentare care reset-ează controllerul.
|
||||||
|
|
||||||
|
**Cluster-wide impact secundar:** NFS server pe pvemini exportă `/mnt/backup` către `10.0.20.0/24`. Clienți activi: pveelite + pve1. Când Kingston hang-uiește, pveelite + pve1 au mount-uri NFS stuck → orice operație care atinge `/mnt/pve/backup-pvemini-nfs` se înghețează. Plus dependency chain pe pvemini: `nfs-server.service` depinde de `/mnt/backup` mount → cade în cascadă cu `local-fs.target`.
|
||||||
|
|
||||||
|
**Pierdere de date:** ZERO. ZFS rpool a detectat 14 erori de checksum la boot pe `nvme-eui.0025384551a1560c-part3` (Samsung 990 PRO) — efect colateral al opririi neașteptate, nu probleme media — și a făcut auto-resilver de 33.6 MB cu 0 erori.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cronologie (EEST)
|
||||||
|
|
||||||
|
| Ora | Eveniment | Sursa |
|
||||||
|
|-----|-----------|-------|
|
||||||
|
| 2026-04-20 14:19:58 | Boot precedent pvemini (post-incident cluster outage) | journalctl |
|
||||||
|
| → 10 zile uptime normal | Joburi vzdump rulează zilnic 02:00 + 22:00. Replicare ZFS continuă neîntrerupt. | - |
|
||||||
|
| **2026-04-30 22:13:01** | **pvemini se oprește brusc.** Cauza opririi opace în log-uri. | journalctl --list-boots |
|
||||||
|
| 22:15:35 | Boot attempt #1. systemd: `Expecting device dev-nvme2n1p1.device` | journalctl -b -2 |
|
||||||
|
| 22:17:05 | Timeout 90s. `Dependency failed for mnt-backup.mount`, `nfs-server.service`, `local-fs.target`. Reached `emergency.target`. | journalctl -b -2 |
|
||||||
|
| 22:36:52 | Boot attempt #2 (probabil user retry via reset). Same timeout. | journalctl -b -2 |
|
||||||
|
| 22:38:45 | Boot attempt #3. Same timeout. | journalctl -b -2 |
|
||||||
|
| 22:40:15 | Al treilea timeout. Emergency mode persist. | journalctl -b -2 |
|
||||||
|
| ~22:40 | **User: cold power cycle complet** (oprește buton + repornește) | user |
|
||||||
|
| **22:41:37** | Boot reușit. `Found device dev-nvme2n1p1.device - KINGSTON SNV3S2000G 1`. fsck recovering journal → clean. | journalctl -b -1 |
|
||||||
|
| 22:41:40 | smartd identifică toate 3 NVMe-uri. ZED detectează 14 checksum errors pe Samsung 990 PRO part3, resilver 33.6M cu 0 errors. | zed, smartd |
|
||||||
|
| 23:18:22 | User: shutdown deliberat pentru investigație. | journalctl |
|
||||||
|
| 23:37:09 | Boot stabil. Storage `backup` active, `/mnt/backup` mounted, NFS export reactiv. | journalctl -b 0 |
|
||||||
|
| 23:14 → 00:30 | Investigație + sinteză plan prevenție. | acest document |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Analiză detaliată
|
||||||
|
|
||||||
|
### 1. Hardware: Kingston SNV3S2000G — diagnostic SMART
|
||||||
|
|
||||||
|
```
|
||||||
|
Model Number: KINGSTON SNV3S2000G
|
||||||
|
Serial Number: 50026B76873E864F
|
||||||
|
Firmware Version: P3BR0A27
|
||||||
|
Capacity: 2.00 TB (1.79 TiB usable)
|
||||||
|
Power On Hours: 5,884
|
||||||
|
Power Cycles: 18
|
||||||
|
Unsafe Shutdowns: 12 (mare, dar EFECT al hangurilor anterioare, nu cauză)
|
||||||
|
Critical Warning: 0x00 (clean)
|
||||||
|
Available Spare: 100%
|
||||||
|
Percentage Used: 24%
|
||||||
|
Media and Data Integrity Errors: 0
|
||||||
|
Error Information Log Entries: 0
|
||||||
|
Data Units Written: 56,733,416 [29.0 TB]
|
||||||
|
Data Units Read: 1,274,664 [652 GB]
|
||||||
|
Temperature Sensor 1: 49 Celsius (idle, OK)
|
||||||
|
Temperature Sensor 2: 74 Celsius ⚠️ (warning at 75, critical at 80)
|
||||||
|
Temperature Sensor 3: 28 Celsius (idle, OK)
|
||||||
|
SMART overall-health: PASSED
|
||||||
|
```
|
||||||
|
|
||||||
|
**Concluzia tehnică:**
|
||||||
|
- Wear (24%) și endurance (29 TB written din ~600 TBW spec) = **OK**, nu e eroare de uzură
|
||||||
|
- Media errors = 0 → NAND-ul fizic e bun
|
||||||
|
- Temperature Sensor 2 la 74°C **idle** (sistem tocmai bootat, nu sub load) → controllerul intern sau zona NAND principală e supraîncălzită cronic
|
||||||
|
- Modelul SNV3S = DRAM-less QLC, controller silicon-motion entry-level, cunoscut pentru:
|
||||||
|
- Thermal throttle agresiv >70°C
|
||||||
|
- Controller hang sub workload de scriere secvențială mare (cazul backup-urilor zstd-compressed)
|
||||||
|
- SLC cache mic, post-cache-fill performance dramatic mai mică + temperatură mare
|
||||||
|
|
||||||
|
**Kingston nu e potrivit pentru rolul de backup target la load-ul de aici.**
|
||||||
|
|
||||||
|
### 2. Cascade systemd dependency
|
||||||
|
|
||||||
|
Lanțul de dependențe care pune pvemini în emergency mode:
|
||||||
|
|
||||||
|
```
|
||||||
|
/dev/nvme2n1p1 (NVMe Kingston) timeout 90s
|
||||||
|
└─> dev-nvme2n1p1.device FAILED
|
||||||
|
├─> systemd-fsck@dev-nvme2n1p1.service (dependency)
|
||||||
|
├─> mnt-backup.mount (RequiresMountsFor)
|
||||||
|
│ ├─> nfs-server.service (After=mnt-backup.mount)
|
||||||
|
│ │ ├─> nfs-mountd.service
|
||||||
|
│ │ └─> nfs-idmapd.service
|
||||||
|
│ └─> local-fs.target (Wants=)
|
||||||
|
│ └─> multi-user.target → boot fail
|
||||||
|
└─> emergency.target (fallback)
|
||||||
|
└─> systemd-ask-password-console.service ("Give root password")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Punct critic de design:** mount-ul `/mnt/backup` nu are `nofail` în fstab. Orice failure pe Kingston blochează boot-ul. Cu `nofail`, sistemul ar continua boot-ul fără mount, primești alertă pe `pvesm status`, dar restul cluster-ului (NFS pentru pveelite/pve1 + dependencies) e mai degradat decât rupt.
|
||||||
|
|
||||||
|
### 3. NFS server activ + clienți consequenți
|
||||||
|
|
||||||
|
```
|
||||||
|
/etc/exports:
|
||||||
|
/mnt/backup 10.0.20.0/24(rw,sync,no_subtree_check,no_root_squash)
|
||||||
|
|
||||||
|
Active clients (NFSv4.2):
|
||||||
|
pveelite (10.0.20.202) — confirmed, callback UP
|
||||||
|
pve1 (10.0.20.200) — confirmed, callback UP
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observație importantă:** Proxmox storage `backup-pvemini-nfs` apare `disabled` în `pvesm status`, **dar mount-urile kernel pe pveelite + pve1 sunt încă VII**. Clienții se reconectează automat după restart pvemini, dar în timpul hangului orice operație se înghețează.
|
||||||
|
|
||||||
|
Asta amplifică incidentul: chiar și pveelite + pve1 (care n-au probleme cu Kingston) ar putea ajunge să-și înghețe niște procese (vzdump, df, ls pe `/mnt/pve/backup-pvemini-nfs`) cât timp pvemini era hung.
|
||||||
|
|
||||||
|
### 4. ZFS rpool — fals pozitiv, nu altă problemă
|
||||||
|
|
||||||
|
În log-ul de boot 22:41 apar 14 erori de checksum pe `rpool` (Samsung 990 PRO mirror, OS pool):
|
||||||
|
```
|
||||||
|
zed[1809]: eid=1 class=checksum pool='rpool' vdev=nvme-eui.0025384551a1560c-part3 err=52
|
||||||
|
```
|
||||||
|
|
||||||
|
**NU e o problemă a Samsung-ului.** SMART pe ambele Samsung 990 PRO confirmă:
|
||||||
|
- `Percentage Used: 3%`
|
||||||
|
- `Media Errors: 0`
|
||||||
|
- `Error Log Entries: 0`
|
||||||
|
- Temperature 43-55°C
|
||||||
|
|
||||||
|
Erorile au apărut pentru că Kingston-ul a hang-uit I/O subsystemul în timpul activității, scrierile spre rpool n-au mai ajuns coerent pe ambele oglinzi → la reboot ZFS a detectat divergența, a făcut resilver de 33.6 MB din partenerul de mirror, **0 errors finale**. Mecanism normal ZFS — exact ce trebuie să facă mirror-ul.
|
||||||
|
|
||||||
|
### 5. Severitate finală — DE CE MEDIE și nu CRITICĂ
|
||||||
|
|
||||||
|
Infrastructura ROMFASTSQL are **două mecanisme separate** de protecție date, pe storage-uri fizic separate:
|
||||||
|
|
||||||
|
| Mecanism | Storage | Disc fizic | Frecvență max | Status după hang |
|
||||||
|
|----------|---------|------------|---------------|------------------|
|
||||||
|
| **Replicare ZFS** | `rpool/data` | Samsung 990 PRO mirror | 5 min (CT 108 + VM 201) | ✅ Continuă neafectată |
|
||||||
|
| **Backup vzdump** | `/mnt/backup` | Kingston SNV3S | 24h | ⚠️ Stop pe durata hangului |
|
||||||
|
|
||||||
|
**Replicare ZFS configurată (verificat 04-30 23:55):**
|
||||||
|
- CT 108 Oracle XE → pve1 + pveelite, schedule `*/5 min` (RPO 5 min)
|
||||||
|
- VM 201 Windows IIS → pve1 + pveelite, schedule `*/5 min` (RPO 5 min)
|
||||||
|
- CT 171 Claude Agent → pve1 + pveelite, `*/15 min`
|
||||||
|
- CT 106 Gitea, CT 110 MoltBot → toate noduri, `*/2h`
|
||||||
|
- CT 100/101/103/104/105, VM 109 → 24h
|
||||||
|
- Toate joburile: FailCount=0, Yes/Enabled
|
||||||
|
|
||||||
|
**De ce contează:** dacă Kingston-ul ar muri DEFINITIV chiar acum, infrastructura **continuă să funcționeze**:
|
||||||
|
- Toate VM/CT-urile rămân online (live data e pe Samsung)
|
||||||
|
- DR rapid prin `pvesr switchover` posibil în <1 min
|
||||||
|
- Pierderi efective: capacitate de archive/rollback istoric (ex: cineva DROP TABLE acum 3 zile, descoperă azi → fără vzdump nu poți rollback)
|
||||||
|
|
||||||
|
Severity = MEDIE pentru că: efectul vizibil e amplu (pvemini hang, cluster degradat), dar **nu există pierdere de servicii sau date**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cine scrie pe `/mnt/backup` — referință
|
||||||
|
|
||||||
|
**Backup vzdump zilnic 02:00 (storage `backup`):**
|
||||||
|
| ID | Serviciu | Severitate dacă pierdem archive |
|
||||||
|
|----|----------|--------------------------------|
|
||||||
|
| CT 100 | portainer | 🟡 MEDIE (recoverable din replicare) |
|
||||||
|
| CT 104 | Flowise (chatbot AI) | 🟠 IMPORTANT |
|
||||||
|
| CT 106 | Gitea (git server) | 🟠 IMPORTANT |
|
||||||
|
| CT 108 | **Oracle XE producție** | 🔴 IMPORTANT (rollback istoric pierdut) |
|
||||||
|
| CT 171 | Claude Agent | 🟡 MEDIE |
|
||||||
|
| VM 201 | **Windows IIS + ROA** | 🔴 IMPORTANT |
|
||||||
|
|
||||||
|
**Backup vzdump 22:00 + sâmbătă 15:00 (storage `backup-pvemini-nfs`):**
|
||||||
|
- CT 101 minecraft, CT 110 MoltBot, VM 109 Oracle DR
|
||||||
|
|
||||||
|
**NFS export către cluster:**
|
||||||
|
- Path: `/mnt/backup` exportat `10.0.20.0/24(rw,sync,no_root_squash)`
|
||||||
|
- Clienți activi: pveelite, pve1
|
||||||
|
|
||||||
|
**Samba windows-share:**
|
||||||
|
- `/mnt/backup/windows-share` cu samba `[backup-share]` pentru `backup-user`
|
||||||
|
- Conținut actual: 1 fișier text de test (irelevant)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plan de prevenție
|
||||||
|
|
||||||
|
### 🔴 Tier 1 — Stop la cascada de azi (5-15 min, low risk, REVERSIBIL)
|
||||||
|
|
||||||
|
**1. `nofail` pe `/mnt/backup` în fstab**
|
||||||
|
```bash
|
||||||
|
ssh root@10.0.20.201
|
||||||
|
# Editare /etc/fstab linia /mnt/backup:
|
||||||
|
# vechi: /dev/nvme2n1p1 /mnt/backup ext4 defaults 0 2
|
||||||
|
# nou: /dev/nvme2n1p1 /mnt/backup ext4 defaults,nofail,x-systemd.device-timeout=10s 0 2
|
||||||
|
systemctl daemon-reload
|
||||||
|
```
|
||||||
|
Boot-ul nu mai cade în emergency mode dacă SSD-ul e mort. `pvesm status` raportează `inactive`. Joburile vzdump eșuează cu eroare clară (nu hang).
|
||||||
|
|
||||||
|
**2. Decuplează NFS server de mount**
|
||||||
|
```bash
|
||||||
|
# /etc/systemd/system/nfs-server.service.d/no-backup-dependency.conf
|
||||||
|
[Unit]
|
||||||
|
RequiresMountsFor=
|
||||||
|
After=
|
||||||
|
```
|
||||||
|
Sau, mai sigur, dezactivează exportul `/mnt/backup` complet (nu mai e folosit pentru Proxmox cluster):
|
||||||
|
```bash
|
||||||
|
# /etc/exports — comentează linia
|
||||||
|
# /mnt/backup 10.0.20.0/24(rw,sync,no_subtree_check,no_root_squash)
|
||||||
|
exportfs -ra
|
||||||
|
# Verifică pe pveelite + pve1 că nu mai au nimic care folosește mount-ul:
|
||||||
|
ssh root@10.0.20.202 "umount /mnt/pve/backup-pvemini-nfs 2>/dev/null"
|
||||||
|
ssh root@10.0.20.200 "umount /mnt/pve/backup-pvemini-nfs 2>/dev/null"
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Hardware watchdog activ**
|
||||||
|
```bash
|
||||||
|
# /etc/systemd/system.conf
|
||||||
|
RuntimeWatchdogSec=30s
|
||||||
|
RebootWatchdogSec=10min
|
||||||
|
```
|
||||||
|
Dacă pvemini intră în hang persistent (>30s fără ping watchdog), kernel-ul resetează automat. Combinat cu `kernel.panic=10` (deja setat după 04-20), recovery autonom fără intervenție fizică.
|
||||||
|
|
||||||
|
**4. smartd alerting configurat (nu doar monitor)**
|
||||||
|
```bash
|
||||||
|
# /etc/smartd.conf — adăugare per device
|
||||||
|
/dev/nvme2 -d nvme -W 0,70,75 -l error -l selftest -m root@romfast.ro
|
||||||
|
```
|
||||||
|
Trimite email la temperature >70°C (warning) sau >75°C (critical), plus orice eroare critică. **Necesită fix DKIM/SPF pe romfast.ro** (rămas din planul 04-20).
|
||||||
|
|
||||||
|
### 🟠 Tier 2 — Detecție înainte să dăuneze (30-60 min)
|
||||||
|
|
||||||
|
**5. Înlocuiește Kingston-ul** pentru rolul de backup target
|
||||||
|
- Recomandare: Samsung 990 PRO 2TB / WD Red SN700 / Crucial T500 (TLC + DRAM, EWS suficient)
|
||||||
|
- Buget: ~700-1000 RON
|
||||||
|
- Migrare: copy `/mnt/backup` pe disc nou, swap fizic, nou mount cu `nofail` din start
|
||||||
|
|
||||||
|
**6. Backup secundar oglindit pe pve1**
|
||||||
|
- pve1 are 32 GB RAM nefolosit + storage `local` (1.25 TiB liber)
|
||||||
|
- Mirror nightly al `/mnt/backup` → `/var/lib/vz/dump/` pe pve1 prin rsync
|
||||||
|
- Dacă Kingston moare, ai copia pe pve1 ca insurance archive
|
||||||
|
|
||||||
|
**7. Prometheus node-exporter cu metrice SMART/temperature**
|
||||||
|
- Trend de temperatură vizibil grafic, nu doar prag
|
||||||
|
- Alertă proactivă când pattern-ul de temperatură se modifică (presimptom de hang)
|
||||||
|
|
||||||
|
### 🟡 Tier 3 — Reducere blast radius (1-2 zile)
|
||||||
|
|
||||||
|
**8. Mută destinația principală vzdump pe pve1**
|
||||||
|
- pvemini = host primary services, NU și backup target (dublu rol = dublu SPOF)
|
||||||
|
- pve1 = quorum-only acum, ar putea găzdui storage backup principal
|
||||||
|
- Modifică joburile vzdump: `storage backup` → storage nou pe pve1 montat NFS pe pvemini
|
||||||
|
|
||||||
|
**9. Termină rămășițele din 2026-04-20**
|
||||||
|
- [ ] kdump-tools pe pvemini (crash dump capture, necesită `crashkernel=256M` în GRUB + reboot)
|
||||||
|
- [ ] apcupsd/nut UPS monitoring (cablul e conectat, dar nu există jurnal evenimente)
|
||||||
|
- [ ] **DKIM/SPF fix `romfast.ro`** — fără ăsta, alertele smartd nu ajung pe Gmail (vezi log 2026-04-20 00:00:02)
|
||||||
|
- [ ] OOM alerting cron (oom-alert.sh)
|
||||||
|
|
||||||
|
### 🟢 Tier 4 — Capacity planning
|
||||||
|
|
||||||
|
**10. RAM upgrade pveelite la 32+ GB** (rămas din 04-20, ~300 RON)
|
||||||
|
- Permite HA failover real al serviciilor pvemini către pveelite în caz de hang lung
|
||||||
|
- Acum pveelite (16 GB) nu poate prelua CT 108 + VM 201 + restul în paralel
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Acțiuni — executate 2026-04-30 23:14 → 2026-05-01 00:20
|
||||||
|
|
||||||
|
### Investigație
|
||||||
|
- [x] Diagnostic SMART pe toate 3 NVMe-uri (Kingston + 2x Samsung 990 PRO)
|
||||||
|
- [x] Verificare ZFS rpool — auto-resilver 33.6M, 0 erori, OK
|
||||||
|
- [x] Analiză cronologie hang via journalctl --list-boots + journalctl -b -2
|
||||||
|
- [x] Identificare scriitori `/mnt/backup` (vzdump joburi, NFS export, samba windows-share)
|
||||||
|
- [x] Verificare replicări ZFS — toate joburile FailCount=0
|
||||||
|
- [x] Documentare incident (acest fișier)
|
||||||
|
|
||||||
|
### Tier 1 prevention — APLICAT (2026-05-01 00:11 → 00:20)
|
||||||
|
|
||||||
|
Backup-uri config la `/root/backup-tier1-prevention-2026-04-30/` pe pvemini.
|
||||||
|
|
||||||
|
- [x] **Tier 1.1** `nofail` în fstab pentru `/mnt/backup`
|
||||||
|
```
|
||||||
|
/dev/nvme2n1p1 /mnt/backup ext4 defaults,nofail,x-systemd.device-timeout=10s 0 2
|
||||||
|
```
|
||||||
|
Efect: la boot, dacă Kingston nu răspunde, sistemul nu mai cade în emergency mode. Storage `backup` apare `inactive` în pvesm — semnal vizual clar.
|
||||||
|
|
||||||
|
- [x] **Tier 1.3** Hardware watchdog activat
|
||||||
|
- `iTCO_wdt` încărcat (anterior doar `softdog` software era activ, ineficient la kernel hang)
|
||||||
|
- `/etc/systemd/system.conf`:
|
||||||
|
```
|
||||||
|
RuntimeWatchdogSec=30s
|
||||||
|
WatchdogDevice=/dev/watchdog1
|
||||||
|
RebootWatchdogSec=10min
|
||||||
|
```
|
||||||
|
- `systemctl daemon-reexec` aplicat — systemd ping-uiește activ `/dev/watchdog1` (use count = 2)
|
||||||
|
- Persistență la boot: `/etc/modules-load.d/itco-wdt.conf` + blacklist softdog în `/etc/modprobe.d/blacklist-softdog.conf`
|
||||||
|
- Verificare: `wdctl /dev/watchdog1` → Identity: `iTCO_wdt`, Timeout: 30s, Timeleft: 30s
|
||||||
|
|
||||||
|
Efect: dacă pvemini hang-uiește total (orice cauză — Kingston, kernel panic, RAM error), hardware-ul resetează automat în maxim 30s. Combinat cu `kernel.panic=10` (deja setat din 2026-04-20) → recovery autonom, fără intervenție fizică. Va salva și de incidente similare cu cel din 2026-04-20 (hang total fără logs).
|
||||||
|
|
||||||
|
### Tier 2 + Distantare temporală — APLICAT (2026-05-01 00:30)
|
||||||
|
|
||||||
|
Backup config la `/root/backup-tier1-prevention-2026-04-30/jobs.cfg.original`.
|
||||||
|
|
||||||
|
- [x] **Restructurare joburi vzdump pentru reducere stres thermal Kingston pvemini**
|
||||||
|
|
||||||
|
**Schimbări fundamentale:**
|
||||||
|
1. **Distantare temporală** — joburile rulează la ore diferite (în loc de 6 consecutive în 8 minute), permitând cooldown între ele
|
||||||
|
2. **Alternare zile par/impar** — fiecare CT/VM primește backup la 2 zile (în loc de zilnic), reducând la jumătate scrisul
|
||||||
|
3. **Mutare CT 101 + CT 110 pe pve1 `backup-ssd`** — scoate ~55 GB scrieri/2 zile de pe Kingston pvemini, le pune pe Kingston SA400S37 SATA pe pve1 (rece 30°C, 98% life left)
|
||||||
|
|
||||||
|
**Joburi vechi șterse:**
|
||||||
|
- `backup-fbb668c0-726e` (02:00 zilnic, 6 servicii) → suprimat
|
||||||
|
- `backup-981b377f-a661` (22:00 zilnic, 2 servicii) → suprimat
|
||||||
|
|
||||||
|
**Joburi noi configurate:**
|
||||||
|
|
||||||
|
| Job ID | Schedule | Storage | VM/CT |
|
||||||
|
|--------|----------|---------|-------|
|
||||||
|
| `backup-impare-oracle` | `*-*-01/2 02:00` | backup (Kingston pvemini) | CT 108 Oracle |
|
||||||
|
| `backup-impare-rest` | `*-*-01/2 03:00` | backup (Kingston pvemini) | CT 100, 106, 171 |
|
||||||
|
| `backup-pare-flowise` | `*-*-02/2 02:00` | backup (Kingston pvemini) | CT 104 Flowise |
|
||||||
|
| `backup-pare-windows` | `*-*-02/2 03:30` | backup (Kingston pvemini) | VM 201 IIS |
|
||||||
|
| `backup-pve1-minecraft` | `*-*-01/2 22:00` | **backup-ssd (pve1)** | CT 101 minecraft |
|
||||||
|
| `backup-pve1-moltbot` | `*-*-02/2 22:00` | **backup-ssd (pve1)** | CT 110 MoltBot |
|
||||||
|
| `backup-f1c87584-8eb2` | sat lunar 15:00 | backup-pvemini-nfs | VM 109 (păstrat) |
|
||||||
|
|
||||||
|
**Retenție:** `keep-last=2, keep-weekly=1` — păstrăm 2 backup-uri recente + 1 săptămânal vechi.
|
||||||
|
|
||||||
|
**Calendar de execuție pe Kingston pvemini:**
|
||||||
|
- **Zile impare** (1, 3, 5, 7, ...): 02:00 CT 108 → 03:00 CT 100, 106, 171 (1h cooldown)
|
||||||
|
- **Zile pare** (2, 4, 6, 8, ...): 02:00 CT 104 → 03:30 VM 201 (1.5h cooldown)
|
||||||
|
- **Sâmbăta lunar** (1-7 ale lunii): 15:00 VM 109
|
||||||
|
|
||||||
|
**Calendar de execuție pe pve1 backup-ssd:**
|
||||||
|
- **Zile impare**: 22:00 CT 101 minecraft (~38 GB)
|
||||||
|
- **Zile pare**: 22:00 CT 110 MoltBot (~17 GB)
|
||||||
|
|
||||||
|
**Beneficii thermale așteptate:**
|
||||||
|
- Reducere scrieri pe Kingston pvemini: ~50% (alternanță par/impar)
|
||||||
|
- Plus: ~55 GB / 2 zile mutate pe pve1
|
||||||
|
- Cooldown 1+ oră între joburi pe Kingston pvemini (vs. 0 înainte)
|
||||||
|
- Net total: ~75% reducere stres thermic concentrat pe Kingston pvemini
|
||||||
|
|
||||||
|
### Tier 1 — SKIPPED cu motivație
|
||||||
|
|
||||||
|
- [ ] ~~**Tier 1.2** Decuplare NFS server / dezactivare export `/mnt/backup`~~
|
||||||
|
**Motiv:** verificare cluster a arătat că `backup-pvemini-nfs` este `active` pe pveelite și pve1 (mount NFS live). Joburile vzdump 22:00 (CT 101 minecraft, CT 110 MoltBot — ambele live pe pve1) și sâmbătă 15:00 (VM 109 Oracle DR — live pe pveelite) folosesc acest mount pentru a scrie pe Kingston. Dezactivarea exportului ar rupe acele joburi.
|
||||||
|
**Plan:** se rezolvă în Tier 3.8 prin mutarea backup target-ului pe pve1 (storage local), când joburile vor fi reconfigurate.
|
||||||
|
|
||||||
|
- [ ] ~~**Tier 1.4** smartd alerting cu email~~
|
||||||
|
**Motiv:** depinde de DKIM/SPF fix `romfast.ro` rămas din 2026-04-20. Fără DKIM, Gmail respinge alertele. Configurare locală în log e oricum activă deja prin smartd default.
|
||||||
|
|
||||||
|
### Monitoring — APLICAT (2026-05-01 00:31)
|
||||||
|
|
||||||
|
- [x] **`/opt/scripts/kingston-thermal-monitor.sh`** — rulează la fiecare 30 min via cron pe pvemini
|
||||||
|
- Citește SMART JSON via `smartctl -j -A /dev/nvme2`
|
||||||
|
- Loghează CSV trend în `/var/log/kingston-thermal.csv` (timestamp, temperaturi, spare, used, errors, etc.)
|
||||||
|
- State management în `/var/run/kingston-thermal-state` (ok/warning/critical)
|
||||||
|
- Trimite mail alertă DOAR la tranziție de la stare bună la mai rea (no spam)
|
||||||
|
- Trigger automat journal logging via `logger -t kingston-thermal`
|
||||||
|
- Praguri: Sensor 2 ≥70°C warning, ≥75°C critical, plus media errors / spare / used
|
||||||
|
|
||||||
|
Testare la deploy: state inițial = `warning` (Sensor 2 = 74°C), alertă inițială trimisă către mmarius28@gmail.com.
|
||||||
|
|
||||||
|
Verificare trend: `journalctl -t kingston-thermal --since today` sau `tail /var/log/kingston-thermal.csv`
|
||||||
|
|
||||||
|
## Acțiuni — RĂMAS DE FĂCUT
|
||||||
|
|
||||||
|
- [ ] **MONITORIZARE** — după primul ciclu complet de backup-uri par+impar (3 zile, până 2026-05-04), verifică `tail -100 /var/log/kingston-thermal.csv` și calculează trend Sensor 2:
|
||||||
|
- Așteptare: media zilnică Sensor 2 sub 70°C, vârfuri în timp de backup ≤72°C
|
||||||
|
- Dacă rămâne ≥74°C constant: problema e structural în hardware (controller / cooling pasiv insuficient), nu în load — trecere la Tier 2.5 (înlocuire SSD).
|
||||||
|
- [ ] **Verificare fizică Kingston pvemini** — are heatsink M.2? Are flux de aer? E sub alt component cald? (răcire pasivă insuficientă = cauză probabilă)
|
||||||
|
- [ ] **DKIM/SPF fix `romfast.ro`** — prerequisite pentru Tier 1.4 + restul alertelor email (rămas din 2026-04-20)
|
||||||
|
- [ ] **Tier 1.4** smartd email alerting cu prag temperatură 70°C (după DKIM fix)
|
||||||
|
- [ ] **Tier 2.5** Înlocuire Kingston SNV3S → SSD adecvat (Samsung 990 PRO / WD Red SN700 / Crucial T500)
|
||||||
|
- [ ] **Tier 2.6** Backup secundar oglindit (mirror nightly /mnt/backup → /mnt/pve/backup-ssd via rsync ca insurance)
|
||||||
|
- [ ] **Tier 3.8** NFS export pentru `backup-ssd` ca să poată găzdui și VM 109 (acum doar local pe pve1)
|
||||||
|
- [ ] **Verificare** `zpool scrub rpool` — confirmă sănătate Samsung după turbulență (durată estimată 15-40 min, online)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anexă — comenzi de verificare
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stare curentă storage și mount
|
||||||
|
ssh root@10.0.20.201 "pvesm status; df -h /mnt/backup; mount | grep nvme2"
|
||||||
|
|
||||||
|
# SMART Kingston (vezi temperatura Sensor 2)
|
||||||
|
ssh root@10.0.20.201 "smartctl -a /dev/nvme2 | grep -iE 'temperature|critical|spare|percentage|media'"
|
||||||
|
|
||||||
|
# Verificare cronologie boot-uri
|
||||||
|
ssh root@10.0.20.201 "journalctl --list-boots --no-pager | tail -10"
|
||||||
|
|
||||||
|
# Verificare clienți NFS activi
|
||||||
|
ssh root@10.0.20.201 "cat /proc/fs/nfsd/clients/*/info 2>/dev/null | grep -E 'name|address'"
|
||||||
|
|
||||||
|
# Verificare replicare (cea care chiar te salvează)
|
||||||
|
ssh root@10.0.20.201 "pvesr status"
|
||||||
|
|
||||||
|
# Verificare scrub rpool
|
||||||
|
ssh root@10.0.20.201 "zpool status rpool"
|
||||||
|
```
|
||||||
128
proxmox/cluster/scripts/kingston-thermal-monitor.sh
Normal file
128
proxmox/cluster/scripts/kingston-thermal-monitor.sh
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Kingston SNV3S2000G (nvme2) thermal + health monitor
|
||||||
|
# Runs every 30 min from cron, logs CSV trend, alerts on threshold transitions
|
||||||
|
# Schedule (cron on pvemini): */30 * * * * /opt/scripts/kingston-thermal-monitor.sh
|
||||||
|
#
|
||||||
|
# Background: incident 2026-04-30 — Kingston backup SSD hung due to thermal stress
|
||||||
|
# (Sensor 2 = 74°C idle). This script tracks recovery after distantare temporala +
|
||||||
|
# alternare par/impar deployed 2026-05-01.
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
|
||||||
|
DEVICE="/dev/nvme2"
|
||||||
|
DEVICE_LABEL="Kingston SNV3S2000G (backup SSD)"
|
||||||
|
LOG_FILE="/var/log/kingston-thermal.csv"
|
||||||
|
STATE_FILE="/var/run/kingston-thermal-state"
|
||||||
|
ALERT_RECIPIENT="${ALERT_RECIPIENT:-mmarius28@gmail.com}"
|
||||||
|
ALERT_FROM="ups@romfast.ro"
|
||||||
|
|
||||||
|
# Thresholds
|
||||||
|
WARN_TEMP=70 # Sensor 2 warning
|
||||||
|
CRIT_TEMP=75 # Sensor 2 critical (kernel warning at 75 per SMART spec)
|
||||||
|
WARN_SPARE=50 # below = warning
|
||||||
|
WARN_USED=80 # above = warning
|
||||||
|
|
||||||
|
# Initialize log with header if missing
|
||||||
|
if [ ! -f "$LOG_FILE" ]; then
|
||||||
|
echo "timestamp,critical_warning,temp_composite,sensor1,sensor2,sensor3,available_spare,percentage_used,media_errors,unsafe_shutdowns,warning_temp_time,critical_comp_time,data_units_written,power_on_hours" > "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read SMART JSON
|
||||||
|
SMART=$(smartctl -j -A "$DEVICE" 2>/dev/null) || {
|
||||||
|
logger -t kingston-thermal "ERROR: smartctl failed for $DEVICE"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract values (jq -r so unquoted)
|
||||||
|
CRIT_WARN=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.critical_warning')
|
||||||
|
TEMP_COMP=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.temperature')
|
||||||
|
TEMP_S1=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.temperature_sensors[0] // "0"')
|
||||||
|
TEMP_S2=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.temperature_sensors[1] // "0"')
|
||||||
|
TEMP_S3=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.temperature_sensors[2] // "0"')
|
||||||
|
SPARE=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.available_spare')
|
||||||
|
USED=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.percentage_used')
|
||||||
|
MEDIA_ERR=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.media_errors')
|
||||||
|
UNSAFE=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.unsafe_shutdowns')
|
||||||
|
WARN_TIME=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.warning_temp_time')
|
||||||
|
CRIT_TIME=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.critical_comp_time')
|
||||||
|
DUW=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.data_units_written')
|
||||||
|
POH=$(echo "$SMART" | jq -r '.nvme_smart_health_information_log.power_on_hours')
|
||||||
|
|
||||||
|
TS=$(date -Iseconds)
|
||||||
|
|
||||||
|
# Append to CSV log
|
||||||
|
echo "$TS,$CRIT_WARN,$TEMP_COMP,$TEMP_S1,$TEMP_S2,$TEMP_S3,$SPARE,$USED,$MEDIA_ERR,$UNSAFE,$WARN_TIME,$CRIT_TIME,$DUW,$POH" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
# Determine current state
|
||||||
|
STATE="ok"
|
||||||
|
REASON=""
|
||||||
|
if [ "$CRIT_WARN" != "0" ]; then
|
||||||
|
STATE="critical"; REASON="Critical Warning flag = $CRIT_WARN (NVMe firmware reports issue)"
|
||||||
|
elif [ "$MEDIA_ERR" -gt 0 ]; then
|
||||||
|
STATE="critical"; REASON="Media errors = $MEDIA_ERR (data integrity loss)"
|
||||||
|
elif [ "$TEMP_S2" -ge "$CRIT_TEMP" ]; then
|
||||||
|
STATE="critical"; REASON="Sensor 2 = ${TEMP_S2}°C >= ${CRIT_TEMP}°C critical threshold"
|
||||||
|
elif [ "$TEMP_S2" -ge "$WARN_TEMP" ]; then
|
||||||
|
STATE="warning"; REASON="Sensor 2 = ${TEMP_S2}°C >= ${WARN_TEMP}°C warning threshold"
|
||||||
|
elif [ "$SPARE" -lt "$WARN_SPARE" ]; then
|
||||||
|
STATE="warning"; REASON="Available Spare = ${SPARE}% (below ${WARN_SPARE}%)"
|
||||||
|
elif [ "$USED" -ge "$WARN_USED" ]; then
|
||||||
|
STATE="warning"; REASON="Percentage Used = ${USED}% (above ${WARN_USED}%)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# State management — alert only on transitions to a worse state
|
||||||
|
PREV_STATE=$(cat "$STATE_FILE" 2>/dev/null || echo "ok")
|
||||||
|
|
||||||
|
# Severity ranking for transition detection
|
||||||
|
state_rank() {
|
||||||
|
case "$1" in
|
||||||
|
ok) echo 0 ;;
|
||||||
|
warning) echo 1 ;;
|
||||||
|
critical) echo 2 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
PREV_RANK=$(state_rank "$PREV_STATE")
|
||||||
|
CURR_RANK=$(state_rank "$STATE")
|
||||||
|
|
||||||
|
if [ "$CURR_RANK" -gt "$PREV_RANK" ]; then
|
||||||
|
# Worsening transition — send alert
|
||||||
|
SUBJECT="[ALERT-$STATE] $DEVICE_LABEL on $(hostname): $REASON"
|
||||||
|
{
|
||||||
|
echo "Kingston SSD ($DEVICE_LABEL) on $(hostname)"
|
||||||
|
echo "State: $PREV_STATE -> $STATE"
|
||||||
|
echo "Reason: $REASON"
|
||||||
|
echo "Time: $TS"
|
||||||
|
echo
|
||||||
|
echo "=== SMART snapshot ==="
|
||||||
|
echo "Critical Warning: $CRIT_WARN"
|
||||||
|
echo "Temp composite: ${TEMP_COMP}°C"
|
||||||
|
echo "Temp Sensor 1: ${TEMP_S1}°C"
|
||||||
|
echo "Temp Sensor 2: ${TEMP_S2}°C <-- monitored"
|
||||||
|
echo "Temp Sensor 3: ${TEMP_S3}°C"
|
||||||
|
echo "Available Spare: ${SPARE}%"
|
||||||
|
echo "Percentage Used: ${USED}%"
|
||||||
|
echo "Media Errors: $MEDIA_ERR"
|
||||||
|
echo "Unsafe Shutdowns: $UNSAFE"
|
||||||
|
echo "Warning Temp Time: ${WARN_TIME} min"
|
||||||
|
echo "Critical Comp Time: ${CRIT_TIME} min"
|
||||||
|
echo "Power On Hours: $POH"
|
||||||
|
echo "Data Units Written: $DUW (= $((DUW / 2)) MB written total)"
|
||||||
|
echo
|
||||||
|
echo "Last 5 CSV log entries:"
|
||||||
|
tail -5 "$LOG_FILE"
|
||||||
|
echo
|
||||||
|
echo "Investigate: ssh root@$(hostname -I | awk '{print $1}') 'smartctl -a $DEVICE'"
|
||||||
|
} | mail -r "$ALERT_FROM" -s "$SUBJECT" "$ALERT_RECIPIENT" 2>/dev/null || true
|
||||||
|
logger -t kingston-thermal "ALERT $PREV_STATE -> $STATE: $REASON"
|
||||||
|
elif [ "$CURR_RANK" -lt "$PREV_RANK" ]; then
|
||||||
|
# Recovery transition — log only, no alert spam
|
||||||
|
logger -t kingston-thermal "RECOVERY $PREV_STATE -> $STATE (Sensor2=${TEMP_S2}°C, used=${USED}%)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Persist current state
|
||||||
|
echo "$STATE" > "$STATE_FILE"
|
||||||
|
|
||||||
|
# Routine status to journal (visible in journalctl -t kingston-thermal)
|
||||||
|
logger -t kingston-thermal "OK device=$DEVICE state=$STATE temp_comp=${TEMP_COMP}C s1=${TEMP_S1}C s2=${TEMP_S2}C s3=${TEMP_S3}C used=${USED}% spare=${SPARE}% media_err=$MEDIA_ERR"
|
||||||
222
proxmox/cluster/scripts/kingston-thermal-report.sh
Normal file
222
proxmox/cluster/scripts/kingston-thermal-report.sh
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Kingston SNV3S2000G — daily thermal trend report
|
||||||
|
# Reads /var/log/kingston-thermal.csv (populated every 30 min by kingston-thermal-monitor.sh)
|
||||||
|
# Produces human-readable summary: current state, 24h min/max/avg, day-over-day delta, alert events.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# /opt/scripts/kingston-thermal-report.sh # raport pe stdout
|
||||||
|
# /opt/scripts/kingston-thermal-report.sh --mail # raport via mail la mmarius28@gmail.com
|
||||||
|
#
|
||||||
|
# Schedule (suggested cron): 0 8 * * * /opt/scripts/kingston-thermal-report.sh --mail
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
|
||||||
|
LOG_FILE="/var/log/kingston-thermal.csv"
|
||||||
|
STATE_FILE="/var/run/kingston-thermal-state"
|
||||||
|
ALERT_RECIPIENT="${ALERT_RECIPIENT:-mmarius28@gmail.com}"
|
||||||
|
ALERT_FROM="ups@romfast.ro"
|
||||||
|
|
||||||
|
if [ ! -f "$LOG_FILE" ]; then
|
||||||
|
echo "Eroare: $LOG_FILE nu exista. Scriptul de monitoring nu a rulat inca?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ROW_COUNT=$(awk 'NR>1' "$LOG_FILE" | wc -l)
|
||||||
|
if [ "$ROW_COUNT" -eq 0 ]; then
|
||||||
|
echo "Eroare: Fara date in $LOG_FILE (doar header)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Helper: filter rows in last N hours
|
||||||
|
filter_last_hours() {
|
||||||
|
local hours="$1"
|
||||||
|
local cutoff
|
||||||
|
cutoff=$(date -d "$hours hours ago" -Iseconds)
|
||||||
|
awk -F, -v cutoff="$cutoff" 'NR>1 && $1 >= cutoff' "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stats: min, max, avg pentru o coloana data
|
||||||
|
stats_col() {
|
||||||
|
local col="$1"
|
||||||
|
awk -F, -v col="$col" '
|
||||||
|
BEGIN { min=999; max=-999; sum=0; n=0 }
|
||||||
|
{ v=$col; if (v<min) min=v; if (v>max) max=v; sum+=v; n++ }
|
||||||
|
END {
|
||||||
|
if (n==0) print "N/A,N/A,N/A,0"
|
||||||
|
else printf "%d,%d,%.1f,%d\n", min, max, sum/n, n
|
||||||
|
}
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Time spent above threshold (count rows * 30 min, in ore)
|
||||||
|
time_above_threshold() {
|
||||||
|
local col="$1"
|
||||||
|
local threshold="$2"
|
||||||
|
awk -F, -v col="$col" -v t="$threshold" '
|
||||||
|
NR>1 && $col >= t { n++ }
|
||||||
|
END { printf "%.1f\n", (n*30/60) }
|
||||||
|
' "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Latest reading
|
||||||
|
LATEST=$(tail -1 "$LOG_FILE")
|
||||||
|
LATEST_TS=$(echo "$LATEST" | awk -F, '{print $1}')
|
||||||
|
LATEST_S2=$(echo "$LATEST" | awk -F, '{print $5}')
|
||||||
|
LATEST_COMP=$(echo "$LATEST" | awk -F, '{print $3}')
|
||||||
|
LATEST_SPARE=$(echo "$LATEST" | awk -F, '{print $7}')
|
||||||
|
LATEST_USED=$(echo "$LATEST" | awk -F, '{print $8}')
|
||||||
|
LATEST_MEDIA=$(echo "$LATEST" | awk -F, '{print $9}')
|
||||||
|
LATEST_CRITWARN=$(echo "$LATEST" | awk -F, '{print $2}')
|
||||||
|
LATEST_WARNTIME=$(echo "$LATEST" | awk -F, '{print $11}')
|
||||||
|
LATEST_CRITTIME=$(echo "$LATEST" | awk -F, '{print $12}')
|
||||||
|
CURRENT_STATE=$(cat "$STATE_FILE" 2>/dev/null || echo "unknown")
|
||||||
|
|
||||||
|
# Stats last 24h
|
||||||
|
LAST24=$(filter_last_hours 24)
|
||||||
|
S24_COUNT=$(echo "$LAST24" | grep -c '^' || true)
|
||||||
|
[ -z "$LAST24" ] && S24_COUNT=0
|
||||||
|
|
||||||
|
if [ "$S24_COUNT" -gt 0 ]; then
|
||||||
|
S24_S2_STATS=$(echo "$LAST24" | stats_col 5)
|
||||||
|
S24_S2_MIN=$(echo "$S24_S2_STATS" | cut -d, -f1)
|
||||||
|
S24_S2_MAX=$(echo "$S24_S2_STATS" | cut -d, -f2)
|
||||||
|
S24_S2_AVG=$(echo "$S24_S2_STATS" | cut -d, -f3)
|
||||||
|
else
|
||||||
|
S24_S2_MIN="N/A"; S24_S2_MAX="N/A"; S24_S2_AVG="N/A"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stats 24-48h ago
|
||||||
|
LAST48_RAW=$(filter_last_hours 48)
|
||||||
|
CUTOFF24=$(date -d '24 hours ago' -Iseconds)
|
||||||
|
LAST48=$(echo "$LAST48_RAW" | awk -F, -v cutoff="$CUTOFF24" '$1 < cutoff')
|
||||||
|
S48_COUNT=$(echo "$LAST48" | grep -c '^' || true)
|
||||||
|
[ -z "$LAST48" ] && S48_COUNT=0
|
||||||
|
|
||||||
|
if [ "$S48_COUNT" -gt 0 ]; then
|
||||||
|
S48_S2_STATS=$(echo "$LAST48" | stats_col 5)
|
||||||
|
S48_S2_AVG=$(echo "$S48_S2_STATS" | cut -d, -f3)
|
||||||
|
else
|
||||||
|
S48_S2_AVG="N/A"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Alert events (24h)
|
||||||
|
ALERT_EVENTS=$(journalctl -t kingston-thermal --since '24 hours ago' --no-pager 2>/dev/null | grep -E 'ALERT|RECOVERY' || echo "(niciun eveniment de tranzitie in 24h)")
|
||||||
|
|
||||||
|
# Backup jobs (24h)
|
||||||
|
BACKUP_EVENTS=$(journalctl -u pvescheduler --since '24 hours ago' --no-pager 2>/dev/null | grep -iE 'backup|vzdump' | tail -10 || echo "(niciun job inregistrat)")
|
||||||
|
[ -z "$BACKUP_EVENTS" ] && BACKUP_EVENTS="(niciun job inregistrat)"
|
||||||
|
|
||||||
|
# Status indicator
|
||||||
|
case "$CURRENT_STATE" in
|
||||||
|
ok) STATUS_LINE="OK" ;;
|
||||||
|
warning) STATUS_LINE="WARNING" ;;
|
||||||
|
critical) STATUS_LINE="CRITICAL" ;;
|
||||||
|
*) STATUS_LINE="UNKNOWN" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Trend 24h vs 48h
|
||||||
|
TREND_LINE="(prea putine date pentru trend)"
|
||||||
|
if [ "$S48_S2_AVG" != "N/A" ] && [ "$S24_S2_AVG" != "N/A" ]; then
|
||||||
|
DELTA=$(awk -v a="$S24_S2_AVG" -v b="$S48_S2_AVG" 'BEGIN { printf "%.1f", a-b }')
|
||||||
|
if awk "BEGIN { exit !($DELTA < -1) }"; then
|
||||||
|
TREND_LINE="Sensor 2 a SCAZUT cu ${DELTA}C fata de ieri (${S48_S2_AVG} -> ${S24_S2_AVG}) - imbunatatire"
|
||||||
|
elif awk "BEGIN { exit !($DELTA > 1) }"; then
|
||||||
|
TREND_LINE="Sensor 2 a CRESCUT cu +${DELTA}C fata de ieri (${S48_S2_AVG} -> ${S24_S2_AVG}) - atentie!"
|
||||||
|
else
|
||||||
|
TREND_LINE="Sensor 2 stabil fata de ieri (${S48_S2_AVG} vs ${S24_S2_AVG})"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Time above thresholds (cumulative)
|
||||||
|
TIME_ABOVE_70=$(time_above_threshold 5 70)
|
||||||
|
TIME_ABOVE_75=$(time_above_threshold 5 75)
|
||||||
|
TIME_ABOVE_80=$(time_above_threshold 5 80)
|
||||||
|
|
||||||
|
# Build report
|
||||||
|
REPORT="========================================================
|
||||||
|
KINGSTON SNV3S2000G - RAPORT ZILNIC THERMAL
|
||||||
|
Generat: $(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
Host: $(hostname) ($(hostname -I | awk '{print $1}'))
|
||||||
|
Device: /dev/nvme2 (mounted /mnt/backup)
|
||||||
|
========================================================
|
||||||
|
|
||||||
|
STATUS CURENT: $STATUS_LINE
|
||||||
|
Ultima masuratoare: $LATEST_TS
|
||||||
|
- Sensor 2: ${LATEST_S2}C (cel suspect, pragul critic la 75C)
|
||||||
|
- Composite: ${LATEST_COMP}C
|
||||||
|
- Available Spare: ${LATEST_SPARE}%
|
||||||
|
- Percentage Used: ${LATEST_USED}%
|
||||||
|
- Media Errors: $LATEST_MEDIA
|
||||||
|
- Critical Warning Flag: $LATEST_CRITWARN
|
||||||
|
|
||||||
|
ULTIMELE 24h (Sensor 2)
|
||||||
|
Min: ${S24_S2_MIN}C
|
||||||
|
Max: ${S24_S2_MAX}C
|
||||||
|
Medie: ${S24_S2_AVG}C
|
||||||
|
Probe: ${S24_COUNT} masuratori
|
||||||
|
|
||||||
|
TREND vs IERI
|
||||||
|
$TREND_LINE
|
||||||
|
|
||||||
|
TIMP TOTAL PETRECUT PESTE PRAG (cumulativ din log)
|
||||||
|
>=70C (warning): ${TIME_ABOVE_70} ore
|
||||||
|
>=75C (critical): ${TIME_ABOVE_75} ore
|
||||||
|
>=80C (limit): ${TIME_ABOVE_80} ore
|
||||||
|
|
||||||
|
DIN FIRMWARE-UL SSD-ULUI (cumulativ since power-on)
|
||||||
|
Warning Comp Temperature Time: ${LATEST_WARNTIME} min total
|
||||||
|
Critical Comp Temperature Time: ${LATEST_CRITTIME} min total
|
||||||
|
|
||||||
|
EVENIMENTE ALERTA ULTIMELE 24h
|
||||||
|
$ALERT_EVENTS
|
||||||
|
|
||||||
|
JOBURI BACKUP ULTIMELE 24h
|
||||||
|
$BACKUP_EVENTS
|
||||||
|
|
||||||
|
========================================================
|
||||||
|
INTERPRETARE & ACTIUNE RECOMANDATA
|
||||||
|
========================================================
|
||||||
|
"
|
||||||
|
|
||||||
|
# Recomandare contextuala
|
||||||
|
RECOM=""
|
||||||
|
if [ "$CURRENT_STATE" = "critical" ]; then
|
||||||
|
RECOM="CRITIC: Actioneaza AZI. Probabilitate hang iminent. Fa backup la backup-uri pe alt disc si schimba SSD."
|
||||||
|
elif [ "$CURRENT_STATE" = "warning" ] && [ "$S24_COUNT" -lt 5 ]; then
|
||||||
|
RECOM="WARNING (date insuficiente): Mai putin de 24h de date. Asteapta 24-48h pentru evaluare."
|
||||||
|
elif [ "$CURRENT_STATE" = "warning" ]; then
|
||||||
|
if [ "$S24_S2_MAX" != "N/A" ] && awk "BEGIN { exit !($S24_S2_MAX < 75) }"; then
|
||||||
|
RECOM="WARNING marginal: Sensor 2 osciland intre ${S24_S2_MIN}-${S24_S2_MAX}C, sub pragul critic 75C. Distantarea load-ului ajuta, dar SSD-ul e la limita termica. Continua monitorizarea."
|
||||||
|
else
|
||||||
|
RECOM="WARNING serios: Sensor 2 a atins ${S24_S2_MAX}C in 24h, peste 75C. Daca se repeta in 7 zile -> inlocuire SSD necesara."
|
||||||
|
fi
|
||||||
|
elif [ "$CURRENT_STATE" = "ok" ]; then
|
||||||
|
if [ "$S24_COUNT" -lt 5 ]; then
|
||||||
|
RECOM="OK: Date insuficiente pentru raport complet, dar status curent este normal."
|
||||||
|
else
|
||||||
|
RECOM="OK: Sensor 2 stabilizat sub 70C. Masurile de distantare temporala au functionat."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
RECOM="STATE necunoscut. Verifica manual: smartctl -a /dev/nvme2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPORT="${REPORT} ${RECOM}
|
||||||
|
|
||||||
|
Pentru detalii: tail -50 /var/log/kingston-thermal.csv
|
||||||
|
journalctl -t kingston-thermal --since today
|
||||||
|
========================================================
|
||||||
|
"
|
||||||
|
|
||||||
|
# Output
|
||||||
|
if [ "${1:-}" = "--mail" ]; then
|
||||||
|
SUBJECT="[KINGSTON $STATUS_LINE] Sensor 2 avg ${S24_S2_AVG}C ($(hostname))"
|
||||||
|
if echo "$REPORT" | mail -r "$ALERT_FROM" -s "$SUBJECT" "$ALERT_RECIPIENT" 2>/dev/null; then
|
||||||
|
echo "Raport trimis prin mail la $ALERT_RECIPIENT"
|
||||||
|
else
|
||||||
|
echo "Eroare la trimitere mail (Gmail poate respinge fara DKIM); raportul ramane logat local."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$REPORT"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user