Files
123123/ZA.CoreService.ESBCertificateManager/Models/SonicConnection.cs
T

281 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}