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
789 lines
32 KiB
PowerShell
789 lines
32 KiB
PowerShell
#Requires -Version 5.1
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Initial database setup for ROA Oracle.
|
|
|
|
.DESCRIPTION
|
|
Performs initial database configuration including:
|
|
- Validates Oracle installation
|
|
- 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.
|
|
|
|
.PARAMETER ServiceName
|
|
Database service name. Auto-detects: ROA for non-CDB, XEPDB1 for Oracle XE CDB.
|
|
|
|
.PARAMETER SystemPassword
|
|
SYSTEM user password. Default: romfastsoft
|
|
|
|
.PARAMETER ContafinPassword
|
|
CONTAFIN_ORACLE user password. Default: ROMFASTSOFT
|
|
|
|
.PARAMETER DatafileDir
|
|
Directory for tablespace datafiles. Auto-detects if not specified.
|
|
|
|
.PARAMETER TablespaceSize
|
|
Initial size of ROA tablespace in MB. Default: 1000
|
|
|
|
.PARAMETER SkipSqlnetConfig
|
|
Skip sqlnet.ora configuration.
|
|
|
|
.PARAMETER ResetSystemPassword
|
|
Reseteaza parola lui SYSTEM in CDB root, cu aceeasi valoare ($SystemPassword).
|
|
Necesar DOAR pe o baza mai veche, unde SYSTEM a apucat sa intre in
|
|
EXPIRED(GRACE) inainte ca profilul din CDB root sa fie corectat (Step 4b):
|
|
statusul nu se curata singur cand profilul devine UNLIMITED, cere un
|
|
ALTER USER. Pe o instalare noua nu e nevoie - contul nu apuca sa expire.
|
|
Implicit OFF, pentru ca schimba o parola.
|
|
|
|
.EXAMPLE
|
|
.\01-setup-database.ps1
|
|
|
|
.EXAMPLE
|
|
.\01-setup-database.ps1 -ServiceName "XEPDB1" -SystemPassword "mypassword"
|
|
|
|
.EXAMPLE
|
|
.\01-setup-database.ps1 -OracleHome "C:\app\oracle\product\21c\dbhomeXE" -DatafileDir "D:\oradata"
|
|
|
|
.NOTES
|
|
File Name : 01-setup-database.ps1
|
|
Prerequisite : Oracle Database installed, PowerShell 5.1+
|
|
Copyright 2024 : ROMFAST
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$OracleHome,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$ServiceName,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$DbHost,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[int]$Port = 1521,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$SystemPassword = "romfastsoft",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$ContafinPassword = "ROMFASTSOFT",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$DatafileDir,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[int]$TablespaceSize = 1000,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$SkipSqlnetConfig,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$ResetSystemPassword,
|
|
|
|
# Calea fizica a directorului Oracle DMPDIR. Aceeasi valoare trebuie data
|
|
# tuturor pasilor care ating DMPDIR (01, 02, 03, 05, 06), altfel ultimul
|
|
# care ruleaza repozitioneaza obiectul si ceilalti citesc din alta parte.
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$DmpDir = "C:\DMPDIR"
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
# Load config.ps1 if exists (provides defaults)
|
|
$configPath = Join-Path (Split-Path $PSScriptRoot -Parent) "config.ps1"
|
|
if (Test-Path $configPath) {
|
|
. $configPath
|
|
# Use config values if parameters not provided
|
|
if (-not $OracleHome -and $ORACLE_HOME) { $OracleHome = $ORACLE_HOME }
|
|
if (-not $ServiceName -and $SERVICE_NAME) { $ServiceName = $SERVICE_NAME }
|
|
if (-not $DbHost -and $DB_HOST) { $DbHost = $DB_HOST }
|
|
if (-not $Port -and $DB_PORT) { $Port = $DB_PORT }
|
|
if ($SystemPassword -eq "romfastsoft" -and $SYSTEM_PASSWORD) { $SystemPassword = $SYSTEM_PASSWORD }
|
|
if ($ContafinPassword -eq "ROMFASTSOFT" -and $CONTAFIN_PASSWORD) { $ContafinPassword = $CONTAFIN_PASSWORD }
|
|
if (-not $DatafileDir -and $DATAFILE_DIR) { $DatafileDir = $DATAFILE_DIR }
|
|
}
|
|
|
|
# Source library functions
|
|
. "$PSScriptRoot\lib\logging-functions.ps1"
|
|
. "$PSScriptRoot\lib\oracle-functions.ps1"
|
|
|
|
# Initialize logging
|
|
$logPath = Join-Path $PSScriptRoot "..\logs\01-setup-database_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
|
|
Initialize-LogFile -LogPath $logPath -ScriptName "01-setup-database.ps1"
|
|
|
|
try {
|
|
Write-LogSection "ROA Database Initial Setup"
|
|
|
|
# Step 1: Validate Oracle installation
|
|
Write-Log "Detecting Oracle installation..."
|
|
|
|
$oraHome = Get-OracleHome -OracleHome $OracleHome
|
|
Write-LogSuccess "Oracle Home: $oraHome"
|
|
|
|
# Step 2: Auto-detect service name if not specified
|
|
if (-not $ServiceName) {
|
|
Write-Log "Auto-detecting service name..."
|
|
|
|
# First try to connect to XE to check CDB status
|
|
try {
|
|
$version = Get-OracleVersion -OracleHome $oraHome -ServiceName "XE" -DbHost $DbHost -Port $Port -Password $SystemPassword
|
|
if ($version.IsCDB) {
|
|
$ServiceName = "XEPDB1"
|
|
Write-Log "Detected CDB installation, using PDB: XEPDB1"
|
|
}
|
|
else {
|
|
$ServiceName = "XE"
|
|
Write-Log "Detected non-CDB installation, using service: XE"
|
|
}
|
|
}
|
|
catch {
|
|
# Try ROA as fallback
|
|
try {
|
|
$null = Test-OracleConnection -OracleHome $oraHome -ServiceName "ROA" -DbHost $DbHost -Port $Port -Password $SystemPassword
|
|
$ServiceName = "ROA"
|
|
Write-Log "Using existing service: ROA"
|
|
}
|
|
catch {
|
|
$ServiceName = "XEPDB1"
|
|
Write-Log "Defaulting to XEPDB1"
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-LogSuccess "Service Name: $ServiceName"
|
|
if ($DbHost) {
|
|
Write-Log "Database Host: ${DbHost}:${Port}"
|
|
}
|
|
|
|
# Step 3: Test connection
|
|
Write-Log "Testing database connection..."
|
|
if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port -Password $SystemPassword)) {
|
|
throw "Cannot connect to database. Please verify ServiceName, Host and SystemPassword."
|
|
}
|
|
Write-LogSuccess "Database connection successful"
|
|
|
|
# Get version info
|
|
$versionInfo = Get-OracleVersion -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port -Password $SystemPassword
|
|
Write-Log "Oracle Version: $($versionInfo.Version)"
|
|
Write-Log "Oracle Edition: $($versionInfo.Edition)"
|
|
Write-Log "CDB Mode: $($versionInfo.IsCDB)"
|
|
|
|
# Check container info
|
|
$containerInfo = Test-PDB -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port -Password $SystemPassword
|
|
Write-Log "Container: $($containerInfo.ContainerName)"
|
|
if ($containerInfo.IsPDB) {
|
|
Write-Log "Connected to PDB (Pluggable Database)"
|
|
}
|
|
elseif ($containerInfo.IsNonCDB) {
|
|
Write-Log "Connected to non-CDB database"
|
|
}
|
|
|
|
# Step 4: Configure DEFAULT_PROFILE (no password expiry)
|
|
Write-LogSection "Configuring DEFAULT_PROFILE"
|
|
|
|
$profileSql = @"
|
|
SET ECHO OFF FEEDBACK ON VERIFY OFF
|
|
PROMPT Configuring DEFAULT_PROFILE - disabling password expiration...
|
|
ALTER PROFILE DEFAULT LIMIT
|
|
PASSWORD_LIFE_TIME UNLIMITED
|
|
PASSWORD_GRACE_TIME UNLIMITED
|
|
PASSWORD_REUSE_TIME UNLIMITED
|
|
PASSWORD_REUSE_MAX UNLIMITED
|
|
FAILED_LOGIN_ATTEMPTS UNLIMITED
|
|
PASSWORD_LOCK_TIME UNLIMITED;
|
|
COMMIT;
|
|
SELECT 'PROFILE_CONFIGURED' FROM dual;
|
|
EXIT;
|
|
"@
|
|
|
|
$result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
|
-Username "SYS" -Password $SystemPassword -SqlCommand $profileSql -AsSysdba
|
|
|
|
if ($result -match "PROFILE_CONFIGURED") {
|
|
Write-LogSuccess "DEFAULT_PROFILE configured successfully"
|
|
}
|
|
else {
|
|
Write-LogWarning "Could not verify profile configuration"
|
|
Write-LogDebug $result
|
|
}
|
|
|
|
# Step 4b: Acelasi profil si in CDB root
|
|
# ALTER PROFILE dintr-un PDB NU acopera CDB root: acolo DEFAULT ramane cu
|
|
# PASSWORD_LIFE_TIME 180, deci SYSTEM din root expira la ~6 luni de la instalare
|
|
# (ORA-28002, apoi ORA-28001). Verificat pe VM 302: SYSTEM era EXPIRED(GRACE).
|
|
# Se comuta pe CDB$ROOT ca SYSDBA si se altereaza profilul local de acolo.
|
|
if ($containerInfo.IsPDB) {
|
|
Write-Log "Applying DEFAULT_PROFILE in CDB root as well..."
|
|
|
|
$profileCdbSql = @"
|
|
SET ECHO OFF FEEDBACK ON VERIFY OFF
|
|
ALTER SESSION SET CONTAINER = CDB`$ROOT;
|
|
ALTER PROFILE DEFAULT LIMIT
|
|
PASSWORD_LIFE_TIME UNLIMITED
|
|
PASSWORD_GRACE_TIME UNLIMITED
|
|
PASSWORD_REUSE_TIME UNLIMITED
|
|
PASSWORD_REUSE_MAX UNLIMITED
|
|
FAILED_LOGIN_ATTEMPTS UNLIMITED
|
|
PASSWORD_LOCK_TIME UNLIMITED;
|
|
SELECT 'PROFILE_CDB_CONFIGURED' FROM dual;
|
|
EXIT;
|
|
"@
|
|
|
|
$resultCdb = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
|
-Username "SYS" -Password $SystemPassword -SqlCommand $profileCdbSql -AsSysdba
|
|
|
|
if ($resultCdb -match "PROFILE_CDB_CONFIGURED" -and $resultCdb -notmatch "ORA-") {
|
|
Write-LogSuccess "DEFAULT_PROFILE configured in CDB root"
|
|
}
|
|
else {
|
|
Write-LogWarning "Could not configure DEFAULT_PROFILE in CDB root"
|
|
Write-LogWarning "SYSTEM din CDB root va expira la ~180 de zile. Manual:"
|
|
Write-LogWarning " ALTER SESSION SET CONTAINER=CDB`$ROOT;"
|
|
Write-LogWarning " ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED CONTAINER=ALL;"
|
|
Write-LogDebug $resultCdb
|
|
}
|
|
|
|
# Step 4c (optional): curata EXPIRED(GRACE) pe SYSTEM din CDB root.
|
|
# ALTER PROFILE opreste expirarile viitoare, dar NU reseteaza statusul
|
|
# unui cont deja intrat in perioada de gratie - asta cere un ALTER USER.
|
|
# Opt-in, fiindca scrie o parola.
|
|
if ($ResetSystemPassword) {
|
|
Write-Log "Resetting SYSTEM password in CDB root (clears EXPIRED(GRACE))..."
|
|
|
|
$resetSql = @"
|
|
SET ECHO OFF FEEDBACK ON VERIFY OFF
|
|
ALTER SESSION SET CONTAINER = CDB`$ROOT;
|
|
ALTER USER SYSTEM IDENTIFIED BY "$SystemPassword";
|
|
SELECT 'SYSTEM_STATUS:' || account_status AS rez FROM dba_users WHERE username = 'SYSTEM';
|
|
EXIT;
|
|
"@
|
|
|
|
$resetResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
|
-Username "SYS" -Password $SystemPassword -SqlCommand $resetSql -AsSysdba
|
|
|
|
if ($resetResult -match "SYSTEM_STATUS:OPEN" -and $resetResult -notmatch "ORA-") {
|
|
Write-LogSuccess "SYSTEM in CDB root: OPEN"
|
|
}
|
|
else {
|
|
Write-LogWarning "Nu am putut confirma resetarea lui SYSTEM in CDB root. Manual:"
|
|
Write-LogWarning " ALTER SESSION SET CONTAINER=CDB`$ROOT;"
|
|
Write-LogWarning " ALTER USER SYSTEM IDENTIFIED BY <parola>;"
|
|
Write-LogDebug $resetResult
|
|
}
|
|
}
|
|
}
|
|
|
|
# Step 5: Configure sqlnet.ora for old client compatibility
|
|
if (-not $SkipSqlnetConfig) {
|
|
Write-LogSection "Configuring sqlnet.ora"
|
|
|
|
# Oracle 21c uses read-only Oracle Home - config goes to 'homes' directory
|
|
# Check for Oracle 21c homes directory structure first
|
|
$oraBase = Split-Path (Split-Path (Split-Path $oraHome -Parent) -Parent) -Parent
|
|
$homesDir = Join-Path $oraBase "product\21c\homes"
|
|
|
|
# Find the actual Oracle Base Home (where user configs go)
|
|
$networkAdmin = $null
|
|
if (Test-Path $homesDir) {
|
|
# Oracle 21c with read-only Oracle Home
|
|
$oraBaseHome = Get-ChildItem -Path $homesDir -Directory |
|
|
Where-Object { $_.Name -match "OraDB" } |
|
|
Select-Object -First 1
|
|
if ($oraBaseHome) {
|
|
$networkAdmin = Join-Path $oraBaseHome.FullName "network\admin"
|
|
Write-Log "Detected Oracle 21c read-only Home structure"
|
|
Write-Log "Using Oracle Base Home: $($oraBaseHome.FullName)"
|
|
}
|
|
}
|
|
|
|
# Fallback to traditional location
|
|
if (-not $networkAdmin -or -not (Test-Path (Split-Path $networkAdmin -Parent))) {
|
|
$networkAdmin = Join-Path $oraHome "network\admin"
|
|
Write-Log "Using traditional Oracle Home network/admin location"
|
|
}
|
|
|
|
$sqlnetOra = Join-Path $networkAdmin "sqlnet.ora"
|
|
|
|
if (-not (Test-Path -Path $networkAdmin)) {
|
|
New-Item -ItemType Directory -Path $networkAdmin -Force | Out-Null
|
|
}
|
|
|
|
# Backup existing sqlnet.ora
|
|
if (Test-Path -Path $sqlnetOra) {
|
|
$backupPath = "$sqlnetOra.bak_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
|
Copy-Item -Path $sqlnetOra -Destination $backupPath -Force
|
|
Write-Log "Backed up existing sqlnet.ora to $backupPath"
|
|
}
|
|
|
|
# Create sqlnet.ora with compatibility settings
|
|
$sqlnetContent = @"
|
|
# sqlnet.ora - ROA Oracle Configuration
|
|
# Generated by 01-setup-database.ps1 on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
|
|
|
|
# Allow connections from old Oracle Instant Client 10g/11g (ODBC drivers)
|
|
# These clients don't support newer authentication methods
|
|
SQLNET.ALLOWED_LOGON_VERSION_SERVER=8
|
|
SQLNET.ALLOWED_LOGON_VERSION_CLIENT=8
|
|
|
|
# Disable encryption requirement for old clients
|
|
SQLNET.ENCRYPTION_SERVER = ACCEPTED
|
|
SQLNET.CRYPTO_CHECKSUM_SERVER = ACCEPTED
|
|
|
|
# Timp maxim pentru finalizarea handshake-ului de conectare (setare de server).
|
|
# Nu limiteaza durata operatiilor de dupa conectare.
|
|
SQLNET.INBOUND_CONNECT_TIMEOUT = 60
|
|
|
|
# ATENTIE: NU pune aici SQLNET.RECV_TIMEOUT / SQLNET.SEND_TIMEOUT.
|
|
# Fisierul asta e citit si de clientii de pe aceeasi masina (sqlplus, impdp,
|
|
# expdp). RECV_TIMEOUT limiteaza cat asteapta CLIENTUL un pachet de raspuns,
|
|
# iar in timpul unui import Data Pump serverul poate lucra minute intregi fara
|
|
# sa trimita nimic (creare de indecsi, constrangeri). Cu RECV_TIMEOUT = 30,
|
|
# clientul impdp moare cu ORA-12609 "TNS: Receive timeout occurred", desi jobul
|
|
# de pe server continua si se termina cu bine. Rezultatul e si mai perfid decat
|
|
# un esec curat: 03-import-contafin.ps1 numara obiectele imediat dupa moartea
|
|
# clientului, gaseste 0 si raporteaza esec pentru un import care a reusit.
|
|
# Verificat pe VM 302 (2026-08-25).
|
|
|
|
# TNS Names directory
|
|
NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT)
|
|
"@
|
|
|
|
Set-Content -Path $sqlnetOra -Value $sqlnetContent -Encoding ASCII
|
|
Write-LogSuccess "sqlnet.ora configured at $sqlnetOra"
|
|
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 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('\'))
|
|
if ($tnsAdminFull -ne [IO.Path]::GetFullPath($networkAdmin.TrimEnd('\'))) {
|
|
Write-LogWarning "TNS_ADMIN de masina arata spre $tnsAdminFull - acolo se citeste sqlnet.ora, nu din Oracle Home."
|
|
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_$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"
|
|
}
|
|
}
|
|
|
|
# Restart listener to apply changes
|
|
Write-Log "Restarting listener to apply sqlnet.ora changes..."
|
|
$lsnrctl = Join-Path $oraHome "bin\lsnrctl.exe"
|
|
if (Test-Path $lsnrctl) {
|
|
try {
|
|
& $lsnrctl stop 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
$lsnrOutput = & $lsnrctl start 2>&1
|
|
Write-LogSuccess "Listener restarted successfully"
|
|
|
|
# Wait for services to register (can take 30-60 seconds)
|
|
Write-Log "Waiting for database services to register with listener..."
|
|
$maxWait = 60
|
|
$waited = 0
|
|
$interval = 5
|
|
while ($waited -lt $maxWait) {
|
|
Start-Sleep -Seconds $interval
|
|
$waited += $interval
|
|
# Check if our service is registered
|
|
$lsnrStatus = & $lsnrctl status 2>&1 | Out-String
|
|
if ($lsnrStatus -match $ServiceName) {
|
|
Write-Log "Service $ServiceName registered after ${waited}s"
|
|
break
|
|
}
|
|
Write-Log "Waiting... (${waited}s/${maxWait}s)"
|
|
}
|
|
if ($waited -ge $maxWait) {
|
|
Write-LogWarning "Service may not be fully registered yet. Continuing anyway..."
|
|
}
|
|
}
|
|
catch {
|
|
Write-LogWarning "Could not restart listener: $_"
|
|
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"
|
|
}
|
|
|
|
# Step 6: Get datafile path
|
|
if (-not $DatafileDir) {
|
|
Write-Log "Auto-detecting datafile path..."
|
|
$DatafileDir = Get-DatafilePath -OracleHome $oraHome -ServiceName $ServiceName -Password $SystemPassword
|
|
}
|
|
Write-LogSuccess "Datafile directory: $DatafileDir"
|
|
|
|
# Step 7: Create tablespace ROA
|
|
Write-LogSection "Creating Tablespace ROA"
|
|
|
|
# Check if tablespace already exists
|
|
$checkTsSql = @"
|
|
SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF
|
|
SELECT 'TS_EXISTS' FROM dba_tablespaces WHERE tablespace_name = 'ROA';
|
|
EXIT;
|
|
"@
|
|
|
|
$tsResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
|
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $checkTsSql -Silent
|
|
|
|
if ($tsResult -match "TS_EXISTS") {
|
|
Write-LogWarning "Tablespace ROA already exists, skipping creation"
|
|
}
|
|
else {
|
|
$datafilePath = Join-Path $DatafileDir "TS_ROA.DBF"
|
|
|
|
$createTsSql = @"
|
|
SET ECHO OFF FEEDBACK ON VERIFY OFF
|
|
PROMPT Creating tablespace ROA...
|
|
CREATE SMALLFILE TABLESPACE "ROA"
|
|
DATAFILE '$datafilePath' SIZE ${TablespaceSize}M
|
|
AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED
|
|
LOGGING
|
|
EXTENT MANAGEMENT LOCAL
|
|
SEGMENT SPACE MANAGEMENT AUTO;
|
|
COMMIT;
|
|
SELECT 'TS_CREATED' FROM dual;
|
|
EXIT;
|
|
"@
|
|
|
|
$result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
|
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $createTsSql
|
|
|
|
if ($result -match "TS_CREATED") {
|
|
Write-LogSuccess "Tablespace ROA created successfully"
|
|
Write-Log "Datafile: $datafilePath"
|
|
}
|
|
else {
|
|
Write-LogError "Failed to create tablespace ROA"
|
|
Write-LogDebug $result
|
|
throw "Tablespace creation failed"
|
|
}
|
|
}
|
|
|
|
# Step 8: Create user CONTAFIN_ORACLE
|
|
Write-LogSection "Creating User CONTAFIN_ORACLE"
|
|
|
|
# Check if user already exists
|
|
if (Test-OracleUser -OracleHome $oraHome -ServiceName $ServiceName `
|
|
-Password $SystemPassword -SchemaName "CONTAFIN_ORACLE") {
|
|
Write-LogWarning "User CONTAFIN_ORACLE already exists"
|
|
|
|
# Update password
|
|
$updatePwdSql = @"
|
|
SET ECHO OFF FEEDBACK ON VERIFY OFF
|
|
ALTER USER CONTAFIN_ORACLE IDENTIFIED BY "$ContafinPassword";
|
|
COMMIT;
|
|
SELECT 'PWD_UPDATED' FROM dual;
|
|
EXIT;
|
|
"@
|
|
$result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
|
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $updatePwdSql -Silent
|
|
|
|
if ($result -match "PWD_UPDATED") {
|
|
Write-Log "Password updated for CONTAFIN_ORACLE"
|
|
}
|
|
}
|
|
else {
|
|
$createUserSql = @"
|
|
SET ECHO OFF FEEDBACK ON VERIFY OFF
|
|
|
|
PROMPT Creating user CONTAFIN_ORACLE...
|
|
CREATE USER CONTAFIN_ORACLE
|
|
IDENTIFIED BY "$ContafinPassword"
|
|
DEFAULT TABLESPACE ROA
|
|
TEMPORARY TABLESPACE TEMP
|
|
PROFILE DEFAULT;
|
|
|
|
PROMPT Granting role privileges...
|
|
GRANT CONNECT TO CONTAFIN_ORACLE;
|
|
GRANT RESOURCE TO CONTAFIN_ORACLE;
|
|
|
|
PROMPT Granting system privileges...
|
|
GRANT CREATE ANY CONTEXT TO CONTAFIN_ORACLE;
|
|
GRANT CREATE SESSION TO CONTAFIN_ORACLE;
|
|
GRANT CREATE VIEW TO CONTAFIN_ORACLE;
|
|
GRANT DELETE ANY TABLE TO CONTAFIN_ORACLE;
|
|
GRANT DROP ANY CONTEXT TO CONTAFIN_ORACLE;
|
|
GRANT EXECUTE ANY PROCEDURE TO CONTAFIN_ORACLE;
|
|
GRANT INSERT ANY TABLE TO CONTAFIN_ORACLE;
|
|
GRANT SELECT ANY DICTIONARY TO CONTAFIN_ORACLE;
|
|
GRANT SELECT ANY SEQUENCE TO CONTAFIN_ORACLE;
|
|
GRANT SELECT ANY TABLE TO CONTAFIN_ORACLE;
|
|
GRANT UNLIMITED TABLESPACE TO CONTAFIN_ORACLE;
|
|
GRANT UPDATE ANY TABLE TO CONTAFIN_ORACLE;
|
|
GRANT CREATE PUBLIC SYNONYM TO CONTAFIN_ORACLE;
|
|
GRANT DROP PUBLIC SYNONYM TO CONTAFIN_ORACLE;
|
|
|
|
COMMIT;
|
|
SELECT 'USER_CREATED' FROM dual;
|
|
EXIT;
|
|
"@
|
|
|
|
$result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
|
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $createUserSql
|
|
|
|
if ($result -match "USER_CREATED") {
|
|
Write-LogSuccess "User CONTAFIN_ORACLE created successfully"
|
|
}
|
|
else {
|
|
Write-LogError "Failed to create user CONTAFIN_ORACLE"
|
|
Write-LogDebug $result
|
|
throw "User creation failed"
|
|
}
|
|
}
|
|
|
|
# Verify user privileges
|
|
$verifyUserSql = @"
|
|
SET PAGESIZE 100 FEEDBACK OFF VERIFY OFF HEADING ON ECHO OFF LINESIZE 200
|
|
SELECT privilege FROM dba_sys_privs WHERE grantee = 'CONTAFIN_ORACLE' ORDER BY privilege;
|
|
EXIT;
|
|
"@
|
|
|
|
$privileges = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
|
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $verifyUserSql -Silent
|
|
|
|
Write-Log "CONTAFIN_ORACLE privileges verified"
|
|
|
|
# Step 9: Create DMPDIR directory for Data Pump
|
|
Write-LogSection "Creating Data Pump Directory"
|
|
|
|
if (-not (New-OracleDirectory -OracleHome $oraHome -ServiceName $ServiceName `
|
|
-Password $SystemPassword -DirectoryName "DMPDIR" -DirectoryPath $DmpDir)) {
|
|
throw "Directorul Data Pump DMPDIR ($DmpDir) nu e utilizabil de catre baza. Vezi mesajele de mai sus."
|
|
}
|
|
|
|
Write-LogSuccess "Data Pump directory DMPDIR created: $DmpDir"
|
|
|
|
# Step 10: Reset passwords with 10G verifier (required for old client auth)
|
|
# After sqlnet.ora is configured, passwords must be reset to generate 10G hash
|
|
if (-not $SkipSqlnetConfig) {
|
|
Write-LogSection "Resetting Passwords for Old Client Compatibility"
|
|
Write-Log "Resetting passwords to generate 10G password verifiers..."
|
|
|
|
$resetPwdSql = @"
|
|
SET ECHO OFF FEEDBACK ON VERIFY OFF
|
|
-- Reset CONTAFIN_ORACLE password to generate 10G verifier
|
|
ALTER USER CONTAFIN_ORACLE IDENTIFIED BY "$ContafinPassword";
|
|
-- Verify password versions
|
|
SELECT USERNAME, PASSWORD_VERSIONS FROM DBA_USERS WHERE USERNAME = 'CONTAFIN_ORACLE';
|
|
SELECT 'PWD_RESET_DONE' FROM DUAL;
|
|
EXIT;
|
|
"@
|
|
|
|
$result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port `
|
|
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $resetPwdSql
|
|
|
|
if ($result -match "PWD_RESET_DONE") {
|
|
Write-LogSuccess "Password reset completed - 10G verifier now available"
|
|
if ($result -match "10G") {
|
|
Write-Log "Verified: Password has 10G verifier for old client compatibility"
|
|
}
|
|
}
|
|
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
|
|
Write-LogSection "Setup Complete"
|
|
Write-LogSuccess "Database initial setup completed successfully!"
|
|
Write-Host ""
|
|
Write-Log "Summary:"
|
|
Write-Log " Oracle Home: $oraHome"
|
|
Write-Log " Service Name: $ServiceName"
|
|
Write-Log " Oracle Version: $($versionInfo.Version)"
|
|
Write-Log " Container: $($containerInfo.ContainerName)"
|
|
Write-Log " Tablespace ROA: $DatafileDir\TS_ROA.DBF"
|
|
Write-Log " User: CONTAFIN_ORACLE"
|
|
Write-Log " Data Pump Dir: DMPDIR -> $dmpDir"
|
|
if (-not $SkipSqlnetConfig) {
|
|
Write-Log " Old Client Auth: Enabled (sqlnet.ora + 10G verifier)"
|
|
}
|
|
Write-Host ""
|
|
Write-Log "Next steps:"
|
|
Write-Log " 1. Run 02-create-sys-objects.ps1 to install SYS objects"
|
|
Write-Log " 2. Run 03-import-contafin.ps1 to import CONTAFIN_ORACLE schema"
|
|
|
|
Close-LogFile -Success $true
|
|
exit 0
|
|
}
|
|
catch {
|
|
Write-LogError "Setup failed: $_"
|
|
Write-LogError $_.ScriptStackTrace
|
|
Close-LogFile -Success $false
|
|
exit 1
|
|
}
|