Added endpoints, tests and updated dependencies

1. Added interface/wireguard, ip/address.
2. Updated dependencies.
3. Added tests and some comments
This commit is contained in:
Ishan Jain 2022-04-29 17:35:49 +05:30
parent 42e5144e5f
commit f39ed466f6
16 changed files with 355 additions and 88 deletions

41
Cargo.lock generated
View File

@ -196,9 +196,9 @@ checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "http"
version = "0.2.6"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb"
dependencies = [
"bytes",
"fnv",
@ -218,9 +218,9 @@ dependencies = [
[[package]]
name = "httparse"
version = "1.7.0"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6330e8a36bd8c859f3fa6d9382911fbb7147ec39807f63b923933a247240b9ba"
checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
[[package]]
name = "httpdate"
@ -337,9 +337,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.123"
version = "0.2.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
[[package]]
name = "log"
@ -369,7 +369,6 @@ dependencies = [
"base64",
"reqwest",
"serde",
"serde_derive",
"thiserror",
"tokio",
"tokio-test",
@ -479,9 +478,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pin-project-lite"
version = "0.2.8"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
@ -662,6 +661,9 @@ name = "serde"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
@ -721,9 +723,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "syn"
version = "1.0.91"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52"
dependencies = [
"proc-macro2",
"quote",
@ -766,9 +768,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.5.1"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
@ -781,14 +783,15 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.17.0"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"once_cell",
"pin-project-lite",
"socket2",
"tokio-macros",
@ -885,9 +888,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.20"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b"
checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c"
dependencies = [
"proc-macro2",
"quote",
@ -911,9 +914,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "unicode-bidi"
version = "0.3.7"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-normalization"

View File

@ -13,7 +13,6 @@ tokio = { version = "1.17.0", features = ["macros"] }
[dependencies]
base64 = "0.13.0"
reqwest = { version = "0.11.4", features = ["rustls-tls", "serde_json", "json"] }
serde = "1.0.130"
serde_derive = "1.0.136"
serde = { version = "1.0.130", features = ["derive"] }
thiserror = "1.0.30"
url = "2.2.2"

View File

@ -3,6 +3,7 @@ use reqwest::{
Method, Request, Url,
};
use serde::de::DeserializeOwned;
use std::num::ParseFloatError;
use thiserror::Error;
pub struct Client {
@ -67,4 +68,7 @@ pub enum ClientError {
#[error(transparent)]
ReqwestError(#[from] reqwest::Error),
#[error(transparent)]
ParseFloatError(#[from] ParseFloatError),
}

View File

@ -1,13 +0,0 @@
use crate::{interface::types::Interface, Client, ClientError};
pub async fn list(client: &mut Client) -> Result<Vec<Interface>, ClientError> {
let url = super::BASE;
client.execute_get::<Vec<Interface>>(&url).await
}
pub async fn get(client: &mut Client, ifid: &str) -> Result<Interface, ClientError> {
let url = format!("{}/{}", super::BASE, ifid);
client.execute_get::<Interface>(&url).await
}

View File

@ -1,7 +1,17 @@
mod interface;
mod types;
pub mod wireguard;
pub use interface::*;
pub use types::*;
pub use crate::interface::types::{Interface, InterfaceType, Mtu};
use crate::{Client, ClientError};
const BASE: &str = "rest/interface";
pub async fn list(client: &mut Client) -> Result<Vec<Interface>, ClientError> {
client.execute_get::<Vec<Interface>>(BASE).await
}
pub async fn get(client: &mut Client, ifid: &str) -> Result<Interface, ClientError> {
let url = format!("{}/{}", BASE, ifid);
client.execute_get::<Interface>(&url).await
}

View File

@ -1,58 +1,143 @@
use serde_derive::Deserialize;
use serde_derive::Serialize;
use crate::serde_helpers::{
deserialize_bool, deserialize_u16, deserialize_u64, maybe_deserialize_u16,
};
use serde::{
de::{Error, Unexpected},
Deserialize, Deserializer, Serialize,
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum InterfaceType {
#[serde(rename = "vlan")]
VLAN,
#[serde(rename = "ether")]
Ethernet,
#[serde(rename = "bridge")]
Bridge,
#[serde(rename = "wg")]
Wireguard,
#[serde(rename = "pppoe-out")]
PPPoE,
}
impl Default for InterfaceType {
fn default() -> Self {
Self::Ethernet
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Mtu {
#[serde(rename = "auto")]
Auto,
Value(u16),
}
impl Default for Mtu {
fn default() -> Self {
Self::Auto
}
}
pub fn maybe_deserialize_mtu<'de, D>(deserializer: D) -> Result<Option<Mtu>, D::Error>
where
D: Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(deserializer)?;
if s == "auto" {
return Ok(Some(Mtu::Auto));
}
match s.parse::<u16>() {
Ok(v) => Ok(Some(Mtu::Value(v))),
Err(_) => Err(Error::invalid_value(Unexpected::Str(s), &"an integer")),
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Interface {
#[serde(rename = ".id")]
pub id: String,
#[serde(rename = "actual-mtu")]
pub actual_mtu: Option<String>,
#[serde(default, rename = "actual-mtu", deserialize_with = "deserialize_u16")]
pub actual_mtu: u16,
pub comment: Option<String>,
#[serde(rename = "default-name")]
pub default_name: Option<String>,
pub disabled: String,
#[serde(rename = "fp-rx-byte")]
pub fp_rx_byte: String,
#[serde(rename = "fp-rx-packet")]
pub fp_rx_packet: String,
#[serde(rename = "fp-tx-byte")]
pub fp_tx_byte: String,
#[serde(rename = "fp-tx-packet")]
pub fp_tx_packet: String,
pub l2mtu: Option<String>,
#[serde(deserialize_with = "deserialize_bool")]
pub disabled: bool,
#[serde(default, rename = "fp-rx-byte", deserialize_with = "deserialize_u64")]
pub fp_rx_byte: u64,
#[serde(default, rename = "fp-rx-packet", deserialize_with = "deserialize_u64")]
pub fp_rx_packet: u64,
#[serde(default, rename = "fp-tx-byte", deserialize_with = "deserialize_u64")]
pub fp_tx_byte: u64,
#[serde(default, rename = "fp-tx-packet", deserialize_with = "deserialize_u64")]
pub fp_tx_packet: u64,
#[serde(default, deserialize_with = "deserialize_u16")]
pub l2mtu: u16,
#[serde(rename = "last-link-up-time")]
pub last_link_up_time: Option<String>,
#[serde(rename = "link-downs")]
pub link_downs: String,
#[serde(default, rename = "link-downs", deserialize_with = "deserialize_u16")]
pub link_downs: u16,
#[serde(rename = "mac-address")]
pub mac_address: Option<String>,
#[serde(rename = "max-l2mtu")]
pub max_l2mtu: Option<String>,
pub mtu: Option<String>,
#[serde(
default,
rename = "max-l2mtu",
deserialize_with = "maybe_deserialize_u16"
)]
pub max_l2mtu: Option<u16>,
#[serde(default, deserialize_with = "maybe_deserialize_mtu")]
pub mtu: Option<Mtu>,
pub name: String,
pub running: String,
#[serde(rename = "rx-byte")]
pub rx_byte: String,
#[serde(rename = "rx-drop")]
pub rx_drop: String,
#[serde(rename = "rx-error")]
pub rx_error: String,
#[serde(rename = "rx-packet")]
pub rx_packet: String,
#[serde(deserialize_with = "deserialize_bool")]
pub running: bool,
#[serde(rename = "rx-byte", deserialize_with = "deserialize_u64")]
pub rx_byte: u64,
#[serde(rename = "rx-drop", deserialize_with = "deserialize_u64")]
pub rx_drop: u64,
#[serde(rename = "rx-error", deserialize_with = "deserialize_u64")]
pub rx_error: u64,
#[serde(rename = "rx-packet", deserialize_with = "deserialize_u64")]
pub rx_packet: u64,
pub slave: Option<String>,
#[serde(rename = "tx-byte")]
pub tx_byte: String,
#[serde(rename = "tx-drop")]
pub tx_drop: String,
#[serde(rename = "tx-error")]
pub tx_error: String,
#[serde(rename = "tx-packet")]
pub tx_packet: String,
#[serde(rename = "tx-queue-drop")]
pub tx_queue_drop: String,
#[serde(rename = "tx-byte", deserialize_with = "deserialize_u64")]
pub tx_byte: u64,
#[serde(rename = "tx-drop", deserialize_with = "deserialize_u64")]
pub tx_drop: u64,
#[serde(rename = "tx-error", deserialize_with = "deserialize_u64")]
pub tx_error: u64,
#[serde(rename = "tx-packet", deserialize_with = "deserialize_u64")]
pub tx_packet: u64,
#[serde(rename = "tx-queue-drop", deserialize_with = "deserialize_u64")]
pub tx_queue_drop: u64,
#[serde(rename = "type")]
pub type_field: String,
pub type_field: InterfaceType,
#[serde(rename = "last-link-down-time")]
pub last_link_down_time: Option<String>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WireguardInterface {
#[serde(rename = ".id")]
pub id: String,
pub comment: String,
#[serde(deserialize_with = "deserialize_bool")]
pub disabled: bool,
#[serde(rename = "listen-port", deserialize_with = "deserialize_u16")]
pub listen_port: u16,
#[serde(deserialize_with = "deserialize_u16")]
pub mtu: u16,
pub name: String,
#[serde(rename = "private-key")]
pub private_key: String,
#[serde(rename = "public-key")]
pub public_key: String,
#[serde(deserialize_with = "deserialize_bool")]
pub running: bool,
}

View File

@ -0,0 +1,15 @@
use crate::{interface::types::WireguardInterface, Client, ClientError};
/// list all wireguard interfaces
pub async fn list(client: &mut Client) -> Result<Vec<WireguardInterface>, ClientError> {
let url = format!("{}/wireguard", super::BASE);
client.execute_get::<Vec<WireguardInterface>>(&url).await
}
/// get wireguard interface with the provided id
pub async fn get(client: &mut Client, wg_id: &str) -> Result<WireguardInterface, ClientError> {
let url = format!("{}/wireguard/{}", super::BASE, wg_id);
client.execute_get::<WireguardInterface>(&url).await
}

16
src/ip/address.rs Normal file
View File

@ -0,0 +1,16 @@
pub use crate::ip::types::Address;
use crate::{Client, ClientError};
/// list all configurd ipv4 addresses
pub async fn list(client: &mut Client) -> Result<Vec<Address>, ClientError> {
let url = format!("{}/address", super::BASE);
client.execute_get::<Vec<Address>>(&url).await
}
/// get details of a specific ipv4 address
pub async fn get(client: &mut Client, aid: &str) -> Result<Address, ClientError> {
let url = format!("{}/address/{}", super::BASE, aid);
client.execute_get::<Address>(&url).await
}

View File

@ -1,38 +1,42 @@
use crate::{
ip::types::{DhcpServer, Lease, Network},
Client, ClientError,
};
pub use crate::ip::types::{DhcpServer, Lease, Network};
use crate::{Client, ClientError};
/// list all dhcp servers
pub async fn list(client: &mut Client) -> Result<Vec<DhcpServer>, ClientError> {
let url = format!("{}/dhcp-server", super::BASE);
client.execute_get::<Vec<DhcpServer>>(&url).await
}
/// get details of a specific dhcp server. takes dhcp server name as input
pub async fn get(client: &mut Client, dhcp_server_id: &str) -> Result<DhcpServer, ClientError> {
let url = format!("{}/dhcp-server/{}", super::BASE, dhcp_server_id);
client.execute_get::<DhcpServer>(&url).await
}
/// list all networks
pub async fn list_network(client: &mut Client) -> Result<Vec<Network>, ClientError> {
let url = format!("{}/dhcp-server/network", super::BASE);
client.execute_get::<Vec<Network>>(&url).await
}
/// get details of a specific network
pub async fn get_network(client: &mut Client, nid: &str) -> Result<Network, ClientError> {
let url = format!("{}/dhcp-server/network/{}", super::BASE, nid);
client.execute_get::<Network>(&url).await
}
/// list all dhcp leases from all dhcp servers
pub async fn list_leases(client: &mut Client) -> Result<Vec<Lease>, ClientError> {
let url = format!("{}/dhcp-server/lease", super::BASE);
client.execute_get::<Vec<Lease>>(&url).await
}
/// get details of a specific lease
pub async fn get_lease(client: &mut Client, lease_id: &str) -> Result<Lease, ClientError> {
let url = format!("{}/dhcp-server/lease/{}", super::BASE, lease_id);

View File

@ -1,6 +1,5 @@
pub mod address;
pub mod dhcp_server;
mod types;
pub use types::*;
const BASE: &str = "rest/ip";

View File

@ -1,6 +1,5 @@
use crate::serde_helpers::deserialize_bool;
use serde_derive::Deserialize;
use serde_derive::Serialize;
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -88,3 +87,22 @@ pub struct Network {
#[serde(rename = "wins-server")]
pub wins_server: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Address {
#[serde(rename = ".id")]
pub id: String,
#[serde(rename = "actual-interface")]
pub actual_interface: String,
pub address: String,
pub comment: Option<String>,
#[serde(deserialize_with = "deserialize_bool")]
pub disabled: bool,
#[serde(deserialize_with = "deserialize_bool")]
pub dynamic: bool,
pub interface: String,
#[serde(deserialize_with = "deserialize_bool")]
pub invalid: bool,
pub network: String,
}

View File

@ -1,4 +1,7 @@
use serde::{de::Error, Deserialize, Deserializer};
use serde::{
de::{Error, Unexpected},
Deserialize, Deserializer,
};
pub fn deserialize_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
@ -12,3 +15,51 @@ where
_ => Err(Error::unknown_variant(s, &["true", "false"])),
}
}
pub fn deserialize_u16<'de, D>(deserializer: D) -> Result<u16, D::Error>
where
D: Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(deserializer)?;
match s.parse::<u16>() {
Ok(v) => Ok(v),
Err(_) => Err(Error::invalid_value(Unexpected::Str(s), &"an integer")),
}
}
pub fn deserialize_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(deserializer)?;
match s.parse::<u64>() {
Ok(v) => Ok(v),
Err(_) => Err(Error::invalid_value(Unexpected::Str(s), &"an integer")),
}
}
pub fn maybe_deserialize_u16<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
where
D: Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(deserializer)?;
match s.parse::<u16>() {
Ok(v) => Ok(Some(v)),
Err(_) => Err(Error::invalid_value(Unexpected::Str(s), &"an integer ")),
}
}
pub fn maybe_deserialize_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
where
D: Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(deserializer)?;
match s.parse::<u64>() {
Ok(v) => Ok(Some(v)),
Err(_) => Err(Error::invalid_value(Unexpected::Str(s), &"an integer")),
}
}

View File

@ -1,7 +1,31 @@
use crate::{system::types::Health, Client, ClientError};
pub use crate::system::types::Health;
use crate::{Client, ClientError};
/// health can be used to get all health parameters of the device
pub async fn health(client: &mut Client) -> Result<Vec<Health>, ClientError> {
let url = format!("{}/health", super::BASE);
client.execute_get::<Vec<Health>>(&url).await
}
/// voltage can be used to get device's voltag reading
pub async fn voltage(client: &mut Client) -> Result<f32, ClientError> {
let url = format!("{}/health/voltage", super::BASE);
let response = client.execute_get::<Health>(&url).await?;
response
.value
.parse::<f32>()
.map_err(ClientError::ParseFloatError)
}
/// temperature can be used to get device's temperature reading
pub async fn temperature(client: &mut Client) -> Result<String, ClientError> {
let url = format!("{}/health/temperature", super::BASE);
let mut response = client.execute_get::<Health>(&url).await?;
response.value.push_str(&response.type_field);
Ok(response.value)
}

View File

@ -1,5 +1,5 @@
use serde_derive::Deserialize;
use serde_derive::Serialize;
use serde::Deserialize;
use serde::Serialize;
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]

View File

@ -26,3 +26,27 @@ async fn get_interface() -> Result<(), ClientError> {
Ok(())
}
#[tokio::test]
async fn list_wireguard_interfaces() -> Result<(), ClientError> {
let base = Url::parse("https://10.0.10.1")?;
let mut client = Client::new(base, "admin".to_string(), "ifd783far".to_string(), true)
.expect("error in creating client");
let _ = mikrotik::interface::wireguard::list(&mut client).await?;
Ok(())
}
#[tokio::test]
async fn get_wireguard_interface() -> Result<(), ClientError> {
let base = Url::parse("https://10.0.10.1")?;
let mut client = Client::new(base, "admin".to_string(), "ifd783far".to_string(), true)
.expect("error in creating client");
let response = mikrotik::interface::wireguard::list(&mut client).await?;
let _ = mikrotik::interface::wireguard::get(&mut client, &response[0].id).await?;
Ok(())
}

View File

@ -82,3 +82,31 @@ async fn get_lease() -> Result<(), ClientError> {
Ok(())
}
#[tokio::test]
async fn list_address() -> Result<(), ClientError> {
let base = Url::parse("https://10.0.10.1")?;
let mut client = Client::new(base, "admin".to_string(), "ifd783far".to_string(), true)
.expect("error in creating client");
let response = mikrotik::ip::address::list(&mut client).await?;
println!("{:?}", response);
Ok(())
}
#[tokio::test]
async fn get_address() -> Result<(), ClientError> {
let base = Url::parse("https://10.0.10.1")?;
let mut client = Client::new(base, "admin".to_string(), "ifd783far".to_string(), true)
.expect("error in creating client");
let response = mikrotik::ip::address::list(&mut client).await?;
let response = mikrotik::ip::address::get(&mut client, &response[0].id).await?;
println!("{:?}", response);
Ok(())
}