<# .SYNOPSIS Porneste nodurile Proxmox prin Wake-on-LAN. .DESCRIPTION Trimite magic packet catre placile de retea de PRODUCTIE ale nodurilor si asteapta sa apara pe retea. De ce merge abia din 2026-08-29: pana atunci pve1 si pveelite aveau IP-ul principal pe dongle-uri USB Realtek, care nu suporta WoL si oricum pierd alimentarea cand nodul e oprit. Mutarea lui vmbr0 pe interfetele onboard a facut WoL posibil - vezi ../docs/switch-2.5g-dedicat-noduri.md. NU trezi un singur nod si atat, daca ai oprit tot clusterul: un nod pornit singur nu are quorum, /etc/pve ramane read-only si nu poti porni niciun guest. Implicit scriptul le trezeste pe toate trei. RULEAZA DE PE STATIA DE ADMIN, care trebuie sa fie pe acelasi segment L2 (10.0.20.0/24) - magic packet-ul e broadcast si nu trece prin router. .PARAMETER Node Trezeste doar nodul dat. Implicit: toate trei. .PARAMETER Wait Secunde de asteptat sa apara nodurile (implicit 300). .PARAMETER NoWait Trimite pachetele si iese, fara sa astepte. .EXAMPLE .\wake-cluster.ps1 .EXAMPLE .\wake-cluster.ps1 -Node pveelite #> [CmdletBinding()] param( [ValidateSet('pve1','pvemini','pveelite')] [string]$Node, [int]$Wait = 300, [switch]$NoWait ) $ErrorActionPreference = 'Stop' # MAC-ul interfetei de PRODUCTIE (portul puntii vmbr0), nu al dongle-ului de insula. # Citite de pe noduri pe 2026-08-29 cu: ethtool ; cat /sys/class/net//address $Nodes = [ordered]@{ pve1 = @{ Ip = '10.0.20.200'; Mac = 'fc:3f:db:0a:0d:d8'; If = 'eno1' } pvemini = @{ Ip = '10.0.20.201'; Mac = '58:47:ca:7d:51:3c'; If = 'enp90s0' } pveelite = @{ Ip = '10.0.20.202'; Mac = '84:69:93:57:b2:ea'; If = 'eno1' } } function Write-Step { param([string]$Text) Write-Host ""; Write-Host "== $Text" -ForegroundColor Green } function Write-Ok { param([string]$Text) Write-Host " [ok] $Text" -ForegroundColor Green } function Write-Warn { param([string]$Text) Write-Host " [!] $Text" -ForegroundColor Yellow } function Write-Info { param([string]$Text) Write-Host " $Text" -ForegroundColor DarkGray } function Test-Alive { param([string]$Address, [int]$TimeoutMs = 1500) try { $p = New-Object System.Net.NetworkInformation.Ping return ($p.Send($Address, $TimeoutMs).Status -eq 'Success') } catch { return $false } } function Send-MagicPacket { param([string]$Mac) $bytes = ($Mac -split '[:-]') | ForEach-Object { [byte]([Convert]::ToInt32($_, 16)) } if ($bytes.Count -ne 6) { throw "MAC invalid: $Mac" } # Magic packet: 6 octeti 0xFF, apoi MAC-ul repetat de 16 ori. $packet = New-Object byte[] 102 for ($i = 0; $i -lt 6; $i++) { $packet[$i] = 0xFF } for ($r = 0; $r -lt 16; $r++) { [Array]::Copy($bytes, 0, $packet, 6 + $r * 6, 6) } # Trimis si pe broadcast global, si pe cel de subretea: unele stive le trateaza # diferit, iar pachetul e mic - nu costa nimic sa mearga pe ambele. # Porturile 9 si 7 sunt cele uzuale; placile asculta oricum dupa continut. $udp = New-Object System.Net.Sockets.UdpClient try { $udp.EnableBroadcast = $true foreach ($dest in @('255.255.255.255', '10.0.20.255')) { foreach ($port in @(9, 7)) { [void]$udp.Send($packet, $packet.Length, $dest, $port) } } } finally { $udp.Close() } } $targets = if ($Node) { @($Node) } else { $Nodes.Keys } Write-Step "Trimit magic packet" $deja = @() foreach ($n in $targets) { if (Test-Alive $Nodes[$n].Ip) { Write-Ok "$n ($($Nodes[$n].Ip)) - deja pornit, nu trimit" $deja += $n continue } Send-MagicPacket -Mac $Nodes[$n].Mac Write-Ok "$n -> $($Nodes[$n].Mac) ($($Nodes[$n].If))" } $asteptate = @($targets | Where-Object { $deja -notcontains $_ }) if ($asteptate.Count -eq 0) { Write-Warn "Toate nodurile cerute erau deja pornite." exit 0 } if ($NoWait) { Write-Info "-NoWait: nu astept. Verifica singur cu ping." exit 0 } Write-Step "Astept nodurile (max ${Wait}s)" # Retrimitem periodic: daca placa nu era inca alimentata la primul pachet # (alimentare tocmai revenita, de exemplu), al doilea o prinde. $waited = 0 $up = @() while ($waited -lt $Wait) { foreach ($n in $asteptate) { if ($up -contains $n) { continue } if (Test-Alive $Nodes[$n].Ip) { $up += $n Write-Ok "$n a pornit (${waited}s)" } } if ($up.Count -eq $asteptate.Count) { break } Start-Sleep -Seconds 5 $waited += 5 if ($waited % 60 -eq 0) { foreach ($n in $asteptate) { if ($up -notcontains $n) { Send-MagicPacket -Mac $Nodes[$n].Mac } } Write-Info "am retrimis pachetele (${waited}s)" } } $lipsa = @($asteptate | Where-Object { $up -notcontains $_ }) Write-Step "Rezultat" if ($lipsa.Count -eq 0) { Write-Ok "toate nodurile cerute au pornit" Write-Host "" Write-Host " Urmatorul pas: .\cluster-startup.ps1" Write-Host "" } else { Write-Warn "NU au pornit: $($lipsa -join ', ')" Write-Info "Cauza cea mai probabila e in BIOS, nu in retea:" Write-Info " - 'Wake on LAN' / 'Power On by PCI-E' dezactivat" Write-Info " - ErP Ready / Deep Sleep ACTIVAT (taie alimentarea placii in S5)" Write-Info "Verifica si ca statia de admin e pe acelasi segment L2 (10.0.20.0/24)." Write-Info "Pe nodurile pornite, starea WoL se vede cu: ethtool | grep Wake-on" exit 1 }