Skip to content

Commit dd04881

Browse files
committed
fix GPUDevice instance
1 parent 9bebbf9 commit dd04881

File tree

3 files changed

+446
-135
lines changed

3 files changed

+446
-135
lines changed

Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,19 @@ uuid = { version = "1.8.0", features = ["v4"] }
2525
libvirt = "0.1.0"
2626
governor = { version = "0.8.0", features = ["std", "nohashmap"] }
2727

28+
# Platform-specific dependencies
2829
[target.'cfg(target_os = "linux")'.dependencies]
2930
libvirt = "0.1.0"
31+
nvml-wrapper = "0.12.0"
32+
glob = "0.3"
3033

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

3538
[target.'cfg(target_os = "windows")'.dependencies]
3639
winapi = { version = "0.3", features = ["dxgi", "d3dcommon"] }
37-
dxgi = "0.1.7"
40+
dxgi = "0.4"
3841

3942
[lib]
4043
name = "gpu_share_vm_manager"

src/core/vm.rs

Lines changed: 241 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,87 @@
11
use serde::{Deserialize, Serialize};
22
use std::path::PathBuf;
3-
// use anyhow::Result;
3+
use anyhow::{Result, Context};
4+
use virt::domain::Domain;
5+
use crate::utils::os::Platform;
46

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

20+
/// Virtual Machine Runtime State
1621
#[derive(Debug, Serialize, Deserialize)]
1722
pub struct VirtualMachine {
1823
pub id: String,
1924
pub name: String,
2025
pub status: VMStatus,
2126
pub resources: VMResources,
27+
#[serde(skip_serializing_if = "Option::is_none")]
28+
pub host_platform: Option<Platform>,
2229
}
2330

24-
#[derive(Debug, Serialize, Deserialize)]
31+
/// Virtual Machine Status
32+
#[derive(Debug, Serialize, Deserialize, PartialEq)]
2533
pub enum VMStatus {
26-
Running, // Vrooooom! 🏎️
27-
Stopped, // Taking a nap 😴
28-
Failed, // Houston, we have a problem! 🚨
29-
Creating, // Building the dream machine 🏗️
30-
Deleting, // Time to say goodbye 👋
31-
}
32-
33-
impl From<u32> for VMStatus {
34-
fn from(state: u32) -> Self {
35-
match state {
36-
1 => VMStatus::Running,
37-
5 => VMStatus::Stopped,
38-
_ => VMStatus::Failed,
39-
}
40-
}
34+
Running,
35+
Stopped,
36+
Paused,
37+
Suspended,
38+
Crashed,
39+
Creating,
40+
Migrating,
41+
Deleting,
42+
Unknown,
4143
}
4244

45+
/// Virtual Machine Resource Utilization
4346
#[derive(Debug, Serialize, Deserialize)]
4447
pub struct VMResources {
45-
pub cpu_cores: u32, // The brain power! 🧠
46-
pub memory_mb: u64, // RAM - because we all need memories
47-
pub gpu_attached: bool, // Got that gaming power? 🎮
48+
pub cpu_usage: f32,
49+
pub memory_usage: f32,
50+
pub disk_usage: f32,
51+
pub network_rx: u64,
52+
pub network_tx: u64,
53+
#[serde(skip_serializing_if = "Option::is_none")]
54+
pub gpu_usage: Option<f32>,
4855
}
4956

50-
/* Cross-platform VM configuration
51-
Handles platform-specific virtualization settings
52-
*/
5357
impl VMConfig {
54-
// Generate platform-optimized XML configuration
55-
pub fn to_platform_xml(&self) -> Result<String> {
58+
/// Create new VM configuration with platform defaults
59+
pub fn new(name: &str, memory_gb: u64, vcpus: u32) -> Self {
60+
let mut disk_path = PathBuf::new();
61+
62+
#[cfg(target_os = "linux")]
63+
disk_path.push("/var/lib/libvirt/images");
64+
65+
#[cfg(target_os = "macos")]
66+
disk_path.push("/Users/Shared/VirtualMachines");
67+
68+
#[cfg(target_os = "windows")]
69+
disk_path.push("C:\\VirtualMachines");
70+
71+
disk_path.push(format!("{}.qcow2", name));
72+
73+
VMConfig {
74+
name: name.to_string(),
75+
memory_kb: memory_gb * 1024 * 1024,
76+
vcpus,
77+
disk_path,
78+
disk_size_gb: 20, // Default size
79+
gpu_passthrough: None,
80+
}
81+
}
82+
83+
/// Generate platform-optimized XML configuration
84+
pub fn to_xml(&self) -> Result<String> {
5685
let arch = match Platform::current() {
5786
Platform::Linux | Platform::Windows => "x86_64",
5887
Platform::MacOS => {
@@ -72,7 +101,9 @@ impl VMConfig {
72101
_ => "pc-q35-6.2",
73102
};
74103

75-
format!(
104+
let devices = self.platform_specific_devices()?;
105+
106+
Ok(format!(
76107
r#"
77108
<domain type='kvm'>
78109
<name>{}</name>
@@ -85,54 +116,197 @@ impl VMConfig {
85116
{}
86117
</domain>
87118
"#,
88-
self.name,
89-
self.memory_kb,
90-
self.vcpus,
91-
arch,
92-
machine_type,
93-
self.platform_specific_devices()
94-
)
119+
self.name, self.memory_kb, self.vcpus, arch, machine_type, devices
120+
))
95121
}
96122

97123
/// Platform-specific device configuration
98-
fn platform_specific_devices(&self) -> String {
124+
fn platform_specific_devices(&self) -> Result<String> {
125+
let mut devices = String::new();
126+
127+
// Common devices
128+
devices.push_str(
129+
r#"
130+
<devices>
131+
<console type='pty'/>
132+
<channel type='unix'>
133+
<target type='virtio' name='org.qemu.guest_agent.0'/>
134+
</channel>
135+
"#
136+
);
137+
138+
// Platform-specific devices
99139
match Platform::current() {
100140
Platform::MacOS => {
101-
// Apple Silicon T2 security device emulation
102-
r#"
103-
<devices>
141+
devices.push_str(
142+
r#"
104143
<controller type='usb' model='qemu-xhci'/>
105144
<input type='keyboard' bus='virtio'/>
106145
<input type='mouse' bus='virtio'/>
107146
<graphics type='cocoa'/>
108-
</devices>
109-
"#
147+
"#
148+
);
110149
}
111150
Platform::Windows => {
112-
// Windows Hyper-V enlightenment features
113-
r#"
114-
<features>
115-
<hyperv>
116-
<relaxed state='on'/>
117-
<vapic state='on'/>
118-
<spinlocks state='on' retries='8191'/>
119-
</hyperv>
120-
</features>
121-
"#
151+
devices.push_str(
152+
r#"
153+
<features>
154+
<hyperv>
155+
<relaxed state='on'/>
156+
<vapic state='on'/>
157+
<spinlocks state='on' retries='8191'/>
158+
</hyperv>
159+
</features>
160+
<video>
161+
<model type='qxl' ram='65536' vram='65536'/>
162+
</video>
163+
"#
164+
);
122165
}
123166
_ => {
124-
// Standard QEMU devices for Linux
125-
r#"
126-
<devices>
127-
<emulator>/usr/bin/qemu-system-x86_64</emulator>
128-
<disk type='file' device='disk'>
129-
<driver name='qemu' type='qcow2'/>
130-
<source file='{}'/>
131-
<target dev='vda' bus='virtio'/>
132-
</disk>
133-
</devices>
134-
"#
167+
devices.push_str(
168+
r#"
169+
<video>
170+
<model type='virtio'/>
171+
</video>
172+
"#
173+
);
135174
}
136-
}.to_string()
175+
}
176+
177+
// GPU passthrough
178+
if let Some(gpu_id) = &self.gpu_passthrough {
179+
devices.push_str(&format!(
180+
r#"
181+
<hostdev mode='subsystem' type='pci' managed='yes'>
182+
<source>
183+
<address domain='0x0000' bus='{}' slot='{}' function='0x0'/>
184+
</source>
185+
</hostdev>
186+
"#,
187+
&gpu_id[0..2], &gpu_id[2..4]
188+
));
189+
}
190+
191+
devices.push_str("</devices>");
192+
Ok(devices)
193+
}
194+
}
195+
196+
impl VirtualMachine {
197+
/// Create new VM instance from libvirt domain
198+
pub fn from_domain(domain: &Domain) -> Result<Self> {
199+
let info = domain.get_info().context("Failed to get domain info")?;
200+
201+
Ok(Self {
202+
id: domain.get_uuid_string().context("Failed to get UUID")?,
203+
name: domain.get_name().context("Failed to get name")?,
204+
status: VMStatus::from(info.state),
205+
resources: VMResources::default(),
206+
host_platform: Some(Platform::current()),
207+
})
208+
}
209+
210+
/// Start VM
211+
pub fn start(&self) -> Result<()> {
212+
// Implementation varies by platform
213+
#[cfg(target_os = "linux")]
214+
self.start_linux()?;
215+
216+
#[cfg(target_os = "macos")]
217+
self.start_macos()?;
218+
219+
#[cfg(target_os = "windows")]
220+
self.start_windows()?;
221+
222+
Ok(())
223+
}
224+
225+
#[cfg(target_os = "linux")]
226+
fn start_linux(&self) -> Result<()> {
227+
// Use virsh commands or libvirt API
228+
Ok(())
229+
}
230+
231+
#[cfg(target_os = "macos")]
232+
fn start_macos(&self) -> Result<()> {
233+
// Use hyperkit or native hypervisor framework
234+
Ok(())
235+
}
236+
237+
#[cfg(target_os = "windows")]
238+
fn start_windows(&self) -> Result<()> {
239+
// Use Hyper-V manager
240+
Ok(())
241+
}
242+
243+
/// Get memory statistics
244+
pub fn memory_stats(&self) -> Result<Vec<u64>> {
245+
// Implementation varies by platform
246+
Ok(vec![
247+
self.resources.memory_usage as u64,
248+
self.resources.memory_usage as u64 * 1024,
249+
])
250+
}
251+
252+
/// Get vCPU statistics
253+
pub fn vcpu_stats(&self) -> Result<Vec<u64>> {
254+
Ok(vec![
255+
self.resources.cpu_usage as u64,
256+
self.vcpus as u64,
257+
])
258+
}
259+
}
260+
261+
impl Default for VMResources {
262+
fn default() -> Self {
263+
Self {
264+
cpu_usage: 0.0,
265+
memory_usage: 0.0,
266+
disk_usage: 0.0,
267+
network_rx: 0,
268+
network_tx: 0,
269+
gpu_usage: None,
270+
}
271+
}
272+
}
273+
274+
impl From<u32> for VMStatus {
275+
fn from(state: u32) -> Self {
276+
match state {
277+
1 => VMStatus::Running,
278+
2 => VMStatus::Stopped,
279+
3 => VMStatus::Paused,
280+
4 => VMStatus::Suspended,
281+
5 => VMStatus::Crashed,
282+
_ => VMStatus::Unknown,
283+
}
284+
}
285+
}
286+
287+
#[cfg(test)]
288+
mod tests {
289+
use super::*;
290+
291+
#[test]
292+
fn test_vm_config_creation() {
293+
let config = VMConfig::new("test-vm", 4, 2);
294+
assert_eq!(config.memory_kb, 4 * 1024 * 1024);
295+
assert!(config.disk_path.to_string_lossy().contains("test-vm"));
296+
}
297+
298+
#[test]
299+
fn test_vm_status_conversion() {
300+
assert_eq!(VMStatus::from(1), VMStatus::Running);
301+
assert_eq!(VMStatus::from(5), VMStatus::Crashed);
302+
assert_eq!(VMStatus::from(99), VMStatus::Unknown);
303+
}
304+
305+
#[test]
306+
fn test_xml_generation() {
307+
let config = VMConfig::new("test-xml", 2, 1);
308+
let xml = config.to_xml().unwrap();
309+
assert!(xml.contains("test-xml"));
310+
assert!(xml.contains("KiB"));
137311
}
138312
}

0 commit comments

Comments
 (0)