From 958a90a615682f376dfb9a85c613ac1f901adfa9 Mon Sep 17 00:00:00 2001 From: jiangcuo Date: Wed, 16 Apr 2025 03:28:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=BA=9B=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 3 + src/main.rs | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 286 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4c0a10e..6c55ea9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,6 @@ wmi = "0.15.2" regex = "1.11.1" chrono = "0.4.40" netuser-rs = { git = "https://github.com/secur30nly/netuser-rs.git"} +sysinfo = "0.29.10" +hostname = "0.3.1" +local-ip-address = "0.5.6" diff --git a/src/main.rs b/src/main.rs index 66675ca..f43d81d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,10 @@ use wmi::{COMLibrary, WMIConnection, Variant, FilterValue}; use std::collections::HashMap; use regex; use netuser_rs; +use sysinfo::{System, SystemExt, CpuExt, NetworkExt}; +use std::net::{IpAddr, Ipv4Addr}; +use hostname; +use local_ip_address::{local_ip, local_ipv6, list_afinet_netifas}; fn is_admin() -> bool { unsafe { @@ -136,8 +140,8 @@ struct VirtualMachine { name: String, state: String, status: String, - cpu_cores: u32, - memory_mb: u64, + cores: u32, + memory: u64, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -165,6 +169,20 @@ struct Snapshot { creation_time: String, } +#[derive(Debug, Serialize, Deserialize, Clone)] +struct HostInfo { + hostname: String, + cpu_model: String, + cpu_cores: usize, + cpu_usage: f32, + memory_total: f64, + memory_used: f64, + memory_usage: f32, + ip_addresses: Vec, + os_info: String, + uptime: u64, +} + // 获取WMI连接 fn get_wmi_connection() -> Result { let com_lib = COMLibrary::new()?; @@ -269,10 +287,10 @@ async fn list_vm() -> Result { }; // 获取CPU核心数 - let cpu_cores = get_vm_cpu_cores(&wmi_con, &vm_id)?; + let cores = get_vm_cpu_cores(&wmi_con, &vm_id)?; // 获取内存大小 - let memory_mb = get_vm_memory(&wmi_con, &vm_id)?; + let memory = get_vm_memory(&wmi_con, &vm_id)?; // 创建VM对象 vms.push(VirtualMachine { @@ -280,8 +298,8 @@ async fn list_vm() -> Result { name: vm.get("ElementName").and_then(variant_to_string).unwrap_or_default(), state, status: vm.get("Status").and_then(variant_to_string).unwrap_or_default(), - cpu_cores, - memory_mb, + cores, + memory, }); } @@ -1248,6 +1266,259 @@ async fn delete_snapshot_handler(query: web::Query Result { + // 使用PowerShell的Suspend-VM命令暂停虚拟机 + let output = Command::new("powershell.exe") + .args(["-Command", &format!("Get-VM -Id '{}' | Suspend-VM -Confirm:$false", vmid)]) + .output()?; + + if output.status.success() { + Ok("已暂停".to_string()) + } else { + let error = String::from_utf8_lossy(&output.stderr); + Ok(format!("暂停失败: {}", error)) + } +} + +async fn resume_vm(vmid: &str) -> Result { + // 使用PowerShell的Resume-VM命令恢复虚拟机 + let output = Command::new("powershell.exe") + .args(["-Command", &format!("Get-VM -Id '{}' | Resume-VM", vmid)]) + .output()?; + + if output.status.success() { + Ok("已恢复".to_string()) + } else { + let error = String::from_utf8_lossy(&output.stderr); + Ok(format!("恢复失败: {}", error)) + } +} + +async fn reset_vm(vmid: &str) -> Result { + // 使用PowerShell的Restart-VM命令重启虚拟机 + let output = Command::new("powershell.exe") + .args(["-Command", &format!("Get-VM -Id '{}' | Restart-VM -Force -Confirm:$false", vmid)]) + .output()?; + + if output.status.success() { + Ok("已重启".to_string()) + } else { + let error = String::from_utf8_lossy(&output.stderr); + Ok(format!("重启失败: {}", error)) + } +} + + +async fn pause_vm_handler(query: web::Query>, req: actix_web::HttpRequest, config: web::Data) -> impl Responder { + let key = req.headers().get("Key").and_then(|v| v.to_str().ok()); + if key != Some(&config.key) { + return HttpResponse::Unauthorized().json(serde_json::json!({ "error": "Key error" })); + } + + let vmid = query.get("VMID"); + match vmid { + Some(id) => { + if let Ok(true) = check_vmid(id).await { + match pause_vm(id).await { + Ok(result) => HttpResponse::Ok().body(result), + Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({ + "error": format!("Failed to pause VM: {}", e) + })), + } + } else { + HttpResponse::BadRequest().json(serde_json::json!({ "error": "Invalid VMID" })) + } + }, + None => HttpResponse::BadRequest().json(serde_json::json!({ "error": "Need VMID" })), + } +} + +async fn resume_vm_handler(query: web::Query>, req: actix_web::HttpRequest, config: web::Data) -> impl Responder { + let key = req.headers().get("Key").and_then(|v| v.to_str().ok()); + if key != Some(&config.key) { + return HttpResponse::Unauthorized().json(serde_json::json!({ "error": "Key error" })); + } + + let vmid = query.get("VMID"); + match vmid { + Some(id) => { + if let Ok(true) = check_vmid(id).await { + match resume_vm(id).await { + Ok(result) => HttpResponse::Ok().body(result), + Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({ + "error": format!("Failed to resume VM: {}", e) + })), + } + } else { + HttpResponse::BadRequest().json(serde_json::json!({ "error": "Invalid VMID" })) + } + }, + None => HttpResponse::BadRequest().json(serde_json::json!({ "error": "Need VMID" })), + } +} + + +async fn reset_vm_handler(query: web::Query>, req: actix_web::HttpRequest, config: web::Data) -> impl Responder { + let key = req.headers().get("Key").and_then(|v| v.to_str().ok()); + if key != Some(&config.key) { + return HttpResponse::Unauthorized().json(serde_json::json!({ "error": "Key error" })); + } + + let vmid = query.get("VMID"); + match vmid { + Some(id) => { + if let Ok(true) = check_vmid(id).await { + match reset_vm(id).await { + Ok(result) => HttpResponse::Ok().body(result), + Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({ + "error": format!("Failed to reset VM: {}", e) + })), + } + } else { + HttpResponse::BadRequest().json(serde_json::json!({ "error": "Invalid VMID" })) + } + }, + None => HttpResponse::BadRequest().json(serde_json::json!({ "error": "Need VMID" })), + } +} + +async fn get_host_info() -> Result { + // 初始化系统信息 + let mut sys = System::new_all(); + sys.refresh_all(); + + // 获取主机名 + let hostname = match hostname::get() { + Ok(name) => name.to_string_lossy().to_string(), + Err(_) => "未知主机名".to_string(), + }; + + // 获取CPU信息 + let cpu_model = if !sys.cpus().is_empty() { + sys.cpus()[0].brand().to_string() + } else { + "未知CPU型号".to_string() + }; + let cpu_cores = sys.cpus().len(); + + // 计算CPU平均使用率并取整到两位小数 + let cpu_usage = if !sys.cpus().is_empty() { + let mut total = 0.0; + for cpu in sys.cpus() { + total += cpu.cpu_usage(); + } + let avg = total / (cpu_cores as f32); + (avg * 100.0).round() / 100.0 // 保留两位小数 + } else { + 0.0 + }; + + // 获取内存信息并转换为GB (sysinfo返回的是KB) + let memory_total_kb = sys.total_memory(); + let memory_used_kb = sys.used_memory(); + + // 直接转换为MB以后GB,确保使用浮点数除法以保留精度 + let memory_total_gb = (memory_total_kb as f64) / (1024.0 * 1024.0 * 1024.0); + let memory_used_gb = (memory_used_kb as f64) / (1024.0 * 1024.0 * 1024.0); + + // 四舍五入到两位小数 + let memory_total_gb = (memory_total_gb * 100.0).round() / 100.0; + let memory_used_gb = (memory_used_gb * 100.0).round() / 100.0; + + // 计算内存使用率并取整到两位小数 + let memory_usage = if memory_total_kb > 0 { + let usage = (memory_used_kb as f32 / memory_total_kb as f32) * 100.0; + (usage * 100.0).round() / 100.0 // 保留两位小数 + } else { + 0.0 + }; + + // 获取IP地址列表 + let mut ip_addresses = Vec::new(); + + // 使用local_ip_address库获取本地IP地址 + match local_ip() { + Ok(ip) => { + ip_addresses.push(format!("主IP地址:{}", ip)); + }, + Err(e) => { + ip_addresses.push(format!("无法获取IPv4地址: {}", e)); + } + } + + // 使用local_ip_address库获取本地IPv6地址 + match local_ipv6() { + Ok(ip) => { + ip_addresses.push(format!("主IPv6地址:{}", ip)); + }, + Err(_) => { + // IPv6可能不可用,忽略错误 + } + } + + // 获取所有网络接口和IP地址 + match list_afinet_netifas() { + Ok(if_list) => { + for (iface, ip) in if_list { + // 跳过回环接口 + if iface.to_lowercase().contains("loopback") { + continue; + } + ip_addresses.push(format!("{}:{}", iface, ip)); + } + }, + Err(e) => { + ip_addresses.push(format!("无法获取网络接口列表: {}", e)); + } + } + + // 使用sysinfo获取网络接口流量信息 + for (interface_name, network) in sys.networks() { + // 跳过回环接口 + if interface_name == "lo" || interface_name.contains("loopback") { + continue; + } + } + + // 获取操作系统信息 + let os_info = format!("{} {}", sys.name().unwrap_or_default(), sys.os_version().unwrap_or_default()); + + // 获取系统运行时间 + let uptime = sys.uptime(); + + // 构建主机信息结构 + let host_info = HostInfo { + hostname, + cpu_model, + cpu_cores, + cpu_usage, + memory_total: memory_total_gb, + memory_used: memory_used_gb, + memory_usage, + ip_addresses, + os_info, + uptime, + }; + + // 转换为JSON + let json = serde_json::to_string(&host_info)?; + Ok(json) +} + +async fn get_host_info_handler(req: actix_web::HttpRequest, config: web::Data) -> impl Responder { + let key = req.headers().get("Key").and_then(|v| v.to_str().ok()); + if key != Some(&config.key) { + return HttpResponse::Unauthorized().json(serde_json::json!({ "error": "Key error" })); + } + + match get_host_info().await { + Ok(result) => HttpResponse::Ok().body(result), + Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({ + "error": format!("Failed to get host info: {}", e) + })), + } +} + #[actix_web::main] async fn main() -> std::io::Result<()> { // 检查管理员权限 @@ -1292,8 +1563,12 @@ async fn main() -> std::io::Result<()> { .route("/api2/DeleteSnapShot", web::post().to(delete_snapshot_handler)) .route("/api2/StopVM", web::post().to(stop_vm_handler)) .route("/api2/StartVM", web::post().to(start_vm_handler)) + .route("/api2/PauseVM", web::post().to(pause_vm_handler)) + .route("/api2/ResumeVM", web::post().to(resume_vm_handler)) + .route("/api2/ResetVM", web::post().to(reset_vm_handler)) .route("/api2/ListVM", web::get().to(list_vm_handler)) .route("/api2/GetTicket", web::post().to(get_vm_ticket_handler)) + .route("/api2/GetHostInfo", web::get().to(get_host_info_handler)) }); let server = if tls_enabled { @@ -1323,3 +1598,5 @@ async fn main() -> std::io::Result<()> { server.run().await } + + \ No newline at end of file