diff --git a/src/main.rs b/src/main.rs index 1fc208e..d0b1610 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,10 @@ struct Cli { #[arg(long, default_value = "/etc/zmgr")] registry: String, + /// Show what would be done without executing system commands + #[arg(long, short = 'n')] + dry_run: bool, + #[command(subcommand)] command: Commands, } @@ -124,10 +128,14 @@ fn main() -> miette::Result<()> { let cli = Cli::parse(); let registry = std::path::PathBuf::from(&cli.registry); + let dry_run = cli.dry_run; + match cli.command { Commands::Init { force } => cmd_init(®istry, force), - Commands::Create { name, template } => cmd_create(®istry, &name, template.as_deref()), - Commands::Destroy { name } => cmd_destroy(®istry, &name), + Commands::Create { name, template } => { + cmd_create(®istry, &name, template.as_deref(), dry_run) + } + Commands::Destroy { name } => cmd_destroy(®istry, &name, dry_run), Commands::List => cmd_list(®istry), Commands::Status { name } => cmd_status(®istry, name.as_deref()), Commands::Import { name } => cmd_import(®istry, name.as_deref()), @@ -172,7 +180,12 @@ fn cmd_init(registry: &Path, force: bool) -> Result<()> { Ok(()) } -fn cmd_create(registry: &Path, name: &str, template_name: Option<&str>) -> Result<()> { +fn cmd_create( + registry: &Path, + name: &str, + template_name: Option<&str>, + dry_run: bool, +) -> Result<()> { config::ensure_initialized(registry)?; if Zone::exists(registry, name) { @@ -194,9 +207,6 @@ fn cmd_create(registry: &Path, name: &str, template_name: Option<&str>) -> Resul let address = format!("{ip}/{prefix_len}"); let vnic = format!("{name}0"); - // Create VNIC - exec::dladm_create_vnic(&vnic, &pool.stub)?; - // Build zonecfg commands let autoboot = if tmpl.autoboot { "true" } else { "false" }; let zonecfg_cmds = format!( @@ -215,6 +225,40 @@ fn cmd_create(registry: &Path, name: &str, template_name: Option<&str>) -> Resul cfg.zonepath_prefix, tmpl.brand, tmpl.ip_type, pool.gateway ); + if dry_run { + println!("[dry-run] Would create zone '{name}'"); + println!(); + println!(" template: {tmpl_name}"); + println!(" brand: {}", tmpl.brand); + println!(" address: {address}"); + println!(" vnic: {vnic} (on stub {})", pool.stub); + println!(" gateway: {}", pool.gateway); + println!(" zonepath: {}/{name}", cfg.zonepath_prefix); + println!(" autoboot: {}", tmpl.autoboot); + println!(); + println!("Commands that would be executed:"); + println!(); + println!(" dladm create-vnic -l {} {vnic}", pool.stub); + println!(); + println!(" zonecfg -z {name} <) -> Resul Ok(()) } -fn cmd_destroy(registry: &Path, name: &str) -> Result<()> { +fn cmd_destroy(registry: &Path, name: &str, dry_run: bool) -> Result<()> { config::ensure_initialized(registry)?; let zone = Zone::load(registry, name)?; + if dry_run { + println!("[dry-run] Would destroy zone '{name}'"); + println!(); + println!(" template: {}", zone.template); + println!(" address: {}", zone.address); + println!(" vnic: {}", zone.vnic); + println!(); + println!("Commands that would be executed:"); + println!(); + println!(" zoneadm -z {name} halt"); + println!(" zoneadm -z {name} uninstall -F"); + println!(" zonecfg -z {name} delete -F"); + println!(" dladm delete-vnic {}", zone.vnic); + println!(); + println!("Registry entry that would be removed:"); + println!(" {}/zones/{name}.kdl", registry.display()); + return Ok(()); + } + // Halt if running (ignore errors — zone may already be halted) let _ = exec::zoneadm_halt(name);