namespace ZA.CoreService.ESBCertificateManager.Models; /// /// Verbindungskonfiguration für eine Progress Sonic ESB Management Instanz. /// Name = Verbindungs-Alias (hier oft gleich ContainerName, z.B. "DE-Test"). /// DomainName = Sonic-Domain (z.B. "proalpha-test"). /// Container wird in KnownContainers / DeploymentTarget.ContainerName geführt (z.B. "DE-Test"). /// public sealed class SonicConnection { /// /// Alias der Verbindung; referenziert von . /// public required string Name { get; init; } /// Sonic-Domain, z.B. "proalpha-test" (nicht der Containername). public required string DomainName { get; init; } /// /// Domain-Manager-/Broker-URL, z.B. "tcp://dekun-painwbdet:13070". /// public required string ConnectionUrl { get; init; } public required string Username { get; init; } public required string Password { get; init; } public SonicManagementMode ManagementMode { get; init; } = SonicManagementMode.WinRm; /// Sonic-Installationsroot, z.B. "C:\Sonic\MQ10.0". public string SonicHome { get; init; } = @"C:\Sonic\MQ10.0"; /// /// Container-Namen (kurz), z.B. ["DE-Test"]. Nicht die Domain. /// public List KnownContainers { get; init; } = []; public int ManagementHttpPort { get; init; } = 8080; public string ApiBasePath { get; init; } = "/api/v1"; public string ContainerListPath { get; init; } = string.Empty; public string ContainerRestartPath { get; init; } = string.Empty; public string ContainerStopPath { get; init; } = string.Empty; public string ContainerStartPath { get; init; } = string.Empty; public int WinRmPort { get; init; } = 5985; public string WinRmRestartScript { get; init; } = string.Empty; public string WinRmContainerListScript { get; init; } = string.Empty; public string WinRmXapiImportScript { get; init; } = string.Empty; public int TimeoutSeconds { get; init; } = 120; public int PostRestartDelaySeconds { get; init; } = 20; public string ResolveRestartScript() => string.IsNullOrWhiteSpace(WinRmRestartScript) ? DefaultRestartScript : WinRmRestartScript; public string ResolveContainerListScript() => string.IsNullOrWhiteSpace(WinRmContainerListScript) ? DefaultContainerListScript : WinRmContainerListScript; /// /// Echter Neustart mit Prozess-Verifikation (sichtbar in SMC): /// alte PIDs müssen weg, neue PIDs müssen erscheinen – sonst Fehler. /// public const string DefaultRestartScript = """ $ErrorActionPreference = 'Stop' $sonicHome = '{sonicHome}' $domain = '{domain}' $container = '{container}' $connectionUrl = '{connectionUrl}' $username = '{username}' $password = '{password}' $bin = Join-Path $sonicHome 'bin' $stopBat = Join-Path $bin 'stopcontainer.bat' $startBat = Join-Path $bin 'startcontainer.bat' $esbAdmin = Join-Path $bin 'esbadmin.bat' # Kurzname = Container (z.B. DE-Test), Full = Domain.Container (z.B. proalpha-test.DE-Test) $shortName = if ($container -like '*.*') { ($container -split '\.', 2)[1] } else { $container } $fullName = if ($container -like '*.*') { $container } else { "$domain.$container" } $markers = @($shortName, $fullName, "$domain.$shortName") | Select-Object -Unique function Get-ContainerPids { $pids = @() Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $cmd = $_.CommandLine if (-not $cmd) { return $false } if ($_.Name -notmatch 'java|javaw|sonic') { return $false } foreach ($m in $markers) { if ($cmd -like "*$m*") { return $true } } return $false } | ForEach-Object { $pids += [int]$_.ProcessId } return @($pids | Select-Object -Unique) } function Wait-PidsGone([int[]]$pids, [int]$seconds) { $deadline = (Get-Date).AddSeconds($seconds) while ((Get-Date) -lt $deadline) { $alive = @($pids | Where-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue }) if ($alive.Count -eq 0) { return $true } Start-Sleep -Seconds 2 } return $false } function Wait-NewPids([int[]]$oldPids, [int]$seconds) { $deadline = (Get-Date).AddSeconds($seconds) while ((Get-Date) -lt $deadline) { $now = @(Get-ContainerPids) $fresh = @($now | Where-Object { $oldPids -notcontains $_ }) if ($fresh.Count -gt 0) { return ,$fresh } Start-Sleep -Seconds 2 } return ,@() } Write-Output "INFO:Domain=$domain Container=$shortName Full=$fullName" $before = @(Get-ContainerPids) Write-Output "INFO:PIDsVorher=$($before -join ',')" if ($before.Count -eq 0) { Write-Output "WARN:Kein laufender Java/Sonic-Prozess für '$shortName' gefunden – Stop ggf. schon offline; starte trotzdem." } # 1) Optional: esbadmin (SMC-kompatibel über Domain Manager) if (Test-Path -LiteralPath $esbAdmin) { Write-Output "INFO:Versuche esbadmin Stop/Start" $scriptText = "connect $domain $connectionUrl $username $password`r`nstop container $shortName`r`nstart container $shortName`r`nexit`r`n" $tmp = [System.IO.Path]::GetTempFileName() + '.txt' Set-Content -LiteralPath $tmp -Value $scriptText -Encoding ASCII try { $p = Start-Process -FilePath 'cmd.exe' -ArgumentList @('/c', "`"$esbAdmin`" < `"$tmp`"") -Wait -PassThru -NoNewWindow Write-Output "INFO:esbadmin ExitCode=$($p.ExitCode)" } finally { Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue } Start-Sleep -Seconds 5 } # 2) stopcontainer.bat if (Test-Path -LiteralPath $stopBat) { foreach ($n in @($fullName, $shortName)) { Write-Output "INFO:stopcontainer $n" $sp = Start-Process -FilePath 'cmd.exe' -ArgumentList @('/c', "`"$stopBat`" `"$n`"") -Wait -PassThru -NoNewWindow Write-Output "INFO:stopcontainer ExitCode=$($sp.ExitCode) Name=$n" } } else { Write-Output "WARN:stopcontainer.bat fehlt: $stopBat" } Start-Sleep -Seconds 3 $still = @(Get-ContainerPids) # 3) Harter Stop der alten PIDs – sonst sieht SMC oft keinen Neustart if ($still.Count -gt 0) { Write-Output "INFO:Force-Stop PIDs=$($still -join ',')" foreach ($procId in $still) { Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue } } if ($before.Count -gt 0) { if (-not (Wait-PidsGone -pids $before -seconds 45)) { throw "Container-Prozess läuft noch nach Stop (PIDs=$($before -join ',')). SMC würde keinen Stop sehen." } Write-Output "INFO:Alte PIDs beendet" } Start-Sleep -Seconds 3 # 4) Start if (-not (Test-Path -LiteralPath $startBat)) { throw "startcontainer.bat nicht gefunden: $startBat (SonicHome prüfen)" } $started = $false foreach ($n in @($fullName, $shortName)) { Write-Output "INFO:startcontainer $n" $st = Start-Process -FilePath 'cmd.exe' -ArgumentList @('/c', "`"$startBat`" `"$n`"") -Wait -PassThru -NoNewWindow Write-Output "INFO:startcontainer ExitCode=$($st.ExitCode) Name=$n" if ($st.ExitCode -eq 0) { $started = $true; break } } if (-not $started) { throw "startcontainer fehlgeschlagen für $fullName / $shortName" } $after = @(Wait-NewPids -oldPids $before -seconds 60) if ($after.Count -eq 0) { # falls Prozess mit gleicher PID-Liste zurückkam: mindestens irgendeinen Treffer verlangen $any = @(Get-ContainerPids) if ($any.Count -eq 0) { throw "Nach Start kein Prozess für Container '$shortName' sichtbar. In SMC prüfen / SonicHome & ContainerName prüfen." } Write-Output "INFO:PIDsNachher=$($any -join ',')" } else { Write-Output "INFO:PIDsNachher=$($after -join ',')" } Write-Output "OK:ContainerRestartVerified Domain=$domain Container=$shortName" """; public const string DefaultContainerListScript = """ $ErrorActionPreference = 'Continue' $sonicHome = '{sonicHome}' $domain = '{domain}' $names = New-Object System.Collections.Generic.List[string] function Add-Name([string]$n) { if ([string]::IsNullOrWhiteSpace($n)) { return } $n = $n.Trim() if (-not $names.Contains($n)) { [void]$names.Add($n) } } if (-not (Test-Path -LiteralPath $sonicHome)) { Write-Output "WARN:SonicHomeNichtGefunden:$sonicHome" } else { Write-Output "INFO:SonicHomeOk:$sonicHome Domain=$domain" Get-ChildItem -LiteralPath $sonicHome -Directory -ErrorAction SilentlyContinue | ForEach-Object { if ($_.Name -like '*.cache') { Add-Name ($_.Name -replace '\.cache$', '') } Get-ChildItem -LiteralPath $_.FullName -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -like '*.cache' } | ForEach-Object { Add-Name ($_.Name -replace '\.cache$', '') } } Get-ChildItem -LiteralPath $sonicHome -Recurse -Filter 'container.xml' -File -ErrorAction SilentlyContinue | Select-Object -First 40 | ForEach-Object { Add-Name (Split-Path $_.DirectoryName -Leaf) } } Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match 'sonic|mf\.framework|container\.xml' } | ForEach-Object { if ($_.CommandLine -match '([A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+)\.cache') { Add-Name $Matches[1] } elseif ($_.CommandLine -match '\\([A-Za-z0-9_\-]+)\\container\.xml') { Add-Name $Matches[1] } } # Kurzform Domain.Container → Container foreach ($n in @($names.ToArray())) { if ($n -like '*.*') { $parts = $n -split '\.', 2 if ($parts.Count -eq 2) { Add-Name $parts[1] } } } if ($names.Count -eq 0) { Write-Output "WARN:KeineContainerGefunden Domain=$domain SonicHome=$sonicHome" } $names | Sort-Object -Unique """; } public enum SonicManagementMode { HttpApi = 0, WinRm = 1, LocalCmd = 2 }