diff --git a/ZA.CoreService.ESBCertificateManager/Services/SonicBinRestartExecutor.cs b/ZA.CoreService.ESBCertificateManager/Services/SonicBinRestartExecutor.cs index 8c7b04c..4cfe2d0 100644 --- a/ZA.CoreService.ESBCertificateManager/Services/SonicBinRestartExecutor.cs +++ b/ZA.CoreService.ESBCertificateManager/Services/SonicBinRestartExecutor.cs @@ -5,10 +5,10 @@ using ZA.CoreService.ESBCertificateManager.Models; namespace ZA.CoreService.ESBCertificateManager.Services; /// -/// Neustart über die offiziellen Sonic-Bin-Scripts (laut Doku): -/// SonicHome\bin\stopcontainer.bat Domain.Container -/// SonicHome\bin\startcontainer.bat Domain.Container -/// Die Scripts setzen selbst JAVA_HOME – kein separates System-Java nötig. +/// Neustart über Sonic-Server-Scripts: +/// stopcontainer.bat / startcontainer.bat +/// Diese liegen nur in einer vollen MQ/ESB-Server-Installation – nicht in einer +/// reinen Sonic Management Console (SMC/Client). /// public sealed class SonicBinRestartExecutor { @@ -32,16 +32,28 @@ public sealed class SonicBinRestartExecutor return (false, $"SonicHome existiert nicht: '{sonicHome}'", null); } - string stop = Path.Combine(sonicHome, "bin", "stopcontainer.bat"); - string start = Path.Combine(sonicHome, "bin", "startcontainer.bat"); - if (!File.Exists(stop) || !File.Exists(start)) + (string? stop, string? start, string searchNote) = LocateContainerScripts(sonicHome); + if (stop is null || start is null) { + string binDir = Path.Combine(sonicHome, "bin"); + string binListing = DescribeDirectory(binDir); + bool looksLikeSmcOnly = Directory.Exists(Path.Combine(sonicHome, "lib")) + && !File.Exists(Path.Combine(binDir, "stopcontainer.bat")); + + string why = looksLikeSmcOnly + ? "Das sieht nach einer Sonic Management Console / Client-Installation aus " + + "(lib vorhanden, aber keine Server-Scripts). " + + "stopcontainer.bat gibt es nur auf dem Sonic-SERVER, nicht in der reinen SMC." + : "Server-Scripts wurden unter SonicHome nicht gefunden."; + return (false, - $"stopcontainer.bat/startcontainer.bat fehlen unter '{Path.Combine(sonicHome, "bin")}'.", - $"stop={File.Exists(stop)}; start={File.Exists(start)}"); + why + $" Gesucht unter '{sonicHome}'.", + $"{searchNote}\nInhalt von bin: {binListing}\n" + + "Lösung A: SonicHome auf den Server-Installationspfad setzen (dort wo stopcontainer.bat liegt).\n" + + "Lösung B: App lässt automatisch MfApi/SMC-Verbindung versuchen (ConnectionUrl + Login)."); } - return (true, null, $"SonicHome={sonicHome}"); + return (true, null, $"stop={stop}; start={start}"); } public async Task<(bool Success, string Status, string? Detail)> RestartAsync( @@ -56,9 +68,8 @@ public sealed class SonicBinRestartExecutor } string sonicHome = _connection.SonicHome.Trim(); - string bin = Path.Combine(sonicHome, "bin"); - string stopBat = Path.Combine(bin, "stopcontainer.bat"); - string startBat = Path.Combine(bin, "startcontainer.bat"); + (string? stopBat, string? startBat, _) = LocateContainerScripts(sonicHome); + string bin = Path.GetDirectoryName(stopBat!)!; string shortName = containerName.Contains('.') ? containerName[(containerName.IndexOf('.') + 1)..] @@ -69,35 +80,33 @@ public sealed class SonicBinRestartExecutor StringBuilder log = new(); log.AppendLine($"SonicHome={sonicHome}"); + log.AppendLine($"stop={stopBat}"); + log.AppendLine($"start={startBat}"); log.AppendLine($"Container={fullName} (kurz={shortName})"); (bool stopOk, string stopOut, string stopErr, int stopCode) = - await RunBatAsync(stopBat, fullName, bin, cancellationToken); + await RunBatAsync(stopBat!, fullName, bin, cancellationToken); log.AppendLine($"STOP ExitCode={stopCode}"); if (stopOut.Length > 0) log.AppendLine("STOP out: " + Truncate(stopOut)); if (stopErr.Length > 0) log.AppendLine("STOP err: " + Truncate(stopErr)); - // stopcontainer kann ExitCode!=0 liefern wenn Container schon down – trotzdem starten await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); (bool startOk, string startOut, string startErr, int startCode) = - await RunBatAsync(startBat, fullName, bin, cancellationToken); + await RunBatAsync(startBat!, fullName, bin, cancellationToken); log.AppendLine($"START ExitCode={startCode}"); if (startOut.Length > 0) log.AppendLine("START out: " + Truncate(startOut)); if (startErr.Length > 0) log.AppendLine("START err: " + Truncate(startErr)); - if (!startOk || startCode != 0) + if (startCode != 0 + && !string.Equals(shortName, fullName, StringComparison.OrdinalIgnoreCase)) { - // Fallback: Kurzname ohne Domain - if (!string.Equals(shortName, fullName, StringComparison.OrdinalIgnoreCase)) - { - log.AppendLine($"Retry START mit Kurzname '{shortName}'…"); - (startOk, startOut, startErr, startCode) = - await RunBatAsync(startBat, shortName, bin, cancellationToken); - log.AppendLine($"START(short) ExitCode={startCode}"); - if (startOut.Length > 0) log.AppendLine("START out: " + Truncate(startOut)); - if (startErr.Length > 0) log.AppendLine("START err: " + Truncate(startErr)); - } + log.AppendLine($"Retry START mit Kurzname '{shortName}'…"); + (_, startOut, startErr, startCode) = + await RunBatAsync(startBat!, shortName, bin, cancellationToken); + log.AppendLine($"START(short) ExitCode={startCode}"); + if (startOut.Length > 0) log.AppendLine("START out: " + Truncate(startOut)); + if (startErr.Length > 0) log.AppendLine("START err: " + Truncate(startErr)); } await Task.Delay( @@ -113,6 +122,92 @@ public sealed class SonicBinRestartExecutor return (true, $"Container '{fullName}' neugestartet (SonicBin)", log.ToString()); } + /// + /// Sucht stop/startcontainer.bat unter SonicHome\bin und rekursiv (max. Tiefe 4). + /// + public static (string? StopBat, string? StartBat, string Note) LocateContainerScripts(string sonicHome) + { + List candidates = []; + + void AddDir(string? dir) + { + if (!string.IsNullOrWhiteSpace(dir) && Directory.Exists(dir) && !candidates.Contains(dir)) + { + candidates.Add(dir); + } + } + + AddDir(Path.Combine(sonicHome, "bin")); + AddDir(Path.Combine(sonicHome, "MQ_HOME", "bin")); + AddDir(Path.Combine(sonicHome, "MQ", "bin")); + + try + { + string? parent = Directory.GetParent(sonicHome)?.FullName; + if (parent is not null) + { + foreach (string child in Directory.EnumerateDirectories(parent)) + { + AddDir(Path.Combine(child, "bin")); + } + } + } + catch + { + // ignore + } + + // Rekursiv nach Dateinamen suchen + try + { + foreach (string file in Directory.EnumerateFiles(sonicHome, "stopcontainer.bat", SearchOption.AllDirectories) + .Take(20)) + { + AddDir(Path.GetDirectoryName(file)); + } + } + catch + { + // ignore permission issues + } + + foreach (string dir in candidates) + { + string stop = Path.Combine(dir, "stopcontainer.bat"); + string start = Path.Combine(dir, "startcontainer.bat"); + if (File.Exists(stop) && File.Exists(start)) + { + return (stop, start, $"Scripts gefunden in '{dir}'"); + } + } + + return (null, null, $"Keine Scripts in {candidates.Count} geprüften bin-Ordnern."); + } + + private static string DescribeDirectory(string dir) + { + if (!Directory.Exists(dir)) + { + return "(Ordner existiert nicht)"; + } + + try + { + string[] names = Directory.GetFileSystemEntries(dir) + .Select(Path.GetFileName) + .Where(n => n is not null) + .Cast() + .OrderBy(n => n) + .Take(25) + .ToArray(); + return names.Length == 0 ? "(leer)" : string.Join(", ", names); + } + catch (Exception ex) + { + return $"(nicht lesbar: {ex.Message})"; + } + } + private static async Task<(bool Started, string StdOut, string StdErr, int ExitCode)> RunBatAsync( string batPath, string argument, diff --git a/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs b/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs index 6144902..1c2bcbe 100644 --- a/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs +++ b/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs @@ -200,11 +200,49 @@ public sealed class SonicManagementClient : IDisposable if (UsesScripts) { - // LocalCmd / WinRm: bevorzugter Doku-Weg über die .bat-Dateien (ohne extra Java-Suche). if (Mode == SonicManagementMode.LocalCmd) { - return await new SonicBinRestartExecutor(_connection) - .RestartAsync(containerName, cancellationToken); + (bool binOk, string binStatus, string? binDetail) = + await new SonicBinRestartExecutor(_connection) + .RestartAsync(containerName, cancellationToken); + + if (binOk) + { + return (true, binStatus, binDetail); + } + + // Reine SMC/Client-Installation ohne Server-bin → wie die Console selbst + // über ConnectionUrl + SMC-Login (MfApi) neu starten. + bool batsMissing = binDetail?.Contains("stopcontainer", StringComparison.OrdinalIgnoreCase) == true + || binStatus.Contains("SonicBin", StringComparison.OrdinalIgnoreCase); + + if (batsMissing) + { + SonicMfApiExecutor mf = new(_connection); + (bool mfOk, string? mfOut, string? mfErr) = + await mf.RestartAsync(containerName, cancellationToken); + if (mfOk) + { + await Task.Delay( + TimeSpan.FromSeconds(Math.Clamp(_connection.PostRestartDelaySeconds, 0, 120)), + cancellationToken); + return (true, $"Container '{containerName}' neugestartet (SMC/MfApi)", + $"Kein stopcontainer.bat unter SonicHome – SMC-Verbindung genutzt.\n{mfOut}"); + } + + return (false, "Neustart fehlgeschlagen", + "Ursache: Unter SonicHome fehlen stopcontainer.bat/startcontainer.bat.\n" + + "Das ist typisch, wenn nur die Sonic Management Console (Client) installiert ist –\n" + + "die Scripts liegen auf dem Sonic-SERVER.\n\n" + + $"SonicBin: {binDetail}\n\n" + + $"SMC/MfApi-Fallback: {mfErr}\n{mfOut}\n\n" + + "Was tun:\n" + + "1) SonicHome auf den Server-Pfad setzen (Ordner mit bin\\stopcontainer.bat), ODER\n" + + "2) Java + Client-JARs unter SonicHome\\lib bereitstellen (wie SMC),\n" + + " ConnectionUrl/User/Pass = dieselben Werte wie beim SMC-Login."); + } + + return (false, binStatus, binDetail); } return await RestartViaScriptAsync(containerName, cancellationToken); diff --git a/ZA.CoreService.ESBCertificateManager/appsettings.json b/ZA.CoreService.ESBCertificateManager/appsettings.json index 3b446f7..74e2ae6 100644 --- a/ZA.CoreService.ESBCertificateManager/appsettings.json +++ b/ZA.CoreService.ESBCertificateManager/appsettings.json @@ -13,7 +13,7 @@ "Username": "Administrator", "Password": "Administrator", "ManagementMode": "LocalCmd", - "SonicHome": "C:\\Sonic\\MQ10.0", + "SonicHome": "C:\\DEV\\MQ10.0", "JavaHome": "", "JavaPath": "", "MfClientLibPath": "",