Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a449727ad | ||
|
|
76e280d809 | ||
|
|
58b7826162 | ||
|
|
d20e0df618 | ||
|
|
f98daec656 | ||
|
|
89a694ba21 | ||
|
|
aac71ec2cd | ||
|
|
b1e1789fd1 | ||
|
|
a5557b44fa | ||
|
|
38fa04b540 | ||
|
|
3c100877be | ||
|
|
dcceb7b719 | ||
|
|
f584fd68fe | ||
|
|
f7e9bd43e2 | ||
|
|
09b31a4ac9 | ||
|
|
ed6777ffed | ||
|
|
121a428755 | ||
|
|
e45ced8b73 | ||
|
|
dcfcb5289d | ||
|
|
4d19873776 | ||
|
|
c73f53d740 | ||
|
|
ade63d0e43 | ||
|
|
d1b95bcb9d | ||
|
|
7a9cdb6ecf | ||
|
|
ada6b6ca83 | ||
|
|
a89a7ad4b6 |
@@ -28,6 +28,9 @@ x86/
|
|||||||
[Aa][Rr][Mm]64/
|
[Aa][Rr][Mm]64/
|
||||||
bld/
|
bld/
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
|
# Bundled JRE for MfApi
|
||||||
|
!ZA.CoreService.ESBCertificateManager/Runtime/bin/
|
||||||
|
!ZA.CoreService.ESBCertificateManager/Runtime/bin/**
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
[Oo]ut/
|
[Oo]ut/
|
||||||
[Ll]og/
|
[Ll]og/
|
||||||
|
|||||||
@@ -7,28 +7,102 @@ public static class AppSettingsLoader
|
|||||||
{
|
{
|
||||||
public static AppSettings Load()
|
public static AppSettings Load()
|
||||||
{
|
{
|
||||||
// Zuerst Output-Verzeichnis, dann aktuelles Arbeitsverzeichnis (z.B. VS Debug).
|
|
||||||
string basePath = Directory.Exists(AppContext.BaseDirectory)
|
string basePath = Directory.Exists(AppContext.BaseDirectory)
|
||||||
? AppContext.BaseDirectory
|
? AppContext.BaseDirectory
|
||||||
: Directory.GetCurrentDirectory();
|
: Directory.GetCurrentDirectory();
|
||||||
|
|
||||||
string settingsPath = Path.Combine(basePath, "appsettings.json");
|
string settingsPath = Path.Combine(basePath, "appsettings.json");
|
||||||
|
|
||||||
if (!File.Exists(settingsPath))
|
if (!File.Exists(settingsPath))
|
||||||
{
|
{
|
||||||
string cwdPath = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
|
string cwdPath = Path.Combine(
|
||||||
|
Directory.GetCurrentDirectory(),
|
||||||
|
"appsettings.json");
|
||||||
|
|
||||||
if (File.Exists(cwdPath))
|
if (File.Exists(cwdPath))
|
||||||
{
|
{
|
||||||
basePath = Directory.GetCurrentDirectory();
|
basePath = Directory.GetCurrentDirectory();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException(
|
||||||
|
"Die Datei appsettings.json wurde nicht gefunden.",
|
||||||
|
settingsPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IConfigurationRoot configuration = new ConfigurationBuilder()
|
IConfigurationRoot configuration = new ConfigurationBuilder()
|
||||||
.SetBasePath(basePath)
|
.SetBasePath(basePath)
|
||||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
.AddJsonFile(
|
||||||
|
"appsettings.json",
|
||||||
|
optional: false,
|
||||||
|
reloadOnChange: false)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
AppSettings settings = new();
|
AppSettings settings = new();
|
||||||
configuration.Bind(settings);
|
configuration.Bind(settings);
|
||||||
|
|
||||||
|
Validate(settings);
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void Validate(
|
||||||
|
AppSettings settings)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(settings.EnvironmentCode))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"EnvironmentCode fehlt in appsettings.json.");
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.EnvironmentCode =
|
||||||
|
settings.EnvironmentCode
|
||||||
|
.Trim()
|
||||||
|
.ToUpperInvariant();
|
||||||
|
|
||||||
|
string[] allowedEnvironmentCodes =
|
||||||
|
[
|
||||||
|
"DEV",
|
||||||
|
"TEST",
|
||||||
|
"PROD"
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!allowedEnvironmentCodes.Contains(
|
||||||
|
settings.EnvironmentCode,
|
||||||
|
StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"EnvironmentCode '{settings.EnvironmentCode}' ist ungültig. " +
|
||||||
|
"Erlaubt sind DEV, TEST und PROD.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(
|
||||||
|
settings.Database.ConnectionString))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Database:ConnectionString fehlt in appsettings.json.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(
|
||||||
|
settings.Runtime.JavaExecutablePath))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Runtime:JavaExecutablePath fehlt in appsettings.json.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(
|
||||||
|
settings.Runtime.SonicClientLibraryPath))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Runtime:SonicClientLibraryPath fehlt in appsettings.json.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(
|
||||||
|
settings.AlwaysEncrypted.CertificateThumbprint))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"AlwaysEncrypted:CertificateThumbprint fehlt.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using ZA.CoreService.ESBCertificateManager.Models;
|
|
||||||
|
|
||||||
namespace ZA.CoreService.ESBCertificateManager.Data;
|
|
||||||
|
|
||||||
public interface ITargetRepository
|
|
||||||
{
|
|
||||||
Task<IReadOnlyList<DeploymentTarget>> GetActiveTargetsAsync(CancellationToken cancellationToken = default);
|
|
||||||
string SourceDescription { get; }
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using ZA.CoreService.ESBCertificateManager.Models;
|
|
||||||
|
|
||||||
namespace ZA.CoreService.ESBCertificateManager.Data;
|
|
||||||
|
|
||||||
public sealed class JsonTargetRepository : ITargetRepository
|
|
||||||
{
|
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true,
|
|
||||||
Converters = { new JsonStringEnumConverter() }
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly string _filePath;
|
|
||||||
|
|
||||||
public JsonTargetRepository(string filePath)
|
|
||||||
{
|
|
||||||
_filePath = filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SourceDescription => $"Offline-Sample ({Path.GetFileName(_filePath)})";
|
|
||||||
|
|
||||||
public async Task<IReadOnlyList<DeploymentTarget>> GetActiveTargetsAsync(
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (!File.Exists(_filePath))
|
|
||||||
{
|
|
||||||
throw new FileNotFoundException(
|
|
||||||
$"Sample-Zieldatei wurde nicht gefunden: {_filePath}",
|
|
||||||
_filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
await using FileStream stream = File.OpenRead(_filePath);
|
|
||||||
List<DeploymentTarget>? targets = await JsonSerializer.DeserializeAsync<List<DeploymentTarget>>(
|
|
||||||
stream,
|
|
||||||
JsonOptions,
|
|
||||||
cancellationToken);
|
|
||||||
|
|
||||||
return (targets ?? [])
|
|
||||||
.Where(t => t.IsActive)
|
|
||||||
.OrderBy(t => t.SortOrder)
|
|
||||||
.ThenBy(t => t.Name)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
using Microsoft.Data.SqlClient;
|
|
||||||
using ZA.CoreService.ESBCertificateManager.Models;
|
|
||||||
|
|
||||||
namespace ZA.CoreService.ESBCertificateManager.Data;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Schreibt Deployment-Lauf-Ergebnisse in die SQL-Datenbank.
|
|
||||||
/// Tabellen: dbo.DeploymentRuns (GUID-PK) + dbo.DeploymentTargetResults.
|
|
||||||
/// Ist kein ConnectionString konfiguriert, werden alle Operationen still übersprungen.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class SqlRunLogger
|
|
||||||
{
|
|
||||||
private readonly string? _connectionString;
|
|
||||||
|
|
||||||
public SqlRunLogger(string? connectionString)
|
|
||||||
{
|
|
||||||
_connectionString = string.IsNullOrWhiteSpace(connectionString)
|
|
||||||
? null
|
|
||||||
: connectionString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsEnabled => _connectionString is not null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Legt einen neuen Lauf-Datensatz an.
|
|
||||||
/// </summary>
|
|
||||||
public async Task BeginRunAsync(
|
|
||||||
Guid runId,
|
|
||||||
DateTimeOffset startedAt,
|
|
||||||
string certificateFilePath,
|
|
||||||
string certificateFingerprint,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (_connectionString is null) return;
|
|
||||||
|
|
||||||
const string sql = """
|
|
||||||
INSERT INTO dbo.DeploymentRuns
|
|
||||||
(Id, StartedAtUtc, SourceFile, SourceFingerprint, StartedBy, MachineName, OverallStatus)
|
|
||||||
VALUES
|
|
||||||
(@Id, @StartedAtUtc, @SourceFile, @SourceFingerprint, @StartedBy, @MachineName, N'Running');
|
|
||||||
""";
|
|
||||||
|
|
||||||
await using SqlConnection conn = new(_connectionString);
|
|
||||||
await conn.OpenAsync(cancellationToken);
|
|
||||||
await using SqlCommand cmd = new(sql, conn);
|
|
||||||
|
|
||||||
cmd.Parameters.AddWithValue("@Id", runId);
|
|
||||||
cmd.Parameters.AddWithValue("@StartedAtUtc", startedAt.UtcDateTime);
|
|
||||||
cmd.Parameters.AddWithValue("@SourceFile", Path.GetFileName(certificateFilePath));
|
|
||||||
cmd.Parameters.AddWithValue("@SourceFingerprint", certificateFingerprint);
|
|
||||||
cmd.Parameters.AddWithValue("@StartedBy", Environment.UserName);
|
|
||||||
cmd.Parameters.AddWithValue("@MachineName", Environment.MachineName);
|
|
||||||
|
|
||||||
await cmd.ExecuteNonQueryAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Aktualisiert den Lauf-Datensatz mit Endzeitpunkt und Gesamtstatus.
|
|
||||||
/// </summary>
|
|
||||||
public async Task CompleteRunAsync(
|
|
||||||
Guid runId,
|
|
||||||
DateTimeOffset finishedAt,
|
|
||||||
bool overallSuccess,
|
|
||||||
int successCount,
|
|
||||||
int totalCount,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (_connectionString is null) return;
|
|
||||||
|
|
||||||
string status = overallSuccess
|
|
||||||
? "Success"
|
|
||||||
: (successCount > 0 ? "PartialFailure" : "Failure");
|
|
||||||
|
|
||||||
const string sql = """
|
|
||||||
UPDATE dbo.DeploymentRuns
|
|
||||||
SET FinishedAtUtc = @FinishedAtUtc,
|
|
||||||
OverallStatus = @OverallStatus
|
|
||||||
WHERE Id = @Id;
|
|
||||||
""";
|
|
||||||
|
|
||||||
await using SqlConnection conn = new(_connectionString);
|
|
||||||
await conn.OpenAsync(cancellationToken);
|
|
||||||
await using SqlCommand cmd = new(sql, conn);
|
|
||||||
|
|
||||||
cmd.Parameters.AddWithValue("@Id", runId);
|
|
||||||
cmd.Parameters.AddWithValue("@FinishedAtUtc", finishedAt.UtcDateTime);
|
|
||||||
cmd.Parameters.AddWithValue("@OverallStatus", status);
|
|
||||||
|
|
||||||
await cmd.ExecuteNonQueryAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Schreibt das Ergebnis eines einzelnen Deployment-Ziels.
|
|
||||||
/// </summary>
|
|
||||||
public async Task WriteTargetResultAsync(
|
|
||||||
Guid runId,
|
|
||||||
TargetStepResult step,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (_connectionString is null) return;
|
|
||||||
|
|
||||||
string status = step.Success ? "Success" : "Failure";
|
|
||||||
|
|
||||||
const string sql = """
|
|
||||||
INSERT INTO dbo.DeploymentTargetResults
|
|
||||||
(DeploymentRunId, TargetId, TargetName,
|
|
||||||
StartedAtUtc, FinishedAtUtc,
|
|
||||||
CopySucceeded, RestartSucceeded, TlsSucceeded,
|
|
||||||
ObservedFingerprint, Status, ErrorMessage)
|
|
||||||
VALUES
|
|
||||||
(@RunId, @TargetId, @TargetName,
|
|
||||||
@StartedAtUtc, @FinishedAtUtc,
|
|
||||||
@CopySucceeded, @RestartSucceeded, @TlsSucceeded,
|
|
||||||
@ObservedFingerprint, @Status, @ErrorMessage);
|
|
||||||
""";
|
|
||||||
|
|
||||||
await using SqlConnection conn = new(_connectionString);
|
|
||||||
await conn.OpenAsync(cancellationToken);
|
|
||||||
await using SqlCommand cmd = new(sql, conn);
|
|
||||||
|
|
||||||
cmd.Parameters.AddWithValue("@RunId", runId);
|
|
||||||
cmd.Parameters.AddWithValue("@TargetId", step.TargetId);
|
|
||||||
cmd.Parameters.AddWithValue("@TargetName", step.TargetName);
|
|
||||||
cmd.Parameters.AddWithValue("@StartedAtUtc", step.StartedAt.UtcDateTime);
|
|
||||||
cmd.Parameters.AddWithValue("@FinishedAtUtc", step.FinishedAt.UtcDateTime);
|
|
||||||
cmd.Parameters.AddWithValue("@CopySucceeded", step.CopySucceeded);
|
|
||||||
cmd.Parameters.AddWithValue("@RestartSucceeded", step.RestartSucceeded);
|
|
||||||
cmd.Parameters.AddWithValue("@TlsSucceeded", step.TlsSucceeded);
|
|
||||||
cmd.Parameters.AddWithValue("@ObservedFingerprint", (object?)step.ObservedFingerprint ?? DBNull.Value);
|
|
||||||
cmd.Parameters.AddWithValue("@Status", status);
|
|
||||||
cmd.Parameters.AddWithValue("@ErrorMessage", step.Success ? DBNull.Value : (object?)(step.Detail ?? step.StatusText));
|
|
||||||
|
|
||||||
await cmd.ExecuteNonQueryAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Persistiert ein vollständiges <see cref="DeploymentRunResult"/> in einem einzigen Aufruf.
|
|
||||||
/// </summary>
|
|
||||||
public async Task PersistRunResultAsync(
|
|
||||||
DeploymentRunResult runResult,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (_connectionString is null) return;
|
|
||||||
|
|
||||||
await BeginRunAsync(
|
|
||||||
runResult.RunId,
|
|
||||||
runResult.StartedAt,
|
|
||||||
runResult.CertificateFilePath,
|
|
||||||
runResult.CertificateFingerprint,
|
|
||||||
cancellationToken);
|
|
||||||
|
|
||||||
foreach (TargetStepResult step in runResult.TargetResults)
|
|
||||||
{
|
|
||||||
await WriteTargetResultAsync(runResult.RunId, step, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
int successCount = runResult.TargetResults.Count(r => r.Success);
|
|
||||||
|
|
||||||
await CompleteRunAsync(
|
|
||||||
runResult.RunId,
|
|
||||||
runResult.FinishedAt,
|
|
||||||
runResult.OverallSuccess,
|
|
||||||
successCount,
|
|
||||||
runResult.TargetResults.Count,
|
|
||||||
cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
using Microsoft.Data.SqlClient;
|
|
||||||
using ZA.CoreService.ESBCertificateManager.Models;
|
|
||||||
|
|
||||||
namespace ZA.CoreService.ESBCertificateManager.Data;
|
|
||||||
|
|
||||||
public sealed class SqlTargetRepository : ITargetRepository
|
|
||||||
{
|
|
||||||
private readonly string _connectionString;
|
|
||||||
|
|
||||||
public SqlTargetRepository(string connectionString)
|
|
||||||
{
|
|
||||||
_connectionString = connectionString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SourceDescription => "SQL Server (dbo.DeploymentTargets)";
|
|
||||||
|
|
||||||
public async Task<IReadOnlyList<DeploymentTarget>> GetActiveTargetsAsync(
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(_connectionString))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Es ist keine SQL-Verbindungszeichenfolge konfiguriert.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const string sql = """
|
|
||||||
SELECT
|
|
||||||
Id,
|
|
||||||
Name,
|
|
||||||
Environment,
|
|
||||||
IsActive,
|
|
||||||
CertificateTargetPath,
|
|
||||||
CertificateFileName,
|
|
||||||
ContainerName,
|
|
||||||
RestartType,
|
|
||||||
RestartCommand,
|
|
||||||
RestartArguments,
|
|
||||||
RestartTimeoutSeconds,
|
|
||||||
SonicConnectionName,
|
|
||||||
XapiSourcePath,
|
|
||||||
TlsHost,
|
|
||||||
TlsPort,
|
|
||||||
TlsServerName,
|
|
||||||
ExpectedFingerprint,
|
|
||||||
SortOrder
|
|
||||||
FROM dbo.DeploymentTargets
|
|
||||||
WHERE IsActive = 1
|
|
||||||
ORDER BY SortOrder, Name;
|
|
||||||
""";
|
|
||||||
|
|
||||||
List<DeploymentTarget> targets = [];
|
|
||||||
|
|
||||||
await using SqlConnection connection = new(_connectionString);
|
|
||||||
await connection.OpenAsync(cancellationToken);
|
|
||||||
|
|
||||||
await using SqlCommand command = new(sql, connection);
|
|
||||||
await using SqlDataReader reader = await command.ExecuteReaderAsync(cancellationToken);
|
|
||||||
|
|
||||||
int id = reader.GetOrdinal("Id");
|
|
||||||
int name = reader.GetOrdinal("Name");
|
|
||||||
int environment = reader.GetOrdinal("Environment");
|
|
||||||
int isActive = reader.GetOrdinal("IsActive");
|
|
||||||
int certificateTargetPath = reader.GetOrdinal("CertificateTargetPath");
|
|
||||||
int certificateFileName = reader.GetOrdinal("CertificateFileName");
|
|
||||||
int containerName = reader.GetOrdinal("ContainerName");
|
|
||||||
int restartType = reader.GetOrdinal("RestartType");
|
|
||||||
int restartCommand = reader.GetOrdinal("RestartCommand");
|
|
||||||
int restartArguments = reader.GetOrdinal("RestartArguments");
|
|
||||||
int restartTimeoutSeconds = reader.GetOrdinal("RestartTimeoutSeconds");
|
|
||||||
int sonicConnectionName = reader.GetOrdinal("SonicConnectionName");
|
|
||||||
int xapiSourcePath = reader.GetOrdinal("XapiSourcePath");
|
|
||||||
int tlsHost = reader.GetOrdinal("TlsHost");
|
|
||||||
int tlsPort = reader.GetOrdinal("TlsPort");
|
|
||||||
int tlsServerName = reader.GetOrdinal("TlsServerName");
|
|
||||||
int expectedFingerprint = reader.GetOrdinal("ExpectedFingerprint");
|
|
||||||
int sortOrder = reader.GetOrdinal("SortOrder");
|
|
||||||
|
|
||||||
while (await reader.ReadAsync(cancellationToken))
|
|
||||||
{
|
|
||||||
RestartType parsedRestartType = Enum.TryParse(
|
|
||||||
reader.GetString(restartType),
|
|
||||||
ignoreCase: true,
|
|
||||||
out RestartType parsed)
|
|
||||||
? parsed
|
|
||||||
: RestartType.None;
|
|
||||||
|
|
||||||
targets.Add(new DeploymentTarget
|
|
||||||
{
|
|
||||||
Id = reader.GetInt32(id),
|
|
||||||
Name = reader.GetString(name),
|
|
||||||
Environment = reader.GetString(environment),
|
|
||||||
IsActive = reader.GetBoolean(isActive),
|
|
||||||
TargetDirectory = reader.GetString(certificateTargetPath),
|
|
||||||
CertificateFileName = reader.GetString(certificateFileName),
|
|
||||||
ContainerName = reader.GetString(containerName),
|
|
||||||
RestartType = parsedRestartType,
|
|
||||||
RestartCommand = reader.IsDBNull(restartCommand) ? string.Empty : reader.GetString(restartCommand),
|
|
||||||
RestartArguments = reader.GetString(restartArguments),
|
|
||||||
RestartTimeoutSeconds = reader.GetInt32(restartTimeoutSeconds),
|
|
||||||
SonicConnectionName = reader.GetString(sonicConnectionName),
|
|
||||||
XapiSourcePath = reader.GetString(xapiSourcePath),
|
|
||||||
TlsHost = reader.GetString(tlsHost),
|
|
||||||
TlsPort = reader.IsDBNull(tlsPort) ? null : reader.GetInt32(tlsPort),
|
|
||||||
TlsServerName = reader.GetString(tlsServerName),
|
|
||||||
ExpectedFingerprint = reader.IsDBNull(expectedFingerprint) ? null : reader.GetString(expectedFingerprint),
|
|
||||||
SortOrder = reader.GetInt32(sortOrder)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return targets;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
using ZA.CoreService.ESBCertificateManager.Models;
|
|
||||||
|
|
||||||
namespace ZA.CoreService.ESBCertificateManager.Data;
|
|
||||||
|
|
||||||
public static class TargetRepositoryFactory
|
|
||||||
{
|
|
||||||
public static async Task<(ITargetRepository Repository, string LoadMessage)> CreateAsync(
|
|
||||||
AppSettings settings,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
string samplePath = Path.GetFullPath(
|
|
||||||
Path.Combine(AppContext.BaseDirectory, settings.SampleTargetsPath));
|
|
||||||
|
|
||||||
if (settings.UseOfflineSampleData)
|
|
||||||
{
|
|
||||||
ITargetRepository jsonRepo = new JsonTargetRepository(samplePath);
|
|
||||||
_ = await jsonRepo.GetActiveTargetsAsync(cancellationToken);
|
|
||||||
return (jsonRepo, $"Ziele geladen aus {jsonRepo.SourceDescription}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(settings.ConnectionString))
|
|
||||||
{
|
|
||||||
return OfflineSample(samplePath, "Keine SQL-Verbindung konfiguriert – Offline-Sample wird verwendet.");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SqlTargetRepository sqlRepo = new(settings.ConnectionString);
|
|
||||||
_ = await sqlRepo.GetActiveTargetsAsync(cancellationToken);
|
|
||||||
return (sqlRepo, $"Ziele geladen aus {sqlRepo.SourceDescription}.");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return OfflineSample(
|
|
||||||
samplePath,
|
|
||||||
$"SQL nicht erreichbar ({ex.Message}). Offline-Sample wird verwendet.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (ITargetRepository Repository, string LoadMessage) OfflineSample(
|
|
||||||
string samplePath,
|
|
||||||
string message)
|
|
||||||
=> (new JsonTargetRepository(samplePath), message);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"Id": 1,
|
|
||||||
"Name": "DE-Test Container",
|
|
||||||
"Environment": "TEST",
|
|
||||||
"IsActive": true,
|
|
||||||
"TargetDirectory": "DeploySandbox/target-esb",
|
|
||||||
"CertificateFileName": "esb-cert.cer",
|
|
||||||
"ContainerName": "DE-Test",
|
|
||||||
"RestartType": "SonicContainer",
|
|
||||||
"RestartCommand": "",
|
|
||||||
"RestartArguments": "",
|
|
||||||
"RestartTimeoutSeconds": 120,
|
|
||||||
"SonicConnectionName": "DE-Test",
|
|
||||||
"XapiSourcePath": "",
|
|
||||||
"TlsHost": "",
|
|
||||||
"TlsPort": null,
|
|
||||||
"TlsServerName": "",
|
|
||||||
"ExpectedFingerprint": null,
|
|
||||||
"SortOrder": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": 2,
|
|
||||||
"Name": "Lokaler CMD-Test (Echo)",
|
|
||||||
"Environment": "DEV",
|
|
||||||
"IsActive": false,
|
|
||||||
"TargetDirectory": "DeploySandbox/target-cmd",
|
|
||||||
"CertificateFileName": "esb-cert.cer",
|
|
||||||
"ContainerName": "",
|
|
||||||
"RestartType": "Command",
|
|
||||||
"RestartCommand": "cmd.exe",
|
|
||||||
"RestartArguments": "/c echo Neustart-Simulation OK",
|
|
||||||
"RestartTimeoutSeconds": 15,
|
|
||||||
"SonicConnectionName": "",
|
|
||||||
"XapiSourcePath": "",
|
|
||||||
"TlsHost": "",
|
|
||||||
"TlsPort": null,
|
|
||||||
"TlsServerName": "",
|
|
||||||
"ExpectedFingerprint": null,
|
|
||||||
"SortOrder": 20
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
DATABASE: [master]
|
||||||
|
OBJECTNAME: DropExisting
|
||||||
|
ACTION: DROP
|
||||||
|
IDX: 001
|
||||||
|
DATE: 2026-07-29
|
||||||
|
USER: CUR
|
||||||
|
COMMENT: Loescht die befuellte Alt-DB (Tippfehler-Name) und die Ziel-DB
|
||||||
|
vollstaendig inkl. aller Tabellen und Always-Encrypted-Keys
|
||||||
|
(CMK/CEK). Danach: 01_CreateDatabase -> 02_CreateSchema -> 03_Seed.
|
||||||
|
Voraussetzung Client: Always-Encrypted-Zertifikat mit Thumbprint
|
||||||
|
aus appsettings (CurrentUser\My) muss vorhanden bleiben.
|
||||||
|
ACHTUNG: destruktiv, keine Datenmigration.
|
||||||
|
*/
|
||||||
|
USE [master];
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* Alte DB mit Tippfehler im Namen */
|
||||||
|
IF DB_ID(N'EsbZertifkatManager') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER DATABASE [EsbZertifkatManager]
|
||||||
|
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
|
||||||
|
|
||||||
|
DROP DATABASE [EsbZertifkatManager];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* Ziel-DB (falls bereits angelegt / befuellt) */
|
||||||
|
IF DB_ID(N'EsbZertifikatManager') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER DATABASE [EsbZertifikatManager]
|
||||||
|
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
|
||||||
|
|
||||||
|
DROP DATABASE [EsbZertifikatManager];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
DATABASE: [EsbZertifikatManager]
|
||||||
|
OBJECTNAME: Database
|
||||||
|
ACTION: CREATE
|
||||||
|
IDX: 002
|
||||||
|
DATE: 2026-07-29
|
||||||
|
USER: CUR
|
||||||
|
COMMENT: Legt die leere Ziel-DB an. Vorher 00_DropExisting.sql ausfuehren
|
||||||
|
(loescht Alt- und Ziel-DB inkl. Always Encrypted).
|
||||||
|
Reihenfolge: 00_DropExisting -> 01_CreateDatabase ->
|
||||||
|
02_CreateSchema -> 03_Seed.
|
||||||
|
*/
|
||||||
|
USE [master];
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF DB_ID(N'EsbZertifikatManager') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
THROW 50001,
|
||||||
|
N'Datenbank [EsbZertifikatManager] existiert noch. Zuerst 00_DropExisting.sql ausfuehren.',
|
||||||
|
1;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE DATABASE [EsbZertifikatManager];
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER DATABASE [EsbZertifikatManager] SET RECOVERY SIMPLE;
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER DATABASE [EsbZertifikatManager] SET READ_WRITE;
|
||||||
|
GO
|
||||||
@@ -0,0 +1,607 @@
|
|||||||
|
/*
|
||||||
|
DATABASE: [EsbZertifikatManager]
|
||||||
|
OBJECTNAME: Schema
|
||||||
|
ACTION: CREATE
|
||||||
|
IDX: 002
|
||||||
|
DATE: 2026-07-29
|
||||||
|
USER: CUR
|
||||||
|
COMMENT: 6 Kern-Tabellen + Always Encrypted (CMK/CEK).
|
||||||
|
Vorher 00_DropExisting + 01_CreateDatabase ausfuehren.
|
||||||
|
CMK-KEY_PATH = CurrentUser/My/<Thumbprint aus appsettings>.
|
||||||
|
CEK-ENCRYPTED_VALUE ist an dieses Zertifikat gebunden.
|
||||||
|
Bei neuem Cert: CEK in SSMS neu erzeugen und ENCRYPTED_VALUE ersetzen.
|
||||||
|
Connection String muss Column Encryption Setting=Enabled enthalten.
|
||||||
|
*/
|
||||||
|
USE [EsbZertifikatManager];
|
||||||
|
GO
|
||||||
|
|
||||||
|
SET ANSI_NULLS ON;
|
||||||
|
GO
|
||||||
|
|
||||||
|
SET QUOTED_IDENTIFIER ON;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Reste bereinigen: Kind-Tabellen -> AE-Tabellen -> CEK -> CMK */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[CertificateBackup]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[CertificateBackup];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[DeploymentRunStep]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[DeploymentRunStep];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[DeploymentRunContainer]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[DeploymentRunContainer];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[DeploymentRun]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[DeploymentRun];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[DeploymentAction]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[DeploymentAction];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[TlsEndpoint]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[TlsEndpoint];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[CertificateTarget]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[CertificateTarget];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[CertificateArtifact]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[CertificateArtifact];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[AppUser]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[AppUser];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[SonicCredential]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[SonicCredential];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[SonicContainer]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[SonicContainer];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* SonicConnection kann AE-Spalten haben — vor CEK-Drop entfernen */
|
||||||
|
IF OBJECT_ID(N'[dbo].[SonicConnection]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[SonicConnection];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[EsbService]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[EsbService];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[Country]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[Country];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[Environment]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[Environment];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[Company]', N'U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP TABLE [dbo].[Company];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [sys].[column_encryption_keys] AS cek
|
||||||
|
WHERE cek.[name] = N'CEK_ESB_ZertifikatManager'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
DROP COLUMN ENCRYPTION KEY [CEK_ESB_ZertifikatManager];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [sys].[column_master_keys] AS cmk
|
||||||
|
WHERE cmk.[name] = N'CMK_ESB_ZertifikatManager'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
DROP COLUMN MASTER KEY [CMK_ESB_ZertifikatManager];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Always Encrypted – neu anlegen */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
CREATE COLUMN MASTER KEY [CMK_ESB_ZertifikatManager]
|
||||||
|
WITH
|
||||||
|
(
|
||||||
|
KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
|
||||||
|
KEY_PATH = N'CurrentUser/My/95E906CCC9C5F227969877CF3BD46D2AD7168853'
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE COLUMN ENCRYPTION KEY [CEK_ESB_ZertifikatManager]
|
||||||
|
WITH VALUES
|
||||||
|
(
|
||||||
|
COLUMN_MASTER_KEY = [CMK_ESB_ZertifikatManager],
|
||||||
|
ALGORITHM = 'RSA_OAEP',
|
||||||
|
ENCRYPTED_VALUE = 0x017000630075007200720065006E00740075007300650072002F006D0079002F003900350065003900300036006300630063003900630035006600320032003700390036003900380037003700630066003300620064003400360064003200610064003700310036003800380035003300000063875F8F9E6CA28F5C6AB80295C9526F00C6708A9E3D8EEF172189C88A445F21D08CA11BCA77B3460C8F109A0C30B3CF9E3E7007BC4EACE9563591014C6FDE01F15E72F6992C7862F1C22C7E69D00418B26E700F95112E870A17C4510891AE687B522F71A58E70DFAE211F6B233628FB17E87F2837726E4801CB45A2357CE57194239950B19930CE5C8B7AF9A0CFF86AFA52DF97D392C77DE694ECA336A0DBCB6E5157AF8713636D90D121BDA1CBC4909C3A1102BE9FE561321FED761A672D2E534A5C94391704C79D007804DE628961BB2A4744D88E975C8287459F2D7D42F643078CEFF8842DFE4C826CF4B47BE2A59DC65D495F39BE45AA583E2784DE039F
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Company */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[Company]', N'U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE [dbo].[Company]
|
||||||
|
(
|
||||||
|
[CompanyId] [int] IDENTITY(1, 1) NOT NULL,
|
||||||
|
[CompanyCode] [nvarchar](30) NOT NULL,
|
||||||
|
[CompanyName] [nvarchar](200) NOT NULL,
|
||||||
|
[IsActive] [bit] NOT NULL,
|
||||||
|
[CreationDateTime] [datetime2](3) NOT NULL,
|
||||||
|
[CreatedBy] [nvarchar](128) NOT NULL,
|
||||||
|
[ModifiedDateTime] [datetime2](3) NULL,
|
||||||
|
[ModifiedBy] [nvarchar](128) NULL,
|
||||||
|
CONSTRAINT [pk_Company] PRIMARY KEY CLUSTERED ([CompanyId] ASC),
|
||||||
|
CONSTRAINT [uc_Company_CompanyCode] UNIQUE NONCLUSTERED ([CompanyCode] ASC)
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_Company_IsActive]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[Company]
|
||||||
|
ADD CONSTRAINT [dc_Company_IsActive]
|
||||||
|
DEFAULT ((1)) FOR [IsActive];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_Company_CreationDateTime]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[Company]
|
||||||
|
ADD CONSTRAINT [dc_Company_CreationDateTime]
|
||||||
|
DEFAULT (SYSUTCDATETIME()) FOR [CreationDateTime];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_Company_CreatedBy]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[Company]
|
||||||
|
ADD CONSTRAINT [dc_Company_CreatedBy]
|
||||||
|
DEFAULT (SUSER_SNAME()) FOR [CreatedBy];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Environment */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[Environment]', N'U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE [dbo].[Environment]
|
||||||
|
(
|
||||||
|
[EnvironmentId] [int] IDENTITY(1, 1) NOT NULL,
|
||||||
|
[EnvironmentCode] [nvarchar](30) NOT NULL,
|
||||||
|
[EnvironmentName] [nvarchar](100) NOT NULL,
|
||||||
|
[SortOrder] [int] NOT NULL,
|
||||||
|
[IsActive] [bit] NOT NULL,
|
||||||
|
[CreationDateTime] [datetime2](3) NOT NULL,
|
||||||
|
[CreatedBy] [nvarchar](128) NOT NULL,
|
||||||
|
[ModifiedDateTime] [datetime2](3) NULL,
|
||||||
|
[ModifiedBy] [nvarchar](128) NULL,
|
||||||
|
CONSTRAINT [pk_Environment] PRIMARY KEY CLUSTERED ([EnvironmentId] ASC),
|
||||||
|
CONSTRAINT [uc_Environment_EnvironmentCode] UNIQUE NONCLUSTERED ([EnvironmentCode] ASC),
|
||||||
|
CONSTRAINT [uc_Environment_SortOrder] UNIQUE NONCLUSTERED ([SortOrder] ASC)
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_Environment_IsActive]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[Environment]
|
||||||
|
ADD CONSTRAINT [dc_Environment_IsActive]
|
||||||
|
DEFAULT ((1)) FOR [IsActive];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_Environment_CreationDateTime]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[Environment]
|
||||||
|
ADD CONSTRAINT [dc_Environment_CreationDateTime]
|
||||||
|
DEFAULT (SYSUTCDATETIME()) FOR [CreationDateTime];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_Environment_CreatedBy]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[Environment]
|
||||||
|
ADD CONSTRAINT [dc_Environment_CreatedBy]
|
||||||
|
DEFAULT (SUSER_SNAME()) FOR [CreatedBy];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* SonicConnection */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[SonicConnection]', N'U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE [dbo].[SonicConnection]
|
||||||
|
(
|
||||||
|
[SonicConnectionId] [int] IDENTITY(1, 1) NOT NULL,
|
||||||
|
[EnvironmentId] [int] NOT NULL,
|
||||||
|
[ConnectionName] [nvarchar](150) NOT NULL,
|
||||||
|
[ManagementHost] [nvarchar](255) NOT NULL,
|
||||||
|
[ManagementPort] [int] NOT NULL,
|
||||||
|
[DomainName] [nvarchar](150) NOT NULL,
|
||||||
|
[ConnectionProtocol] [nvarchar](20) NOT NULL,
|
||||||
|
[IsActive] [bit] NOT NULL,
|
||||||
|
[CreationDateTime] [datetime2](3) NOT NULL,
|
||||||
|
[CreatedBy] [nvarchar](128) NOT NULL,
|
||||||
|
[ModifiedDateTime] [datetime2](3) NULL,
|
||||||
|
[ModifiedBy] [nvarchar](128) NULL,
|
||||||
|
CONSTRAINT [pk_SonicConnection] PRIMARY KEY CLUSTERED ([SonicConnectionId] ASC),
|
||||||
|
CONSTRAINT [uc_SonicConnection_ConnectionName] UNIQUE NONCLUSTERED ([ConnectionName] ASC),
|
||||||
|
CONSTRAINT [fk_SonicConnection_EnvironmentId]
|
||||||
|
FOREIGN KEY ([EnvironmentId])
|
||||||
|
REFERENCES [dbo].[Environment] ([EnvironmentId]),
|
||||||
|
CONSTRAINT [cc_SonicConnection_ManagementPort]
|
||||||
|
CHECK ([ManagementPort] >= (1) AND [ManagementPort] <= (65535))
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicConnection_ConnectionProtocol]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicConnection]
|
||||||
|
ADD CONSTRAINT [dc_SonicConnection_ConnectionProtocol]
|
||||||
|
DEFAULT (N'tcp') FOR [ConnectionProtocol];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicConnection_IsActive]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicConnection]
|
||||||
|
ADD CONSTRAINT [dc_SonicConnection_IsActive]
|
||||||
|
DEFAULT ((1)) FOR [IsActive];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicConnection_CreationDateTime]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicConnection]
|
||||||
|
ADD CONSTRAINT [dc_SonicConnection_CreationDateTime]
|
||||||
|
DEFAULT (SYSUTCDATETIME()) FOR [CreationDateTime];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicConnection_CreatedBy]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicConnection]
|
||||||
|
ADD CONSTRAINT [dc_SonicConnection_CreatedBy]
|
||||||
|
DEFAULT (SUSER_SNAME()) FOR [CreatedBy];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [sys].[indexes] AS i
|
||||||
|
WHERE i.[object_id] = OBJECT_ID(N'[dbo].[SonicConnection]')
|
||||||
|
AND i.[name] = N'idx_SonicConnection_EnvironmentActive'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX [idx_SonicConnection_EnvironmentActive]
|
||||||
|
ON [dbo].[SonicConnection]
|
||||||
|
(
|
||||||
|
[EnvironmentId] ASC,
|
||||||
|
[IsActive] ASC
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* SonicCredential (Always Encrypted) */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[SonicCredential]', N'U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE [dbo].[SonicCredential]
|
||||||
|
(
|
||||||
|
[SonicCredentialId] [int] IDENTITY(1, 1) NOT NULL,
|
||||||
|
[SonicConnectionId] [int] NOT NULL,
|
||||||
|
[CredentialName] [nvarchar](150) NOT NULL,
|
||||||
|
[CredentialUserName] [nvarchar](256)
|
||||||
|
COLLATE Latin1_General_BIN2
|
||||||
|
ENCRYPTED WITH
|
||||||
|
(
|
||||||
|
COLUMN_ENCRYPTION_KEY = [CEK_ESB_ZertifikatManager],
|
||||||
|
ENCRYPTION_TYPE = Randomized,
|
||||||
|
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'
|
||||||
|
) NULL,
|
||||||
|
[CredentialSecret] [nvarchar](512)
|
||||||
|
COLLATE Latin1_General_BIN2
|
||||||
|
ENCRYPTED WITH
|
||||||
|
(
|
||||||
|
COLUMN_ENCRYPTION_KEY = [CEK_ESB_ZertifikatManager],
|
||||||
|
ENCRYPTION_TYPE = Randomized,
|
||||||
|
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'
|
||||||
|
) NULL,
|
||||||
|
[IsDefault] [bit] NOT NULL,
|
||||||
|
[IsActive] [bit] NOT NULL,
|
||||||
|
[CreationDateTime] [datetime2](3) NOT NULL,
|
||||||
|
[CreatedBy] [nvarchar](128) NOT NULL,
|
||||||
|
[ModifiedDateTime] [datetime2](3) NULL,
|
||||||
|
[ModifiedBy] [nvarchar](128) NULL,
|
||||||
|
CONSTRAINT [pk_SonicCredential] PRIMARY KEY CLUSTERED ([SonicCredentialId] ASC),
|
||||||
|
CONSTRAINT [uc_SonicCredential_CredentialName]
|
||||||
|
UNIQUE NONCLUSTERED ([SonicConnectionId] ASC, [CredentialName] ASC),
|
||||||
|
CONSTRAINT [fk_SonicCredential_SonicConnectionId]
|
||||||
|
FOREIGN KEY ([SonicConnectionId])
|
||||||
|
REFERENCES [dbo].[SonicConnection] ([SonicConnectionId])
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicCredential_IsDefault]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicCredential]
|
||||||
|
ADD CONSTRAINT [dc_SonicCredential_IsDefault]
|
||||||
|
DEFAULT ((0)) FOR [IsDefault];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicCredential_IsActive]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicCredential]
|
||||||
|
ADD CONSTRAINT [dc_SonicCredential_IsActive]
|
||||||
|
DEFAULT ((1)) FOR [IsActive];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicCredential_CreationDateTime]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicCredential]
|
||||||
|
ADD CONSTRAINT [dc_SonicCredential_CreationDateTime]
|
||||||
|
DEFAULT (SYSUTCDATETIME()) FOR [CreationDateTime];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicCredential_CreatedBy]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicCredential]
|
||||||
|
ADD CONSTRAINT [dc_SonicCredential_CreatedBy]
|
||||||
|
DEFAULT (SUSER_SNAME()) FOR [CreatedBy];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* SonicContainer */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[SonicContainer]', N'U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE [dbo].[SonicContainer]
|
||||||
|
(
|
||||||
|
[SonicContainerId] [int] IDENTITY(1, 1) NOT NULL,
|
||||||
|
[CompanyId] [int] NOT NULL,
|
||||||
|
[SonicConnectionId] [int] NOT NULL,
|
||||||
|
[ContainerName] [nvarchar](250) NOT NULL,
|
||||||
|
[ContainerDisplayName] [nvarchar](250) NULL,
|
||||||
|
[CertificateCheckUrl] [nvarchar](500) NULL,
|
||||||
|
[RestartTimeoutSeconds] [int] NOT NULL,
|
||||||
|
[IsActive] [bit] NOT NULL,
|
||||||
|
[CreationDateTime] [datetime2](3) NOT NULL,
|
||||||
|
[CreatedBy] [nvarchar](128) NOT NULL,
|
||||||
|
[ModifiedDateTime] [datetime2](3) NULL,
|
||||||
|
[ModifiedBy] [nvarchar](128) NULL,
|
||||||
|
CONSTRAINT [pk_SonicContainer] PRIMARY KEY CLUSTERED ([SonicContainerId] ASC),
|
||||||
|
CONSTRAINT [uc_SonicContainer_CompanyContainer]
|
||||||
|
UNIQUE NONCLUSTERED ([CompanyId] ASC, [ContainerName] ASC),
|
||||||
|
CONSTRAINT [fk_SonicContainer_CompanyId]
|
||||||
|
FOREIGN KEY ([CompanyId])
|
||||||
|
REFERENCES [dbo].[Company] ([CompanyId]),
|
||||||
|
CONSTRAINT [fk_SonicContainer_SonicConnectionId]
|
||||||
|
FOREIGN KEY ([SonicConnectionId])
|
||||||
|
REFERENCES [dbo].[SonicConnection] ([SonicConnectionId]),
|
||||||
|
CONSTRAINT [cc_SonicContainer_RestartTimeoutSeconds]
|
||||||
|
CHECK ([RestartTimeoutSeconds] >= (10) AND [RestartTimeoutSeconds] <= (3600))
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicContainer_RestartTimeoutSeconds]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicContainer]
|
||||||
|
ADD CONSTRAINT [dc_SonicContainer_RestartTimeoutSeconds]
|
||||||
|
DEFAULT ((180)) FOR [RestartTimeoutSeconds];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicContainer_IsActive]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicContainer]
|
||||||
|
ADD CONSTRAINT [dc_SonicContainer_IsActive]
|
||||||
|
DEFAULT ((1)) FOR [IsActive];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicContainer_CreationDateTime]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicContainer]
|
||||||
|
ADD CONSTRAINT [dc_SonicContainer_CreationDateTime]
|
||||||
|
DEFAULT (SYSUTCDATETIME()) FOR [CreationDateTime];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_SonicContainer_CreatedBy]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicContainer]
|
||||||
|
ADD CONSTRAINT [dc_SonicContainer_CreatedBy]
|
||||||
|
DEFAULT (SUSER_SNAME()) FOR [CreatedBy];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [sys].[indexes] AS i
|
||||||
|
WHERE i.[object_id] = OBJECT_ID(N'[dbo].[SonicContainer]')
|
||||||
|
AND i.[name] = N'idx_SonicContainer_ConnectionActive'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX [idx_SonicContainer_ConnectionActive]
|
||||||
|
ON [dbo].[SonicContainer]
|
||||||
|
(
|
||||||
|
[SonicConnectionId] ASC,
|
||||||
|
[IsActive] ASC
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [sys].[indexes] AS i
|
||||||
|
WHERE i.[object_id] = OBJECT_ID(N'[dbo].[SonicContainer]')
|
||||||
|
AND i.[name] = N'idx_SonicContainer_CompanyActive'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX [idx_SonicContainer_CompanyActive]
|
||||||
|
ON [dbo].[SonicContainer]
|
||||||
|
(
|
||||||
|
[CompanyId] ASC,
|
||||||
|
[IsActive] ASC
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* CertificateTarget */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[CertificateTarget]', N'U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE [dbo].[CertificateTarget]
|
||||||
|
(
|
||||||
|
[CertificateTargetId] [int] IDENTITY(1, 1) NOT NULL,
|
||||||
|
[SonicContainerId] [int] NOT NULL,
|
||||||
|
[TargetDirectory] [nvarchar](500) NOT NULL,
|
||||||
|
[TargetFileName] [nvarchar](260) NOT NULL,
|
||||||
|
[BackupEnabled] [bit] NOT NULL,
|
||||||
|
[BackupDirectoryName] [nvarchar](100) NOT NULL,
|
||||||
|
[IsActive] [bit] NOT NULL,
|
||||||
|
[CreationDateTime] [datetime2](3) NOT NULL,
|
||||||
|
[CreatedBy] [nvarchar](128) NOT NULL,
|
||||||
|
[ModifiedDateTime] [datetime2](3) NULL,
|
||||||
|
[ModifiedBy] [nvarchar](128) NULL,
|
||||||
|
CONSTRAINT [pk_CertificateTarget] PRIMARY KEY CLUSTERED ([CertificateTargetId] ASC),
|
||||||
|
CONSTRAINT [uc_CertificateTarget_Destination]
|
||||||
|
UNIQUE NONCLUSTERED
|
||||||
|
(
|
||||||
|
[SonicContainerId] ASC,
|
||||||
|
[TargetDirectory] ASC,
|
||||||
|
[TargetFileName] ASC
|
||||||
|
),
|
||||||
|
CONSTRAINT [fk_CertificateTarget_SonicContainerId]
|
||||||
|
FOREIGN KEY ([SonicContainerId])
|
||||||
|
REFERENCES [dbo].[SonicContainer] ([SonicContainerId])
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_CertificateTarget_BackupEnabled]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[CertificateTarget]
|
||||||
|
ADD CONSTRAINT [dc_CertificateTarget_BackupEnabled]
|
||||||
|
DEFAULT ((1)) FOR [BackupEnabled];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_CertificateTarget_BackupDirectoryName]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[CertificateTarget]
|
||||||
|
ADD CONSTRAINT [dc_CertificateTarget_BackupDirectoryName]
|
||||||
|
DEFAULT (N'Backup') FOR [BackupDirectoryName];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_CertificateTarget_IsActive]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[CertificateTarget]
|
||||||
|
ADD CONSTRAINT [dc_CertificateTarget_IsActive]
|
||||||
|
DEFAULT ((1)) FOR [IsActive];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_CertificateTarget_CreationDateTime]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[CertificateTarget]
|
||||||
|
ADD CONSTRAINT [dc_CertificateTarget_CreationDateTime]
|
||||||
|
DEFAULT (SYSUTCDATETIME()) FOR [CreationDateTime];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID(N'[dbo].[dc_CertificateTarget_CreatedBy]', N'D') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[CertificateTarget]
|
||||||
|
ADD CONSTRAINT [dc_CertificateTarget_CreatedBy]
|
||||||
|
DEFAULT (SUSER_SNAME()) FOR [CreatedBy];
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [sys].[indexes] AS i
|
||||||
|
WHERE i.[object_id] = OBJECT_ID(N'[dbo].[CertificateTarget]')
|
||||||
|
AND i.[name] = N'idx_CertificateTarget_ContainerActive'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX [idx_CertificateTarget_ContainerActive]
|
||||||
|
ON [dbo].[CertificateTarget]
|
||||||
|
(
|
||||||
|
[SonicContainerId] ASC,
|
||||||
|
[IsActive] ASC
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
@@ -0,0 +1,351 @@
|
|||||||
|
/*
|
||||||
|
DATABASE: [EsbZertifikatManager]
|
||||||
|
OBJECTNAME: Seed
|
||||||
|
ACTION: INSERT
|
||||||
|
IDX: 002
|
||||||
|
DATE: 2026-07-29
|
||||||
|
USER: CUR
|
||||||
|
COMMENT: KOMPLETTE Seed-Datei (alles in einem Lauf):
|
||||||
|
- Company: DE, PL, AT
|
||||||
|
- Environment: TEST2, TEST, PROD
|
||||||
|
- SonicConnection: DE-Test (TEST)
|
||||||
|
- SonicContainer: ct-ZADBService
|
||||||
|
- CertificateTarget: Platzhalter-Pfad
|
||||||
|
Keine Credentials (Always Encrypted / Setup-Wizard).
|
||||||
|
Voraussetzung: 00_DropExisting, 01_CreateDatabase, 02_CreateSchema,
|
||||||
|
AlwaysEncrypted-Zertifikat importiert (Install-Certificate.ps1).
|
||||||
|
*/
|
||||||
|
USE [EsbZertifikatManager];
|
||||||
|
GO
|
||||||
|
|
||||||
|
SET ANSI_NULLS ON;
|
||||||
|
GO
|
||||||
|
|
||||||
|
SET QUOTED_IDENTIFIER ON;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Company */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[Company] AS company
|
||||||
|
WHERE company.[CompanyCode] = N'DE'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[Company]
|
||||||
|
(
|
||||||
|
[CompanyCode],
|
||||||
|
[CompanyName],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(N'DE', N'ZIEHL-ABEGG Deutschland', 1, SYSUTCDATETIME(), SUSER_SNAME());
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[Company] AS company
|
||||||
|
WHERE company.[CompanyCode] = N'PL'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[Company]
|
||||||
|
(
|
||||||
|
[CompanyCode],
|
||||||
|
[CompanyName],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(N'PL', N'ZIEHL-ABEGG Polen', 1, SYSUTCDATETIME(), SUSER_SNAME());
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[Company] AS company
|
||||||
|
WHERE company.[CompanyCode] = N'AT'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[Company]
|
||||||
|
(
|
||||||
|
[CompanyCode],
|
||||||
|
[CompanyName],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(N'AT', N'ZIEHL-ABEGG Oesterreich', 1, SYSUTCDATETIME(), SUSER_SNAME());
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Environment */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[Environment] AS environment
|
||||||
|
WHERE environment.[EnvironmentCode] = N'TEST2'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[Environment]
|
||||||
|
(
|
||||||
|
[EnvironmentCode],
|
||||||
|
[EnvironmentName],
|
||||||
|
[SortOrder],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(N'TEST2', N'Test 2', 1, 1, SYSUTCDATETIME(), SUSER_SNAME());
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[Environment] AS environment
|
||||||
|
WHERE environment.[EnvironmentCode] = N'TEST'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[Environment]
|
||||||
|
(
|
||||||
|
[EnvironmentCode],
|
||||||
|
[EnvironmentName],
|
||||||
|
[SortOrder],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(N'TEST', N'Test', 2, 1, SYSUTCDATETIME(), SUSER_SNAME());
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[Environment] AS environment
|
||||||
|
WHERE environment.[EnvironmentCode] = N'PROD'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[Environment]
|
||||||
|
(
|
||||||
|
[EnvironmentCode],
|
||||||
|
[EnvironmentName],
|
||||||
|
[SortOrder],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(N'PROD', N'Production', 3, 1, SYSUTCDATETIME(), SUSER_SNAME());
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* TEST-System: DE-Test + ct-ZADBService + CertificateTarget */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
DECLARE @EnvironmentIdTest [int];
|
||||||
|
DECLARE @CompanyIdDe [int];
|
||||||
|
DECLARE @SonicConnectionIdDeTest [int];
|
||||||
|
DECLARE @SonicContainerId [int];
|
||||||
|
|
||||||
|
SELECT @EnvironmentIdTest = environment.[EnvironmentId]
|
||||||
|
FROM [dbo].[Environment] AS environment
|
||||||
|
WHERE environment.[EnvironmentCode] = N'TEST';
|
||||||
|
|
||||||
|
SELECT @CompanyIdDe = company.[CompanyId]
|
||||||
|
FROM [dbo].[Company] AS company
|
||||||
|
WHERE company.[CompanyCode] = N'DE';
|
||||||
|
|
||||||
|
IF @EnvironmentIdTest IS NULL
|
||||||
|
OR @CompanyIdDe IS NULL
|
||||||
|
BEGIN
|
||||||
|
THROW 50002,
|
||||||
|
N'Seed unvollstaendig: Environment TEST oder Company DE fehlt.',
|
||||||
|
1;
|
||||||
|
END;
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[SonicConnection] AS connection
|
||||||
|
WHERE connection.[ConnectionName] = N'DE-Test'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[SonicConnection]
|
||||||
|
(
|
||||||
|
[EnvironmentId],
|
||||||
|
[ConnectionName],
|
||||||
|
[ManagementHost],
|
||||||
|
[ManagementPort],
|
||||||
|
[DomainName],
|
||||||
|
[ConnectionProtocol],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@EnvironmentIdTest,
|
||||||
|
N'DE-Test',
|
||||||
|
N'dekun-painwbdet',
|
||||||
|
13070,
|
||||||
|
N'proalpha-test',
|
||||||
|
N'tcp',
|
||||||
|
1,
|
||||||
|
SYSUTCDATETIME(),
|
||||||
|
SUSER_SNAME()
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
|
||||||
|
SELECT @SonicConnectionIdDeTest = connection.[SonicConnectionId]
|
||||||
|
FROM [dbo].[SonicConnection] AS connection
|
||||||
|
WHERE connection.[ConnectionName] = N'DE-Test';
|
||||||
|
|
||||||
|
IF @SonicConnectionIdDeTest IS NULL
|
||||||
|
BEGIN
|
||||||
|
THROW 50003,
|
||||||
|
N'Seed fehlgeschlagen: SonicConnection DE-Test nicht gefunden.',
|
||||||
|
1;
|
||||||
|
END;
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[SonicContainer] AS container
|
||||||
|
WHERE container.[SonicConnectionId] = @SonicConnectionIdDeTest
|
||||||
|
AND container.[ContainerName] = N'ct-ZADBService'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[SonicContainer]
|
||||||
|
(
|
||||||
|
[CompanyId],
|
||||||
|
[SonicConnectionId],
|
||||||
|
[ContainerName],
|
||||||
|
[ContainerDisplayName],
|
||||||
|
[RestartTimeoutSeconds],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@CompanyIdDe,
|
||||||
|
@SonicConnectionIdDeTest,
|
||||||
|
N'ct-ZADBService',
|
||||||
|
N'ZADBService',
|
||||||
|
180,
|
||||||
|
1,
|
||||||
|
SYSUTCDATETIME(),
|
||||||
|
SUSER_SNAME()
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
|
||||||
|
SELECT @SonicContainerId = container.[SonicContainerId]
|
||||||
|
FROM [dbo].[SonicContainer] AS container
|
||||||
|
WHERE container.[SonicConnectionId] = @SonicConnectionIdDeTest
|
||||||
|
AND container.[ContainerName] = N'ct-ZADBService';
|
||||||
|
|
||||||
|
IF @SonicContainerId IS NULL
|
||||||
|
BEGIN
|
||||||
|
THROW 50004,
|
||||||
|
N'Seed fehlgeschlagen: SonicContainer ct-ZADBService nicht gefunden.',
|
||||||
|
1;
|
||||||
|
END;
|
||||||
|
|
||||||
|
/* Demo-Pfad für Zertifikat-Austausch (UNC server.p12) – immer sicherstellen */
|
||||||
|
DECLARE @DemoDirectory [nvarchar](500) =
|
||||||
|
N'\\dekun-painwbdet\e$\proalpha-inwb\test\Containers\proalpha-test.ct-ZADBService\certs';
|
||||||
|
DECLARE @DemoFileName [nvarchar](260) = N'server.p12';
|
||||||
|
|
||||||
|
UPDATE [dbo].[CertificateTarget]
|
||||||
|
SET
|
||||||
|
[TargetDirectory] = @DemoDirectory,
|
||||||
|
[TargetFileName] = @DemoFileName,
|
||||||
|
[BackupEnabled] = 1,
|
||||||
|
[BackupDirectoryName] = N'Backup',
|
||||||
|
[IsActive] = 1,
|
||||||
|
[ModifiedDateTime] = SYSUTCDATETIME(),
|
||||||
|
[ModifiedBy] = SUSER_SNAME()
|
||||||
|
WHERE [SonicContainerId] = @SonicContainerId
|
||||||
|
AND
|
||||||
|
(
|
||||||
|
[TargetFileName] IN (N'server.crt', N'server.p12')
|
||||||
|
OR [TargetDirectory] LIKE N'C:\ESB\Certificates%'
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[CertificateTarget] AS target
|
||||||
|
WHERE target.[SonicContainerId] = @SonicContainerId
|
||||||
|
AND target.[TargetDirectory] = @DemoDirectory
|
||||||
|
AND target.[TargetFileName] = @DemoFileName
|
||||||
|
AND target.[IsActive] = 1
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[CertificateTarget]
|
||||||
|
(
|
||||||
|
[SonicContainerId],
|
||||||
|
[TargetDirectory],
|
||||||
|
[TargetFileName],
|
||||||
|
[BackupEnabled],
|
||||||
|
[BackupDirectoryName],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@SonicContainerId,
|
||||||
|
@DemoDirectory,
|
||||||
|
@DemoFileName,
|
||||||
|
1,
|
||||||
|
N'Backup',
|
||||||
|
1,
|
||||||
|
SYSUTCDATETIME(),
|
||||||
|
SUSER_SNAME()
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* Kontrolle */
|
||||||
|
SELECT
|
||||||
|
company.[CompanyCode],
|
||||||
|
environment.[EnvironmentCode],
|
||||||
|
connection.[ConnectionName],
|
||||||
|
connection.[ManagementHost],
|
||||||
|
connection.[ManagementPort],
|
||||||
|
connection.[DomainName],
|
||||||
|
container.[ContainerName],
|
||||||
|
target.[TargetDirectory],
|
||||||
|
target.[TargetFileName]
|
||||||
|
FROM [dbo].[SonicConnection] AS connection
|
||||||
|
INNER JOIN [dbo].[Environment] AS environment
|
||||||
|
ON environment.[EnvironmentId] = connection.[EnvironmentId]
|
||||||
|
INNER JOIN [dbo].[SonicContainer] AS container
|
||||||
|
ON container.[SonicConnectionId] = connection.[SonicConnectionId]
|
||||||
|
INNER JOIN [dbo].[Company] AS company
|
||||||
|
ON company.[CompanyId] = container.[CompanyId]
|
||||||
|
LEFT JOIN [dbo].[CertificateTarget] AS target
|
||||||
|
ON target.[SonicContainerId] = container.[SonicContainerId]
|
||||||
|
ORDER BY
|
||||||
|
environment.[SortOrder],
|
||||||
|
company.[CompanyCode],
|
||||||
|
connection.[ConnectionName];
|
||||||
|
GO
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
COMMENT: Stellt den Demo-Zertifikatspfad für DE-Test / ct-ZADBService sicher.
|
||||||
|
Kann allein gegen eine bestehende EsbZertifikatManager-DB laufen
|
||||||
|
(ohne Drop/Create). EnvironmentCode muss TEST sein (appsettings).
|
||||||
|
*/
|
||||||
|
USE [EsbZertifikatManager];
|
||||||
|
GO
|
||||||
|
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
SET XACT_ABORT ON;
|
||||||
|
|
||||||
|
DECLARE @SonicContainerId [int];
|
||||||
|
DECLARE @TargetDirectory [nvarchar](500) =
|
||||||
|
N'\\dekun-painwbdet\e$\proalpha-inwb\test\Containers\proalpha-test.ct-ZADBService\certs';
|
||||||
|
DECLARE @TargetFileName [nvarchar](260) = N'server.p12';
|
||||||
|
|
||||||
|
SELECT @SonicContainerId = container.[SonicContainerId]
|
||||||
|
FROM [dbo].[SonicContainer] AS container
|
||||||
|
INNER JOIN [dbo].[SonicConnection] AS connection
|
||||||
|
ON connection.[SonicConnectionId] = container.[SonicConnectionId]
|
||||||
|
INNER JOIN [dbo].[Environment] AS environment
|
||||||
|
ON environment.[EnvironmentId] = connection.[EnvironmentId]
|
||||||
|
WHERE environment.[EnvironmentCode] = N'TEST'
|
||||||
|
AND connection.[ConnectionName] = N'DE-Test'
|
||||||
|
AND container.[ContainerName] = N'ct-ZADBService';
|
||||||
|
|
||||||
|
IF @SonicContainerId IS NULL
|
||||||
|
BEGIN
|
||||||
|
THROW 50010,
|
||||||
|
N'Demo-Pfad: Container ct-ZADBService / DE-Test / TEST nicht gefunden. Zuerst 03_Seed.sql ausführen.',
|
||||||
|
1;
|
||||||
|
END;
|
||||||
|
|
||||||
|
/* Alte Platzhalter am gleichen Container auf Demo-Pfad umbiegen */
|
||||||
|
UPDATE [dbo].[CertificateTarget]
|
||||||
|
SET
|
||||||
|
[TargetDirectory] = @TargetDirectory,
|
||||||
|
[TargetFileName] = @TargetFileName,
|
||||||
|
[BackupEnabled] = 1,
|
||||||
|
[BackupDirectoryName] = N'Backup',
|
||||||
|
[IsActive] = 1,
|
||||||
|
[ModifiedDateTime] = SYSUTCDATETIME(),
|
||||||
|
[ModifiedBy] = SUSER_SNAME()
|
||||||
|
WHERE [SonicContainerId] = @SonicContainerId
|
||||||
|
AND
|
||||||
|
(
|
||||||
|
[TargetFileName] IN (N'server.crt', N'server.p12')
|
||||||
|
OR [TargetDirectory] LIKE N'C:\ESB\Certificates%'
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[CertificateTarget] AS target
|
||||||
|
WHERE target.[SonicContainerId] = @SonicContainerId
|
||||||
|
AND target.[TargetDirectory] = @TargetDirectory
|
||||||
|
AND target.[TargetFileName] = @TargetFileName
|
||||||
|
AND target.[IsActive] = 1
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [dbo].[CertificateTarget]
|
||||||
|
(
|
||||||
|
[SonicContainerId],
|
||||||
|
[TargetDirectory],
|
||||||
|
[TargetFileName],
|
||||||
|
[BackupEnabled],
|
||||||
|
[BackupDirectoryName],
|
||||||
|
[IsActive],
|
||||||
|
[CreationDateTime],
|
||||||
|
[CreatedBy]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@SonicContainerId,
|
||||||
|
@TargetDirectory,
|
||||||
|
@TargetFileName,
|
||||||
|
1,
|
||||||
|
N'Backup',
|
||||||
|
1,
|
||||||
|
SYSUTCDATETIME(),
|
||||||
|
SUSER_SNAME()
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
target.[CertificateTargetId],
|
||||||
|
company.[CompanyCode],
|
||||||
|
environment.[EnvironmentCode],
|
||||||
|
connection.[ConnectionName],
|
||||||
|
container.[ContainerName],
|
||||||
|
target.[TargetDirectory],
|
||||||
|
target.[TargetFileName],
|
||||||
|
target.[IsActive]
|
||||||
|
FROM [dbo].[CertificateTarget] AS target
|
||||||
|
INNER JOIN [dbo].[SonicContainer] AS container
|
||||||
|
ON container.[SonicContainerId] = target.[SonicContainerId]
|
||||||
|
INNER JOIN [dbo].[SonicConnection] AS connection
|
||||||
|
ON connection.[SonicConnectionId] = container.[SonicConnectionId]
|
||||||
|
INNER JOIN [dbo].[Environment] AS environment
|
||||||
|
ON environment.[EnvironmentId] = connection.[EnvironmentId]
|
||||||
|
INNER JOIN [dbo].[Company] AS company
|
||||||
|
ON company.[CompanyId] = container.[CompanyId]
|
||||||
|
WHERE container.[SonicContainerId] = @SonicContainerId;
|
||||||
|
|
||||||
|
PRINT N'Demo-Pfad ist aktiv: \\dekun-painwbdet\e$\...\certs\server.p12';
|
||||||
|
GO
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
DATABASE: [EsbZertifikatManager]
|
||||||
|
OBJECTNAME: SonicContainer.CertificateCheckUrl
|
||||||
|
ACTION: ALTER
|
||||||
|
IDX: 005
|
||||||
|
DATE: 2026-07-31
|
||||||
|
USER: CUR
|
||||||
|
COMMENT: URL/Host:Port fuer TLS-Zertifikatspruefung (curl-aehnlich) am Container.
|
||||||
|
*/
|
||||||
|
USE [EsbZertifikatManager];
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF COL_LENGTH(N'dbo.SonicContainer', N'CertificateCheckUrl') IS NULL
|
||||||
|
BEGIN
|
||||||
|
IF COL_LENGTH(N'dbo.SonicContainer', N'vertifiaktechecurl') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXEC sp_rename
|
||||||
|
N'dbo.SonicContainer.vertifiaktechecurl',
|
||||||
|
N'CertificateCheckUrl',
|
||||||
|
N'COLUMN';
|
||||||
|
END
|
||||||
|
ELSE IF COL_LENGTH(N'dbo.SonicContainer', N'VerifyCertificateCheckCurl') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXEC sp_rename
|
||||||
|
N'dbo.SonicContainer.VerifyCertificateCheckCurl',
|
||||||
|
N'CertificateCheckUrl',
|
||||||
|
N'COLUMN';
|
||||||
|
END
|
||||||
|
ELSE IF COL_LENGTH(N'dbo.SonicContainer', N'CertificateCheckCurl') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXEC sp_rename
|
||||||
|
N'dbo.SonicContainer.CertificateCheckCurl',
|
||||||
|
N'CertificateCheckUrl',
|
||||||
|
N'COLUMN';
|
||||||
|
END
|
||||||
|
ELSE IF COL_LENGTH(N'dbo.SonicContainer', N'ZertifikatCheckCurl') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXEC sp_rename
|
||||||
|
N'dbo.SonicContainer.ZertifikatCheckCurl',
|
||||||
|
N'CertificateCheckUrl',
|
||||||
|
N'COLUMN';
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[SonicContainer]
|
||||||
|
ADD [CertificateCheckUrl] [nvarchar](500) NULL;
|
||||||
|
END
|
||||||
|
END;
|
||||||
|
GO
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,41 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Importiert das Always-Encrypted-Zertifikat in CurrentUser\My.
|
||||||
|
.NOTES
|
||||||
|
PFX-Passwort (nur Dev): ESB-AE-Dev-Only!
|
||||||
|
Thumbprint muss mit appsettings AlwaysEncrypted:CertificateThumbprint uebereinstimmen.
|
||||||
|
#>
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$pfxPath = Join-Path $scriptDir "ESB_ZertifikatManager_AE.pfx"
|
||||||
|
$thumbPath = Join-Path $scriptDir "thumbprint.txt"
|
||||||
|
|
||||||
|
if (-not (Test-Path $pfxPath)) {
|
||||||
|
throw "PFX nicht gefunden: $pfxPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
$expectedThumb = (Get-Content -Path $thumbPath -Raw).Trim()
|
||||||
|
$password = ConvertTo-SecureString -String "ESB-AE-Dev-Only!" -Force -AsPlainText
|
||||||
|
|
||||||
|
$imported = Import-PfxCertificate `
|
||||||
|
-FilePath $pfxPath `
|
||||||
|
-CertStoreLocation "Cert:\CurrentUser\My" `
|
||||||
|
-Password $password `
|
||||||
|
-Exportable
|
||||||
|
|
||||||
|
Write-Host "Zertifikat importiert:"
|
||||||
|
Write-Host " Subject : $($imported.Subject)"
|
||||||
|
Write-Host " Thumbprint : $($imported.Thumbprint)"
|
||||||
|
Write-Host " PrivateKey : $($imported.HasPrivateKey)"
|
||||||
|
|
||||||
|
if (-not [string]::Equals(
|
||||||
|
$imported.Thumbprint,
|
||||||
|
$expectedThumb,
|
||||||
|
[System.StringComparison]::OrdinalIgnoreCase)) {
|
||||||
|
Write-Warning "Thumbprint weicht von thumbprint.txt ab ($expectedThumb)."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "appsettings.json AlwaysEncrypted:CertificateThumbprint ="
|
||||||
|
Write-Host $imported.Thumbprint
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0x017000630075007200720065006E00740075007300650072002F006D0079002F003900350065003900300036006300630063003900630035006600320032003700390036003900380037003700630066003300620064003400360064003200610064003700310036003800380035003300000063875F8F9E6CA28F5C6AB80295C9526F00C6708A9E3D8EEF172189C88A445F21D08CA11BCA77B3460C8F109A0C30B3CF9E3E7007BC4EACE9563591014C6FDE01F15E72F6992C7862F1C22C7E69D00418B26E700F95112E870A17C4510891AE687B522F71A58E70DFAE211F6B233628FB17E87F2837726E4801CB45A2357CE57194239950B19930CE5C8B7AF9A0CFF86AFA52DF97D392C77DE694ECA336A0DBCB6E5157AF8713636D90D121BDA1CBC4909C3A1102BE9FE561321FED761A672D2E534A5C94391704C79D007804DE628961BB2A4744D88E975C8287459F2D7D42F643078CEFF8842DFE4C826CF4B47BE2A59DC65D495F39BE45AA583E2784DE039F
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ESB-AE-Dev-Only!
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
95E906CCC9C5F227969877CF3BD46D2AD7168853
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# Datenbank EsbZertifikatManager
|
||||||
|
|
||||||
|
Schlankes Schema (6 Tabellen) + Always Encrypted.
|
||||||
|
|
||||||
|
## Einmalige Reihenfolge
|
||||||
|
|
||||||
|
1. **Zertifikat importieren** (auf jedem Client-PC):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd Database\AlwaysEncrypted
|
||||||
|
.\Install-Certificate.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
- PFX: `ESB_ZertifikatManager_AE.pfx`
|
||||||
|
- Passwort: siehe `pfx-password.txt` (`ESB-AE-Dev-Only!`)
|
||||||
|
- Thumbprint: `95E906CCC9C5F227969877CF3BD46D2AD7168853`
|
||||||
|
|
||||||
|
2. In SSMS:
|
||||||
|
|
||||||
|
1. `00_DropExisting.sql`
|
||||||
|
2. `01_CreateDatabase.sql`
|
||||||
|
3. `02_CreateSchema.sql` (CMK/CEK + Tabellen)
|
||||||
|
4. `03_Seed.sql` (**komplett**: Companies, Environments, DE-Test, Container, Target)
|
||||||
|
|
||||||
|
3. App starten → Einstellungen → System **DE-Test** → Benutzerprofil anlegen → Übernehmen.
|
||||||
|
|
||||||
|
## Seed-Inhalt (`03_Seed.sql`)
|
||||||
|
|
||||||
|
| Objekt | Werte |
|
||||||
|
|---|---|
|
||||||
|
| Company | DE, PL, AT |
|
||||||
|
| Environment | TEST2, TEST, PROD |
|
||||||
|
| SonicConnection | DE-Test → `tcp://dekun-painwbdet:13070`, Domain `proalpha-test`, Env TEST |
|
||||||
|
| SonicContainer | ct-ZADBService (Company DE) |
|
||||||
|
| CertificateTarget | Demo-UNC `\\dekun-painwbdet\e$\...\certs\server.p12` |
|
||||||
|
| SonicCredential | **nicht** geseedet (Setup-Wizard / AE) |
|
||||||
|
|
||||||
|
## Demo-Pfad nachziehen (bestehende DB)
|
||||||
|
|
||||||
|
Wenn der alte Platzhalter noch in SQL liegt:
|
||||||
|
|
||||||
|
```text
|
||||||
|
04_EnsureDemoPath.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
Zusätzlich: in `appsettings.json` ist `"Demo": { "Enabled": true, ... }` –
|
||||||
|
die App zeigt den Demo-Pfad auch ohne SQL-Update in der Zielliste (Kopieren testen).
|
||||||
|
|
||||||
|
## TLS-Prüf-URL am Container (bestehende DB)
|
||||||
|
|
||||||
|
Für Live-Zertifikatsprüfung (curl-ähnlich) die Spalte nachziehen:
|
||||||
|
|
||||||
|
```text
|
||||||
|
05_AddCertificateCheckUrl.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach in Einstellungen → Container die Prüf-URL setzen, z. B. `https://server:8443`.
|
||||||
|
|
||||||
|
## Always Encrypted
|
||||||
|
|
||||||
|
| Objekt | Name |
|
||||||
|
|---|---|
|
||||||
|
| CMK | `CMK_ESB_ZertifikatManager` → `CurrentUser/My/95E906CCC9C5F227969877CF3BD46D2AD7168853` |
|
||||||
|
| CEK | `CEK_ESB_ZertifikatManager` |
|
||||||
|
| Spalten | `SonicCredential.CredentialUserName`, `CredentialSecret` |
|
||||||
|
|
||||||
|
Connection String braucht `Column Encryption Setting=Enabled`.
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,437 @@
|
|||||||
|
# Dateiübersicht nach Ordnerstruktur
|
||||||
|
|
||||||
|
Was **jede einzelne Datei** macht – inkl. Sonic Management API.
|
||||||
|
Projektroot der App: `ZA.CoreService.ESBCertificateManager/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. Gesamtbild (wer ruft wen)
|
||||||
|
|
||||||
|
```text
|
||||||
|
Program.cs
|
||||||
|
└─ Form1.cs (UI)
|
||||||
|
├─ Zertifikat lesen → CertificateInfo
|
||||||
|
├─ Ziele ← SonicContainerDiscovery (KnownContainers / Live)
|
||||||
|
└─ Neustart
|
||||||
|
└─ DeploymentOrchestrator
|
||||||
|
└─ RestartExecutor
|
||||||
|
└─ SonicManagementClient
|
||||||
|
└─ SonicMfApiExecutor
|
||||||
|
└─ java + Tools/SonicMfContainerTool
|
||||||
|
└─ Sonic JARs (externe API)
|
||||||
|
└─ Domain Manager tcp://…:13070
|
||||||
|
```
|
||||||
|
|
||||||
|
Konfiguration: `appsettings.json` → `AppSettingsLoader` → `AppSettings` / `SonicConnection`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Projektroot (`ZA.CoreService.ESBCertificateManager/`)
|
||||||
|
|
||||||
|
### `Program.cs`
|
||||||
|
Einstiegspunkt der WinForms-App.
|
||||||
|
`ApplicationConfiguration.Initialize()` + `Application.Run(new Form1())`.
|
||||||
|
Keine Business-Logik.
|
||||||
|
|
||||||
|
### `Form1.cs`
|
||||||
|
**Gesamte Benutzeroberfläche** (Dark-Design, Sidebar, Zertifikatskarte, Grid, Buttons).
|
||||||
|
Wichtige Aufgaben:
|
||||||
|
|
||||||
|
| Bereich | Was passiert |
|
||||||
|
|---------|----------------|
|
||||||
|
| Zertifikat | Dateidialog → `X509Certificate2` → Subject/Issuer/Fingerprint/Gültigkeit anzeigen |
|
||||||
|
| Ziele | Grid aus `KnownContainers`; Button „Container laden“ → Live-Discovery |
|
||||||
|
| Prüfung | `ValidateRestartOnly` (ContainerName + SonicConnectionName) |
|
||||||
|
| Neustart | Bestätigung → `DeploymentOrchestrator.RestartOnlyAsync` → Ergebnisdialog |
|
||||||
|
| Status | Statuszeile, Fortschritt im Grid, SonicHome/Java-Anzeige |
|
||||||
|
|
||||||
|
Deploy-Button ist vorhanden im Layout, aber **ausgeblendet/deaktiviert**.
|
||||||
|
|
||||||
|
### `Form1.Designer.cs`
|
||||||
|
WinForms-Designer-Stub. `InitializeComponent()` ist praktisch leer – das Layout wird in `Form1.cs` per Code gebaut.
|
||||||
|
|
||||||
|
### `Form1.resx`
|
||||||
|
Ressourcen-Datei zum Formular (Standard WinForms). Kaum eigene Inhalte.
|
||||||
|
|
||||||
|
### `appsettings.json`
|
||||||
|
**Laufzeitkonfiguration** – die „Online“-Quelle für Sonic:
|
||||||
|
|
||||||
|
- `LogDirectory` – wohin Logs geschrieben werden
|
||||||
|
- `SonicConnections[]` – Domain, URL, User/Pass, Java, Lib-Pfad, **KnownContainers**
|
||||||
|
|
||||||
|
Beispiel-Verbindung `DE-Test` → Domain `proalpha-test`, Container `ct-ZADBService`.
|
||||||
|
|
||||||
|
### `ZA.CoreService.ESBCertificateManager.csproj`
|
||||||
|
Projektdatei: .NET 8 WinForms, NuGet (Configuration), welche Dateien nach `bin` kopiert werden (`appsettings`, `Tools`, `Assets`, `Docs`).
|
||||||
|
|
||||||
|
### `ZA.CoreService.ESBCertificateManager.csproj.user`
|
||||||
|
Lokale Visual-Studio-Benutzereinstellungen (Debugger usw.). Nicht fachlich relevant.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Ordner `Configuration/`
|
||||||
|
|
||||||
|
### `AppSettingsLoader.cs`
|
||||||
|
Liest `appsettings.json` über `Microsoft.Extensions.Configuration` und bindet sie an `AppSettings`.
|
||||||
|
Sucht zuerst im Output-Verzeichnis (`AppContext.BaseDirectory`), sonst im Arbeitsverzeichnis.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Ordner `Models/` (Datenstrukturen)
|
||||||
|
|
||||||
|
### `AppSettings.cs`
|
||||||
|
Root-Settings-Objekt:
|
||||||
|
|
||||||
|
- `LogDirectory`
|
||||||
|
- `List<SonicConnection> SonicConnections`
|
||||||
|
|
||||||
|
### `SonicConnection.cs`
|
||||||
|
Eine Sonic-/SMC-Verbindung:
|
||||||
|
|
||||||
|
| Property | Bedeutung |
|
||||||
|
|----------|-----------|
|
||||||
|
| `Name` | Alias, z. B. `DE-Test` |
|
||||||
|
| `DomainName` | z. B. `proalpha-test` |
|
||||||
|
| `ConnectionUrl` | `tcp://host:port` wie SMC |
|
||||||
|
| `Username` / `Password` | SMC-Login |
|
||||||
|
| `SonicHome` / `JavaHome` / `JavaPath` | Pfade |
|
||||||
|
| `MfClientLibPath` | Ordner mit Client-JARs |
|
||||||
|
| `KnownContainers` | Containerliste für die UI |
|
||||||
|
| `TimeoutSeconds` / `PostRestartDelaySeconds` | Timeouts |
|
||||||
|
| `ManagementModeDisplay` | Anzeigetext „MfApi …“ |
|
||||||
|
|
||||||
|
Enthält noch ältere Hilfsfelder/Enums (WinRM etc.) aus der Historie – der aktive Client nutzt nur den MfApi-Pfad.
|
||||||
|
|
||||||
|
### `DeploymentTarget.cs`
|
||||||
|
Ein **Ziel** im Grid (ein Container zum Neustarten):
|
||||||
|
|
||||||
|
- `Id`, `Name`, `Environment`, `IsActive`
|
||||||
|
- `ContainerName` – z. B. `ct-ZADBService`
|
||||||
|
- `SonicConnectionName` – Verweis auf `SonicConnection.Name`
|
||||||
|
- `RestartType` – für Neustart typischerweise `SonicContainer`
|
||||||
|
- Weitere Felder (TargetDirectory, Tls*, Xapi*) können noch im Model stehen, werden für den aktuellen Neustart-Pfad nicht mehr aktiv genutzt
|
||||||
|
|
||||||
|
### `CertificateInfo.cs`
|
||||||
|
Ergebnis der lokalen Zertifikatsprüfung:
|
||||||
|
|
||||||
|
- Subject, Issuer
|
||||||
|
- FingerprintSha256
|
||||||
|
- ValidFrom / ValidUntil
|
||||||
|
- IsCurrentlyValid
|
||||||
|
|
||||||
|
### `DeploymentRunResult.cs`
|
||||||
|
Ergebnisstrukturen nach einem Lauf:
|
||||||
|
|
||||||
|
- `DeploymentRunResult` – Gesamtlauf (Liste Zielergebnisse, Zeiten, OverallSuccess)
|
||||||
|
- `TargetStepResult` – ein Ziel (Success, StatusText, Detail, …)
|
||||||
|
- `ValidationIssue` / `PreflightValidationResult` – Vorabprüfung
|
||||||
|
- `TargetProgressUpdate` – Fortschritt für die UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Ordner `Services/` (Geschäftslogik)
|
||||||
|
|
||||||
|
### `DeploymentOrchestrator.cs`
|
||||||
|
Orchestriert **nur Neustart** (kein Deploy/TLS):
|
||||||
|
|
||||||
|
1. `ValidateRestartOnly` → Preflight
|
||||||
|
2. `RestartOnlyAsync` → für jedes Ziel `RestartExecutor`
|
||||||
|
3. Schreibt Log über `RunLogger`
|
||||||
|
4. Meldet Progress an die UI
|
||||||
|
|
||||||
|
### `RestartExecutor.cs`
|
||||||
|
Mappt ein `DeploymentTarget` auf die passende `SonicConnection` und ruft
|
||||||
|
`SonicManagementClient.RestartContainerAsync(containerName)` auf.
|
||||||
|
|
||||||
|
### `PreflightValidator.cs`
|
||||||
|
Prüft vor dem Neustart:
|
||||||
|
|
||||||
|
- mindestens ein Ziel ausgewählt
|
||||||
|
- `ContainerName` gesetzt
|
||||||
|
- `SonicConnectionName` gesetzt
|
||||||
|
|
||||||
|
### `SonicManagementClient.cs`
|
||||||
|
**Dünne Fassade** zur MfApi (kein WinRM/HTTP/XApi mehr):
|
||||||
|
|
||||||
|
| Methode | Zweck |
|
||||||
|
|---------|--------|
|
||||||
|
| `CheckConnectionAsync` | Ping / Soft-Fallback KnownContainers+TCP |
|
||||||
|
| `GetContainersAsync` | Liste von Containern (API oder KnownContainers) |
|
||||||
|
| `RestartContainerAsync` | Neustart + Wartezeit `PostRestartDelaySeconds` |
|
||||||
|
|
||||||
|
### `SonicMfApiExecutor.cs`
|
||||||
|
**Brücke C# → Java.** Das ist der technische Kern „unsere Seite der API“:
|
||||||
|
|
||||||
|
1. Java 8+ finden (`JavaPath` / Suche)
|
||||||
|
2. Classpath aus allen JARs unter `MfClientLibPath` / `SonicHome\lib` bauen
|
||||||
|
3. `Tools\SonicMfContainerTool.class` laden (Version prüfen)
|
||||||
|
4. Prozess starten: `java -cp … SonicMfContainerTool ping|list|restart …`
|
||||||
|
5. Passwort über Env `ESB_SONIC_PASSWORD`
|
||||||
|
6. Erfolg bei Neustart nur wenn Output `OK:RestartInvoked` enthält
|
||||||
|
|
||||||
|
Öffentliche Methoden:
|
||||||
|
|
||||||
|
- `TestConnectionAsync` → Tool-Befehl `ping`
|
||||||
|
- `ListContainersAsync` → `list`
|
||||||
|
- `RestartAsync` → `restart`
|
||||||
|
- `ResolveRuntimePaths` → für Statuszeile in der UI
|
||||||
|
|
||||||
|
### `SonicContainerDiscovery.cs`
|
||||||
|
Baut die Zielliste:
|
||||||
|
|
||||||
|
- `BuildTargetsFromConfig()` – aus `KnownContainers` in appsettings (Start der App)
|
||||||
|
- `DiscoverAsync()` – live per `SonicManagementClient` listen + mit KnownContainers mergen
|
||||||
|
|
||||||
|
### `RunLogger.cs`
|
||||||
|
Schreibt Textdateien unter `LogDirectory` (ein Log pro Lauf). Redaktiert ggf. secrets-ähnliche Strings.
|
||||||
|
|
||||||
|
### `PathResolver.cs`
|
||||||
|
Löst relative Pfade gegen `AppContext.BaseDirectory` auf (z. B. für Logs).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Ordner `Tools/` (Java + Sonic-API)
|
||||||
|
|
||||||
|
Hier liegt der **eigentliche Aufruf der Sonic Management Application API**.
|
||||||
|
|
||||||
|
### `SonicMfContainerTool.java`
|
||||||
|
Kleines Java-Hilfsprogramm (Reflection, kompilierbar ohne Sonic-JARs auf dem Compile-Classpath).
|
||||||
|
|
||||||
|
**Befehle:**
|
||||||
|
|
||||||
|
| Befehl | Bedeutung |
|
||||||
|
|--------|-----------|
|
||||||
|
| `ping` | Connect + Agenten zählen |
|
||||||
|
| `list` | Containernamen der Domain ausgeben |
|
||||||
|
| `restart` | Lifecycle auf einem Container |
|
||||||
|
|
||||||
|
**Connect (API-Login):**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Hashtable: ConnectionURLs, DefaultUser, DefaultPassword
|
||||||
|
→ new JMSConnectorAddress(env)
|
||||||
|
→ new JMSConnectorClient().connect(address, timeout)
|
||||||
|
```
|
||||||
|
|
||||||
|
Klassen aus Sonic-JARs:
|
||||||
|
|
||||||
|
- `com.sonicsw.mf.jmx.client.JMSConnectorAddress`
|
||||||
|
- `com.sonicsw.mf.jmx.client.JMSConnectorClient`
|
||||||
|
|
||||||
|
**Neustart – Ziel:**
|
||||||
|
|
||||||
|
```text
|
||||||
|
ObjectName = {Domain}.{Container}:ID=AGENT
|
||||||
|
Beispiel: proalpha-test.ct-ZADBService:ID=AGENT
|
||||||
|
```
|
||||||
|
|
||||||
|
**Neustart – Wege (nacheinander):**
|
||||||
|
|
||||||
|
1. **MBean.invoke** `"stop"` / `"restart"` / `"shutdown"` ← bevorzugt remote
|
||||||
|
2. **IAgentProxy** über `MFProxyFactory.createAgentProxy` (oft *unbounded*-Fehler)
|
||||||
|
3. **DomainManager**-Operationen wie `restartContainer` / `stopContainer`
|
||||||
|
|
||||||
|
Erfolg:
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK:RestartInvoked method=…
|
||||||
|
INFO:ToolVersion=…
|
||||||
|
```
|
||||||
|
|
||||||
|
### `SonicMfContainerTool.class` / `SonicMfContainerTool$Args.class`
|
||||||
|
Vorkompilierter Java-8-Bytecode (major 52), den die App zur Laufzeit nutzt (ohne `javac` auf dem Zielrechner).
|
||||||
|
|
||||||
|
### `SonicMfContainerTool.jar`
|
||||||
|
Optional verpackte Variante des Tools (Classpath-Alternative).
|
||||||
|
|
||||||
|
### `verify-mfapi-classpath.bat`
|
||||||
|
Hilfsskript: prüft manuell, ob `JMSConnectorAddress` mit den JARs unter `C:\DEV\MQ10.0\lib` ladbar ist.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Ordner `Docs/` (Dokumentation)
|
||||||
|
|
||||||
|
### `Komplette-Erklaerung.md`
|
||||||
|
Schritt-für-Schritt: Zertifikat, Preflight, C#-Kette, Java-API, Fehlerbilder, Präsentationsskript.
|
||||||
|
|
||||||
|
### `Neustart-und-Management-API.md`
|
||||||
|
Kompaktere API-/Neustart-Erklärung inkl. „Was ist unnötig in der großen Sonic-API?“.
|
||||||
|
|
||||||
|
### `Dateiuebersicht-nach-Ordner.md`
|
||||||
|
Diese Datei – Katalog aller Projektdateien.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Ordner `Assets/`
|
||||||
|
|
||||||
|
### `logo.png`
|
||||||
|
UI-/Branding-Grafik.
|
||||||
|
|
||||||
|
### `TestCertificates/esb-test-cert.cer`
|
||||||
|
Beispiel-Zertifikat zum lokalen Test der Dateierkennung (kein Sonic-Deploy).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Ordner `Demo/`
|
||||||
|
|
||||||
|
### `Demo/Central/esb-cert.cer`
|
||||||
|
Weiteres Beispielzertifikat. Nicht Teil der Online-Neustart-Logik.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Sibling-Ordner `doc-10.0.10/` (neben der App, im Repo)
|
||||||
|
|
||||||
|
Produkt-Dokumentation Aurea CX Messenger / Sonic (nicht App-Code).
|
||||||
|
|
||||||
|
### `CXMessenger_2017_R3.htm`
|
||||||
|
Index der offiziellen Doku. Wichtiger Link:
|
||||||
|
|
||||||
|
- **Management Application API Reference** → `Docs2017/api/mgmt_api/`
|
||||||
|
Das ist die API-Familie, die SMC und unser Java-Tool nutzen.
|
||||||
|
|
||||||
|
### `Docs2017/api/analytics-offloader/…`
|
||||||
|
Javadoc-Stub für Analytics Offload – **nicht** der Neustart-Pfad.
|
||||||
|
|
||||||
|
### PDFs / vollständige `mgmt_api`
|
||||||
|
Im Index verlinkt; können im Checkout fehlen. Für Reviews idealerweise nachziehen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Die Sonic Management Application API – detailliert
|
||||||
|
|
||||||
|
### 10.1 Was „API“ hier bedeutet
|
||||||
|
|
||||||
|
Es ist **keine REST-URL der WinForms-App**.
|
||||||
|
Es ist die **Java Management Application API** von Sonic/Aurea:
|
||||||
|
|
||||||
|
- Transport: JMS zum Domain Manager (`tcp://host:port`)
|
||||||
|
- Steuerung: JMX-ähnliche ObjectNames + Operationen
|
||||||
|
- Authentifizierung: SMC-User/Pass
|
||||||
|
|
||||||
|
Dieselbe Schicht wie der Restart in der Sonic Management Console.
|
||||||
|
|
||||||
|
### 10.2 Welche Sonic-Klassen wir wirklich brauchen
|
||||||
|
|
||||||
|
| Klasse | Aufgabe |
|
||||||
|
|--------|---------|
|
||||||
|
| `JMSConnectorAddress` | Verbindungsparameter kapseln |
|
||||||
|
| `JMSConnectorClient` | Verbinden / disconnect / invoke / queryNames |
|
||||||
|
| `MFProxyFactory` | `createAgentProxy` |
|
||||||
|
| `IAgentProxy` | typisierte Methoden stop/restart/… (remote oft eingeschränkt) |
|
||||||
|
| ObjectName `domain.container:ID=AGENT` | Ziel-Agent |
|
||||||
|
|
||||||
|
Benötigte JARs typischerweise unter `C:\DEV\MQ10.0\lib`:
|
||||||
|
|
||||||
|
- `mgmt_client.jar`
|
||||||
|
- `mfcontext.jar`
|
||||||
|
- `sonic_Client.jar`
|
||||||
|
(+ weitere Abhängigkeiten im selben Ordner)
|
||||||
|
|
||||||
|
### 10.3 Warum die Sonic-API „viele unnötige Funktionen“ hat
|
||||||
|
|
||||||
|
Die Produkt-API deckt ab:
|
||||||
|
|
||||||
|
- Messaging (Queues, Topics, Durable Subs)
|
||||||
|
- Broker-Admin
|
||||||
|
- ESB
|
||||||
|
- Metrics / Notifications
|
||||||
|
- Config / Directory
|
||||||
|
|
||||||
|
**Für unseren Use-Case reichen 3 Schritte:** Connect → Agent finden → Stop/Restart.
|
||||||
|
|
||||||
|
Der Rest der JARs/Javadoc ist Produktumfang, nicht App-Feature.
|
||||||
|
|
||||||
|
### 10.4 „Unbounded client connector“
|
||||||
|
|
||||||
|
Remote-Clients (SMC, unser Tool) sind oft **unbounded**.
|
||||||
|
Dann schlägt `IAgentProxy.restart()` fehl mit:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Operation unsupported for unbounded client connector
|
||||||
|
```
|
||||||
|
|
||||||
|
Deshalb bevorzugt das Tool **MBean.invoke("stop")**.
|
||||||
|
Operativ entspricht das oft SMC-Restart: Stop + Auto-Relaunch durch Launch Daemon.
|
||||||
|
|
||||||
|
### 10.5 Sequenz eines Neustarts (API-Ebene)
|
||||||
|
|
||||||
|
```text
|
||||||
|
1. C#: RestartExecutor → SonicManagementClient → SonicMfApiExecutor
|
||||||
|
2. C#: startet java -cp Tools;lib\*.jar SonicMfContainerTool restart …
|
||||||
|
3. Java: JMSConnectorClient.connect(url, user, pass)
|
||||||
|
4. Java: ObjectName = proalpha-test.ct-ZADBService:ID=AGENT
|
||||||
|
5. Java: connector.invoke(on, "stop", …) // oder Fallback
|
||||||
|
6. Java: OK:RestartInvoked
|
||||||
|
7. C#: PostRestartDelaySeconds warten
|
||||||
|
8. UI: Erfolg/Fehler + Log
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Zertifikatsprüfung – welche Dateien
|
||||||
|
|
||||||
|
| Datei | Rolle |
|
||||||
|
|-------|--------|
|
||||||
|
| `Form1.cs` | Dateiauswahl, PFX-Passwort, Anzeige |
|
||||||
|
| `Models/CertificateInfo.cs` | Ergebnisobjekt |
|
||||||
|
| `Assets/...` / `Demo/...` | optionale Testdateien |
|
||||||
|
|
||||||
|
Ablauf:
|
||||||
|
|
||||||
|
1. Datei wählen (`.cer/.crt/.pem/.pfx`)
|
||||||
|
2. Als `X509Certificate2` laden
|
||||||
|
3. Subject/Issuer/Fingerprint/Gültigkeit berechnen
|
||||||
|
4. Anzeigen – **lokal verifiziert**
|
||||||
|
|
||||||
|
Nicht enthalten: Kopieren auf ESB, TLS-Probe gegen Live-Host, CA-Kettenprüfung.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Was absichtlich fehlt (gelöscht / deaktiviert)
|
||||||
|
|
||||||
|
| Früher | Status |
|
||||||
|
|--------|--------|
|
||||||
|
| `Data/targets.sample.json` + JSON-Repository | entfernt – Ziele aus KnownContainers |
|
||||||
|
| SQL-Repos / Schema | entfernt |
|
||||||
|
| WinRM / LocalCmd / stopcontainer.bat-Executor | entfernt |
|
||||||
|
| CertificateDeployer / TlsCertificateProbe | entfernt |
|
||||||
|
| XApi-Import | entfernt |
|
||||||
|
| Deploy-Button | UI ausgeblendet |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Schnell-Tabelle „Datei → Ein Satz“
|
||||||
|
|
||||||
|
| Datei | Ein Satz |
|
||||||
|
|-------|----------|
|
||||||
|
| `Program.cs` | Startet die App |
|
||||||
|
| `Form1.cs` | UI + Zertifikat + Neustart-Klick |
|
||||||
|
| `Form1.Designer.cs` | Leerer Designer-Stub |
|
||||||
|
| `Form1.resx` | Formular-Ressourcen |
|
||||||
|
| `appsettings.json` | Sonic-Verbindung + KnownContainers + Java/Libs |
|
||||||
|
| `*.csproj` | Build/Copy-Regeln |
|
||||||
|
| `Configuration/AppSettingsLoader.cs` | JSON → Settings-Objekt |
|
||||||
|
| `Models/AppSettings.cs` | Settings-Root |
|
||||||
|
| `Models/SonicConnection.cs` | Eine SMC-/Domain-Verbindung |
|
||||||
|
| `Models/DeploymentTarget.cs` | Ein Container-Ziel im Grid |
|
||||||
|
| `Models/CertificateInfo.cs` | Lokales Zertifikats-Ergebnis |
|
||||||
|
| `Models/DeploymentRunResult.cs` | Lauf-/Validierungs-DTOs |
|
||||||
|
| `Services/DeploymentOrchestrator.cs` | Neustart über alle Ziele |
|
||||||
|
| `Services/RestartExecutor.cs` | Ziel → SonicClient |
|
||||||
|
| `Services/PreflightValidator.cs` | Pflichtfelder prüfen |
|
||||||
|
| `Services/SonicManagementClient.cs` | Fassade ping/list/restart |
|
||||||
|
| `Services/SonicMfApiExecutor.cs` | Java-Prozess + Classpath |
|
||||||
|
| `Services/SonicContainerDiscovery.cs` | KnownContainers + Live-Liste |
|
||||||
|
| `Services/RunLogger.cs` | Datei-Logs |
|
||||||
|
| `Services/PathResolver.cs` | Relative Pfade |
|
||||||
|
| `Tools/SonicMfContainerTool.java` | Sonic Management API Aufrufe |
|
||||||
|
| `Tools/*.class` / `.jar` | Vorkompiliertes Tool |
|
||||||
|
| `Tools/verify-mfapi-classpath.bat` | Classpath-Selbsttest |
|
||||||
|
| `Docs/*.md` | Erklärungen |
|
||||||
|
| `Assets/*` | Logo / Testzertifikat |
|
||||||
|
| `Demo/*` | Demo-Zertifikat |
|
||||||
|
| `doc-10.0.10/*` | Offizielle Sonic/CX-Messenger-Doku |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Ende der Dateiübersicht. Für Ablauf-Details siehe `Komplette-Erklaerung.md`.*
|
||||||
@@ -0,0 +1,530 @@
|
|||||||
|
# Komplette Erklärung: ESB Certificate Manager
|
||||||
|
|
||||||
|
Was passiert – **einzeln, von oben nach unten**.
|
||||||
|
Zertifikat prüfen · Ziele laden · Sonic-API · Container-Neustart.
|
||||||
|
|
||||||
|
Diese Datei ist zum Erklären (Präsentation, Kollegen, Übergabe).
|
||||||
|
Technischer Fokus: aktueller Stand mit **MfApi-Neustart** und **lokaler Zertifikatserkennung**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil A – Was die Anwendung ist
|
||||||
|
|
||||||
|
## A1. Zweck in einem Satz
|
||||||
|
|
||||||
|
Die WinForms-App hilft dabei,
|
||||||
|
|
||||||
|
1. ein **Zertifikat als Datei zu erkennen und lokal zu prüfen**, und
|
||||||
|
2. einen **Sonic-/ESB-Container remote neu zu starten** – so ähnlich wie der Restart-Button in der **Sonic Management Console (SMC)**.
|
||||||
|
|
||||||
|
## A2. Was sie bewusst *nicht* macht (aktuell)
|
||||||
|
|
||||||
|
| Nicht aktiv | Erklärung |
|
||||||
|
|-------------|-----------|
|
||||||
|
| Zertifikat auf den Server **kopieren** | Deploy-Button ist ausgeblendet |
|
||||||
|
| Nach dem Deploy **TLS gegen den Endpoint prüfen** | früher TLS-Probe – entfernt/deaktiviert |
|
||||||
|
| **XApi** importieren | nicht mehr im Pfad |
|
||||||
|
| Neustart per **`stopcontainer.bat`** | oft in SMC-only-Installationen nicht vorhanden |
|
||||||
|
| Neustart per **WinRM / Windows-Login** | SMC-User ≠ Windows-User |
|
||||||
|
| Eigene **HTTP-REST-API** der App | die App *ist* kein Server; sie *ruft* Sonic auf |
|
||||||
|
|
||||||
|
## A3. Die wichtigsten Namen in eurer Umgebung
|
||||||
|
|
||||||
|
| Name | Typ | Beispiel |
|
||||||
|
|------|-----|----------|
|
||||||
|
| Verbindungs-Alias | Eintrag in `appsettings` | `DE-Test` |
|
||||||
|
| Domain | Sonic-Domain | `proalpha-test` |
|
||||||
|
| Container (kurz) | Laufzeit-Container | `ct-ZADBService` |
|
||||||
|
| ObjectName (JMX) | vollständige Agent-Adresse | `proalpha-test.ct-ZADBService:ID=AGENT` |
|
||||||
|
| ConnectionUrl | Domain-Manager wie in SMC | `tcp://dekun-painwbdet:13070` |
|
||||||
|
| Login | SMC-Credentials | `Administrator` / `Administrator` |
|
||||||
|
| Sonic-Libs | Client-JARs | `C:\DEV\MQ10.0\lib` |
|
||||||
|
| Java | Runtime für das Hilfstool | JRE 1.8+ |
|
||||||
|
|
||||||
|
**Merksatz:**
|
||||||
|
`DE-Test` = Verbindung. `ct-ZADBService` = Container. Nicht vertauschen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil B – Programmstart (was zuerst passiert)
|
||||||
|
|
||||||
|
## B1. Start der App
|
||||||
|
|
||||||
|
1. `Program.cs` startet `Form1`.
|
||||||
|
2. `AppSettingsLoader` liest `appsettings.json` (aus dem Output-Ordner der App).
|
||||||
|
3. Es werden gebaut:
|
||||||
|
- `DeploymentOrchestrator` (Neustart-Orchestrierung)
|
||||||
|
- `SonicContainerDiscovery` (Container-Liste optional laden)
|
||||||
|
4. Die UI (Dark-Design) wird aufgebaut.
|
||||||
|
5. Beim ersten Anzeigen (`Shown`) werden Ziele geladen.
|
||||||
|
|
||||||
|
## B2. Ziele laden
|
||||||
|
|
||||||
|
1. Ziele kommen aus `appsettings.json` → `SonicConnections[].KnownContainers` (kein Offline-JSON).
|
||||||
|
2. Jede Zeile hat u. a.:
|
||||||
|
- Anzeigename (`DE-Test / ct-ZADBService`)
|
||||||
|
- Umgebung = Domain
|
||||||
|
- **ContainerName** (z. B. `ct-ZADBService`)
|
||||||
|
- **SonicConnectionName** (z. B. `DE-Test`)
|
||||||
|
- Status-Spalte
|
||||||
|
|
||||||
|
Optional: Button **„Container laden“** fragt Sonic live per API und merged mit `KnownContainers`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil C – Zertifikat: Erkennen & Verifizieren (Schritt für Schritt)
|
||||||
|
|
||||||
|
Hier geht es **nur um die lokale Datei**. Es wird nichts auf den ESB geschrieben und kein TLS zum Server aufgebaut.
|
||||||
|
|
||||||
|
## C1. Benutzer wählt Datei
|
||||||
|
|
||||||
|
1. Klick auf Dateiauswahl in der UI.
|
||||||
|
2. `OpenFileDialog` filtert: `.cer`, `.crt`, `.pem`, `.pfx`.
|
||||||
|
3. Abbruch → nichts ändert sich.
|
||||||
|
4. Dateiendung wird geprüft (`IsSupportedCertificateFile`).
|
||||||
|
5. Unbekanntes Format → Warnung, Ende.
|
||||||
|
|
||||||
|
## C2. Datei lesen
|
||||||
|
|
||||||
|
1. Aufruf `TryReadSelectedCertificate(pfad)`.
|
||||||
|
2. Intern: `X509Certificate2` laden.
|
||||||
|
3. **PFX-Sonderfall:**
|
||||||
|
- Zuerst Versuch *ohne* Passwort.
|
||||||
|
- Bei `CryptographicException` → Passwort-Dialog.
|
||||||
|
- Falsches Passwort → Fehlermeldung, kein Zertifikat geladen.
|
||||||
|
4. Andere Fehler (Datei kaputt, kein X.509) → Fehlermeldung.
|
||||||
|
|
||||||
|
## C3. Was aus dem Zertifikat extrahiert wird
|
||||||
|
|
||||||
|
| Feld | Quelle | Anzeige |
|
||||||
|
|------|--------|---------|
|
||||||
|
| Subject | `GetNameInfo(SimpleName)` bzw. `Subject` | „Ausgestellt für“ |
|
||||||
|
| Issuer | Issuer-SimpleName bzw. `Issuer` | „Ausgestellt von“ |
|
||||||
|
| Gültig von / bis | `NotBefore` / `NotAfter` | Datum |
|
||||||
|
| Fingerprint | SHA-256 Hash, mit `:` formatiert | z. B. `AB:CD:…` |
|
||||||
|
| Gültigkeitsstatus | `now` zwischen NotBefore und NotAfter? | grün GÜLTIG / rot UNGÜLTIG |
|
||||||
|
|
||||||
|
## C4. Was „Verifizierung“ hier genau heißt
|
||||||
|
|
||||||
|
| Prüfung | Passiert? | Bedeutung |
|
||||||
|
|---------|-----------|-----------|
|
||||||
|
| Datei lesbar als X.509 | ja | Format/Inhalt ok |
|
||||||
|
| Zeitlich gültig | ja | nicht abgelaufen / noch nicht gültig |
|
||||||
|
| Fingerprint anzeigen | ja | manueller Abgleich möglich |
|
||||||
|
| Signaturkette gegen Windows-CA | **nein** | nicht implementiert |
|
||||||
|
| Datei auf ESB kopieren | **nein** | Deploy aus |
|
||||||
|
| TLS-Handshake zum Live-System + Fingerprint vergleichen | **nein** | TLS-Probe aus |
|
||||||
|
|
||||||
|
**Erklärsatz:**
|
||||||
|
> Wir verifizieren das Zertifikat **als Datei auf dem PC** (lesbar + Gültigkeitszeitraum + Fingerprint).
|
||||||
|
> Wir verifizieren **nicht**, dass genau dieses Zertifikat schon am laufenden ESB hängt.
|
||||||
|
> Der Neustart ist davon getrennt.
|
||||||
|
|
||||||
|
## C5. UI nach erfolgreichem Laden
|
||||||
|
|
||||||
|
1. Pfad steht im Textfeld.
|
||||||
|
2. Metadaten-Karte wird gefüllt.
|
||||||
|
3. Statuszeile: „Zertifikat ausgewählt: …“.
|
||||||
|
4. Sidebar-Schritt kann auf „Ziele“ springen.
|
||||||
|
5. Neustart braucht das Zertifikat **nicht zwingend** – der Neustart hängt am Container, nicht an der Datei.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil D – Vor dem Neustart (Preflight)
|
||||||
|
|
||||||
|
## D1. Benutzer hakt Ziel(e) an
|
||||||
|
|
||||||
|
Im Grid: Checkbox „Selected“ setzen.
|
||||||
|
|
||||||
|
## D2. Button „ESB neu starten“
|
||||||
|
|
||||||
|
1. `GetSelectedTargets()` sammelt alle angehakten Zeilen.
|
||||||
|
2. `ValidateRestartOnly` prüft für jedes Ziel:
|
||||||
|
- `ContainerName` nicht leer
|
||||||
|
- `SonicConnectionName` nicht leer
|
||||||
|
3. Fehler → Dialog „Neustart blockiert“ mit Liste der Probleme.
|
||||||
|
4. OK → Bestätigungsdialog (Containername, Hinweis auf Management API).
|
||||||
|
5. Nein → Abbruch. Ja → Neustart startet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil E – Neustart Gesamtablauf (C#-Seite)
|
||||||
|
|
||||||
|
Alles von hier läuft **ohne** Zertifikatskopieren.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Form1.RunRestartOnlyAsync
|
||||||
|
→ DeploymentOrchestrator.RestartOnlyAsync
|
||||||
|
→ für jedes Ziel:
|
||||||
|
RestartExecutor.ExecuteAsync
|
||||||
|
→ SonicManagementClient.RestartContainerAsync
|
||||||
|
→ SonicMfApiExecutor.RestartAsync
|
||||||
|
→ java SonicMfContainerTool restart …
|
||||||
|
```
|
||||||
|
|
||||||
|
## E1. Orchestrator
|
||||||
|
|
||||||
|
Pro Ziel:
|
||||||
|
|
||||||
|
1. Status im Grid: „Neustart…“
|
||||||
|
2. `RestartExecutor.ExecuteAsync(target)`
|
||||||
|
3. Ergebnis + Detail in Logdatei unter `Logs\`
|
||||||
|
4. Status im Grid aktualisieren
|
||||||
|
5. Am Ende: Erfolgs-/Fehlerdialog mit Tool-Ausgabe
|
||||||
|
|
||||||
|
## E2. RestartExecutor
|
||||||
|
|
||||||
|
1. Liest `target.ContainerName` (z. B. `ct-ZADBService`).
|
||||||
|
2. Sucht in `appsettings` die `SonicConnection` mit `Name == SonicConnectionName` (`DE-Test`).
|
||||||
|
3. Fehlt die Verbindung → Fehler „Sonic-Verbindung nicht gefunden“.
|
||||||
|
4. Sonst: `new SonicManagementClient(connection)` und `RestartContainerAsync(containerName)`.
|
||||||
|
|
||||||
|
## E3. SonicManagementClient
|
||||||
|
|
||||||
|
Schlanke Fassade, nur MfApi:
|
||||||
|
|
||||||
|
1. Ruft `_mfApi.RestartAsync(containerName)` auf.
|
||||||
|
2. Bei Erfolg: wartet `PostRestartDelaySeconds` (z. B. 20 s), damit der Container Zeit zum Hochfahren hat.
|
||||||
|
3. Liefert Status-Text + komplette Java-Ausgabe zurück.
|
||||||
|
4. Bei Fehler: Fehlermeldung + Output für den Dialog.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil F – SonicMfApiExecutor: Java vorbereiten und starten
|
||||||
|
|
||||||
|
Das ist die Brücke von .NET zu Sonic.
|
||||||
|
|
||||||
|
## F1. Java finden
|
||||||
|
|
||||||
|
Reihenfolge grob:
|
||||||
|
|
||||||
|
1. `JavaPath` aus appsettings (bevorzugt)
|
||||||
|
2. `JavaHome`
|
||||||
|
3. Umgebungsvariablen / PATH / Registry / unter SonicHome
|
||||||
|
|
||||||
|
Bedingung: Runtime muss **Java 8+** können (Class-File major ≥ 52).
|
||||||
|
Zu alte Sonic-JREs (6/7) werden übersprungen.
|
||||||
|
|
||||||
|
## F2. Classpath bauen (Sonic-JARs)
|
||||||
|
|
||||||
|
1. `MfClientLibPath` (z. B. `C:\DEV\MQ10.0\lib`) oder Suche unter `SonicHome`.
|
||||||
|
2. Es müssen echte Client-JARs da sein, u. a.:
|
||||||
|
- `mgmt_client.jar`
|
||||||
|
- `mfcontext.jar`
|
||||||
|
- `sonic_Client.jar`
|
||||||
|
3. **Alle** `*.jar` im Lib-Ordner werden explizit in den Classpath geschrieben (kein fragiles `lib\*` mit kaputten Quotes).
|
||||||
|
4. Das Tool-Verzeichnis `Tools\` kommt **vorne** in den Classpath, damit die richtige `SonicMfContainerTool.class` geladen wird.
|
||||||
|
|
||||||
|
## F3. Java-Tool laden
|
||||||
|
|
||||||
|
1. Bevorzugt vorcompilierte `Tools\SonicMfContainerTool.class` (Java 8, major 52).
|
||||||
|
2. Alte/zu kleine Class-Dateien werden verworfen.
|
||||||
|
3. Temp-Cache alter Compilate wird gelöscht.
|
||||||
|
4. Falls nötig: aus `.java` mit `javac --release 8` neu bauen.
|
||||||
|
|
||||||
|
## F4. Prozess starten
|
||||||
|
|
||||||
|
Ungefähr:
|
||||||
|
|
||||||
|
```text
|
||||||
|
"C:\...\java.exe"
|
||||||
|
-cp "C:\...\Tools;C:\DEV\MQ10.0\lib\mgmt_client.jar;...;C:\DEV\MQ10.0\lib\mfcontext.jar;..."
|
||||||
|
SonicMfContainerTool
|
||||||
|
restart
|
||||||
|
--domain proalpha-test
|
||||||
|
--url tcp://dekun-painwbdet:13070
|
||||||
|
--user Administrator
|
||||||
|
--container ct-ZADBService
|
||||||
|
--timeout 120
|
||||||
|
```
|
||||||
|
|
||||||
|
Wichtig:
|
||||||
|
|
||||||
|
- Argumente über `ProcessStartInfo.ArgumentList` (keine doppelten Anführungszeichen).
|
||||||
|
- Passwort über Umgebung `ESB_SONIC_PASSWORD`, nicht in der sichtbaren Kommandozeile.
|
||||||
|
- stdout/stderr werden gelesen und an die UI zurückgegeben.
|
||||||
|
|
||||||
|
## F5. Erfolgsauswertung in C#
|
||||||
|
|
||||||
|
Neustart gilt nur als Erfolg, wenn die Ausgabe enthält:
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK:RestartInvoked
|
||||||
|
```
|
||||||
|
|
||||||
|
Sonst: Fehlerdialog mit kompletter Tool-Ausgabe.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil G – Die Sonic Management Application API (Java-Tool)
|
||||||
|
|
||||||
|
Hier passiert der eigentliche „API-Call“ – **nicht REST**, sondern **JMS/JMX Management Framework**.
|
||||||
|
|
||||||
|
Offizielle Produktwelt (Aurea CX Messenger / Sonic):
|
||||||
|
|
||||||
|
- Index: `doc-10.0.10/CXMessenger_2017_R3.htm`
|
||||||
|
- Eintrag: **Management Application API Reference** (`Docs2017/api/mgmt_api/`)
|
||||||
|
|
||||||
|
Das ist dieselbe API-Familie, die die SMC für Restart nutzt.
|
||||||
|
|
||||||
|
## G1. Warum „API“ hier kompliziert klingt
|
||||||
|
|
||||||
|
Die Sonic-Produkt-API enthält **sehr viele** Bereiche:
|
||||||
|
|
||||||
|
- Broker / Queues / Topics
|
||||||
|
- ESB-Services
|
||||||
|
- Metrics / Notifications
|
||||||
|
- Directory / Config
|
||||||
|
- Agent Lifecycle
|
||||||
|
|
||||||
|
**Für unseren Neustart brauchen wir nur einen schmalen Teil:**
|
||||||
|
|
||||||
|
1. Verbinden zum Domain Manager
|
||||||
|
2. Agent-ObjectName ansprechen
|
||||||
|
3. Lifecycle: stop/restart
|
||||||
|
|
||||||
|
Der Rest der JARs ist Produktumfang – nicht „unsere App-API mit 100 Features“.
|
||||||
|
|
||||||
|
## G2. Schritt 1 – Tool startet
|
||||||
|
|
||||||
|
Ausgabe:
|
||||||
|
|
||||||
|
```text
|
||||||
|
INFO:ToolVersion=2026-07-24c-mbean-stop
|
||||||
|
```
|
||||||
|
|
||||||
|
Wenn diese Zeile fehlt → oft noch eine **alte** `.class` im Einsatz.
|
||||||
|
|
||||||
|
## G3. Schritt 2 – Argumente parsen
|
||||||
|
|
||||||
|
Pflicht:
|
||||||
|
|
||||||
|
- Befehl: `ping` | `list` | `restart`
|
||||||
|
- `--domain`
|
||||||
|
- `--url`
|
||||||
|
- `--user`
|
||||||
|
- bei `restart`: `--container`
|
||||||
|
- Passwort: `--password` oder `ESB_SONIC_PASSWORD`
|
||||||
|
|
||||||
|
## G4. Schritt 3 – Connect (der eigentliche API-Login)
|
||||||
|
|
||||||
|
Java (vereinfacht, per Reflection):
|
||||||
|
|
||||||
|
```text
|
||||||
|
Hashtable env:
|
||||||
|
ConnectionURLs = tcp://dekun-painwbdet:13070
|
||||||
|
DefaultUser = Administrator
|
||||||
|
DefaultPassword = ***
|
||||||
|
|
||||||
|
JMSConnectorAddress address = new JMSConnectorAddress(env)
|
||||||
|
JMSConnectorClient connector = new JMSConnectorClient()
|
||||||
|
connector.connect(address, timeout)
|
||||||
|
```
|
||||||
|
|
||||||
|
Klassen kommen aus den Sonic-JARs:
|
||||||
|
|
||||||
|
- `com.sonicsw.mf.jmx.client.JMSConnectorAddress`
|
||||||
|
- `com.sonicsw.mf.jmx.client.JMSConnectorClient`
|
||||||
|
|
||||||
|
Ausgabe bei Erfolg:
|
||||||
|
|
||||||
|
```text
|
||||||
|
INFO:Connected url=tcp://dekun-painwbdet:13070 user=Administrator
|
||||||
|
```
|
||||||
|
|
||||||
|
Das entspricht dem SMC-Login gegen denselben Domain Manager.
|
||||||
|
|
||||||
|
## G5. Schritt 4 – Ziel-Agent bestimmen
|
||||||
|
|
||||||
|
ObjectName:
|
||||||
|
|
||||||
|
```text
|
||||||
|
proalpha-test.ct-ZADBService:ID=AGENT
|
||||||
|
```
|
||||||
|
|
||||||
|
Muster:
|
||||||
|
|
||||||
|
```text
|
||||||
|
{DomainName}.{ContainerName}:ID=AGENT
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional: per `queryNames` nach vorhandenen Agenten suchen; wenn das scheitert, Preferred-Name verwenden.
|
||||||
|
|
||||||
|
## G6. Schritt 5 – Lifecycle (Neustart) – drei Wege nacheinander
|
||||||
|
|
||||||
|
### Weg 1 – MBean invoke (bevorzugt remote)
|
||||||
|
|
||||||
|
```text
|
||||||
|
connector.invoke(objectName, "stop", [], [])
|
||||||
|
// oder "restart", "shutdown"
|
||||||
|
```
|
||||||
|
|
||||||
|
Warum zuerst `stop`?
|
||||||
|
|
||||||
|
- Remote-Clients sind oft **„unbounded“**.
|
||||||
|
- `IAgentProxy.restart()` wirft dann häufig:
|
||||||
|
`Operation unsupported for unbounded client connector`
|
||||||
|
- SMC-Restart wirkt für den Operator oft wie: **Stop + automatischer Relaunch** durch Launch Daemon / Host Manager.
|
||||||
|
|
||||||
|
Erfolg:
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK:RestartInvoked method=MBean.stop container=ct-ZADBService domain=proalpha-test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Weg 2 – Typisierter Proxy
|
||||||
|
|
||||||
|
```text
|
||||||
|
IAgentProxy agent = MFProxyFactory.createAgentProxy(connector, objectName)
|
||||||
|
agent.stop() / agent.restart() / agent.shutdown()
|
||||||
|
```
|
||||||
|
|
||||||
|
Schön dokumentiert, remote oft eingeschränkt (unbounded).
|
||||||
|
|
||||||
|
### Weg 3 – DomainManager / Manager-MBeans
|
||||||
|
|
||||||
|
Suche nach Operationen wie:
|
||||||
|
|
||||||
|
- `restartContainer(containerName)`
|
||||||
|
- `stopContainer(containerName)`
|
||||||
|
|
||||||
|
Fallback, falls Agent-direkt nicht klappt.
|
||||||
|
|
||||||
|
## G7. Schritt 6 – Disconnect
|
||||||
|
|
||||||
|
`connector.disconnect()` im `finally`.
|
||||||
|
|
||||||
|
## G8. Befehle des Tools im Überblick
|
||||||
|
|
||||||
|
| Befehl | Zweck |
|
||||||
|
|--------|--------|
|
||||||
|
| `ping` | Verbindungstest + Agenten zählen |
|
||||||
|
| `list` | Containernamen der Domain ausgeben |
|
||||||
|
| `restart` | Lifecycle auf einem Container |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil H – Komplette Kette als nummerierte Liste
|
||||||
|
|
||||||
|
So kannst du es jemandem erzählen:
|
||||||
|
|
||||||
|
### Zertifikat
|
||||||
|
|
||||||
|
1. App starten
|
||||||
|
2. Zertifikatsdatei wählen
|
||||||
|
3. Datei als X.509 lesen (PFX ggf. mit Passwort)
|
||||||
|
4. Subject, Issuer, Fingerprint, Gültigkeit anzeigen
|
||||||
|
5. Lokal „verifiziert“ = lesbar + im Gültigkeitszeitraum
|
||||||
|
|
||||||
|
### Neustart
|
||||||
|
|
||||||
|
6. Ziel im Grid anhaken (`ct-ZADBService` / Verbindung `DE-Test`)
|
||||||
|
7. „ESB neu starten“ → Preflight → Bestätigung
|
||||||
|
8. C# findet Sonic-Verbindung in appsettings
|
||||||
|
9. C# findet Java 8+ und baut Classpath aus `C:\DEV\MQ10.0\lib`
|
||||||
|
10. C# startet `SonicMfContainerTool restart …`
|
||||||
|
11. Java verbindet per Management API an `tcp://…:13070`
|
||||||
|
12. Java adressiert `proalpha-test.ct-ZADBService:ID=AGENT`
|
||||||
|
13. Java ruft Stop/Restart per MBean (Fallback Proxy/Manager)
|
||||||
|
14. Java schreibt `OK:RestartInvoked`
|
||||||
|
15. C# wartet `PostRestartDelaySeconds`
|
||||||
|
16. UI zeigt Erfolg/Fehler + Logausgabe
|
||||||
|
17. Optional in SMC prüfen: Uptime/Status des Containers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil I – Konfiguration im Detail
|
||||||
|
|
||||||
|
## I1. `appsettings.json` (Sonic-Block)
|
||||||
|
|
||||||
|
| Feld | Rolle |
|
||||||
|
|------|--------|
|
||||||
|
| `Name` | Alias, von Zielen referenziert (`DE-Test`) |
|
||||||
|
| `DomainName` | Sonic-Domain |
|
||||||
|
| `ConnectionUrl` | Domain-Manager-URL (wie SMC) |
|
||||||
|
| `Username` / `Password` | SMC-Login |
|
||||||
|
| `SonicHome` | Installationsroot |
|
||||||
|
| `MfClientLibPath` | Ordner mit Client-JARs |
|
||||||
|
| `JavaPath` / `JavaHome` | Java 8+ |
|
||||||
|
| `KnownContainers` | Fallback-Liste, z. B. `ct-ZADBService` |
|
||||||
|
| `TimeoutSeconds` | Timeout Java/API |
|
||||||
|
| `PostRestartDelaySeconds` | Wartezeit nach erfolgreichem Invoke |
|
||||||
|
|
||||||
|
Felder wie `WinRm*`, `Container*Path`, `ManagementHttpPort` können noch in der JSON stehen, werden vom schlanken MfApi-Client **nicht mehr genutzt**.
|
||||||
|
|
||||||
|
## I2. Container-Liste
|
||||||
|
|
||||||
|
Kein separates Sample-JSON. Container stehen in:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"KnownContainers": [ "ct-ZADBService" ]
|
||||||
|
```
|
||||||
|
|
||||||
|
Live-Erweiterung über UI-Button „Container laden“.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil J – Dateien und Verantwortlichkeiten
|
||||||
|
|
||||||
|
| Datei | Verantwortung |
|
||||||
|
|-------|----------------|
|
||||||
|
| `Form1.cs` | UI, Zertifikat lesen, Neustart-Button, Dialoge |
|
||||||
|
| `DeploymentOrchestrator.cs` | Neustart über alle gewählten Ziele |
|
||||||
|
| `PreflightValidator.cs` | Pflichtfelder prüfen |
|
||||||
|
| `RestartExecutor.cs` | Ziel → SonicConnection |
|
||||||
|
| `SonicManagementClient.cs` | Fassade MfApi |
|
||||||
|
| `SonicMfApiExecutor.cs` | Java/Classpath/Prozess |
|
||||||
|
| `Tools/SonicMfContainerTool.java` | Connect + Lifecycle über Sonic-API |
|
||||||
|
| `appsettings.json` | Laufzeitkonfiguration inkl. KnownContainers |
|
||||||
|
| `Logs\` | Laufprotokolle |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil K – Erfolgs- und Fehlerbilder
|
||||||
|
|
||||||
|
## K1. Gesunde Ausgabe (Auszug)
|
||||||
|
|
||||||
|
```text
|
||||||
|
INFO:ToolVersion=2026-07-24c-mbean-stop
|
||||||
|
INFO:Connected url=tcp://dekun-painwbdet:13070 user=Administrator
|
||||||
|
INFO:PreferredObjectName=proalpha-test.ct-ZADBService:ID=AGENT
|
||||||
|
INFO:Trying MBean.invoke(stop) on …
|
||||||
|
OK:RestartInvoked method=MBean.stop container=ct-ZADBService domain=proalpha-test
|
||||||
|
```
|
||||||
|
|
||||||
|
## K2. Häufige Fehler
|
||||||
|
|
||||||
|
| Meldung | Bedeutung | Typische Lösung |
|
||||||
|
|---------|-----------|-----------------|
|
||||||
|
| `ClassNotFoundException: JMSConnectorAddress` | JARs fehlen / Classpath kaputt | `MfClientLibPath`, neuer Build |
|
||||||
|
| `UnsupportedClassVersionError` | Java-Version mismatch | Tool als Java 8 + Runtime Java 8+ |
|
||||||
|
| `Operation unsupported for unbounded…` | Proxy-Restart remote gesperrt | MBean-stop-Pfad / aktuelles Tool |
|
||||||
|
| Kein `ToolVersion=` | Alte Class-Datei | Neu bauen, richtigen bin-Ordner starten |
|
||||||
|
| Container `DE-Test` | Alias statt Kurzname | `ct-ZADBService` |
|
||||||
|
| `stopcontainer.bat` fehlt | SMC-only | normal – MfApi nutzen |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil L – Wie man es jemandem erklärt (Kurzskript)
|
||||||
|
|
||||||
|
1. **UI:** Zertifikat laden → wir sehen Subject/Fingerprint/Gültigkeit. Das ist lokale Dateiprüfung.
|
||||||
|
2. **Neustart:** Wir starten denselben Management-Weg wie die SMC, nicht per Batch-Datei und nicht per FTP.
|
||||||
|
3. **Technik:** C# orchestriert; ein kleines Java-Tool + Sonic-Client-JARs sprechen mit dem Domain Manager.
|
||||||
|
4. **API:** Sonic Management Application API (JMS/JMX). Sehr große Produkt-API, wir nutzen nur Connect + Agent-Stop/Restart.
|
||||||
|
5. **Haken:** Remote heißt „unbounded“ – deshalb oft `stop` per MBean statt `IAgentProxy.restart()`.
|
||||||
|
6. **Ergebnis:** Container `ct-ZADBService` in Domain `proalpha-test` wird neu gestartet; danach in der SMC Status/Uptime prüfen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teil M – Abgrenzung: Was ist „unsere API“ vs. Sonic-API?
|
||||||
|
|
||||||
|
| Ebene | Beschreibung |
|
||||||
|
|-------|--------------|
|
||||||
|
| **Unsere App** | WinForms + C#-Services. Kein öffentlicher REST-Endpoint. |
|
||||||
|
| **Unser Java-Tool** | Dünner Adapter (`ping`/`list`/`restart`) mit Fallbacks. |
|
||||||
|
| **Sonic Management Application API** | Produkt-API in den JARs; offiziell dokumentiert; SMC nutzt dieselbe Familie. |
|
||||||
|
| **Unnötig für den Use-Case** | MQ Messaging API, ESB API, Metrics, XApi, WinRM, Bat-Scripts, TLS-Probe – solange nur „Datei prüfen + Neustart“ gefragt ist. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Ende der kompletten Erklärung. Bei Abweichungen gilt der Code im Projekt (Branch mit MfApi-Neustart).*
|
||||||
@@ -0,0 +1,507 @@
|
|||||||
|
# MfApi-Restart – vollständige Erklärung
|
||||||
|
|
||||||
|
Stand: Tool-Version `2026-07-24d-restart-only`
|
||||||
|
Zweck dieses Dokuments: erklären, **wie die Sonic Management Application API (MfApi) in dieser App genutzt wird**, Schicht für Schicht – von Button-Klick bis MBean-Aufruf.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Was ist „die API“ hier?
|
||||||
|
|
||||||
|
Es gibt **keine eigene REST-API** der WinForms-App.
|
||||||
|
|
||||||
|
Gemeint ist die **Sonic / Aurea MF Management Application API** (wie in der Sonic Management Console, SMC):
|
||||||
|
|
||||||
|
| Begriff | Bedeutung |
|
||||||
|
|---------|-----------|
|
||||||
|
| **MfApi** | Management Framework API über JMS/JMX-Client |
|
||||||
|
| **Domain Manager** | zentrale Verwaltung der Sonic-Domain |
|
||||||
|
| **Container / Agent** | laufende ESB-Instanz, z. B. `ct-ZADBService` |
|
||||||
|
| **SMC** | Sonic Management Console (UI mit denselben Credentials/URL) |
|
||||||
|
| **MBean** | verwaltbares Objekt im Domain Manager (`ObjectName`) |
|
||||||
|
|
||||||
|
Die App startet lokal einen **Java-Prozess**, der die offiziellen Sonic-Client-JARs lädt und denselben Neustart-Intent ausführt wie **Restart** in der SMC.
|
||||||
|
|
||||||
|
### Warum Java und nicht reines .NET?
|
||||||
|
|
||||||
|
Die Sonic-Client-Bibliotheken (`mgmt_client.jar`, `mfcontext.jar`, `sonic_Client.jar`, …) sind **Java**.
|
||||||
|
C# orchestriert nur: Konfiguration lesen → `java.exe` starten → stdout auswerten.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Was die App bewusst *nicht* mehr macht
|
||||||
|
|
||||||
|
Nach dem Slimming ist die API-Schicht **nur Restart**:
|
||||||
|
|
||||||
|
| Entfernt | Früher |
|
||||||
|
|----------|--------|
|
||||||
|
| `ping` | Verbindungstest über Agent-Query |
|
||||||
|
| `list` | Live-Containerliste aus dem Domain Manager |
|
||||||
|
| IAgentProxy-Fallback | oft „Operation unsupported for unbounded client connector“ |
|
||||||
|
| DomainManager-Scan | `restartContainer` / Manager-MBeans |
|
||||||
|
| WinRM / `stopcontainer.bat` | lokaler Script-Pfad |
|
||||||
|
| HTTP-REST-Pfade | ungenutzt |
|
||||||
|
|
||||||
|
Container kommen ausschließlich aus **`appsettings.json` → `KnownContainers`**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Architektur-Überblick
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ WinForms UI (Form1) │
|
||||||
|
│ Button „ESB neu starten“ │
|
||||||
|
└────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ DeploymentOrchestrator │
|
||||||
|
│ Preflight → Schleife über Ziele → Logging / Progress │
|
||||||
|
└────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ RestartExecutor │
|
||||||
|
│ Ziel → SonicConnection (Name-Match) │
|
||||||
|
└────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ SonicManagementClient │
|
||||||
|
│ Restart + PostRestartDelay │
|
||||||
|
└────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ SonicMfApiExecutor (.NET) │
|
||||||
|
│ Java finden, Classpath bauen, Process starten, Output prüfen │
|
||||||
|
└────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│ Process: java -cp … SonicMfContainerTool restart …
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ SonicMfContainerTool (Java) │
|
||||||
|
│ connect → MBean.invoke(stop|restart) → OK:RestartInvoked │
|
||||||
|
└────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│ tcp://…:13070 (JMS Management)
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Sonic Domain Manager / Broker │
|
||||||
|
│ ObjectName: proalpha-test.ct-ZADBService:ID=AGENT │
|
||||||
|
│ Launch Daemon startet Container nach stop ggf. neu │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Konfiguration (`appsettings.json`)
|
||||||
|
|
||||||
|
Beispiel (aktuell):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"LogDirectory": "Logs",
|
||||||
|
"SonicConnections": [
|
||||||
|
{
|
||||||
|
"Name": "DE-Test",
|
||||||
|
"DomainName": "proalpha-test",
|
||||||
|
"ConnectionUrl": "tcp://dekun-painwbdet:13070",
|
||||||
|
"Username": "Administrator",
|
||||||
|
"Password": "Administrator",
|
||||||
|
"SonicHome": "C:\\DEV\\MQ10.0",
|
||||||
|
"JavaHome": "C:\\Program Files (x86)\\Java\\jre1.8.0_501",
|
||||||
|
"JavaPath": "C:\\Program Files (x86)\\Java\\jre1.8.0_501\\bin\\java.exe",
|
||||||
|
"MfClientLibPath": "C:\\DEV\\MQ10.0\\lib",
|
||||||
|
"KnownContainers": [ "ct-ZADBService" ],
|
||||||
|
"TimeoutSeconds": 120,
|
||||||
|
"PostRestartDelaySeconds": 20
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Felder erklärt
|
||||||
|
|
||||||
|
| Feld | Rolle |
|
||||||
|
|------|--------|
|
||||||
|
| **Name** | Alias der Verbindung. `DeploymentTarget.SonicConnectionName` muss dazu passen (hier `DE-Test`). |
|
||||||
|
| **DomainName** | Sonic-Domain (`proalpha-test`). Teil des JMX-`ObjectName`. |
|
||||||
|
| **ConnectionUrl** | Dieselbe TCP-URL wie in der SMC (`tcp://host:port`). |
|
||||||
|
| **Username / Password** | SMC-Login (nicht Windows/WinRM). Passwort geht als Env `ESB_SONIC_PASSWORD` an Java. |
|
||||||
|
| **SonicHome** | Installationsroot; Fallback für `lib\*.jar`, wenn `MfClientLibPath` leer/unbrauchbar. |
|
||||||
|
| **JavaPath** | Bevorzugter Pfad zu `java.exe` (**Java 8+**, Class major ≥ 52). |
|
||||||
|
| **JavaHome** | Alternativ `JavaHome\bin\java.exe`. |
|
||||||
|
| **MfClientLibPath** | Ordner mit Client-JARs (`mgmt_client.jar`, `mfcontext.jar`, …). |
|
||||||
|
| **KnownContainers** | Liste der Container-**Kurznamen** für die Grid-Ziele. |
|
||||||
|
| **TimeoutSeconds** | Timeout für Java-Prozess und Tool-Connect (5–600 s, Clamp in Code). |
|
||||||
|
| **PostRestartDelaySeconds** | Wartezeit **nach** erfolgreichem `OK:RestartInvoked` (0–120 s), bevor UI „fertig“ meldet. |
|
||||||
|
|
||||||
|
### Wichtige Namensunterscheidung
|
||||||
|
|
||||||
|
| Name | Beispiel | Ist |
|
||||||
|
|------|----------|-----|
|
||||||
|
| Verbindungs-Alias | `DE-Test` | Eintrag in `SonicConnections[].Name` |
|
||||||
|
| Domain | `proalpha-test` | Sonic-Domain |
|
||||||
|
| Container | `ct-ZADBService` | Agent / Container in SMC |
|
||||||
|
|
||||||
|
Häufiger Fehler: Alias `DE-Test` als Containername verwenden. Der ObjectName wäre dann falsch.
|
||||||
|
|
||||||
|
Korrekt:
|
||||||
|
|
||||||
|
```text
|
||||||
|
proalpha-test.ct-ZADBService:ID=AGENT
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Ziele (DeploymentTarget)
|
||||||
|
|
||||||
|
Beim Start baut `SonicContainerDiscovery.BuildTargetsFromConfig()` aus jeder Connection und jedem `KnownContainers`-Eintrag ein Ziel:
|
||||||
|
|
||||||
|
| Property | Quelle / Wert |
|
||||||
|
|----------|----------------|
|
||||||
|
| `Name` | `"DE-Test / ct-ZADBService"` |
|
||||||
|
| `Environment` | `DomainName` |
|
||||||
|
| `ContainerName` | `ct-ZADBService` |
|
||||||
|
| `SonicConnectionName` | `DE-Test` |
|
||||||
|
| `RestartType` | `SonicContainer` |
|
||||||
|
|
||||||
|
Es gibt **keinen Live-Query** mehr („Container laden“ wurde entfernt).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Aufrufkette – jede Methode
|
||||||
|
|
||||||
|
### 6.1 UI – `Form1`
|
||||||
|
|
||||||
|
| Schritt | Methode / Ereignis | Aufgabe |
|
||||||
|
|---------|-------------------|---------|
|
||||||
|
| 1 | `btnRestartOnly.Click` | Startet `RunRestartOnlyAsync()` |
|
||||||
|
| 2 | `GetSelectedTargets()` | Angehakte Grid-Zeilen |
|
||||||
|
| 3 | `_orchestrator.ValidateRestartOnly(...)` | Vorabprüfung |
|
||||||
|
| 4 | MessageBox-Bestätigung | Nutzer bestätigt Neustart |
|
||||||
|
| 5 | `_orchestrator.RestartOnlyAsync(...)` | eigentlicher Lauf |
|
||||||
|
| 6 | Grid / Statuszeile | Ergebnis anzeigen (kein Erfolgs-Popup) |
|
||||||
|
|
||||||
|
### 6.2 `PreflightValidator.ValidateRestartOnly`
|
||||||
|
|
||||||
|
Prüft grob:
|
||||||
|
|
||||||
|
- mindestens ein Ziel gewählt
|
||||||
|
- `ContainerName` gesetzt
|
||||||
|
- `SonicConnectionName` gesetzt
|
||||||
|
|
||||||
|
Fehler → Abbruch **vor** Java.
|
||||||
|
|
||||||
|
### 6.3 `DeploymentOrchestrator`
|
||||||
|
|
||||||
|
| Methode | Aufgabe |
|
||||||
|
|---------|---------|
|
||||||
|
| `ValidateRestartOnly` | reicht an Preflight durch |
|
||||||
|
| `RestartOnlyAsync` | Log öffnen, alle gewählten Ziele nacheinander |
|
||||||
|
| `RestartTargetOnlyAsync` | Progress „Neustart…“ → Executor → `TargetStepResult` |
|
||||||
|
|
||||||
|
Kein Deploy, kein TLS, kein SQL.
|
||||||
|
|
||||||
|
### 6.4 `RestartExecutor.ExecuteAsync`
|
||||||
|
|
||||||
|
1. `ContainerName` prüfen
|
||||||
|
2. `SonicConnection` per `Name == target.SonicConnectionName` finden
|
||||||
|
3. `new SonicManagementClient(connection)`
|
||||||
|
4. `RestartContainerAsync(target.ContainerName)`
|
||||||
|
|
||||||
|
### 6.5 `SonicManagementClient.RestartContainerAsync`
|
||||||
|
|
||||||
|
1. `_mfApi.RestartAsync(containerName)`
|
||||||
|
2. Bei Erfolg: `Task.Delay(PostRestartDelaySeconds)`
|
||||||
|
3. Status/Detail-String für UI/Log zurückgeben
|
||||||
|
|
||||||
|
Die Verzögerung gibt dem Launch Daemon Zeit, den Container wieder hochzufahren. Sie ist **kein** aktiver SMC-Status-Poll.
|
||||||
|
|
||||||
|
### 6.6 `SonicMfApiExecutor` – die C#-Brücke
|
||||||
|
|
||||||
|
#### `PrepareTool()`
|
||||||
|
|
||||||
|
1. **Java** über `ResolveJavaExe()`:
|
||||||
|
- `JavaPath`
|
||||||
|
- `JavaHome\bin\java.exe`
|
||||||
|
- `JAVA_HOME` / `JRE_HOME`
|
||||||
|
- `java.exe` aus PATH
|
||||||
|
2. **Classpath** über `ResolveSonicClasspath()`:
|
||||||
|
- alle `*.jar` aus `MfClientLibPath`, sonst `SonicHome\lib` / `SonicHome`
|
||||||
|
- bevorzugte Reihenfolge: `mgmt_client.jar`, `mfcontext.jar`, `sonic_Client.jar`, `sonic_Crypto.jar`, `mf_common.jar`, dann Rest
|
||||||
|
3. **Tool-Verzeichnis** mit `SonicMfContainerTool.class` (`Tools\` neben der EXE)
|
||||||
|
|
||||||
|
Ergebnis-Classpath grob:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<App>\Tools;<MfClientLibPath>\mgmt_client.jar;...;<weitere jars>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `RestartAsync(containerName)`
|
||||||
|
|
||||||
|
Startet:
|
||||||
|
|
||||||
|
```text
|
||||||
|
java -cp "<Tools>;<jars>" SonicMfContainerTool restart
|
||||||
|
--domain proalpha-test
|
||||||
|
--url tcp://dekun-painwbdet:13070
|
||||||
|
--user Administrator
|
||||||
|
--container ct-ZADBService
|
||||||
|
--timeout 120
|
||||||
|
```
|
||||||
|
|
||||||
|
- Argumente über `ProcessStartInfo.ArgumentList` (keine manuellen Quotes → sonst Classpath-Bugs)
|
||||||
|
- Passwort: Umgebungsvariable `ESB_SONIC_PASSWORD`
|
||||||
|
- stdout/stderr lesen, auf Timeout killen
|
||||||
|
|
||||||
|
#### Erfolgskriterium in C#
|
||||||
|
|
||||||
|
Alles muss gelten:
|
||||||
|
|
||||||
|
1. `ExitCode == 0`
|
||||||
|
2. Ausgabe enthält `OK:RestartInvoked`
|
||||||
|
3. Ausgabe enthält **kein** `ERROR:`
|
||||||
|
|
||||||
|
Sonst Fehlertext (+ Classpath-Hinweis bei `ClassNotFoundException` / `JMSConnectorAddress`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Java-Tool – der eigentliche API-Call
|
||||||
|
|
||||||
|
Datei: `Tools/SonicMfContainerTool.java`
|
||||||
|
Bytecode: `Tools/SonicMfContainerTool.class` (Java 8 / major 52)
|
||||||
|
Version-Stamp: `TOOL_VERSION = "2026-07-24d-restart-only"` (erste INFO-Zeile)
|
||||||
|
|
||||||
|
### 7.1 `main`
|
||||||
|
|
||||||
|
1. `INFO:ToolVersion=…` ausgeben
|
||||||
|
2. Args parsen – nur Befehl `restart` erlaubt
|
||||||
|
3. Pflicht: `--domain`, `--url`, `--user`, `--container`
|
||||||
|
4. Passwort: `--password` oder Env `ESB_SONIC_PASSWORD`
|
||||||
|
5. `connect(...)`
|
||||||
|
6. `restart(...)`
|
||||||
|
7. `disconnect` im `finally`
|
||||||
|
|
||||||
|
### 7.2 `connect` – Einloggen wie SMC
|
||||||
|
|
||||||
|
```text
|
||||||
|
Hashtable env:
|
||||||
|
ConnectionURLs = tcp://…
|
||||||
|
DefaultUser = Administrator
|
||||||
|
DefaultPassword = …
|
||||||
|
|
||||||
|
JMSConnectorAddress(env)
|
||||||
|
JMSConnectorClient()
|
||||||
|
optional: setTimeout / setRequestTimeout
|
||||||
|
client.connect(address [, timeout])
|
||||||
|
```
|
||||||
|
|
||||||
|
Klassen (aus den JARs, per Reflection):
|
||||||
|
|
||||||
|
- `com.sonicsw.mf.jmx.client.JMSConnectorAddress`
|
||||||
|
- `com.sonicsw.mf.jmx.client.JMSConnectorClient`
|
||||||
|
|
||||||
|
Reflection statt direkter Compile-Abhängigkeit: das Tool kompiliert ohne Sonic-JARs auf dem Build-Rechner; zur Laufzeit müssen die JARs im Classpath liegen.
|
||||||
|
|
||||||
|
### 7.3 `restart` – ObjectName und Lifecycle
|
||||||
|
|
||||||
|
1. Kurzname bilden (`proalpha-test.ct-ZADBService` → `ct-ZADBService`)
|
||||||
|
2. ObjectName:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<DomainName>.<ContainerKurzname>:ID=AGENT
|
||||||
|
→ proalpha-test.ct-ZADBService:ID=AGENT
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Operationen der Reihe nach:
|
||||||
|
|
||||||
|
| Versuch | Operation | Bedeutung |
|
||||||
|
|---------|-----------|-----------|
|
||||||
|
| 1 | `stop` | Agent stoppen (SMC macht oft faktisch Stop; Daemon startet neu) |
|
||||||
|
| 2 | `restart` | falls vorhanden und erlaubt |
|
||||||
|
|
||||||
|
4. Aufruf:
|
||||||
|
|
||||||
|
```text
|
||||||
|
connector.invoke(objectName, op, new Object[0], new String[0])
|
||||||
|
```
|
||||||
|
|
||||||
|
Das ist der JMX/`MBeanServerConnection.invoke`-Weg über den Sonic-Management-Connector.
|
||||||
|
|
||||||
|
### 7.4 Side-Effect bei `stop`
|
||||||
|
|
||||||
|
Wenn `stop` eine Exception wirft, deren Message auf Verbindungsabbruch hindeutet (`disconnect`, `closed`, `not connected`, `connection lost`), wertet das Tool das als **Erfolg**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK:RestartInvoked method=MBean.stop(side-effect) …
|
||||||
|
```
|
||||||
|
|
||||||
|
Grund: der Agent geht runter und reißt oft die Management-Session – das ist erwartbar, kein „echter“ Fehlschlag.
|
||||||
|
|
||||||
|
### 7.5 Erfolgszeile
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK:RestartInvoked method=MBean.stop container=ct-ZADBService domain=proalpha-test tool=2026-07-24d-restart-only
|
||||||
|
```
|
||||||
|
|
||||||
|
C# sucht genau nach `OK:RestartInvoked`.
|
||||||
|
|
||||||
|
### 7.6 Ausgabe-Konventionen
|
||||||
|
|
||||||
|
| Präfix | Bedeutung |
|
||||||
|
|---------|-----------|
|
||||||
|
| `INFO:` | Fortschritt / ObjectName / Trying … |
|
||||||
|
| `WARN:` | fehlgeschlagener Versuch, Weiterversuch |
|
||||||
|
| `ERROR:` | fatal, ExitCode ≠ 0 |
|
||||||
|
| `OK:RestartInvoked` | Erfolg |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Sequenz (zeitlich)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant UI as Form1
|
||||||
|
participant Orch as DeploymentOrchestrator
|
||||||
|
participant RE as RestartExecutor
|
||||||
|
participant MC as SonicManagementClient
|
||||||
|
participant EX as SonicMfApiExecutor
|
||||||
|
participant JV as SonicMfContainerTool
|
||||||
|
participant DM as Sonic Domain Manager
|
||||||
|
|
||||||
|
UI->>Orch: RestartOnlyAsync(selected)
|
||||||
|
Orch->>RE: ExecuteAsync(target)
|
||||||
|
RE->>MC: RestartContainerAsync(ct-ZADBService)
|
||||||
|
MC->>EX: RestartAsync(...)
|
||||||
|
EX->>EX: PrepareTool (java + jars + class)
|
||||||
|
EX->>JV: Process start restart …
|
||||||
|
JV->>DM: JMSConnectorClient.connect
|
||||||
|
JV->>DM: invoke(…:ID=AGENT, "stop")
|
||||||
|
DM-->>JV: ok / disconnect side-effect
|
||||||
|
JV-->>EX: OK:RestartInvoked
|
||||||
|
EX-->>MC: Success=true
|
||||||
|
MC->>MC: Delay PostRestartDelaySeconds
|
||||||
|
MC-->>UI: Status + Detail
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Dateien und Verantwortlichkeiten
|
||||||
|
|
||||||
|
| Datei | Rolle |
|
||||||
|
|-------|--------|
|
||||||
|
| `Form1.cs` | UI, Auswahl, Bestätigung, Status |
|
||||||
|
| `Services/DeploymentOrchestrator.cs` | Lauf orchestrieren, Log |
|
||||||
|
| `Services/PreflightValidator.cs` | Vorabprüfung |
|
||||||
|
| `Services/RestartExecutor.cs` | Ziel → Connection |
|
||||||
|
| `Services/SonicManagementClient.cs` | Restart + Delay |
|
||||||
|
| `Services/SonicMfApiExecutor.cs` | Java-Prozess, Classpath, Erfolgsauswertung |
|
||||||
|
| `Services/SonicContainerDiscovery.cs` | KnownContainers → Grid-Ziele |
|
||||||
|
| `Models/SonicConnection.cs` | Konfigurationsmodell |
|
||||||
|
| `Models/DeploymentTarget.cs` | Zielzeile |
|
||||||
|
| `Tools/SonicMfContainerTool.java` | API-Client (Quellcode) |
|
||||||
|
| `Tools/SonicMfContainerTool.class` | ausgeliefert / Java-8-Bytecode |
|
||||||
|
| `appsettings.json` | Verbindungen + Containerliste |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Laufzeit-Voraussetzungen
|
||||||
|
|
||||||
|
1. **Java 8+** erreichbar (`JavaPath` empfohlen)
|
||||||
|
2. **`MfClientLibPath`** zeigt auf Ordner mit Sonic-Client-JARs
|
||||||
|
3. **`Tools\SonicMfContainerTool.class`** liegt neben der gebauten App
|
||||||
|
4. Netzwerk zu `ConnectionUrl` (Port z. B. 13070)
|
||||||
|
5. Gültige SMC-Credentials
|
||||||
|
6. Containername stimmt mit SMC überein (`ct-ZADBService`)
|
||||||
|
7. Domain Manager / Launch Daemon müssen Container nach Stop wieder starten können
|
||||||
|
|
||||||
|
### Tool neu kompilieren
|
||||||
|
|
||||||
|
```text
|
||||||
|
javac --release 8 -encoding UTF-8 Tools\SonicMfContainerTool.java
|
||||||
|
```
|
||||||
|
|
||||||
|
(oder `-source 1.8 -target 1.8`)
|
||||||
|
|
||||||
|
Danach App neu bauen, damit `.class` nach `bin\…\Tools\` kopiert wird (csproj Content-Copy).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Typische Fehler und Ursache
|
||||||
|
|
||||||
|
| Symptom | Ursache | Maßnahme |
|
||||||
|
|---------|---------|----------|
|
||||||
|
| `ClassNotFoundException: JMSConnectorAddress` | Classpath ohne MF-JARs / falsche Quotes | `MfClientLibPath` prüfen; `ArgumentList` nicht manuell quoten |
|
||||||
|
| `UnsupportedClassVersionError` | Runtime zu alt oder `.class` zu neu | Java 8+ Runtime; Tool mit `--release 8` bauen |
|
||||||
|
| `Java nicht gefunden` | `JavaPath` falsch | appsettings korrigieren |
|
||||||
|
| `Tools\SonicMfContainerTool.class nicht gefunden` | Build/Copy fehlt | Projekt neu bauen |
|
||||||
|
| `Neustart fehlgeschlagen` + Attempts | MBean `stop`/`restart` abgelehnt | ObjectName/Rechte/Containerstatus in SMC prüfen |
|
||||||
|
| Timeout | Domain Manager hängt / Netz | `TimeoutSeconds`, Firewall, Broker |
|
||||||
|
| Falscher Container | Alias statt Kurzname | `KnownContainers`: `ct-ZADBService` |
|
||||||
|
| Kein `ToolVersion=` in Output | alte/falsche `.class` | Tools neu kompilieren, Output-Ordner prüfen |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Verhältnis zur SMC
|
||||||
|
|
||||||
|
| SMC | Diese App |
|
||||||
|
|-----|-----------|
|
||||||
|
| Login mit User/Pass auf Domain-URL | dieselben Werte in appsettings |
|
||||||
|
| Container im Baum wählen | `KnownContainers` + Grid |
|
||||||
|
| Rechtsklick → Restart | `MBean.invoke("stop"|"restart")` auf `…:ID=AGENT` |
|
||||||
|
| UI zeigt Online/Offline | App wartet nur `PostRestartDelaySeconds`, pollt Status nicht |
|
||||||
|
|
||||||
|
Operativ oft: **Stop am Agent-MBean**; der **Launch Daemon** bringt den Container wieder hoch – analog zu vielen SMC-Restart-Verhalten bei remote „unbounded“ Clients.
|
||||||
|
|
||||||
|
Früher getestet: `IAgentProxy.restart()` schlägt remote häufig mit
|
||||||
|
`Operation unsupported for unbounded client connector` fehl – deshalb der direkte MBean-Weg.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Sicherheitshinweise
|
||||||
|
|
||||||
|
- Passwort steht in `appsettings.json` (Klartext) und kurzzeitig in der Prozess-Umgebung `ESB_SONIC_PASSWORD`.
|
||||||
|
- Nicht in öffentliche Repos committen bzw. Secrets auslagern.
|
||||||
|
- Logs (`LogDirectory`) können Tool-Output enthalten – ggf. sensible Zeilen beachten.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Manueller Test (ohne UI)
|
||||||
|
|
||||||
|
Mit denselben Werten wie in appsettings:
|
||||||
|
|
||||||
|
```text
|
||||||
|
set ESB_SONIC_PASSWORD=Administrator
|
||||||
|
|
||||||
|
java -cp "Tools;C:\DEV\MQ10.0\lib\*" SonicMfContainerTool restart ^
|
||||||
|
--domain proalpha-test ^
|
||||||
|
--url tcp://dekun-painwbdet:13070 ^
|
||||||
|
--user Administrator ^
|
||||||
|
--container ct-ZADBService ^
|
||||||
|
--timeout 120
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartet u. a.:
|
||||||
|
|
||||||
|
```text
|
||||||
|
INFO:ToolVersion=2026-07-24d-restart-only
|
||||||
|
INFO:ObjectName=proalpha-test.ct-ZADBService:ID=AGENT
|
||||||
|
INFO:Trying MBean.invoke(stop)
|
||||||
|
OK:RestartInvoked method=MBean.stop container=ct-ZADBService domain=proalpha-test tool=2026-07-24d-restart-only
|
||||||
|
```
|
||||||
|
|
||||||
|
(Unter Windows Classpath ggf. mit `;` und expliziten JAR-Namen statt `*`, je nach Shell.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Kurzfassung
|
||||||
|
|
||||||
|
1. UI wählt Ziel aus `KnownContainers`.
|
||||||
|
2. C# findet die passenden Sonic-Verbindungsdaten.
|
||||||
|
3. C# startet Java mit MF-Client-JARs + `SonicMfContainerTool`.
|
||||||
|
4. Java verbindet sich wie die SMC an den Domain Manager.
|
||||||
|
5. Java ruft auf dem Agent-MBean `stop` (sonst `restart`) auf.
|
||||||
|
6. Bei `OK:RestartInvoked` wartet C# noch `PostRestartDelaySeconds` und meldet Erfolg.
|
||||||
|
|
||||||
|
**Der einzige echte Sonic-API-Call der App ist:**
|
||||||
|
`JMSConnectorClient.invoke(<domain>.<container>:ID=AGENT, "stop"|"restart", …)`.
|
||||||
@@ -0,0 +1,371 @@
|
|||||||
|
# ESB Certificate Manager – Neustart, Management-API & Zertifikatsprüfung
|
||||||
|
|
||||||
|
Ausführliche Erklärung für Präsentation, Übergabe und Troubleshooting.
|
||||||
|
Stand: Branch `fix/mfapi-classpath-ct-zadbservice` (MfApi-Neustart, Zertifikat erkennen).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Was die App heute macht (und was nicht)
|
||||||
|
|
||||||
|
### Aktiv
|
||||||
|
|
||||||
|
| Funktion | Kurzbeschreibung |
|
||||||
|
|----------|------------------|
|
||||||
|
| **Zertifikat erkennen** | Datei wählen (`.cer` / `.crt` / `.pem` / `.pfx`), Metadaten anzeigen, Gültigkeit prüfen |
|
||||||
|
| **Container laden** | Optional: Liste aus Sonic Domain Manager + Fallback `KnownContainers` |
|
||||||
|
| **ESB neu starten** | Container-Neustart über Sonic **Management Application API** (wie Restart in der SMC) |
|
||||||
|
|
||||||
|
### Bewusst nicht (mehr) aktiv
|
||||||
|
|
||||||
|
| Entfernt / deaktiviert | Warum |
|
||||||
|
|------------------------|--------|
|
||||||
|
| Zertifikat auf Zielpfad **kopieren** (Deploy) | UI-Button ausgeblendet; war eigener Deploy-Pfad |
|
||||||
|
| **TLS-Probe** nach Deploy | Fingerprint gegen laufenden Endpoint – nicht Teil des aktuellen Scope |
|
||||||
|
| **XApi-Import** | Extra-Skript/WinRM-Pfad, oft nicht vorhanden |
|
||||||
|
| **WinRM** / `stopcontainer.bat` | SMC-only-Installationen haben die Server-Scripts oft nicht; Logins ≠ Windows |
|
||||||
|
| **HTTP-REST** Management | Kein stabiler Endpoint in dieser Umgebung nachweisbar |
|
||||||
|
| **SQL- / Offline-Sample-Ziele** | Container kommen aus `KnownContainers` / Live-Discovery |
|
||||||
|
|
||||||
|
Kurz: **Datei prüfen + Neustart**. Alles andere war Ballast für den aktuellen Use-Case.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Begriffe (wichtig für die Erklärung)
|
||||||
|
|
||||||
|
| Begriff | Bedeutung in diesem Projekt |
|
||||||
|
|---------|-----------------------------|
|
||||||
|
| **SMC** | Sonic Management Console – die GUI, mit der Operatoren Container starten/stoppen/neustarten |
|
||||||
|
| **Domain** | Logische Sonic-Domain, hier: `proalpha-test` |
|
||||||
|
| **Container** | Laufzeit-Container / Agent, hier: `ct-ZADBService` |
|
||||||
|
| **Verbindungs-Alias** | Name in `appsettings` (`DE-Test`) – **nicht** der Containername |
|
||||||
|
| **ObjectName** | JMX-Name: `proalpha-test.ct-ZADBService:ID=AGENT` |
|
||||||
|
| **Domain Manager / Broker-URL** | `tcp://dekun-painwbdet:13070` – dieselbe URL wie beim SMC-Login |
|
||||||
|
| **MfApi** | Unser Modusname für „Management Application API“ über Java-Client-JARs |
|
||||||
|
| **unbounded client connector** | Remote-JMS-Client (wie die SMC von außen). Manche Proxy-Methoden (z. B. `IAgentProxy.restart()`) sind dafür gesperrt |
|
||||||
|
| **Launch Daemon** | Prozess auf dem Host, der einen gestoppten Container oft automatisch wieder startet (SMC „Restart“ ≈ Stop + Auto-Relaunch) |
|
||||||
|
|
||||||
|
**Merksatz für Präsentationen:**
|
||||||
|
> `DE-Test` ist die Verbindung. `ct-ZADBService` ist der Container. Die Domain heißt `proalpha-test`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Architektur – Überblick
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ WinForms UI (Form1) │
|
||||||
|
│ - Zertifikat wählen / anzeigen │
|
||||||
|
│ - Ziele anhaken │
|
||||||
|
│ - Button „ESB neu starten“ │
|
||||||
|
└───────────────────────────┬─────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ DeploymentOrchestrator.RestartOnlyAsync │
|
||||||
|
│ → PreflightValidator (ContainerName + SonicConnection) │
|
||||||
|
│ → RestartExecutor │
|
||||||
|
└───────────────────────────┬─────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ SonicManagementClient (dünne Fassade, nur MfApi) │
|
||||||
|
│ → SonicMfApiExecutor │
|
||||||
|
└───────────────────────────┬─────────────────────────────────┘
|
||||||
|
│ startet Prozess
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ java.exe (Java 8+) │
|
||||||
|
│ Classpath: Tools\ + C:\DEV\MQ10.0\lib\*.jar │
|
||||||
|
│ Klasse: SonicMfContainerTool │
|
||||||
|
│ Befehl: restart --domain … --url … --user … --container …│
|
||||||
|
└───────────────────────────┬─────────────────────────────────┘
|
||||||
|
│ JMS / JMX (Management Framework)
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Sonic Domain Manager │
|
||||||
|
│ tcp://dekun-painwbdet:13070 │
|
||||||
|
│ Agent: proalpha-test.ct-ZADBService:ID=AGENT │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
Es gibt **keine eigene REST-API der WinForms-App**.
|
||||||
|
Die „API“ ist die **Sonic Management Application API** (Java, JARs unter `MQ10.0\lib`), dieselbe Schicht, die auch die SMC nutzt.
|
||||||
|
|
||||||
|
Offizielle Doku-Verweise (Index im Repo):
|
||||||
|
|
||||||
|
- `doc-10.0.10/CXMessenger_2017_R3.htm` → **Management Application API Reference** (`Docs2017/api/mgmt_api/`)
|
||||||
|
- Bücher: MQ/ESB Configuration and Management Guide
|
||||||
|
|
||||||
|
(Die PDF-/HTML-API-Ordner können im Checkout fehlen – der Index verlinkt sie trotzdem.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Zertifikat – Erkennen & „Verifizieren“
|
||||||
|
|
||||||
|
### 4.1 Was wir heute tun
|
||||||
|
|
||||||
|
Die App **liest und prüft die Zertifikatsdatei lokal**. Sie spielt das Zertifikat **nicht** auf den ESB und prüft **nicht** per TLS-Handshake gegen einen Server (dieser Pfad ist deaktiviert).
|
||||||
|
|
||||||
|
Ablauf in der UI (`Form1`):
|
||||||
|
|
||||||
|
1. Benutzer wählt Datei (`.cer`, `.crt`, `.pem`, `.pfx`).
|
||||||
|
2. Datei wird mit `X509Certificate2` geladen.
|
||||||
|
3. Bei `.pfx` ggf. Passwort-Dialog.
|
||||||
|
4. Anzeige:
|
||||||
|
- Subject
|
||||||
|
- Issuer
|
||||||
|
- Gültig bis
|
||||||
|
- SHA-256-Fingerprint (formatiert mit `:`)
|
||||||
|
- Status **GÜLTIG** / **UNGÜLTIG / ABGELAUFEN** (Vergleich mit `DateTimeOffset.Now` und `NotBefore`/`NotAfter`)
|
||||||
|
|
||||||
|
Das ist **Dateiverifikation / Metadaten-Check**, kein Deploy und kein Remote-TLS-Verify.
|
||||||
|
|
||||||
|
### 4.2 Was „Zertifikat verifizieren“ bedeuten kann (für die Erklärung)
|
||||||
|
|
||||||
|
| Stufe | Bedeutung | Status in dieser App |
|
||||||
|
|-------|-----------|----------------------|
|
||||||
|
| **A. Datei lesen** | Datei ist ein gültiges X.509, Parsing ok | ✅ aktiv |
|
||||||
|
| **B. Zeitliche Gültigkeit** | `now` zwischen NotBefore und NotAfter | ✅ aktiv |
|
||||||
|
| **C. Fingerprint anzeigen** | SHA-256 zum manuellen Abgleich | ✅ aktiv |
|
||||||
|
| **D. Auf Ziel kopieren** | Datei nach `TargetDirectory` legen | ❌ deaktiviert |
|
||||||
|
| **E. TLS-Probe** | TCP/TLS zum Host:Port, Remote-Zertifikat lesen, Fingerprint vergleichen | ❌ entfernt / nicht aktiv |
|
||||||
|
| **F. Trust-Chain / CA** | Kette gegen Windows-Truststore prüfen | ❌ nicht implementiert |
|
||||||
|
|
||||||
|
Wenn jemand fragt „Wird das Zertifikat verifiziert?“:
|
||||||
|
|
||||||
|
> Wir verifizieren die **Datei lokal** (lesbar, Gültigkeitszeitraum, Fingerprint).
|
||||||
|
> Ein Abgleich gegen den **laufenden ESB-Endpoint per TLS** ist aktuell nicht Teil der App.
|
||||||
|
> Der Neustart betrifft den **Container**, nicht automatisch die Zertifikatsinstallation.
|
||||||
|
|
||||||
|
### 4.3 Typische Demo-Sätze
|
||||||
|
|
||||||
|
- „Hier lade ich das Zertifikat – Subject/Issuer/Fingerprint erscheinen sofort.“
|
||||||
|
- „Rot = abgelaufen oder außerhalb des Gültigkeitsfensters.“
|
||||||
|
- „Den Fingerprint kann man mit dem erwarteten Wert aus der Doku/SMC abgleichen.“
|
||||||
|
- „Kopieren auf den Server und TLS-Nachprüfung wären der nächste Ausbauschritt – bewusst getrennt vom Neustart.“
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Die Sonic Management Application API – warum sie kompliziert wirkt
|
||||||
|
|
||||||
|
### 5.1 Was Sonic bereitstellt (Produkt-API, nicht unsere Erfindung)
|
||||||
|
|
||||||
|
Sonic/Aurea CX Messenger hat mehrere API-Welten:
|
||||||
|
|
||||||
|
| API | Zweck | Nutzen wir für Neustart? |
|
||||||
|
|-----|--------|---------------------------|
|
||||||
|
| **Management Application API** (`mgmt_api`) | Domain Manager, Agenten, Restart/Stop wie SMC | ✅ ja |
|
||||||
|
| **CX Messenger MQ API** | Messaging (Queues, Producer/Consumer) | ❌ nein |
|
||||||
|
| **ESB API** | ESB-Services/Prozesse | ❌ nein |
|
||||||
|
| **Metrics / Notifications** | Monitoring | ❌ nein |
|
||||||
|
| **Administrative Programming Guide** | erweiterte Admin-Szenarien | nur indirekt als Konzept |
|
||||||
|
|
||||||
|
Für den Container-Neustart ist laut Produkt-Doku-Index die **Management Application API** der richtige Einstieg – analog zum SMC-Button „Restart“.
|
||||||
|
|
||||||
|
### 5.2 Kernklassen (die wirklich gebraucht werden)
|
||||||
|
|
||||||
|
| Klasse / Konzept | Rolle |
|
||||||
|
|------------------|--------|
|
||||||
|
| `JMSConnectorAddress` | Verbindungsparameter (URL, User, Pass) |
|
||||||
|
| `JMSConnectorClient` | JMS/JMX-Verbindung zum Domain Manager |
|
||||||
|
| `ObjectName` | Ziel-Agent, z. B. `domain.container:ID=AGENT` |
|
||||||
|
| `MFProxyFactory.createAgentProxy` | Typisierter Proxy (`IAgentProxy`) |
|
||||||
|
| `IAgentProxy` / MBean-Operationen | `stop` / `restart` / `shutdown` … |
|
||||||
|
| Client-JARs unter `lib` | u. a. `mgmt_client.jar`, `mfcontext.jar`, `sonic_Client.jar` |
|
||||||
|
|
||||||
|
### 5.3 Warum es trotzdem „viele unnötige Funktionen“ zu geben scheint
|
||||||
|
|
||||||
|
**Ja – die Sonic-API ist enorm breit.** Sie deckt Broker-Verwaltung, Queues, Durable Subscribers, Metrics, Config-Beans, Directory Service, Host Manager usw. ab.
|
||||||
|
**Für unseren Use-Case brauchen wir nur einen schmalen Schnitt:**
|
||||||
|
|
||||||
|
1. Verbinden
|
||||||
|
2. Agent-ObjectName finden
|
||||||
|
3. Lifecycle: Stop/Restart
|
||||||
|
|
||||||
|
Alles andere in den JARs und in der `mgmt_api`-Javadoc ist **Produktumfang**, nicht „unsere App-API“.
|
||||||
|
|
||||||
|
Zusätzlich wirkt **unser Java-Tool** (`SonicMfContainerTool`) kompliziert, weil es **Fallbacks** hat:
|
||||||
|
|
||||||
|
| Schritt | Was | Warum |
|
||||||
|
|---------|-----|--------|
|
||||||
|
| 1 | `MBean.invoke("stop"|"restart"|"shutdown")` | Remote/„unbounded“ oft der Weg, den SMC faktisch nutzt |
|
||||||
|
| 2 | `IAgentProxy.stop/restart/shutdown` | „Schöner“ typisierter Pfad – scheitert oft mit *unbounded* |
|
||||||
|
| 3 | DomainManager/`restartContainer` | Weitere Fallback-Variante |
|
||||||
|
|
||||||
|
Das ist **Absicherung**, kein Feature-Wahn. Man könnte später auf den einen Pfad reduzieren, der in eurer Umgebung stabil ist (vermutlich MBean `stop`).
|
||||||
|
|
||||||
|
### 5.4 „Unbounded client connector“ – Kernpunkt für Erklärungen
|
||||||
|
|
||||||
|
Remote-Clients (SMC, unser Tool) verbinden sich als **unbounded** JMS-Connector.
|
||||||
|
|
||||||
|
Manche Methoden am typisierten Proxy prüfen das und werfen:
|
||||||
|
|
||||||
|
```text
|
||||||
|
javax.management.JMRuntimeException:
|
||||||
|
Operation unsupported for unbounded client connector.
|
||||||
|
```
|
||||||
|
|
||||||
|
Deshalb:
|
||||||
|
|
||||||
|
- `IAgentProxy.restart()` allein ist **kein** zuverlässiger Remote-Weg.
|
||||||
|
- Stattdessen: JMX `invoke` auf dem Agent-MBean, typischerweise **`stop`**.
|
||||||
|
- Wenn der Container unter einem Launch Daemon / Host Manager läuft, kommt er wieder hoch → Operator-Wirkung „Restart“.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Neustart – End-to-End-Ablauf
|
||||||
|
|
||||||
|
### 6.1 Konfiguration (`appsettings.json`)
|
||||||
|
|
||||||
|
Beispiel (relevante Felder):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Name": "DE-Test",
|
||||||
|
"DomainName": "proalpha-test",
|
||||||
|
"ConnectionUrl": "tcp://dekun-painwbdet:13070",
|
||||||
|
"Username": "Administrator",
|
||||||
|
"Password": "Administrator",
|
||||||
|
"SonicHome": "C:\\DEV\\MQ10.0",
|
||||||
|
"JavaPath": "C:\\Program Files (x86)\\Java\\jre1.8.0_501\\bin\\java.exe",
|
||||||
|
"MfClientLibPath": "C:\\DEV\\MQ10.0\\lib",
|
||||||
|
"KnownContainers": [ "ct-ZADBService" ],
|
||||||
|
"TimeoutSeconds": 120,
|
||||||
|
"PostRestartDelaySeconds": 20
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Container in `appsettings` unter der Verbindung `DE-Test`:
|
||||||
|
|
||||||
|
- `KnownContainers`: `[ "ct-ZADBService" ]`
|
||||||
|
|
||||||
|
### 6.2 UI → C#
|
||||||
|
|
||||||
|
1. Benutzer hakt Ziel an → **ESB neu starten**.
|
||||||
|
2. `PreflightValidator`: `ContainerName` + `SonicConnectionName` müssen gesetzt sein.
|
||||||
|
3. `RestartExecutor` sucht die `SonicConnection` und ruft `SonicManagementClient.RestartContainerAsync` auf.
|
||||||
|
4. `SonicMfApiExecutor`:
|
||||||
|
- Java 8+ finden
|
||||||
|
- Classpath aus allen JARs unter `MfClientLibPath` / `SonicHome\lib` bauen
|
||||||
|
- `Tools\SonicMfContainerTool.class` (Java-8-Bytecode) laden
|
||||||
|
- Prozess starten mit sauberer Argumentübergabe (`ArgumentList`, keine doppelten Quotes)
|
||||||
|
|
||||||
|
Ungefährer Aufruf:
|
||||||
|
|
||||||
|
```text
|
||||||
|
java -cp "<Tools>;<jar1>;<jar2>;..." SonicMfContainerTool restart
|
||||||
|
--domain proalpha-test
|
||||||
|
--url tcp://dekun-painwbdet:13070
|
||||||
|
--user Administrator
|
||||||
|
--container ct-ZADBService
|
||||||
|
--timeout 120
|
||||||
|
```
|
||||||
|
|
||||||
|
Passwort: Umgebungsvariable `ESB_SONIC_PASSWORD` (nicht auf der Kommandozeile).
|
||||||
|
|
||||||
|
### 6.3 Java-Tool (`SonicMfContainerTool`)
|
||||||
|
|
||||||
|
1. Loggt `INFO:ToolVersion=…` (Build-Kontrolle).
|
||||||
|
2. `connect` über `JMSConnectorAddress` + `JMSConnectorClient`.
|
||||||
|
3. ObjectName: bevorzugt `proalpha-test.ct-ZADBService:ID=AGENT`.
|
||||||
|
4. Lifecycle-Versuche (siehe Abschnitt 5.3).
|
||||||
|
5. Erfolg: Zeile `OK:RestartInvoked method=…`
|
||||||
|
6. C# wertet `OK:RestartInvoked` als Erfolg und wartet `PostRestartDelaySeconds`.
|
||||||
|
|
||||||
|
### 6.4 Erfolgsdefinition
|
||||||
|
|
||||||
|
| Signal | Bedeutung |
|
||||||
|
|--------|-----------|
|
||||||
|
| `INFO:Connected …` | Login/URL ok |
|
||||||
|
| `INFO:ToolVersion=…` | Richtige Tool-Version läuft |
|
||||||
|
| `OK:RestartInvoked …` | Lifecycle-Aufruf durchgekommen |
|
||||||
|
| SMC zeigt Offline → Online / Uptime reset | Operative Bestätigung |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Braucht die API „viele unnötige Funktionen“?
|
||||||
|
|
||||||
|
### Kurze Antwort
|
||||||
|
|
||||||
|
**Die Sonic-Produkt-API ja – unser benötigter Schnitt nein.**
|
||||||
|
|
||||||
|
### Aufschlüsselung
|
||||||
|
|
||||||
|
| Schicht | Viele Funktionen? | Für uns nötig? |
|
||||||
|
|---------|-------------------|----------------|
|
||||||
|
| Sonic `mgmt_api` + Client-JARs | Ja, sehr breit | Nur Connect + Agent-Lifecycle |
|
||||||
|
| Unser C#-Client früher (WinRM, HTTP, XApi, Bat) | Ja, historisch gewachsen | Nein – weitgehend entfernt |
|
||||||
|
| `SonicMfContainerTool` Fallbacks (MBean / IAgentProxy / DomainManager) | Mehrere Wege | Nützlich bis ein Weg überall stabil ist; danach ausdünnbar |
|
||||||
|
| Zertifikat TLS-Probe / Deploy | Extra-Komplexität | Aktuell nicht nötig |
|
||||||
|
|
||||||
|
### Was man später noch verschlanken könnte
|
||||||
|
|
||||||
|
1. Im Java-Tool nur noch den einen erfolgreichen Weg behalten (z. B. nur `MBean.invoke("stop")`).
|
||||||
|
2. In `appsettings` tote Felder entfernen (`WinRm*`, `Container*Path`, `ManagementHttpPort`, …) – stehen teils noch in JSON, werden vom schlanken Client nicht mehr genutzt.
|
||||||
|
3. `SonicMfApiExecutor` Java-Discovery vereinfachen, sobald `JavaPath` überall fest gesetzt ist.
|
||||||
|
4. Optional: Zertifikat-Deploy + TLS-Probe als **eigenes** Modul wieder einführen – klar getrennt vom Neustart.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Voraussetzungen auf dem Rechner (Checkliste)
|
||||||
|
|
||||||
|
| Voraussetzung | Beispiel / Hinweis |
|
||||||
|
|---------------|--------------------|
|
||||||
|
| SMC-Client / MQ-Libs | `C:\DEV\MQ10.0\lib` mit `mgmt_client.jar`, `mfcontext.jar`, `sonic_Client.jar` |
|
||||||
|
| Java 8+ | z. B. `jre1.8.0_501\bin\java.exe` (nicht zu alte Sonic-JRE 6/7) |
|
||||||
|
| Netzwerk | Port `13070` zum Domain Manager erreichbar |
|
||||||
|
| Credentials | Dieselben wie SMC-Login |
|
||||||
|
| Containername | Exakt wie in SMC (`ct-ZADBService`), nicht der Alias `DE-Test` |
|
||||||
|
| App-Build | Frische `Tools\SonicMfContainerTool.class` (Ausgabe muss `ToolVersion` zeigen) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Typische Fehler & Interpretation
|
||||||
|
|
||||||
|
| Fehler / Symptom | Ursache | Maßnahme |
|
||||||
|
|------------------|---------|----------|
|
||||||
|
| `ClassNotFoundException: JMSConnectorAddress` | Classpath/JARs fehlen oder Quotes kaputt | `MfClientLibPath`, neuen Build, ArgumentList |
|
||||||
|
| `UnsupportedClassVersionError` | Tool mit zu neuem Java gebaut / zu alte Runtime | Tool major 52 + Java 8+ Runtime |
|
||||||
|
| `Operation unsupported for unbounded…` | `IAgentProxy.restart` remote gesperrt | MBean `stop` / neuer Tool-Build mit Fallbacks |
|
||||||
|
| Kein `INFO:ToolVersion=` | Alte `.class` im Output | Neu bauen, Debug/Release-Ordner prüfen |
|
||||||
|
| Container `DE-Test` | Alias statt Kurzname | `ct-ZADBService` setzen |
|
||||||
|
| `stopcontainer.bat` fehlt | SMC-only | Erwartet – MfApi nutzen, nicht Bat |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Dateien im Code (Orientierung)
|
||||||
|
|
||||||
|
| Datei | Rolle |
|
||||||
|
|-------|--------|
|
||||||
|
| `Form1.cs` | UI: Zertifikat + Ziele + Neustart (Design) |
|
||||||
|
| `Services/DeploymentOrchestrator.cs` | Nur `RestartOnlyAsync` |
|
||||||
|
| `Services/RestartExecutor.cs` | Mappt Ziel → Sonic-Verbindung |
|
||||||
|
| `Services/SonicManagementClient.cs` | Fassade MfApi |
|
||||||
|
| `Services/SonicMfApiExecutor.cs` | Java starten, Classpath, Tool |
|
||||||
|
| `Tools/SonicMfContainerTool.java` | Eigentliche Management-API-Aufrufe |
|
||||||
|
| `appsettings.json` | Domain, URL, Login, Java, Lib-Pfad, KnownContainers |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Präsentations-Skript (kurz)
|
||||||
|
|
||||||
|
1. **Problem:** Zertifikat prüfen und ESB-Container neu starten – ohne manuelle SMC-Klicks jedes Mal.
|
||||||
|
2. **Zertifikat:** Datei laden → Subject/Issuer/Fingerprint/Gültigkeit. Lokal verifiziert, noch kein Deploy.
|
||||||
|
3. **Neustart:** Dieselbe Management-API wie die SMC, nicht FTP, nicht `stopcontainer.bat`.
|
||||||
|
4. **Technik:** C# orchestriert, Java + Sonic-Client-JARs sprechen mit dem Domain Manager.
|
||||||
|
5. **Komplexität:** Die Sonic-API ist riesig; wir nutzen nur Connect + Agent-Stop/Restart. Fallbacks wegen „unbounded connector“.
|
||||||
|
6. **Ergebnis:** Container `ct-ZADBService` in Domain `proalpha-test` wird remote neu gestartet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Offene Punkte / nächste sinnvolle Ausbauten
|
||||||
|
|
||||||
|
1. Zertifikat **nach** Erkennung auf den Zielhost legen (Deploy) – bewusst getrennt.
|
||||||
|
2. Optional **TLS-Probe** (Remote-Fingerprint) nach Deploy.
|
||||||
|
3. Java-Tool auf den einen erfolgreichen Lifecycle-Pfad reduzieren.
|
||||||
|
4. `appsettings` von Altlast-Feldern (WinRM/HTTP) bereinigen.
|
||||||
|
5. Fehlende offizielle `mgmt_api`-HTML/PDF ins Repo legen, damit man in Reviews direkt die Klassen verlinken kann.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Dokument erzeugt für Übergabe/Erklärung. Bei Abweichungen gilt der Code auf Branch `fix/mfapi-classpath-ct-zadbservice`.*
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
ClientSize = new Size(800, 450);
|
ClientSize = new Size(800, 450);
|
||||||
Name = "Form1";
|
Name = "Form1";
|
||||||
Text = "Form1";
|
Text = "Form1";
|
||||||
|
Load += Form1_Load;
|
||||||
ResumeLayout(false);
|
ResumeLayout(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,16 +2,66 @@ namespace ZA.CoreService.ESBCertificateManager.Models;
|
|||||||
|
|
||||||
public sealed class AppSettings
|
public sealed class AppSettings
|
||||||
{
|
{
|
||||||
public string ConnectionString { get; set; } = string.Empty;
|
public string EnvironmentCode { get; set; } = "TEST";
|
||||||
public bool UseOfflineSampleData { get; set; } = true;
|
|
||||||
public string SampleTargetsPath { get; set; } = "Data/targets.sample.json";
|
|
||||||
public string LogDirectory { get; set; } = "Logs";
|
public string LogDirectory { get; set; } = "Logs";
|
||||||
public int TlsTimeoutSeconds { get; set; } = 8;
|
|
||||||
public int TlsRetryCount { get; set; } = 2;
|
public DatabaseSettings Database { get; set; } = new();
|
||||||
|
|
||||||
|
public RuntimeSettings Runtime { get; set; } = new();
|
||||||
|
|
||||||
|
public AlwaysEncryptedSettings AlwaysEncrypted { get; set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verbindungskonfigurationen für Progress Sonic ESB Management Instanzen.
|
/// Wenn aktiv, erscheint der Demo-UNC-Pfad auch ohne SQL-Update in der Zielliste.
|
||||||
/// Jeder Eintrag entspricht einer Sonic-Domain (z.B. einer Umgebung oder Tochtergesellschaft).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<SonicConnection> SonicConnections { get; set; } = [];
|
public DemoSettings Demo { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DemoSettings
|
||||||
|
{
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
|
||||||
|
public string TargetDirectory { get; set; } =
|
||||||
|
DemoCertificatePath.Directory;
|
||||||
|
|
||||||
|
public string TargetFileName { get; set; } =
|
||||||
|
DemoCertificatePath.FileName;
|
||||||
|
|
||||||
|
public string ContainerName { get; set; } =
|
||||||
|
"ct-ZADBService";
|
||||||
|
|
||||||
|
public string DisplayName { get; set; } =
|
||||||
|
"DE / Demo ZADBService";
|
||||||
|
|
||||||
|
public string SonicConnectionName { get; set; } =
|
||||||
|
"DE-Test";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DatabaseSettings
|
||||||
|
{
|
||||||
|
public string ConnectionString { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class RuntimeSettings
|
||||||
|
{
|
||||||
|
public string JavaExecutablePath { get; set; } =
|
||||||
|
@"Runtime\bin\java.exe";
|
||||||
|
|
||||||
|
public string SonicClientLibraryPath { get; set; } =
|
||||||
|
@"Runtime\SonicClient\lib";
|
||||||
|
|
||||||
|
public bool SetupCompleted { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class AlwaysEncryptedSettings
|
||||||
|
{
|
||||||
|
public string CertificateThumbprint { get; set; } =
|
||||||
|
string.Empty;
|
||||||
|
|
||||||
|
public string StoreName { get; set; } =
|
||||||
|
"My";
|
||||||
|
|
||||||
|
public string StoreLocation { get; set; } =
|
||||||
|
"CurrentUser";
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
using ZA.CoreService.ESBCertificateManager.Services;
|
||||||
|
|
||||||
|
namespace ZA.CoreService.ESBCertificateManager.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aus dbo.SonicConnection geladene Verbindungsdaten.
|
||||||
|
/// Zugangsdaten liegen ausschließlich in dbo.SonicCredential.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DatabaseSonicConnection
|
||||||
|
{
|
||||||
|
public int Id { get; init; }
|
||||||
|
|
||||||
|
public required string Name { get; init; }
|
||||||
|
|
||||||
|
public required string ManagementHost { get; init; }
|
||||||
|
|
||||||
|
public int ManagementPort { get; init; }
|
||||||
|
|
||||||
|
public string? ConnectionProtocol { get; init; }
|
||||||
|
|
||||||
|
public string? DomainName { get; init; }
|
||||||
|
|
||||||
|
public string? EnvironmentCode { get; init; }
|
||||||
|
|
||||||
|
public bool IsActive { get; init; }
|
||||||
|
|
||||||
|
public string BuildConnectionUrl()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(ConnectionProtocol))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Bei der Sonic-Verbindung '{Name}' fehlt ConnectionProtocol.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(ManagementHost))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Bei der Sonic-Verbindung '{Name}' fehlt ManagementHost.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ManagementPort is < 1 or > 65535)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Bei der Sonic-Verbindung '{Name}' fehlt ein gültiger ManagementPort.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string protocol = ConnectionProtocol
|
||||||
|
.Trim()
|
||||||
|
.TrimEnd(':', '/');
|
||||||
|
|
||||||
|
string host = ManagementHost.Trim();
|
||||||
|
|
||||||
|
return $"{protocol}://{host}:{ManagementPort}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public SonicConnection ToRuntimeConnection(
|
||||||
|
RuntimeSettings runtimeSettings,
|
||||||
|
SonicCredentialProfile credential)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(credential);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(DomainName))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Bei der Sonic-Verbindung '{Name}' fehlt DomainName.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!credential.IsComplete)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Bei der Sonic-Verbindung '{Name}' fehlen die Zugangsdaten. " +
|
||||||
|
"Bitte die Einstellungen öffnen und ein Benutzerprofil hinterlegen.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string? javaExecutablePath =
|
||||||
|
JavaRuntimeLocator.Resolve(
|
||||||
|
runtimeSettings.JavaExecutablePath);
|
||||||
|
|
||||||
|
if (javaExecutablePath is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Java 8 wurde nicht gefunden. " +
|
||||||
|
"Bitte Runtime:JavaExecutablePath auf " +
|
||||||
|
@"jre1.8*\bin\java.exe setzen.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string sonicClientLibraryPath =
|
||||||
|
PathResolver.ResolvePath(
|
||||||
|
runtimeSettings.SonicClientLibraryPath);
|
||||||
|
|
||||||
|
string javaBinDirectory =
|
||||||
|
Path.GetDirectoryName(javaExecutablePath)
|
||||||
|
?? string.Empty;
|
||||||
|
|
||||||
|
string javaHome =
|
||||||
|
Directory.GetParent(javaBinDirectory)?.FullName
|
||||||
|
?? string.Empty;
|
||||||
|
|
||||||
|
string sonicHome =
|
||||||
|
Directory.GetParent(sonicClientLibraryPath)?.FullName
|
||||||
|
?? string.Empty;
|
||||||
|
|
||||||
|
return new SonicConnection
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
DomainName = DomainName,
|
||||||
|
ConnectionUrl = BuildConnectionUrl(),
|
||||||
|
Username = credential.UserName,
|
||||||
|
|
||||||
|
// Nur Laufzeitwert, bereits clientseitig entschlüsselt.
|
||||||
|
Password = credential.Secret,
|
||||||
|
|
||||||
|
JavaPath = javaExecutablePath,
|
||||||
|
JavaHome = javaHome,
|
||||||
|
MfClientLibPath = sonicClientLibraryPath,
|
||||||
|
SonicHome = sonicHome,
|
||||||
|
|
||||||
|
TimeoutSeconds = 120,
|
||||||
|
PostRestartDelaySeconds = 20
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
namespace ZA.CoreService.ESBCertificateManager.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fester Demo-Pfad für den ersten Zertifikat-Austausch-Test (TEST / DE).
|
||||||
|
/// </summary>
|
||||||
|
public static class DemoCertificatePath
|
||||||
|
{
|
||||||
|
public const string Directory =
|
||||||
|
@"\\dekun-painwbdet\e$\proalpha-inwb\test\Containers\proalpha-test.ct-ZADBService\certs";
|
||||||
|
|
||||||
|
public const string FileName = "server.p12";
|
||||||
|
|
||||||
|
public static string FullPath =>
|
||||||
|
Path.Combine(Directory, FileName);
|
||||||
|
}
|
||||||
@@ -9,6 +9,16 @@ public sealed class DeploymentTarget
|
|||||||
public required string TargetDirectory { get; init; }
|
public required string TargetDirectory { get; init; }
|
||||||
public required string CertificateFileName { get; init; }
|
public required string CertificateFileName { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Vollständiger Dateipfad (UNC oder lokal) zur Zieldatei.</summary>
|
||||||
|
public string FullTargetPath =>
|
||||||
|
Path.Combine(TargetDirectory, CertificateFileName);
|
||||||
|
|
||||||
|
public bool BackupEnabled { get; init; } = true;
|
||||||
|
|
||||||
|
public string BackupDirectoryName { get; init; } = "Backup";
|
||||||
|
|
||||||
|
public int? BackupRetentionDays { get; init; }
|
||||||
|
|
||||||
/// <summary>Name des Sonic-ESB-Containers (z.B. "sonic-container-a").</summary>
|
/// <summary>Name des Sonic-ESB-Containers (z.B. "sonic-container-a").</summary>
|
||||||
public string ContainerName { get; init; } = string.Empty;
|
public string ContainerName { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
namespace ZA.CoreService.ESBCertificateManager.Models;
|
||||||
|
|
||||||
|
public sealed class LocalSetupSelection
|
||||||
|
{
|
||||||
|
public string EnvironmentCode { get; set; } =
|
||||||
|
string.Empty;
|
||||||
|
|
||||||
|
public int SonicConnectionId { get; set; }
|
||||||
|
|
||||||
|
public int SonicCredentialId { get; set; }
|
||||||
|
|
||||||
|
public string ConnectionName { get; set; } =
|
||||||
|
string.Empty;
|
||||||
|
|
||||||
|
public string CredentialName { get; set; } =
|
||||||
|
string.Empty;
|
||||||
|
|
||||||
|
public DateTimeOffset SavedAtUtc { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
namespace ZA.CoreService.ESBCertificateManager.Models;
|
||||||
|
|
||||||
|
public sealed class CompanyOption
|
||||||
|
{
|
||||||
|
public int CompanyId { get; init; }
|
||||||
|
|
||||||
|
public required string CompanyCode { get; init; }
|
||||||
|
|
||||||
|
public required string CompanyName { get; init; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{CompanyCode} — {CompanyName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ContainerOption
|
||||||
|
{
|
||||||
|
public int SonicContainerId { get; init; }
|
||||||
|
|
||||||
|
public int SonicConnectionId { get; init; }
|
||||||
|
|
||||||
|
public int CompanyId { get; init; }
|
||||||
|
|
||||||
|
public required string ContainerName { get; init; }
|
||||||
|
|
||||||
|
public string? ContainerDisplayName { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Endpoint für TLS-Zertifikatsprüfung, z. B. https://host:8443 oder host:8443.
|
||||||
|
/// </summary>
|
||||||
|
public string? CertificateCheckUrl { get; init; }
|
||||||
|
|
||||||
|
public required string CompanyCode { get; init; }
|
||||||
|
|
||||||
|
public required string ConnectionName { get; init; }
|
||||||
|
|
||||||
|
public int RestartTimeoutSeconds { get; init; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
string display =
|
||||||
|
string.IsNullOrWhiteSpace(ContainerDisplayName)
|
||||||
|
? ContainerName
|
||||||
|
: ContainerDisplayName;
|
||||||
|
|
||||||
|
return $"{CompanyCode} / {display} ({ConnectionName})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class CertificateTargetOption
|
||||||
|
{
|
||||||
|
public int CertificateTargetId { get; init; }
|
||||||
|
|
||||||
|
public int SonicContainerId { get; init; }
|
||||||
|
|
||||||
|
public required string TargetDirectory { get; init; }
|
||||||
|
|
||||||
|
public required string TargetFileName { get; init; }
|
||||||
|
|
||||||
|
public bool BackupEnabled { get; init; }
|
||||||
|
|
||||||
|
public required string BackupDirectoryName { get; init; }
|
||||||
|
|
||||||
|
public required string ContainerName { get; init; }
|
||||||
|
|
||||||
|
public required string ConnectionName { get; init; }
|
||||||
|
|
||||||
|
public required string CompanyCode { get; init; }
|
||||||
|
|
||||||
|
public string FullTargetPath =>
|
||||||
|
Path.Combine(TargetDirectory, TargetFileName);
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{CompanyCode} / {ContainerName}: {FullTargetPath}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,398 +1,48 @@
|
|||||||
namespace ZA.CoreService.ESBCertificateManager.Models;
|
namespace ZA.CoreService.ESBCertificateManager.Models;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verbindungskonfiguration für eine Progress Sonic ESB Management Instanz.
|
/// Sonic-Verbindung für MfApi-Container-Restart (wie SMC).
|
||||||
/// Name = Verbindungs-Alias (hier oft gleich ContainerName, z.B. "DE-Test").
|
|
||||||
/// DomainName = Sonic-Domain (z.B. "proalpha-test").
|
|
||||||
/// Container wird in KnownContainers / DeploymentTarget.ContainerName geführt (z.B. "DE-Test").
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SonicConnection
|
public sealed class SonicConnection
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>Alias; referenziert von DeploymentTarget.SonicConnectionName.</summary>
|
||||||
/// Alias der Verbindung; referenziert von <see cref="DeploymentTarget.SonicConnectionName"/>.
|
|
||||||
/// </summary>
|
|
||||||
public required string Name { get; init; }
|
public required string Name { get; init; }
|
||||||
|
|
||||||
/// <summary>Sonic-Domain, z.B. "proalpha-test" (nicht der Containername).</summary>
|
/// <summary>Sonic-Domain, z.B. proalpha-test.</summary>
|
||||||
public required string DomainName { get; init; }
|
public required string DomainName { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Broker-/Management-URL wie in der SMC, z.B. tcp://host:13070.</summary>
|
||||||
/// Dieselbe Broker-/Management-URL wie in der Sonic Management Console (SMC),
|
|
||||||
/// z.B. "tcp://dekun-painwbdet:13070". Keine separate Domain-Console nötig.
|
|
||||||
/// </summary>
|
|
||||||
public required string ConnectionUrl { get; init; }
|
public required string ConnectionUrl { get; init; }
|
||||||
|
|
||||||
/// <summary>Login wie in der Sonic Management Console (nicht für WinRM).</summary>
|
|
||||||
public required string Username { get; init; }
|
public required string Username { get; init; }
|
||||||
|
|
||||||
/// <summary>Passwort wie in der Sonic Management Console (nicht für WinRM).</summary>
|
|
||||||
public required string Password { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Windows-Konto für WinRM (<c>Invoke-Command -Credential</c>).
|
/// Nur zur Laufzeit aus Always Encrypted geladen.
|
||||||
/// Leer = aktueller Prozess-Benutzer ohne explizite Credentials.
|
/// Darf nicht geloggt oder dauerhaft gespeichert werden.
|
||||||
/// Nicht mit <see cref="Username"/>/<see cref="Password"/> (Sonic SMC) verwechseln.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string WinRmUsername { get; init; } = string.Empty;
|
public string Password { get; init; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>Windows-Passwort für WinRM; nur relevant wenn <see cref="WinRmUsername"/> gesetzt ist.</summary>
|
|
||||||
public string WinRmPassword { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MfApi = Sonic MF Management Runtime API über ConnectionUrl + SMC-Logins (Standard);
|
/// Verweis auf eine generische Windows-Anmeldeinformation.
|
||||||
/// WinRm / LocalCmd = optionaler Fallback über stopcontainer/startcontainer;
|
/// Enthält ausdrücklich kein Kennwort.
|
||||||
/// HttpApi = REST (falls vorhanden).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <summary>
|
public string CredentialReference { get; init; } = string.Empty;
|
||||||
/// Standard: MfApi = Management Application API (wie SMC):
|
|
||||||
/// JMSConnectorClient + MFProxyFactory.createAgentProxy + IAgentProxy.restart.
|
|
||||||
/// Laut CX-Messenger-Doku-Index: Docs2017/api/mgmt_api (nicht stopcontainer.bat).
|
|
||||||
/// LocalCmd nur wenn Server-Scripts existieren (bei reiner SMC oft nicht der Fall).
|
|
||||||
/// </summary>
|
|
||||||
public SonicManagementMode ManagementMode { get; init; } = SonicManagementMode.MfApi;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Installationsroot (optional; Libs oft unter lib).</summary>
|
||||||
/// Effektiver Modus: bei WinRm und lokalem ConnectionUrl-Host wird LocalCmd erzwungen.
|
|
||||||
/// MfApi bleibt unverändert (nutzt Domain-Manager-Verbindung).
|
|
||||||
/// </summary>
|
|
||||||
public SonicManagementMode EffectiveManagementMode
|
|
||||||
=> ManagementMode == SonicManagementMode.WinRm && IsConnectionHostLocal(ConnectionUrl)
|
|
||||||
? SonicManagementMode.LocalCmd
|
|
||||||
: ManagementMode;
|
|
||||||
|
|
||||||
/// <summary>True wenn WinRm konfiguriert war, aber wegen lokalem Host auf LocalCmd umgestellt wurde.</summary>
|
|
||||||
public bool IsLocalCmdAutoForced
|
|
||||||
=> ManagementMode == SonicManagementMode.WinRm
|
|
||||||
&& EffectiveManagementMode == SonicManagementMode.LocalCmd;
|
|
||||||
|
|
||||||
/// <summary>Anzeigetext für UI (inkl. Auto-Erkennung).</summary>
|
|
||||||
public string ManagementModeDisplay
|
|
||||||
=> IsLocalCmdAutoForced
|
|
||||||
? "LocalCmd (Host lokal erkannt)"
|
|
||||||
: EffectiveManagementMode switch
|
|
||||||
{
|
|
||||||
SonicManagementMode.MfApi => "MfApi (SMC Management Application API)",
|
|
||||||
SonicManagementMode.LocalCmd => "LocalCmd (nur wenn stopcontainer.bat existiert)",
|
|
||||||
_ => EffectiveManagementMode.ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// True wenn der Host aus ConnectionUrl dieser Maschine entspricht
|
|
||||||
/// (localhost / 127.0.0.1 / ::1 / Computername).
|
|
||||||
/// </summary>
|
|
||||||
public static bool IsConnectionHostLocal(string? connectionUrl)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(connectionUrl))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string host;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
host = new Uri(connectionUrl).Host;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
host = connectionUrl.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(host))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host.Equals("localhost", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| host.Equals("127.0.0.1", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| host.Equals("::1", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| host.Equals("[::1]", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
string machine = Environment.MachineName;
|
|
||||||
if (host.Equals(machine, StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| host.StartsWith(machine + ".", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string dnsName = System.Net.Dns.GetHostName();
|
|
||||||
if (!string.IsNullOrWhiteSpace(dnsName)
|
|
||||||
&& (host.Equals(dnsName, StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| host.StartsWith(dnsName + ".", StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// DNS optional
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sonic-/MQ-/ESB-Installationsroot, z.B. "C:\Sonic\MQ10.0" (Client-JARs oft unter lib).
|
|
||||||
/// Bei ESB-Home: ggf. MfClientLibPath auf MQ\lib setzen.
|
|
||||||
/// </summary>
|
|
||||||
public string SonicHome { get; init; } = @"C:\Sonic\MQ10.0";
|
public string SonicHome { get; init; } = @"C:\Sonic\MQ10.0";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Optional: JRE/JDK-Home.</summary>
|
||||||
/// Optional: JRE/JDK-Home (z.B. C:\Sonic\MQ10.0\jre) oder Pfad zu java.exe.
|
|
||||||
/// Leer = automatische Suche (JAVA_HOME/JRE_HOME, setenv.bat, rekursiv unter SonicHome, Registry, …).
|
|
||||||
/// </summary>
|
|
||||||
public string JavaHome { get; init; } = string.Empty;
|
public string JavaHome { get; init; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Optional: direkter Pfad zu java.exe.</summary>
|
||||||
/// Optionaler direkter Pfad zu java.exe (überschreibt JavaHome wenn gesetzt).
|
|
||||||
/// </summary>
|
|
||||||
public string JavaPath { get; init; } = string.Empty;
|
public string JavaPath { get; init; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Ordner mit mgmt_client.jar / mfcontext.jar / …</summary>
|
||||||
/// Optionaler Pfad zu Sonic-Client-JARs für MfApi (mgmt_client.jar etc.).
|
|
||||||
/// Leer = SonicHome\lib, MQ_HOME\lib, ESB_HOME\lib bzw. nested MQ*/lib.
|
|
||||||
/// </summary>
|
|
||||||
public string MfClientLibPath { get; init; } = string.Empty;
|
public string MfClientLibPath { get; init; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Container-Kurznamen, z.B. ct-ZADBService.</summary>
|
||||||
/// Container-Namen (kurz), z.B. ["DE-Test"]. Nicht die Domain.
|
|
||||||
/// </summary>
|
|
||||||
public List<string> KnownContainers { get; init; } = [];
|
public List<string> KnownContainers { get; init; } = [];
|
||||||
|
|
||||||
public int ManagementHttpPort { get; init; } = 8080;
|
|
||||||
public string ApiBasePath { get; init; } = "/api/v1";
|
|
||||||
public string ContainerListPath { get; init; } = string.Empty;
|
|
||||||
public string ContainerRestartPath { get; init; } = string.Empty;
|
|
||||||
public string ContainerStopPath { get; init; } = string.Empty;
|
|
||||||
public string ContainerStartPath { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
public int WinRmPort { get; init; } = 5985;
|
|
||||||
public string WinRmRestartScript { get; init; } = string.Empty;
|
|
||||||
public string WinRmContainerListScript { get; init; } = string.Empty;
|
|
||||||
public string WinRmXapiImportScript { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
public int TimeoutSeconds { get; init; } = 120;
|
public int TimeoutSeconds { get; init; } = 120;
|
||||||
public int PostRestartDelaySeconds { get; init; } = 20;
|
public int PostRestartDelaySeconds { get; init; } = 20;
|
||||||
|
|
||||||
public string ResolveRestartScript()
|
public string ManagementModeDisplay => "MfApi (SMC Restart)";
|
||||||
=> string.IsNullOrWhiteSpace(WinRmRestartScript)
|
|
||||||
? DefaultRestartScript
|
|
||||||
: WinRmRestartScript;
|
|
||||||
|
|
||||||
public string ResolveContainerListScript()
|
|
||||||
=> string.IsNullOrWhiteSpace(WinRmContainerListScript)
|
|
||||||
? DefaultContainerListScript
|
|
||||||
: WinRmContainerListScript;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Offizieller MF-Container-Neustart laut Aurea CX Messenger Doku:
|
|
||||||
/// SonicHome\bin\stopcontainer.bat Domain.Container
|
|
||||||
/// SonicHome\bin\startcontainer.bat Domain.Container
|
|
||||||
/// Danach Prozess-Verifikation (alte PIDs weg, neue PIDs da).
|
|
||||||
/// </summary>
|
|
||||||
public const string DefaultRestartScript =
|
|
||||||
"""
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
$sonicHome = '{sonicHome}'
|
|
||||||
$domain = '{domain}'
|
|
||||||
$container = '{container}'
|
|
||||||
|
|
||||||
$bin = Join-Path $sonicHome 'bin'
|
|
||||||
$stopBat = Join-Path $bin 'stopcontainer.bat'
|
|
||||||
$startBat = Join-Path $bin 'startcontainer.bat'
|
|
||||||
|
|
||||||
# Kurzname = Container (z.B. DE-Test), Full = Domain.Container (z.B. proalpha-test.DE-Test)
|
|
||||||
$shortName = if ($container -like '*.*') { ($container -split '\.', 2)[1] } else { $container }
|
|
||||||
$fullName = if ($container -like '*.*') { $container } else { "$domain.$container" }
|
|
||||||
$markers = @($shortName, $fullName, "$domain.$shortName") | Select-Object -Unique
|
|
||||||
|
|
||||||
function Get-ContainerPids {
|
|
||||||
$pids = @()
|
|
||||||
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
|
||||||
Where-Object {
|
|
||||||
$cmd = $_.CommandLine
|
|
||||||
if (-not $cmd) { return $false }
|
|
||||||
if ($_.Name -notmatch 'java|javaw|sonic') { return $false }
|
|
||||||
foreach ($m in $markers) {
|
|
||||||
if ($cmd -like "*$m*") { return $true }
|
|
||||||
}
|
|
||||||
return $false
|
|
||||||
} |
|
|
||||||
ForEach-Object { $pids += [int]$_.ProcessId }
|
|
||||||
return @($pids | Select-Object -Unique)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Wait-PidsGone([int[]]$pids, [int]$seconds) {
|
|
||||||
$deadline = (Get-Date).AddSeconds($seconds)
|
|
||||||
while ((Get-Date) -lt $deadline) {
|
|
||||||
$alive = @($pids | Where-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue })
|
|
||||||
if ($alive.Count -eq 0) { return $true }
|
|
||||||
Start-Sleep -Seconds 2
|
|
||||||
}
|
|
||||||
return $false
|
|
||||||
}
|
|
||||||
|
|
||||||
function Wait-NewPids([int[]]$oldPids, [int]$seconds) {
|
|
||||||
$deadline = (Get-Date).AddSeconds($seconds)
|
|
||||||
while ((Get-Date) -lt $deadline) {
|
|
||||||
$now = @(Get-ContainerPids)
|
|
||||||
$fresh = @($now | Where-Object { $oldPids -notcontains $_ })
|
|
||||||
if ($fresh.Count -gt 0) { return ,$fresh }
|
|
||||||
Start-Sleep -Seconds 2
|
|
||||||
}
|
|
||||||
return ,@()
|
|
||||||
}
|
|
||||||
|
|
||||||
function Invoke-ContainerBat([string]$bat, [string]$name) {
|
|
||||||
if (-not (Test-Path -LiteralPath $bat)) {
|
|
||||||
throw "Sonic BAT fehlt: $bat (SonicHome pruefen)"
|
|
||||||
}
|
|
||||||
$arg = '/c "' + $bat + '" "' + $name + '"'
|
|
||||||
$p = Start-Process -FilePath 'cmd.exe' -ArgumentList $arg -Wait -PassThru -NoNewWindow
|
|
||||||
return [int]$p.ExitCode
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output "INFO:Domain=$domain Container=$shortName Full=$fullName"
|
|
||||||
Write-Output "INFO:RestartVia=stopcontainer/startcontainer (MF Container)"
|
|
||||||
$before = @(Get-ContainerPids)
|
|
||||||
Write-Output "INFO:PIDsBefore=$($before -join ',')"
|
|
||||||
|
|
||||||
if ($before.Count -eq 0) {
|
|
||||||
Write-Output "WARN:No running Java/Sonic process for '$shortName' - starting anyway"
|
|
||||||
}
|
|
||||||
|
|
||||||
# 1) Offiziell: stopcontainer.bat Domain.Container (danach Kurzname als Fallback)
|
|
||||||
$stopOk = $false
|
|
||||||
foreach ($n in @($fullName, $shortName)) {
|
|
||||||
Write-Output "INFO:stopcontainer $n"
|
|
||||||
$code = Invoke-ContainerBat -bat $stopBat -name $n
|
|
||||||
Write-Output "INFO:stopcontainer ExitCode=$code Name=$n"
|
|
||||||
if ($code -eq 0) { $stopOk = $true; break }
|
|
||||||
}
|
|
||||||
if (-not $stopOk) {
|
|
||||||
Write-Output "WARN:stopcontainer non-zero; will force-stop remaining PIDs if any"
|
|
||||||
}
|
|
||||||
|
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
$still = @(Get-ContainerPids)
|
|
||||||
|
|
||||||
# 2) Force-Stop falls BAT den Prozess nicht beendet (sonst kein echter Restart in SMC)
|
|
||||||
if ($still.Count -gt 0) {
|
|
||||||
Write-Output "INFO:Force-Stop PIDs=$($still -join ',')"
|
|
||||||
foreach ($procId in $still) {
|
|
||||||
Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($before.Count -gt 0) {
|
|
||||||
if (-not (Wait-PidsGone -pids $before -seconds 45)) {
|
|
||||||
throw "Container process still running after stop (PIDs=$($before -join ','))"
|
|
||||||
}
|
|
||||||
Write-Output "INFO:Old PIDs gone"
|
|
||||||
}
|
|
||||||
|
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
|
|
||||||
# 3) Offiziell: startcontainer.bat Domain.Container
|
|
||||||
$started = $false
|
|
||||||
foreach ($n in @($fullName, $shortName)) {
|
|
||||||
Write-Output "INFO:startcontainer $n"
|
|
||||||
$code = Invoke-ContainerBat -bat $startBat -name $n
|
|
||||||
Write-Output "INFO:startcontainer ExitCode=$code Name=$n"
|
|
||||||
if ($code -eq 0) { $started = $true; break }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $started) {
|
|
||||||
throw "startcontainer failed for $fullName / $shortName"
|
|
||||||
}
|
|
||||||
|
|
||||||
$after = @(Wait-NewPids -oldPids $before -seconds 60)
|
|
||||||
if ($after.Count -eq 0) {
|
|
||||||
$any = @(Get-ContainerPids)
|
|
||||||
if ($any.Count -eq 0) {
|
|
||||||
throw "After start no process for container '$shortName'. Check SMC / SonicHome / ContainerName."
|
|
||||||
}
|
|
||||||
Write-Output "INFO:PIDsAfter=$($any -join ',')"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Output "INFO:PIDsAfter=$($after -join ',')"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output "OK:ContainerRestartVerified Domain=$domain Container=$shortName"
|
|
||||||
""";
|
|
||||||
|
|
||||||
public const string DefaultContainerListScript =
|
|
||||||
"""
|
|
||||||
$ErrorActionPreference = 'Continue'
|
|
||||||
$sonicHome = '{sonicHome}'
|
|
||||||
$domain = '{domain}'
|
|
||||||
$names = New-Object System.Collections.Generic.List[string]
|
|
||||||
|
|
||||||
function Add-Name([string]$n) {
|
|
||||||
if ([string]::IsNullOrWhiteSpace($n)) { return }
|
|
||||||
$n = $n.Trim()
|
|
||||||
if (-not $names.Contains($n)) { [void]$names.Add($n) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not (Test-Path -LiteralPath $sonicHome)) {
|
|
||||||
Write-Output "WARN:SonicHomeNichtGefunden:$sonicHome"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Output "INFO:SonicHomeOk:$sonicHome Domain=$domain"
|
|
||||||
|
|
||||||
Get-ChildItem -LiteralPath $sonicHome -Directory -ErrorAction SilentlyContinue |
|
|
||||||
ForEach-Object {
|
|
||||||
if ($_.Name -like '*.cache') {
|
|
||||||
Add-Name ($_.Name -replace '\.cache$', '')
|
|
||||||
}
|
|
||||||
Get-ChildItem -LiteralPath $_.FullName -Directory -ErrorAction SilentlyContinue |
|
|
||||||
Where-Object { $_.Name -like '*.cache' } |
|
|
||||||
ForEach-Object { Add-Name ($_.Name -replace '\.cache$', '') }
|
|
||||||
}
|
|
||||||
|
|
||||||
Get-ChildItem -LiteralPath $sonicHome -Recurse -Filter 'container.xml' -File -ErrorAction SilentlyContinue |
|
|
||||||
Select-Object -First 40 |
|
|
||||||
ForEach-Object {
|
|
||||||
Add-Name (Split-Path $_.DirectoryName -Leaf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
|
||||||
Where-Object { $_.CommandLine -match 'sonic|mf\.framework|container\.xml' } |
|
|
||||||
ForEach-Object {
|
|
||||||
if ($_.CommandLine -match '([A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+)\.cache') {
|
|
||||||
Add-Name $Matches[1]
|
|
||||||
}
|
|
||||||
elseif ($_.CommandLine -match '\\([A-Za-z0-9_\-]+)\\container\.xml') {
|
|
||||||
Add-Name $Matches[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Kurzform Domain.Container → Container
|
|
||||||
foreach ($n in @($names.ToArray())) {
|
|
||||||
if ($n -like '*.*') {
|
|
||||||
$parts = $n -split '\.', 2
|
|
||||||
if ($parts.Count -eq 2) { Add-Name $parts[1] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($names.Count -eq 0) {
|
|
||||||
Write-Output "WARN:KeineContainerGefunden Domain=$domain SonicHome=$sonicHome"
|
|
||||||
}
|
|
||||||
|
|
||||||
$names | Sort-Object -Unique
|
|
||||||
""";
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum SonicManagementMode
|
|
||||||
{
|
|
||||||
HttpApi = 0,
|
|
||||||
WinRm = 1,
|
|
||||||
LocalCmd = 2,
|
|
||||||
/// <summary>Sonic MF Management API über Domain-Manager (ConnectionUrl + SMC-Credentials).</summary>
|
|
||||||
MfApi = 3
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
namespace ZA.CoreService.ESBCertificateManager.Models;
|
||||||
|
|
||||||
|
public sealed class SonicCredentialProfile
|
||||||
|
{
|
||||||
|
public int SonicCredentialId { get; init; }
|
||||||
|
|
||||||
|
public int SonicConnectionId { get; init; }
|
||||||
|
|
||||||
|
public required string CredentialName { get; init; }
|
||||||
|
|
||||||
|
public required string UserName { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Durch Microsoft.Data.SqlClient clientseitig entschlüsselt.
|
||||||
|
/// Darf niemals angezeigt oder protokolliert werden.
|
||||||
|
/// </summary>
|
||||||
|
public required string Secret { get; init; }
|
||||||
|
|
||||||
|
public bool IsDefault { get; init; }
|
||||||
|
|
||||||
|
public bool IsActive { get; init; }
|
||||||
|
|
||||||
|
public bool IsComplete =>
|
||||||
|
!string.IsNullOrWhiteSpace(UserName)
|
||||||
|
&& !string.IsNullOrEmpty(Secret);
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return CredentialName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
namespace ZA.CoreService.ESBCertificateManager.Models;
|
||||||
|
|
||||||
|
public sealed class SonicSystemOption
|
||||||
|
{
|
||||||
|
public int SonicConnectionId { get; init; }
|
||||||
|
|
||||||
|
public required string ConnectionName { get; init; }
|
||||||
|
|
||||||
|
public required string EnvironmentCode { get; init; }
|
||||||
|
|
||||||
|
public required string DomainName { get; init; }
|
||||||
|
|
||||||
|
public required string ManagementHost { get; init; }
|
||||||
|
|
||||||
|
public int ManagementPort { get; init; }
|
||||||
|
|
||||||
|
public required string ConnectionProtocol { get; init; }
|
||||||
|
|
||||||
|
public required string ValidationContainerName { get; init; }
|
||||||
|
|
||||||
|
public string ConnectionUrl
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string protocol = ConnectionProtocol
|
||||||
|
.Trim()
|
||||||
|
.TrimEnd(':', '/');
|
||||||
|
|
||||||
|
return
|
||||||
|
$"{protocol}://{ManagementHost.Trim()}:{ManagementPort}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return ConnectionName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,95 @@
|
|||||||
namespace ZA.CoreService.ESBCertificateManager
|
using System;
|
||||||
{
|
using System.Windows.Forms;
|
||||||
internal static class Program
|
using ZA.CoreService.ESBCertificateManager.Configuration;
|
||||||
{
|
using ZA.CoreService.ESBCertificateManager.Models;
|
||||||
|
using ZA.CoreService.ESBCertificateManager.Services;
|
||||||
|
using ZA.CoreService.ESBCertificateManager.Setup;
|
||||||
|
|
||||||
[STAThread]
|
namespace ZA.CoreService.ESBCertificateManager;
|
||||||
static void Main()
|
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
private static void Main()
|
||||||
|
{
|
||||||
|
ApplicationConfiguration.Initialize();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
ApplicationConfiguration.Initialize();
|
AppSettings settings =
|
||||||
Application.Run(new Form1());
|
AppSettingsLoader.Load();
|
||||||
|
|
||||||
|
SonicSetupRepository repository =
|
||||||
|
new(
|
||||||
|
settings.Database.ConnectionString);
|
||||||
|
|
||||||
|
SetupCoordinator coordinator =
|
||||||
|
new(
|
||||||
|
settings,
|
||||||
|
repository);
|
||||||
|
|
||||||
|
SetupCheckResult setupResult =
|
||||||
|
coordinator
|
||||||
|
.CheckAsync()
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
|
||||||
|
if (!setupResult.IsReady)
|
||||||
|
{
|
||||||
|
using SetupWizardForm setupForm =
|
||||||
|
new(
|
||||||
|
coordinator,
|
||||||
|
isSettingsMode: false);
|
||||||
|
|
||||||
|
DialogResult setupDialogResult =
|
||||||
|
setupForm.ShowDialog();
|
||||||
|
|
||||||
|
if (setupDialogResult != DialogResult.OK)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupCheckResult finalCheck =
|
||||||
|
coordinator
|
||||||
|
.CheckAsync()
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
|
||||||
|
if (!finalCheck.IsReady)
|
||||||
|
{
|
||||||
|
MessageBox.Show(
|
||||||
|
"Einrichtung unvollständig.",
|
||||||
|
"Einstellungen",
|
||||||
|
MessageBoxButtons.OK,
|
||||||
|
MessageBoxIcon.Warning);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DatabaseSchemaPatcher
|
||||||
|
.EnsureCertificateCheckUrlColumnAsync(
|
||||||
|
settings.Database.ConnectionString)
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ohne ALTER-Rechte weiter starten; Repositories nutzen Fallback-SQL.
|
||||||
|
}
|
||||||
|
|
||||||
|
Application.Run(
|
||||||
|
new Form1());
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
MessageBox.Show(
|
||||||
|
"Start fehlgeschlagen.",
|
||||||
|
"Fehler",
|
||||||
|
MessageBoxButtons.OK,
|
||||||
|
MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
Copyright © 1993, 2026, Oracle and/or its affiliates.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This software and related documentation are provided under a
|
||||||
|
license agreement containing restrictions on use and
|
||||||
|
disclosure and are protected by intellectual property laws.
|
||||||
|
Except as expressly permitted in your license agreement or
|
||||||
|
allowed by law, you may not use, copy, reproduce, translate,
|
||||||
|
broadcast, modify, license, transmit, distribute, exhibit,
|
||||||
|
perform, publish, or display any part, in any form, or by
|
||||||
|
any means. Reverse engineering, disassembly, or
|
||||||
|
decompilation of this software, unless required by law for
|
||||||
|
interoperability, is prohibited.
|
||||||
|
|
||||||
|
The information contained herein is subject to change
|
||||||
|
without notice and is not warranted to be error-free. If you
|
||||||
|
find any errors, please report them to us in writing.
|
||||||
|
|
||||||
|
If this is software or related documentation that is
|
||||||
|
delivered to the U.S. Government or anyone licensing it on
|
||||||
|
behalf of the U.S. Government, the following notice is
|
||||||
|
applicable:
|
||||||
|
|
||||||
|
U.S. GOVERNMENT END USERS: Oracle programs, including any
|
||||||
|
operating system, integrated software, any programs
|
||||||
|
installed on the hardware, and/or documentation, delivered
|
||||||
|
to U.S. Government end users are "commercial computer
|
||||||
|
software" pursuant to the applicable Federal Acquisition
|
||||||
|
Regulation and agency-specific supplemental regulations. As
|
||||||
|
such, use, duplication, disclosure, modification, and
|
||||||
|
adaptation of the programs, including any operating system,
|
||||||
|
integrated software, any programs installed on the hardware,
|
||||||
|
and/or documentation, shall be subject to license terms and
|
||||||
|
license restrictions applicable to the programs. No other
|
||||||
|
rights are granted to the U.S. Government.
|
||||||
|
|
||||||
|
This software or hardware is developed for general use in a
|
||||||
|
variety of information management applications. It is not
|
||||||
|
developed or intended for use in any inherently dangerous
|
||||||
|
applications, including applications that may create a risk
|
||||||
|
of personal injury. If you use this software or hardware in
|
||||||
|
dangerous applications, then you shall be responsible to
|
||||||
|
take all appropriate fail-safe, backup, redundancy, and
|
||||||
|
other measures to ensure its safe use. Oracle Corporation
|
||||||
|
and its affiliates disclaim any liability for any damages
|
||||||
|
caused by use of this software or hardware in dangerous
|
||||||
|
applications.
|
||||||
|
|
||||||
|
Oracle and Java are registered trademarks of Oracle and/or
|
||||||
|
its affiliates. Other names may be trademarks of their
|
||||||
|
respective owners.
|
||||||
|
|
||||||
|
Intel and Intel Xeon are trademarks or registered trademarks
|
||||||
|
of Intel Corporation. All SPARC trademarks are used under
|
||||||
|
license and are trademarks or registered trademarks of SPARC
|
||||||
|
International, Inc. AMD, Opteron, the AMD logo, and the AMD
|
||||||
|
Opteron logo are trademarks or registered trademarks of
|
||||||
|
Advanced Micro Devices. UNIX is a registered trademark of
|
||||||
|
The Open Group.
|
||||||
|
|
||||||
|
This software or hardware and documentation may provide
|
||||||
|
access to or information on content, products, and services
|
||||||
|
from third parties. Oracle Corporation and its affiliates
|
||||||
|
are not responsible for and expressly disclaim all
|
||||||
|
warranties of any kind with respect to third-party content,
|
||||||
|
products, and services. Oracle Corporation and its
|
||||||
|
affiliates will not be responsible for any loss, costs, or
|
||||||
|
damages incurred due to your access to or use of third-party
|
||||||
|
content, products, or services.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Please refer to https://java.com/otnlicense
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user