Explain SMC-only install: no stopcontainer.bat, fallback to MfApi.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-24 09:22:19 +02:00
co-authored by Cursor
parent 9fdca44a66
commit e01b29be06
3 changed files with 164 additions and 31 deletions
@@ -5,10 +5,10 @@ using ZA.CoreService.ESBCertificateManager.Models;
namespace ZA.CoreService.ESBCertificateManager.Services;
/// <summary>
/// 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).
/// </summary>
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());
}
/// <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(
string batPath,
string argument,