update
This commit is contained in:
parent
7d1310a002
commit
4717a41c33
344
src/main.rs
344
src/main.rs
@ -415,7 +415,7 @@ async fn get_vm_network_info(vm_id: Option<&str>) -> Result<String> {
|
||||
Ok(configs) => {
|
||||
configs
|
||||
},
|
||||
Err(e) => {
|
||||
Err(_e) => {
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
@ -674,38 +674,184 @@ async fn get_all() -> Result<String> {
|
||||
Ok(json)
|
||||
}
|
||||
|
||||
// 从实例ID中提取UUID
|
||||
fn extract_uuid_from_instance_id(instance_id: &str) -> String {
|
||||
let re_str = r"\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}";
|
||||
let re = regex::Regex::new(re_str).unwrap();
|
||||
|
||||
if let Some(captures) = re.captures(instance_id) {
|
||||
if let Some(m) = captures.get(0) {
|
||||
return m.as_str().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
async fn get_snapshot(vmid: Option<&str>) -> Result<String> {
|
||||
let wmi_con = get_wmi_connection()?;
|
||||
|
||||
let mut snapshots = Vec::new();
|
||||
let wql = if let Some(id) = vmid {
|
||||
format!("SELECT * FROM Msvm_VirtualSystemSettingData WHERE VirtualSystemType='Microsoft:Hyper-V:Snapshot:Realized' AND InstanceID LIKE '%{}%'", id)
|
||||
|
||||
let powershell_cmd = if let Some(id) = vmid {
|
||||
// 针对特定VM获取快照
|
||||
format!(
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; \
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8; \
|
||||
Get-VM -Id '{}' | Get-VMSnapshot | ForEach-Object {{ \
|
||||
$snap = $_; \
|
||||
[PSCustomObject]@{{ \
|
||||
Name = $snap.Name; \
|
||||
CreationTime = $snap.CreationTime.ToString('yyyy-MM-dd HH:mm:ss'); \
|
||||
}} \
|
||||
}} | ConvertTo-Json -Depth 1",
|
||||
id
|
||||
)
|
||||
} else {
|
||||
"SELECT * FROM Msvm_VirtualSystemSettingData WHERE VirtualSystemType='Microsoft:Hyper-V:Snapshot:Realized'".to_string()
|
||||
// 获取所有VM的快照
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; \
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8; \
|
||||
Get-VM | Get-VMSnapshot | ForEach-Object { \
|
||||
$snap = $_; \
|
||||
[PSCustomObject]@{ \
|
||||
VMName = $snap.VMName; \
|
||||
Name = $snap.Name; \
|
||||
CreationTime = $snap.CreationTime.ToString('yyyy-MM-dd HH:mm:ss'); \
|
||||
} \
|
||||
} | ConvertTo-Json -Depth 1".to_string()
|
||||
};
|
||||
|
||||
let snapshot_results: Vec<HashMap<String, Variant>> = wmi_con.raw_query(&wql)?;
|
||||
// 执行PowerShell命令
|
||||
let output = Command::new("powershell.exe")
|
||||
.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &powershell_cmd])
|
||||
.output()?;
|
||||
|
||||
for snapshot in snapshot_results {
|
||||
// 提取快照信息
|
||||
snapshots.push(Snapshot {
|
||||
name: snapshot.get("ElementName").and_then(variant_to_string).unwrap_or_default(),
|
||||
creation_time: snapshot.get("CreationTime").and_then(variant_to_string).unwrap_or_default(),
|
||||
});
|
||||
if output.status.success() {
|
||||
// 确保使用UTF-8解码输出
|
||||
let stdout = match String::from_utf8(output.stdout.clone()) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
// 如果UTF-8解码失败,尝试使用系统默认编码
|
||||
String::from_utf8_lossy(&output.stdout).to_string()
|
||||
}
|
||||
};
|
||||
|
||||
// 检查输出是否为空
|
||||
if stdout.trim().is_empty() || stdout.trim() == "null" {
|
||||
return Ok("[]".to_string());
|
||||
}
|
||||
|
||||
// 尝试解析JSON输出
|
||||
if let Ok(json_value) = serde_json::from_str::<serde_json::Value>(&stdout) {
|
||||
// 处理单个对象或数组
|
||||
if json_value.is_array() {
|
||||
// 处理数组结果
|
||||
if let Some(snapshots_array) = json_value.as_array() {
|
||||
for snapshot in snapshots_array {
|
||||
// 提取名称和创建时间
|
||||
let name = snapshot["Name"].as_str().unwrap_or_default().to_string();
|
||||
let creation_time = snapshot["CreationTime"].as_str().unwrap_or_default().to_string();
|
||||
|
||||
if !name.is_empty() {
|
||||
snapshots.push(Snapshot {
|
||||
name,
|
||||
creation_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if json_value.is_object() {
|
||||
// 处理单个结果
|
||||
let name = json_value["Name"].as_str().unwrap_or_default().to_string();
|
||||
let creation_time = json_value["CreationTime"].as_str().unwrap_or_default().to_string();
|
||||
|
||||
if !name.is_empty() {
|
||||
snapshots.push(Snapshot {
|
||||
name,
|
||||
creation_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// 尝试替换可能引起问题的字符并重新解析
|
||||
let cleaned_output = stdout
|
||||
.replace('\u{FEFF}', "") // 删除BOM标记
|
||||
.replace('\u{FFFD}', "") // 删除UTF-8替换字符
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
if !cleaned_output.is_empty() && cleaned_output != "null" {
|
||||
if let Ok(json_value) = serde_json::from_str::<serde_json::Value>(&cleaned_output) {
|
||||
// 处理JSON
|
||||
// ... 类似上面的处理逻辑
|
||||
if json_value.is_array() {
|
||||
if let Some(snapshots_array) = json_value.as_array() {
|
||||
for snapshot in snapshots_array {
|
||||
let name = snapshot["Name"].as_str().unwrap_or_default().to_string();
|
||||
let creation_time = snapshot["CreationTime"].as_str().unwrap_or_default().to_string();
|
||||
|
||||
if !name.is_empty() {
|
||||
snapshots.push(Snapshot {
|
||||
name,
|
||||
creation_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if json_value.is_object() {
|
||||
let name = json_value["Name"].as_str().unwrap_or_default().to_string();
|
||||
let creation_time = json_value["CreationTime"].as_str().unwrap_or_default().to_string();
|
||||
|
||||
if !name.is_empty() {
|
||||
snapshots.push(Snapshot {
|
||||
name,
|
||||
creation_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
// 如果高级命令失败,尝试使用更基本的命令
|
||||
let basic_cmd = if let Some(id) = vmid {
|
||||
format!(
|
||||
"$OutputEncoding = [System.Text.Encoding]::UTF8; \
|
||||
Get-VM -Id '{}' | Get-VMSnapshot | Select-Object -Property Name, CreationTime | ConvertTo-Json",
|
||||
id
|
||||
)
|
||||
} else {
|
||||
"$OutputEncoding = [System.Text.Encoding]::UTF8; \
|
||||
Get-VM | Get-VMSnapshot | Select-Object -Property VMName, Name, CreationTime | ConvertTo-Json".to_string()
|
||||
};
|
||||
|
||||
let basic_output = Command::new("powershell.exe")
|
||||
.args(["-Command", &basic_cmd])
|
||||
.output()?;
|
||||
|
||||
if basic_output.status.success() {
|
||||
let basic_stdout = String::from_utf8_lossy(&basic_output.stdout).to_string();
|
||||
|
||||
if !basic_stdout.trim().is_empty() && basic_stdout.trim() != "null" {
|
||||
// 尝试解析基本命令的JSON输出
|
||||
if let Ok(json_value) = serde_json::from_str::<serde_json::Value>(&basic_stdout) {
|
||||
if json_value.is_array() {
|
||||
if let Some(snapshots_array) = json_value.as_array() {
|
||||
for snapshot in snapshots_array {
|
||||
let name = snapshot["Name"].as_str().unwrap_or_default().to_string();
|
||||
let creation_time = snapshot["CreationTime"].as_str().unwrap_or_default().to_string();
|
||||
|
||||
if !name.is_empty() {
|
||||
snapshots.push(Snapshot {
|
||||
name,
|
||||
creation_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if json_value.is_object() {
|
||||
let name = json_value["Name"].as_str().unwrap_or_default().to_string();
|
||||
let creation_time = json_value["CreationTime"].as_str().unwrap_or_default().to_string();
|
||||
|
||||
if !name.is_empty() {
|
||||
snapshots.push(Snapshot {
|
||||
name,
|
||||
creation_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let basic_stderr = String::from_utf8_lossy(&basic_output.stderr);
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为JSON
|
||||
@ -810,6 +956,51 @@ async fn get_snapshot_handler(query: web::Query<std::collections::HashMap<String
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_snapshot(vmid: &str, snapshot_name: &str) -> Result<String> {
|
||||
// 使用PowerShell创建快照
|
||||
let output = Command::new("powershell.exe")
|
||||
.args([
|
||||
"-Command",
|
||||
&format!("Get-VM -Id '{}' | Checkpoint-VM -SnapshotName '{}' -ErrorAction Stop", vmid, snapshot_name)
|
||||
])
|
||||
.output()?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok("快照创建成功".to_string())
|
||||
} else {
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
Ok(format!("快照创建失败: {}", error))
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_snapshot_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");
|
||||
// 使用一个固定的String变量来存储默认快照名称
|
||||
let default_snapshot_name = "Snapshot".to_string();
|
||||
let snapshot_name = query.get("Name").unwrap_or(&default_snapshot_name);
|
||||
|
||||
match vmid {
|
||||
Some(id) => {
|
||||
if let Ok(true) = check_vmid(id).await {
|
||||
match create_snapshot(id, snapshot_name).await {
|
||||
Ok(result) => HttpResponse::Ok().body(result),
|
||||
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": format!("Failed to create snapshot: {}", e)
|
||||
})),
|
||||
}
|
||||
} else {
|
||||
HttpResponse::BadRequest().json(serde_json::json!({ "error": "Invalid VMID" }))
|
||||
}
|
||||
},
|
||||
None => HttpResponse::BadRequest().json(serde_json::json!({ "error": "Need VMID" })),
|
||||
}
|
||||
}
|
||||
|
||||
async fn stop_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) {
|
||||
@ -961,6 +1152,102 @@ async fn generate_vm_ticket(_vmid: &str) -> Result<(String, String)> {
|
||||
Ok((username, password))
|
||||
}
|
||||
|
||||
async fn restore_snapshot(vmid: &str, snapshot_name: &str) -> Result<String> {
|
||||
// 使用PowerShell恢复快照
|
||||
let output = Command::new("powershell.exe")
|
||||
.args([
|
||||
"-Command",
|
||||
&format!(
|
||||
"$VM = Get-VM -Id '{}'; $Snapshot = Get-VMSnapshot -VM $VM -Name '{}'; Restore-VMSnapshot -VMSnapshot $Snapshot -Confirm:$false",
|
||||
vmid,
|
||||
snapshot_name
|
||||
)
|
||||
])
|
||||
.output()?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok("快照恢复成功".to_string())
|
||||
} else {
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
Ok(format!("快照恢复失败: {}", error))
|
||||
}
|
||||
}
|
||||
|
||||
async fn restore_snapshot_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");
|
||||
let snapshot_name = query.get("Name");
|
||||
|
||||
match (vmid, snapshot_name) {
|
||||
(Some(id), Some(name)) => {
|
||||
if let Ok(true) = check_vmid(id).await {
|
||||
match restore_snapshot(id, name).await {
|
||||
Ok(result) => HttpResponse::Ok().body(result),
|
||||
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": format!("Failed to restore snapshot: {}", e)
|
||||
})),
|
||||
}
|
||||
} else {
|
||||
HttpResponse::BadRequest().json(serde_json::json!({ "error": "Invalid VMID" }))
|
||||
}
|
||||
},
|
||||
(None, _) => HttpResponse::BadRequest().json(serde_json::json!({ "error": "Need VMID" })),
|
||||
(_, None) => HttpResponse::BadRequest().json(serde_json::json!({ "error": "Need snapshot name" })),
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete_snapshot(vmid: &str, snapshot_name: &str) -> Result<String> {
|
||||
// 使用PowerShell删除快照
|
||||
let output = Command::new("powershell.exe")
|
||||
.args([
|
||||
"-Command",
|
||||
&format!(
|
||||
"$VM = Get-VM -Id '{}'; $Snapshot = Get-VMSnapshot -VM $VM -Name '{}'; Remove-VMSnapshot -VMSnapshot $Snapshot -Confirm:$false",
|
||||
vmid,
|
||||
snapshot_name
|
||||
)
|
||||
])
|
||||
.output()?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok("快照删除成功".to_string())
|
||||
} else {
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
Ok(format!("快照删除失败: {}", error))
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete_snapshot_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");
|
||||
let snapshot_name = query.get("Name");
|
||||
|
||||
match (vmid, snapshot_name) {
|
||||
(Some(id), Some(name)) => {
|
||||
if let Ok(true) = check_vmid(id).await {
|
||||
match delete_snapshot(id, name).await {
|
||||
Ok(result) => HttpResponse::Ok().body(result),
|
||||
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": format!("Failed to delete snapshot: {}", e)
|
||||
})),
|
||||
}
|
||||
} else {
|
||||
HttpResponse::BadRequest().json(serde_json::json!({ "error": "Invalid VMID" }))
|
||||
}
|
||||
},
|
||||
(None, _) => HttpResponse::BadRequest().json(serde_json::json!({ "error": "Need VMID" })),
|
||||
(_, None) => HttpResponse::BadRequest().json(serde_json::json!({ "error": "Need snapshot name" })),
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
// 检查管理员权限
|
||||
@ -1000,6 +1287,9 @@ async fn main() -> std::io::Result<()> {
|
||||
.route("/api2/GetALL", web::get().to(get_all_handler))
|
||||
.route("/api2/GetNetWork", web::get().to(get_network_handler))
|
||||
.route("/api2/GetSnapShot", web::get().to(get_snapshot_handler))
|
||||
.route("/api2/CreateSnapShot", web::post().to(create_snapshot_handler))
|
||||
.route("/api2/RestoreSnapShot", web::post().to(restore_snapshot_handler))
|
||||
.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/ListVM", web::get().to(list_vm_handler))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user