diff --git a/ZA.CoreService.ESBCertificateManager/Form1.cs b/ZA.CoreService.ESBCertificateManager/Form1.cs
index 54195ad..8b29338 100644
--- a/ZA.CoreService.ESBCertificateManager/Form1.cs
+++ b/ZA.CoreService.ESBCertificateManager/Form1.cs
@@ -1829,23 +1829,41 @@ namespace ZA.CoreService.ESBCertificateManager
return;
}
- // Grid mit entdeckten Containern befüllen
+ // Grid mit entdeckten + konfigurierten Containern befüllen
_loadedTargets = result.DiscoveredTargets;
BindTargetsToGrid(result.DiscoveredTargets);
- int configured = result.DiscoveredTargets.Count(t => !string.IsNullOrWhiteSpace(t.TargetDirectory));
- int discovered = result.RawContainerNames.Count;
+ int total = result.DiscoveredTargets.Count;
+ int remote = result.RawContainerNames.Count;
+ string domain = string.IsNullOrWhiteSpace(result.DomainName)
+ ? selectedConnection
+ : result.DomainName;
- lblSonicStatus.Text = discovered > 0
- ? $"{discovered} Container gefunden, {configured} konfiguriert"
- : $"Verbindung ok – keine Container in Domain '{selectedConnection}'";
- lblSonicStatus.ForeColor = discovered > 0 ? GreenColor : GoldColor;
+ if (total == 0)
+ {
+ lblSonicStatus.Text = $"Verb. ok – 0 Container in Domain '{domain}'";
+ lblSonicStatus.ForeColor = GoldColor;
+ SetStatus(
+ result.ErrorMessage
+ ?? $"Sonic Domain '{domain}': keine Container. KnownContainers/SonicHome in appsettings prüfen.",
+ isError: true);
+ }
+ else if (remote == 0)
+ {
+ lblSonicStatus.Text = $"Verb. ok – Domain '{domain}': 0 remote, {total} aus Config";
+ lblSonicStatus.ForeColor = GoldColor;
+ SetStatus(
+ $"Domain '{domain}': Remote leer – {total} Ziel(e) aus appsettings/KnownContainers. "
+ + (result.ErrorMessage ?? string.Empty),
+ isError: false);
+ }
+ else
+ {
+ lblSonicStatus.Text = $"Domain '{domain}': {remote} remote, {total} gesamt";
+ lblSonicStatus.ForeColor = GreenColor;
+ SetStatus($"Sonic Domain '{domain}': {total} Ziel(e) geladen ({remote} remote).", isError: false);
+ }
- SetStatus(
- $"Sonic {selectedConnection}: {discovered} Container geladen.",
- isError: false);
-
- // Nicht-konfigurierte Ziele visuell markieren
MarkUnconfiguredRows();
}
catch (OperationCanceledException)
diff --git a/ZA.CoreService.ESBCertificateManager/Models/SonicConnection.cs b/ZA.CoreService.ESBCertificateManager/Models/SonicConnection.cs
index 227da4b..8dc20b0 100644
--- a/ZA.CoreService.ESBCertificateManager/Models/SonicConnection.cs
+++ b/ZA.CoreService.ESBCertificateManager/Models/SonicConnection.cs
@@ -39,6 +39,12 @@ public sealed class SonicConnection
///
public string SonicHome { get; init; } = @"C:\Sonic\MQ10.0";
+ ///
+ /// Bekannte Container-Namen dieser Domain (Fallback, wenn Remote-Discovery nichts findet).
+ /// z.B. [ "ESB", "DomainManager" ] oder vollqualifiziert [ "proalpha-test.ESB" ].
+ ///
+ public List KnownContainers { get; init; } = [];
+
// ---------------------------------------------------------------
// HTTP REST API (ManagementMode = HttpApi)
// ---------------------------------------------------------------
@@ -127,21 +133,63 @@ public sealed class SonicConnection
"""
$ErrorActionPreference = 'Continue'
$sonicHome = '{sonicHome}'
- $names = @()
+ $domain = '{domain}'
+ $names = New-Object System.Collections.Generic.List[string]
- # Windows-Dienste mit Sonic/ESB im Namen
- $names += @(Get-Service -ErrorAction SilentlyContinue |
- Where-Object { $_.DisplayName -match 'Sonic|ESB|MQ' -or $_.Name -match 'Sonic|ESB|MQ' } |
- Select-Object -ExpandProperty Name)
-
- # Container-Cache-Ordner unter SonicHome (Domain.Container.cache)
- if (Test-Path -LiteralPath $sonicHome) {
- $names += @(Get-ChildItem -LiteralPath $sonicHome -Directory -ErrorAction SilentlyContinue |
- Where-Object { $_.Name -like '*.cache' } |
- ForEach-Object { $_.Name -replace '\.cache$', '' })
+ function Add-Name([string]$n) {
+ if ([string]::IsNullOrWhiteSpace($n)) { return }
+ $n = $n.Trim()
+ if (-not $names.Contains($n)) { [void]$names.Add($n) }
}
- $names | Where-Object { $_ } | Sort-Object -Unique
+ if (-not (Test-Path -LiteralPath $sonicHome)) {
+ Write-Output "WARN:SonicHomeNichtGefunden:$sonicHome"
+ }
+ else {
+ Write-Output "INFO:SonicHomeOk:$sonicHome"
+
+ # *.cache Ordner (Domain.Container.cache) – 1–2 Ebenen
+ 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$', '') }
+ }
+
+ # container.xml / Containers-Verzeichnisse
+ Get-ChildItem -LiteralPath $sonicHome -Recurse -Filter 'container.xml' -File -ErrorAction SilentlyContinue |
+ Select-Object -First 40 |
+ ForEach-Object {
+ $parent = Split-Path $_.DirectoryName -Leaf
+ Add-Name $parent
+ }
+ }
+
+ # Windows-Dienste
+ Get-Service -ErrorAction SilentlyContinue |
+ Where-Object { $_.DisplayName -match 'Sonic|ESB|MQ|Aurea' -or $_.Name -match 'Sonic|ESB|MQ|Aurea' } |
+ ForEach-Object {
+ Add-Name $_.Name
+ Add-Name $_.DisplayName
+ }
+
+ # Prozess-Hinweise (laufende Container)
+ 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]
+ }
+ }
+
+ if ($names.Count -eq 0 -and -not [string]::IsNullOrWhiteSpace($domain)) {
+ Write-Output "WARN:KeineContainerGefunden Domain=$domain SonicHome=$sonicHome"
+ }
+
+ $names | Sort-Object -Unique
""";
}
diff --git a/ZA.CoreService.ESBCertificateManager/Services/SonicContainerDiscovery.cs b/ZA.CoreService.ESBCertificateManager/Services/SonicContainerDiscovery.cs
index a2e8077..edcdb81 100644
--- a/ZA.CoreService.ESBCertificateManager/Services/SonicContainerDiscovery.cs
+++ b/ZA.CoreService.ESBCertificateManager/Services/SonicContainerDiscovery.cs
@@ -3,8 +3,8 @@ using ZA.CoreService.ESBCertificateManager.Models;
namespace ZA.CoreService.ESBCertificateManager.Services;
///
-/// Fragt alle konfigurierten Sonic-Management-Verbindungen nach ihren Containern
-/// und gleicht die gefundenen Container mit den bereits konfigurierten Deployment-Zielen ab.
+/// Fragt konfigurierte Sonic-Verbindungen nach Containern ab
+/// und merged Remote-Treffer mit KnownContainers / Sample-Zielen.
///
public sealed class SonicContainerDiscovery
{
@@ -18,10 +18,6 @@ public sealed class SonicContainerDiscovery
public bool HasConnections => _connections.Count > 0;
public IReadOnlyList Connections => _connections;
- ///
- /// Verbindet sich mit der angegebenen Sonic-Instanz, liest die Container-Liste
- /// und reichert sie mit konfigurierten Ziel-Metadaten an.
- ///
public async Task DiscoverAsync(
string connectionName,
IReadOnlyList knownTargets,
@@ -45,28 +41,116 @@ public sealed class SonicContainerDiscovery
$"Management-Konsole nicht erreichbar: {pingError}");
}
- (bool listOk, IReadOnlyList containerNames, string? listError) =
+ (bool listOk, IReadOnlyList remoteNames, string? listError) =
await client.GetContainersAsync(cancellationToken);
+ List diagnostics = [];
+ List remoteContainers = [];
+
+ foreach (string line in remoteNames)
+ {
+ if (line.StartsWith("WARN:", StringComparison.OrdinalIgnoreCase)
+ || line.StartsWith("INFO:", StringComparison.OrdinalIgnoreCase))
+ {
+ diagnostics.Add(line);
+ continue;
+ }
+
+ remoteContainers.Add(line);
+ }
+
if (!listOk)
{
- // Fallback: CheckConnection erfolgreich, aber /containers nicht gefunden –
- // dies kann passieren wenn die API-Pfade abweichen. Container-Namen sind dann leer.
- containerNames = [];
+ diagnostics.Add($"Listen-Fehler: {listError}");
+ remoteContainers = [];
}
- List discovered = BuildTargets(connection, containerNames, knownTargets);
+ // Fallback: explizit in appsettings hinterlegte Container
+ List mergedNames = MergeContainerNames(
+ remoteContainers,
+ connection.KnownContainers,
+ knownTargets
+ .Where(t => string.Equals(t.SonicConnectionName, connection.Name, StringComparison.OrdinalIgnoreCase))
+ .Select(t => t.ContainerName)
+ .Where(n => !string.IsNullOrWhiteSpace(n)));
+
+ List discovered = BuildTargets(connection, mergedNames, knownTargets);
+
+ string? hint = BuildHint(connection, remoteContainers.Count, connection.KnownContainers.Count, diagnostics, listOk, listError);
return new SonicDiscoveryResult
{
ConnectionName = connectionName,
+ DomainName = connection.DomainName,
Success = true,
- ErrorMessage = listOk ? null : $"Container-Liste konnte nicht geladen werden: {listError}",
+ ErrorMessage = hint,
DiscoveredTargets = discovered,
- RawContainerNames = containerNames
+ RawContainerNames = remoteContainers,
+ ResolvedPath = resolvedPath
};
}
+ private static List MergeContainerNames(
+ IEnumerable remote,
+ IEnumerable knownConfigured,
+ IEnumerable knownFromTargets)
+ {
+ List result = [];
+
+ foreach (string name in remote.Concat(knownConfigured).Concat(knownFromTargets))
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ continue;
+ }
+
+ if (!result.Any(existing => ContainerNamesMatch(existing, name, domain: null)))
+ {
+ result.Add(name.Trim());
+ }
+ }
+
+ return result;
+ }
+
+ private static string? BuildHint(
+ SonicConnection connection,
+ int remoteCount,
+ int knownConfigCount,
+ List diagnostics,
+ bool listOk,
+ string? listError)
+ {
+ List parts = [];
+
+ if (!listOk && !string.IsNullOrWhiteSpace(listError))
+ {
+ parts.Add(listError!);
+ }
+
+ if (remoteCount == 0)
+ {
+ parts.Add(
+ $"Remote hat 0 Container geliefert (Domain '{connection.DomainName}', SonicHome='{connection.SonicHome}').");
+
+ if (knownConfigCount > 0)
+ {
+ parts.Add($"Fallback: {knownConfigCount} KnownContainers aus appsettings.");
+ }
+ else
+ {
+ parts.Add("Tipp: KnownContainers in appsettings setzen oder SonicHome korrigieren.");
+ }
+ }
+
+ foreach (string d in diagnostics.Take(3))
+ {
+ parts.Add(d);
+ }
+
+ return parts.Count == 0 ? null : string.Join(" ", parts);
+ }
+
private static List BuildTargets(
SonicConnection connection,
IReadOnlyList containerNames,
@@ -77,19 +161,19 @@ public sealed class SonicContainerDiscovery
foreach (string containerName in containerNames)
{
- // Bekanntes, voll-konfiguriertes Ziel suchen (nach ContainerName + SonicConnectionName)
DeploymentTarget? existing = knownTargets.FirstOrDefault(t =>
- string.Equals(t.ContainerName, containerName, StringComparison.OrdinalIgnoreCase) &&
- string.Equals(t.SonicConnectionName, connection.Name, StringComparison.OrdinalIgnoreCase));
+ string.Equals(t.SonicConnectionName, connection.Name, StringComparison.OrdinalIgnoreCase)
+ && ContainerNamesMatch(t.ContainerName, containerName, connection.DomainName));
if (existing is not null)
{
- result.Add(existing);
+ if (!result.Any(r => r.Id == existing.Id))
+ {
+ result.Add(existing);
+ }
}
else
{
- // Minimales Ziel aus der Discovery erzeugen.
- // TargetDirectory/CertificateFileName sind leer → Preflight-Validator zeigt Warnung.
result.Add(new DeploymentTarget
{
Id = syntheticId--,
@@ -106,15 +190,16 @@ public sealed class SonicContainerDiscovery
}
}
- // Konfigurierte Ziele dieser Verbindung, die NICHT in der Discovery aufgetaucht sind,
- // trotzdem anzeigen (könnten offline / gestoppt sein).
foreach (DeploymentTarget known in knownTargets)
{
if (!string.Equals(known.SonicConnectionName, connection.Name, StringComparison.OrdinalIgnoreCase))
+ {
continue;
+ }
bool alreadyAdded = result.Any(r =>
- string.Equals(r.ContainerName, known.ContainerName, StringComparison.OrdinalIgnoreCase));
+ ContainerNamesMatch(r.ContainerName, known.ContainerName, connection.DomainName)
+ || r.Id == known.Id);
if (!alreadyAdded)
{
@@ -124,13 +209,44 @@ public sealed class SonicContainerDiscovery
return result;
}
+
+ internal static bool ContainerNamesMatch(string? a, string? b, string? domain)
+ {
+ if (string.IsNullOrWhiteSpace(a) || string.IsNullOrWhiteSpace(b))
+ {
+ return false;
+ }
+
+ if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ string aShort = StripDomainPrefix(a, domain);
+ string bShort = StripDomainPrefix(b, domain);
+ return string.Equals(aShort, bShort, StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static string StripDomainPrefix(string name, string? domain)
+ {
+ if (!string.IsNullOrWhiteSpace(domain)
+ && name.StartsWith(domain + ".", StringComparison.OrdinalIgnoreCase))
+ {
+ return name[(domain.Length + 1)..];
+ }
+
+ int dot = name.IndexOf('.');
+ return dot > 0 ? name[(dot + 1)..] : name;
+ }
}
public sealed class SonicDiscoveryResult
{
public required string ConnectionName { get; init; }
+ public string DomainName { get; init; } = string.Empty;
public bool Success { get; init; }
public string? ErrorMessage { get; init; }
+ public string? ResolvedPath { get; init; }
public IReadOnlyList DiscoveredTargets { get; init; } = [];
public IReadOnlyList RawContainerNames { get; init; } = [];
diff --git a/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs b/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs
index 717b34b..23e0f2d 100644
--- a/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs
+++ b/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs
@@ -185,9 +185,11 @@ public sealed class SonicManagementClient : IDisposable
List names = (output ?? string.Empty)
.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
- .Where(l => l.Length > 0 && !l.StartsWith("WARNING", StringComparison.OrdinalIgnoreCase))
+ .Where(l => l.Length > 0)
+ .Where(l => !l.StartsWith("WARNING:", StringComparison.OrdinalIgnoreCase))
.ToList();
+ // INFO:/WARN:-Zeilen aus dem Script bewusst durchreichen (Diagnose in Discovery)
return (true, names, null);
}
diff --git a/ZA.CoreService.ESBCertificateManager/appsettings.json b/ZA.CoreService.ESBCertificateManager/appsettings.json
index 71e2704..f3c1560 100644
--- a/ZA.CoreService.ESBCertificateManager/appsettings.json
+++ b/ZA.CoreService.ESBCertificateManager/appsettings.json
@@ -14,6 +14,10 @@
"Password": "Administrator",
"ManagementMode": "WinRm",
"SonicHome": "C:\\Sonic\\MQ10.0",
+ "KnownContainers": [
+ "ESB",
+ "DomainManager"
+ ],
"WinRmPort": 5985,
"TimeoutSeconds": 90,
"PostRestartDelaySeconds": 15,