增加一些功能
This commit is contained in:
parent
4717a41c33
commit
958a90a615
@ -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"
|
||||
|
||||
289
src/main.rs
289
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<String>,
|
||||
os_info: String,
|
||||
uptime: u64,
|
||||
}
|
||||
|
||||
// 获取WMI连接
|
||||
fn get_wmi_connection() -> Result<WMIConnection> {
|
||||
let com_lib = COMLibrary::new()?;
|
||||
@ -269,10 +287,10 @@ async fn list_vm() -> Result<String> {
|
||||
};
|
||||
|
||||
// 获取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<String> {
|
||||
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<std::collections::HashMap<Str
|
||||
}
|
||||
}
|
||||
|
||||
async fn pause_vm(vmid: &str) -> Result<String> {
|
||||
// 使用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<String> {
|
||||
// 使用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<String> {
|
||||
// 使用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<std::collections::HashMap<String, String>>, req: actix_web::HttpRequest, config: web::Data<Config>) -> 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<std::collections::HashMap<String, String>>, req: actix_web::HttpRequest, config: web::Data<Config>) -> 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<std::collections::HashMap<String, String>>, req: actix_web::HttpRequest, config: web::Data<Config>) -> 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<String> {
|
||||
// 初始化系统信息
|
||||
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<Config>) -> 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
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user