Slim MfApi stack to restart-only path.
Drop ping/list discovery, WinRM scripts, and heavy Java probing so only KnownContainers + MBean restart remain. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,8 +3,7 @@ using ZA.CoreService.ESBCertificateManager.Models;
|
||||
namespace ZA.CoreService.ESBCertificateManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Fragt konfigurierte Sonic-Verbindungen nach Containern ab
|
||||
/// und merged Remote-Treffer mit KnownContainers / Sample-Zielen.
|
||||
/// Liefert Ziele nur aus appsettings KnownContainers (kein Live-Ping/List).
|
||||
/// </summary>
|
||||
public sealed class SonicContainerDiscovery
|
||||
{
|
||||
@@ -18,9 +17,6 @@ public sealed class SonicContainerDiscovery
|
||||
public bool HasConnections => _connections.Count > 0;
|
||||
public IReadOnlyList<SonicConnection> Connections => _connections;
|
||||
|
||||
/// <summary>
|
||||
/// Ziele nur aus appsettings KnownContainers (kein Offline-JSON).
|
||||
/// </summary>
|
||||
public IReadOnlyList<DeploymentTarget> BuildTargetsFromConfig()
|
||||
{
|
||||
List<DeploymentTarget> result = [];
|
||||
@@ -49,252 +45,4 @@ public sealed class SonicContainerDiscovery
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<SonicDiscoveryResult> DiscoverAsync(
|
||||
string connectionName,
|
||||
IReadOnlyList<DeploymentTarget> knownTargets,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
SonicConnection? connection = _connections
|
||||
.FirstOrDefault(c => string.Equals(c.Name, connectionName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (connection is null)
|
||||
{
|
||||
return SonicDiscoveryResult.Failed(connectionName,
|
||||
$"Sonic-Verbindung '{connectionName}' ist nicht in AppSettings konfiguriert.");
|
||||
}
|
||||
|
||||
using SonicManagementClient client = new(connection);
|
||||
|
||||
(bool reachable, string? pingError, string? resolvedPath) = await client.CheckConnectionAsync(cancellationToken);
|
||||
|
||||
List<string> diagnostics = [];
|
||||
List<string> remoteContainers = [];
|
||||
bool listOk = false;
|
||||
string? listError = null;
|
||||
|
||||
if (!reachable)
|
||||
{
|
||||
// Trotzdem KnownContainers nutzen – Neustart kann über SonicBin (stop/startcontainer) gehen.
|
||||
diagnostics.Add($"Management-Ping: {pingError}");
|
||||
if (connection.KnownContainers.Count == 0
|
||||
&& !knownTargets.Any(t =>
|
||||
string.Equals(t.SonicConnectionName, connection.Name, StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.IsNullOrWhiteSpace(t.ContainerName)))
|
||||
{
|
||||
return SonicDiscoveryResult.Failed(connectionName,
|
||||
$"Management-Konsole nicht erreichbar: {pingError}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(listOk, IReadOnlyList<string> remoteNames, listError) =
|
||||
await client.GetContainersAsync(cancellationToken);
|
||||
|
||||
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)
|
||||
{
|
||||
diagnostics.Add($"Listen-Fehler: {listError}");
|
||||
remoteContainers = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: explizit in appsettings hinterlegte Container
|
||||
List<string> 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<DeploymentTarget> 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 = hint,
|
||||
DiscoveredTargets = discovered,
|
||||
RawContainerNames = remoteContainers,
|
||||
ResolvedPath = resolvedPath
|
||||
};
|
||||
}
|
||||
|
||||
private static List<string> MergeContainerNames(
|
||||
IEnumerable<string> remote,
|
||||
IEnumerable<string> knownConfigured,
|
||||
IEnumerable<string> knownFromTargets)
|
||||
{
|
||||
List<string> 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<string> diagnostics,
|
||||
bool listOk,
|
||||
string? listError)
|
||||
{
|
||||
List<string> 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<DeploymentTarget> BuildTargets(
|
||||
SonicConnection connection,
|
||||
IReadOnlyList<string> containerNames,
|
||||
IReadOnlyList<DeploymentTarget> knownTargets)
|
||||
{
|
||||
List<DeploymentTarget> result = [];
|
||||
int syntheticId = -1;
|
||||
|
||||
foreach (string containerName in containerNames)
|
||||
{
|
||||
DeploymentTarget? existing = knownTargets.FirstOrDefault(t =>
|
||||
string.Equals(t.SonicConnectionName, connection.Name, StringComparison.OrdinalIgnoreCase)
|
||||
&& ContainerNamesMatch(t.ContainerName, containerName, connection.DomainName));
|
||||
|
||||
if (existing is not null)
|
||||
{
|
||||
if (!result.Any(r => r.Id == existing.Id))
|
||||
{
|
||||
result.Add(existing);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(new DeploymentTarget
|
||||
{
|
||||
Id = syntheticId--,
|
||||
Name = $"{connection.Name} / {containerName}",
|
||||
Environment = connection.DomainName,
|
||||
IsActive = true,
|
||||
TargetDirectory = string.Empty,
|
||||
CertificateFileName = string.Empty,
|
||||
ContainerName = containerName,
|
||||
RestartType = RestartType.SonicContainer,
|
||||
SonicConnectionName = connection.Name,
|
||||
SortOrder = result.Count * 10
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
foreach (DeploymentTarget known in knownTargets)
|
||||
{
|
||||
if (!string.Equals(known.SonicConnectionName, connection.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool alreadyAdded = result.Any(r =>
|
||||
ContainerNamesMatch(r.ContainerName, known.ContainerName, connection.DomainName)
|
||||
|| r.Id == known.Id);
|
||||
|
||||
if (!alreadyAdded)
|
||||
{
|
||||
result.Add(known);
|
||||
}
|
||||
}
|
||||
|
||||
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<DeploymentTarget> DiscoveredTargets { get; init; } = [];
|
||||
public IReadOnlyList<string> RawContainerNames { get; init; } = [];
|
||||
|
||||
public static SonicDiscoveryResult Failed(string connectionName, string error)
|
||||
=> new() { ConnectionName = connectionName, Success = false, ErrorMessage = error };
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using ZA.CoreService.ESBCertificateManager.Models;
|
||||
namespace ZA.CoreService.ESBCertificateManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Sonic MfApi (wie SMC-Neustart) – ohne WinRM/HTTP/XApi.
|
||||
/// Schlanke Fassade: nur Container-Neustart über MfApi.
|
||||
/// </summary>
|
||||
public sealed class SonicManagementClient : IDisposable
|
||||
{
|
||||
@@ -16,45 +16,6 @@ public sealed class SonicManagementClient : IDisposable
|
||||
_mfApi = new SonicMfApiExecutor(connection);
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string? Error, string? ResolvedPath)> CheckConnectionAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
(bool ok, string? error) = await _mfApi.TestConnectionAsync(cancellationToken);
|
||||
if (ok)
|
||||
{
|
||||
return (true, null, $"MfApi {_connection.ConnectionUrl} Domain={_connection.DomainName}");
|
||||
}
|
||||
|
||||
if (_connection.KnownContainers.Count > 0
|
||||
&& await IsTcpReachableAsync(_connection.ConnectionUrl, cancellationToken))
|
||||
{
|
||||
return (true, null,
|
||||
$"MfApi-Ping fehlgeschlagen, KnownContainers/TCP ok. Hinweis: {Truncate(error)}");
|
||||
}
|
||||
|
||||
return (false, error ?? "MfApi-Verbindung fehlgeschlagen", null);
|
||||
}
|
||||
|
||||
public async Task<(bool Success, IReadOnlyList<string> ContainerNames, string? Error)> GetContainersAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
(bool ok, IReadOnlyList<string> names, string? error) =
|
||||
await _mfApi.ListContainersAsync(cancellationToken);
|
||||
|
||||
if (ok && names.Count > 0)
|
||||
{
|
||||
return (true, names, null);
|
||||
}
|
||||
|
||||
if (_connection.KnownContainers.Count > 0)
|
||||
{
|
||||
return (true, _connection.KnownContainers,
|
||||
ok ? null : $"Liste leer/fehlerhaft – KnownContainers. {error}");
|
||||
}
|
||||
|
||||
return (false, [], error ?? "Keine Container gefunden.");
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Status, string? Detail)> RestartContainerAsync(
|
||||
string containerName,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -77,27 +38,6 @@ public sealed class SonicManagementClient : IDisposable
|
||||
$"Fehler: {error}\n\n{output}");
|
||||
}
|
||||
|
||||
private static async Task<bool> IsTcpReachableAsync(string connectionUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri uri = new(connectionUrl);
|
||||
using System.Net.Sockets.TcpClient tcp = new();
|
||||
using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
await tcp.ConnectAsync(uri.Host, uri.Port > 0 ? uri.Port : 2506, cts.Token);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string Truncate(string? s, int max = 180)
|
||||
=> string.IsNullOrWhiteSpace(s) ? string.Empty
|
||||
: s.Length <= max ? s : s[..max] + "…";
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user