From abce5184b3e03c97f9ecbe90948b852e0236d33d Mon Sep 17 00:00:00 2001 From: leon3s Date: Sun, 14 Jan 2024 02:04:30 +0100 Subject: [PATCH] wip action --- .vscode/settings.json | 3 + bin/nanocld/src/models/system.rs | 14 ++- bin/nanocld/src/objects/generic/mod.rs | 13 +++ bin/nanocld/src/objects/vm.rs | 6 +- bin/nanocld/src/services/vm_image.rs | 4 +- bin/nanocld/src/utils/vm.rs | 4 +- bin/nanocld/src/utils/vm_image.rs | 116 +++++++++++++------------ 7 files changed, 95 insertions(+), 65 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 89a8a3892..c27a1ab2c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,7 +23,9 @@ "dotenv", "dotenvy", "errno", + "iface", "Insertable", + "keygen", "metrs", "Metrsd", "nanocl", @@ -31,6 +33,7 @@ "ncproxy", "nstore", "ntex", + "qcow", "schemars", "statefile", "utoipa" diff --git a/bin/nanocld/src/models/system.rs b/bin/nanocld/src/models/system.rs index f12766cb8..50a7e7dd2 100644 --- a/bin/nanocld/src/models/system.rs +++ b/bin/nanocld/src/models/system.rs @@ -1,13 +1,16 @@ use std::sync::Arc; use ntex::rt; -use nanocl_error::io::{IoResult, FromIo, IoError}; +use nanocl_error::{ + io::{IoResult, FromIo, IoError}, + http::HttpResult, +}; use nanocl_stubs::{ config::DaemonConfig, system::{Event, EventPartial, NativeEventAction, EventActor, EventKind}, }; -use crate::{vars, utils, repositories::generic::*}; +use crate::{vars, utils, repositories::generic::*, objects::generic::StateAction}; use super::{Pool, EventDb, RawEventEmitter, RawEventClient, TaskManager}; @@ -82,6 +85,13 @@ impl SystemState { Ok(system_state) } + pub async fn exec_action(&self, action: A) -> HttpResult + where + A: StateAction, + { + action.fn_action(self).await + } + pub async fn emit_event(&self, new_ev: EventPartial) -> IoResult<()> { let ev: Event = EventDb::create_try_from(new_ev, &self.pool) .await? diff --git a/bin/nanocld/src/objects/generic/mod.rs b/bin/nanocld/src/objects/generic/mod.rs index 5acf003b0..f4beb768b 100644 --- a/bin/nanocld/src/objects/generic/mod.rs +++ b/bin/nanocld/src/objects/generic/mod.rs @@ -1,3 +1,5 @@ +use nanocl_error::http::HttpResult; + mod create; mod delete; mod patch; @@ -11,3 +13,14 @@ pub use patch::*; pub use put::*; pub use inspect::*; pub use process::*; + +use crate::models::SystemState; + +pub trait StateAction { + type StateActionOut; + + async fn fn_action( + &self, + state: &SystemState, + ) -> HttpResult; +} diff --git a/bin/nanocld/src/objects/vm.rs b/bin/nanocld/src/objects/vm.rs index 442bf3c9b..f77e3f3d9 100644 --- a/bin/nanocld/src/objects/vm.rs +++ b/bin/nanocld/src/objects/vm.rs @@ -9,7 +9,7 @@ use nanocl_stubs::{ }; use crate::{ - utils, + utils::{self, vm_image::ActionVmDelete}, repositories::generic::*, models::{ VmDb, SystemState, VmObjCreateIn, VmImageDb, SpecDb, VmObjPutIn, @@ -87,7 +87,9 @@ impl ObjDelByPk for VmDb { VmDb::del_process_by_pk(&container_name, Some(options), state).await?; VmDb::del_by_pk(pk, &state.pool).await?; SpecDb::del_by_kind_key(pk, &state.pool).await?; - utils::vm_image::delete_by_name(&vm.spec.disk.image, &state.pool).await?; + state + .exec_action(ActionVmDelete(&vm.spec.disk.image)) + .await?; Ok(vm) } } diff --git a/bin/nanocld/src/services/vm_image.rs b/bin/nanocld/src/services/vm_image.rs index 8c52feba6..3d3c995c8 100644 --- a/bin/nanocld/src/services/vm_image.rs +++ b/bin/nanocld/src/services/vm_image.rs @@ -11,7 +11,7 @@ use nanocl_stubs::{ }; use crate::{ - utils, + utils::{self, vm_image::ActionVmDelete}, repositories::generic::*, models::{SystemState, VmImageDb}, }; @@ -212,7 +212,7 @@ pub async fn delete_vm_image( path: web::types::Path<(String, String)>, ) -> HttpResult { let name = path.1.to_owned(); - utils::vm_image::delete_by_name(&name, &state.pool).await?; + state.exec_action(ActionVmDelete(&name)).await?; Ok(web::HttpResponse::Ok().into()) } diff --git a/bin/nanocld/src/utils/vm.rs b/bin/nanocld/src/utils/vm.rs index ce77860df..16b4eadff 100644 --- a/bin/nanocld/src/utils/vm.rs +++ b/bin/nanocld/src/utils/vm.rs @@ -19,7 +19,7 @@ pub async fn create_instance( state: &SystemState, ) -> HttpResult<()> { let mut labels: HashMap = HashMap::new(); - let vmimagespath = format!("{}/vms/images", state.config.state_dir); + let img_path = format!("{}/vms/images", state.config.state_dir); labels.insert("io.nanocl.v".to_owned(), vm.spec.vm_key.clone()); labels.insert("io.nanocl.n".to_owned(), vm.namespace_name.clone()); let mut args: Vec = @@ -102,7 +102,7 @@ pub async fn create_instance( .clone() .unwrap_or(vm.namespace_name.to_owned()), ), - binds: Some(vec![format!("{vmimagespath}:{vmimagespath}")]), + binds: Some(vec![format!("{img_path}:{img_path}")]), devices: Some(devices), cap_add: Some(vec!["NET_ADMIN".into()]), ..Default::default() diff --git a/bin/nanocld/src/utils/vm_image.rs b/bin/nanocld/src/utils/vm_image.rs index 1491b8ec1..83c399c47 100644 --- a/bin/nanocld/src/utils/vm_image.rs +++ b/bin/nanocld/src/utils/vm_image.rs @@ -9,25 +9,35 @@ use nanocl_stubs::vm_image::{VmImageCloneStream, VmImageResizePayload}; use crate::{ utils, + objects::generic::*, repositories::generic::*, models::{Pool, VmImageDb, QemuImgInfo, VmImageUpdateDb, SystemState}, }; -/// Delete a vm image from the database and from the filesystem -pub async fn delete_by_name(name: &str, pool: &Pool) -> HttpResult<()> { - let vm_image = VmImageDb::read_by_pk(name, pool).await?; - let children = VmImageDb::read_by_parent(name, pool).await?; - if !children.is_empty() { - return Err(HttpError::conflict(format!( - "Vm image {name} has children images please delete them first" - ))); - } - let filepath = vm_image.path.clone(); - if let Err(err) = fs::remove_file(&filepath).await { - log::warn!("Error while deleting the file {filepath}: {err}"); +pub struct ActionVmDelete<'a>(pub &'a str); + +impl<'a> StateAction for ActionVmDelete<'a> { + type StateActionOut = (); + + async fn fn_action( + &self, + state: &SystemState, + ) -> HttpResult { + let name = self.0; + let vm_image = VmImageDb::read_by_pk(name, &state.pool).await?; + let children = VmImageDb::read_by_parent(name, &state.pool).await?; + if !children.is_empty() { + return Err(HttpError::conflict(format!( + "Vm image {name} has children images please delete them first" + ))); + } + let filepath = vm_image.path.clone(); + if let Err(err) = fs::remove_file(&filepath).await { + log::warn!("Error while deleting the file {filepath}: {err}"); + } + VmImageDb::del_by_pk(name, &state.pool).await?; + Ok(()) } - VmImageDb::del_by_pk(name, pool).await?; - Ok(()) } /// Get the info of a vm image using qemu-img info command and parse the output @@ -69,8 +79,8 @@ pub async fn create_snap( if VmImageDb::read_by_pk(name, &state.pool).await.is_ok() { return Err(HttpError::conflict(format!("Vm image {name} already used"))); } - let imagepath = image.path.clone(); - let snapshotpath = + let img_path = image.path.clone(); + let snapshot_path = format!("{}/vms/images/{}.img", state.config.state_dir, name); let output = Command::new("qemu-img") .args([ @@ -80,8 +90,8 @@ pub async fn create_snap( "-f", "qcow2", "-b", - &imagepath, - &snapshotpath, + &img_path, + &snapshot_path, ]) .output() .await @@ -97,12 +107,12 @@ pub async fn create_snap( )?; let size = format!("{size}G"); let output = Command::new("qemu-img") - .args(["resize", &snapshotpath, &size]) + .args(["resize", &snapshot_path, &size]) .output() .await .map_err(|err| { HttpError::internal_server_error(format!( - "Failed to resize snapshot {imagepath}: {err}" + "Failed to resize snapshot {img_path}: {err}" )) })?; output.status.success().then_some(()).ok_or( @@ -110,15 +120,15 @@ pub async fn create_snap( "Failed to resize snapshot {name}: {output:#?}" )), )?; - let image_info = get_info(&snapshotpath).await?; + let img_info = get_info(&snapshot_path).await?; let snap_image = VmImageDb { name: name.to_owned(), created_at: chrono::Utc::now().naive_utc(), kind: "Snapshot".into(), - path: snapshotpath.clone(), - format: image_info.format, - size_actual: image_info.actual_size, - size_virtual: image_info.virtual_size, + path: snapshot_path.clone(), + format: img_info.format, + size_actual: img_info.actual_size, + size_virtual: img_info.virtual_size, parent: Some(image.name.clone()), }; let snap_image = VmImageDb::create_from(snap_image, &state.pool).await?; @@ -148,19 +158,11 @@ pub async fn clone( let daemon_conf = state.config.clone(); let pool = Arc::clone(&state.pool); rt::spawn(async move { - let imagepath = image.path.clone(); - let newbasepath = + let img_path = image.path.clone(); + let base_path = format!("{}/vms/images/{}.img", daemon_conf.state_dir, name); let mut child = match Command::new("qemu-img") - .args([ - "convert", - "-p", - "-O", - "qcow2", - "-c", - &imagepath, - &newbasepath, - ]) + .args(["convert", "-p", "-O", "qcow2", "-c", &img_path, &base_path]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() @@ -184,7 +186,7 @@ pub async fn clone( } Ok(stdout) => stdout, }; - let txpg = tx.clone(); + let tx_ptr = tx.clone(); rt::spawn(async move { let mut buf = [0; 1024]; loop { @@ -201,7 +203,7 @@ pub async fn clone( .unwrap(); let stream = VmImageCloneStream::Progress(progress); let stream = serde_json::to_string(&stream).unwrap(); - let _ = txpg.send(Ok(Bytes::from(format!("{stream}\r\n")))); + let _ = tx_ptr.send(Ok(Bytes::from(format!("{stream}\r\n")))); } _ => break, } @@ -230,21 +232,21 @@ pub async fn clone( let _ = tx.send(Err(err.clone())); return Err(err); }; - let image_info = match get_info(&newbasepath).await { + let img_info = match get_info(&base_path).await { Err(err) => { let _ = tx.send(Err(err.clone())); return Err(err); } - Ok(image_info) => image_info, + Ok(img_info) => img_info, }; let new_base_image = VmImageDb { name: name.to_owned(), created_at: chrono::Utc::now().naive_utc(), kind: "Base".into(), - path: newbasepath.clone(), - format: image_info.format, - size_actual: image_info.actual_size, - size_virtual: image_info.virtual_size, + path: base_path.clone(), + format: img_info.format, + size_actual: img_info.actual_size, + size_virtual: img_info.virtual_size, parent: None, }; let vm = match VmImageDb::create_from(new_base_image, &pool).await { @@ -268,15 +270,15 @@ pub async fn resize( payload: &VmImageResizePayload, pool: &Pool, ) -> HttpResult { - let imagepath = image.path.clone(); + let img_path = image.path.clone(); let size = format!("{}G", payload.size); let mut args = vec!["resize"]; if payload.shrink { args.push("--shrink"); } - args.push(&imagepath); + args.push(&img_path); args.push(&size); - let ouput = + let output = Command::new("qemu-img") .args(args) .output() @@ -286,18 +288,18 @@ pub async fn resize( "Unable to resize image {err}" )) })?; - if !ouput.status.success() { - let output = String::from_utf8(ouput.stdout).unwrap_or_default(); + if !output.status.success() { + let output = String::from_utf8(output.stdout).unwrap_or_default(); return Err(HttpError::internal_server_error(format!( "Unable to resize image {output}" ))); } - let image_info = get_info(&imagepath).await?; + let img_info = get_info(&img_path).await?; let res = VmImageDb::update_pk( &image.name, VmImageUpdateDb { - size_actual: image_info.actual_size, - size_virtual: image_info.virtual_size, + size_actual: img_info.actual_size, + size_virtual: img_info.virtual_size, }, pool, ) @@ -322,21 +324,21 @@ pub async fn create( pool: &Pool, ) -> HttpResult { // Get image info - let image_info = match utils::vm_image::get_info(filepath).await { + let img_info = match utils::vm_image::get_info(filepath).await { Err(err) => { let fp2 = filepath.to_owned(); let _ = web::block(move || std::fs::remove_file(fp2)).await; return Err(err); } - Ok(image_info) => image_info, + Ok(img_info) => img_info, }; let vm_image = VmImageDb { name: name.to_owned(), created_at: chrono::Utc::now().naive_utc(), kind: "Base".into(), - format: image_info.format, - size_actual: image_info.actual_size, - size_virtual: image_info.virtual_size, + format: img_info.format, + size_actual: img_info.actual_size, + size_virtual: img_info.virtual_size, path: filepath.to_owned(), parent: None, };