Compare commits
No commits in common. "e4df2e507551ac13d5146ae2961ba73d889ba2dd" and "6da77159b1599ef8b3ed00c0563ef346e241ba39" have entirely different histories.
e4df2e5075
...
6da77159b1
24 changed files with 302 additions and 818 deletions
347
Cargo.lock
generated
347
Cargo.lock
generated
|
@ -108,50 +108,6 @@ version = "1.0.86"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
|
||||
[[package]]
|
||||
name = "askama"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28"
|
||||
dependencies = [
|
||||
"askama_derive",
|
||||
"askama_escape",
|
||||
"humansize",
|
||||
"num-traits",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_derive"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83"
|
||||
dependencies = [
|
||||
"askama_parser",
|
||||
"basic-toml",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_escape"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
|
||||
|
||||
[[package]]
|
||||
name = "askama_parser"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "3.4.0"
|
||||
|
@ -165,13 +121,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.81"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 1.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -198,61 +154,6 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.1",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"sync_wrapper 0.1.2",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.73"
|
||||
|
@ -286,15 +187,6 @@ version = "1.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
|
@ -656,16 +548,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dhcproto"
|
||||
version = "0.12.0"
|
||||
|
@ -1263,101 +1145,12 @@ dependencies = [
|
|||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.53"
|
||||
|
@ -1573,12 +1366,6 @@ version = "0.1.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
|
@ -1616,22 +1403,6 @@ dependencies = [
|
|||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
@ -1743,12 +1514,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
|
@ -1881,6 +1646,7 @@ dependencies = [
|
|||
"moka",
|
||||
"nzr-api",
|
||||
"serde",
|
||||
"tarpc",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
|
@ -1895,20 +1661,6 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "omyacid"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"askama",
|
||||
"axum",
|
||||
"moka",
|
||||
"nzr-api",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
|
@ -2113,12 +1865,6 @@ version = "0.3.26"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
|
@ -2352,12 +2098,6 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
|
@ -2422,16 +2162,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_path_to_error"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.7"
|
||||
|
@ -2555,9 +2285,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -2861,18 +2591,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
|
||||
|
||||
[[package]]
|
||||
name = "syslog"
|
||||
version = "7.0.0"
|
||||
|
@ -3002,16 +2720,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
|
@ -3019,17 +2734,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
|
@ -3163,34 +2877,6 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
|
@ -3319,15 +3005,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[workspace]
|
||||
members = ["nzrd", "nzr-api", "client", "nzrdhcp", "nzr-virt", "omyacid"]
|
||||
members = ["nzrd", "nzr-api", "client", "nzrdhcp", "nzr-virt"]
|
||||
resolver = "2"
|
||||
|
|
|
@ -32,7 +32,6 @@ pub struct SOAConfig {
|
|||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DNSConfig {
|
||||
pub listen_addr: String,
|
||||
pub port: u16,
|
||||
pub default_zone: Name,
|
||||
pub soa: SOAConfig,
|
||||
}
|
||||
|
@ -41,15 +40,6 @@ pub struct DNSConfig {
|
|||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DHCPConfig {
|
||||
pub listen_addr: String,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
/// Cloud-init configuration, used by omyacid.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct CloudConfig {
|
||||
pub listen_addr: String,
|
||||
pub port: u16,
|
||||
pub admin_user: String,
|
||||
}
|
||||
|
||||
/// Server<->Client RPC configuration.
|
||||
|
@ -72,7 +62,6 @@ pub struct Config {
|
|||
pub storage: StorageConfig,
|
||||
pub dns: DNSConfig,
|
||||
pub dhcp: DHCPConfig,
|
||||
pub cloud: CloudConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
@ -95,9 +84,8 @@ impl Default for Config {
|
|||
base_image_pool: "images".to_owned(),
|
||||
},
|
||||
dns: DNSConfig {
|
||||
listen_addr: "127.0.0.1".to_owned(),
|
||||
port: 5353,
|
||||
default_zone: Name::from_utf8("servers.local").unwrap(),
|
||||
listen_addr: "127.0.0.1:5353".to_owned(),
|
||||
default_zone: Name::from_utf8("servers.locaddral").unwrap(),
|
||||
soa: SOAConfig {
|
||||
nzr_domain: Name::from_utf8("nzr.local").unwrap(),
|
||||
contact: Name::from_utf8("admin.nzr.local").unwrap(),
|
||||
|
@ -108,12 +96,6 @@ impl Default for Config {
|
|||
},
|
||||
dhcp: DHCPConfig {
|
||||
listen_addr: "127.0.0.1".to_owned(),
|
||||
port: 67,
|
||||
},
|
||||
cloud: CloudConfig {
|
||||
listen_addr: "0.0.0.0".to_owned(),
|
||||
port: 80,
|
||||
admin_user: "admin".to_owned(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::net::Ipv4Addr;
|
||||
|
||||
use model::{CreateStatus, Instance, Subnet};
|
||||
|
||||
pub mod args;
|
||||
|
@ -8,15 +6,6 @@ pub mod model;
|
|||
pub mod net;
|
||||
|
||||
pub use hickory_proto;
|
||||
use net::mac::MacAddr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum InstanceQuery {
|
||||
Name(String),
|
||||
MacAddr(MacAddr),
|
||||
Ipv4Addr(Ipv4Addr),
|
||||
}
|
||||
|
||||
#[tarpc::service]
|
||||
pub trait Nazrin {
|
||||
|
@ -29,8 +18,6 @@ pub trait Nazrin {
|
|||
/// This should involve deleting all related disks and clearing
|
||||
/// the lease information from the subnet data, if any.
|
||||
async fn delete_instance(name: String) -> Result<(), String>;
|
||||
/// Gets a single instance by the given InstanceQuery.
|
||||
async fn find_instance(query: InstanceQuery) -> Result<Option<Instance>, String>;
|
||||
/// Gets a list of existing instances.
|
||||
async fn get_instances(with_status: bool) -> Result<Vec<Instance>, String>;
|
||||
/// Cleans up unusable entries in the database.
|
||||
|
@ -47,20 +34,4 @@ pub trait Nazrin {
|
|||
async fn get_subnets() -> Result<Vec<Subnet>, String>;
|
||||
/// Deletes an existing subnet.
|
||||
async fn delete_subnet(interface: String) -> Result<(), String>;
|
||||
// Gets the cloud-init user-data for the given instance.
|
||||
async fn get_instance_userdata(id: i32) -> Result<Vec<u8>, String>;
|
||||
}
|
||||
|
||||
/// Create a new NazrinClient.
|
||||
pub fn new_client(sock: tokio::net::UnixStream) -> NazrinClient {
|
||||
use tarpc::tokio_serde::formats::Bincode;
|
||||
use tarpc::tokio_util::codec::LengthDelimitedCodec;
|
||||
|
||||
let framed_io = LengthDelimitedCodec::builder()
|
||||
.length_field_type::<u32>()
|
||||
.new_framed(sock);
|
||||
let transport = tarpc::serde_transport::new(framed_io, Bincode::default());
|
||||
NazrinClient::new(Default::default(), transport).spawn()
|
||||
}
|
||||
|
||||
pub use tarpc::context::current as default_ctx;
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
DROP TABLE instances;
|
||||
DROP TABLE subnets;
|
|
@ -1 +0,0 @@
|
|||
ALTER TABLE instances ADD COLUMN ci_metadata TEXT NOT NULL;
|
|
@ -1 +0,0 @@
|
|||
ALTER TABLE instances DROP COLUMN ci_metadata;
|
149
nzrd/src/cloud.rs
Normal file
149
nzrd/src/cloud.rs
Normal file
|
@ -0,0 +1,149 @@
|
|||
use std::net::Ipv4Addr;
|
||||
|
||||
use hickory_server::proto::rr::Name;
|
||||
use serde::Serialize;
|
||||
use serde_with::skip_serializing_none;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use nzr_api::net::{cidr::CidrV4, mac::MacAddr};
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Metadata<'a> {
|
||||
instance_id: &'a str,
|
||||
local_hostname: &'a str,
|
||||
public_keys: Option<Vec<&'a String>>,
|
||||
}
|
||||
|
||||
impl<'a> Metadata<'a> {
|
||||
pub fn new(instance_id: &'a str) -> Self {
|
||||
Self {
|
||||
instance_id,
|
||||
local_hostname: instance_id,
|
||||
public_keys: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ssh_pubkeys(mut self, pubkeys: &'a [String]) -> Self {
|
||||
self.public_keys = Some(pubkeys.iter().filter(|i| !i.is_empty()).collect());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct NetworkMeta<'a> {
|
||||
version: u32,
|
||||
ethernets: HashMap<String, EtherNic<'a>>,
|
||||
#[serde(skip)]
|
||||
ethnum: u8,
|
||||
}
|
||||
|
||||
impl<'a> NetworkMeta<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
version: 2,
|
||||
ethernets: HashMap::new(),
|
||||
ethnum: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Define a NIC with a static address.
|
||||
pub fn static_nic(
|
||||
mut self,
|
||||
match_data: EtherMatch<'a>,
|
||||
cidr: &'a CidrV4,
|
||||
gateway: &'a Ipv4Addr,
|
||||
dns: DNSMeta<'a>,
|
||||
) -> Self {
|
||||
self.ethernets.insert(
|
||||
format!("eth{}", self.ethnum),
|
||||
EtherNic {
|
||||
r#match: match_data,
|
||||
addresses: Some(vec![cidr]),
|
||||
gateway4: Some(gateway),
|
||||
dhcp4: false,
|
||||
nameservers: Some(dns),
|
||||
},
|
||||
);
|
||||
self.ethnum += 1;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn dhcp_nic(mut self, match_data: EtherMatch<'a>) -> Self {
|
||||
self.ethernets.insert(
|
||||
format!("eth{}", self.ethnum),
|
||||
EtherNic {
|
||||
r#match: match_data,
|
||||
addresses: None,
|
||||
gateway4: None,
|
||||
dhcp4: true,
|
||||
nameservers: None,
|
||||
},
|
||||
);
|
||||
self.ethnum += 1;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Ethernets<'a> {
|
||||
nics: Vec<EtherNic<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct EtherNic<'a> {
|
||||
r#match: EtherMatch<'a>,
|
||||
addresses: Option<Vec<&'a CidrV4>>,
|
||||
gateway4: Option<&'a Ipv4Addr>,
|
||||
dhcp4: bool,
|
||||
nameservers: Option<DNSMeta<'a>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
pub struct EtherMatch<'a> {
|
||||
name: Option<&'a str>,
|
||||
macaddress: Option<&'a MacAddr>,
|
||||
driver: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> EtherMatch<'a> {
|
||||
#[allow(dead_code)]
|
||||
pub fn name(name: &'a str) -> Self {
|
||||
Self {
|
||||
name: Some(name),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mac_addr(addr: &'a MacAddr) -> Self {
|
||||
Self {
|
||||
macaddress: Some(addr),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn driver(driver: &'a str) -> Self {
|
||||
Self {
|
||||
driver: Some(driver),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct DNSMeta<'a> {
|
||||
search: Vec<Name>,
|
||||
addresses: &'a Vec<Ipv4Addr>,
|
||||
}
|
||||
|
||||
impl<'a> DNSMeta<'a> {
|
||||
pub fn with_addrs(search: Option<Vec<Name>>, addrs: &'a Vec<Ipv4Addr>) -> Self {
|
||||
Self {
|
||||
addresses: addrs,
|
||||
search: search.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use nzr_virt::{datasize, dom, vol};
|
|||
use tokio::sync::RwLock;
|
||||
|
||||
use super::*;
|
||||
use crate::cloud::Metadata;
|
||||
use crate::ctrl::vm::Progress;
|
||||
use crate::ctx::Context;
|
||||
use crate::model::{Instance, Subnet};
|
||||
|
@ -85,7 +86,14 @@ pub async fn new_instance(
|
|||
};
|
||||
|
||||
// generate cloud-init data
|
||||
let db_inst = Instance::insert(&ctx, &args.name, &subnet, lease.clone(), None).await?;
|
||||
let ci_meta = {
|
||||
let m = Metadata::new(&args.name).ssh_pubkeys(&args.ssh_keys);
|
||||
serde_yaml::to_string(&m)
|
||||
.map_err(|err| cmd_error!("Couldn't generate cloud-init metadata: {err}"))
|
||||
}?;
|
||||
|
||||
let db_inst =
|
||||
Instance::insert(&ctx, &args.name, &subnet, lease.clone(), ci_meta, None).await?;
|
||||
|
||||
progress!(prog_task, 30.0, "Creating instance images...");
|
||||
// create primary volume from base image
|
||||
|
|
|
@ -3,7 +3,6 @@ use diesel::{
|
|||
SqliteConnection,
|
||||
};
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
use log::trace;
|
||||
use nzr_virt::{vol, Connection};
|
||||
use std::ops::Deref;
|
||||
use thiserror::Error;
|
||||
|
@ -12,10 +11,6 @@ use crate::dns::ZoneData;
|
|||
use nzr_api::config::Config;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||
|
||||
#[cfg(not(test))]
|
||||
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||
|
||||
pub struct PoolRefs {
|
||||
|
@ -69,7 +64,6 @@ impl InnerCtx {
|
|||
baseimg: conn.get_pool(&config.storage.base_image_pool).await?,
|
||||
};
|
||||
|
||||
trace!("Connecting to database");
|
||||
let db_uri = config.db_uri.clone();
|
||||
let sqldb = tokio::task::spawn_blocking(|| {
|
||||
let manager = ConnectionManager::<SqliteConnection>::new(db_uri);
|
||||
|
@ -80,7 +74,6 @@ impl InnerCtx {
|
|||
.unwrap()?;
|
||||
|
||||
{
|
||||
trace!("Running pending migrations");
|
||||
let mut conn = sqldb.get()?;
|
||||
tokio::task::spawn_blocking(move || {
|
||||
conn.run_pending_migrations(MIGRATIONS)
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
mod cloud;
|
||||
mod cmd;
|
||||
mod ctrl;
|
||||
mod ctx;
|
||||
mod dns;
|
||||
mod model;
|
||||
mod rpc;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
use hickory_server::ServerFuture;
|
||||
use log::LevelFilter;
|
||||
use log::*;
|
||||
use model::{Instance, Subnet};
|
||||
use nzr_api::config;
|
||||
use std::{net::IpAddr, str::FromStr};
|
||||
use std::str::FromStr;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
#[tokio::main(flavor = "multi_thread")]
|
||||
|
@ -59,10 +62,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
// DNS init
|
||||
let mut dns_listener = ServerFuture::new(ctx.zones.catalog());
|
||||
let dns_socket = {
|
||||
let dns_ip: IpAddr = ctx.config.dns.listen_addr.parse()?;
|
||||
UdpSocket::bind((dns_ip, ctx.config.dns.port)).await?
|
||||
};
|
||||
let dns_socket = UdpSocket::bind(ctx.config.dns.listen_addr.as_str()).await?;
|
||||
dns_listener.register_socket(dns_socket);
|
||||
|
||||
tokio::select! {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use std::{net::Ipv4Addr, str::FromStr};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
pub mod tx;
|
||||
|
||||
use diesel::{associations::HasTable, prelude::*};
|
||||
|
@ -35,6 +33,7 @@ diesel::table! {
|
|||
mac_addr -> Text,
|
||||
subnet_id -> Integer,
|
||||
host_num -> Integer,
|
||||
ci_metadata -> Text,
|
||||
ci_userdata -> Nullable<Binary>,
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +71,7 @@ pub struct Instance {
|
|||
pub mac_addr: MacAddr,
|
||||
pub subnet_id: i32,
|
||||
pub host_num: i32,
|
||||
pub ci_metadata: String,
|
||||
pub ci_userdata: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
|
@ -91,17 +91,6 @@ impl Instance {
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
pub async fn get(ctx: &Context, id: i32) -> Result<Option<Self>, ModelError> {
|
||||
ctx.spawn_db(move |mut db| {
|
||||
self::instances::table
|
||||
.find(id)
|
||||
.load::<Instance>(&mut db)
|
||||
.map(|m| m.into_iter().next())
|
||||
})
|
||||
.await?
|
||||
.map_err(ModelError::Db)
|
||||
}
|
||||
|
||||
pub async fn all_in_subnet(ctx: &Context, net: &Subnet) -> Result<Vec<Self>, ModelError> {
|
||||
let subnet = net.clone();
|
||||
|
||||
|
@ -133,19 +122,6 @@ impl Instance {
|
|||
Ok(res.into_iter().next())
|
||||
}
|
||||
|
||||
/// Gets an Instance model with the given MAC address.
|
||||
pub async fn get_by_mac(ctx: &Context, addr: MacAddr) -> Result<Option<Self>, ModelError> {
|
||||
ctx.spawn_db(move |mut db| {
|
||||
use self::instances::dsl::{instances, mac_addr};
|
||||
instances
|
||||
.filter(mac_addr.eq(addr))
|
||||
.select(Instance::as_select())
|
||||
.load::<Instance>(&mut db)
|
||||
})
|
||||
.await?
|
||||
.map_or_else(|e| Err(ModelError::Db(e)), |m| Ok(m.into_iter().next()))
|
||||
}
|
||||
|
||||
/// Gets an Instance model by the IPv4 address that has been assigned to it.
|
||||
pub async fn get_by_ip4(ctx: &Context, ip_addr: Ipv4Addr) -> Result<Option<Self>, ModelError> {
|
||||
use self::instances::dsl::host_num;
|
||||
|
@ -181,6 +157,7 @@ impl Instance {
|
|||
name: impl AsRef<str>,
|
||||
subnet: &Subnet,
|
||||
lease: nzr_api::model::Lease,
|
||||
ci_meta: impl Into<String>,
|
||||
ci_user: Option<Vec<u8>>,
|
||||
) -> Result<Self, ModelError> {
|
||||
// Get highest host addr + 1 for our addr
|
||||
|
@ -192,6 +169,7 @@ impl Instance {
|
|||
|
||||
let wanted_name = name.as_ref().to_owned();
|
||||
let netid = subnet.id;
|
||||
let ci_meta = ci_meta.into();
|
||||
|
||||
if addr_num > subnet.end_host {
|
||||
Err(cidr::Error::HostBitsTooLarge)?;
|
||||
|
@ -206,6 +184,7 @@ impl Instance {
|
|||
mac_addr.eq(lease.mac_addr),
|
||||
subnet_id.eq(netid),
|
||||
host_num.eq(addr_num),
|
||||
ci_metadata.eq(ci_meta),
|
||||
ci_userdata.eq(ci_user),
|
||||
);
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
use diesel::Connection;
|
||||
use diesel_migrations::MigrationHarness;
|
||||
|
||||
#[test]
|
||||
fn migrations() {
|
||||
let mut sql = diesel::SqliteConnection::establish(":memory:").unwrap();
|
||||
let pending = sql.pending_migrations(crate::ctx::MIGRATIONS).unwrap();
|
||||
assert!(!pending.is_empty(), "No migrations found");
|
||||
for migration in pending {
|
||||
sql.run_migration(&migration).unwrap();
|
||||
}
|
||||
|
||||
sql.revert_all_migrations(crate::ctx::MIGRATIONS).unwrap();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use futures::{future, StreamExt};
|
||||
use nzr_api::{args, model, InstanceQuery, Nazrin};
|
||||
use nzr_api::{args, model, Nazrin};
|
||||
use std::sync::Arc;
|
||||
use tarpc::server::{BaseChannel, Channel};
|
||||
use tarpc::tokio_serde::formats::Bincode;
|
||||
|
@ -114,27 +114,6 @@ impl Nazrin for NzrServer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn find_instance(
|
||||
self,
|
||||
_: tarpc::context::Context,
|
||||
query: nzr_api::InstanceQuery,
|
||||
) -> Result<Option<model::Instance>, String> {
|
||||
let res = match query {
|
||||
InstanceQuery::Name(name) => Instance::get_by_name(&self.ctx, name).await,
|
||||
InstanceQuery::MacAddr(addr) => Instance::get_by_mac(&self.ctx, addr).await,
|
||||
InstanceQuery::Ipv4Addr(addr) => Instance::get_by_ip4(&self.ctx, addr).await,
|
||||
}
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if let Some(inst) = res {
|
||||
inst.api_model(&self.ctx)
|
||||
.await
|
||||
.map_or_else(|e| Err(e.to_string()), |m| Ok(Some(m)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_instances(
|
||||
self,
|
||||
_: tarpc::context::Context,
|
||||
|
@ -237,21 +216,6 @@ impl Nazrin for NzrServer {
|
|||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_instance_userdata(
|
||||
self,
|
||||
_: tarpc::context::Context,
|
||||
id: i32,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
let Some(db_model) = Instance::get(&self.ctx, id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
else {
|
||||
return Err("Instance doesn't exist".to_owned());
|
||||
};
|
||||
|
||||
Ok(db_model.ci_userdata.unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
54
nzrd/src/test.rs
Normal file
54
nzrd/src/test.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use std::{net::Ipv4Addr, str::FromStr};
|
||||
|
||||
use crate::cloud::*;
|
||||
use nzr_api::net::{cidr::CidrV4, mac::MacAddr};
|
||||
|
||||
#[test]
|
||||
fn cloud_metadata() {
|
||||
let expected = r#"
|
||||
instance-id: my-instance
|
||||
local-hostname: my-instance
|
||||
public-keys:
|
||||
- ssh-key 123456 admin@laptop
|
||||
"#
|
||||
.trim_start();
|
||||
let pubkeys = vec!["ssh-key 123456 admin@laptop".to_owned(), "".to_owned()];
|
||||
let meta = Metadata::new("my-instance").ssh_pubkeys(&pubkeys);
|
||||
|
||||
let meta_xml = serde_yaml::to_string(&meta).unwrap();
|
||||
assert_eq!(meta_xml, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cloud_netdata() {
|
||||
let expected = r#"
|
||||
version: 2
|
||||
ethernets:
|
||||
eth0:
|
||||
match:
|
||||
macaddress: 02:15:42:0b:ee:01
|
||||
addresses:
|
||||
- 192.0.2.69/24
|
||||
gateway4: 192.0.2.1
|
||||
dhcp4: false
|
||||
nameservers:
|
||||
search: []
|
||||
addresses:
|
||||
- 192.0.2.1
|
||||
"#
|
||||
.trim_start();
|
||||
let mac_addr = MacAddr::new(0x02, 0x15, 0x42, 0x0b, 0xee, 0x01);
|
||||
let cidr = CidrV4::from_str("192.0.2.69/24").unwrap();
|
||||
let gateway = Ipv4Addr::from_str("192.0.2.1").unwrap();
|
||||
|
||||
let dns = vec![gateway];
|
||||
let netconfig = NetworkMeta::new().static_nic(
|
||||
EtherMatch::mac_addr(&mac_addr),
|
||||
&cidr,
|
||||
&gateway,
|
||||
DNSMeta::with_addrs(None, &dns),
|
||||
);
|
||||
|
||||
let net_xml = serde_yaml::to_string(&netconfig).unwrap();
|
||||
assert_eq!(net_xml, expected);
|
||||
}
|
|
@ -11,5 +11,11 @@ tokio = { version = "1.39.2", features = ["rt-multi-thread", "net", "macros"] }
|
|||
nzr-api = { path = "../nzr-api" }
|
||||
tracing = { version = "0.1.40", features = ["log"] }
|
||||
tracing-subscriber = "0.3.18"
|
||||
tarpc = { version = "0.34", features = [
|
||||
"tokio1",
|
||||
"unix",
|
||||
"serde-transport",
|
||||
"serde-transport-bincode",
|
||||
] }
|
||||
moka = { version = "0.12.8", features = ["future"] }
|
||||
anyhow = "1.0.86"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use std::hash::RandomState;
|
||||
use std::net::IpAddr;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use anyhow::Context as _;
|
||||
|
@ -11,6 +10,7 @@ use nzr_api::{
|
|||
net::mac::MacAddr,
|
||||
NazrinClient,
|
||||
};
|
||||
use tarpc::{tokio_serde::formats::Bincode, tokio_util::codec::LengthDelimitedCodec};
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::net::UnixStream;
|
||||
|
||||
|
@ -26,7 +26,7 @@ impl Context {
|
|||
async fn hydrate_hosts(&self) -> Result<()> {
|
||||
let instances = self
|
||||
.api_client
|
||||
.get_instances(nzr_api::default_ctx(), false)
|
||||
.get_instances(tarpc::context::current(), false)
|
||||
.await?
|
||||
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
||||
|
||||
|
@ -52,7 +52,7 @@ impl Context {
|
|||
async fn hydrate_nets(&self) -> Result<()> {
|
||||
let subnets = self
|
||||
.api_client
|
||||
.get_subnets(nzr_api::default_ctx())
|
||||
.get_subnets(tarpc::context::current())
|
||||
.await?
|
||||
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
||||
|
||||
|
@ -68,19 +68,21 @@ impl Context {
|
|||
let sock = UnixStream::connect(&cfg.rpc.socket_path)
|
||||
.await
|
||||
.context("Connection to nzrd failed")?;
|
||||
nzr_api::new_client(sock)
|
||||
};
|
||||
let framed_io = LengthDelimitedCodec::builder()
|
||||
.length_field_type::<u32>()
|
||||
.new_framed(sock);
|
||||
let transport = tarpc::serde_transport::new(framed_io, Bincode::default());
|
||||
NazrinClient::new(Default::default(), transport)
|
||||
}
|
||||
.spawn();
|
||||
|
||||
let listen_addr: SocketAddr = {
|
||||
let ip: IpAddr = cfg
|
||||
.dhcp
|
||||
.listen_addr
|
||||
.parse()
|
||||
.context("Malformed listen address")?;
|
||||
(ip, cfg.dhcp.port).into()
|
||||
};
|
||||
let listen_addr: SocketAddr = cfg
|
||||
.dhcp
|
||||
.listen_addr
|
||||
.parse()
|
||||
.context("Malformed listen address")?;
|
||||
|
||||
let server_sock = UdpSocket::bind(listen_addr)
|
||||
let server_sock = UdpSocket::bind(&listen_addr)
|
||||
.await
|
||||
.context("Unable to listen")?;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{net::Ipv4Addr, process::ExitCode};
|
|||
use ctx::Context;
|
||||
use dhcproto::{
|
||||
v4::{DhcpOption, Message, MessageType, Opcode, OptionCode},
|
||||
Decodable, Decoder, Encodable, Encoder,
|
||||
Decodable, Decoder,
|
||||
};
|
||||
use nzr_api::{config::Config, net::mac::MacAddr};
|
||||
use std::net::SocketAddr;
|
||||
|
@ -17,8 +17,8 @@ const DEFAULT_LEASE: u32 = 86400;
|
|||
fn make_reply(msg: &Message, msg_type: MessageType, lease_addr: Option<Ipv4Addr>) -> Message {
|
||||
let mut resp = Message::new(
|
||||
EMPTY_V4,
|
||||
lease_addr.unwrap_or(EMPTY_V4),
|
||||
EMPTY_V4,
|
||||
lease_addr.unwrap_or(EMPTY_V4),
|
||||
msg.giaddr(),
|
||||
msg.chaddr(),
|
||||
);
|
||||
|
@ -46,7 +46,6 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
|||
tracing::info!("Received DHCP payload with invalid addr (different media type?)");
|
||||
return;
|
||||
};
|
||||
tracing::debug!("Client MAC is {client_mac}!!!");
|
||||
|
||||
let instance = match ctx.instance_by_mac(client_mac).await {
|
||||
Ok(Some(i)) => i,
|
||||
|
@ -60,11 +59,6 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
|||
}
|
||||
};
|
||||
|
||||
tracing::info!(
|
||||
"Recieved {msg_type:?} from {client_mac} (assuming {})",
|
||||
&instance.name
|
||||
);
|
||||
|
||||
let mut lease_time = None;
|
||||
let mut nak = false;
|
||||
|
||||
|
@ -74,18 +68,12 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
|||
make_reply(msg, MessageType::Offer, Some(instance.lease.addr.addr))
|
||||
}
|
||||
MessageType::Request => {
|
||||
if let Some(DhcpOption::RequestedIpAddress(addr)) =
|
||||
msg.opts().get(OptionCode::RequestedIpAddress)
|
||||
{
|
||||
if *addr == instance.lease.addr.addr {
|
||||
make_reply(msg, MessageType::Ack, Some(instance.lease.addr.addr))
|
||||
} else {
|
||||
nak = true;
|
||||
make_reply(msg, MessageType::Nak, None)
|
||||
}
|
||||
} else {
|
||||
if msg.ciaddr() != instance.lease.addr.addr {
|
||||
nak = true;
|
||||
make_reply(msg, MessageType::Nak, None)
|
||||
} else {
|
||||
lease_time = Some(DEFAULT_LEASE);
|
||||
make_reply(msg, MessageType::Ack, Some(instance.lease.addr.addr))
|
||||
}
|
||||
}
|
||||
MessageType::Decline => {
|
||||
|
@ -98,80 +86,57 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
|||
}
|
||||
MessageType::Release => {
|
||||
// We only provide static leases
|
||||
tracing::debug!("Ignoring DHCPRELEASE");
|
||||
tracing::trace!("Ignoring DHCPRELEASE");
|
||||
return;
|
||||
}
|
||||
MessageType::Inform => make_reply(msg, MessageType::Ack, None),
|
||||
other => {
|
||||
tracing::info!("Received unhandled message {other:?}");
|
||||
tracing::trace!("Received unhandled message {other:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
let opts = response.opts_mut();
|
||||
let giaddr = if msg.giaddr().is_unspecified() {
|
||||
todo!("no relay??")
|
||||
} else {
|
||||
msg.giaddr()
|
||||
let opts = response.opts_mut();
|
||||
let giaddr = if msg.giaddr().is_unspecified() {
|
||||
todo!("no relay??")
|
||||
} else {
|
||||
msg.giaddr()
|
||||
};
|
||||
|
||||
opts.insert(DhcpOption::ServerIdentifier(giaddr));
|
||||
if let Some(time) = lease_time {
|
||||
opts.insert(DhcpOption::AddressLeaseTime(time));
|
||||
}
|
||||
|
||||
if !nak {
|
||||
// Get general networking info
|
||||
let subnet = match ctx.get_subnet(&instance.lease.subnet).await {
|
||||
Ok(Some(net)) => net,
|
||||
Ok(None) => {
|
||||
tracing::error!("nzrd says '{}' isn't a subnet", &instance.lease.subnet);
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Error getting subnet: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
opts.insert(DhcpOption::ServerIdentifier(giaddr));
|
||||
if let Some(time) = lease_time {
|
||||
opts.insert(DhcpOption::AddressLeaseTime(time));
|
||||
opts.insert(DhcpOption::Hostname(instance.name.clone()));
|
||||
|
||||
if !subnet.dns.is_empty() {
|
||||
opts.insert(DhcpOption::DomainNameServer(subnet.dns));
|
||||
}
|
||||
|
||||
if !nak {
|
||||
// Get general networking info
|
||||
let subnet = match ctx.get_subnet(&instance.lease.subnet).await {
|
||||
Ok(Some(net)) => net,
|
||||
Ok(None) => {
|
||||
tracing::error!("nzrd says '{}' isn't a subnet", &instance.lease.subnet);
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Error getting subnet: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
opts.insert(DhcpOption::Hostname(instance.name.clone()));
|
||||
|
||||
if !subnet.dns.is_empty() {
|
||||
opts.insert(DhcpOption::DomainNameServer(subnet.dns));
|
||||
}
|
||||
|
||||
if let Some(name) = subnet.domain_name {
|
||||
opts.insert(DhcpOption::DomainName(name.to_utf8()));
|
||||
}
|
||||
|
||||
if let Some(gw) = subnet.gateway4 {
|
||||
opts.insert(DhcpOption::Router(Vec::from(&[gw])));
|
||||
}
|
||||
|
||||
opts.insert(DhcpOption::SubnetMask(instance.lease.addr.netmask()));
|
||||
if let Some(name) = subnet.domain_name {
|
||||
opts.insert(DhcpOption::DomainName(name.to_utf8()));
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"Sending message {:?} with yiaddr {}",
|
||||
response
|
||||
.opts()
|
||||
.get(OptionCode::MessageType)
|
||||
.unwrap_or(&DhcpOption::End),
|
||||
response.yiaddr()
|
||||
);
|
||||
if let Some(gw) = subnet.gateway4 {
|
||||
opts.insert(DhcpOption::Router(Vec::from(&[gw])));
|
||||
}
|
||||
|
||||
// unicast it back
|
||||
let mut resp_buf = Vec::new();
|
||||
let mut enc = Encoder::new(&mut resp_buf);
|
||||
if let Err(err) = response.encode(&mut enc) {
|
||||
tracing::error!("Couldn't encode response: {err}");
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = ctx.sock().send_to(&resp_buf, from).await {
|
||||
tracing::error!("Couldn't send response: {err}");
|
||||
opts.insert(DhcpOption::SubnetMask(instance.lease.addr.netmask()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
[package]
|
||||
name = "omyacid"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nzr-api = { path = "../nzr-api" }
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
||||
axum = "0.7"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
anyhow = "1"
|
||||
askama = "0.12"
|
||||
moka = { version = "0.12.8", features = ["future"] }
|
|
@ -1,95 +0,0 @@
|
|||
use std::hash::RandomState;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use anyhow::Result;
|
||||
use moka::future::Cache;
|
||||
use nzr_api::config::Config;
|
||||
use nzr_api::model::Instance;
|
||||
use nzr_api::InstanceQuery;
|
||||
use nzr_api::NazrinClient;
|
||||
use tokio::net::UnixStream;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct InstanceMeta {
|
||||
pub inst: Instance,
|
||||
pub userdata: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Context {
|
||||
api_client: NazrinClient,
|
||||
config: Arc<Config>,
|
||||
host_cache: Cache<Ipv4Addr, InstanceMeta, RandomState>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub async fn new(cfg: Config) -> Result<Self> {
|
||||
let api_client = {
|
||||
let sock = UnixStream::connect(&cfg.rpc.socket_path)
|
||||
.await
|
||||
.context("Connection to nzrd failed")?;
|
||||
nzr_api::new_client(sock)
|
||||
};
|
||||
|
||||
let host_cache = Cache::builder()
|
||||
.time_to_live(Duration::from_secs(15))
|
||||
.max_capacity(5)
|
||||
.build();
|
||||
|
||||
Ok(Self {
|
||||
api_client,
|
||||
host_cache,
|
||||
config: Arc::new(cfg),
|
||||
})
|
||||
}
|
||||
|
||||
// Internal function to hydrate the instance metadata, if needed
|
||||
async fn get_instmeta(&self, addr: Ipv4Addr) -> Result<Option<InstanceMeta>> {
|
||||
if let Some(meta) = self.host_cache.get(&addr).await {
|
||||
tracing::debug!("Cache hit!");
|
||||
Ok(Some(meta))
|
||||
} else {
|
||||
let inst = self
|
||||
.api_client
|
||||
.find_instance(nzr_api::default_ctx(), InstanceQuery::Ipv4Addr(addr))
|
||||
.await
|
||||
.context("RPC error")?
|
||||
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
||||
if let Some(inst) = inst {
|
||||
let userdata = self
|
||||
.api_client
|
||||
.get_instance_userdata(nzr_api::default_ctx(), inst.id)
|
||||
.await
|
||||
.context("RPC error")?
|
||||
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
||||
|
||||
let meta = InstanceMeta { inst, userdata };
|
||||
|
||||
self.host_cache.insert(addr, meta.clone()).await;
|
||||
|
||||
Ok(Some(meta))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_instance(&self, addr: Ipv4Addr) -> Result<Option<Instance>> {
|
||||
self.get_instmeta(addr)
|
||||
.await
|
||||
.map(|opt| opt.map(|im| im.inst))
|
||||
}
|
||||
|
||||
pub async fn get_inst_userdata(&self, addr: Ipv4Addr) -> Result<Option<Vec<u8>>> {
|
||||
self.get_instmeta(addr)
|
||||
.await
|
||||
.map(|opt| opt.map(|im| im.userdata))
|
||||
}
|
||||
|
||||
pub fn cfg(&self) -> &Config {
|
||||
&self.config
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
mod ctx;
|
||||
mod model;
|
||||
|
||||
use std::{
|
||||
net::{IpAddr, SocketAddr},
|
||||
process::ExitCode,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use askama::Template;
|
||||
use axum::{
|
||||
extract::{ConnectInfo, State},
|
||||
http::StatusCode,
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use model::Metadata;
|
||||
use nzr_api::config::Config;
|
||||
|
||||
async fn get_meta_data(
|
||||
State(ctx): State<ctx::Context>,
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
) -> Result<String, StatusCode> {
|
||||
if let IpAddr::V4(ip) = addr.ip() {
|
||||
match ctx.get_instance(ip).await {
|
||||
Ok(Some(inst)) => {
|
||||
let meta = Metadata {
|
||||
inst_name: &inst.name,
|
||||
ssh_pubkeys: Vec::new(), // TODO
|
||||
username: Some(ctx.cfg().cloud.admin_user.as_ref()),
|
||||
};
|
||||
|
||||
meta.render().map_err(|e| {
|
||||
tracing::error!("Renderer error: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})
|
||||
}
|
||||
Ok(None) => {
|
||||
tracing::warn!("Request from unregistered server {ip}");
|
||||
Err(StatusCode::FORBIDDEN)
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!("{err}");
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(StatusCode::BAD_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_user_data(
|
||||
State(ctx): State<ctx::Context>,
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
) -> Result<Vec<u8>, StatusCode> {
|
||||
if let IpAddr::V4(ip) = addr.ip() {
|
||||
match ctx.get_inst_userdata(ip).await {
|
||||
Ok(Some(data)) => Ok(data),
|
||||
Ok(None) => {
|
||||
tracing::warn!("Request from unregistered server {ip}");
|
||||
Err(StatusCode::FORBIDDEN)
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!("{err}");
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(StatusCode::BAD_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> ExitCode {
|
||||
tracing_subscriber::fmt().init();
|
||||
let cfg: Config = match Config::figment().extract() {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
tracing::error!("Unable to get configuration: {err}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
let http_sock = {
|
||||
let addr = match IpAddr::from_str(&cfg.cloud.listen_addr) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => {
|
||||
tracing::error!("Invalid listen IP address ({err})");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
match tokio::net::TcpListener::bind((addr, cfg.cloud.port)).await {
|
||||
Ok(sock) => sock,
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to bind to {addr}:{}: {err}", cfg.cloud.port);
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ctx = match ctx::Context::new(cfg).await {
|
||||
Ok(ctx) => ctx,
|
||||
Err(err) => {
|
||||
tracing::error!("{err}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
let app = Router::new()
|
||||
.route("/meta-data", get(get_meta_data))
|
||||
.route("/user-data", get(get_user_data))
|
||||
.with_state(ctx);
|
||||
|
||||
if let Err(err) = axum::serve(http_sock, app).await {
|
||||
tracing::error!("axum error: {err}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
ExitCode::SUCCESS
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
use askama::Template;
|
||||
#[derive(Template)]
|
||||
#[template(path = "meta-data.yml")]
|
||||
pub struct Metadata<'a> {
|
||||
pub inst_name: &'a str,
|
||||
pub ssh_pubkeys: Vec<&'a String>,
|
||||
pub username: Option<&'a str>,
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
instance_id: "iid-{{ inst_name }}"
|
||||
local_hostname: "{{ inst_name }}"
|
||||
{% if !ssh_pubkeys.is_empty() -%}
|
||||
public_keys:
|
||||
{% for key in ssh_pubkeys -%}
|
||||
- "{{ key }}"
|
||||
{% endfor %}
|
||||
{% endif -%}
|
||||
{% if let Some(user) = username -%}
|
||||
default_username: "{{ user }}"
|
||||
{%- endif %}
|
Loading…
Reference in a new issue