Skip to content

Commit

Permalink
fix GPUDevice instance
Browse files Browse the repository at this point in the history
  • Loading branch information
virjilakrum committed Jan 29, 2025
1 parent 9bebbf9 commit dd04881
Show file tree
Hide file tree
Showing 3 changed files with 446 additions and 135 deletions.
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,19 @@ uuid = { version = "1.8.0", features = ["v4"] }
libvirt = "0.1.0"
governor = { version = "0.8.0", features = ["std", "nohashmap"] }

# Platform-specific dependencies
[target.'cfg(target_os = "linux")'.dependencies]
libvirt = "0.1.0"
nvml-wrapper = "0.12.0"
glob = "0.3"

[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.24.0"
metal = { version = "0.31.0", features = ["private"] }
metal = { version = "0.24", features = ["private"] }

[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["dxgi", "d3dcommon"] }
dxgi = "0.1.7"
dxgi = "0.4"

[lib]
name = "gpu_share_vm_manager"
Expand Down
308 changes: 241 additions & 67 deletions src/core/vm.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,87 @@
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
// use anyhow::Result;
use anyhow::{Result, Context};
use virt::domain::Domain;
use crate::utils::os::Platform;

// The configuration for our virtual machines
// Because every VM needs a good config, like every developer needs coffee! ☕
/// Virtual Machine Configuration
/// Platform-agnostic configuration with platform-specific optimizations
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct VMConfig {
pub name: String,
pub memory_kb: u64, // Memory in kilobytes (we're old school!)
pub vcpus: u32, // Virtual CPUs (the more the merrier!)
pub disk_path: PathBuf, // Where we store our VM's digital dreams
pub disk_size_gb: u64, // How much space for those dreams
pub memory_kb: u64,
pub vcpus: u32,
pub disk_path: PathBuf,
pub disk_size_gb: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub gpu_passthrough: Option<String>,
}

/// Virtual Machine Runtime State
#[derive(Debug, Serialize, Deserialize)]
pub struct VirtualMachine {
pub id: String,
pub name: String,
pub status: VMStatus,
pub resources: VMResources,
#[serde(skip_serializing_if = "Option::is_none")]
pub host_platform: Option<Platform>,
}

#[derive(Debug, Serialize, Deserialize)]
/// Virtual Machine Status
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum VMStatus {
Running, // Vrooooom! 🏎️
Stopped, // Taking a nap 😴
Failed, // Houston, we have a problem! 🚨
Creating, // Building the dream machine 🏗️
Deleting, // Time to say goodbye 👋
}

impl From<u32> for VMStatus {
fn from(state: u32) -> Self {
match state {
1 => VMStatus::Running,
5 => VMStatus::Stopped,
_ => VMStatus::Failed,
}
}
Running,
Stopped,
Paused,
Suspended,
Crashed,
Creating,
Migrating,
Deleting,
Unknown,
}

/// Virtual Machine Resource Utilization
#[derive(Debug, Serialize, Deserialize)]
pub struct VMResources {
pub cpu_cores: u32, // The brain power! 🧠
pub memory_mb: u64, // RAM - because we all need memories
pub gpu_attached: bool, // Got that gaming power? 🎮
pub cpu_usage: f32,
pub memory_usage: f32,
pub disk_usage: f32,
pub network_rx: u64,
pub network_tx: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub gpu_usage: Option<f32>,
}

/* Cross-platform VM configuration
Handles platform-specific virtualization settings
*/
impl VMConfig {
// Generate platform-optimized XML configuration
pub fn to_platform_xml(&self) -> Result<String> {
/// Create new VM configuration with platform defaults
pub fn new(name: &str, memory_gb: u64, vcpus: u32) -> Self {
let mut disk_path = PathBuf::new();

#[cfg(target_os = "linux")]
disk_path.push("/var/lib/libvirt/images");

#[cfg(target_os = "macos")]
disk_path.push("/Users/Shared/VirtualMachines");

#[cfg(target_os = "windows")]
disk_path.push("C:\\VirtualMachines");

disk_path.push(format!("{}.qcow2", name));

VMConfig {
name: name.to_string(),
memory_kb: memory_gb * 1024 * 1024,
vcpus,
disk_path,
disk_size_gb: 20, // Default size
gpu_passthrough: None,
}
}

/// Generate platform-optimized XML configuration
pub fn to_xml(&self) -> Result<String> {
let arch = match Platform::current() {
Platform::Linux | Platform::Windows => "x86_64",
Platform::MacOS => {
Expand All @@ -72,7 +101,9 @@ impl VMConfig {
_ => "pc-q35-6.2",
};

format!(
let devices = self.platform_specific_devices()?;

Ok(format!(
r#"
<domain type='kvm'>
<name>{}</name>
Expand All @@ -85,54 +116,197 @@ impl VMConfig {
{}
</domain>
"#,
self.name,
self.memory_kb,
self.vcpus,
arch,
machine_type,
self.platform_specific_devices()
)
self.name, self.memory_kb, self.vcpus, arch, machine_type, devices
))
}

/// Platform-specific device configuration
fn platform_specific_devices(&self) -> String {
fn platform_specific_devices(&self) -> Result<String> {
let mut devices = String::new();

// Common devices
devices.push_str(
r#"
<devices>
<console type='pty'/>
<channel type='unix'>
<target type='virtio' name='org.qemu.guest_agent.0'/>
</channel>
"#
);

// Platform-specific devices
match Platform::current() {
Platform::MacOS => {
// Apple Silicon T2 security device emulation
r#"
<devices>
devices.push_str(
r#"
<controller type='usb' model='qemu-xhci'/>
<input type='keyboard' bus='virtio'/>
<input type='mouse' bus='virtio'/>
<graphics type='cocoa'/>
</devices>
"#
"#
);
}
Platform::Windows => {
// Windows Hyper-V enlightenment features
r#"
<features>
<hyperv>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
</hyperv>
</features>
"#
devices.push_str(
r#"
<features>
<hyperv>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
</hyperv>
</features>
<video>
<model type='qxl' ram='65536' vram='65536'/>
</video>
"#
);
}
_ => {
// Standard QEMU devices for Linux
r#"
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='{}'/>
<target dev='vda' bus='virtio'/>
</disk>
</devices>
"#
devices.push_str(
r#"
<video>
<model type='virtio'/>
</video>
"#
);
}
}.to_string()
}

// GPU passthrough
if let Some(gpu_id) = &self.gpu_passthrough {
devices.push_str(&format!(
r#"
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='{}' slot='{}' function='0x0'/>
</source>
</hostdev>
"#,
&gpu_id[0..2], &gpu_id[2..4]
));
}

devices.push_str("</devices>");
Ok(devices)
}
}

impl VirtualMachine {
/// Create new VM instance from libvirt domain
pub fn from_domain(domain: &Domain) -> Result<Self> {
let info = domain.get_info().context("Failed to get domain info")?;

Ok(Self {
id: domain.get_uuid_string().context("Failed to get UUID")?,
name: domain.get_name().context("Failed to get name")?,
status: VMStatus::from(info.state),
resources: VMResources::default(),
host_platform: Some(Platform::current()),
})
}

/// Start VM
pub fn start(&self) -> Result<()> {
// Implementation varies by platform
#[cfg(target_os = "linux")]
self.start_linux()?;

#[cfg(target_os = "macos")]
self.start_macos()?;

#[cfg(target_os = "windows")]
self.start_windows()?;

Ok(())
}

#[cfg(target_os = "linux")]
fn start_linux(&self) -> Result<()> {
// Use virsh commands or libvirt API
Ok(())
}

#[cfg(target_os = "macos")]
fn start_macos(&self) -> Result<()> {
// Use hyperkit or native hypervisor framework
Ok(())
}

#[cfg(target_os = "windows")]
fn start_windows(&self) -> Result<()> {
// Use Hyper-V manager
Ok(())
}

/// Get memory statistics
pub fn memory_stats(&self) -> Result<Vec<u64>> {
// Implementation varies by platform
Ok(vec![
self.resources.memory_usage as u64,
self.resources.memory_usage as u64 * 1024,
])
}

/// Get vCPU statistics
pub fn vcpu_stats(&self) -> Result<Vec<u64>> {
Ok(vec![
self.resources.cpu_usage as u64,
self.vcpus as u64,
])
}
}

impl Default for VMResources {
fn default() -> Self {
Self {
cpu_usage: 0.0,
memory_usage: 0.0,
disk_usage: 0.0,
network_rx: 0,
network_tx: 0,
gpu_usage: None,
}
}
}

impl From<u32> for VMStatus {
fn from(state: u32) -> Self {
match state {
1 => VMStatus::Running,
2 => VMStatus::Stopped,
3 => VMStatus::Paused,
4 => VMStatus::Suspended,
5 => VMStatus::Crashed,
_ => VMStatus::Unknown,
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_vm_config_creation() {
let config = VMConfig::new("test-vm", 4, 2);
assert_eq!(config.memory_kb, 4 * 1024 * 1024);
assert!(config.disk_path.to_string_lossy().contains("test-vm"));
}

#[test]
fn test_vm_status_conversion() {
assert_eq!(VMStatus::from(1), VMStatus::Running);
assert_eq!(VMStatus::from(5), VMStatus::Crashed);
assert_eq!(VMStatus::from(99), VMStatus::Unknown);
}

#[test]
fn test_xml_generation() {
let config = VMConfig::new("test-xml", 2, 1);
let xml = config.to_xml().unwrap();
assert!(xml.contains("test-xml"));
assert!(xml.contains("KiB"));
}
}
Loading

0 comments on commit dd04881

Please sign in to comment.