Can export entire server configuration
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-06-17 21:17:25 +02:00
parent 1080ab5cb2
commit ab16bd7bcf
9 changed files with 369 additions and 3 deletions

View File

@ -1,14 +1,24 @@
use crate::actors::vnc_tokens_actor::VNC_TOKEN_LIFETIME;
use crate::app_config::AppConfig;
use crate::constants;
use crate::constants::{DISK_NAME_MAX_LEN, DISK_NAME_MIN_LEN, DISK_SIZE_MAX, DISK_SIZE_MIN};
use crate::controllers::{HttpResult, LibVirtReq};
use crate::extractors::local_auth_extractor::LocalAuthEnabled;
use crate::libvirt_rest_structures::hypervisor::HypervisorInfo;
use crate::libvirt_rest_structures::net::NetworkInfo;
use crate::libvirt_rest_structures::nw_filter::NetworkFilter;
use crate::libvirt_rest_structures::vm::VMInfo;
use crate::nat::nat_hook;
use crate::utils::net_utils;
use actix_web::{HttpResponse, Responder};
use crate::utils::time_utils::{format_date, time};
use crate::{api_tokens, constants};
use actix_files::NamedFile;
use actix_web::{HttpRequest, HttpResponse, Responder};
use serde::Serialize;
use std::fs::File;
use std::io::Write;
use sysinfo::{Components, Disks, Networks, System};
use zip::ZipWriter;
use zip::write::SimpleFileOptions;
#[derive(serde::Serialize)]
struct StaticConfig {
@ -199,3 +209,85 @@ pub async fn networks_list() -> HttpResult {
pub async fn bridges_list() -> HttpResult {
Ok(HttpResponse::Ok().json(net_utils::bridges_list()?))
}
/// Add JSON file to ZIP
fn zip_json<E: Serialize, F>(
zip: &mut ZipWriter<File>,
dir: &str,
content: &Vec<E>,
file_name: F,
) -> anyhow::Result<()>
where
F: Fn(&E) -> String,
{
for entry in content {
let file_encoded = serde_json::to_string(&entry)?;
let options = SimpleFileOptions::default()
.compression_method(zip::CompressionMethod::Deflated)
.unix_permissions(0o750);
zip.start_file(format!("{dir}/{}.json", file_name(entry)), options)?;
zip.write_all(file_encoded.as_bytes())?;
}
Ok(())
}
/// Export all configuration elements at once
pub async fn export_all_configs(req: HttpRequest, client: LibVirtReq) -> HttpResult {
// Perform extractions
let vms = client
.get_full_domains_list()
.await?
.into_iter()
.map(VMInfo::from_domain)
.collect::<Result<Vec<_>, _>>()?;
let networks = client
.get_full_networks_list()
.await?
.into_iter()
.map(NetworkInfo::from_xml)
.collect::<Result<Vec<_>, _>>()?;
let nw_filters = client
.get_full_network_filters_list()
.await?
.into_iter()
.map(NetworkFilter::lib2rest)
.collect::<Result<Vec<_>, _>>()?;
let tokens = api_tokens::full_list().await?;
// Create ZIP file
let dest_dir = tempfile::tempdir_in(&AppConfig::get().temp_dir)?;
let zip_path = dest_dir.path().join("export.zip");
let file = File::create(&zip_path)?;
let mut zip = ZipWriter::new(file);
// Encode entities to JSON
zip_json(&mut zip, "vms", &vms, |v| v.name.to_string())?;
zip_json(&mut zip, "networks", &networks, |v| v.name.0.to_string())?;
zip_json(
&mut zip,
"nw_filters",
&nw_filters,
|v| match constants::BUILTIN_NETWORK_FILTER_RULES.contains(&v.name.0.as_str()) {
true => format!("builtin/{}", v.name.0),
false => v.name.0.to_string(),
},
)?;
zip_json(&mut zip, "tokens", &tokens, |v| v.id.0.to_string())?;
// Finalize ZIP and return response
zip.finish()?;
let file = File::open(zip_path)?;
let file = NamedFile::from_file(
file,
format!(
"export_{}.zip",
format_date(time() as i64).unwrap().replace('/', "-")
),
)?;
Ok(file.into_response(&req))
}