fix(oracle): instanta citeste TNS_ADMIN-ul de masina, nu Oracle Home

Continuarea corectiei 3 din ec66377. Aceea rezolva doar jumatatea vizibila a
problemei; jumatatea ascunsa ar fi lovit la primul reboot al serverului, dupa
plecarea de la client. Gasit la VADECO, verificat pe loc.

1. ORA-12514 la toti clientii, cu baza perfect sanatoasa.

   TNS_ADMIN de masina nu decide doar de unde se citeste sqlnet.ora: instanta
   rezolva de acolo si aliasurile din tnsnames.ora. La Oracle XE LOCAL_LISTENER
   e implicit aliasul LISTENER_XE, definit doar in tnsnames.ora din Oracle Home.
   Cand serviciul reporneste dupa ce ROAClient a pus TNS_ADMIN pe folderul
   instantclient-ului, aliasul nu se mai rezolva, instanta nu se mai
   inregistreaza la listener, si atunci: v$instance OPEN, v$pdbs READ WRITE,
   listener pornit pe 0.0.0.0:1521, lsnrctl arata doar CLRExtProc, si absolut
   orice client primeste ORA-12514.

   Capcana e de timp, nu de continut: o instanta pornita inainte ca TNS_ADMIN
   sa existe nu il vede, deci instalarea pare impecabila si cade la primul
   reboot. Reprodus la VADECO cu un simplu Restart-Service OracleServiceXE.

   Pasul 01 scrie acum si tnsnames.ora in TNS_ADMIN (aliasul ROA pentru
   ROAClient - sablonul livrat cu el arata spre HOST=SERVER_ROA, inexistent -
   plus LISTENER_XE pentru instanta) si, mai important, fixeaza LOCAL_LISTENER
   pe adresa literala. Doar asta din urma rezista si daca ROAClient se
   instaleaza dupa kit, sau daca folderul instantclient se muta.

2. ORA-01017 desi parola e corecta.

   Cu SQLNET.ALLOWED_LOGON_VERSION_SERVER=8 serverul autentifica orice client
   pre-12c pe baza verificatorului 10G. Un cont fara 10G in PASSWORD_VERSIONS
   nu se mai poate conecta din Instant Client 10/11, si eroarea nu e ORA-28040,
   ci ORA-01017 - fix cea care trimite pe pista parolei. Dupa o instalare 21c,
   SYSTEM are doar 11G 12C; CONTAFIN_ORACLE scapa doar pentru ca pasul 01 ii
   rescrie parola dupa ce a scris sqlnet.ora. Pasul 01 rescrie acum si parola
   lui SYSTEM, din CDB$ROOT, cu aceeasi valoare.

   Subtilitate: interogat din PDB, PASSWORD_VERSIONS al unui utilizator comun
   ramane cel vechi si dupa reparatie, desi autentificarea foloseste definitia
   din root si functioneaza. Coloana nu e o dovada pentru SYSTEM; conectarea e.

3. Pasul 07 verifica acum ce nu se vede pe hartie.

   Toate cele trei erori trec de verificarile de pana acum: fisierul exista,
   parametrul e setat, contul e OPEN, obiectele sunt valide. Sectiunea noua
   "Conectivitate client vechi (Instant Client)" verifica unde se citeste
   efectiv sqlnet.ora, daca tnsnames.ora din TNS_ADMIN mai e sablonul, daca
   LOCAL_LISTENER e alias sau adresa, ce verificatoare de parola au conturile,
   si - singurul lucru care le prinde pe toate - face o conectare adevarata cu
   Instant Client-ul gasit pe masina. La esec, raportul spune si unde sa te
   uiti. Parametri noi: -ContafinPassword, -InstantClientDir.

4. Get-ServerLanIPv4, in biblioteca.

   Pe serverele la care intram prin Tailscale, un tnsnames.ora cu 100.x nu duce
   nicaieri de pe o statie. Functia alege IP-ul din LAN sarind peste Tailscale
   (100.64.0.0/10) si APIPA, dupa ruta implicita cand sunt mai multe placi.
   Get-ListenerHost o foloseste si el la fallback-ul pe 0.0.0.0, unde inainte
   lua prima adresa nefiltrata.

La VADECO a mai fost nevoie de o corectie manuala, in afara kitului: listener.ora
avea HOST = <nume>.ts.net, deci pornirea listenerului depindea de Tailscale.
Regula generala e in documentatie.

Detaliile, cu dovezi si comenzi de diagnostic: docs/lectii-conectare-client-oracle.md

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SzF1hf4aFS1tmJWpPMiwGp
This commit is contained in:
Marius
2026-08-25 22:14:45 +03:00
parent 4062577e17
commit a5ea8c7ffa
6 changed files with 748 additions and 29 deletions

View File

@@ -17,6 +17,65 @@
# Source logging functions
. "$PSScriptRoot\logging-functions.ps1"
<#
.SYNOPSIS
IP-ul din LAN al serverului - cel pe care il vad statiile de lucru.
.DESCRIPTION
Alege adresa IPv4 a interfetei care are ruta implicita, sarind peste:
- loopback (127.x) si APIPA (169.254.x);
- intervalul Tailscale (100.64.0.0/10). Pe serverele la care intram prin
Tailscale, adresa 100.x exista si e perfect functionala pentru noi, dar
e inutila intr-un tnsnames.ora dus pe statii: statia nu e in tailnet.
Daca nu gaseste nimic potrivit, intoarce sirul gol - apelantul decide.
.OUTPUTS
String. Adresa IPv4 din LAN, sau "" daca nu poate fi determinata.
.EXAMPLE
$ip = Get-ServerLanIPv4
#>
function Get-ServerLanIPv4 {
[CmdletBinding()]
param()
$eTailscale = { param($ip) $ip -match '^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.' }
$adrese = @()
try {
$adrese = @(Get-NetIPAddress -AddressFamily IPv4 -ErrorAction Stop |
Where-Object {
$_.IPAddress -notmatch '^(127\.|169\.254\.)' -and
-not (& $eTailscale $_.IPAddress)
})
}
catch {
# Masini fara modulul NetTCPIP: cadem pe rezolvarea numelui propriu.
$ipuri = @([Net.Dns]::GetHostAddresses([Net.Dns]::GetHostName()) |
Where-Object { $_.AddressFamily -eq 'InterNetwork' } |
ForEach-Object { $_.IPAddressToString } |
Where-Object { $_ -notmatch '^(127\.|169\.254\.)' -and -not (& $eTailscale $_) })
if ($ipuri.Count -gt 0) { return $ipuri[0] }
return ""
}
if ($adrese.Count -eq 0) { return "" }
if ($adrese.Count -eq 1) { return $adrese[0].IPAddress }
# Mai multe placi: o alegem pe cea cu ruta implicita.
try {
$rute = @(Get-NetRoute -DestinationPrefix '0.0.0.0/0' -ErrorAction Stop |
Sort-Object RouteMetric, ifMetric)
foreach ($r in $rute) {
$potrivire = $adrese | Where-Object { $_.InterfaceIndex -eq $r.ifIndex } | Select-Object -First 1
if ($potrivire) { return $potrivire.IPAddress }
}
}
catch { }
return $adrese[0].IPAddress
}
<#
.SYNOPSIS
Get the listener host address.
@@ -64,9 +123,7 @@ function Get-ListenerHost {
# If listener is bound to 0.0.0.0, try to get actual IP
if ($listenerHost -eq "0.0.0.0") {
$ip = (Get-NetIPAddress -AddressFamily IPv4 |
Where-Object { $_.IPAddress -notmatch "^127\." -and $_.PrefixOrigin -ne "WellKnown" } |
Select-Object -First 1).IPAddress
$ip = Get-ServerLanIPv4
if ($ip) { return $ip }
return "localhost"
}