diff --git a/src/main.rs b/src/main.rs index 35b12a5..7e28351 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,48 @@ -use anyhow::Result; use clap::Parser; -use kvm_install_vm::Cli; +use kvm_install_vm::{Cli, vm::VirtualMachine}; +use std::process; -#[tokio::main] -async fn main() -> Result<()> { +fn main() { // Initialize logger env_logger::init(); // Parse command line arguments - let cli = Cli::parse(); + let args = Cli::parse(); println!("Starting kvm-install-vm Rust implementation..."); - println!("VM Name: {}", cli.name); - println!("Distribution: {}", cli.distro); + println!("VM Name: {}", args.name); + println!("Distribution: {}", args.distro); println!("Configuration:"); - println!(" vCPUs: {}", cli.vcpus); - println!(" Memory: {} MB", cli.memory_mb); - println!(" Disk Size: {} GB", cli.disk_size_gb); + println!(" vCPUs: {}", args.vcpus); + println!(" Memory: {} MB", args.memory_mb); + println!(" Disk Size: {} GB", args.disk_size_gb); - if cli.dry_run { - println!("Dry run mode - no VM will be created"); - return Ok(()); - } + let disk_path = format!("/home/giovanni/virt/images/{}.qcow2", args.name); + let vm_name = args.name.clone(); + + let mut vm = VirtualMachine::new( + args.name, + args.vcpus, + args.memory_mb, + args.disk_size_gb, + disk_path, + // args.distro, + ); - println!("VM creation would start here..."); + if let Err(e) = vm.connect(None) { + eprintln!("Failed to connect to libvirt: {}", e); + process::exit(1); + } - Ok(()) + match vm.create() { + Ok(domain) => { + println!("Successfully created VM: {}", vm_name); + println!("Domain ID: {}", domain.get_id().unwrap_or(0)); + }, + Err(e) => { + eprintln!("Failed to create VM: {}", e); + process::exit(1); + } + } } diff --git a/src/vm.rs b/src/vm.rs index e25223b..a6be595 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,24 +1,102 @@ -use anyhow::Result; +use anyhow::{Result, Context}; +use std::path::Path; +use virt::connect::Connect; +use virt::domain::Domain; pub struct VirtualMachine { - pub name: String, - pub vcpus: u32, - pub memory_mb: u32, - pub disk_size_gb: u32, + name: String, + vcpus: u32, + memory_mb: u32, + disk_size_gb: u32, + disk_path: String, + // distro: String, + connection: Option, } impl VirtualMachine { - pub fn new(name: String, vcpus: u32, memory_mb: u32, disk_size_gb: u32) -> Self { + pub fn new(name: String, vcpus: u32, memory_mb: u32, disk_size_gb: u32, disk_path: String) -> Self { VirtualMachine { name, vcpus, memory_mb, + disk_path, disk_size_gb, + // distro, + connection: None, } } - pub fn create(&self) -> Result<()> { - println!("Creating VM: {}", self.name); + pub fn connect(&mut self, uri: Option<&str>) -> Result<()> { + // Connect to libvirt daemon, default to "qemu:///system" if no URI provided + let uri = uri.or(Some("qemu:///system")); + self.connection = Some(Connect::open(uri).context("Failed to connect to libvirt")?); Ok(()) } + + pub fn create(&mut self) -> Result { + // Ensure connection is established + let conn = match &self.connection { + Some(c) => c, + None => return Err(anyhow::anyhow!("Connection not established")), + }; + + if !Path::new(&self.disk_path).exists() { + self.create_disk_image()?; + } + + let xml = self.generate_domain_xml()?; + + let domain = Domain::define_xml(&conn, &xml).context("Failed to define domain from XML")?; + domain.create().context("Failed to start the domain")?; + + Ok(domain) + } + + fn create_disk_image(&self) -> Result<()> { + // Create disk image using qemu-img + let output = std::process::Command::new("qemu-img") + .args(&["create", "-f", "qcow2", &self.disk_path, &format!("{}G", self.disk_size_gb)]) + .output() + .context("Failed to execute qemu-img command")?; + + if !output.status.success() { + return Err(anyhow::anyhow!("Failed to create disk image: {:?}", output.stderr)); + } + + Ok(()) + } + + fn generate_domain_xml(&self) -> Result { + // Generate domain XML + let xml = format!( r#" + + {} + {} + {} + + hvm + + + + + + + + + + + + + + + + + + + + + "#, self.name, self.memory_mb, self.vcpus, self.disk_path); + + Ok(xml) + } }