use ureq; use crate::success; use crate::external; use crate::types::{ LunaNodeResponse, LunaNodeRequest, }; use lunanode_macros::lunanode_response; use serde_json; use serde::{Serialize, Deserialize}; use serde_with::{ DisplayFromStr, serde_as, }; use uuid; #[serde_as] #[derive(Serialize, Deserialize, PartialEq, Debug)] /// Defines a VM (used for requests using the VM section) pub struct VirtualMachine { /// the UUID of the VM vm_id: String, // should be UUIDv4 /// the name of the VM set by the user name: String, // the name set by the user #[serde_as(as="DisplayFromStr")] /// The plan ID, cross-reference with /plan/list/ plan_id: i32, // should be more strict /// The hostname set by the user hostname: String, // the hostname set by the user #[serde(rename="primaryip")] /// The primary (public) IP address of the VM. primary_ip: std::net::Ipv4Addr, #[serde(rename="privateip")] /// The private IP address of the VM. private_ip: std::net::Ipv4Addr, #[serde_as(as="DisplayFromStr")] /// RAM of the VM in GB ram: i32, // in GB, may need to be a float #[serde_as(as="DisplayFromStr")] /// The number of virtual CPU cores vcpu: i32, // CPU cores #[serde_as(as="DisplayFromStr")] /// The storage amount of the VM in GB storage: i32, // in GB #[serde_as(as="DisplayFromStr")] /// bandwidth in GB/month bandwidth: i32, // in GB/month /// The region where the VM is located region: LNRegion, // could be more strict /// The status, which should be more strict than a String. 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 } impl ToString for VMListResponse { fn to_string(&self) -> String { "N/A".to_string() } } impl LunaNodeResponse for VMListResponse {} #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all="lowercase")] enum AttachmentType { Unattached, Vm, } #[serde_as] #[derive(Serialize, Deserialize, Debug)] struct FloatingIp { attached_id: Option, attached_name: Option, attached_type: AttachmentType, hostname: String, ip: std::net::Ipv4Addr, region: LNRegion, reverse: Option, #[serde_as(as="DisplayFromStr")] time_updated: chrono::DateTime, } #[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: String, } #[derive(Serialize, Deserialize, Debug)] #[serde(untagged)] /// A generic IP address type, with two variants: V4, and V6. This is used for generic types returned from the API. For example, a list of IP addresses without the IP type specified. pub enum IPAddress { V4(std::net::Ipv4Addr), V6(std::net::Ipv6Addr), } #[derive(Serialize, Deserialize, Debug)] 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, #[serde(rename="reverse")] /// The reverse DNS assigned to the VM. This is optional. rdns: Option, } #[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, Hash, Eq, PartialEq)] #[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, #[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 } #[lunanode_response] pub struct PlanListResponse { plans: Vec, } #[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"(?) } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct Volume { #[serde_as(as="DisplayFromStr")] /// The personal ID used for the volume. id: i32, #[serde_as(as="DisplayFromStr")] /// the UUID for the volume identification: String, /// the name set by the user name: String, /// The region where the volume is located. region: LNRegion, /// The size of the volume, in GB #[serde_as(as="DisplayFromStr")] size: i32, /// The status of the volume; this should be more string, but for now at least "active". status: String, #[serde_as(as="DisplayFromStr")] /// a datetime (explicitly set to UTC) of when the volume was created time_created: chrono::DateTime, #[serde_as(as="DisplayFromStr")] /// ID of the user who created the volume user_id: i32, } #[serde_as] #[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", #[serde_as(as="DisplayFromStr")] /// An (explicitly UTC) datetime of when the VM was created time_created: chrono::DateTime, // should be a datetime } #[lunanode_response] pub struct LNImageDetailResponse { details: LNImageDetails, } #[lunanode_response] pub struct LNImageListResponse { images: Vec, } #[lunanode_response] pub struct BillingCreditResponse { /// Money left in the account in USD #[serde_as(as="DisplayFromStr")] credit: f32, } #[lunanode_response] pub struct VolumeListResponse { volumes: Vec, } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct Network { /// The name set by the user when creating the network. name: String, #[serde_as(as="DisplayFromStr")] net_id: i32, /// The network subnet. This should be strictly typed as an IP address w/ a subnet attached. TODO: subnet type subnet: String, } #[lunanode_response] pub struct NetworkListResponse { networks: Vec, } #[lunanode_response] pub struct FloatingIpListResponse { ips: Vec, } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct Zone { #[serde_as(as="DisplayFromStr")] /// The lunanode id of the zone. id: i32, /// The domain (or subdomain) being managed by the zone. name: String, /// The default TTL used for this zone. This can be overridden manually when specifying new records. #[serde_as(as="DisplayFromStr")] ttl: i32, } #[lunanode_response] pub struct ZoneListResponse { zones: std::collections::HashMap, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename="UPPERCASE")] enum RecordType { A, AAAA, CNAME, ALIAS, MX, NS, TXT, SPF, SRV } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct Record { /// The record id #[serde_as(as="DisplayFromStr")] id: i32, /// The record type, one of A, AAAA, CNAME, ALIAS, MX, NS, TXT, SPF, SRV #[serde(rename="type")] record_type: RecordType, /// The name of the record (for example, if setting an ip address for example.org, this field would show: example.org." name: String, /// The data assigned to this record. It shoudl always be a IPv4/v6 address, unless it's an NS data: String, #[serde_as(as="DisplayFromStr")] /// The auxiliary value, only used on some record types. aux: i32, #[serde_as(as="DisplayFromStr")] /// The TTL for the record. ttl: i32, /// Unknown value policy: String, /// The region the record applies to, if specificed. Otherwise None. Currently a String due to type issues. region: String, #[serde(rename="regiongroup")] /// This value can be any region group defined by the user or auto. This could be stricter. region_group: String, /// This value can be either Canada or France, and it should be stricter. country: String, /// The continent as set by the user for this record. May be none or auto. continent: String, #[serde_as(as="DisplayFromStr")] /// Is the record for everywhere (global); this should be a bool global: i32, #[serde_as(as="DisplayFromStr")] /// The latitude of the record, if given. latitude: f32, #[serde_as(as="DisplayFromStr")] /// Longitude of a record, if given. longitude: f32, #[serde(with="external")] /// Is active in inactive record. status: bool, #[serde_as(as="DisplayFromStr")] /// Monitor id, if given. monitor_id: i32, /// Optional check name? check_name: Option, /// The "nice" version of the status as a string. status_nice: String, } #[lunanode_response] pub struct RecordListResponse { records: std::collections::HashMap, } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct DynRecord { #[serde_as(as="DisplayFromStr")] id: i32, name: String, ip: IPAddress, } #[lunanode_response] pub struct DynListResponse { dyns: std::collections::HashMap, } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct SshKey { #[serde_as(as="DisplayFromStr")] id: i32, /// Name of key, as specified by the user. name: String, /// The value of the SSH key; this is not more strict, only because the value is meant to be literal and not a "structured" representation of an SSH key. In addition, this would take a lot of extra boilerplate. value: String, } #[lunanode_response] pub struct RegionListResponse { regions: std::collections::HashMap, } #[lunanode_response] pub struct SshKeyResponse { #[serde(flatten)] /// List of keys stored in the LunaNode cloud. The String is just an index that I can't get to flatten :grumpy programmer noises: keys: std::collections::HashMap, } #[lunanode_response] pub struct EmailUsageResponse { #[serde_as(as="DisplayFromStr")] domains: i32, /// Usage of email in GB, this should be an i32. storage: String, /// Usage of email in GB with GB appended to the end of the string, storage_nice: String, #[serde_as(as="DisplayFromStr")] messages: i32, } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct EmailDomainInfo { #[serde_as(as="DisplayFromStr")] id: i32, name: String, storage: String, storage_nice: String, } #[lunanode_response] pub struct EmailDomainListResponse { #[serde(flatten)] domains: std::collections::HashMap, } #[lunanode_response] pub struct MonitorCheckListResponse { /// This is not done yet. A Check type will be required. TODO checks: Vec, } #[derive(Serialize, Deserialize, Debug)] #[serde(untagged)] pub enum ParamType { Single(String), Options(std::collections::HashMap), } #[derive(Serialize, Deserialize, Debug)] pub struct Param { #[serde(with="external")] required: bool, r#type: ParamType, name: String, placeholder: Option, } #[derive(Serialize, Deserialize, Debug)] pub struct MonitorTypes { /// The name of the monitor type. name: String, /// The short name of the monitor type. short_name: String, params: std::collections::HashMap } #[lunanode_response] pub struct MonitorCheckTypesResponse { types: std::collections::HashMap, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all="lowercase")] pub enum ContactType { Email, Sms, Voice, Http, } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct Contact { #[serde_as(as="DisplayFromStr")] id: i32, r#type: ContactType, data: String, } #[derive(Serialize, Deserialize, Debug)] #[serde(untagged)] pub enum Contacts { /// Need an empty vec of some kind to hold the blank value, which appears as []. Empty(Vec), /// Note that this HashMap should be indexed by i32, not String. This is a limitation of serde as far as I can tell. Populated(std::collections::HashMap), } #[lunanode_response] pub struct MonitorContactListResponse { /// A list of contacts in your LunaNode account. This should be deserialized as Option>, but I haven't gotten around to serializing this in that way. TODO. contacts: Contacts, } #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct EmailUser { /// The user's ID. #[serde_as(as="DisplayFromStr")] id: i32, /// The user's email. This may be more strict in the future. email: String, #[serde_as(as="DisplayFromStr")] /// The user's storage usage in MB. storage: i32, /// The user's storage usage, with an "MB" or "GB" designation afterwards. storage_nice: String, } #[lunanode_response] pub struct EmailUserListResponse { #[serde(flatten)] users: std::collections::HashMap, } #[lunanode_response] pub struct VmStartResponse {} #[lunanode_response] pub struct VmStopResponse {} #[lunanode_response] pub struct VmRebootResponse {} #[lunanode_response] pub struct EmailUserAddResponse {} #[lunanode_response] pub struct EmailUserRemoveResponse {} #[lunanode_response] pub struct EmailUserSetPasswordResponse {} #[serde_as] #[derive(Serialize, Deserialize, Debug)] pub struct VmIp { floating: std::net::Ipv4Addr, /// A network UUID network: String, #[serde_as(as="DisplayFromStr")] network_id: i32, /// Port UUID? What is this? port: String, /// This is an ip/subnet pair, so it could/should be stricter. subnet: String, } #[lunanode_response] pub struct VmIpListResponse { ips: std::collections::HashMap, } #[lunanode_response] pub struct DynRemoveResponse {} #[lunanode_response] pub struct DynAddResponse {}