// SPDX-License-Identifier: Apache-2.0 use super::grpc::{self, proto}; use super::{injector, Dtrfs, Error}; use crate::config::Config; use crate::utils::block_on; use log::{debug, info}; #[derive(Clone, Debug, Default, PartialEq)] pub struct Request { hostname: String, vcpus: u32, memory_mib: u32, disk_size_mib: u32, dtrfs: Option, } impl Request { pub fn process_request( hostname: String, uuid: &str, vcpus: u32, memory_mb: u32, disk_size_gb: u32, dtrfs: &str, ) -> Result<(), Error> { let dtrfs = match dtrfs { "" => None, "latest" => Some(super::DEFAULT_DTRFS.clone()), path => { log::info!("Loading DTRFS from path: {path}"); Some(Dtrfs::load_from_file(path)?) } }; let req = Self { hostname, vcpus, memory_mib: memory_mb, disk_size_mib: disk_size_gb, dtrfs }; if req == Self::default() { log::info!("Skipping hardware upgrade (no arguments specified)."); return Ok(()); } log::info!("Starting VM updated based on req: {req:#?}"); req.update(uuid) } fn update(&self, uuid: &str) -> Result<(), Error> { info!("Starting the process of updating the VM. {self:?}"); let update_vm_resp = self.send_update_vm_request(uuid)?; debug!("The response for Update VM is: {update_vm_resp:#?}"); if !update_vm_resp.error.is_empty() { return Err(Error::Node(update_vm_resp.error)); } eprintln!("The modifications got approved. Proceeding with update..."); let updated_contract = block_on(grpc::get_contract_by_uuid(uuid))?; debug!("Got the current contract for the VM after update. {updated_contract:#?}"); if !(self.vcpus != 0 || self.memory_mib != 0 || self.dtrfs.is_some()) { eprintln!("vCPUs and kernel did not get modified. Secret injection is not required."); return Ok(()); } let args = update_vm_resp.args.ok_or(Error::NoMeasurement)?; let measurement_args = injector::Args { uuid: update_vm_resp.uuid, vcpus: updated_contract.vcpus, kernel: updated_contract.kernel_sha, initrd: updated_contract.dtrfs_sha, args: args.clone(), }; let measurement = measurement_args.get_measurement()?; injector::execute(measurement, args.dtrfs_api_endpoint, None, &updated_contract.hostname)?; Ok(()) } // returns node IP and data regarding the new VM fn send_update_vm_request(&self, uuid: &str) -> Result { let (kernel_url, kernel_sha, dtrfs_url, dtrfs_sha) = match self.dtrfs.clone() { Some(dtrfs) => (dtrfs.kernel_url, dtrfs.kernel_sha, dtrfs.dtrfs_url, dtrfs.dtrfs_sha), None => (String::new(), String::new(), String::new(), String::new()), }; Ok(block_on(grpc::update_vm(proto::UpdateVmReq { uuid: uuid.to_string(), hostname: self.hostname.clone(), admin_pubkey: Config::get_detee_wallet()?, disk_size_mib: self.disk_size_mib * 1024, vcpus: self.vcpus, memory_mib: self.memory_mib * 1024, kernel_url, kernel_sha, dtrfs_url, dtrfs_sha, }))?) } } pub fn expand_vm_hours(uuid: &str, hours: u32) -> Result<(), Error> { let contract = super::get_one_contract(uuid)?; // vcpus: u32, memory_mb: u32, disk_size_gb: u32, public_ipv4: bool, hours: u32, node_price: let locked_nano = contract.nano_per_minute * 60 * (hours as u64); block_on(grpc::extend_vm(uuid.to_string(), Config::get_detee_wallet()?, locked_nano))?; Ok(()) }