281 lines
11 KiB
C#
281 lines
11 KiB
C#
namespace ZA.CoreService.ESBCertificateManager.Models;
|
||
|
||
/// <summary>
|
||
/// 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").
|
||
/// </summary>
|
||
public sealed class SonicConnection
|
||
{
|
||
/// <summary>
|
||
/// Alias der Verbindung; referenziert von <see cref="DeploymentTarget.SonicConnectionName"/>.
|
||
/// </summary>
|
||
public required string Name { get; init; }
|
||
|
||
/// <summary>Sonic-Domain, z.B. "proalpha-test" (nicht der Containername).</summary>
|
||
public required string DomainName { get; init; }
|
||
|
||
/// <summary>
|
||
/// Domain-Manager-/Broker-URL, z.B. "tcp://dekun-painwbdet:13070".
|
||
/// </summary>
|
||
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;
|
||
|
||
/// <summary>Sonic-Installationsroot, z.B. "C:\Sonic\MQ10.0".</summary>
|
||
public string SonicHome { get; init; } = @"C:\Sonic\MQ10.0";
|
||
|
||
/// <summary>
|
||
/// Container-Namen (kurz), z.B. ["DE-Test"]. Nicht die Domain.
|
||
/// </summary>
|
||
public List<string> 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;
|
||
|
||
/// <summary>
|
||
/// Echter Neustart mit Prozess-Verifikation (sichtbar in SMC):
|
||
/// alte PIDs müssen weg, neue PIDs müssen erscheinen – sonst Fehler.
|
||
/// </summary>
|
||
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
|
||
}
|