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:
@@ -10,8 +10,11 @@
|
||||
- Auto-detects CDB vs non-CDB configuration
|
||||
- Configures DEFAULT_PROFILE (disables password expiry)
|
||||
- Configures sqlnet.ora for Instant Client 10/11 compatibility
|
||||
- Writes sqlnet.ora and tnsnames.ora into the machine TNS_ADMIN as well
|
||||
- Pins LOCAL_LISTENER to a literal address (no tnsnames alias dependency)
|
||||
- Creates tablespace ROA
|
||||
- Creates user CONTAFIN_ORACLE with required privileges
|
||||
- Rewrites the CONTAFIN_ORACLE and SYSTEM passwords so both get a 10G verifier
|
||||
|
||||
.PARAMETER OracleHome
|
||||
Oracle home directory. If not specified, auto-detects from registry or environment.
|
||||
@@ -362,13 +365,18 @@ NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT)
|
||||
Write-Log "Old client compatibility enabled (SQLNET.ALLOWED_LOGON_VERSION=8)"
|
||||
|
||||
# Daca exista TNS_ADMIN la nivel de masina, ACELA castiga - si pentru
|
||||
# clienti, si pentru serviciul Oracle, care mosteneste variabilele de
|
||||
# masina. Instalarea ROAClient il pune sa arate spre instantclient-ul
|
||||
# ei (D:\ROA\instantclient_11_2_0_2), unde de obicei nu exista niciun
|
||||
# sqlnet.ora: atunci serverul cade pe valorile implicite, adica
|
||||
# ALLOWED_LOGON_VERSION_SERVER=12, si clientii vechi primesc ORA-28040
|
||||
# desi fisierul de mai sus a fost scris corect. Scriem acelasi continut
|
||||
# si acolo, altfel toata configurarea de compatibilitate e degeaba.
|
||||
# clienti, si pentru procesele bazei, care mostenesc variabilele de
|
||||
# masina la pornirea serviciului. Instalarea ROAClient il pune sa arate
|
||||
# spre instantclient-ul ei (D:\ROA\instantclient_11_2_0_2), unde de
|
||||
# obicei nu exista niciun sqlnet.ora: atunci serverul cade pe valorile
|
||||
# implicite, adica ALLOWED_LOGON_VERSION_SERVER=12, si clientii vechi
|
||||
# primesc ORA-28040 desi fisierul de mai sus a fost scris corect.
|
||||
# Scriem acelasi continut si acolo, altfel toata configurarea de
|
||||
# compatibilitate e degeaba.
|
||||
# Atentie: efectul apare abia la primul restart al serviciului Oracle -
|
||||
# instanta pornita INAINTE ca TNS_ADMIN sa existe nu il vede. De aceea
|
||||
# instalarea poate parea perfecta si sa cada la primul reboot. Detalii
|
||||
# in docs/lectii-conectare-client-oracle.md.
|
||||
$tnsAdmin = [Environment]::GetEnvironmentVariable('TNS_ADMIN', 'Machine')
|
||||
if ($tnsAdmin) {
|
||||
$tnsAdminFull = [IO.Path]::GetFullPath($tnsAdmin.TrimEnd('\'))
|
||||
@@ -377,12 +385,62 @@ NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT)
|
||||
if (-not (Test-Path $tnsAdminFull)) {
|
||||
New-Item -ItemType Directory -Path $tnsAdminFull -Force | Out-Null
|
||||
}
|
||||
$stampTns = Get-Date -Format 'yyyyMMdd_HHmmss'
|
||||
|
||||
$tnsSqlnet = Join-Path $tnsAdminFull "sqlnet.ora"
|
||||
if (Test-Path $tnsSqlnet) {
|
||||
Copy-Item $tnsSqlnet "$tnsSqlnet.bak_$(Get-Date -Format 'yyyyMMdd_HHmmss')" -Force
|
||||
Copy-Item $tnsSqlnet "$tnsSqlnet.bak_$stampTns" -Force
|
||||
}
|
||||
Set-Content -Path $tnsSqlnet -Value $sqlnetContent -Encoding ASCII
|
||||
Write-LogSuccess "sqlnet.ora scris si in TNS_ADMIN: $tnsSqlnet"
|
||||
|
||||
# tnsnames.ora din TNS_ADMIN - doua motive, amandoua serioase:
|
||||
# 1. ROAClient cauta aici aliasul ROA. Sablonul livrat cu el
|
||||
# arata spre HOST=SERVER_ROA / SID=ROA, care nu exista.
|
||||
# 2. instanta rezolva LOCAL_LISTENER tot de aici dupa un
|
||||
# restart. La Oracle XE, LOCAL_LISTENER e implicit aliasul
|
||||
# LISTENER_XE, definit doar in tnsnames.ora din Oracle
|
||||
# Home. Cand instanta reporneste cu TNS_ADMIN pe folderul
|
||||
# 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."
|
||||
}
|
||||
|
||||
$tnsNames = Join-Path $tnsAdminFull "tnsnames.ora"
|
||||
if (Test-Path $tnsNames) {
|
||||
$aliasuriVechi = @([regex]::Matches((Get-Content $tnsNames -Raw),
|
||||
'(?m)^\s*([A-Za-z0-9_\.]+)\s*=') |
|
||||
ForEach-Object { $_.Groups[1].Value })
|
||||
Copy-Item $tnsNames "$tnsNames.bak_$stampTns" -Force
|
||||
Write-LogWarning "Rescriu $tnsNames (aliasuri existente: $($aliasuriVechi -join ', ')). Copia veche: tnsnames.ora.bak_$stampTns"
|
||||
}
|
||||
|
||||
$tnsContent = @"
|
||||
# tnsnames.ora - generat de 01-setup-database.ps1 pe $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
|
||||
#
|
||||
# HOST e IP-ul serverului din LAN, nu localhost: acelasi fisier trebuie sa
|
||||
# functioneze si copiat pe statiile de lucru.
|
||||
|
||||
ROA =
|
||||
(DESCRIPTION =
|
||||
(ADDRESS = (PROTOCOL = TCP)(HOST = $gazdaTns)(PORT = $Port))
|
||||
(CONNECT_DATA =
|
||||
(SERVER = DEDICATED)
|
||||
(SERVICE_NAME = $ServiceName)
|
||||
)
|
||||
)
|
||||
|
||||
# Folosit de instanta pentru LOCAL_LISTENER. Fara el, o instanta care
|
||||
# porneste cu TNS_ADMIN aici nu se mai inregistreaza la listener.
|
||||
LISTENER_XE =
|
||||
(ADDRESS = (PROTOCOL = TCP)(HOST = $gazdaTns)(PORT = $Port))
|
||||
"@
|
||||
Set-Content -Path $tnsNames -Value $tnsContent -Encoding ASCII
|
||||
Write-LogSuccess "tnsnames.ora scris in TNS_ADMIN: alias ROA -> ${gazdaTns}:$Port/$ServiceName"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,6 +479,60 @@ NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT)
|
||||
Write-Log "Please restart listener manually: lsnrctl stop && lsnrctl start"
|
||||
}
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# LOCAL_LISTENER ca adresa literala, nu ca alias
|
||||
# --------------------------------------------------------------------
|
||||
# La Oracle XE, LOCAL_LISTENER e implicit aliasul LISTENER_XE, care se
|
||||
# rezolva din tnsnames.ora - deci depinde de TNS_ADMIN. Daca cineva
|
||||
# instaleaza ROAClient dupa noi (sau muta folderul instantclient),
|
||||
# aliasul nu se mai rezolva la urmatoarea pornire a instantei, aceasta
|
||||
# nu se mai inregistreaza la listener si toti clientii iau ORA-12514.
|
||||
# Adresa literala e in spfile si nu depinde de niciun fisier de retea.
|
||||
Write-LogSection "LOCAL_LISTENER"
|
||||
|
||||
$gazdaListener = Get-ServerLanIPv4
|
||||
if (-not $gazdaListener) {
|
||||
$gazdaListener = $env:COMPUTERNAME
|
||||
Write-LogWarning "Nu am putut determina IP-ul din LAN; pun $gazdaListener in LOCAL_LISTENER."
|
||||
}
|
||||
|
||||
$numeDbSql = @"
|
||||
SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF
|
||||
SELECT 'DBNAME:' || name FROM v`$database;
|
||||
EXIT;
|
||||
"@
|
||||
$numeDbOut = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
||||
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $numeDbSql -Silent
|
||||
|
||||
# LOCAL_LISTENER e parametru de instanta: intr-un CDB se pune din root,
|
||||
# al carui serviciu poarta numele bazei (XE la Oracle XE).
|
||||
$serviciiDeIncercat = @()
|
||||
if ($numeDbOut -match 'DBNAME:(\S+)') { $serviciiDeIncercat += $Matches[1].Trim() }
|
||||
if ($serviciiDeIncercat -notcontains $ServiceName) { $serviciiDeIncercat += $ServiceName }
|
||||
|
||||
$llSql = @"
|
||||
SET FEEDBACK OFF VERIFY OFF HEADING OFF PAGESIZE 0
|
||||
ALTER SYSTEM SET LOCAL_LISTENER='(ADDRESS=(PROTOCOL=TCP)(HOST=$gazdaListener)(PORT=$Port))' SCOPE=BOTH;
|
||||
ALTER SYSTEM REGISTER;
|
||||
SELECT 'LL_DONE' FROM DUAL;
|
||||
EXIT;
|
||||
"@
|
||||
$llPus = $false
|
||||
foreach ($srv in $serviciiDeIncercat) {
|
||||
$llOut = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $srv -DbHost $DbHost -Port $Port `
|
||||
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $llSql -Silent
|
||||
if ($llOut -match 'LL_DONE') {
|
||||
Write-LogSuccess "LOCAL_LISTENER = (ADDRESS=(PROTOCOL=TCP)(HOST=$gazdaListener)(PORT=$Port)), pus prin serviciul $srv"
|
||||
$llPus = $true
|
||||
break
|
||||
}
|
||||
Write-LogDebug "LOCAL_LISTENER prin serviciul ${srv}: $llOut"
|
||||
}
|
||||
if (-not $llPus) {
|
||||
Write-LogWarning "Nu am putut seta LOCAL_LISTENER. Ramane pe alias (LISTENER_XE)."
|
||||
Write-LogWarning "Risc: dupa un restart al instantei cu alt TNS_ADMIN, inregistrarea la listener poate esua (ORA-12514)."
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Log "Skipping sqlnet.ora configuration as requested"
|
||||
@@ -602,6 +714,47 @@ EXIT;
|
||||
else {
|
||||
Write-LogWarning "Could not verify password reset"
|
||||
}
|
||||
|
||||
# SYSTEM are nevoie de acelasi tratament, si nu e o rafinare.
|
||||
# Cu SQLNET.ALLOWED_LOGON_VERSION_SERVER=8, serverul autentifica orice
|
||||
# client pre-12c pe baza verificatorului 10G. Un cont care nu are 10G
|
||||
# in PASSWORD_VERSIONS (SYSTEM, dupa o instalare 21c, are doar 11G 12C)
|
||||
# nu se mai poate conecta din Instant Client 10/11 - si eroarea NU e
|
||||
# ORA-28040, ci ORA-01017 "invalid username/password", care trimite pe
|
||||
# o pista gresita ore intregi. Aceeasi parola, doar rescrisa, produce
|
||||
# verificatorul care lipseste.
|
||||
$sysPwdSql = if ($versionInfo.IsCDB) {
|
||||
@"
|
||||
SET ECHO OFF FEEDBACK OFF VERIFY OFF HEADING OFF PAGESIZE 0
|
||||
ALTER USER SYSTEM IDENTIFIED BY "$SystemPassword" CONTAINER=ALL;
|
||||
SELECT 'SYS_PWD_DONE' FROM DUAL;
|
||||
EXIT;
|
||||
"@
|
||||
} else {
|
||||
@"
|
||||
SET ECHO OFF FEEDBACK OFF VERIFY OFF HEADING OFF PAGESIZE 0
|
||||
ALTER USER SYSTEM IDENTIFIED BY "$SystemPassword";
|
||||
SELECT 'SYS_PWD_DONE' FROM DUAL;
|
||||
EXIT;
|
||||
"@
|
||||
}
|
||||
|
||||
# Utilizator comun => se modifica din root, al carui serviciu poarta
|
||||
# numele bazei. Reutilizam lista construita pentru LOCAL_LISTENER.
|
||||
$sysPwdPus = $false
|
||||
foreach ($srv in $serviciiDeIncercat) {
|
||||
$sysOut = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $srv -DbHost $DbHost -Port $Port `
|
||||
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $sysPwdSql -Silent
|
||||
if ($sysOut -match 'SYS_PWD_DONE') {
|
||||
Write-LogSuccess "SYSTEM a primit verificator 10G (parola nu se schimba, doar se rescrie)"
|
||||
$sysPwdPus = $true
|
||||
break
|
||||
}
|
||||
Write-LogDebug "Rescrierea parolei SYSTEM prin serviciul ${srv}: $sysOut"
|
||||
}
|
||||
if (-not $sysPwdPus) {
|
||||
Write-LogWarning "Nu am putut rescrie parola lui SYSTEM. Conturile fara verificator 10G dau ORA-01017 din Instant Client 10/11."
|
||||
}
|
||||
}
|
||||
|
||||
# Summary
|
||||
|
||||
Reference in New Issue
Block a user