first commit
This commit is contained in:
129
Services/AuroraService.cs
Normal file
129
Services/AuroraService.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using HanaToolbox.Config;
|
||||
using HanaToolbox.Logging;
|
||||
using HanaToolbox.Services.Interfaces;
|
||||
|
||||
namespace HanaToolbox.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Performs the Aurora schema refresh:
|
||||
/// Drop old _AURORA schema → Export source → Import-rename → Update company name → Grant privileges.
|
||||
/// </summary>
|
||||
public sealed class AuroraService(
|
||||
IUserSwitcher switcher,
|
||||
IHdbClientLocator locator,
|
||||
INotificationService ntfy,
|
||||
AppLogger logger) : IAuroraService
|
||||
{
|
||||
public async Task RunAsync(
|
||||
AuroraConfig config, HanaConfig hana, string sid,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(config.SourceSchema))
|
||||
{
|
||||
logger.Error("Aurora: SourceSchema is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
var hdbsql = locator.LocateHdbsql(hana.HdbsqlPath, sid, hana.InstanceNumber);
|
||||
var threads = config.Threads > 0 ? config.Threads : Math.Max(1, Environment.ProcessorCount / 2);
|
||||
var aurora = $"{config.SourceSchema}_AURORA";
|
||||
var tmpDir = Path.Combine(config.BackupBasePath, $"{aurora}_TEMP_{DateTime.Now:yyyyMMdd_HHmmss}");
|
||||
|
||||
logger.Step($"Starting Aurora refresh: '{config.SourceSchema}' → '{aurora}'");
|
||||
|
||||
// 1. Drop old Aurora schema (ignore errors if it doesn't exist)
|
||||
logger.Step($"Dropping old schema '{aurora}' (if exists)...");
|
||||
await RunSql(hdbsql, config.AdminUserKey,
|
||||
$"DROP SCHEMA \"{aurora}\" CASCADE;", sid, ct);
|
||||
|
||||
// 2. Prepare temp export directory
|
||||
await RunAs(sid, $"mkdir -p \"{tmpDir}\"", ct);
|
||||
|
||||
// 3. Export source schema
|
||||
logger.Step($"Exporting '{config.SourceSchema}' to temp dir...");
|
||||
var exportResult = await RunSql(hdbsql, config.AdminUserKey,
|
||||
$"EXPORT \"{config.SourceSchema}\".\"*\" AS BINARY INTO '{tmpDir}' WITH REPLACE THREADS {threads} NO DEPENDENCIES;",
|
||||
sid, ct);
|
||||
|
||||
if (!exportResult.Success)
|
||||
{
|
||||
logger.Error($"Aurora export failed: {exportResult.StdErr}");
|
||||
await ntfy.SendAsync("Aurora Failed", $"Export of '{config.SourceSchema}' FAILED.", ct);
|
||||
await Cleanup(tmpDir, sid, ct);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Import → rename
|
||||
logger.Step($"Importing as '{aurora}'...");
|
||||
var importResult = await RunSql(hdbsql, config.AdminUserKey,
|
||||
$"IMPORT \"{config.SourceSchema}\".\"*\" AS BINARY FROM '{tmpDir}' WITH REPLACE RENAME SCHEMA \"{config.SourceSchema}\" TO \"{aurora}\" THREADS {threads};",
|
||||
sid, ct);
|
||||
|
||||
if (!importResult.Success)
|
||||
{
|
||||
logger.Error($"Aurora import failed: {importResult.StdErr}");
|
||||
await ntfy.SendAsync("Aurora Failed", $"Import-rename to '{aurora}' FAILED.", ct);
|
||||
await Cleanup(tmpDir, sid, ct);
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. Get original company name from CINF
|
||||
logger.Step("Fetching company name from source schema...");
|
||||
var nameResult = await RunSqlScalar(hdbsql, config.AdminUserKey,
|
||||
$"SELECT \"CompnyName\" FROM \"{config.SourceSchema}\".\"CINF\";", sid, ct);
|
||||
var companyName = string.IsNullOrWhiteSpace(nameResult) ? config.SourceSchema : nameResult;
|
||||
var newName = $"AURORA - {companyName} - {DateTime.Now:yyyy-MM-dd}";
|
||||
|
||||
// 6. Update company name in CINF and OADM
|
||||
logger.Step($"Setting company name to '{newName}'...");
|
||||
await RunSql(hdbsql, config.AdminUserKey,
|
||||
$"UPDATE \"{aurora}\".\"CINF\" SET \"CompnyName\" = '{newName}';", sid, ct);
|
||||
await RunSql(hdbsql, config.AdminUserKey,
|
||||
$"UPDATE \"{aurora}\".\"OADM\" SET \"CompnyName\" = '{newName}', \"PrintHeadr\" = '{newName}';", sid, ct);
|
||||
|
||||
// 7. Grant privileges
|
||||
if (!string.IsNullOrWhiteSpace(config.AuroraUser))
|
||||
{
|
||||
logger.Step($"Granting ALL PRIVILEGES on '{aurora}' to '{config.AuroraUser}'...");
|
||||
await RunSql(hdbsql, config.AdminUserKey,
|
||||
$"GRANT ALL PRIVILEGES ON SCHEMA \"{aurora}\" TO \"{config.AuroraUser}\";", sid, ct);
|
||||
}
|
||||
|
||||
// 8. Cleanup temp directory
|
||||
await Cleanup(tmpDir, sid, ct);
|
||||
|
||||
logger.Success($"Aurora refresh of '{aurora}' completed!");
|
||||
await ntfy.SendAsync("Aurora Complete", $"Aurora refresh of '{aurora}' completed successfully.", ct);
|
||||
}
|
||||
|
||||
private async Task<ProcessResult> RunSql(
|
||||
string hdbsql, string userKey, string sql, string sid, CancellationToken ct)
|
||||
{
|
||||
var tmpFile = Path.Combine("/tmp", $"ht_{Guid.NewGuid():N}.sql");
|
||||
await File.WriteAllTextAsync(tmpFile, sql, ct);
|
||||
var result = await switcher.RunAsAsync(sid,
|
||||
$"\"{hdbsql}\" -U {userKey} -I \"{tmpFile}\" 2>&1", ct);
|
||||
File.Delete(tmpFile);
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<string> RunSqlScalar(
|
||||
string hdbsql, string userKey, string sql, string sid, CancellationToken ct)
|
||||
{
|
||||
var tmpFile = Path.Combine("/tmp", $"ht_{Guid.NewGuid():N}.sql");
|
||||
await File.WriteAllTextAsync(tmpFile, sql, ct);
|
||||
var result = await switcher.RunAsAsync(sid,
|
||||
$"\"{hdbsql}\" -U {userKey} -a -x -I \"{tmpFile}\" 2>&1", ct);
|
||||
File.Delete(tmpFile);
|
||||
return result.StdOut.Replace("\"", "").Trim();
|
||||
}
|
||||
|
||||
private Task<ProcessResult> RunAs(string sid, string cmd, CancellationToken ct) =>
|
||||
switcher.RunAsAsync(sid, cmd, ct);
|
||||
|
||||
private async Task Cleanup(string dir, string sid, CancellationToken ct)
|
||||
{
|
||||
logger.Step($"Cleaning up temp dir '{dir}'...");
|
||||
await RunAs(sid, $"rm -rf \"{dir}\"", ct);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user