maksit-lto-backup/README.md

247 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MaksIT.LTO.Backup
![Line Coverage](https://img.shields.io/badge/Line%20Coverage-39.2%25-yellow)
![Branch Coverage](https://img.shields.io/badge/Branch%20Coverage-28.4%25-yellow)
![Method Coverage](https://img.shields.io/badge/Method%20Coverage-49.5%25-yellowgreen)
![.NET](https://img.shields.io/badge/.NET-10-512BD4)
![License](https://img.shields.io/badge/License-GPLv2-blue)
![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux-0078D6)
Cross-platform toolkit for LTO tape backup and restore. Console, Avalonia UI, and Worker share one service layer.
See [LICENSE.md](LICENSE.md) (GPLv2). Changes: [CHANGELOG.md](CHANGELOG.md). Contributing: [CONTRIBUTING.md](CONTRIBUTING.md).
| Host | Project | Role |
|------|---------|------|
| Console | `MaksIT.LTO.Backup` | Interactive menu / `backup --name` CLI |
| Avalonia | `MaksIT.LTO.Backup.UI` | Code-behind operations UI (Windows + Linux; no MVVM). SimpleTheme **Dark** styles aligned with Cluster Console |
| Worker | `MaksIT.LTO.Backup.Service` | Scheduled library backups (Windows service / systemd) |
Device access modes:
- **Physical** — real drive/library on **Windows** (`\\.\Tape0`, `\\.\Changer0`; admin) or **Linux** (`/dev/nst0`, `/dev/sg*`; `tape`/`disk` group)
- **Emulated** — file-backed drive + library (no hardware; primary CI / no-hardware path)
> Current line is **`0.1.0-alpha.1`** (prerelease, not production-ready). Emulator coverage is the primary validation path; physical tape/changer and Linux SMB are not hardware-proven yet. Use at your own risk.
---
If you find this project useful, please consider supporting its development:
[<img src="https://cdn.buymeacoffee.com/buttons/v2/default-blue.png" alt="Buy Me A Coffee" style="height: 60px; width: 217px;">](https://www.buymeacoffee.com/maksitcom)
---
## Features
- Backup and restore with AES-GCM encrypted on-tape JSON descriptors and SHA-256 file verification
- SchemaVersion 2 layout: BOT index (`MLTI`), payload, filemark, descriptor preamble (`MLTD`), ciphertext, double filemark
- Overwrite or Append write modes (multiple sets per cartridge via BOT set table)
- Fail-fast restore on checksum mismatch (`FailFastOnChecksumMismatch`)
- MAM summary attributes written after backup (app name/version, host, text label)
- Local paths on all platforms; SMB source/destination on **Windows** (WNet) and **Linux** (SMB2 staging via SMBLibrary)
- LTO generation block sizes (LTO1–LTO9) and media-capacity checks
- Drive ops: load, eject, short erase, status, cartridge memory (MAM) / barcode
- Library ops: inventory, move medium (emulated; physical Windows `IOCTL_CHANGER_*` and Linux SG_IO)
- Shared library used by console, Avalonia UI, and Worker
- Live Avalonia monitor for drive status and library inventory
- Avalonia Service tab: install / uninstall / start / stop Worker (Windows + Linux)
- Emulator-backed unit / E2E tests without a physical tape (`{tapeId}.drive.json` + `{tapeId}.blocks.bin`)
## Requirements
- [.NET 10 SDK](https://dotnet.microsoft.com/download)
- **Windows:** physical tape/changer IOCTLs; Avalonia UI; SMB via WNet
- **Linux:** physical tape (`/dev/nst*`) and changer (`/dev/sg*`); Avalonia UI; SMB via managed SMB2 client; or `DeviceMode=Emulated`
- Optional: PowerShell 7+ for RepoUtils test/release engines under `utils/`
## Solution layout
```text
utils/ # RepoUtils release / test engines
src/
MaksIT.LTO.slnx
MaksIT.LTO.Core/ # tape/library APIs + emulators
MaksIT.LTO.Backup.Shared/ # shared config, orchestrators, host helpers
MaksIT.LTO.Backup/ # console host
MaksIT.LTO.Backup.UI/ # Avalonia host (Windows + Linux)
MaksIT.LTO.Backup.Service/ # scheduled Worker (Windows / Linux)
MaksIT.LTO.Tests/ # emulator tests
```
## Scheduled backups (library only)
Automatic scheduling runs **only** when `Topology` is `TapeLibrary`. `StandaloneDrive` remains manual (console / UI).
**Backup jobs** define *what* (source, `Tapes` pool, LTO gen). Drive selection is automatic.
**Aggregations** define *when* and *in what order* jobs run as one wave (e.g. Night / Afternoon):
- `JobNames` — ordered list of backup job names
- `Schedule` — UScheduler-compatible calendar with overdue catch-up (`RunMonth`, `RunWeekday`, `RunTime` UTC `HH:mm`, `MinIntervalMinutes`)
- `Disabled` / `LastRunUtc` — wave-level
The Worker holds an exclusive lease for the whole aggregation (no mid-wave collisions). If another wave is busy, the due wave retries on the next poll without consuming `LastRunUtc`. After each job: unload cartridge so the next job can claim any empty drive.
Worker lifecycle (also from Avalonia **Service** tab):
```text
MaksIT.LTO.Backup.Service --install
MaksIT.LTO.Backup.Service --start
MaksIT.LTO.Backup.Service --stop
MaksIT.LTO.Backup.Service --uninstall
```
Console:
```text
MaksIT.LTO.Backup backup --name "Normal test"
MaksIT.LTO.Backup backup --aggregation "Night"
MaksIT.LTO.Backup backup --aggregation "Night" --scheduled
```
Install/uninstall typically require administrator (Windows) or root (Linux systemd).
## Configuration
One shared model and one shared JSON for the whole solution:
- Model: [`src/MaksIT.LTO.Backup.Shared/Models/Configuration.cs`](src/MaksIT.LTO.Backup.Shared/Models/Configuration.cs)
- File: [`src/MaksIT.LTO.Backup.Shared/configuration.json`](src/MaksIT.LTO.Backup.Shared/configuration.json)
- Loader / workflows: `ConfigurationFileService`, `BackupOrchestrator`, `LibraryOrchestrator` in `MaksIT.LTO.Backup.Shared`
Both console and Avalonia UI link this file into their output. Edit the Shared copy only, or use the UI **Settings** tab and click **Save configuration**.
`DeviceMode` selects the backend (`Physical` / `Emulated`).
`Topology` selects how hardware is used:
- `StandaloneDrive` — **Drive** tab only (load/eject/status/MAM). **Backup** tab runs against that drive.
- `TapeLibrary` — **Library** tab (inventory, move into drive bays, MAM on loaded cartridge). **Backup** tab runs only when a cartridge is in a drive bay.
```json
{
"Configuration": {
"DeviceMode": "Emulated",
"Topology": "StandaloneDrive",
"TapePath": "\\\\.\\Tape0",
"LibraryPath": "\\\\.\\Changer0",
"WriteDelay": 100,
"FailFastOnChecksumMismatch": true,
"Emulator": {
"DataDirectory": ".\\emulator-data",
"Library": { "SlotCount": 24, "DriveCount": 2, "IePortCount": 1 }
},
"Backups": [
{
"Name": "Normal test",
"Barcode": "LTO001",
"LTOGen": "LTO5",
"WriteMode": "Overwrite",
"Source": { "LocalPath": { "Path": "F:\\LTO\\Backup" } },
"Destination": { "LocalPath": { "Path": "F:\\LTO\\Restore" } }
}
]
}
}
```
### On-tape format (SchemaVersion 2)
```text
[BOT index MLTI] → [file blocks…] → FM → [MLTD preamble] → [length-prefixed AES-GCM descriptor] → FM FM
```
Append adds another payload+descriptor set at EOD and rewrites the BOT set table (max 16 sets).
### Descriptor secret
On first run a `secret.txt` is created for descriptor encryption (first line = current key). Keep a safe copy.
To rotate: put the new key on line 1 of `secret.txt` and move the old key to line 2+ or into `secret.history.txt`. Decrypt tries the current key then prior keys.
To avoid leaving the file on disk, set a machine environment variable (used as the preferred encrypt/decrypt key):
```powershell
[System.Environment]::SetEnvironmentVariable(
"LTO_BACKUP_SECRET",
"<secret.txt content here>",
[System.EnvironmentVariableTarget]::Machine
)
```
## Console menu
1. Load tape
2. Backup
3. Restore
4. Eject tape
5. Get device status
6. Tape erase (short)
7. Read cartridge memory
8. Move medium (library)
9. Library inventory
10. Exit
## Build and run
```powershell
cd src
dotnet build MaksIT.LTO.slnx
dotnet run --project .\MaksIT.LTO.Backup
```
Avalonia UI (Windows or Linux):
```powershell
dotnet run --project .\MaksIT.LTO.Backup.UI
```
Publish hosts (or use RepoUtils release below):
```powershell
cd src
dotnet publish .\MaksIT.LTO.Backup -c Release -o ..\releases\console
dotnet publish .\MaksIT.LTO.Backup.UI -c Release -o ..\releases\ui
dotnet publish .\MaksIT.LTO.Backup.Service -c Release -o ..\releases\service
```
Physical mode: Windows needs elevation; Linux needs access to `/dev/nst*` / `/dev/sg*` (often `tape`/`disk` group). Discover changer nodes with `lsscsi -g`.
## Tests and coverage
Emulator-backed tests (no tape required):
```powershell
cd src
dotnet test .\MaksIT.LTO.Tests
```
With Cobertura coverage:
```powershell
cd src
dotnet test .\MaksIT.LTO.Tests --collect:"XPlat Code Coverage" --results-directory .\TestResults
```
Coverage shields at the top of this README use [shields.io](https://shields.io) and are rewritten by the **CoverageBadges** plugin when `utils\Invoke-TestEngine.bat` runs.
## RepoUtils release pipeline
Vendored under `utils/` (from MaksIT RepoUtils community):
| Action | Entry |
|--------|--------|
| Test | `utils\Invoke-TestEngine.bat` |
| Release | `utils\Invoke-ReleasePackage.bat` |
Settings:
- `utils/engines/test/scriptSettings.json`
- `utils/engines/release/scriptSettings.json`
## License
GPLv2 — see [LICENSE.md](./LICENSE.md).
© Maksym Sadovnychyy (MAKS-IT)