2026-06-11 02:50:30 +00:00
|
|
|
// Storage management module
|
|
|
|
|
// Provides operations for managing Proxmox storage
|
|
|
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
|
|
/// Storage information
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct StorageInfo {
|
|
|
|
|
pub storage: String,
|
|
|
|
|
pub node: String,
|
|
|
|
|
pub type_: String,
|
|
|
|
|
pub content: String,
|
|
|
|
|
pub size: u64,
|
|
|
|
|
pub used: u64,
|
|
|
|
|
pub available: u64,
|
|
|
|
|
pub status: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// List all storages
|
|
|
|
|
pub async fn list_storages(
|
|
|
|
|
_client: &crate::proxmox::client::ProxmoxClient,
|
|
|
|
|
_node: &str,
|
|
|
|
|
_ticket: &str,
|
|
|
|
|
) -> Result<Vec<StorageInfo>, String> {
|
|
|
|
|
Err("Not implemented yet".to_string())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get storage status
|
|
|
|
|
pub async fn get_storage_status(
|
|
|
|
|
_client: &crate::proxmox::client::ProxmoxClient,
|
|
|
|
|
_node: &str,
|
|
|
|
|
_storage: &str,
|
|
|
|
|
_ticket: &str,
|
|
|
|
|
) -> Result<StorageInfo, String> {
|
|
|
|
|
Err("Not implemented yet".to_string())
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 02:41:57 +00:00
|
|
|
/// List ISO images available in a storage (client-side filtered from storage content)
|
|
|
|
|
pub async fn list_storage_content_iso(
|
|
|
|
|
client: &crate::proxmox::client::ProxmoxClient,
|
|
|
|
|
node: &str,
|
|
|
|
|
storage: &str,
|
|
|
|
|
ticket: &str,
|
|
|
|
|
) -> Result<Vec<serde_json::Value>, String> {
|
|
|
|
|
let path = format!("nodes/{}/storage/{}/content", node, storage);
|
|
|
|
|
let response: serde_json::Value = client
|
|
|
|
|
.get(&path, Some(ticket))
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| format!("Failed to list storage content for {}/{}: {}", node, storage, e))?;
|
|
|
|
|
|
|
|
|
|
response
|
|
|
|
|
.as_array()
|
|
|
|
|
.map(|arr| {
|
|
|
|
|
arr.iter()
|
|
|
|
|
.filter(|item| {
|
|
|
|
|
item.get("content")
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(|c| c == "iso")
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
})
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
})
|
|
|
|
|
.ok_or_else(|| "Invalid response format from storage content".to_string())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Upload an ISO file to a Proxmox storage pool.
|
|
|
|
|
/// Returns the task UPID string that can be polled for completion.
|
|
|
|
|
pub async fn upload_iso(
|
|
|
|
|
client: &crate::proxmox::client::ProxmoxClient,
|
|
|
|
|
node: &str,
|
|
|
|
|
storage: &str,
|
|
|
|
|
filename: &str,
|
|
|
|
|
file_bytes: Vec<u8>,
|
|
|
|
|
ticket: &str,
|
|
|
|
|
) -> Result<String, String> {
|
|
|
|
|
let path = format!("nodes/{}/storage/{}/upload", node, storage);
|
|
|
|
|
|
|
|
|
|
let file_part = reqwest::multipart::Part::bytes(file_bytes)
|
|
|
|
|
.file_name(filename.to_string())
|
|
|
|
|
.mime_str("application/octet-stream")
|
|
|
|
|
.map_err(|e| format!("Failed to build multipart part: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let form = reqwest::multipart::Form::new()
|
|
|
|
|
.text("content", "iso")
|
|
|
|
|
.text("filename", filename.to_string())
|
|
|
|
|
.part("file", file_part);
|
|
|
|
|
|
|
|
|
|
let task_id: String = client
|
|
|
|
|
.post_multipart(&path, form, Some(ticket))
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| format!("Failed to upload ISO to {}/{}: {}", node, storage, e))?;
|
|
|
|
|
|
|
|
|
|
Ok(task_id)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-11 02:50:30 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_storage_info_serialization() {
|
|
|
|
|
let storage = StorageInfo {
|
|
|
|
|
storage: "local".to_string(),
|
|
|
|
|
node: "pve-node-1".to_string(),
|
|
|
|
|
type_: "dir".to_string(),
|
|
|
|
|
content: "images,backup,iso".to_string(),
|
|
|
|
|
size: 1000000000000,
|
|
|
|
|
used: 300000000000,
|
|
|
|
|
available: 700000000000,
|
|
|
|
|
status: "available".to_string(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let json = serde_json::to_string(&storage).unwrap();
|
|
|
|
|
let deserialized: StorageInfo = serde_json::from_str(&json).unwrap();
|
|
|
|
|
|
|
|
|
|
assert_eq!(storage.storage, deserialized.storage);
|
|
|
|
|
assert_eq!(storage.status, "available");
|
|
|
|
|
}
|
|
|
|
|
}
|