using ZA.CoreService.ESBCertificateManager.Models; namespace ZA.CoreService.ESBCertificateManager.Services; /// /// Dünne Fassade über die Sonic MfApi (wie SMC-Neustart). /// public sealed class SonicManagementClient : IDisposable { private readonly SonicConnection _connection; private readonly SonicMfApiExecutor _mfApi; public SonicManagementClient(SonicConnection connection) { _connection = connection; _mfApi = new SonicMfApiExecutor(connection); } public async Task<(bool Reachable, string? Error, string? Detail)> CheckConnectionAsync( CancellationToken cancellationToken = default) { (bool ok, string? error) = await _mfApi.TestConnectionAsync(cancellationToken); if (ok) { return (true, null, $"MfApi {_connection.ConnectionUrl} Domain={_connection.DomainName}"); } // Soft-Fallback: KnownContainers + TCP reichen für Discovery 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 ContainerNames, string? Error)> GetContainersAsync( CancellationToken cancellationToken = default) { (bool ok, IReadOnlyList 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) { (bool ok, string? output, string? error) = await _mfApi.RestartAsync(containerName, cancellationToken); if (ok) { await Task.Delay( TimeSpan.FromSeconds(Math.Clamp(_connection.PostRestartDelaySeconds, 0, 120)), cancellationToken); return (true, $"Container '{containerName}' neugestartet", $"Domain={_connection.DomainName}; URL={_connection.ConnectionUrl}\n{output}"); } return (false, "Neustart fehlgeschlagen", $"Container '{containerName}', Domain '{_connection.DomainName}', URL {_connection.ConnectionUrl}\n" + $"Fehler: {error}\n\n{output}"); } private static async Task IsTcpReachableAsync(string connectionUrl, CancellationToken cancellationToken) { try { Uri uri = new(connectionUrl); string host = uri.Host; int port = uri.Port > 0 ? uri.Port : 2506; using System.Net.Sockets.TcpClient tcp = new(); using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); cts.CancelAfter(TimeSpan.FromSeconds(5)); await tcp.ConnectAsync(host, port, 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() { // nichts zu dispose'n } }