Browse Source

style: run formatting

rust
Giovanni Torres 1 year ago
parent
commit
1bd40e236f
  1. 4
      src/cli.rs
  2. 34
      src/main.rs
  3. 165
      src/vm.rs
  4. 57
      tests/cli_tests.rs
  5. 36
      tests/list_vms.rs
  6. 85
      tests/vm_tests.rs

4
src/cli.rs

@ -40,10 +40,10 @@ pub enum Commands {
List { List {
#[arg(short = 'a', long, help = "Show all domains (including inactive ones)")] #[arg(short = 'a', long, help = "Show all domains (including inactive ones)")]
all: bool, all: bool,
#[arg(short = 's', long, help = "Show only running domains")] #[arg(short = 's', long, help = "Show only running domains")]
running: bool, running: bool,
#[arg(short = 'i', long, help = "Show only inactive domains")] #[arg(short = 'i', long, help = "Show only inactive domains")]
inactive: bool, inactive: bool,
}, },

34
src/main.rs

@ -68,41 +68,47 @@ fn main() {
} }
} }
} }
Commands::List { all, running, inactive } => { Commands::List {
all,
running,
inactive,
} => {
println!("Listing virtual machines..."); println!("Listing virtual machines...");
// Determine which types of domains to list // Determine which types of domains to list
let filters = (*all, *running, *inactive); let filters = (*all, *running, *inactive);
// If no specific flags are provided, default to showing all domains // If no specific flags are provided, default to showing all domains
let show_all = filters == (false, false, false) || *all; let show_all = filters == (false, false, false) || *all;
match VirtualMachine::list_domains(None) { match VirtualMachine::list_domains(None) {
Ok(domains) => { Ok(domains) => {
// Filter domains based on flags // Filter domains based on flags
let filtered_domains: Vec<_> = domains.into_iter() let filtered_domains: Vec<_> = domains
.into_iter()
.filter(|domain| { .filter(|domain| {
if show_all { if show_all {
return true; return true;
} }
if *running && domain.state == kvm_install_vm::vm::DomainState::Running { if *running && domain.state == kvm_install_vm::vm::DomainState::Running
{
return true; return true;
} }
if *inactive && domain.id.is_none() { if *inactive && domain.id.is_none() {
return true; return true;
} }
false false
}) })
.collect(); .collect();
// Print header // Print header
println!("{:<5} {:<30} {:<10}", "ID", "Name", "State"); println!("{:<5} {:<30} {:<10}", "ID", "Name", "State");
println!("{:-<5} {:-<30} {:-<10}", "", "", ""); println!("{:-<5} {:-<30} {:-<10}", "", "", "");
// Print domains // Print domains
if filtered_domains.is_empty() { if filtered_domains.is_empty() {
println!("No domains found matching the specified criteria"); println!("No domains found matching the specified criteria");
@ -112,7 +118,7 @@ fn main() {
Some(id) => id.to_string(), Some(id) => id.to_string(),
None => "-".to_string(), None => "-".to_string(),
}; };
println!("{:<5} {:<30} {:<10}", id_str, domain.name, domain.state); println!("{:<5} {:<30} {:<10}", id_str, domain.name, domain.state);
} }
} }
@ -124,4 +130,4 @@ fn main() {
} }
} }
} }
} }

165
src/vm.rs

@ -1,11 +1,10 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::path::Path;
use std::fmt; use std::fmt;
use std::path::Path;
use virt::connect::Connect; use virt::connect::Connect;
use virt::domain::Domain; use virt::domain::Domain;
use virt::sys; use virt::sys;
pub struct VirtualMachine { pub struct VirtualMachine {
pub name: String, pub name: String,
pub vcpus: u32, pub vcpus: u32,
@ -18,7 +17,7 @@ pub struct VirtualMachine {
#[derive(Debug)] #[derive(Debug)]
pub struct DomainInfo { pub struct DomainInfo {
pub id: Option<u32>, // None if domain is inactive pub id: Option<u32>, // None if domain is inactive
pub name: String, pub name: String,
pub state: DomainState, pub state: DomainState,
} }
@ -226,88 +225,84 @@ impl VirtualMachine {
} }
pub fn list_domains(uri: Option<&str>) -> Result<Vec<DomainInfo>> { pub fn list_domains(uri: Option<&str>) -> Result<Vec<DomainInfo>> {
let uri = uri.or(Some("qemu:///session")); let uri = uri.or(Some("qemu:///session"));
let conn = Connect::open(uri).context("Failed to connect to libvirt")?; let conn = Connect::open(uri).context("Failed to connect to libvirt")?;
let mut domain_infos = Vec::new(); let mut domain_infos = Vec::new();
// Get active domains // Get active domains
let active_domains = conn.list_all_domains(sys::VIR_CONNECT_LIST_DOMAINS_ACTIVE) let active_domains = conn
.context("Failed to list active domains")?; .list_all_domains(sys::VIR_CONNECT_LIST_DOMAINS_ACTIVE)
.context("Failed to list active domains")?;
// Get inactive domains
let inactive_domains = conn.list_all_domains(sys::VIR_CONNECT_LIST_DOMAINS_INACTIVE) // Get inactive domains
.context("Failed to list inactive domains")?; let inactive_domains = conn
.list_all_domains(sys::VIR_CONNECT_LIST_DOMAINS_INACTIVE)
// Process active domains .context("Failed to list inactive domains")?;
for domain in active_domains {
let name = domain.get_name().context("Failed to get domain name")?; // Process active domains
let id = domain.get_id(); for domain in active_domains {
let name = domain.get_name().context("Failed to get domain name")?;
// Get domain state let id = domain.get_id();
let state = match domain.get_state() {
Ok((state, _reason)) => { // Get domain state
match state { let state = match domain.get_state() {
sys::VIR_DOMAIN_RUNNING => DomainState::Running, Ok((state, _reason)) => match state {
sys::VIR_DOMAIN_PAUSED => DomainState::Paused, sys::VIR_DOMAIN_RUNNING => DomainState::Running,
sys::VIR_DOMAIN_SHUTDOWN => DomainState::Shutdown, sys::VIR_DOMAIN_PAUSED => DomainState::Paused,
sys::VIR_DOMAIN_SHUTOFF => DomainState::Shutoff, sys::VIR_DOMAIN_SHUTDOWN => DomainState::Shutdown,
sys::VIR_DOMAIN_CRASHED => DomainState::Crashed, sys::VIR_DOMAIN_SHUTOFF => DomainState::Shutoff,
_ => DomainState::Unknown, sys::VIR_DOMAIN_CRASHED => DomainState::Crashed,
} _ => DomainState::Unknown,
}, },
Err(_) => DomainState::Unknown, Err(_) => DomainState::Unknown,
}; };
domain_infos.push(DomainInfo { domain_infos.push(DomainInfo { id, name, state });
id, }
name,
state, // Process inactive domains
}); for domain in inactive_domains {
} let name = domain.get_name().context("Failed to get domain name")?;
// Process inactive domains domain_infos.push(DomainInfo {
for domain in inactive_domains { id: None,
let name = domain.get_name().context("Failed to get domain name")?; name,
state: DomainState::Shutoff,
domain_infos.push(DomainInfo { });
id: None, }
name,
state: DomainState::Shutoff, // Sort domains by name for consistent output
}); domain_infos.sort_by(|a, b| a.name.cmp(&b.name));
}
Ok(domain_infos)
// Sort domains by name for consistent output }
domain_infos.sort_by(|a, b| a.name.cmp(&b.name));
/// Pretty print the list of domains
Ok(domain_infos) pub fn print_domain_list(uri: Option<&str>) -> Result<()> {
} let domains = Self::list_domains(uri)?;
/// Pretty print the list of domains if domains.is_empty() {
pub fn print_domain_list(uri: Option<&str>) -> Result<()> { println!("No domains found");
let domains = Self::list_domains(uri)?; return Ok(());
}
if domains.is_empty() {
println!("No domains found"); // Print header
return Ok(()); println!("{:<5} {:<30} {:<10}", "ID", "Name", "State");
} println!("{:-<5} {:-<30} {:-<10}", "", "", "");
// Print header // Print domains
println!("{:<5} {:<30} {:<10}", "ID", "Name", "State"); for domain in domains {
println!("{:-<5} {:-<30} {:-<10}", "", "", ""); let id_str = match domain.id {
Some(id) => id.to_string(),
// Print domains None => "-".to_string(),
for domain in domains { };
let id_str = match domain.id {
Some(id) => id.to_string(), println!("{:<5} {:<30} {:<10}", id_str, domain.name, domain.state);
None => "-".to_string(), }
};
Ok(())
println!("{:<5} {:<30} {:<10}", id_str, domain.name, domain.state); }
}
Ok(())
}
} }
fn extract_disk_paths_from_xml(xml: &str) -> Vec<String> { fn extract_disk_paths_from_xml(xml: &str) -> Vec<String> {

57
tests/cli_tests.rs

@ -15,14 +15,14 @@ fn test_cli_create_defaults() {
let cli = Cli::parse_from(args); let cli = Cli::parse_from(args);
match cli.command { match cli.command {
Commands::Create { Commands::Create {
name, name,
distro, distro,
vcpus, vcpus,
memory_mb, memory_mb,
disk_size_gb, disk_size_gb,
graphics, graphics,
dry_run dry_run,
} => { } => {
assert_eq!(name, "test-vm"); assert_eq!(name, "test-vm");
assert_eq!(distro, "centos8"); assert_eq!(distro, "centos8");
@ -31,7 +31,7 @@ fn test_cli_create_defaults() {
assert_eq!(memory_mb, 1024); assert_eq!(memory_mb, 1024);
assert_eq!(graphics, false); assert_eq!(graphics, false);
assert_eq!(dry_run, false); assert_eq!(dry_run, false);
}, }
_ => panic!("Expected Create command"), _ => panic!("Expected Create command"),
} }
} }
@ -40,11 +40,16 @@ fn test_cli_create_defaults() {
fn test_cli_create_custom_values() { fn test_cli_create_custom_values() {
let args = get_args(&[ let args = get_args(&[
"create", "create",
"--name", "custom-vm", "--name",
"--distro", "ubuntu2004", "custom-vm",
"--vcpus", "4", "--distro",
"--memory-mb", "4096", "ubuntu2004",
"--disk-size-gb", "50", "--vcpus",
"4",
"--memory-mb",
"4096",
"--disk-size-gb",
"50",
"--graphics", "--graphics",
"--dry-run", "--dry-run",
]); ]);
@ -52,14 +57,14 @@ fn test_cli_create_custom_values() {
let cli = Cli::parse_from(args); let cli = Cli::parse_from(args);
match cli.command { match cli.command {
Commands::Create { Commands::Create {
name, name,
distro, distro,
vcpus, vcpus,
memory_mb, memory_mb,
disk_size_gb, disk_size_gb,
graphics, graphics,
dry_run dry_run,
} => { } => {
assert_eq!(name, "custom-vm"); assert_eq!(name, "custom-vm");
assert_eq!(distro, "ubuntu2004"); assert_eq!(distro, "ubuntu2004");
@ -68,7 +73,7 @@ fn test_cli_create_custom_values() {
assert_eq!(memory_mb, 4096); assert_eq!(memory_mb, 4096);
assert_eq!(graphics, true); assert_eq!(graphics, true);
assert_eq!(dry_run, true); assert_eq!(dry_run, true);
}, }
_ => panic!("Expected Create command"), _ => panic!("Expected Create command"),
} }
} }
@ -82,7 +87,7 @@ fn test_cli_destroy_defaults() {
Commands::Destroy { name, remove_disk } => { Commands::Destroy { name, remove_disk } => {
assert_eq!(name, "test-vm"); assert_eq!(name, "test-vm");
assert_eq!(remove_disk, false); assert_eq!(remove_disk, false);
}, }
_ => panic!("Expected Destroy command"), _ => panic!("Expected Destroy command"),
} }
} }
@ -96,7 +101,7 @@ fn test_cli_destroy_with_disk_removal() {
Commands::Destroy { name, remove_disk } => { Commands::Destroy { name, remove_disk } => {
assert_eq!(name, "test-vm"); assert_eq!(name, "test-vm");
assert_eq!(remove_disk, true); assert_eq!(remove_disk, true);
}, }
_ => panic!("Expected Destroy command"), _ => panic!("Expected Destroy command"),
} }
} }

36
tests/list_vms.rs

@ -1,8 +1,8 @@
#[cfg(test)] #[cfg(test)]
mod domain_list_tests { mod domain_list_tests {
use clap::Parser; use clap::Parser;
use kvm_install_vm::vm::{DomainState, VirtualMachine};
use kvm_install_vm::{Cli, cli::Commands}; use kvm_install_vm::{Cli, cli::Commands};
use kvm_install_vm::vm::{VirtualMachine, DomainState};
use std::ffi::OsString; use std::ffi::OsString;
// Helper function for CLI tests // Helper function for CLI tests
@ -19,11 +19,15 @@ mod domain_list_tests {
let cli = Cli::parse_from(args); let cli = Cli::parse_from(args);
match cli.command { match cli.command {
Commands::List { all, running, inactive } => { Commands::List {
all,
running,
inactive,
} => {
assert_eq!(all, false); assert_eq!(all, false);
assert_eq!(running, false); assert_eq!(running, false);
assert_eq!(inactive, false); assert_eq!(inactive, false);
}, }
_ => panic!("Expected List command"), _ => panic!("Expected List command"),
} }
} }
@ -34,11 +38,15 @@ mod domain_list_tests {
let cli = Cli::parse_from(args); let cli = Cli::parse_from(args);
match cli.command { match cli.command {
Commands::List { all, running, inactive } => { Commands::List {
all,
running,
inactive,
} => {
assert_eq!(all, true); assert_eq!(all, true);
assert_eq!(running, true); assert_eq!(running, true);
assert_eq!(inactive, false); assert_eq!(inactive, false);
}, }
_ => panic!("Expected List command"), _ => panic!("Expected List command"),
} }
} }
@ -50,26 +58,28 @@ mod domain_list_tests {
fn test_domain_listing() -> anyhow::Result<()> { fn test_domain_listing() -> anyhow::Result<()> {
// This will connect to libvirt and list domains // This will connect to libvirt and list domains
let domains = VirtualMachine::list_domains(None)?; let domains = VirtualMachine::list_domains(None)?;
// We can't assert much about the actual domains without knowing the test environment, // We can't assert much about the actual domains without knowing the test environment,
// but we can check that the function runs without errors and returns a Vec // but we can check that the function runs without errors and returns a Vec
println!("Found {} domains", domains.len()); println!("Found {} domains", domains.len());
for domain in &domains { for domain in &domains {
println!("Domain: {}, ID: {:?}, State: {:?}", println!(
domain.name, domain.id, domain.state); "Domain: {}, ID: {:?}, State: {:?}",
domain.name, domain.id, domain.state
);
// Check that inactive domains have no ID // Check that inactive domains have no ID
if domain.state == DomainState::Shutoff { if domain.state == DomainState::Shutoff {
assert_eq!(domain.id, None); assert_eq!(domain.id, None);
} }
// If it's running, it should have an ID // If it's running, it should have an ID
if domain.state == DomainState::Running { if domain.state == DomainState::Running {
assert!(domain.id.is_some()); assert!(domain.id.is_some());
} }
} }
Ok(()) Ok(())
} }
} }

85
tests/vm_tests.rs

@ -1,9 +1,9 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use anyhow::Result;
use kvm_install_vm::vm::VirtualMachine; use kvm_install_vm::vm::VirtualMachine;
use std::fs; use std::fs;
use std::process::Command; use std::process::Command;
use anyhow::Result;
use virt::domain::Domain; use virt::domain::Domain;
// Helper function to check if a VM with a given name exists // Helper function to check if a VM with a given name exists
@ -12,14 +12,15 @@ mod tests {
.args(["list", "--all", "--name"]) .args(["list", "--all", "--name"])
.output() .output()
.expect("Failed to execute virsh command"); .expect("Failed to execute virsh command");
let output_str = String::from_utf8_lossy(&output.stdout); let output_str = String::from_utf8_lossy(&output.stdout);
output_str.lines().any(|line| line.trim() == name) output_str.lines().any(|line| line.trim() == name)
} }
// Custom function to generate domain XML for testing // Custom function to generate domain XML for testing
fn generate_test_domain_xml(vm: &VirtualMachine) -> String { fn generate_test_domain_xml(vm: &VirtualMachine) -> String {
format!(r#" format!(
r#"
<domain type='kvm'> <domain type='kvm'>
<name>{}</name> <name>{}</name>
<memory unit='MiB'>{}</memory> <memory unit='MiB'>{}</memory>
@ -46,7 +47,9 @@ mod tests {
<graphics type='vnc' port='-1'/> <graphics type='vnc' port='-1'/>
</devices> </devices>
</domain> </domain>
"#, vm.name, vm.memory_mb, vm.vcpus, vm.disk_path) "#,
vm.name, vm.memory_mb, vm.vcpus, vm.disk_path
)
} }
// Extract disk paths function for testing // Extract disk paths function for testing
@ -79,7 +82,7 @@ mod tests {
let temp_dir = std::env::temp_dir(); let temp_dir = std::env::temp_dir();
let disk_path = temp_dir.join(format!("{}.qcow2", name)); let disk_path = temp_dir.join(format!("{}.qcow2", name));
// Create a minimal VM for testing // Create a minimal VM for testing
let mut vm = VirtualMachine::new( let mut vm = VirtualMachine::new(
name.to_string(), name.to_string(),
@ -90,15 +93,16 @@ mod tests {
); );
vm.connect(None)?; vm.connect(None)?;
// Create disk if it doesn't exist // Create disk if it doesn't exist
if !disk_path.exists() { if !disk_path.exists() {
Command::new("qemu-img") Command::new("qemu-img")
.args([ .args([
"create", "create",
"-f", "qcow2", "-f",
disk_path.to_string_lossy().as_ref(), "qcow2",
"1G" disk_path.to_string_lossy().as_ref(),
"1G",
]) ])
.output() .output()
.expect("Failed to create test disk image"); .expect("Failed to create test disk image");
@ -115,12 +119,16 @@ mod tests {
// Helper to clean up any leftover test resources // Helper to clean up any leftover test resources
fn cleanup_test_resources(name: &str) { fn cleanup_test_resources(name: &str) {
if domain_exists(name) { if domain_exists(name) {
let _ = Command::new("virsh").args(["destroy", name]).output();
let _ = Command::new("virsh") let _ = Command::new("virsh")
.args(["destroy", name]) .args([
.output(); "undefine",
name,
let _ = Command::new("virsh") "--managed-save",
.args(["undefine", name, "--managed-save", "--snapshots-metadata", "--nvram"]) "--snapshots-metadata",
"--nvram",
])
.output(); .output();
} }
@ -170,7 +178,7 @@ mod tests {
"#; "#;
let disk_paths = extract_disk_paths_from_xml(xml); let disk_paths = extract_disk_paths_from_xml(xml);
assert_eq!(disk_paths.len(), 2); assert_eq!(disk_paths.len(), 2);
assert!(disk_paths.contains(&"/path/to/disk1.qcow2".to_string())); assert!(disk_paths.contains(&"/path/to/disk1.qcow2".to_string()));
assert!(disk_paths.contains(&"/path/to/disk2.qcow2".to_string())); assert!(disk_paths.contains(&"/path/to/disk2.qcow2".to_string()));
@ -191,7 +199,7 @@ mod tests {
vm.connect(None)?; vm.connect(None)?;
assert!(vm.connection.is_some()); assert!(vm.connection.is_some());
Ok(()) Ok(())
} }
@ -203,10 +211,10 @@ mod tests {
let test_name = "test-create-destroy-vm"; let test_name = "test-create-destroy-vm";
let temp_dir = std::env::temp_dir(); let temp_dir = std::env::temp_dir();
let disk_path = temp_dir.join(format!("{}.qcow2", test_name)); let disk_path = temp_dir.join(format!("{}.qcow2", test_name));
// Clean up any previous test resources // Clean up any previous test resources
cleanup_test_resources(test_name); cleanup_test_resources(test_name);
// Create a new VM // Create a new VM
let mut vm = VirtualMachine::new( let mut vm = VirtualMachine::new(
test_name.to_string(), test_name.to_string(),
@ -217,41 +225,42 @@ mod tests {
); );
vm.connect(None)?; vm.connect(None)?;
// Create disk if it doesn't exist // Create disk if it doesn't exist
if !disk_path.exists() { if !disk_path.exists() {
Command::new("qemu-img") Command::new("qemu-img")
.args([ .args([
"create", "create",
"-f", "qcow2", "-f",
disk_path.to_string_lossy().as_ref(), "qcow2",
"1G" disk_path.to_string_lossy().as_ref(),
"1G",
]) ])
.output() .output()
.expect("Failed to create test disk image"); .expect("Failed to create test disk image");
} }
// Create the VM via helper function since we can't call the private method // Create the VM via helper function since we can't call the private method
let conn = vm.connection.as_ref().unwrap(); let conn = vm.connection.as_ref().unwrap();
let xml = generate_test_domain_xml(&vm); let xml = generate_test_domain_xml(&vm);
let domain = Domain::define_xml(conn, &xml)?; let domain = Domain::define_xml(conn, &xml)?;
domain.create()?; domain.create()?;
// Verify it exists // Verify it exists
assert!(domain_exists(test_name)); assert!(domain_exists(test_name));
// Now destroy it // Now destroy it
vm.destroy_instance(false)?; vm.destroy_instance(false)?;
// Verify it no longer exists // Verify it no longer exists
assert!(!domain_exists(test_name)); assert!(!domain_exists(test_name));
// Disk should still exist since we used remove_disk=false // Disk should still exist since we used remove_disk=false
assert!(disk_path.exists()); assert!(disk_path.exists());
// Clean up disk // Clean up disk
let _ = fs::remove_file(disk_path); let _ = fs::remove_file(disk_path);
Ok(()) Ok(())
} }
@ -260,24 +269,24 @@ mod tests {
#[ignore] #[ignore]
fn test_destroy_static_method() -> Result<()> { fn test_destroy_static_method() -> Result<()> {
let test_name = "test-static-destroy-vm"; let test_name = "test-static-destroy-vm";
// Create a test VM first // Create a test VM first
create_test_vm(test_name)?; create_test_vm(test_name)?;
// Verify it exists // Verify it exists
assert!(domain_exists(test_name)); assert!(domain_exists(test_name));
// Destroy it with the static method // Destroy it with the static method
VirtualMachine::destroy(test_name, None, true)?; VirtualMachine::destroy(test_name, None, true)?;
// Verify it no longer exists // Verify it no longer exists
assert!(!domain_exists(test_name)); assert!(!domain_exists(test_name));
// Disk should be gone since we used remove_disk=true // Disk should be gone since we used remove_disk=true
let temp_dir = std::env::temp_dir(); let temp_dir = std::env::temp_dir();
let disk_path = temp_dir.join(format!("{}.qcow2", test_name)); let disk_path = temp_dir.join(format!("{}.qcow2", test_name));
assert!(!disk_path.exists()); assert!(!disk_path.exists());
Ok(()) Ok(())
} }
@ -287,4 +296,4 @@ mod tests {
let result = VirtualMachine::destroy("definitely-nonexistent-vm", None, false); let result = VirtualMachine::destroy("definitely-nonexistent-vm", None, false);
assert!(result.is_err()); assert!(result.is_err());
} }
} }

Loading…
Cancel
Save