216 lines
8.8 KiB
Markdown
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;
|
|
```
|