# HANA TUI - Implementation Plan ## Overview A single-binary AOT-compiled C# TUI for managing SAP HANA schemas. Built on **.NET 10** with **Spectre.Console** for rich UI, running against the existing `hdbsql`/`hdbuserstore` tools already installed on the system. --- ## Project Setup Update `hanatui.csproj`: - `true` - `true` - `true` - `true` - NuGet: `Spectre.Console` (core only, no `.Cli`) Build command: ```bash dotnet publish -r linux-x64 -c Release -o bin/publish ``` Output: `bin/publish/hanatui` (~15-20 MB single native binary, no runtime dependency) --- ## File Structure ``` hanatui/ ├── hanatui.csproj ├── PLAN.md ├── Program.cs └── src/ ├── Hana/ │ ├── HdbClientLocator.cs # Finds hdbclient path (/usr/sap/hdbclient etc.) │ ├── HdbCliRunner.cs # Spawns hdbsql/hdbuserstore, streams output │ ├── HdbUserstoreKey.cs # Model: key name, host, port, tenant, user │ ├── SchemaService.cs # Lists schemas, builds/runs SQL queries │ └── SqlQueryBuilder.cs # Builds EXPORT/IMPORT/DROP/BACKUP SQL strings ├── System/ │ ├── SystemStats.cs # Reads /proc/stat + /proc/meminfo, computes deltas │ └── CpuSample.cs # Struct for CPU snapshot (used for delta calc) └── Tui/ ├── KeySelectionScreen.cs # Startup: arrow-key key picker ├── MainMenuScreen.cs # Main menu with current key shown in header ├── OperationForms.cs # Per-op guided input forms (Export/Import/etc.) ├── TaskRunnerScreen.cs # Live split-panel: stats left, log right └── Components/ ├── StatsPanel.cs # CPU bars per core + total, RAM bar, Swap bar └── LogPanel.cs # Scrolling timestamped log lines ``` --- ## Screen Flow ``` [Launch] └─> KeySelectionScreen hdbuserstore list → parse keys → arrow-key picker (or manual entry) └─> MainMenuScreen (shows active key + host in header) └─> OperationForms (per operation) 1. Fetch schemas (spinner while loading) 2. Schema picker (arrow-key selectable list) 3. Additional inputs (path, threads, flags) 4. Confirmation summary before running └─> TaskRunnerScreen ┌──────────────────┬───────────────────┐ │ SYSTEM STATS │ OPERATION LOG │ │ CPU bars │ streaming lines │ │ Total [████░░] │ with timestamps │ │ Core0 [███░░░] │ │ │ Core1 [█████░] │ │ │ ... │ │ │ RAM bar │ │ │ [██████░░] 58% │ │ │ 12.4 / 21.3 GB │ │ │ Swap bar │ │ │ [██░░░░░░] 18% │ │ │ Elapsed: 4m 32s │ │ └──────────────────┴───────────────────┘ [q] → "Press Q again to abort" [q again] → SIGTERM → wait 5s → SIGKILL On complete/abort: - Stats stop refreshing, log stays visible - "Returning in 10s... [any key to stay]" - If key pressed → cancel countdown, stay on result screen - Enter/Esc → back to main menu ``` --- ## Operations | # | Operation | Key inputs | Notes | |---|--------------------|-------------------------------------------------|------------------------------| | 1 | Export Schema | Schema (picker), target path, threads, compress | Shells pigz if available | | 2 | Import Schema | Source schema name, source path, threads, replace | Decompresses .tar.gz if needed | | 3 | Import & Rename | Source name, new name, source path, threads, replace | Adds RENAME clause | | 4 | Copy Schema | Schema (picker), target name, temp path, threads, replace | Export then Import-Rename | | 5 | Drop Schema | Schema (picker), type YES to confirm | Destructive — extra confirm | | 6 | Rename DB | Schema (picker), new company name | Updates CINF + OADM tables | | 7 | Backup Tenant | Target path, threads, compress | BACKUP DATA USING FILE | --- ## System Stats Details | Metric | Source | Method | |--------------|------------------|-----------------------------------------------| | CPU % total | `/proc/stat` | Delta between two reads 500ms apart | | CPU % core N | `/proc/stat` | Per-core lines (`cpu0`, `cpu1`, ...) | | RAM total | `/proc/meminfo` | `MemTotal` | | RAM used | `/proc/meminfo` | `MemTotal - MemAvailable` | | Swap total | `/proc/meminfo` | `SwapTotal` | | Swap used | `/proc/meminfo` | `SwapTotal - SwapFree` | - Refresh interval: 800ms via `System.Threading.Timer` - Bar width adapts to terminal width - Bar format: `[████████░░] 82%` --- ## Abort Behavior 1. First `q` press during running task: - Shows warning in log panel: `[WARN] Press Q again to abort the operation` 2. Second `q` press within ~3 seconds: - Sends SIGTERM to `hdbsql` child process - Waits up to 5 seconds for graceful exit - If still running after 5s: sends SIGKILL - Logs each step to the log panel --- ## Post-Operation Return Behavior - After success or abort: stats panel freezes, log remains - Footer shows: `Returning to menu in 10s... [any key to stay]` - If any key pressed: countdown cancels, footer changes to `[Enter/Esc] Return to menu` - User reads the result at their own pace --- ## AOT Compatibility | Concern | Mitigation | |-------------------------------|------------------------------------------------------| | No runtime reflection | All code uses concrete types, no Activator | | Spectre.Console prompts | SelectionPrompt, TextPrompt — AOT-safe | | Spectre.Console Live/Layout | AnsiConsole.Live() — AOT-compatible | | Process spawning | System.Diagnostics.Process — fully AOT-safe | | /proc file reading | File.ReadAllText — no issues | | Compression | Process (pigz/tar) — no native .NET compression | | No JsonSerializer | Not needed | | No dynamic/Activator | Enforced throughout | --- ## Key Discovery (hdbuserstore) Command: `hdbuserstore list` Sample output: ``` DATA FILE : /home/user/.hdb/hostname/SSFS_HDB.DAT KEY FILE : /home/user/.hdb/hostname/SSFS_HDB.KEY KEY CRONKEY ENV : hostname:30015@NDB USER: SYSTEM KEY DEVKEY ENV : devhost:30015@DEV USER: SYSTEM ``` Parse: lines starting with `KEY ` → key name. Following `ENV :` and `USER:` lines → details. --- ## SQL Queries Used ```sql -- List schemas SELECT SCHEMA_NAME FROM SCHEMAS WHERE SCHEMA_OWNER = 'SYSTEM' AND SCHEMA_NAME NOT IN ('_SYS_SECURITY', 'IFSERV', 'B1if', 'SYSTEM', 'RSP'); -- Export EXPORT "SCHEMA"."*" AS BINARY INTO '/path' WITH REPLACE THREADS N NO DEPENDENCIES; -- Import IMPORT "SCHEMA"."*" AS BINARY FROM '/path' WITH IGNORE EXISTING THREADS N; -- Import-Rename IMPORT "SCHEMA"."*" AS BINARY FROM '/path' WITH IGNORE EXISTING RENAME SCHEMA "OLD" TO "NEW" THREADS N; -- Drop DROP SCHEMA "SCHEMA" CASCADE; -- Rename company UPDATE "SCHEMA".CINF SET "CompnyName" = 'NAME'; UPDATE "SCHEMA".OADM SET "CompnyName" = 'NAME', "PrintHeadr" = 'NAME'; -- Backup tenant BACKUP DATA USING FILE ('/path/prefix'); -- Get tenant name SELECT DATABASE_NAME FROM SYS.M_DATABASES; ```