// 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, 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 { Err("Not implemented yet".to_string()) } /// 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, 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::>() }) .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, ticket: &str, ) -> Result { 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) } #[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"); } }