Files
2026-05-20 11:13:14 +02:00

216 lines
8.8 KiB
Markdown

# 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:
```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<T>, TextPrompt<T> — 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;
```