diff --git a/Cargo.lock b/Cargo.lock index abd2e50..1ae6dea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,6 @@ -# SPDX-License-Identifier: Apache-2.0 - # This file is automatically @generated by Cargo. # It is not intended for manual editing. +# SPDX-License-Identifier: Apache-2.0 version = 4 [[package]] @@ -1184,7 +1183,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=surreal_brain_app#0b195b4589e4ec689af7ddca27dc051716ecee78" +source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=credits-v2#f344c171c5a8d7ae8cad1628396e6b3a1af0f1ba" dependencies = [ "bincode", "prost", diff --git a/Cargo.toml b/Cargo.toml index 757ae85..824da56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ tokio-retry = "0.3.0" detee-sgx = { git = "ssh://git@gitea.detee.cloud/testnet/detee-sgx.git", branch = "hratls", features=["hratls", "qvl"] } shadow-rs = { version = "1.1.1", features = ["metadata"] } -detee-shared = { git = "ssh://git@gitea.detee.cloud/testnet/proto.git", branch = "surreal_brain_app" } +detee-shared = { git = "ssh://git@gitea.detee.cloud/testnet/proto.git", branch = "credits-v2" } # detee-shared = { path = "../detee-shared" } [build-dependencies] diff --git a/src/bin/detee-cli.rs b/src/bin/detee-cli.rs index 484aedd..fa03b11 100644 --- a/src/bin/detee-cli.rs +++ b/src/bin/detee-cli.rs @@ -347,16 +347,16 @@ fn clap_cmd() -> Command { .arg( Arg::new("memory") .long("memory") - .default_value("1000") - .value_parser(clap::value_parser!(u32).range(800..123000)) - .help("memory in MB") + .default_value("1") + .value_parser(clap::value_parser!(u32).range(1..500)) + .help("memory in GiB") ) .arg( Arg::new("disk") .long("disk") .default_value("10") .value_parser(clap::value_parser!(u32).range(5..500)) - .help("disk size in GB") + .help("disk size in GiB") ) .arg( Arg::new("distribution") @@ -375,8 +375,8 @@ fn clap_cmd() -> Command { .arg( Arg::new("price") .long("price") - .help("price per unit per minute; check docs") - .default_value("20000") + .help("maxium accepted price per unit per minute") + .default_value("4000") .value_parser(clap::value_parser!(u64).range(1..50000000)) ) .arg( diff --git a/src/general/grpc.rs b/src/general/grpc.rs index 540db7b..7117337 100644 --- a/src/general/grpc.rs +++ b/src/general/grpc.rs @@ -96,7 +96,7 @@ pub async fn kick_contract(contract_uuid: String, reason: String) -> Result Result<(), Error> { diff --git a/src/snp/cli_handler.rs b/src/snp/cli_handler.rs index db206ea..76c16fb 100644 --- a/src/snp/cli_handler.rs +++ b/src/snp/cli_handler.rs @@ -69,8 +69,8 @@ fn handle_vm_deploy(matches: &ArgMatches) -> Result("vcpus").unwrap(), - memory_mb: *matches.get_one::("memory").unwrap(), - disk_size_gb: *matches.get_one::("disk").unwrap(), + memory_gib: *matches.get_one::("memory").unwrap(), + disk_size_gib: *matches.get_one::("disk").unwrap(), dtrfs: None, hours: *matches.get_one::("hours").unwrap(), price: *matches.get_one::("price").unwrap(), diff --git a/src/snp/deploy.rs b/src/snp/deploy.rs index 8d11d29..12731d4 100644 --- a/src/snp/deploy.rs +++ b/src/snp/deploy.rs @@ -4,8 +4,7 @@ use super::{ grpc::{self, proto}, injector, Distro, Dtrfs, Error, VmSshArgs, DEFAULT_ARCHLINUX, DEFAULT_DTRFS, }; -use crate::config::Config; -use crate::utils::block_on; +use crate::{config::Config, utils::block_on}; use log::{debug, info}; use serde::{Deserialize, Serialize}; @@ -51,8 +50,8 @@ pub struct Request { pub ipv4: IPv4Config, pub public_ipv6: bool, pub vcpus: u32, - pub memory_mb: u32, - pub disk_size_gb: u32, + pub memory_gib: u32, + pub disk_size_gib: u32, pub dtrfs: Option, pub distro: Option, } @@ -70,8 +69,8 @@ impl Request { } pub fn deploy(&self) -> Result { - let (node_ip, new_vm_resp) = self.send_vm_request()?; - info!("Got confirmation from the node {node_ip} that VM started."); + let new_vm_resp = self.calculate_and_send_request()?; + info!("Got confirmation from the node that the VM started."); debug!("IPs and ports assigned by node are: {new_vm_resp:#?}"); if !new_vm_resp.error.is_empty() { return Err(Error::Node(new_vm_resp.error)); @@ -108,9 +107,96 @@ impl Request { } // returns node IP and data regarding the new VM - fn send_vm_request(&self) -> Result<(String, proto::NewVmResp), Error> { - let admin_pubkey = Config::get_detee_wallet()?; - let node = self.get_node()?; + fn calculate_and_send_request(&self) -> Result { + let new_vm_req = self.get_cheapest_offer()?; + + info!( + "The best offer (from node {}) is for {} nanocredits (covering all {} hours), + and includes the following HW spec: {} vCPUs, {} MiB Mem, {} MiB Disk", + new_vm_req.node_pubkey, + self.hours, + new_vm_req.locked_nano, + new_vm_req.vcpus, + new_vm_req.memory_mib, + new_vm_req.disk_size_mib + ); + + let new_vm_resp = block_on(grpc::create_vm(new_vm_req))?; + if !new_vm_resp.error.is_empty() { + return Err(Error::Node(new_vm_resp.error)); + } + Ok(new_vm_resp) + } + + fn get_cheapest_offer(&self) -> Result { + let (free_ports, offers_ipv4) = match &self.ipv4 { + IPv4Config::PublishPorts(vec) => (vec.len() as u32, false), + IPv4Config::PublicIPv4 => (0, true), + }; + let filters = proto::VmNodeFilters { + free_ports, + offers_ipv4, + offers_ipv6: self.public_ipv6, + vcpus: self.vcpus, + memory_mib: self.memory_gib * 1024, + storage_mib: self.disk_size_gib * 1024, + country: self.location.country.clone().unwrap_or_default(), + region: self.location.region.clone().unwrap_or_default(), + city: self.location.city.clone().unwrap_or_default(), + ip: self.location.node_ip.clone().unwrap_or_default(), + node_pubkey: String::new(), + }; + let node_list = match block_on(grpc::get_node_list(filters)) { + Ok(node_list) => Ok(node_list), + Err(e) => { + log::error!("Coult not get node from brain: {e:?}"); + Err(Error::NoValidNodeFound) + } + }?; + + let mut offer_map = std::collections::BTreeMap::new(); + for node in node_list.iter() { + let (price, new_vm_req) = self.calculate_vm_request(Config::get_detee_wallet()?, node); + debug!( + "Node {} can offer the VM at {} nanocredits for {} hours. Spec: {} vCPUs, {} MiB mem, {} MiB disk.", + node.ip, price, self.hours, new_vm_req.vcpus, new_vm_req.memory_mib, new_vm_req.disk_size_mib + ); + offer_map.insert(price, new_vm_req); + } + + Ok(offer_map.pop_first().unwrap().1.clone()) + } + + fn calculate_vm_request( + &self, + admin_pubkey: String, + node: &proto::VmNodeListResp, + ) -> (u64, proto::NewVmReq) { + let memory_per_cpu = node.memory_mib / node.vcpus; + let disk_per_cpu = node.disk_mib / node.vcpus; + let mut vcpus = self.vcpus; + if vcpus < (self.memory_gib * 1024).div_ceil(memory_per_cpu as u32) { + vcpus = (self.memory_gib * 1024).div_ceil(memory_per_cpu as u32); + } + if vcpus < (self.disk_size_gib * 1024).div_ceil(disk_per_cpu as u32) { + vcpus = (self.disk_size_gib * 1024).div_ceil(disk_per_cpu as u32); + } + + let memory_mib = vcpus * memory_per_cpu as u32; + let disk_mib = vcpus * disk_per_cpu as u32; + + let nanocredits = super::calculate_nanocredits( + vcpus, + memory_mib, + disk_mib, + match self.ipv4 { + IPv4Config::PublishPorts(_) => false, + IPv4Config::PublicIPv4 => true, + }, + self.hours, + node.price, + ); + let (extra_ports, public_ipv4): (Vec, bool) = match &self.ipv4 { IPv4Config::PublishPorts(vec) => (vec.to_vec(), false), IPv4Config::PublicIPv4 => (Vec::new(), true), @@ -124,63 +210,26 @@ impl Request { DEFAULT_DTRFS.dtrfs_sha.clone(), ), }; - let locked_nano = super::calculate_nanolp( - self.vcpus, - self.memory_mb, - self.disk_size_gb, - public_ipv4, - self.hours, - self.price, - ); + let brain_req = proto::NewVmReq { uuid: String::new(), hostname: self.hostname.clone(), admin_pubkey, - node_pubkey: node.node_pubkey, + node_pubkey: node.node_pubkey.clone(), extra_ports, public_ipv4, public_ipv6: self.public_ipv6, - disk_size_gb: self.disk_size_gb, - vcpus: self.vcpus, - memory_mb: self.memory_mb, + disk_size_mib: disk_mib * 1024, + vcpus, + memory_mib: memory_mib * 1024, kernel_url, kernel_sha, dtrfs_url, dtrfs_sha, price_per_unit: self.price, - locked_nano, + locked_nano: nanocredits, }; - let new_vm_resp = block_on(grpc::create_vm(brain_req))?; - if !new_vm_resp.error.is_empty() { - return Err(Error::Node(new_vm_resp.error)); - } - Ok((node.ip, new_vm_resp)) - } - pub fn get_node(&self) -> Result { - let (free_ports, offers_ipv4) = match &self.ipv4 { - IPv4Config::PublishPorts(vec) => (vec.len() as u32, false), - IPv4Config::PublicIPv4 => (0, true), - }; - let filters = proto::VmNodeFilters { - free_ports, - offers_ipv4, - offers_ipv6: self.public_ipv6, - vcpus: self.vcpus, - memory_mb: self.memory_mb, - storage_gb: self.disk_size_gb, - country: self.location.country.clone().unwrap_or_default(), - region: self.location.region.clone().unwrap_or_default(), - city: self.location.city.clone().unwrap_or_default(), - ip: self.location.node_ip.clone().unwrap_or_default(), - node_pubkey: String::new(), - }; - match block_on(grpc::get_one_node(filters)) { - Ok(node) => Ok(node), - Err(e) => { - log::error!("Coult not get node from brain: {e:?}"); - Err(Error::NoValidNodeFound) - } - } + (nanocredits, brain_req) } } diff --git a/src/snp/mod.rs b/src/snp/mod.rs index 3de89ba..9bdeb16 100644 --- a/src/snp/mod.rs +++ b/src/snp/mod.rs @@ -340,7 +340,7 @@ pub fn inspect_node(ip: String) -> Result { Ok(block_on(grpc::get_one_node(req))?) } -pub fn calculate_nanolp( +pub fn calculate_nanocredits( vcpus: u32, memory_mb: u32, disk_size_gb: u32, diff --git a/src/snp/update.rs b/src/snp/update.rs index 3a68e82..5eba59b 100644 --- a/src/snp/update.rs +++ b/src/snp/update.rs @@ -12,8 +12,8 @@ use log::{debug, info}; pub struct Request { hostname: String, vcpus: u32, - memory_mb: u32, - disk_size_gb: u32, + memory_mib: u32, + disk_size_mib: u32, dtrfs: Option, } @@ -34,7 +34,7 @@ impl Request { Some(Dtrfs::load_from_file(path)?) } }; - let req = Self { hostname, vcpus, memory_mb, disk_size_gb, dtrfs }; + 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(()); @@ -90,9 +90,9 @@ impl Request { uuid: uuid.to_string(), hostname: self.hostname.clone(), admin_pubkey: Config::get_detee_wallet()?, - disk_size_gb: self.disk_size_gb, + disk_size_mib: self.disk_size_mib * 1024, vcpus: self.vcpus, - memory_mb: self.memory_mb, + memory_mib: self.memory_mib * 1024, kernel_url, kernel_sha, dtrfs_url,