feat: first-draft preview-capable zosstorage

- CLI: add topology selection (-t/--topology), preview flags (--show/--report), and removable policy override (--allow-removable) (src/cli/args.rs)
- Config: built-in sensible defaults; deterministic overlays for logging, fstab, removable, topology (src/config/loader.rs)
- Device: discovery via /proc + /sys with include/exclude regex and removable policy (src/device/discovery.rs)
- Idempotency: detection via blkid; safe emptiness checks (src/idempotency/mod.rs)
- Partition: topology-driven planning (Single, DualIndependent, BtrfsRaid1, SsdHddBcachefs) (src/partition/plan.rs)
- FS: planning + creation (mkfs.vfat, mkfs.btrfs, bcachefs format) and UUID capture via blkid (src/fs/plan.rs)
- Orchestrator: pre-flight with preview JSON (disks, partition_plan, filesystems_planned, mount scheme). Skips emptiness in preview; supports stdout+file (src/orchestrator/run.rs)
- Util/Logging/Types/Errors: process execution, tracing, shared types (src/util/mod.rs, src/logging/mod.rs, src/types.rs, src/errors.rs)
- Docs: add README with exhaustive usage and preview JSON shape (README.md)

Builds and unit tests pass: discovery, util, idempotency helpers, and fs parser tests.
This commit is contained in:
2025-09-29 11:37:07 +02:00
commit 507bc172c2
38 changed files with 6558 additions and 0 deletions

121
src/cli/args.rs Normal file
View File

@@ -0,0 +1,121 @@
// REGION: API
// api: cli::LogLevelArg { Error, Warn, Info, Debug }
// api: cli::Cli { config: Option<String>, log_level: LogLevelArg, log_to_file: bool, fstab: bool, force: bool }
// api: cli::from_args() -> crate::cli::Cli
// REGION: API-END
//
// REGION: RESPONSIBILITIES
// - Define non-interactive CLI flags mirroring kernel cmdline semantics.
// - Provide a stable parsing entry (from_args) suitable for initramfs.
// Non-goals: config validation, IO, or side effects beyond parsing.
// REGION: RESPONSIBILITIES-END
//
// REGION: EXTENSION_POINTS
// ext: add --dry-run to support planning without changes (future).
// ext: add --report-path to override JSON report location (future).
// REGION: EXTENSION_POINTS-END
//
// REGION: SAFETY
// safety: no interactive prompts; default values are explicit; parsing errors should be clear.
// REGION: SAFETY-END
//
// REGION: ERROR_MAPPING
// errmap: clap parsing errors are emitted by clap; higher layers should handle exit strategy.
// REGION: ERROR_MAPPING-END
//
// REGION: TODO
// todo: consider hidden/unstable flags gated by build features for developers.
// REGION: TODO-END
//! CLI definition mirroring kernel cmdline semantics; non-interactive.
use clap::{Parser, ValueEnum};
#[derive(Debug, Clone, Copy, ValueEnum)]
#[value(rename_all = "kebab_case")]
pub enum LogLevelArg {
Error,
Warn,
Info,
Debug,
}
impl std::fmt::Display for LogLevelArg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
LogLevelArg::Error => "error",
LogLevelArg::Warn => "warn",
LogLevelArg::Info => "info",
LogLevelArg::Debug => "debug",
};
f.write_str(s)
}
}
/// Topology argument (maps to config Topology with snake_case semantics).
#[derive(Debug, Clone, Copy, ValueEnum)]
#[value(rename_all = "kebab_case")]
pub enum TopologyArg {
Single,
DualIndependent,
SsdHddBcachefs,
BtrfsRaid1,
}
impl std::fmt::Display for TopologyArg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
TopologyArg::Single => "single",
TopologyArg::DualIndependent => "dual_independent",
TopologyArg::SsdHddBcachefs => "ssd_hdd_bcachefs",
TopologyArg::BtrfsRaid1 => "btrfs_raid1",
};
f.write_str(s)
}
}
/// zosstorage - one-shot disk initializer for initramfs.
#[derive(Debug, Parser)]
#[command(name = "zosstorage", disable_help_subcommand = true)]
pub struct Cli {
/// Path to YAML configuration (mirrors kernel cmdline key 'zosstorage.config=')
#[arg(short = 'c', long = "config")]
pub config: Option<String>,
/// Log level: error, warn, info, debug
#[arg(short = 'l', long = "log-level", value_enum, default_value_t = LogLevelArg::Info)]
pub log_level: LogLevelArg,
/// Also log to /run/zosstorage/zosstorage.log
#[arg(short = 'L', long = "log-to-file", default_value_t = false)]
pub log_to_file: bool,
/// Enable writing /etc/fstab entries
#[arg(short = 's', long = "fstab", default_value_t = false)]
pub fstab: bool,
/// Select topology (overrides config topology)
#[arg(short = 't', long = "topology", value_enum)]
pub topology: Option<TopologyArg>,
/// Present but non-functional; returns unimplemented error
#[arg(short = 'f', long = "force")]
pub force: bool,
/// Allow removable devices (e.g., USB sticks) to be considered during discovery
/// Overrides config.device_selection.allow_removable when provided
#[arg(long = "allow-removable", default_value_t = false)]
pub allow_removable: bool,
/// Print detection and planning summary as JSON to stdout (non-default)
#[arg(long = "show", default_value_t = false)]
pub show: bool,
/// Write detection/planning JSON report to the given path (overrides config.report.path)
#[arg(long = "report")]
pub report: Option<String>,
}
/// Parse CLI arguments (non-interactive; suitable for initramfs).
pub fn from_args() -> Cli {
Cli::parse()
}