Switch default restart to Sonic MF Management API via Domain Manager.
Use ConnectionUrl and SMC credentials with IAgentProxy.restart; keep WinRM only as optional fallback. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,13 +9,12 @@ namespace ZA.CoreService.ESBCertificateManager.Services;
|
||||
/// <summary>
|
||||
/// Verwaltet Sonic ESB Container über die Management Console.
|
||||
///
|
||||
/// Modus = WinRm (Standard):
|
||||
/// PowerShell Remoting (Invoke-Command) → CMD stop/startcontainer auf dem Sonic-Server.
|
||||
/// WinRM nutzt WinRmUsername/WinRmPassword (Windows); leer = aktueller Benutzer.
|
||||
/// Username/Password bleiben Sonic-SMC-/Domain-Manager-Logins.
|
||||
/// Modus = MfApi (Standard):
|
||||
/// Offizielle Sonic MF Management Runtime API über ConnectionUrl + SMC-Logins
|
||||
/// (JMSConnectorClient → MFProxyFactory.createAgentProxy → IAgentProxy.restart).
|
||||
///
|
||||
/// Modus = LocalCmd:
|
||||
/// Dieselbe CMD/PowerShell-Logik lokal (App muss auf dem Sonic-PC laufen; kein WinRM).
|
||||
/// Modus = WinRm / LocalCmd (optionaler Fallback):
|
||||
/// PowerShell/CMD stopcontainer/startcontainer (nicht Domain-Manager-nativ).
|
||||
///
|
||||
/// Modus = HttpApi:
|
||||
/// HTTP REST API mit automatischer Pfad-Erkennung.
|
||||
@@ -25,6 +24,7 @@ public sealed class SonicManagementClient : IDisposable
|
||||
private readonly SonicConnection _connection;
|
||||
private readonly HttpClient? _http;
|
||||
private readonly WinRmExecutor? _scriptRunner;
|
||||
private readonly SonicMfApiExecutor? _mfApi;
|
||||
|
||||
// Gecachter HTTP-Basis-Pfad (nur im HttpApi-Modus)
|
||||
private string? _resolvedContainerBasePath;
|
||||
@@ -37,14 +37,22 @@ public sealed class SonicManagementClient : IDisposable
|
||||
"/containers"
|
||||
];
|
||||
|
||||
private SonicManagementMode Mode => _connection.EffectiveManagementMode;
|
||||
|
||||
private bool UsesMfApi => Mode == SonicManagementMode.MfApi;
|
||||
|
||||
private bool UsesScripts =>
|
||||
_connection.ManagementMode is SonicManagementMode.WinRm or SonicManagementMode.LocalCmd;
|
||||
Mode is SonicManagementMode.WinRm or SonicManagementMode.LocalCmd;
|
||||
|
||||
public SonicManagementClient(SonicConnection connection)
|
||||
{
|
||||
_connection = connection;
|
||||
|
||||
if (UsesScripts)
|
||||
if (UsesMfApi)
|
||||
{
|
||||
_mfApi = new SonicMfApiExecutor(connection);
|
||||
}
|
||||
else if (UsesScripts)
|
||||
{
|
||||
_scriptRunner = new WinRmExecutor(connection);
|
||||
}
|
||||
@@ -77,16 +85,25 @@ public sealed class SonicManagementClient : IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Prüft die Verbindung zur Management Console.
|
||||
/// WinRm: TCP-Ping auf WinRM-Port + Test-PSSession.
|
||||
/// Http: Probe gegen bekannte API-Pfade.
|
||||
/// MfApi: Domain-Manager über ConnectionUrl + SMC-Credentials.
|
||||
/// WinRm/LocalCmd: Script-Laufzeit.
|
||||
/// Http: Probe gegen bekannte API-Pfade.
|
||||
/// </summary>
|
||||
public async Task<(bool Success, string? Error, string? ResolvedPath)> CheckConnectionAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (UsesMfApi)
|
||||
{
|
||||
(bool ok, string? error) = await _mfApi!.TestConnectionAsync(cancellationToken);
|
||||
string modeLabel =
|
||||
$"MfApi {_connection.ConnectionUrl} Domain={_connection.DomainName}";
|
||||
return (ok, error, ok ? modeLabel : null);
|
||||
}
|
||||
|
||||
if (UsesScripts)
|
||||
{
|
||||
(bool ok, string? error) = await _scriptRunner!.TestConnectionAsync(cancellationToken);
|
||||
string modeLabel = _connection.ManagementMode == SonicManagementMode.LocalCmd
|
||||
string modeLabel = Mode == SonicManagementMode.LocalCmd
|
||||
? $"LocalCmd (SonicHome={_connection.SonicHome})"
|
||||
: $"WinRM auf {ExtractHost(_connection.ConnectionUrl)}:{_connection.WinRmPort}";
|
||||
return (ok, error, ok ? modeLabel : null);
|
||||
@@ -100,7 +117,7 @@ public sealed class SonicManagementClient : IDisposable
|
||||
return (false,
|
||||
$"Keine Sonic HTTP-API unter {_http!.BaseAddress} gefunden.\n" +
|
||||
$"Geprüfte Pfade: {string.Join(", ", GetCandidatePaths())}\n" +
|
||||
$"Tipp: ManagementMode auf 'WinRm' setzen falls kein HTTP-API vorhanden.",
|
||||
$"Tipp: ManagementMode auf 'MfApi' setzen (ConnectionUrl + SMC-Logins).",
|
||||
null);
|
||||
}
|
||||
|
||||
@@ -110,7 +127,7 @@ public sealed class SonicManagementClient : IDisposable
|
||||
{
|
||||
return (false,
|
||||
$"HTTP-Timeout nach {_connection.TimeoutSeconds}s – Port {_connection.ManagementHttpPort} nicht erreichbar.\n" +
|
||||
$"Tipp: ManagementMode auf 'WinRm' setzen.",
|
||||
$"Tipp: ManagementMode auf 'MfApi' setzen.",
|
||||
null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -125,6 +142,11 @@ public sealed class SonicManagementClient : IDisposable
|
||||
public async Task<(bool Success, IReadOnlyList<string> ContainerNames, string? Error)> GetContainersAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (UsesMfApi)
|
||||
{
|
||||
return await GetContainersViaMfApiAsync(cancellationToken);
|
||||
}
|
||||
|
||||
if (UsesScripts)
|
||||
{
|
||||
return await GetContainersViaScriptAsync(cancellationToken);
|
||||
@@ -140,6 +162,11 @@ public sealed class SonicManagementClient : IDisposable
|
||||
string containerName,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (UsesMfApi)
|
||||
{
|
||||
return await RestartViaMfApiAsync(containerName, cancellationToken);
|
||||
}
|
||||
|
||||
if (UsesScripts)
|
||||
{
|
||||
return await RestartViaScriptAsync(containerName, cancellationToken);
|
||||
@@ -156,6 +183,41 @@ public sealed class SonicManagementClient : IDisposable
|
||||
string xapiSourcePath,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (UsesMfApi)
|
||||
{
|
||||
// XApi-Import bleibt script/HTTP; Neustart danach über MfApi.
|
||||
if (!string.IsNullOrWhiteSpace(_connection.WinRmXapiImportScript))
|
||||
{
|
||||
WinRmExecutor local = new(CloneAsLocalCmd(_connection));
|
||||
string script = WinRmExecutor.ApplyScriptTemplate(
|
||||
_connection.WinRmXapiImportScript,
|
||||
containerName,
|
||||
_connection.DomainName,
|
||||
_connection.SonicHome,
|
||||
xapiSourcePath,
|
||||
_connection.ConnectionUrl,
|
||||
_connection.Username,
|
||||
_connection.Password);
|
||||
(bool importOk, string? importOut, string? importErr) =
|
||||
await local.RunScriptAsync(script, cancellationToken);
|
||||
if (!importOk)
|
||||
{
|
||||
return (false, "XApi-Import fehlgeschlagen", importErr ?? importOut);
|
||||
}
|
||||
|
||||
(bool restartOk, string restartStatus, string? restartDetail) =
|
||||
await RestartViaMfApiAsync(containerName, cancellationToken);
|
||||
return restartOk
|
||||
? (true, "XApi importiert + Container neugestartet (MfApi)",
|
||||
$"Import: {importOut} | Restart: {restartDetail}")
|
||||
: (false, restartStatus, restartDetail);
|
||||
}
|
||||
|
||||
return (false, "XApi-Import fehlgeschlagen",
|
||||
"Im MfApi-Modus ist WinRmXapiImportScript für den Import nötig, " +
|
||||
"oder ManagementMode vorübergehend auf LocalCmd/WinRm setzen.");
|
||||
}
|
||||
|
||||
if (UsesScripts)
|
||||
{
|
||||
return await ImportXapiViaScriptAsync(containerName, xapiSourcePath, cancellationToken);
|
||||
@@ -164,6 +226,86 @@ public sealed class SonicManagementClient : IDisposable
|
||||
return await ImportXapiViaHttpAsync(containerName, xapiSourcePath, cancellationToken);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// MfApi (Sonic Domain Manager / IAgentProxy.restart)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
private async Task<(bool, IReadOnlyList<string>, string?)> GetContainersViaMfApiAsync(
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
(bool ok, IReadOnlyList<string> names, string? error) =
|
||||
await _mfApi!.ListContainersAsync(cancellationToken);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return (false, [], $"Container-Liste fehlgeschlagen (MfApi): {error}");
|
||||
}
|
||||
|
||||
if (names.Count == 0 && _connection.KnownContainers.Count > 0)
|
||||
{
|
||||
List<string> withHint =
|
||||
[
|
||||
..names,
|
||||
$"INFO:MfApiListeLeer FallbackKnownContainers={_connection.KnownContainers.Count}"
|
||||
];
|
||||
return (true, withHint, null);
|
||||
}
|
||||
|
||||
return (true, names, null);
|
||||
}
|
||||
|
||||
private async Task<(bool, string, string?)> RestartViaMfApiAsync(
|
||||
string containerName, CancellationToken cancellationToken)
|
||||
{
|
||||
(bool ok, string? output, string? error) =
|
||||
await _mfApi!.RestartAsync(containerName, cancellationToken);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return (false, "Neustart fehlgeschlagen (MfApi)",
|
||||
$"IAgentProxy.restart für '{containerName}' (Domain '{_connection.DomainName}') " +
|
||||
$"über {_connection.ConnectionUrl} fehlgeschlagen.\n" +
|
||||
$"Fehler: {error}\nAusgabe: {output}\n" +
|
||||
"Voraussetzungen: Domain Manager erreichbar, SMC-Logins korrekt, " +
|
||||
"Java/JDK + Sonic-Client-JARs (SonicHome\\lib), Container online.");
|
||||
}
|
||||
|
||||
await Task.Delay(
|
||||
TimeSpan.FromSeconds(Math.Clamp(_connection.PostRestartDelaySeconds, 0, 120)),
|
||||
cancellationToken);
|
||||
|
||||
return (true, $"Container '{containerName}' neugestartet (MfApi)",
|
||||
$"Domain={_connection.DomainName}; ConnectionUrl={_connection.ConnectionUrl}\n{output}");
|
||||
}
|
||||
|
||||
private static SonicConnection CloneAsLocalCmd(SonicConnection source)
|
||||
=> new()
|
||||
{
|
||||
Name = source.Name,
|
||||
DomainName = source.DomainName,
|
||||
ConnectionUrl = source.ConnectionUrl,
|
||||
Username = source.Username,
|
||||
Password = source.Password,
|
||||
WinRmUsername = source.WinRmUsername,
|
||||
WinRmPassword = source.WinRmPassword,
|
||||
ManagementMode = SonicManagementMode.LocalCmd,
|
||||
SonicHome = source.SonicHome,
|
||||
MfClientLibPath = source.MfClientLibPath,
|
||||
KnownContainers = source.KnownContainers,
|
||||
ManagementHttpPort = source.ManagementHttpPort,
|
||||
ApiBasePath = source.ApiBasePath,
|
||||
ContainerListPath = source.ContainerListPath,
|
||||
ContainerRestartPath = source.ContainerRestartPath,
|
||||
ContainerStopPath = source.ContainerStopPath,
|
||||
ContainerStartPath = source.ContainerStartPath,
|
||||
WinRmPort = source.WinRmPort,
|
||||
WinRmRestartScript = source.WinRmRestartScript,
|
||||
WinRmContainerListScript = source.WinRmContainerListScript,
|
||||
WinRmXapiImportScript = source.WinRmXapiImportScript,
|
||||
TimeoutSeconds = source.TimeoutSeconds,
|
||||
PostRestartDelaySeconds = source.PostRestartDelaySeconds
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Script-Implementierungen (WinRM / LocalCmd)
|
||||
// ---------------------------------------------------------------
|
||||
@@ -185,7 +327,7 @@ public sealed class SonicManagementClient : IDisposable
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return (false, [], $"Container-Liste fehlgeschlagen ({_connection.ManagementMode}): {error}");
|
||||
return (false, [], $"Container-Liste fehlgeschlagen ({Mode}): {error}");
|
||||
}
|
||||
|
||||
List<string> names = (output ?? string.Empty)
|
||||
@@ -213,7 +355,7 @@ public sealed class SonicManagementClient : IDisposable
|
||||
(bool ok, string? output, string? error) =
|
||||
await _scriptRunner!.RunScriptAsync(script, cancellationToken);
|
||||
|
||||
string mode = _connection.ManagementMode.ToString();
|
||||
string mode = Mode.ToString();
|
||||
bool verified = (output ?? string.Empty)
|
||||
.Contains("OK:ContainerRestartVerified", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user