diff --git a/.gitignore b/.gitignore index 6cb6f63..6dd44b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,28 @@ -# Claude Code handoff files -.claude/HANDOFF.md - -# Playwright MCP temporary files (screenshots, snapshots, logs) -.playwright-mcp/ - -# Input/backup files (large DMP files) -input/ - -# IDE files -.idea/ -.vscode/ -*.swp -*.swo - -# Secrets - real cPanel DNS hook config holds an API token (template is committed) -cpanel-dns.config.json -**/cpanel-dns.config.json +# Claude Code handoff files +.claude/HANDOFF.md + +# Playwright MCP temporary files (screenshots, snapshots, logs) +.playwright-mcp/ + +# Input/backup files (large DMP files) +input/ + +# IDE files +.idea/ +.vscode/ +*.swp +*.swo + +# Secrets - real cPanel DNS hook config holds an API token (template is committed) +cpanel-dns.config.json +**/cpanel-dns.config.json # Cygwin/Git-Bash crash dumps *.stackdump # Handoff-uri: stare de lucru, nu documentatie de proiect - raman doar pe disc docs/handoff_*.md +docs/handoff_*.sql # Stare runtime salvata de cluster-shutdown.sh (nu e documentatie) proxmox/cluster/scripts/.cluster-state.txt diff --git a/CLAUDE.md b/CLAUDE.md index 1583288..7fab2d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -50,6 +50,7 @@ input/ # Oracle DMP files for import - **Instalare/migrare Oracle — care director se folosește**: `proxmox/lxc108-oracle/docs/instalare-si-migrare-oracle.md` - **ROA Windows setup scripts (XE/SE 21c)**: `proxmox/lxc108-oracle/roa-windows-setup/README.md` - **Conectarea clienților vechi (Instant Client 10/11) la 21c — ORA-28040 / ORA-01017 / ORA-12514**: `docs/lectii-conectare-client-oracle.md` +- **Actualizarea ROA eșuează tăcut fără aliasul TNS pe server (jobul zice SUCCEEDED, dar nu aplică nimic)**: `docs/lectii-actualizare-roa-alias-tns.md` - **Kit de instalare pentru client (DMP-uri șablon + scripturi, gata de dus la client)**: `proxmox/lxc108-oracle/scripts/build-client-kit.ps1` - **VM 302 test environment for ROA setup**: `proxmox/vm302-oracle-test/README.md` - **Diagnostic spațiu Oracle la clienți (job zilnic + emailuri)**: `docs/diagnostic-spatiu-clienti.md` diff --git a/docs/lectii-actualizare-roa-alias-tns.md b/docs/lectii-actualizare-roa-alias-tns.md new file mode 100644 index 0000000..bcf602b --- /dev/null +++ b/docs/lectii-actualizare-roa-alias-tns.md @@ -0,0 +1,158 @@ +# Actualizarea ROA eșuează tăcut fără aliasul TNS pe server + +**Constatat la VADECO, 2026-08-28.** Pe serverul acela actualizarea ROA **nu aplicase niciodată un +script**, deși jobul raporta succes de la instalare. Aceeași defecțiune apare la orice client +instalat cu kitul înainte de corecția din `01-setup-database.ps1`. + +## Simptomul + +`DBMS_SCHEDULER.RUN_JOB('CONTAFIN_ORACLE.UPDATEROA_ZILNIC')` se termină în ~5-7 secunde și +`dba_scheduler_job_run_details` arată **`SUCCEEDED`, `error#=0`**. Dar: + +- nicio schemă nu primește scripturi noi; +- `CONTAFIN_ORACLE.UPD_ISTORIC` rămâne cu un rând `stare=2` deschis (fără `dataora_end`), care + blochează rularea următoare; +- `SCHEMA.versiune` nu capătă rânduri noi. + +## De ce jobul minte + +`PACK_UPDATE` **nu aplică el scripturile**. El generează `D:\DMPDIR\script_master.sql` și +**lansează un `sqlplus.exe` extern**, apoi se termină. Jobul raportează starea *lansării*, nu a +actualizării. Scriptul generat începe așa: + +```sql +SET AUTOPRINT OFF +WHENEVER SQLERROR EXIT SQL.SQLCODE +SPOOL D:\DMPDIR\script_master.log +CONNECT CONTAFIN_ORACLE/@ROA +``` + +Aliasul (`ROA`) vine din **`CONTAFIN_ORACLE.NOM_FIRME.NUME_SERVER`**. Cu `WHENEVER SQLERROR EXIT`, +dacă aliasul nu se rezolvă scriptul **iese la prima linie** și nu face absolut nimic — tăcut, pentru +că singura urmă e în spool. + +Eșecul e mascat dublu: jobul zice `SUCCEEDED`, iar raportarea pe email e oricum nefuncțională +(`ORA-29279: 503 AUTH command used when not advertised` — vezi `docs/handoff_vadeco-update-roa.md`). + +## De ce nu se rezolvă aliasul + +`sqlplus`-ul lansat de bază **moștenește mediul serviciului Oracle**, nu pe al utilizatorului conectat. +Lanțul, la o instalare nouă: + +1. Instalarea pornește instanța. În acel moment `TNS_ADMIN` **nu există** încă la nivel de mașină. +2. `01-setup-database.ps1` scrie apoi `TNS_ADMIN` (spre `D:\ROA\instantclient_11_2_0_2`) și pune + aliasul `ROA` acolo. +3. Serviciul Oracle rulează în continuare cu mediul de la pasul 1 — **fără `TNS_ADMIN`**. Efectul + variabilei apare abia la primul restart al serviciului. +4. Deci `sqlplus`-ul lansat de bază caută `tnsnames.ora` în **Oracle Home**. +5. Oracle 21c XE folosește **read-only Oracle Home**: fișierul real e în + `...\product\21c\homes\OraDB21Home1\network\admin\`, nu în `dbhomeXE\network\admin\` + (acolo e doar `sqlnet.ora.rooh`). +6. Fișierul acela e generat de Oracle și conține `XE`, `LISTENER_XE`, `ORACLR_CONNECTION_DATA` — + **dar nu `ROA`**. → `ORA-12154`. + +Capcana perfidă: `tnsping ROA` **reușește** dintr-o sesiune interactivă, pentru că sesiunea ta *are* +`TNS_ADMIN`. Nimic nu pare greșit până nu te uiți în spool. + +## Diagnostic în 30 de secunde + +Citește spool-ul — el are adevărul, nu jobul: + +```powershell +Get-Content D:\DMPDIR\script_master.log +``` + +Un eșec arată exact așa (fișier de ~100 de octeți): + +``` +28/08/2026 14:26:59 +ERROR: +ORA-12154: TNS:could not resolve the connect identifier specified +``` + +Reproducere deterministă, în mediul în care rulează baza (fără `TNS_ADMIN`): + +```powershell +Remove-Item Env:TNS_ADMIN -ErrorAction SilentlyContinue +"exit" | & "$oraHome\bin\sqlplus.exe" -L -S "CONTAFIN_ORACLE/@ROA" +``` + +`ORA-12154` ⇒ actualizarea ROA nu a funcționat niciodată pe serverul acela. + +## Reparația + +Aliasul trebuie să existe **și** în `tnsnames.ora` al Oracle Home-ului, nu doar în cel din +`TNS_ADMIN`. Din 2026-08-28, `01-setup-database.ps1` îl scrie în ambele locuri, cu același IP din LAN. + +Manual, pe un server deja instalat: + +```powershell +$f = 'D:\app\Administrator\product\21c\homes\OraDB21Home1\network\admin\tnsnames.ora' +Copy-Item $f "$f.bak_$(Get-Date -Format yyyyMMdd_HHmmss)" +Add-Content -Path $f -Encoding ASCII -Value @" + +ROA = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = )(PORT = 1521)) + (CONNECT_DATA = + (SERVER = DEDICATED) + (SERVICE_NAME = XEPDB1) + ) + ) +"@ +``` + +Nu rescrie fișierul întreg: `ORACLR_CONNECTION_DATA` e folosit de extproc, iar `LISTENER_XE` de +înregistrarea instanței la listener. Adaugă doar aliasul. + +Nu e nevoie de restart — următorul `sqlplus` lansat de bază citește fișierul la conectare. + +### După reparație + +```sql +-- 1. Deblochează rularea rămasă agățată, daca e cazul +exec CONTAFIN_ORACLE.PACK_UPDATE.IncheiereActualizare; +commit; + +-- 2. Rulează +exec DBMS_SCHEDULER.RUN_JOB('CONTAFIN_ORACLE.UPDATEROA_ZILNIC', use_current_session => FALSE); +exec DBMS_SCHEDULER.RUN_JOB('CONTAFIN_ORACLE.UPDATERTVAI_ZILNIC', use_current_session => FALSE); +``` + +## Cum verifici că actualizarea chiar s-a aplicat + +**Nu te uita la statusul jobului.** Dovada reală e în două locuri: + +```powershell +# Spool-ul trebuie sa aiba zeci de linii si zero ORA- +(Select-String -Path D:\DMPDIR\script_master.log -Pattern 'ORA-|ERROR' | Measure-Object).Count +``` + +```sql +-- Fiecare schema de firma trebuie sa aiba scriptul zilei +select max(data_script) from VADECO.versiune; +select count(*) from VADECO.versiune where script_final like '%2026_08_28%'; + +-- Si nicio rulare agatata +select count(*) from CONTAFIN_ORACLE.UPD_ISTORIC where stare = 2; -- trebuie 0 +``` + +> **Atenție la o iluzie:** rândurile din `SCHEMA.versiune` vin **cu dump-ul** la un import. O schemă +> proaspăt importată pare „la zi" fără ca actualizarea să fi rulat vreodată local. Compară cu data +> scriptului efectiv aplicat azi, nu cu `max(data_script)` în absolut. + +## Capcane conexe, plătite deja + +- `UPD_ISTORIC` are coloanele `DATAORA_START` / `DATAORA_END`, nu `DATA_START`. +- `VERSIUNE` are `SCRIPT_FINAL` / `DATA_SCRIPT`, nu `NUME_SCRIPT`. +- `UPD_LOG.EXPLICATIE` e `CLOB` și sparge `to_char()` cu `ORA-22835` peste 4000 de caractere. + Folosește `dbms_lob.substr(explicatie, 180, 1)`. +- Un `stare=2` rămas deschis blochează rularea următoare — de aceea `IncheiereActualizare` e primul + pas după orice eșec. + +## Legături + +- Lecții de conectare a clienților vechi (`ORA-28040` / `ORA-01017` / `ORA-12514`): + `docs/lectii-conectare-client-oracle.md` +- Instalarea și configurarea: `proxmox/lxc108-oracle/roa-windows-setup/README.md` +- SMTP-ul nefuncțional care maschează erorile: `docs/handoff_vadeco-update-roa.md` diff --git a/proxmox/lxc108-oracle/roa-windows-setup/README.md b/proxmox/lxc108-oracle/roa-windows-setup/README.md index 5795530..6499607 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/README.md +++ b/proxmox/lxc108-oracle/roa-windows-setup/README.md @@ -353,6 +353,16 @@ EXEC DBMS_SCHEDULER.ENABLE('CONTAFIN_ORACLE.UPDATEROA_ZILNIC'); EXEC DBMS_SCHEDULER.ENABLE('CONTAFIN_ORACLE.UPDATERTVAI_ZILNIC'); ``` +> **`SUCCEEDED` la jobul de update NU înseamnă că s-a aplicat ceva.** Jobul doar +> lansează un `sqlplus` extern și se termină în ~5 secunde. Dacă aliasul TNS din +> `NOM_FIRME.NUME_SERVER` (de regulă `ROA`) nu se rezolvă **în mediul serviciului +> Oracle**, scriptul generat iese la prima linie cu `ORA-12154` și actualizarea nu +> rulează niciodată — tăcut, pentru că jobul raportează succes, iar emailul de +> eroare nu pleacă. `01-setup-database.ps1` scrie aliasul și în `tnsnames.ora` al +> Oracle Home-ului tocmai pentru asta. Verifică `D:\DMPDIR\script_master.log` și +> `SCHEMA.versiune`, nu statusul jobului. Ghidul complet: +> [`../../../docs/lectii-actualizare-roa-alias-tns.md`](../../../docs/lectii-actualizare-roa-alias-tns.md). + Serverul ROMFAST de update e documentat în [`../../vm201-windows/docs/vm201-roa-update-server.md`](../../vm201-windows/docs/vm201-roa-update-server.md). diff --git a/proxmox/lxc108-oracle/roa-windows-setup/scripts/01-setup-database.ps1 b/proxmox/lxc108-oracle/roa-windows-setup/scripts/01-setup-database.ps1 index c6c9ae7..6a17eb4 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/scripts/01-setup-database.ps1 +++ b/proxmox/lxc108-oracle/roa-windows-setup/scripts/01-setup-database.ps1 @@ -364,6 +364,69 @@ NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT) Write-LogSuccess "sqlnet.ora configured at $sqlnetOra" Write-Log "Old client compatibility enabled (SQLNET.ALLOWED_LOGON_VERSION=8)" + # Gazda pentru aliasurile TNS: IP-ul din LAN, nu localhost, ca acelasi + # fisier sa functioneze si copiat pe statiile de lucru. + $gazdaTns = Get-ServerLanIPv4 + if (-not $gazdaTns) { + $gazdaTns = $env:COMPUTERNAME + Write-LogWarning "Nu am putut determina IP-ul din LAN; folosesc numele masinii ($gazdaTns) in tnsnames.ora." + } + + $roaAliasBlock = @" +# Aliasul ROA - necesar si serverului, nu doar statiilor. Vezi comentariul din +# 01-setup-database.ps1 si docs/lectii-actualizare-roa-alias-tns.md. +ROA = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = $gazdaTns)(PORT = $Port)) + (CONNECT_DATA = + (SERVER = DEDICATED) + (SERVICE_NAME = $ServiceName) + ) + ) +"@ + + # Aliasul ROA trebuie sa existe SI in tnsnames.ora al Oracle Home-ului, + # nu doar in cel din TNS_ADMIN (instantclient). + # + # PACK_UPDATE (actualizarea ROA) genereaza D:\DMPDIR\script_master.sql si + # lanseaza un sqlplus EXTERN care incepe cu + # CONNECT CONTAFIN_ORACLE/...@ + # adica, in practica, @ROA. Scriptul are WHENEVER SQLERROR EXIT, deci + # iese la prima linie daca aliasul nu se rezolva - iar jobul + # UPDATEROA_ZILNIC raporteaza TOTUSI SUCCEEDED, pentru ca el doar + # lanseaza procesul si se termina in ~5 secunde. + # + # sqlplus-ul lansat de baza mosteneste mediul serviciului Oracle. Daca + # instanta a pornit INAINTE ca TNS_ADMIN de masina sa existe - cazul + # normal la o instalare noua, unde variabila o scriem noi mai jos - + # serviciul nu are TNS_ADMIN si cade pe tnsnames.ora de aici. Fara + # aliasul de mai jos: ORA-12154, si actualizarea ROA nu se aplica + # niciodata, complet tacut. + # Constatat la VADECO pe 2026-08-28, dupa migrare: actualizarea nu + # rulase niciodata pe serverul acela. Detalii in + # docs/lectii-actualizare-roa-alias-tns.md. + $homeTnsNames = Join-Path $networkAdmin "tnsnames.ora" + if (Test-Path $homeTnsNames) { + $stampHomeTns = Get-Date -Format 'yyyyMMdd_HHmmss' + Copy-Item $homeTnsNames "$homeTnsNames.bak_$stampHomeTns" -Force + # Scot un eventual alias ROA existent (rulare anterioara, alt IP) si + # il rescriu, ca operatia sa fie idempotenta. Restul fisierului - + # XE, LISTENER_XE, ORACLR_CONNECTION_DATA - ramane neatins: sunt + # generate de Oracle si folosite de extproc si de inregistrarea la + # listener. + # Sterg si comentariile lipite imediat deasupra lui "ROA =" (fara + # rand gol intre): sunt antetul blocului nostru si, altfel, s-ar + # acumula cate o copie la fiecare rulare. Un rand gol opreste + # potrivirea, deci antetul fisierului ramane intact. + $continutTns = [regex]::Replace((Get-Content $homeTnsNames -Raw), + '(?ms)(^[ \t]*#[^\r\n]*\r?\n)*^[ \t]*ROA[ \t]*=.*?(?=^[ \t]*[A-Za-z0-9_\.]+[ \t]*=|\z)', '') + $continutTns = $continutTns.TrimEnd() + "`r`n`r`n" + $roaAliasBlock + } else { + $continutTns = "# tnsnames.ora - generat de 01-setup-database.ps1`r`n`r`n" + $roaAliasBlock + } + Set-Content -Path $homeTnsNames -Value $continutTns -Encoding ASCII + Write-LogSuccess "tnsnames.ora (Oracle Home): alias ROA -> ${gazdaTns}:$Port/$ServiceName" + # Daca exista TNS_ADMIN la nivel de masina, ACELA castiga - si pentru # clienti, si pentru procesele bazei, care mostenesc variabilele de # masina la pornirea serviciului. Instalarea ROAClient il pune sa arate @@ -404,12 +467,8 @@ NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT) # instantclient-ului, aliasul nu se mai rezolva, instanta # nu se inregistreaza la listener si TOTI clientii iau # ORA-12514 - cu baza sus si listenerul pornit. - $gazdaTns = Get-ServerLanIPv4 - if (-not $gazdaTns) { - $gazdaTns = $env:COMPUTERNAME - Write-LogWarning "Nu am putut determina IP-ul din LAN; folosesc numele masinii ($gazdaTns) in tnsnames.ora." - } - + # $gazdaTns e calculat mai sus, o singura data, ca aliasul ROA + # sa fie identic in ambele fisiere. $tnsNames = Join-Path $tnsAdminFull "tnsnames.ora" if (Test-Path $tnsNames) { $aliasuriVechi = @([regex]::Matches((Get-Content $tnsNames -Raw),