# Monitorizare UPS pe serverul Oracle de producție (10.0.20.36) **Server:** `ROA-CARAPETRU2` / 10.0.20.36 — Windows 11 Pro, Oracle 19c SE2, instanța `ROA`. Acces: `ssh -p 22122 Administrator@10.0.20.36`. **Pe scurt:** ViewPower nu a putut niciodată dialoga cu acest UPS și a fost dezactivat pe 10 august 2026, lăsând serverul fără nicio alertare timp de 17 zile. UPS-ul, în schimb, comunică perfect cu Windows pe canalul HID nativ. Monitorizarea nouă folosește exact acel canal. ViewPower e închis și nu mai pornește. Se citește împreună cu [`incident-2026-08-10-server36-standby.md`](incident-2026-08-10-server36-standby.md) — cele două se leagă în seara de 10 august. --- ## 1. Diagnosticul: ce era stricat, de fapt ### UPS-ul comunică — și a comunicat tot timpul | Verificare | Rezultat (27.08.2026) | |---|---| | `USB\VID_0665&PID_5161\HID_UPS` | Status **OK**, enumerat corect | | `HID\...&MI_01`, driver `HidBatt` | „HID UPS Battery", Status **OK** | | `Win32_Battery` | `HID UPS`, Status `OK`, BatteryStatus `2` (pe rețea), încărcare **100%** | | Kernel-Power ID **105** (schimbare sursă) | perechi AC↔baterie reale: 10 iun, 13 mai, 5 mai, 25 feb 2026 | Perechile de evenimente 105 sunt dovada care închide discuția: la fiecare pană reală Windows a *văzut* trecerea pe baterie și revenirea. Ultima pană: **10 iunie 2026**. Din 10 august (dezactivarea ViewPower) până azi nu a mai fost niciuna, deci nu s-a pierdut niciun eveniment — noroc, nu proiectare. ### ViewPower nu vorbește limba acestui UPS `C:\ViewPower\log\log4j.log` acoperă **6 aug 15:40:37 → 10 aug 22:34:11** (patru treceri peste miezul nopții, numărate în fișier). Din 5787 de linii, **5777 sunt `QPI return(NAK`**. Zero citiri reușite: nicio tensiune, nicio stare de baterie, niciun eveniment electric. `QPI` este comanda de identificare a protocolului Voltronic. UPS-ul răspunde `(NAK` — „comandă nesuportată" — deci ViewPower rămâne blocat în bucla de identificare și nu ajunge niciodată să interogheze starea. Nu e o problemă de port: `GlobalConfig.portName=USB1F0775B7` din `config\ups.properties` corespunde exact interfeței `HID\VID_0665&PID_5161&MI_00\8&1F0775B7&0&0000`. ViewPower e legat de dispozitivul corect; **setul de comenzi e greșit**. UPS-ul vorbește HID Power Device standard — de aceea îl citește Windows fără niciun driver suplimentar — nu dialectul text pe care îl trimite ViewPower. Nu există setare care să repare asta. ### Cronologia serii de 10 august ``` 22:24:12 upsTomcat pornit (o ultimă încercare de repornire) 22:24:31 ups.properties rescris 22:34:11 ultimul QPI return(NAK 22:34:44 System 7040: upsMonitor auto start → disabled 22:34:46 System 7040: upsTomcat demand start → disabled ``` Cineva a încercat să repare, nu a reușit și a dezactivat serviciile. Din acel moment: **zero monitorizare**. Tray-ul care se deschidea și dădea `start ups monitor service error!` încerca, de fapt, să pornească un serviciu pus pe `Disabled` (registry `Start=4`). ### Ce nu exista oricum, chiar și cu ViewPower funcțional - **Alertare: niciuna.** Logul arată `---------emailReceivers.length()-----0`, iar `SmsInfo.mobileNums` e gol. Nimeni nu ar fi aflat de o pană nici dacă softul mergea. - **Oprire curată a Oracle: niciuna.** `Shutdownconfigure.excuteProgram` e gol — nu rula niciun script înainte de shutdown. - Regula proprie „30 min pe baterie → oprire" (`batModeShutdown=true`, `batModeShutdownTime=30`) era inactivă odată cu serviciile. - Vârsta acumulatorilor neurmărită (`GlobalConfig.batteryLifetime=0`). ### Plasa care exista, totuși Planul de alimentare Windows, pe canalul HID care funcționează: | Setare | Valoare | |---|---| | Critical battery level | **20 %** | | Critical battery action | **Shut down** (și pe AC, și pe DC) | | Low battery level / action | 40 % / *Do nothing* | | Reserve battery level | 25 % | Deci serverul s-ar fi oprit singur la 20 %, dar brutal: fără avertisment, fără `shutdown immediate` pe Oracle, fără ca cineva să afle. --- ## 2. Ce s-a pus în loc Un singur script PowerShell care citește **`GetSystemPowerStatus`** — exact API-ul pe care îl folosește Windows pentru propria acțiune de „critical battery". Consecința practică: dacă oprirea automată a Windows-ului ar funcționa, funcționează și scriptul; nu există un al doilea canal care să se strice separat. | Fișier în repo | Rol | |---|---| | `scripts/ups-monitor/ups-monitor.ps1` | monitorul propriu-zis (buclă + moduri de test) | | `scripts/ups-monitor/install-ups-monitor.ps1` | instalare: copiere, `config.json`, ACL, task programat | | `scripts/ups-monitor/test-battery.ps1` | test: simulează „pe baterie" fără scoaterea din priză | | `scripts/ups-monitor/test-comms-loss.ps1` | test: dezactivează temporar bateria HID și verifică alerta de comunicație | | `scripts/ups-monitor/set-praguri.ps1` | schimbă pragurile din `config.json` fără a atinge parola SMTP, și repornește taskul | | `scripts/ups-monitor/trace-baterie.ps1` | înregistrează curba de descărcare într-un CSV (rulează ca task, sub SYSTEM) | | `scripts/ups-monitor/start-trace.ps1` | pornește / oprește (`-Stop`) taskul de înregistrare | | `scripts/ups-monitor/config.example.json` | șablon; **fișierul real nu se comite** (conține parola SMTP) | Ultimele trei sunt uneltele testului de autonomie — se folosesc împreună, vezi secțiunea 3. Pe server: ``` C:\ROMFAST\ups-monitor\ups-monitor.ps1 C:\ROMFAST\ups-monitor\config.json <- parola SMTP; ACL: doar SYSTEM + Administratori C:\ROMFAST\ups-monitor\state.json <- starea între cicluri C:\ROMFAST\ups-monitor\log\ups-monitor-YYYY-MM.log C:\ROMFAST\_deploy\ <- punctul de livrare (de aici rulează instalarea) ``` Task programat **`ROMFAST UPS Monitor`**, rulează ca `NT AUTHORITY\SYSTEM` (cont deja membru `ORA_DBA` pe acest server, deci `sqlplus / as sysdba` merge fără parolă). ### Ce face | Eveniment | Acțiune | |---|---| | Trecere pe baterie | email imediat: **„PANĂ DE CURENT"** | | Revenirea curentului | email cu durata cât s-a stat pe baterie | | **UPS-ul nu mai e văzut de Windows** | email de alertă, repetat la 24 h cât ține defectul | | Comunicația revine | email de confirmare | | Prag de oprire atins | email, apoi oprire curată | ### Pragurile de oprire ``` 10 minute pe baterie SAU încărcare ≤ 35 % (Windows rămâne plasa de siguranță, la 20 %) ``` Se declanșează doar după **3 citiri consecutive** care confirmă starea pe baterie — o citire aberantă nu poate opri serverul. Iar dacă UPS-ul **nu comunică**, scriptul nu ia nicio decizie de oprire; alertează și atât. ### Secvența de oprire 1. email (pleacă **primul** — în pană de curent poate cădea și switch-ul în orice moment) 2. `Stop-Service OracleOraDB19Home1TNSListener` — nu mai intră conexiuni noi 3. `sqlplus / as sysdba` → `shutdown immediate`, cu timeout 180 s 4. `Stop-Service OracleServiceROA` — plasă de siguranță; serviciul e el însuși configurat `ORA_ROA_SHUTDOWNTYPE=immediate`, `ORA_ROA_SHUTDOWN_TIMEOUT=90`, deci și oprirea lui e curată 5. `shutdown /s /f /t 30` > La revenirea curentului serverul **nu pornește singur** decât dacă e activat > *Restore on AC power loss* în BIOS. De verificat separat, la prima intervenție fizică. --- ## 3. Operare ```powershell # starea curentă: UPS, Oracle, task, ultimele linii de log powershell -ExecutionPolicy Bypass -File C:\ROMFAST\ups-monitor\ups-monitor.ps1 -Mode Status # verifică alertarea pe email powershell -ExecutionPolicy Bypass -File C:\ROMFAST\ups-monitor\ups-monitor.ps1 -Mode TestMail # parcurge secvența de oprire în gol — nu oprește nimic powershell -ExecutionPolicy Bypass -File C:\ROMFAST\ups-monitor\ups-monitor.ps1 -Mode TestShutdown ``` ### Ce s-a validat la punerea în funcțiune (27.08.2026) | Test | Rezultat | |---|---| | Citirea stării | `pe rețea`, 100 %, `Win32_Battery: HID UPS, Status=OK` | | `-Mode TestMail` | email primit — `mail.romfast.ro:587`, STARTTLS, `ups@romfast.ro` | | `-Mode TestShutdown` (dry-run) | secvența parcursă integral; Oracle a rămas `Running` | | Pierderea comunicației (`test-comms-loss.ps1`) | dispozitiv oprit 15:24:44 → alertă **15:26:44** (exact 8 cicluri × 15 s) + email; reactivare → „comunicația a revenit" + email; `Win32_Battery` înapoi la 1 instanță | | Taskul rulează ca SYSTEM | proces `ups-monitor.ps1 -Mode Run`, `state.json` actualizat la fiecare ciclu | | **Trecerea reală pe baterie**, de două ori (3 min de la 15:47:45, apoi 15 min de la 16:08:03) | tranziția s-a văzut instantaneu în ambele sensuri, la ambele teste; emailuri de pană și de revenire la fiecare | Toate cele patru ramuri de alertare au emis email. Cele două scoateri din priză n-au măsurat autonomia — în cincisprezece minute gauge-ul a coborât doar 90 % → 80 % — dar au validat semnalul de sursă și au arătat de ce pragul pe procent nu poate fi criteriul principal (secțiunea 6, punctul 1). ### Testul „pe baterie", fără scoaterea din priză Lanțul de protecție are două jumătăți, și se testează separat: | Jumătatea | Cum se validează | |---|---| | UPS → Windows (*chiar raportează trecerea pe baterie?*) | **deja dovedită de istoric**: perechile de evenimente Kernel-Power **105** la panele reale din 10 iun, 13 mai, 5 mai, 25 feb 2026 | | Windows → script → alertă → oprirea Oracle | `test-battery.ps1`, oricând, fără hardware | ```powershell # repetiție în gol - sigură oricând, în producție powershell -ExecutionPolicy Bypass -File C:\ROMFAST\_deploy\test-battery.ps1 -Percent 30 -Minute 3 # doar trecerea pe baterie, fără atingerea pragului de oprire powershell -ExecutionPolicy Bypass -File C:\ROMFAST\_deploy\test-battery.ps1 -Percent 80 -Minute 3 ``` Scriptul scrie `SIMULARE.json` lângă `config.json`; cât există, monitorul folosește valorile din el în locul citirii reale. Două gărzi, ca un fișier uitat să nu oprească serverul pe date inventate: - **`expira` e obligatoriu.** Fără el fișierul e ignorat; la depășire, monitorul îl șterge singur. - **Oprirea e în gol**, dacă nu se cere explicit `"dryRun": false`. Cât e activă simularea, fiecare ciclu scrie în log o linie `WARN SIMULARE ACTIVA`, iar `-Mode Status` o afișează. `test-battery.ps1` șterge fișierul și dacă e întrerupt. **Repetiție reală**, într-o fereastră de mentenanță — Oracle chiar se oprește și serverul chiar se închide, dar UPS-ul rămâne în priză: ```powershell powershell -ExecutionPolicy Bypass -File C:\ROMFAST\_deploy\test-battery.ps1 -ConfirmReal 'DA-OPRESTE-SERVERUL' ``` Asta e singurul mod de a verifica de-adevăratelea cât durează `shutdown immediate` pe baza de producție — informația de care depinde alegerea pragurilor. **Rămâne netestabilă prin simulare** doar autonomia reală a UPS-ului: câte minute ține sub sarcina actuală. Aceea cere scoaterea din priză — procedura e mai jos. ### Testul de autonomie, cu scoaterea din priză Trei unelte, în ordinea asta. Toate rulează **pe server**, prin `ssh -p 22122`. ```powershell # 1. lărgește pragurile, ca monitorul să nu oprească serverul în timpul testului # (scriptul repornește singur taskul - configurația se citește doar la pornirea buclei) powershell -ExecutionPolicy Bypass -File C:\ROMFAST\ups-monitor\set-praguri.ps1 -Minute 60 -Procent 15 # 2. pornește înregistrarea curbei de descărcare powershell -ExecutionPolicy Bypass -File C:\ROMFAST\ups-monitor\start-trace.ps1 -IntervalSecunde 15 # --- abia acum se scoate din priză; minimum 15-20 de minute, ca să se vadă panta --- # 3. după ce s-a băgat înapoi: oprește înregistrarea și restaurează pragurile de producție powershell -ExecutionPolicy Bypass -File C:\ROMFAST\ups-monitor\start-trace.ps1 -Stop powershell -ExecutionPolicy Bypass -File C:\ROMFAST\ups-monitor\set-praguri.ps1 -Minute 10 -Procent 35 ``` Înregistrarea rulează ca **task programat sub SYSTEM**, nu în sesiunea SSH — dacă în pană cade switch-ul și conexiunea moare, măsurătoarea continuă. Datele rămân în `C:\ROMFAST\ups-monitor\log\trace-baterie.csv`; scriptul **adaugă** la fișierul existent, deci CSV-ul unui test anterior se arhivează întâi sub alt nume, altfel se amestecă rulările. Două lucruri de verificat înainte de a scoate ștecherul: pragul de procent lărgit sub 20 % înseamnă că **plasa de siguranță rămâne Windows-ul**, nu scriptul; și e util ca gauge-ul să fie la 100 %, nu abia revenit dintr-un test anterior. **Dezarmare de urgență** (monitorul rămâne pornit, dar nu mai ia nicio decizie): ```powershell New-Item -ItemType File 'C:\ROMFAST\ups-monitor\DEZACTIVAT' ``` Ștergerea fișierului îl rearmează, fără repornire. Există și `"enabled": false` în `config.json`, dar acela cere repornirea taskului. **Reinstalare / schimbare praguri** (de pe stația de administrare): ```powershell scp -P 22122 ups-monitor.ps1 install-ups-monitor.ps1 Administrator@10.0.20.36:'C:/ROMFAST/_deploy/' ssh -p 22122 Administrator@10.0.20.36 "powershell -ExecutionPolicy Bypass -File C:\ROMFAST\_deploy\install-ups-monitor.ps1 -SmtpPassword ... -ShutdownAfterMinutesOnBattery 10 -ShutdownAtBatteryPercent 35" ``` **Emailuri:** de la `ups@romfast.ro` către `marius.mutu@romfast.ro`, prin `mail.romfast.ro:587` (STARTTLS). Același cont SMTP pe care îl folosesc și cele trei noduri Proxmox — credențialele sunt în `/etc/postfix/sasl_passwd` pe noduri. --- ## 4. Ce s-a întâmplat cu ViewPower - procesele `ViewPower.exe`, `upsTray.exe`, `javaw.exe` — **oprite**; - serviciile `upsMonitor` și `upsTomcat` — rămân **Disabled** (erau deja, din 10 august); - **nu există autostart** — nici în `Run` (HKLM/HKCU), nici în folderele Startup; procesele văzute pe 27 august fuseseră pornite manual; - instalarea din `C:\ViewPower` a fost **lăsată pe disc**, ca referință pentru loguri și configurație. Efect secundar util: nu mai ascultă nimic pe **15178** și **8005** — un Tomcat 8.5.51 cu OpenJDK 11.0.7 (2020–2021) nu mai stă pornit degeaba pe un server de producție. Dezinstalarea completă (`C:\ViewPower\Uninstaller.exe`) e opțională. Înainte de ea merită salvat `C:\ViewPower\datas` — baza Derby de ~71 MB cu istoricul softului. --- ## 5. Capcane plătite (ca să nu se redescopere) **Task-urile programate sunt oprite de Windows exact când serverul trece pe baterie.** E comportamentul implicit. Fără `-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries` la `New-ScheduledTaskSettingsSet`, monitorul ar fi fost ucis fix în secunda în care avea treabă. Asta e greșeala care ar fi trecut neobservată până la prima pană reală. **`$PSScriptRoot` este gol în valoarea implicită a unui parametru.** La legarea parametrilor încă nu e populat, deci `[string]$ConfigPath = (Join-Path $PSScriptRoot 'config.json')` crapă cu *„Cannot bind argument to parameter 'Path' because it is an empty string"*. Se rezolvă în corpul scriptului. **`[TimeSpan]::MaxValue` pentru `-RepetitionDuration` e respins**, deși e documentat ca „la nesfârșit": *„The task XML contains a value which is incorrectly formatted or out of range. Duration: P99999999DT23H59M59S"*. Se omite parametrul — asta chiar înseamnă la nesfârșit. **PowerShell 5.1 nu acceptă `if`/`try` ca expresie** într-un literal de hashtable (`@{ x = if (...) {...} }`) sau la atribuire din `try`. Scriptul e scris în consecință; se verifică înainte de livrare cu: ```powershell $e=$null; [System.Management.Automation.PSParser]::Tokenize((Get-Content x.ps1 -Raw), [ref]$e); $e ``` **Ghilimelele simple supraviețuiesc prin `ssh` → `powershell -File`.** `-SmtpPassword '#Ups2020#'` a ajuns în `config.json` ca `'#Ups2020#'`, cu tot cu apostrofuri, iar serverul de mail a răspuns *„SMTP AUTH is required for message submission on port 587"* — adică nu s-a autentificat deloc. Pentru orice valoare cu caractere speciale: se trimite un `.ps1` cu `scp` și se rulează acolo, nu se construiește comanda din ghilimele. **Task Scheduler refuză ocazional interogarea, iar răspunsul nu înseamnă „nu există".** Pe 27.08.2026 la 16:27, `-Mode Status` a raportat `Task programat: NEINREGISTRAT` deși taskul rula — `State=Running`, proces `ups-monitor.ps1 -Mode Run` viu — iar trei rulări imediat următoare au raportat corect. Cauza era în raportare, nu în task: `catch`-ul trata la fel „nu există" și „n-am putut întreba", și îl afișa pe cel mai alarmant. Într-o pană reală mesajul ăla ar trimite pe cineva să reinstaleze un monitor funcțional. Corectat: absența se stabilește prin `$null -eq $task`, iar o interogare eșuată spune `NEDETERMINAT` și arată eroarea. **Fișierul `.ps1` e intenționat fără diacritice.** PowerShell 5.1 citește un `.ps1` fără BOM ca ANSI; diacriticele din literalii de șir ar ajunge corupte în emailuri. Documentația (`.md`) nu are constrângerea asta. --- ## 6. Rămâne de făcut 1. **Testul de autonomie reală.** Pragurile de 10 min / 35 % sunt alese conservator, nu măsurate. Sunt două necunoscute distincte, iar doar una cere scoaterea din priză: - *cât durează oprirea curată* (`shutdown immediate` + închiderea Windows-ului) — se află dintr-o **repetiție reală** cu `test-battery.ps1 -ConfirmReal ...`, într-o fereastră de mentenanță, cu UPS-ul în priză; - *cât ține UPS-ul sub sarcina actuală* — asta chiar cere scoaterea din priză, după procedura din secțiunea 3. Pragul corect e „autonomia măsurată minus durata opririi, cu marjă". Până când ambele sunt măsurate, pragurile rămân o presupunere rezonabilă, nu o garanție. **Ce s-a aflat pe 27.08.2026, din două scoateri din priză** (3 minute, apoi 15 minute): autonomia tot **nu e măsurată** — în cincisprezece minute gauge-ul a coborât doar de la 90 % la 80 %, deci punctul de oprire nici nu s-a apropiat. Singura cifră existentă rămâne estimarea proprie a UPS-ului, ~70 de minute, nevalidată. S-au aflat însă trei lucruri care schimbă alegerea criteriului. **(a) Gauge-ul de procent nu are o pantă pe care să se poată extrapola.** În testul de 15 minute a stat blocat pe loc patru minute, apoi a accelerat de aproape zece ori: | Interval | Pantă | |---|---| | 16:08 → 16:12 (4 min) | **0** puncte/min — imobil la 90 % | | 16:12 → 16:17 | 0,8 puncte/min | | 16:20 → 16:23 | **2** puncte/min | Extrapolarea făcută la minutul 8 dădea „pragul de 35 % vine peste 220 de minute"; cea de la minutul 14 dădea 22 de minute. Aceeași baterie, aceeași sarcină, zece minute distanță. **(b) La revenirea curentului sare haotic — reproductibil, la aceeași valoare.** Ambele teste, independent: ``` test 1: 96 % -> 65 % -> 75 % -> 90 % în ~100 s test 2: 78 % -> 65 % -> 70 % -> 75 % în ~45 s ``` Nicio baterie nu se încarcă 25 de puncte în 80 de secunde; e recalibrare, nu încărcare. **Acest zgomot nu poate opri însă serverul**: tot blocul de decizie a opririi e înăuntrul ramurii `if ($r.OnBattery)` (`ups-monitor.ps1`), iar saltul se produce după revenirea curentului, când ramura nu se mai execută. Pericolul real e opus și e demonstrat de punctul (a): gauge-ul **rămâne optimist** cât timp bateria chiar se descarcă — opt minute și jumătate blocat la 90 % — deci un prag pe procent reacționează prea târziu, nu prea devreme. **(c) `BatteryLifeTime` nu e o măsurătoare, e o numărătoare inversă.** În testul de 15 minute au trecut 901 secunde, iar estimarea a scăzut cu 872 — raport 0,97, verificat și la două puncte intermediare (240/240 s și 871/872 s). UPS-ul fixează o valoare la începutul penei și scade timpul scurs din ea. Nu aduce nicio informație peste cronometrul propriu al scriptului, iar dacă estimarea inițială e greșită, numărătoarea e greșită cu exact aceeași eroare până în secunda în care se stinge. Semnalul de **sursă** (`ACLineStatus`) a fost, în schimb, impecabil în ambele teste — tranzițiile s-au văzut instantaneu în ambele sensuri, iar emailurile au plecat la fiecare. De aici, direcția pentru pragurile definitive: - **timpul pe baterie rămâne criteriul principal**, fiindcă se sprijină pe `ACLineStatus`, singurul semnal care s-a purtat impecabil; - **`shutdownAtRuntimeSeconds` rămâne `0` (dezactivat)** — punctul (c) arată că ar fi doar al doilea nume al aceluiași cronometru; - procentul rămâne plasă secundară grosieră. Nu are rost coborât mult sub 35 %: fiind optimist, nu aberant, el oricum nu declanșează primul. Ce ar schimba concluzia: o măsurătoare dusă până la o descărcare adâncă, unde s-ar vedea dacă gauge-ul se prăbușește brusc spre final. Pe o bază de producție asta nu se face fără fereastră de mentenanță. 2. **Vârsta acumulatorilor.** Necunoscută. Pe cluster există deja criteriul de vârstă în testul lunar (`proxmox/cluster/ups/`); aici nu există nimic echivalent. 3. **BIOS: *Restore on AC power loss*.** De verificat la prima intervenție fizică — altfel după o pană lungă serverul rămâne oprit până vine cineva să apese butonul. 4. **Legătura cu incidentul din 10 august:** stările de somn (S3) sunt încă disponibile pe acest server. O oprire declanșată de UPS ajunge la `shutdown /s`, nu la sleep, deci nu e afectată — dar problema de fond rămâne deschisă în documentul incidentului.