description="Tired of using a web interface for your VPS, Email, and domain provider? Check out this tool! It's a CLI tool for Lunanode: A Canadian VPS hosting company."
license="AGPL-3.0-only"
readme="README.md"
repository="https://git.tait.tech/tait/lunanode/"
keywords=["vps","cli","email","domain","hosting"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
This is an API to work with the LunaNode's OpenStack-compatible [citation needed] API.
This crate currently covers only the most basic functionality from the Lunanode API. Currently 33/98 (34%) API calls.
If you'd like to contribute additional functionality, please feel free to make an MR.
* VM [8/21]
* List [X]
* Start [X]
* Stop [X]
* Reboot [X]
* Diskswap [ ]
* Rescue [ ]
* Shelve [ ]
* Unshelve [ ]
* Delete [ ]
* Info [X]
* Reimage [ ]
* Resize [ ]
* VNC [ ]
* IP [1/3]
* Floaitng IP Add [ ]
* Floating IP Delete [ ]
* IP List [X]
* IP Add [X]
* IP Delete [X]
* Security group Add [ ]
* Security group Delete [ ]
* Image [4/7]
* Fetch [ ]
* List [X]
* Details [X]
* Delete [X]
* Replicate [X]
* Rename [ ]
* Retrieve [ ]
* Volume [1/12]
* Create [ ]
* List [X]
* Info [ ]
* Attach [ ]
* Detach [ ]
* Extend [ ]
* Rename [ ]
* Delete [ ]
* Snapshot [0/4]
* Create [ ]
* List [ ]
* Replicate [ ]
* Delete [ ]
* Floating IP [1/3]
* List [X]
* Add [ ]
* Delete [ ]
* Network [1/3]
* List [X]
* Add [ ]
* Delete [ ]
* Security Group [0/7]
* List [ ]
* Create [ ]
* Delete [ ]
* Rename [ ]
* Rule List [ ]
* Rule Insert [ ]
* Rule Delete [ ]
* Script [0/5]
* List [ ]
* Get [ ]
* Create [ ]
* Update [ ]
* Delete [ ]
* SSH Key [1/3]
* List [X]
* Add [ ]
* Remove [ ]
* Plan [1/1]
* List [X]
* Region [1/1]
* List [X]
* Monitor [3/10]
* Check [2/4]
* List [X]
* Types [X]
* Add [ ]
* Remove [ ]
* Contact [1/4]
* List [X]
* Add [ ]
* Remove [ ]
* Alert [0/3]
* List [ ]
* Add [ ]
* Remove [ ]
* Email [6/14]
* Usage [X]
* Domain [1/5]
* List [X]
* Add [ ]
* Remove [ ]
* DKIM [0/2]
* Set [ ]
* Unset [ ]
* User [4/4]
* List [X]
* Add [X]
* Remove [X]
* Set Password [X]
* Alias [0/3]
* List [ ]
* Add [ ]
* Remove [ ]
* DNS [6/10]
* Zone [2/3]
* List [X]
* Add [X]
* Remove [ ]
* Record [1/3]
* List [X]
* Add [ ]
* Remove [ ]
* Dyn [3/4]
* List [X]
* Add [X]
* Update [ ]
* Remove [X]
* Billing [1/1]
* Credit [X]
## Installation
@ -8,7 +133,7 @@ This is an API to work with the LunaNode's OpenStack-compatible [citation needed
## API Keys
Add the `LUNANODE_API_KEY` and `LUNANODE_KEY_ID` to your environment variables.
Add the `LUNANODE_API_KEY`, `LUNANODE_KEY_ID` and `LUNANODE_API_PARTIALKEY` (the first 64 chars of `LUNANODE_API_KEY`) to your environment variables.
## Usage
@ -35,13 +160,15 @@ See section help for more details, i.e.: `lunanode image help`
* [ ] Simplify code so there is less boilerplate. In particular, writing out every enum variant is pretty annoying when I need the same info out of them either way.
* [ ] Tests!
* [ ] Create more detailed documentation. `#[deny(missing_docs)]` is on, but some of the decisions in the code aren't explained very well.
* [ ] Cover EVERY API endpoint.
* [ ] Stricter typing. Certain attributes, even though they are recieved as Strings, should really be some kind of `enum`. Make every non-String type some kind of enum.
* [ ] UUIDs should be UUID types and not string. The only library I could find to do this, Uuid, seems to serialize them without the dashes, which screws up Lunanode... annoyingly.
* [ ] Subnets should be more strictly types. They should always be an IP/subnet combo as two fields (std::net::Ipv4Addr, i32(0..32)).
* [ ] Automatically create IDs from names, or other identifiying info instead of using the RESP API's id system.
* For example: `email user add tait@tait.tech` should automatically fetch the domain ID required to make this call normally: `email user add DOMAIN_ID tait@tait.tech`.
* Another example: `vm shutdown my_webserver` would automatically exapnd to find the VM with the name my_webserver and have it shutdown expanding the name into the UUID: `vm shutdown ffff-ffff-ffff-ffffffffffffffff`.
## Support This Project
* [Support me on librapay](https://liberapay.com/tait/).
/// All possible commands that may be used at the command line.
pubenumArgs{
#[clap(subcommand)]
/// See `lunanode image help`
@ -229,7 +268,7 @@ pub enum Args {
}
/// Specifies an internal function which can only be called on LunaNodeRequests whoes response type implements Debug. This could be set in the structure itself, and probably should be.
/// The data string for the record. This is usually a FQDN, followed by a period. If you wanted to set a record for `dev.example.org`, you would put `dev.example.com.` in this field.
/// The result of requesting a list of VMs on the user's Lunanode account.
/// See also: [`crate::requests::VmListRequest`]
pubstructVmListResponse{
/// A list of VMs.
vms: Vec<VirtualMachine>,
#[serde(with="success")]
success: bool,// should be more strict "yes" or "no" as optiobs
}
implToStringforVMListResponse{
fnto_string(&self)-> String{
"N/A".to_string()
}
}
implLunaNodeResponseforVMListResponse{}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all="lowercase")]
/// An enum defining whether an IP is attached to a VM or is simply allocated without being
/// attached to any VM in particular.
/// See also: [`FloatingIp`]
enumAttachmentType{
/// An unattached floating IP.
Unattached,
/// An attached floating IP.
Vm,
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
/// Detailed information about a floating IP
structFloatingIp{
/// A String with a VM UUID if attached, otherwise None.
attached_id: Option<String>,
/// A String with the VM's name if attached, otherwise None.
attached_name: Option<String>,
/// Vm/Unattached
attached_type: AttachmentType,
/// The hostname of the floating IP address, usually something like
/// xxx-xxx-xxx-xxx.rdns.lunanode.com
hostname: String,
/// The IP address of the floating IP.
ip: std::net::Ipv4Addr,
/// The region in which the IP address has been allocated.
region: LNRegion,
/// The rDNS domain, if specified, otherwise, None.
reverse: Option<String>,
#[serde_as(as="DisplayFromStr")]
/// The last time the record was updated.
time_updated: chrono::DateTime<chrono::Utc>,
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
/// Extra detailed information for a VM.
pubstructVMInfoExtra{
#[serde_as(as="DisplayFromStr")]
/// bandwidth allowed over a month in GB
@ -126,36 +137,50 @@ pub struct VMInfoExtra {
#[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.
pubenumIPAddress{
/// IPv4 variant
V4(std::net::Ipv4Addr),
/// IPv6 variant
V6(std::net::Ipv6Addr),
}
#[derive(Serialize, Deserialize, Debug)]
/// An IP address type enum, specifying either V4 or V6.
pubenumIPAddressType{
#[serde(rename="4")]
/// IPv4 variant
V4,
#[serde(rename="6")]
/// IPv6 variant
V6,
}
#[derive(Serialize, Deserialize, Debug)]
/// VM IP address
pubstructVMAddress{
/// The IP address of the VM.
addr: IPAddress,
#[serde(with="external")]
/// Whether the IP is an internal or external kind.
external: bool,
/// The version of IP address used.
version: IPAddressType,
#[serde(rename="reverse")]
/// The reverse DNS assigned to the VM. This is optional.
/// The reverse DNS assigned to the VM, if specified. Otherwise, None.
rdns: Option<String>,
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
/// Detailed VM info.
pubstructVMInfo{
#[serde(rename="additionalip")]
/// A list of additional IPs (which are not the primary IP) which have been assigned to the VM.
additional_ip: Vec<VMAddress>,
#[serde(rename="additionalprivateip")]
/// Any additional private IPs assigned to the VM.
additional_private_ip: Vec<VMAddress>,
/// A list of addresses inherently part of the VM. Think of a primary private IP, or where
/// outgoing internet traffic is routed.
addresses: Vec<VMAddress>,
/// a possibly empty string containing an error message
error_detail: Option<String>,
@ -198,80 +223,114 @@ pub struct VMInfo {
volumes: String,
}
#[derive(Serialize, Deserialize, Debug)]
#[lunanode_response]
/// The result from a [`crate::requests::VmInfoRequest`].
/// The type of contact as stated by lunanode as possible options. This type helps decide how to
/// use the contact in the case of a monitor trip.
/// See also: [`Contact`]
pubenumContactType{
/// A (presumably) valid email.
Email,
/// A (presumably) valid SMS-captable phone or VOIP number.
Sms,
/// A (presumably) valid voice-captable phone or VOIP number.
Voice,
/// A (presumably) valid HTTP(S) URL.
Http,
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
/// A contact for monitoring purposes.
/// See also: [`Contacts`], [`MonitorContactListResponse`]
pubstructContact{
#[serde_as(as="DisplayFromStr")]
id: i32,
@ -570,6 +745,8 @@ pub struct Contact {
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
/// Possible values for contacts listed in Lunanode. This is required because what the API sends
/// back an empty list when there are no contacts, but a hashmap if there is one or more contacts.
pubenumContacts{
/// Need an empty vec of some kind to hold the blank value, which appears as [].
Empty(Vec<String>),
@ -578,6 +755,8 @@ pub enum Contacts {
}
#[lunanode_response]
/// The result of requesting a contact list for monitoring purposes.
/// See also: [`crate::requests::MonitorContactListRequest`]
pubstructMonitorContactListResponse{
/// A list of contacts in your LunaNode account. This should be deserialized as Option<HashMap<String, Contact>>, but I haven't gotten around to serializing this in that way. TODO.
/// A generic function to recieve a specific relsponse type from the server.
/// The Self::response type is the type you're expecting. In the case that this type is incorrect, or the server returns bad data, you will receive an LNError::SerdeError(serde::Error, String), with the String being a raw response from the server.
/// You may also recieve any other error defined in LNError, including timezone errors due to not being able to create a nonce value for the request.
/// The Self::response type is the type you're expecting. In the case that this type is incorrect, or the server returns bad data, you will receive an LunaNodeError::SerdeError(serde::Error, String), with the String being a raw response from the server.
/// You may also recieve any other error defined in LunaNodeError, including timezone errors due to not being able to create a nonce value for the request.