All checks were successful
continuous-integration/drone/push Build is passing
294 lines
9.0 KiB
Rust
294 lines
9.0 KiB
Rust
use crate::actors::vnc_tokens_actor::VNC_TOKEN_LIFETIME;
|
|
use crate::app_config::AppConfig;
|
|
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 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 {
|
|
auth_disabled: bool,
|
|
local_auth_enabled: bool,
|
|
oidc_auth_enabled: bool,
|
|
iso_mimetypes: &'static [&'static str],
|
|
disk_images_mimetypes: &'static [&'static str],
|
|
net_mac_prefix: &'static str,
|
|
builtin_nwfilter_rules: &'static [&'static str],
|
|
nwfilter_chains: &'static [&'static str],
|
|
constraints: ServerConstraints,
|
|
}
|
|
|
|
#[derive(serde::Serialize)]
|
|
struct LenConstraints {
|
|
min: usize,
|
|
max: usize,
|
|
}
|
|
|
|
#[derive(serde::Serialize)]
|
|
struct SLenConstraints {
|
|
min: i64,
|
|
max: i64,
|
|
}
|
|
|
|
#[derive(serde::Serialize)]
|
|
struct ServerConstraints {
|
|
iso_max_size: usize,
|
|
disk_image_max_size: usize,
|
|
vnc_token_duration: u64,
|
|
vm_name_size: LenConstraints,
|
|
vm_title_size: LenConstraints,
|
|
group_id_size: LenConstraints,
|
|
memory_size: LenConstraints,
|
|
disk_name_size: LenConstraints,
|
|
disk_size: LenConstraints,
|
|
disk_image_name_size: LenConstraints,
|
|
net_name_size: LenConstraints,
|
|
net_title_size: LenConstraints,
|
|
net_nat_comment_size: LenConstraints,
|
|
dhcp_reservation_host_name: LenConstraints,
|
|
nwfilter_name_size: LenConstraints,
|
|
nwfilter_comment_size: LenConstraints,
|
|
nwfilter_priority: SLenConstraints,
|
|
nwfilter_selectors_count: LenConstraints,
|
|
api_token_name_size: LenConstraints,
|
|
api_token_description_size: LenConstraints,
|
|
api_token_right_path_size: LenConstraints,
|
|
}
|
|
|
|
pub async fn static_config(local_auth: LocalAuthEnabled) -> impl Responder {
|
|
HttpResponse::Ok().json(StaticConfig {
|
|
auth_disabled: AppConfig::get().unsecure_disable_auth,
|
|
local_auth_enabled: *local_auth,
|
|
oidc_auth_enabled: !AppConfig::get().disable_oidc,
|
|
iso_mimetypes: &constants::ALLOWED_ISO_MIME_TYPES,
|
|
disk_images_mimetypes: &constants::ALLOWED_DISK_IMAGES_MIME_TYPES,
|
|
net_mac_prefix: constants::NET_MAC_ADDR_PREFIX,
|
|
builtin_nwfilter_rules: &constants::BUILTIN_NETWORK_FILTER_RULES,
|
|
nwfilter_chains: &constants::NETWORK_CHAINS,
|
|
constraints: ServerConstraints {
|
|
iso_max_size: constants::ISO_MAX_SIZE.as_bytes(),
|
|
disk_image_max_size: constants::DISK_IMAGE_MAX_SIZE.as_bytes(),
|
|
|
|
vnc_token_duration: VNC_TOKEN_LIFETIME,
|
|
|
|
vm_name_size: LenConstraints { min: 2, max: 50 },
|
|
vm_title_size: LenConstraints { min: 0, max: 50 },
|
|
group_id_size: LenConstraints { min: 3, max: 50 },
|
|
memory_size: LenConstraints {
|
|
min: constants::MIN_VM_MEMORY.as_bytes(),
|
|
max: constants::MAX_VM_MEMORY.as_bytes(),
|
|
},
|
|
disk_name_size: LenConstraints {
|
|
min: DISK_NAME_MIN_LEN,
|
|
max: DISK_NAME_MAX_LEN,
|
|
},
|
|
disk_size: LenConstraints {
|
|
min: DISK_SIZE_MIN.as_bytes(),
|
|
max: DISK_SIZE_MAX.as_bytes(),
|
|
},
|
|
|
|
disk_image_name_size: LenConstraints { min: 5, max: 220 },
|
|
|
|
net_name_size: LenConstraints { min: 2, max: 50 },
|
|
net_title_size: LenConstraints { min: 0, max: 50 },
|
|
net_nat_comment_size: LenConstraints {
|
|
min: 0,
|
|
max: constants::NET_NAT_COMMENT_MAX_SIZE,
|
|
},
|
|
|
|
dhcp_reservation_host_name: LenConstraints { min: 2, max: 250 },
|
|
|
|
nwfilter_name_size: LenConstraints { min: 2, max: 250 },
|
|
nwfilter_comment_size: LenConstraints { min: 0, max: 256 },
|
|
nwfilter_priority: SLenConstraints {
|
|
min: -1000,
|
|
max: 1000,
|
|
},
|
|
nwfilter_selectors_count: LenConstraints { min: 0, max: 1 },
|
|
|
|
api_token_name_size: LenConstraints {
|
|
min: constants::API_TOKEN_NAME_MIN_LENGTH,
|
|
max: constants::API_TOKEN_NAME_MAX_LENGTH,
|
|
},
|
|
|
|
api_token_description_size: LenConstraints {
|
|
min: constants::API_TOKEN_DESCRIPTION_MIN_LENGTH,
|
|
max: constants::API_TOKEN_DESCRIPTION_MAX_LENGTH,
|
|
},
|
|
|
|
api_token_right_path_size: LenConstraints {
|
|
min: 0,
|
|
max: constants::API_TOKEN_RIGHT_PATH_MAX_LENGTH,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
#[derive(serde::Serialize)]
|
|
struct ServerInfo {
|
|
hypervisor: HypervisorInfo,
|
|
system: System,
|
|
components: Components,
|
|
disks: Disks,
|
|
networks: Networks,
|
|
}
|
|
|
|
pub async fn server_info(client: LibVirtReq) -> HttpResult {
|
|
let mut system = System::new();
|
|
system.refresh_all();
|
|
|
|
let mut components = Components::new();
|
|
components.refresh(true);
|
|
|
|
let mut disks = Disks::new();
|
|
disks.refresh(true);
|
|
|
|
let mut networks = Networks::new();
|
|
networks.refresh(true);
|
|
|
|
Ok(HttpResponse::Ok().json(ServerInfo {
|
|
hypervisor: client.get_info().await?,
|
|
system,
|
|
components,
|
|
disks,
|
|
networks,
|
|
}))
|
|
}
|
|
|
|
#[derive(serde::Serialize)]
|
|
struct NetworkHookStatus {
|
|
installed: bool,
|
|
content: String,
|
|
path: &'static str,
|
|
}
|
|
|
|
pub async fn network_hook_status() -> HttpResult {
|
|
Ok(HttpResponse::Ok().json(NetworkHookStatus {
|
|
installed: nat_hook::is_installed()?,
|
|
content: nat_hook::hook_content()?,
|
|
path: constants::NAT_HOOK_PATH,
|
|
}))
|
|
}
|
|
|
|
pub async fn number_vcpus() -> HttpResult {
|
|
let mut system = System::new();
|
|
system.refresh_cpu_all();
|
|
let number_cpus = system.cpus().len();
|
|
assert_ne!(number_cpus, 0, "Got invlid number of CPU!");
|
|
|
|
let mut possible_numbers = vec![1];
|
|
|
|
if number_cpus > 1 {
|
|
for i in 0..(number_cpus / 2) {
|
|
possible_numbers.push(2 + 2 * i);
|
|
}
|
|
}
|
|
|
|
Ok(HttpResponse::Ok().json(possible_numbers))
|
|
}
|
|
|
|
pub async fn networks_list() -> HttpResult {
|
|
Ok(HttpResponse::Ok().json(net_utils::net_list()))
|
|
}
|
|
|
|
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))
|
|
}
|