use ureq; use crate::success; use crate::external; use serde_json; use serde::{Serialize, Deserialize}; use serde_with::{ DisplayFromStr, serde_as, }; use uuid; /// Defines a VM (used for requests using the VM section) #[serde_as] #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct VirtualMachine { #[serde_as(as="DisplayFromStr")] vm_id: uuid::Uuid, // should be UUIDv4 name: String, // the name set by the user #[serde_as(as="DisplayFromStr")] plan_id: i32, // should be more strict hostname: String, // the hostname set by the user #[serde(rename="primaryip")] primary_ip: std::net::Ipv4Addr, #[serde(rename="privateip")] private_ip: std::net::Ipv4Addr, #[serde_as(as="DisplayFromStr")] ram: i32, // in GB, may need to be a float #[serde_as(as="DisplayFromStr")] vcpu: i32, // CPU cores #[serde_as(as="DisplayFromStr")] storage: i32, // in GB #[serde_as(as="DisplayFromStr")] bandwidth: i32, // in GB/month region: String, // could be more strict os_status: String, // should be nmore strict } #[derive(Serialize, Deserialize, Debug)] pub struct VMListResponse { vms: Vec, #[serde(with="success")] success: bool, // should be more strict "yes" or "no" as optiobs } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct VMInfoExtra { #[serde_as(as="DisplayFromStr")] /// bandwidth allowed over a month in GB bandwidth: i32, /// the hostname set by the user hostname: String, // sufficiently vague /// the name set by the user name: String, // sufficiently vague /// the status of the operating system; possible values: "active", ... TODO os_status: String, // should be stricter #[serde_as(as="DisplayFromStr")] /// id of the plan being used on the VM plan_id: i32, #[serde(rename="primaryip")] /// primary (floating) IP primary_ip: std::net::Ipv4Addr, #[serde(rename="privateip")] /// the private (non-floating) IP to connect between nodes private_ip: std::net::Ipv4Addr, #[serde_as(as="DisplayFromStr")] /// RAM meassured in MB ram: i32, /// the datacentre location the VM is running in region: LNRegion, #[serde_as(as="DisplayFromStr")] /// the storage size of the VM in GB storage: i32, #[serde_as(as="DisplayFromStr")] /// number of virtual CPU cores allocated to the VM vcpu: i32, #[serde_as(as="DisplayFromStr")] /// UUIDv4 of the VM vm_id: uuid::Uuid, } #[derive(Serialize, Deserialize, Debug)] #[serde(untagged)] pub enum IPAddress { V4(std::net::Ipv4Addr), V6(std::net::Ipv6Addr), } #[derive(Serialize, Deserialize, Debug)] // TODO: (de)serialize as "4"/"6" respectively pub enum IPAddressType { #[serde(rename="4")] V4, #[serde(rename="6")] V6, } #[derive(Serialize, Deserialize, Debug)] pub struct VMAddress { addr: IPAddress, #[serde(with="external")] external: bool, version: IPAddressType, reverse: Option, // optional rDNS; string is sufficient, since the server will not be able to set it to something invalid. } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct VMInfo { #[serde(rename="additionalip")] additional_ip: Vec, #[serde(rename="additionalprivateip")] additional_private_ip: Vec, addresses: Vec, /// a possibly empty string containing an error message error_detail: Option, /// some unkown string that doesn't seem to be a UUID... maybe the checksum? host_id: String, /// the name of the VM set at creation by the user hostname: String, /// the name of the image being used by the VM image: String, /// the primary, external IP of the VM ip: std::net::Ipv4Addr, /// a list of IPv6 addresses assigned to the VM ipv6: Vec, /// Login details from the VM; this could potentially be a bit more strict to support the storing of username and password separately. /// Also, it's optional. The server may not even report this value at all, not only give a blank one. Fair enough. Seems more secure. login_details: Option, /// the operating system (orignal image used to load the machine) os: String, // sufficiently vague #[serde(rename="privateip")] /// the primary private IP assigned to the machine private_ip: std::net::Ipv4Addr, /// security groups by id that the VM belongs to; should be a vec of i32, but it might take some custom implementations security_group_ids: Vec, /// security groups by name that the VM belongs to security_groups: Vec, #[serde(rename="securitygroups")] /// why is there a second one of these with a different name. This is stupid. security_groups2: Vec, /// an HTML status of the machine status: String, /// the color of the status; this could be stricter status_color: String, // could be more strict /// the non-HTML status, this could potentially be more strict status_nohtml: String, /// A raw status-code string; this could for sure be more strict. status_raw: String, /// A possibly empty string, idk what task_state is tho task_state: String, // a possibly empty string /// A possibly empty string of attached volumes. volumes: String, } #[derive(Serialize, Deserialize, Debug)] pub struct VMInfoResponse { extra: VMInfoExtra, info: VMInfo, #[serde(with="success")] success: bool, } #[derive(Serialize, Deserialize, Debug)] pub enum LNPlanCategory { #[serde(rename="Compute-Optimized")] ComputeOptimized, #[serde(rename="General Purpose")] GeneralPurpose, #[serde(rename="Memory-Optimized")] MemoryOptimized, #[serde(rename="SSD-Cached High-Memory")] SSDCacheHighMemory, #[serde(rename="SSD-Cached Standard")] SSDCacheStandard, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "lowercase")] pub enum LNRegion { Montreal, Roubaix, Toronto } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct LNPlan { #[serde_as(as="DisplayFromStr")] all_regions: i32, // may need to be more strict? #[serde_as(as="DisplayFromStr")] bandwidth: i32, // in Mbps category: LNPlanCategory, // could be more strict, "General Purpose", "Compute Optimized", "RAM Optimized" #[serde_as(as="DisplayFromStr")] cpu_points: f32, // no idea what this meas name: String, // plan name, this could potentially be strictly typed as the types of plans don't often change "s.half", "s.1", "m.1", "m.2", etc. #[serde_as(as="DisplayFromStr")] plan_id: i32, // can be strictly typed, if needed #[serde_as(as="DisplayFromStr")] price: f32, // up to 7 decmial points (f32), and this is the number of US dollars per hour price_monthly_nice: String, // instead of calculating it on your own, this provides a nice reading of the price for clients price_nice: String, // same as above, but for the hour #[serde_as(as="DisplayFromStr")] ram: i32, // in MB regions: Vec, // list of regions by name regions_nice: String, // list of regions concatonated with commans #[serde_as(as="DisplayFromStr")] storage: i32, // per GB #[serde_as(as="DisplayFromStr")] vcpu: i32, // number of vCPU cores } #[derive(Serialize, Deserialize, Debug)] pub struct PlanListResponse { plans: Vec, #[serde(with="success")] success: bool, // should be more strict: "yes" or "no" as options } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct LNImage { #[serde_as(as="DisplayFromStr")] image_id: i32, // id of the image name: String, // set by the user or LunaNode region: LNRegion, status: String, // should be stricter, at least "active", "inactive"(?) } #[derive(Serialize, Deserialize, Debug)] pub struct LNImageDetails { cache_mode: String, // should be stricter, at least "writeback", checksum: String, // should be stricter, to check of checksum type disk_format: String, // should be stricter, at least "iso" hw_disk_bus: String, // should be stricter, at least "ide", hw_video_model: String, // could, in theory, be stricter, at least: "cirrus", hw_vif_model: String, // appropriately vague #[serde(with="success")] is_read_only: bool, // "yes"/"no" libvrt_cpu_mode: String, // should be stricter, at least: "host-model", metadata: Vec<()>, // vec of what? name: String, // sufficiently vague region: LNRegion, // sufficiently typed size: i32, // in MB, maybe? status: String, // should be stricter, at least: "active", time_created: String, // should be a datetime } #[derive(Serialize, Deserialize, Debug)] pub struct LNImageDetailResponse { #[serde(with="success")] success: bool, details: LNImageDetails, } #[derive(Serialize, Deserialize, Debug)] pub struct LNImageListResponse { images: Vec, } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct BillingCreditResponse { /// Money left in the account in USD #[serde_as(as="DisplayFromStr")] credit: f32, #[serde(with="success")] success: bool, // this should be stricter } #[derive(Serialize, Deserialize, Debug)] pub struct LNErrorResponse { #[serde(with="success")] success: bool, error: String, // proper type, full accoutnign of the error from the API } impl std::fmt::Display for LNErrorResponse { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "An API error has occured! {}", self.error) } } impl std::error::Error for LNErrorResponse {} #[derive(Debug)] pub enum LNError { RequestError(ureq::Error), DeserializationError(std::io::Error), TimeError(std::time::SystemTimeError), LunaNodeError(LNErrorResponse), SerdeError(serde_json::Error, String), // the serde error, accompanied by a raw string GetFuckedError, } impl std::fmt::Display for LNError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "An error has occured! {}", self) } } impl std::error::Error for LNError {}