Files
Scripts/hanatui/PLAN.md
T
2026-05-20 11:13:14 +02:00

8.8 KiB

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:

  • <PublishAot>true</PublishAot>
  • <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  • <InvariantGlobalization>true</InvariantGlobalization>
  • <StripSymbols>true</StripSymbols>
  • NuGet: Spectre.Console (core only, no .Cli)

Build command:

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

-- 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;