add hanatui
This commit is contained in:
+215
@@ -0,0 +1,215 @@
|
||||
# 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;
|
||||
```
|
||||
Reference in New Issue
Block a user