Explain SMC-only install: no stopcontainer.bat, fallback to MfApi.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,10 +5,10 @@ using ZA.CoreService.ESBCertificateManager.Models;
|
|||||||
namespace ZA.CoreService.ESBCertificateManager.Services;
|
namespace ZA.CoreService.ESBCertificateManager.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Neustart über die offiziellen Sonic-Bin-Scripts (laut Doku):
|
/// Neustart über Sonic-Server-Scripts:
|
||||||
/// SonicHome\bin\stopcontainer.bat Domain.Container
|
/// stopcontainer.bat / startcontainer.bat
|
||||||
/// SonicHome\bin\startcontainer.bat Domain.Container
|
/// Diese liegen nur in einer vollen MQ/ESB-Server-Installation – nicht in einer
|
||||||
/// Die Scripts setzen selbst JAVA_HOME – kein separates System-Java nötig.
|
/// reinen Sonic Management Console (SMC/Client).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SonicBinRestartExecutor
|
public sealed class SonicBinRestartExecutor
|
||||||
{
|
{
|
||||||
@@ -32,16 +32,28 @@ public sealed class SonicBinRestartExecutor
|
|||||||
return (false, $"SonicHome existiert nicht: '{sonicHome}'", null);
|
return (false, $"SonicHome existiert nicht: '{sonicHome}'", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
string stop = Path.Combine(sonicHome, "bin", "stopcontainer.bat");
|
(string? stop, string? start, string searchNote) = LocateContainerScripts(sonicHome);
|
||||||
string start = Path.Combine(sonicHome, "bin", "startcontainer.bat");
|
if (stop is null || start is null)
|
||||||
if (!File.Exists(stop) || !File.Exists(start))
|
|
||||||
{
|
{
|
||||||
|
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,
|
return (false,
|
||||||
$"stopcontainer.bat/startcontainer.bat fehlen unter '{Path.Combine(sonicHome, "bin")}'.",
|
why + $" Gesucht unter '{sonicHome}'.",
|
||||||
$"stop={File.Exists(stop)}; start={File.Exists(start)}");
|
$"{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(
|
public async Task<(bool Success, string Status, string? Detail)> RestartAsync(
|
||||||
@@ -56,9 +68,8 @@ public sealed class SonicBinRestartExecutor
|
|||||||
}
|
}
|
||||||
|
|
||||||
string sonicHome = _connection.SonicHome.Trim();
|
string sonicHome = _connection.SonicHome.Trim();
|
||||||
string bin = Path.Combine(sonicHome, "bin");
|
(string? stopBat, string? startBat, _) = LocateContainerScripts(sonicHome);
|
||||||
string stopBat = Path.Combine(bin, "stopcontainer.bat");
|
string bin = Path.GetDirectoryName(stopBat!)!;
|
||||||
string startBat = Path.Combine(bin, "startcontainer.bat");
|
|
||||||
|
|
||||||
string shortName = containerName.Contains('.')
|
string shortName = containerName.Contains('.')
|
||||||
? containerName[(containerName.IndexOf('.') + 1)..]
|
? containerName[(containerName.IndexOf('.') + 1)..]
|
||||||
@@ -69,36 +80,34 @@ public sealed class SonicBinRestartExecutor
|
|||||||
|
|
||||||
StringBuilder log = new();
|
StringBuilder log = new();
|
||||||
log.AppendLine($"SonicHome={sonicHome}");
|
log.AppendLine($"SonicHome={sonicHome}");
|
||||||
|
log.AppendLine($"stop={stopBat}");
|
||||||
|
log.AppendLine($"start={startBat}");
|
||||||
log.AppendLine($"Container={fullName} (kurz={shortName})");
|
log.AppendLine($"Container={fullName} (kurz={shortName})");
|
||||||
|
|
||||||
(bool stopOk, string stopOut, string stopErr, int stopCode) =
|
(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}");
|
log.AppendLine($"STOP ExitCode={stopCode}");
|
||||||
if (stopOut.Length > 0) log.AppendLine("STOP out: " + Truncate(stopOut));
|
if (stopOut.Length > 0) log.AppendLine("STOP out: " + Truncate(stopOut));
|
||||||
if (stopErr.Length > 0) log.AppendLine("STOP err: " + Truncate(stopErr));
|
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);
|
await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
|
||||||
|
|
||||||
(bool startOk, string startOut, string startErr, int startCode) =
|
(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}");
|
log.AppendLine($"START ExitCode={startCode}");
|
||||||
if (startOut.Length > 0) log.AppendLine("START out: " + Truncate(startOut));
|
if (startOut.Length > 0) log.AppendLine("START out: " + Truncate(startOut));
|
||||||
if (startErr.Length > 0) log.AppendLine("START err: " + Truncate(startErr));
|
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}'…");
|
log.AppendLine($"Retry START mit Kurzname '{shortName}'…");
|
||||||
(startOk, startOut, startErr, startCode) =
|
(_, startOut, startErr, startCode) =
|
||||||
await RunBatAsync(startBat, shortName, bin, cancellationToken);
|
await RunBatAsync(startBat!, shortName, bin, cancellationToken);
|
||||||
log.AppendLine($"START(short) ExitCode={startCode}");
|
log.AppendLine($"START(short) ExitCode={startCode}");
|
||||||
if (startOut.Length > 0) log.AppendLine("START out: " + Truncate(startOut));
|
if (startOut.Length > 0) log.AppendLine("START out: " + Truncate(startOut));
|
||||||
if (startErr.Length > 0) log.AppendLine("START err: " + Truncate(startErr));
|
if (startErr.Length > 0) log.AppendLine("START err: " + Truncate(startErr));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(
|
await Task.Delay(
|
||||||
TimeSpan.FromSeconds(Math.Clamp(_connection.PostRestartDelaySeconds, 0, 120)),
|
TimeSpan.FromSeconds(Math.Clamp(_connection.PostRestartDelaySeconds, 0, 120)),
|
||||||
@@ -113,6 +122,92 @@ public sealed class SonicBinRestartExecutor
|
|||||||
return (true, $"Container '{fullName}' neugestartet (SonicBin)", log.ToString());
|
return (true, $"Container '{fullName}' neugestartet (SonicBin)", log.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sucht stop/startcontainer.bat unter SonicHome\bin und rekursiv (max. Tiefe 4).
|
||||||
|
/// </summary>
|
||||||
|
public static (string? StopBat, string? StartBat, string Note) LocateContainerScripts(string sonicHome)
|
||||||
|
{
|
||||||
|
List<string> 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<string>()
|
||||||
|
.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(
|
private static async Task<(bool Started, string StdOut, string StdErr, int ExitCode)> RunBatAsync(
|
||||||
string batPath,
|
string batPath,
|
||||||
string argument,
|
string argument,
|
||||||
|
|||||||
@@ -200,11 +200,49 @@ public sealed class SonicManagementClient : IDisposable
|
|||||||
|
|
||||||
if (UsesScripts)
|
if (UsesScripts)
|
||||||
{
|
{
|
||||||
// LocalCmd / WinRm: bevorzugter Doku-Weg über die .bat-Dateien (ohne extra Java-Suche).
|
|
||||||
if (Mode == SonicManagementMode.LocalCmd)
|
if (Mode == SonicManagementMode.LocalCmd)
|
||||||
{
|
{
|
||||||
return await new SonicBinRestartExecutor(_connection)
|
(bool binOk, string binStatus, string? binDetail) =
|
||||||
|
await new SonicBinRestartExecutor(_connection)
|
||||||
.RestartAsync(containerName, cancellationToken);
|
.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);
|
return await RestartViaScriptAsync(containerName, cancellationToken);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"Username": "Administrator",
|
"Username": "Administrator",
|
||||||
"Password": "Administrator",
|
"Password": "Administrator",
|
||||||
"ManagementMode": "LocalCmd",
|
"ManagementMode": "LocalCmd",
|
||||||
"SonicHome": "C:\\Sonic\\MQ10.0",
|
"SonicHome": "C:\\DEV\\MQ10.0",
|
||||||
"JavaHome": "",
|
"JavaHome": "",
|
||||||
"JavaPath": "",
|
"JavaPath": "",
|
||||||
"MfClientLibPath": "",
|
"MfClientLibPath": "",
|
||||||
|
|||||||
Reference in New Issue
Block a user