feat(cluster): corosync ring1 pe insula, test de repornire cu WoL, fixuri in scripturi
Ring1 aplicat si verificat pe cluster live: config_version 17, ring1_addr pe
10.10.10.20x la fiecare nod, interface{linknumber:1}. Validat cu `corosync -t`
inainte de instalare. Testat prin oprirea reala a lui ring0 pe pveelite: link0
disconnected, link1 connected, cvorum 3/3 neatins - exact scenariul care pe
27 august a lasat nodul mort 16 ore.
Test de repornire completa a clusterului, cu Wake-on-LAN (trezire in ~15s).
Insula a urcat singura pe toate trei nodurile; ipoteza enumerarii tarzii a
USB-ului nu s-a materializat. Unealta noua: wake-cluster.ps1.
Testul a scos la iveala o linie ramasa in /etc/fstab pe pve1 si pveelite, care
monta storage-ul NFS de pe IP-ul de productie inaintea lui pvestatd. Backup-ul
trecea tacut pe reteaua gresita, cu storage.cfg corect. cluster-startup verifica
acum asta automat, impreuna cu IP-urile de insula si conectivitatea reala.
Patru bug-uri gasite prin rulare pe cluster live:
- sonda Oracle nu avea timeout: un sqlplus agatat pe o instanta in pornire a
blocat cluster-startup 14 minute, fara mesaj, cu propriul prag de 600s
nefolosit, fiindca bucla n-a apucat o iteratie;
- `bash -c` in loc de `bash -lc`: sqlplus lipsea din PATH, deci baza nu se
oprea si containerul s-ar fi inchis peste ea;
- PowerShell 5.1 pierde ghilimelele duble catre exe-uri native, deci comanda
ajungea rupta pe nod - trecut pe trimitere codificata base64;
- backup-ul de crontab si fisierul de stare se rescriau la o a doua rulare,
lasand monitorizarea oprita permanent si lista de repornit goala.
Documentatia de oprire planificata pornea de la o afirmatie devenita falsa
("nu exista datacenter.cfg") si de la o comanda care ar fi adaugat o a doua
linie `ha:`. Actualizata, impreuna cu inventarul de guest-uri (VM 304 lipsea
din toate listele de ordine si cadea in maturarea de dupa Oracle).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJFQZcA7uerXRaL1NrsXpS
This commit is contained in:
@@ -53,6 +53,7 @@ $NodeShutdownOrder = @('pveelite', 'pve1', 'pvemini')
|
||||
# Orice guest pornit care nu apare aici e oprit la final, intr-o maturare.
|
||||
$GuestShutdownOrder = @(
|
||||
303, # Win11-Adina - desktop, fara dependente
|
||||
304, # Win11-Marius - desktop, fara dependente
|
||||
302, # oracle-test-302 - VM de test
|
||||
310, # Win11-Template - template
|
||||
201, # roacentral - IIS reverse proxy, consumator Oracle
|
||||
@@ -110,6 +111,21 @@ function Invoke-Node {
|
||||
return $out
|
||||
}
|
||||
|
||||
# Comenzi cu ghilimele imbricate NU se trimit prin Invoke-Node.
|
||||
#
|
||||
# PowerShell 5.1 pierde ghilimelele duble cand paseaza un argument catre un
|
||||
# executabil nativ: `bash -lc "printf '...' | sqlplus"` ajunge pe nod ca
|
||||
# `bash -lc printf '...' | sqlplus`, adica printf fara format, iar sqlplus rulat
|
||||
# pe NOD, unde nu exista. Esecul e usor de citit gresit ca "lipseste sqlplus".
|
||||
# In loc sa ne luptam cu nivelurile de citare, trimitem scriptul codificat.
|
||||
# Verificat pe 2026-08-29, dupa ce varianta directa a esuat pe cluster live.
|
||||
function Invoke-NodeScript {
|
||||
param([string]$Node, [string]$Script)
|
||||
$lf = $Script -replace "`r`n", "`n"
|
||||
$b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($lf))
|
||||
return Invoke-Node -Node $Node -Command "echo $b64 | base64 -d | bash"
|
||||
}
|
||||
|
||||
# Ca Invoke-Node, dar respecta -DryRun (pentru comenzi care modifica starea).
|
||||
function Invoke-NodeChange {
|
||||
param([string]$Node, [string]$Command)
|
||||
@@ -226,10 +242,22 @@ if ($running.Count -eq 0) {
|
||||
# reporneasca VM-uri oprite intentionat (302, 310, 301...).
|
||||
# Format identic cu varianta bash, ca cele doua sa fie interschimbabile.
|
||||
if (-not $DryRun) {
|
||||
$lines = @("# stare cluster salvata la $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')")
|
||||
foreach ($g in $running) { $lines += "$($g.Type) $($g.Node) $($g.Id) $($g.Status) $($g.Ha)" }
|
||||
Set-Content -Path $StateFile -Value $lines -Encoding utf8
|
||||
Write-Ok "stare salvata in $StateFile"
|
||||
# Nu rescrie peste o stare existenta cu un inventar gol. Cazul real: scriptul
|
||||
# e rulat a doua oara (dupa -NoNodes, sau dupa o eroare), cand guest-urile sunt
|
||||
# deja oprite - inventarul e gol si am pierde exact lista de repornit.
|
||||
$existing = @()
|
||||
if (Test-Path $StateFile) {
|
||||
$existing = @(Get-Content $StateFile | Where-Object { $_ -notmatch '^\s*#' -and $_.Trim() })
|
||||
}
|
||||
if ($running.Count -eq 0 -and $existing.Count -gt 0) {
|
||||
Write-Warn "Niciun guest pornit, dar $StateFile contine deja $($existing.Count) intrari - il PASTREZ."
|
||||
Write-Warn "Altfel cluster-startup n-ar mai sti ce sa porneasca."
|
||||
} else {
|
||||
$lines = @("# stare cluster salvata la $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')")
|
||||
foreach ($g in $running) { $lines += "$($g.Type) $($g.Node) $($g.Id) $($g.Status) $($g.Ha)" }
|
||||
Set-Content -Path $StateFile -Value $lines -Encoding utf8
|
||||
Write-Ok "stare salvata in $StateFile"
|
||||
}
|
||||
}
|
||||
|
||||
Confirm-Step "Se opresc $($running.Count) guest-uri, apoi nodurile: $($NodeShutdownOrder -join ', ')."
|
||||
@@ -246,6 +274,9 @@ if ($currentPolicy -eq 'freeze') {
|
||||
Write-Ok "deja pe freeze"
|
||||
} else {
|
||||
if ($null -ne $currentPolicy -or ($dcCfg | Measure-Object).Count -gt 0) {
|
||||
# sed sterge DOAR linia 'ha:', nu rescrie fisierul: din 2026-08-29 acolo
|
||||
# sta si 'migration: network=10.10.10.0/24,type=insecure', fara de care
|
||||
# replicarea se intoarce tacut pe reteaua de productie.
|
||||
Invoke-NodeChange -Node 'pvemini' -Command 'cp /etc/pve/datacenter.cfg /root/datacenter.cfg.backup-mentenanta' | Out-Null
|
||||
Invoke-NodeChange -Node 'pvemini' -Command "sed -i '/^ha:/d' /etc/pve/datacenter.cfg; printf 'ha: shutdown_policy=freeze\n' >> /etc/pve/datacenter.cfg" | Out-Null
|
||||
} else {
|
||||
@@ -262,7 +293,10 @@ Write-Step "Dezactivare cron-uri de alerta"
|
||||
$sedCron = "crontab -l | sed -E '/^[^#]/ s%^(.*($CronPattern)\.sh.*)`$%#MENTENANTA \1%' | crontab -"
|
||||
|
||||
foreach ($node in $NodeIp.Keys) {
|
||||
Invoke-NodeChange -Node $node -Command "crontab -l > $CronBackup 2>/dev/null || true" | Out-Null
|
||||
# Backup DOAR daca crontab-ul curent nu e deja comentat. Altfel o a doua rulare
|
||||
# a scriptului (dupa o eroare, de exemplu) ar salva peste backup versiunea deja
|
||||
# comentata, iar cluster-startup ar "restaura" monitorizarea oprita - tacut.
|
||||
Invoke-NodeChange -Node $node -Command "if crontab -l 2>/dev/null | grep -q '^#MENTENANTA'; then echo 'backup pastrat'; else crontab -l > $CronBackup 2>/dev/null || true; echo 'backup nou'; fi" | Out-Null
|
||||
Invoke-NodeChange -Node $node -Command $sedCron | Out-Null
|
||||
if (-not $DryRun) {
|
||||
$n = Invoke-Node -Node $node -Command "crontab -l | grep -c '^#MENTENANTA' || true"
|
||||
@@ -285,13 +319,27 @@ if (-not $oracleGuest) {
|
||||
if ($DryRun) {
|
||||
Write-Host " [dry-run] shutdown immediate in $OracleDocker" -ForegroundColor DarkGray
|
||||
} else {
|
||||
$sqlCmd = 'pct exec ' + $OracleCt + ' -- docker exec ' + $OracleDocker + ' bash -c "printf ''shutdown immediate\nexit\n'' | sqlplus -s / as sysdba"'
|
||||
$out = Invoke-Node -Node $oracleGuest.Node -Command $sqlCmd
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Warn "Shutdown-ul bazei a raportat eroare. Verifica manual inainte de a continua!"
|
||||
$out | Select-Object -Last 5 | Write-Host
|
||||
# 'bash -lc', NU 'bash -c': fara shell de login, PATH-ul Oracle nu e incarcat
|
||||
# si comanda esueaza cu "sqlplus: command not found" - adica baza ramane
|
||||
# DESCHISA, iar containerul ar fi oprit peste ea.
|
||||
# '2>&1' se face pe nod, nu in PowerShell: altfel stderr-ul lui ssh devine
|
||||
# ErrorRecord in PS 5.1 si arunca exceptie in loc sa fie tratat aici.
|
||||
# 'timeout 180': un 'shutdown immediate' poate dura, dar nu la infinit. Fara
|
||||
# el, un sqlplus blocat ar tine scriptul agatat fara niciun mesaj (patit pe
|
||||
# 2026-08-29, la sonda echivalenta din cluster-startup).
|
||||
$sqlScript = @"
|
||||
timeout 180 pct exec $OracleCt -- docker exec $OracleDocker bash -lc "printf 'shutdown immediate\nexit\n' | sqlplus -s / as sysdba" 2>&1
|
||||
"@
|
||||
$out = Invoke-NodeScript -Node $oracleGuest.Node -Script $sqlScript
|
||||
|
||||
# Codul de iesire nu e o dovada: sqlplus intoarce 0 si cand n-a inchis nimic.
|
||||
# Singura confirmare reala e mesajul instantei.
|
||||
if (($out -join "`n") -match 'ORACLE instance shut down') {
|
||||
Write-Ok "instanta Oracle oprita curat"
|
||||
} else {
|
||||
Write-Ok "instanta Oracle oprita"
|
||||
Write-Warn "Baza NU a confirmat 'ORACLE instance shut down'. Ultimele linii:"
|
||||
$out | Select-Object -Last 8 | Write-Host
|
||||
Confirm-Step "Oprirea CT $OracleCt peste o baza posibil deschisa. Continui?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user