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 {
#[arg(short = 'a', long, help = "Show all domains (including inactive ones)")]
all: bool,
#[arg(short = 's', long, help = "Show only running domains")]
running: bool,
#[arg(short = 'i', long, help = "Show only inactive domains")]
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...");
// Determine which types of domains to list
let filters = (*all, *running, *inactive);
// If no specific flags are provided, default to showing all domains
let show_all = filters == (false, false, false) || *all;
match VirtualMachine::list_domains(None) {
Ok(domains) => {
// Filter domains based on flags
let filtered_domains: Vec<_> = domains.into_iter()
let filtered_domains: Vec<_> = domains
.into_iter()
.filter(|domain| {
if show_all {
return true;
}
if *running && domain.state == kvm_install_vm::vm::DomainState::Running {
if *running && domain.state == kvm_install_vm::vm::DomainState::Running
{
return true;
}
if *inactive && domain.id.is_none() {
return true;
}
false
})
.collect();
// Print header
println!("{:<5} {:<30} {:<10}", "ID", "Name", "State");
println!("{:-<5} {:-<30} {:-<10}", "", "", "");
// Print domains
if filtered_domains.is_empty() {
println!("No domains found matching the specified criteria");
@ -112,7 +118,7 @@ fn main() {
Some(id) => id.to_string(),
None => "-".to_string(),
};
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 std::path::Path;
use std::fmt;
use std::path::Path;
use virt::connect::Connect;
use virt::domain::Domain;
use virt::sys;
pub struct VirtualMachine {
pub name: String,
pub vcpus: u32,
@ -18,7 +17,7 @@ pub struct VirtualMachine {
#[derive(Debug)]
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 state: DomainState,
}
@ -226,88 +225,84 @@ impl VirtualMachine {
}
pub fn list_domains(uri: Option<&str>) -> Result<Vec<DomainInfo>> {
let uri = uri.or(Some("qemu:///session"));
let conn = Connect::open(uri).context("Failed to connect to libvirt")?;
let mut domain_infos = Vec::new();
// Get active domains
let active_domains = conn.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)
.context("Failed to list inactive domains")?;
// Process active domains
for domain in active_domains {
let name = domain.get_name().context("Failed to get domain name")?;
let id = domain.get_id();
// Get domain state
let state = match domain.get_state() {
Ok((state, _reason)) => {
match state {
sys::VIR_DOMAIN_RUNNING => DomainState::Running,
sys::VIR_DOMAIN_PAUSED => DomainState::Paused,
sys::VIR_DOMAIN_SHUTDOWN => DomainState::Shutdown,
sys::VIR_DOMAIN_SHUTOFF => DomainState::Shutoff,
sys::VIR_DOMAIN_CRASHED => DomainState::Crashed,
_ => DomainState::Unknown,
}
},
Err(_) => DomainState::Unknown,
};
domain_infos.push(DomainInfo {
id,
name,
state,
});
}
// Process inactive domains
for domain in inactive_domains {
let name = domain.get_name().context("Failed to get domain name")?;
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)
}
/// Pretty print the list of domains
pub fn print_domain_list(uri: Option<&str>) -> Result<()> {
let domains = Self::list_domains(uri)?;
if domains.is_empty() {
println!("No domains found");
return Ok(());
}
// Print header
println!("{:<5} {:<30} {:<10}", "ID", "Name", "State");
println!("{:-<5} {:-<30} {:-<10}", "", "", "");
// Print domains
for domain in domains {
let id_str = match domain.id {
Some(id) => id.to_string(),
None => "-".to_string(),
};
println!("{:<5} {:<30} {:<10}", id_str, domain.name, domain.state);
}
Ok(())
}
let uri = uri.or(Some("qemu:///session"));
let conn = Connect::open(uri).context("Failed to connect to libvirt")?;
let mut domain_infos = Vec::new();
// Get active domains
let active_domains = conn
.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)
.context("Failed to list inactive domains")?;
// Process active domains
for domain in active_domains {
let name = domain.get_name().context("Failed to get domain name")?;
let id = domain.get_id();
// Get domain state
let state = match domain.get_state() {
Ok((state, _reason)) => match state {
sys::VIR_DOMAIN_RUNNING => DomainState::Running,
sys::VIR_DOMAIN_PAUSED => DomainState::Paused,
sys::VIR_DOMAIN_SHUTDOWN => DomainState::Shutdown,
sys::VIR_DOMAIN_SHUTOFF => DomainState::Shutoff,
sys::VIR_DOMAIN_CRASHED => DomainState::Crashed,
_ => DomainState::Unknown,
},
Err(_) => DomainState::Unknown,
};
domain_infos.push(DomainInfo { id, name, state });
}
// Process inactive domains
for domain in inactive_domains {
let name = domain.get_name().context("Failed to get domain name")?;
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)
}
/// Pretty print the list of domains
pub fn print_domain_list(uri: Option<&str>) -> Result<()> {
let domains = Self::list_domains(uri)?;
if domains.is_empty() {
println!("No domains found");
return Ok(());
}
// Print header
println!("{:<5} {:<30} {:<10}", "ID", "Name", "State");
println!("{:-<5} {:-<30} {:-<10}", "", "", "");
// Print domains
for domain in domains {
let id_str = match domain.id {
Some(id) => id.to_string(),
None => "-".to_string(),
};
println!("{:<5} {:<30} {:<10}", id_str, domain.name, domain.state);
}
Ok(())
}
}
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);
match cli.command {
Commands::Create {
name,
distro,
vcpus,
memory_mb,
disk_size_gb,
graphics,
dry_run
Commands::Create {
name,
distro,
vcpus,
memory_mb,
disk_size_gb,
graphics,
dry_run,
} => {
assert_eq!(name, "test-vm");
assert_eq!(distro, "centos8");
@ -31,7 +31,7 @@ fn test_cli_create_defaults() {
assert_eq!(memory_mb, 1024);
assert_eq!(graphics, false);
assert_eq!(dry_run, false);
},
}
_ => panic!("Expected Create command"),
}
}
@ -40,11 +40,16 @@ fn test_cli_create_defaults() {
fn test_cli_create_custom_values() {
let args = get_args(&[
"create",
"--name", "custom-vm",
"--distro", "ubuntu2004",
"--vcpus", "4",
"--memory-mb", "4096",
"--disk-size-gb", "50",
"--name",
"custom-vm",
"--distro",
"ubuntu2004",
"--vcpus",
"4",
"--memory-mb",
"4096",
"--disk-size-gb",
"50",
"--graphics",
"--dry-run",
]);
@ -52,14 +57,14 @@ fn test_cli_create_custom_values() {
let cli = Cli::parse_from(args);
match cli.command {
Commands::Create {
name,
distro,
vcpus,
memory_mb,
disk_size_gb,
graphics,
dry_run
Commands::Create {
name,
distro,
vcpus,
memory_mb,
disk_size_gb,
graphics,
dry_run,
} => {
assert_eq!(name, "custom-vm");
assert_eq!(distro, "ubuntu2004");
@ -68,7 +73,7 @@ fn test_cli_create_custom_values() {
assert_eq!(memory_mb, 4096);
assert_eq!(graphics, true);
assert_eq!(dry_run, true);
},
}
_ => panic!("Expected Create command"),
}
}
@ -82,7 +87,7 @@ fn test_cli_destroy_defaults() {
Commands::Destroy { name, remove_disk } => {
assert_eq!(name, "test-vm");
assert_eq!(remove_disk, false);
},
}
_ => panic!("Expected Destroy command"),
}
}
@ -96,7 +101,7 @@ fn test_cli_destroy_with_disk_removal() {
Commands::Destroy { name, remove_disk } => {
assert_eq!(name, "test-vm");
assert_eq!(remove_disk, true);
},
}
_ => panic!("Expected Destroy command"),
}
}
}

36
tests/list_vms.rs

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

85
tests/vm_tests.rs

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

Loading…
Cancel
Save