connected daemon to the brain (#2)

Instead of grabbing commands from files, the daemon now connects to the brain and receives commands via gRPC.

Reviewed-on: SNP/daemon#2
This commit is contained in:
ghe0 2024-12-30 20:45:01 +00:00
parent 3ba140a830
commit 3c6074f735
13 changed files with 1580 additions and 329 deletions

699
Cargo.lock generated

@ -17,12 +17,103 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.94" version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
[[package]]
name = "async-stream"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
dependencies = [
"async-stream-impl",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-stream-impl"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "async-trait"
version = "0.1.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@ -35,6 +126,53 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "axum"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
dependencies = [
"async-trait",
"axum-core",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"sync_wrapper",
"tower 0.5.2",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"rustversion",
"sync_wrapper",
"tower-layer",
"tower-service",
]
[[package]] [[package]]
name = "backtrace" name = "backtrace"
version = "0.3.74" version = "0.3.74"
@ -56,6 +194,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.6.0" version = "2.6.0"
@ -91,9 +235,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.3" version = "1.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
@ -105,13 +249,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "cidr" name = "colorchoice"
version = "0.3.0" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfc95a0c21d5409adc146dbbb152b5c65aaea32bc2d2f57cf12f850bffdd7ab8" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
dependencies = [
"serde", [[package]]
] name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
@ -148,17 +295,65 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "curve25519-dalek"
version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
"rustc_version",
"subtle",
"zeroize",
]
[[package]]
name = "curve25519-dalek-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "der"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
dependencies = [
"const-oid",
"pem-rfc7468",
"zeroize",
]
[[package]] [[package]]
name = "detee-snp-daemon" name = "detee-snp-daemon"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cidr", "ed25519-dalek",
"env_logger",
"lazy_static",
"log",
"prost",
"prost-types",
"rand", "rand",
"rand_core",
"reqwest", "reqwest",
"serde", "serde",
"serde_yaml", "serde_yaml",
"sha2", "sha2",
"tokio",
"tokio-stream",
"tonic",
"tonic-build",
] ]
[[package]] [[package]]
@ -182,6 +377,37 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "ed25519"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
"pkcs8",
"signature",
]
[[package]]
name = "ed25519-dalek"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core",
"serde",
"sha2",
"subtle",
"zeroize",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.35" version = "0.8.35"
@ -191,6 +417,29 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "env_filter"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
@ -213,6 +462,18 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fiat-crypto"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -332,19 +593,31 @@ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http", "http",
"indexmap", "indexmap 2.7.0",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tracing", "tracing",
] ]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "http" name = "http"
version = "1.2.0" version = "1.2.0"
@ -385,6 +658,18 @@ version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.5.1" version = "1.5.1"
@ -398,6 +683,7 @@ dependencies = [
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
"httpdate",
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
"smallvec", "smallvec",
@ -407,9 +693,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper-rustls" name = "hyper-rustls"
version = "0.27.3" version = "0.27.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"http", "http",
@ -422,6 +708,19 @@ dependencies = [
"tower-service", "tower-service",
] ]
[[package]]
name = "hyper-timeout"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [
"hyper",
"hyper-util",
"pin-project-lite",
"tokio",
"tower-service",
]
[[package]] [[package]]
name = "hyper-tls" name = "hyper-tls"
version = "0.6.0" version = "0.6.0"
@ -596,6 +895,16 @@ dependencies = [
"icu_properties", "icu_properties",
] ]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.7.0" version = "2.7.0"
@ -603,7 +912,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown 0.15.2",
] ]
[[package]] [[package]]
@ -612,6 +921,21 @@ version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.14" version = "1.0.14"
@ -628,6 +952,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.167" version = "0.2.167"
@ -652,6 +982,12 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "matchit"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"
@ -684,6 +1020,12 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "multimap"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.12" version = "0.2.12"
@ -760,12 +1102,51 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "petgraph"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
"indexmap 2.7.0",
]
[[package]]
name = "pin-project"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.15" version = "0.2.15"
@ -778,6 +1159,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"spki",
]
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.31" version = "0.3.31"
@ -793,6 +1184,16 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "prettyplease"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
dependencies = [
"proc-macro2",
"syn",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.92" version = "1.0.92"
@ -802,6 +1203,58 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "prost"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-build"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b"
dependencies = [
"heck",
"itertools",
"log",
"multimap",
"once_cell",
"petgraph",
"prettyplease",
"prost",
"prost-types",
"regex",
"syn",
"tempfile",
]
[[package]]
name = "prost-derive"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "prost-types"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc"
dependencies = [
"prost",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.37" version = "1.0.37"
@ -842,10 +1295,39 @@ dependencies = [
] ]
[[package]] [[package]]
name = "reqwest" name = "regex"
version = "0.12.9" version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
version = "0.12.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe060fe50f524be480214aba758c71f99f90ee8c83c5a36b5e9e1d568eb4eb3"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
@ -877,6 +1359,7 @@ dependencies = [
"system-configuration", "system-configuration",
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
"tower 0.5.2",
"tower-service", "tower-service",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
@ -906,6 +1389,15 @@ version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.42" version = "0.38.42"
@ -921,9 +1413,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.19" version = "0.23.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"rustls-pki-types", "rustls-pki-types",
@ -943,9 +1435,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.10.0" version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
@ -958,6 +1450,12 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "rustversion"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.18" version = "1.0.18"
@ -988,14 +1486,20 @@ dependencies = [
[[package]] [[package]]
name = "security-framework-sys" name = "security-framework-sys"
version = "2.12.1" version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5"
dependencies = [ dependencies = [
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
] ]
[[package]]
name = "semver"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.215" version = "1.0.215"
@ -1018,9 +1522,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.133" version = "1.0.134"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -1046,7 +1550,7 @@ version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [ dependencies = [
"indexmap", "indexmap 2.7.0",
"itoa", "itoa",
"ryu", "ryu",
"serde", "serde",
@ -1070,6 +1574,15 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"rand_core",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -1101,6 +1614,16 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "spki"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
]
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.0" version = "1.2.0"
@ -1200,9 +1723,21 @@ dependencies = [
"mio", "mio",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"tokio-macros",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "tokio-macros"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tokio-native-tls" name = "tokio-native-tls"
version = "0.3.1" version = "0.3.1"
@ -1223,6 +1758,17 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-stream"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.13" version = "0.7.13"
@ -1236,6 +1782,91 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tonic"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
dependencies = [
"async-stream",
"async-trait",
"axum",
"base64",
"bytes",
"h2",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-timeout",
"hyper-util",
"percent-encoding",
"pin-project",
"prost",
"socket2",
"tokio",
"tokio-stream",
"tower 0.4.13",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tonic-build"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11"
dependencies = [
"prettyplease",
"proc-macro2",
"prost-build",
"prost-types",
"quote",
"syn",
]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"indexmap 1.9.3",
"pin-project",
"pin-project-lite",
"rand",
"slab",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
"pin-project-lite",
"sync_wrapper",
"tokio",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.3" version = "0.3.3"
@ -1249,9 +1880,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [ dependencies = [
"pin-project-lite", "pin-project-lite",
"tracing-attributes",
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-attributes"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.33" version = "0.1.33"
@ -1314,6 +1957,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"

@ -4,10 +4,22 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
ed25519-dalek = { version = "2.1.1", default-features = false, features = ["std", "alloc", "rand_core", "pem"] }
rand_core = { version = "0.6.4", features = ["alloc", "getrandom", "std"] }
anyhow = "1.0.94" anyhow = "1.0.94"
cidr = { version = "0.3.0", features = ["serde"] } env_logger = "0.11.6"
rand = "0.8.5" log = "0.4.22"
reqwest = { version = "0.12.9", features = ["blocking"] } reqwest = { version = "0.12.9", features = ["blocking"] }
serde = { version = "1.0.215", features = ["derive"] } serde = { version = "1.0.215", features = ["derive"] }
serde_yaml = "0.9.34" serde_yaml = "0.9.34"
sha2 = "0.10.8" sha2 = "0.10.8"
lazy_static = "1.5.0"
prost = "0.13.4"
prost-types = "0.13.4"
rand = "0.8.5"
tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread"] }
tokio-stream = "0.1.17"
tonic = "0.12"
[build-dependencies]
tonic-build = "0.12"

145
brain.proto Normal file

@ -0,0 +1,145 @@
syntax = "proto3";
package brain;
message Empty {
}
message NodePubkey {
string node_pubkey = 1;
}
message RegisterNodeReq {
string node_pubkey = 1;
string owner_pubkey = 2;
}
message NodeResourceReq {
string node_pubkey = 1;
uint32 avail_ports = 2;
uint32 avail_ipv4 = 3;
uint32 avail_ipv6 = 4;
uint32 avail_vcpus = 5;
uint32 avail_memory_mb = 6;
uint32 avail_storage_gb = 7;
uint32 max_ports_per_vm = 8;
}
message NewVMReq {
string uuid = 1; // UUID is empty when CLI sends request; brain sets UUID
string hostname = 2;
string admin_pubkey = 3;
string node_pubkey = 4;
repeated uint32 extra_ports = 5;
bool public_ipv4 = 6;
bool public_ipv6 = 7;
uint32 disk_size_gb = 8;
uint32 vcpus = 9;
uint32 memory_mb = 10;
string kernel_url = 11;
string kernel_sha = 12;
string dtrfs_url = 13;
string dtrfs_sha = 14;
}
message UpdateVMReq {
string uuid = 1;
uint32 disk_size_gb = 3;
uint32 vcpus = 4;
uint32 memory_mb = 5;
string kernel_url = 6;
string kernel_sha = 7;
string dtrfs_url = 8;
string dtrfs_sha = 9;
}
message UpdateVMResp {
string uuid = 1;
string error = 3;
}
message VMContract {
string uuid = 1;
string hostname = 2;
string admin_pubkey = 3;
string node_pubkey = 4;
repeated uint32 exposed_ports = 5;
string public_ipv4 = 6;
string public_ipv6 = 7;
uint32 disk_size_gb = 8;
uint32 vcpus = 9;
uint32 memory_mb = 10;
string kernel_sha = 11;
string dtrfs_sha = 12;
string created_at = 13;
string updated_at = 14;
}
message ListVMContractsReq {
string admin_pubkey = 1;
string node_pubkey = 2;
string uuid = 3;
}
message NewVmRespIP {
uint32 nic_index = 1;
string address = 2;
string mask = 3;
string gateway = 4;
}
message NewVMResp {
string uuid = 1;
repeated uint32 exposed_ports = 2;
string ovmf_hash = 5;
// This is needed to allow the CLI to build the kernel params from known data.
// The CLI will use the kernel params to get the measurement.
repeated NewVmRespIP ips = 6;
string error = 7;
}
message DeleteVMReq {
string uuid = 1;
}
service BrainDaemonService {
rpc RegisterNode (RegisterNodeReq) returns (Empty);
rpc SendNodeResources (stream NodeResourceReq) returns (Empty);
rpc GetNewVMReqs (NodePubkey) returns (stream NewVMReq);
rpc SendNewVMResp (stream NewVMResp) returns (Empty);
rpc GetDeleteVMReq (NodePubkey) returns (stream DeleteVMReq);
rpc ListVMContracts (ListVMContractsReq) returns (stream VMContract);
rpc GetUpdateVMReq (NodePubkey) returns (stream UpdateVMReq);
rpc SendUpdateVMResp (stream UpdateVMResp) returns (Empty);
}
message NodeFilters {
uint32 free_ports = 1;
bool offers_ipv4 = 2;
bool offers_ipv6 = 3;
uint32 vcpus = 4;
uint32 memory_mb = 5;
uint32 storage_gb = 6;
string country = 7;
string region = 8;
string city = 9;
string ip = 10;
}
message NodeListResp {
string node_pubkey = 1;
string country = 2;
string region = 3;
string city = 4;
string ip = 5; // required for latency test
uint32 server_rating = 6;
uint32 provider_rating = 7;
}
service BrainCliService {
rpc CreateVMContract (NewVMReq) returns (NewVMResp);
rpc ListVMContracts (ListVMContractsReq) returns (stream VMContract);
rpc ListNodes (NodeFilters) returns (stream NodeListResp);
rpc GetOneNode (NodeFilters) returns (NodeListResp);
rpc DeleteVM (DeleteVMReq) returns (Empty);
rpc UpdateVM (UpdateVMReq) returns (UpdateVMResp);
}

6
build.rs Normal file

@ -0,0 +1,6 @@
fn main() {
tonic_build::configure()
.build_server(true)
.compile_protos(&["brain.proto"], &["proto"])
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
}

@ -1,25 +1,24 @@
brain_url: "http://164.92.249.180:31337"
max_cores_per_vm: 4 max_cores_per_vm: 4
max_vcpu_reservation: 8 max_vcpu_reservation: 8
max_mem_reservation_mb: 25000 max_mem_reservation_mb: 25000
network_interfaces: network_interfaces:
- driver: "MACVTAP" - driver: "MACVTAP"
device: "eno8303" device: "eno8303"
ipv4: ipv4_ranges:
- subnet: "173.234.136.152/29" - first_ip: "173.234.136.154"
last_ip: "173.234.136.155"
netmask: "27"
gateway: "173.234.136.158" gateway: "173.234.136.158"
reserved_addrs: - first_ip: "173.234.137.17"
- "173.234.136.153" last_ip: "173.234.137.17"
- "173.234.136.156" netmask: "27"
- "173.234.136.157"
- "173.234.136.158"
- subnet: "173.234.137.16/31"
gateway: "173.234.137.30" gateway: "173.234.137.30"
reserved_addrs: ipv6_ranges:
- "173.234.137.16" - first_ip: "2a0d:3003:b666:a00c:0002:0000:0000:0011"
ipv6: last_ip: "2a0d:3003:b666:a00c:0002:0000:0000:fffc"
- subnet: "2a0d:3003:b666:a00c:2::/112" netmask: "64"
gateway: "2a0d:3003:b666:a00c::1" gateway: "2a0d:3003:b666:a00c::1"
reserved_addrs: []
volumes: volumes:
- path: "/opt/detee_vms/" - path: "/opt/detee_vms/"
max_reservation_gb: 200 max_reservation_gb: 200

3
rustfmt.toml Normal file

@ -0,0 +1,3 @@
reorder_impl_items = true
use_small_heuristics = "Max"
imports_granularity = "Crate"

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
OVMF_PATH="/var/lib/detee/boot/0346619257269b9a61ee003e197d521b8e2283483070d163a34940d6a1d40d76";
[[ -z "$VM_UUID" ]] && { [[ -z "$VM_UUID" ]] && {
echo "Environment variable VM_UUID is not set." echo "Environment variable VM_UUID is not set."
@ -65,7 +66,7 @@ qemu-system-x86_64 $qemu_device_params \
-machine q35,confidential-guest-support=sev0,memory-backend=ram1 \ -machine q35,confidential-guest-support=sev0,memory-backend=ram1 \
-smp $VCPUS,maxcpus=$VCPUS \ -smp $VCPUS,maxcpus=$VCPUS \
-m $MEMORY,slots=5,maxmem=$MAX_MEMORY \ -m $MEMORY,slots=5,maxmem=$MAX_MEMORY \
-no-reboot -bios /usr/share/edk2/ovmf/OVMF.amdsev.fd \ -no-reboot -bios "$OVMF_PATH" \
-drive file=${DISK},if=none,id=disk0,format=qcow2 \ -drive file=${DISK},if=none,id=disk0,format=qcow2 \
-device virtio-blk-pci,drive=disk0 \ -device virtio-blk-pci,drive=disk0 \
-object memory-backend-memfd,id=ram1,size=$MEMORY,share=true,prealloc=false \ -object memory-backend-memfd,id=ram1,size=$MEMORY,share=true,prealloc=false \

@ -1,9 +1,10 @@
#![allow(dead_code)] #![allow(dead_code)]
use cidr::{Ipv4Cidr, Ipv6Cidr}; use anyhow::Result;
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashSet; use std::{
use std::net::{Ipv4Addr, Ipv6Addr}; net::{Ipv4Addr, Ipv6Addr},
use std::ops::Range; ops::Range,
};
#[derive(Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]
pub struct Volume { pub struct Volume {
@ -13,24 +14,26 @@ pub struct Volume {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct IPv4Range { pub struct IPv4Range {
pub subnet: Ipv4Cidr, pub first_ip: Ipv4Addr,
pub last_ip: Ipv4Addr,
pub netmask: String,
pub gateway: Ipv4Addr, pub gateway: Ipv4Addr,
pub reserved_addrs: HashSet<Ipv4Addr>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct IPv6Range { pub struct IPv6Range {
pub subnet: Ipv6Cidr, pub first_ip: Ipv6Addr,
pub last_ip: Ipv6Addr,
pub netmask: String,
pub gateway: Ipv6Addr, pub gateway: Ipv6Addr,
pub reserved_addrs: HashSet<Ipv6Addr>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Interface { pub struct Interface {
pub driver: InterfaceType, pub driver: InterfaceType,
pub device: String, pub device: String,
pub ipv4: Vec<IPv4Range>, pub ipv4_ranges: Vec<IPv4Range>,
pub ipv6: Vec<IPv6Range>, pub ipv6_ranges: Vec<IPv6Range>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -42,6 +45,7 @@ pub enum InterfaceType {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Config { pub struct Config {
pub brain_url: String,
pub max_cores_per_vm: usize, pub max_cores_per_vm: usize,
pub max_vcpu_reservation: usize, pub max_vcpu_reservation: usize,
pub max_mem_reservation_mb: usize, pub max_mem_reservation_mb: usize,
@ -60,10 +64,7 @@ mod range_format {
where where
S: Serializer, S: Serializer,
{ {
let range_repr = RangeRepr { let range_repr = RangeRepr { start: range.start, end: range.end };
start: range.start,
end: range.end,
};
range_repr.serialize(serializer) range_repr.serialize(serializer)
} }
@ -83,10 +84,93 @@ mod range_format {
} }
impl Config { impl Config {
pub fn load_from_disk(path: &str) -> Result<Self, Box<dyn std::error::Error>> { pub fn load_from_disk(path: &str) -> Result<Self> {
let content = std::fs::read_to_string(path)?; let content = std::fs::read_to_string(path)?;
let config: Config = serde_yaml::from_str(&content)?; let config: Config = serde_yaml::from_str(&content)?;
for nic in &config.network_interfaces {
for range in &nic.ipv4_ranges {
let ipv4_netmask = range.netmask.parse::<u8>()?;
if ipv4_netmask > 32 {
return Err(anyhow::anyhow!(
"IPv4 netmask must be in short format: a number from 1 to 32"
));
}
if range.first_ip.to_bits() > range.last_ip.to_bits() {
return Err(anyhow::anyhow!(
"For range {range:?} first ip is bigger than last ip."
));
}
let expected_netmask = std::cmp::min(
calc_ipv4_netmask(range.first_ip, range.gateway),
calc_ipv4_netmask(range.last_ip, range.gateway),
);
if expected_netmask < ipv4_netmask as u32 {
return Err(anyhow::anyhow!(
"Your netmask is too small to include the IPs and also the gateway: {range:?}"
));
}
}
for range in &nic.ipv6_ranges {
let ipv6_netmask = range.netmask.parse::<u8>()?;
if ipv6_netmask > 128 {
return Err(anyhow::anyhow!(
"IPv6 netmask must be in short format: a number from 1 to 128"
));
}
if range.first_ip.to_bits() > range.last_ip.to_bits() {
return Err(anyhow::anyhow!(
"For range {range:?} first ip is bigger than last ip."
));
}
let expected_netmask = std::cmp::min(
calc_ipv6_netmask(range.first_ip, range.gateway),
calc_ipv6_netmask(range.last_ip, range.gateway),
);
if expected_netmask < ipv6_netmask as u128 {
return Err(anyhow::anyhow!(
"Your netmask is too small to include the IPs and also the gateway: {range:?}"
));
}
}
}
Ok(config) Ok(config)
} }
} }
fn calc_ipv4_netmask(ip: Ipv4Addr, gateway: Ipv4Addr) -> u32 {
// Convert the IPs to u32 for easier bit manipulation
let ip_u32 = u32::from(ip);
let gateway_u32 = u32::from(gateway);
// Find the smallest common prefix
let mut prefix_len = 0;
for i in 1..=32 {
if (ip_u32 >> (32 - i)) == (gateway_u32 >> (32 - i)) {
prefix_len = i;
} else {
break;
}
}
// Return the mask as a string
prefix_len
}
fn calc_ipv6_netmask(ip: Ipv6Addr, gateway: Ipv6Addr) -> u128 {
// Convert the IPs to u128 for easier bit manipulation
let ip_u128 = u128::from(ip);
let gateway_u128 = u128::from(gateway);
// Find the smallest common prefix
let mut prefix_len = 0;
for i in 1..=128 {
if (ip_u128 >> (128 - i)) == (gateway_u128 >> (128 - i)) {
prefix_len = i;
} else {
break;
}
}
// Return the mask as a string
prefix_len
}

@ -4,7 +4,11 @@ pub(crate) const DEFAULT_OVMF: &str = "/usr/share/edk2/ovmf/OVMF.amdsev.fd";
pub(crate) const VM_BOOT_DIR: &str = "/var/lib/detee/boot/"; pub(crate) const VM_BOOT_DIR: &str = "/var/lib/detee/boot/";
pub(crate) const USED_RESOURCES: &str = "/etc/detee/daemon/used_resources.yaml"; pub(crate) const USED_RESOURCES: &str = "/etc/detee/daemon/used_resources.yaml";
pub(crate) const VM_CONFIG_DIR: &str = "/etc/detee/daemon/vms/"; pub(crate) const VM_CONFIG_DIR: &str = "/etc/detee/daemon/vms/";
pub(crate) const SECRET_KEY_PATH: &str = "/etc/detee/daemon/node_secret_key.pem";
pub(crate) const DAEMON_CONFIG_PATH: &str = "/etc/detee/daemon/config.yaml"; pub(crate) const DAEMON_CONFIG_PATH: &str = "/etc/detee/daemon/config.yaml";
pub(crate) const START_VM_SCRIPT: &str = "/usr/local/bin/detee/start_qemu_vm.sh"; pub(crate) const START_VM_SCRIPT: &str = "/usr/local/bin/detee/start_qemu_vm.sh";
// TODO: research if other CPU types provide better performance // TODO: research if other CPU types provide better performance
pub(crate) const QEMU_VM_CPU_TYPE: &str = "EPYC-v4"; pub(crate) const QEMU_VM_CPU_TYPE: &str = "EPYC-v4";
// If you modify this, also modify scripts/start_qemu_vm.sh
pub(crate) const OVMF_HASH: &str = "0346619257269b9a61ee003e197d521b8e2283483070d163a34940d6a1d40d76";
pub(crate) const OVMF_URL: &str = "https://drive.google.com/uc?export=download&id=1V-vLkaiLaGmFSjrN84Z6nELQOxKNAoSJ";

226
src/grpc.rs Normal file

@ -0,0 +1,226 @@
#![allow(dead_code)]
pub mod brain {
tonic::include_proto!("brain");
}
use anyhow::Result;
use brain::{
brain_daemon_service_client::BrainDaemonServiceClient, DeleteVmReq, ListVmContractsReq,
NewVmReq, NewVmResp, NodePubkey, NodeResourceReq, RegisterNodeReq, UpdateVmReq, UpdateVmResp,
VmContract,
};
use lazy_static::lazy_static;
use log::{debug, error, info, warn};
use std::{fs::File, io::Write};
use tokio::{
sync::mpsc::{Receiver, Sender},
task::JoinSet,
};
use tokio_stream::{wrappers::ReceiverStream, StreamExt};
use tonic::transport::Channel;
lazy_static! {
static ref PUBLIC_KEY: String = get_public_key();
}
fn create_secret_key() -> Result<ed25519_dalek::SigningKey> {
use ed25519_dalek::pkcs8::{spki::der::pem::LineEnding, EncodePrivateKey};
let key_path = crate::constants::SECRET_KEY_PATH;
info!("Creating new secret key at {}", key_path);
let sk = ed25519_dalek::SigningKey::generate(&mut rand_core::OsRng);
let sk_pem = sk.to_pkcs8_pem(LineEnding::default()).unwrap();
let mut file = File::create(key_path)?;
file.write_all(sk_pem.as_bytes())?;
Ok(sk)
}
fn load_secret_key() -> Result<ed25519_dalek::SigningKey> {
use ed25519_dalek::pkcs8::DecodePrivateKey;
let secret_key_pem = match std::fs::read_to_string(crate::constants::SECRET_KEY_PATH) {
Ok(secret_key_pem) => secret_key_pem,
Err(e) => {
warn!("Could not load secret key due to error: {e:?}");
return Ok(create_secret_key()?);
}
};
Ok(ed25519_dalek::SigningKey::from_pkcs8_pem(&secret_key_pem)?)
}
pub fn get_public_key() -> String {
use ed25519_dalek::pkcs8::{spki::der::pem::LineEnding, EncodePublicKey};
let pubkey = load_secret_key()
.unwrap()
.verifying_key()
.to_public_key_pem(LineEnding::default())
.unwrap()
.lines()
.nth(1)
.unwrap()
.to_string();
log::info!("Loaded the following public key: {pubkey}");
pubkey
}
pub async fn list_contracts(brain_url: String) -> Result<Vec<VmContract>> {
let mut client = BrainDaemonServiceClient::connect(brain_url).await?;
let mut contracts = Vec::new();
let mut grpc_stream = client
.list_vm_contracts(ListVmContractsReq {
node_pubkey: PUBLIC_KEY.to_string(),
..Default::default()
})
.await?
.into_inner();
while let Some(stream_update) = grpc_stream.next().await {
match stream_update {
Ok(node) => {
debug!("Received contract from brain: {node:?}");
contracts.push(node);
}
Err(e) => {
warn!("Received error instead of contracts: {e:?}");
}
}
}
info!("Brain terminated list_contracts stream.");
Ok(contracts)
}
async fn listen_for_new_vm_reqs(
mut client: BrainDaemonServiceClient<Channel>,
tx: Sender<NewVmReq>,
) -> Result<()> {
debug!("starting listen_for_new_vm_reqs");
let node_pubkey = PUBLIC_KEY.clone();
let mut grpc_stream = client.get_new_vm_reqs(NodePubkey { node_pubkey }).await?.into_inner();
while let Some(stream_update) = grpc_stream.next().await {
match stream_update {
Ok(req) => {
info!("Received new vm request: {req:?}");
let _ = tx.send(req).await;
}
Err(e) => {
warn!("Brain disconnected from listen_for_new_vm_reqs: {e}");
}
}
}
debug!("listen_for_new_vm_reqs is about to exit");
Ok(())
}
async fn send_newvm_resp(
mut client: BrainDaemonServiceClient<Channel>,
rx: Receiver<NewVmResp>,
) -> Result<()> {
debug!("starting send_newvm_resp stream");
let rx_stream = ReceiverStream::new(rx);
client.send_new_vm_resp(rx_stream).await?;
debug!("send_newvm_resp is about to exit");
Ok(())
}
async fn send_node_resources(
mut client: BrainDaemonServiceClient<Channel>,
rx: Receiver<NodeResourceReq>,
) -> Result<()> {
debug!("starting send_newvm_resp stream");
let rx_stream = ReceiverStream::new(rx).map(|mut node_resources| {
node_resources.node_pubkey = get_public_key();
node_resources
});
client.send_node_resources(rx_stream).await?;
debug!("send_newvm_resp is about to exit");
Ok(())
}
async fn register_node(mut client: BrainDaemonServiceClient<Channel>) {
debug!("Starting node registration...");
let req = RegisterNodeReq {
node_pubkey: PUBLIC_KEY.clone(),
owner_pubkey: "IamTheOwnerOf".to_string() + &PUBLIC_KEY,
};
match client.register_node(req).await {
Ok(_) => {
info!("Registered as 10.0.10.1 from Bruma/Cyrodiil with ID {}", PUBLIC_KEY.clone())
}
Err(e) => error!("Could not register node data: {e:?}"),
};
}
async fn listen_for_deleted_vms(
mut client: BrainDaemonServiceClient<Channel>,
tx: Sender<DeleteVmReq>,
) -> Result<()> {
debug!("starting listen_for_new_vm_reqs");
let node_pubkey = PUBLIC_KEY.clone();
let mut grpc_stream = client.get_delete_vm_req(NodePubkey { node_pubkey }).await?.into_inner();
while let Some(stream_update) = grpc_stream.next().await {
match stream_update {
Ok(req) => {
info!("Received delete vm request: {req:?}");
let _ = tx.send(req).await;
}
Err(e) => {
warn!("Brain disconnected from listen_for_deleted_vms: {e}");
}
}
}
debug!("listen_for_new_vm_reqs is about to exit");
Ok(())
}
async fn listen_for_update_vm_reqs(
mut client: BrainDaemonServiceClient<Channel>,
tx: Sender<UpdateVmReq>,
) -> Result<()> {
debug!("starting listen_for_update_vm_reqs");
let node_pubkey = PUBLIC_KEY.clone();
let mut grpc_stream = client.get_update_vm_req(NodePubkey { node_pubkey }).await?.into_inner();
while let Some(stream_update) = grpc_stream.next().await {
match stream_update {
Ok(req) => {
info!("Received update vm request: {req:?}");
let _ = tx.send(req).await;
}
Err(e) => {
warn!("Brain disconnected from listen_for_update_vm_reqs: {e}");
}
}
}
debug!("listen_for_update_vm_reqs is about to exit");
Ok(())
}
async fn send_updatevm_resp(
mut client: BrainDaemonServiceClient<Channel>,
rx: Receiver<UpdateVmResp>,
) -> Result<()> {
debug!("starting send_updatevm_resp stream");
let rx_stream = ReceiverStream::new(rx);
client.send_update_vm_resp(rx_stream).await?;
debug!("send_updatevm_resp is about to exit");
Ok(())
}
pub struct ConnectionData {
pub brain_url: String,
pub newvm_tx: Sender<NewVmReq>,
pub confirm_vm_rx: Receiver<NewVmResp>,
pub delete_vm_tx: Sender<DeleteVmReq>,
pub resources_rx: Receiver<NodeResourceReq>,
}
pub async fn connect_and_run(cd: ConnectionData) -> Result<()> {
let client = BrainDaemonServiceClient::connect(cd.brain_url).await?;
let mut streaming_tasks = JoinSet::new();
register_node(client.clone()).await;
streaming_tasks.spawn(listen_for_new_vm_reqs(client.clone(), cd.newvm_tx));
streaming_tasks.spawn(send_newvm_resp(client.clone(), cd.confirm_vm_rx));
streaming_tasks.spawn(listen_for_deleted_vms(client.clone(), cd.delete_vm_tx));
streaming_tasks.spawn(send_node_resources(client.clone(), cd.resources_rx));
let task_output = streaming_tasks.join_next().await;
warn!("One stream exited: {task_output:?}");
Ok(())
}

@ -1,62 +1,234 @@
#[allow(dead_code)]
mod config; mod config;
mod constants; mod constants;
mod grpc;
mod state; mod state;
mod tcontract;
use crate::config::Config; use crate::{config::Config, grpc::brain};
use crate::state::NewVMRequest; use anyhow::Result;
use crate::state::UpdateVMReq; use log::{debug, info, warn};
use std::fs::read_dir; use tokio::{
sync::mpsc::{Receiver, Sender},
time::{sleep, Duration},
};
fn main() -> Result<(), Box<dyn std::error::Error>> { #[allow(dead_code)]
let config = Config::load_from_disk(crate::constants::DAEMON_CONFIG_PATH)?; struct VMHandler {
let mut res = match state::Resources::load_from_disk() { new_vm_req_chan: Receiver<brain::NewVmReq>,
new_vm_resp_chan: Sender<brain::NewVmResp>,
delete_vm_chan: Receiver<brain::DeleteVmReq>,
resources_chan: Sender<brain::NodeResourceReq>,
config: Config,
res: state::Resources,
}
#[allow(dead_code)]
impl VMHandler {
fn new(
new_vm_req_chan: Receiver<brain::NewVmReq>,
new_vm_resp_chan: Sender<brain::NewVmResp>,
delete_vm_chan: Receiver<brain::DeleteVmReq>,
resources_chan: Sender<brain::NodeResourceReq>,
) -> Self {
let config = match Config::load_from_disk(crate::constants::DAEMON_CONFIG_PATH) {
Ok(config) => config,
Err(e) => panic!("Could not load config: {e:?}"),
};
let res = match state::Resources::load_from_disk() {
Ok(res) => res, Ok(res) => res,
Err(e) => { Err(e) => {
println!("Could not load resources from disk: {e:?}"); warn!("Could not load resources from disk: {e:?}");
println!("Creating new resource calculator."); info!("Creating new resource calculator.");
state::Resources::new(&config.volumes) state::Resources::new(&config.volumes)
} }
}; };
Self { new_vm_req_chan, new_vm_resp_chan, delete_vm_chan, resources_chan, config, res }
}
for entry in read_dir("/etc/detee/daemon/newvmreq/")? { fn get_available_ips(&self) -> (u32, u32) {
let entry = entry?; let mut avail_ipv4 = 0;
let path = entry.path(); let mut avail_ipv6 = 0;
if path.is_file() { for nic in self.config.network_interfaces.iter() {
let new_vm_req = NewVMRequest::from_file(path.to_str().unwrap())?; for range in nic.ipv4_ranges.iter() {
let vm = state::VM::new(new_vm_req, &config, &mut res).unwrap(); avail_ipv4 += (range.last_ip.to_bits() + 1) - range.first_ip.to_bits();
vm.start()?; }
println!("started vm {}", vm.uuid); for range in nic.ipv6_ranges.iter() {
} avail_ipv6 += (range.last_ip.to_bits() + 1) - range.first_ip.to_bits();
} }
}
for entry in read_dir("/etc/detee/daemon/deletevm/")? { (
let entry = entry?; avail_ipv4.saturating_sub(self.res.reserved_ipv4.len() as u32),
let path = entry.path(); avail_ipv6.saturating_sub(self.res.reserved_ipv6.len() as u128) as u32,
if path.is_file() { )
let vm_id = path.file_name().unwrap().to_str().unwrap(); }
let content = std::fs::read_to_string(
crate::constants::VM_CONFIG_DIR.to_string() + vm_id + ".yaml", async fn send_node_resources(&mut self) {
)?; let (avail_ipv4, avail_ipv6) = self.get_available_ips();
let vm: crate::state::VM = serde_yaml::from_str(&content)?; let mut avail_storage_gb = 0;
vm.delete(&mut res)?; for volume in self.config.volumes.iter() {
println!("deleted vm {}", vm.uuid); avail_storage_gb += volume.max_reservation_gb;
} if let Some(reservation) = self.res.reserved_storage.get(&volume.path) {
} avail_storage_gb -= reservation;
}
for entry in read_dir("/etc/detee/daemon/updatedvmreq/")? { }
let entry = entry?; let avail_storage_gb = avail_storage_gb as u32;
let path = entry.path(); let res = brain::NodeResourceReq {
if path.is_file() { node_pubkey: String::new(),
let update_vm_req = UpdateVMReq::from_file(path.to_str().unwrap())?; avail_ports: (self.config.public_port_range.len() - self.res.reserved_ports.len())
let content = std::fs::read_to_string( as u32,
crate::constants::VM_CONFIG_DIR.to_string() + &update_vm_req.uuid + ".yaml", avail_ipv4,
)?; avail_ipv6,
let mut vm: crate::state::VM = serde_yaml::from_str(&content)?; avail_vcpus: (self.config.max_vcpu_reservation - self.res.reserved_vcpus) as u32,
vm.update(update_vm_req, &config, &mut res).unwrap(); avail_memory_mb: (self.config.max_mem_reservation_mb - self.res.reserved_memory) as u32,
println!("updated vm {}", vm.uuid); avail_storage_gb,
max_ports_per_vm: self.config.max_ports_per_vm as u32,
};
debug!("sending node resources on brain: {res:?}");
let _ = self.resources_chan.send(res).await;
}
async fn handle_new_vm_req(&mut self, new_vm_req: brain::NewVmReq) {
debug!("Processing new vm request: {new_vm_req:?}");
let uuid = new_vm_req.uuid.clone();
match state::VM::new(new_vm_req.into(), &self.config, &mut self.res) {
Ok(vm) => match vm.start() {
Ok(_) => {
info!("Succesfully started VM {uuid}");
let _ = self.new_vm_resp_chan.send(vm.into()).await;
self.send_node_resources().await;
}
Err(e) => {
log::error!("Could not start VM {uuid}: {e:?}");
let _ = self
.new_vm_resp_chan
.send(brain::NewVmResp {
uuid,
error: "This node has an internal error. Choose another node."
.to_string(),
..Default::default()
})
.await;
}
},
Err(e) => match e {
crate::state::VMCreationErrors::VMAlreadyExists(vm) => {
log::info!(
"Got NewVmReq for VM {}, that already exist. Will send NewVmResp.",
vm.uuid
);
let _ = self.new_vm_resp_chan.send(vm.into()).await;
}
_ => {
warn!("Refusing to service vm {uuid} due to error: {e:?}");
let _ = self
.new_vm_resp_chan
.send(brain::NewVmResp {
uuid,
error: format!("{e:?}"),
..Default::default()
})
.await;
}
},
} }
} }
fn handle_delete_vm(&mut self, delete_vm_req: brain::DeleteVmReq) -> Result<()> {
let vm_id = delete_vm_req.uuid;
let content =
std::fs::read_to_string(constants::VM_CONFIG_DIR.to_string() + &vm_id + ".yaml")?;
let vm: state::VM = serde_yaml::from_str(&content)?;
vm.delete(&mut self.res)?;
Ok(()) Ok(())
}
async fn run(mut self) {
self.send_node_resources().await;
loop {
tokio::select! {
Some(new_vm_req) = self.new_vm_req_chan.recv() => {
self.handle_new_vm_req(new_vm_req).await;
}
Some(delete_vm_req) = self.delete_vm_chan.recv() => {
let uuid = delete_vm_req.uuid.clone();
if let Err(e) = self.handle_delete_vm(delete_vm_req) {
log::error!("Could not delete vm {uuid}: {e:?}");
} else {
self.send_node_resources().await;
}
}
else => {
log::error!("All data channels closed.");
return;
}
}
}
}
fn clear_deleted_contracts(&mut self, contracts: Vec<brain::VmContract>) {
for uuid in self.res.existing_vms.clone() {
if contracts.iter().find(|c| c.uuid == uuid).is_none() {
info!("VM {uuid} exists locally but not found in brain. Deleting...");
let content = match std::fs::read_to_string(
crate::constants::VM_CONFIG_DIR.to_string() + &uuid + ".yaml",
) {
Ok(content) => content,
Err(e) => {
log::error!("Could not find VM config for {uuid}. Cannot delete VM: {e:?}");
continue;
}
};
let vm: crate::state::VM = match serde_yaml::from_str(&content) {
Ok(vm) => vm,
Err(e) => {
log::error!("VM config corrupted for {uuid}. Cannot delete VM: {e:?}");
continue;
}
};
match vm.delete(&mut self.res) {
Ok(()) => info!("Successfully deleted VM {uuid}"),
Err(e) => log::error!("Deletion failed for VM {uuid}: {e:?}"),
}
}
}
}
}
#[tokio::main]
async fn main() {
env_logger::builder().filter_level(log::LevelFilter::Debug).init();
loop {
let (newvm_tx, newvm_rx) = tokio::sync::mpsc::channel(6);
let (confirm_vm_tx, confirm_vm_rx) = tokio::sync::mpsc::channel(6);
let (delete_vm_tx, delete_vm_rx) = tokio::sync::mpsc::channel(6);
let (resources_tx, resources_rx) = tokio::sync::mpsc::channel(6);
let mut vm_handler = VMHandler::new(newvm_rx, confirm_vm_tx, delete_vm_rx, resources_tx);
let brain_url = vm_handler.config.brain_url.clone();
info!("Trying to get VM Contracts from Brain to see if some Contracts got removed...");
match grpc::list_contracts(brain_url.clone()).await {
Ok(contracts) => vm_handler.clear_deleted_contracts(contracts),
Err(e) => log::error!("Could not get contracts from brain: {e:?}"),
};
tokio::spawn(async move {
vm_handler.run().await;
});
info!("Connecting to brain...");
if let Err(e) = grpc::connect_and_run(grpc::ConnectionData {
brain_url,
newvm_tx,
confirm_vm_rx,
delete_vm_tx,
resources_rx,
})
.await
{
log::error!("The connection broke: {e}");
}
sleep(Duration::from_secs(3)).await;
}
} }

@ -1,32 +1,28 @@
#![allow(dead_code)] #![allow(dead_code)]
use crate::config::Config; use crate::{config::Config, constants::*, grpc::brain};
use crate::constants::*; use anyhow::{anyhow, Result};
use anyhow::anyhow; use serde::{Deserialize, Serialize};
use anyhow::Result;
use serde::Deserialize;
use serde::Serialize;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::collections::HashMap; use std::{
use std::collections::HashSet; collections::{HashMap, HashSet},
use std::fs; fs,
use std::fs::remove_file; fs::{remove_file, File},
use std::fs::File; io::{Read, Write},
use std::io::Read; path::Path,
use std::io::Write; process::Command,
use std::net::{Ipv4Addr, Ipv6Addr}; };
use std::path::Path;
use std::process::Command;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct Resources { pub struct Resources {
existing_vms: HashSet<String>, pub existing_vms: HashSet<String>,
// QEMU does not support MHz limiation // QEMU does not support MHz limiation
reserved_vcpus: usize, pub reserved_vcpus: usize,
reserved_memory: usize, pub reserved_memory: usize,
reserved_ports: HashSet<u16>, pub reserved_ports: HashSet<u16>,
reserved_storage: HashMap<String, usize>, pub reserved_storage: HashMap<String, usize>,
reserved_ips: HashSet<String>, pub reserved_ipv4: HashSet<String>,
reserved_if_names: HashSet<String>, pub reserved_ipv6: HashSet<String>,
pub reserved_if_names: HashSet<String>,
// sha256sum -> absolute path // sha256sum -> absolute path
boot_files: HashSet<String>, boot_files: HashSet<String>,
} }
@ -60,7 +56,8 @@ impl Resources {
reserved_memory: 0, reserved_memory: 0,
reserved_ports: HashSet::new(), reserved_ports: HashSet::new(),
reserved_storage: HashMap::new(), reserved_storage: HashMap::new(),
reserved_ips: HashSet::new(), reserved_ipv4: HashSet::new(),
reserved_ipv6: HashSet::new(),
reserved_if_names: HashSet::new(), reserved_if_names: HashSet::new(),
boot_files: HashSet::new(), boot_files: HashSet::new(),
}; };
@ -111,11 +108,8 @@ impl Resources {
fn available_if_name(&mut self) -> String { fn available_if_name(&mut self) -> String {
use rand::{distributions::Alphanumeric, Rng}; use rand::{distributions::Alphanumeric, Rng};
loop { loop {
let mut interface_name: String = rand::thread_rng() let mut interface_name: String =
.sample_iter(&Alphanumeric) rand::thread_rng().sample_iter(&Alphanumeric).take(9).map(char::from).collect();
.take(9)
.map(char::from)
.collect();
interface_name = "detee".to_string() + &interface_name; interface_name = "detee".to_string() + &interface_name;
if !self.reserved_if_names.contains(&interface_name) { if !self.reserved_if_names.contains(&interface_name) {
return interface_name; return interface_name;
@ -123,13 +117,15 @@ impl Resources {
} }
} }
fn available_ipv4(&mut self, config: &Config) -> Option<VMNIC> { fn available_ipv4(&mut self, nics: &Vec<crate::config::Interface>) -> Option<VMNIC> {
for nic in config.network_interfaces.iter() { for nic in nics.iter() {
for range in nic.ipv4.iter() { for range in nic.ipv4_ranges.iter() {
for ip in range.subnet.iter().skip(1) { let first_ip = range.first_ip.to_bits();
if !range.reserved_addrs.contains(&ip.address()) let last_ip = range.last_ip.to_bits();
&& !self.reserved_ips.contains(&ip.address().to_string()) for ip in first_ip..last_ip + 1 {
&& ip.address() != range.gateway let ip_addr = std::net::Ipv4Addr::from_bits(ip);
if !self.reserved_ipv4.contains(&ip_addr.to_string())
&& ip_addr != range.gateway
{ {
let if_config = match nic.driver { let if_config = match nic.driver {
crate::config::InterfaceType::MACVTAP => InterfaceConfig::MACVTAP { crate::config::InterfaceType::MACVTAP => InterfaceConfig::MACVTAP {
@ -140,14 +136,14 @@ impl Resources {
name: self.available_if_name(), name: self.available_if_name(),
device: nic.device.clone(), device: nic.device.clone(),
}, },
crate::config::InterfaceType::Bridge => InterfaceConfig::Bridge { crate::config::InterfaceType::Bridge => {
device: nic.device.clone(), InterfaceConfig::Bridge { device: nic.device.clone() }
}, }
}; };
let mut ips = Vec::new(); let mut ips = Vec::new();
ips.push(IPConfig { ips.push(IPConfig {
address: ip.address().to_string(), address: ip_addr.to_string(),
mask: calc_ipv4_netmask(ip.address(), range.gateway), mask: range.netmask.clone(),
gateway: range.gateway.to_string(), gateway: range.gateway.to_string(),
}); });
return Some(VMNIC { if_config, ips }); return Some(VMNIC { if_config, ips });
@ -158,13 +154,15 @@ impl Resources {
None None
} }
fn available_ipv6(&mut self, config: &Config) -> Option<VMNIC> { fn available_ipv6(&mut self, nics: &Vec<crate::config::Interface>) -> Option<VMNIC> {
for nic in config.network_interfaces.iter() { for nic in nics.iter() {
for range in nic.ipv6.iter() { for range in nic.ipv6_ranges.iter() {
for ip in range.subnet.iter().skip(1) { let first_ip = range.first_ip.to_bits();
if !range.reserved_addrs.contains(&ip.address()) let last_ip = range.last_ip.to_bits();
&& !self.reserved_ips.contains(&ip.address().to_string()) for ip in first_ip..last_ip + 1 {
&& ip.address() != range.gateway let ip_addr = std::net::Ipv6Addr::from_bits(ip);
if !self.reserved_ipv6.contains(&ip_addr.to_string())
&& ip_addr != range.gateway
{ {
let if_config = match nic.driver { let if_config = match nic.driver {
crate::config::InterfaceType::MACVTAP => InterfaceConfig::MACVTAP { crate::config::InterfaceType::MACVTAP => InterfaceConfig::MACVTAP {
@ -175,14 +173,14 @@ impl Resources {
name: self.available_if_name(), name: self.available_if_name(),
device: nic.device.clone(), device: nic.device.clone(),
}, },
crate::config::InterfaceType::Bridge => InterfaceConfig::Bridge { crate::config::InterfaceType::Bridge => {
device: nic.device.clone(), InterfaceConfig::Bridge { device: nic.device.clone() }
}, }
}; };
let mut ips = Vec::new(); let mut ips = Vec::new();
ips.push(IPConfig { ips.push(IPConfig {
address: ip.address().to_string(), address: ip_addr.to_string(),
mask: calc_ipv6_netmask(ip.address(), range.gateway), mask: range.netmask.clone(),
gateway: range.gateway.to_string(), gateway: range.gateway.to_string(),
}); });
return Some(VMNIC { if_config, ips }); return Some(VMNIC { if_config, ips });
@ -213,7 +211,7 @@ impl Resources {
Ok(()) Ok(())
} }
fn download_boot_file(&mut self, url: String, sha: String) -> Result<()> { fn find_or_download_file(&mut self, url: String, sha: String) -> Result<()> {
if !self.boot_files.contains(&sha) { if !self.boot_files.contains(&sha) {
download_and_check_sha(&url, &sha)?; download_and_check_sha(&url, &sha)?;
} }
@ -230,7 +228,14 @@ impl Resources {
self.reserved_if_names.insert(vtap); self.reserved_if_names.insert(vtap);
} }
for ip in nic.ips.iter() { for ip in nic.ips.iter() {
self.reserved_ips.insert(ip.address.clone()); if let Ok(ip_address) = ip.address.parse::<std::net::IpAddr>() {
if ip_address.is_ipv4() {
self.reserved_ipv4.insert(ip.address.clone());
}
if ip_address.is_ipv6() {
self.reserved_ipv6.insert(ip.address.clone());
}
}
} }
} }
for (host_port, _) in vm.fw_ports.iter() { for (host_port, _) in vm.fw_ports.iter() {
@ -253,15 +258,20 @@ impl Resources {
self.reserved_if_names.remove(&vtap); self.reserved_if_names.remove(&vtap);
} }
for ip in nic.ips.iter() { for ip in nic.ips.iter() {
self.reserved_ips.remove(&ip.address.clone()); if let Ok(ip_address) = ip.address.parse::<std::net::IpAddr>() {
if ip_address.is_ipv4() {
self.reserved_ipv4.remove(&ip.address);
}
if ip_address.is_ipv6() {
self.reserved_ipv6.remove(&ip.address);
}
}
} }
} }
for (host_port, _) in vm.fw_ports.iter() { for (host_port, _) in vm.fw_ports.iter() {
self.reserved_ports.remove(host_port); self.reserved_ports.remove(host_port);
} }
self.reserved_storage self.reserved_storage.entry(vm.storage_dir.clone()).and_modify(|gb| *gb -= vm.disk_size_gb);
.entry(vm.storage_dir.clone())
.and_modify(|gb| *gb -= vm.disk_size_gb);
let _ = self.save_to_disk(); let _ = self.save_to_disk();
} }
} }
@ -333,12 +343,6 @@ pub struct VMNIC {
ips: Vec<IPConfig>, ips: Vec<IPConfig>,
} }
impl VMNIC {
fn new() -> VMNIC {
todo!("implement this here to improve code elegance of resource reservation");
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct VM { pub struct VM {
pub uuid: String, pub uuid: String,
@ -356,6 +360,35 @@ pub struct VM {
storage_dir: String, storage_dir: String,
} }
impl Into<brain::NewVmResp> for VM {
fn into(self) -> brain::NewVmResp {
let mut nic_index: u32 = 0;
let mut ips: Vec<brain::NewVmRespIp> = Vec::new();
if self.fw_ports.len() > 0 {
nic_index += 1;
}
// TODO: when brain supports multiple IPs per VM, fix this
for nic in self.nics {
for ip in nic.ips {
ips.push(brain::NewVmRespIp {
nic_index,
address: ip.address,
mask: ip.mask,
gateway: ip.gateway,
});
}
nic_index += 1;
}
brain::NewVmResp {
uuid: self.uuid,
exposed_ports: self.fw_ports.iter().map(|(p, _)| *p as u32).collect(),
ips,
ovmf_hash: crate::constants::OVMF_HASH.to_string(),
error: "".to_string(),
}
}
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct NewVMRequest { pub struct NewVMRequest {
uuid: String, uuid: String,
@ -373,11 +406,23 @@ pub struct NewVMRequest {
dtrfs_sha: String, dtrfs_sha: String,
} }
impl NewVMRequest { impl From<brain::NewVmReq> for NewVMRequest {
pub fn from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>> { fn from(req: brain::NewVmReq) -> Self {
let content = std::fs::read_to_string(path)?; Self {
let request: NewVMRequest = serde_yaml::from_str(&content)?; uuid: req.uuid,
Ok(request) hostname: req.hostname,
admin_key: req.admin_pubkey,
extra_ports: req.extra_ports.iter().map(|&port| port as u16).collect(),
public_ipv4: req.public_ipv4,
public_ipv6: req.public_ipv6,
disk_size_gb: req.disk_size_gb as usize,
vcpus: req.vcpus as usize,
memory_mb: req.memory_mb as usize,
kernel_url: req.kernel_url,
kernel_sha: req.kernel_sha,
dtrfs_url: req.dtrfs_url,
dtrfs_sha: req.dtrfs_sha,
}
} }
} }
@ -394,17 +439,9 @@ pub struct UpdateVMReq {
dtrfs_sha: String, dtrfs_sha: String,
} }
impl UpdateVMReq {
pub fn from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let content = std::fs::read_to_string(path)?;
let request: UpdateVMReq = serde_yaml::from_str(&content)?;
Ok(request)
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum VMCreationErrors { pub enum VMCreationErrors {
VMAlreadyExists, VMAlreadyExists(VM),
NATandIPv4Conflict, NATandIPv4Conflict,
TooManyCores, TooManyCores,
NotEnoughPorts, NotEnoughPorts,
@ -426,7 +463,11 @@ impl VM {
res: &mut Resources, res: &mut Resources,
) -> Result<Self, VMCreationErrors> { ) -> Result<Self, VMCreationErrors> {
if res.existing_vms.contains(&req.uuid) { if res.existing_vms.contains(&req.uuid) {
return Err(VMCreationErrors::VMAlreadyExists); let content = std::fs::read_to_string(VM_CONFIG_DIR.to_string() + &req.uuid + ".yaml")
.map_err(|e| VMCreationErrors::ServerDiskError(e.to_string()))?;
let vm: crate::state::VM = serde_yaml::from_str(&content)
.map_err(|e| VMCreationErrors::ServerDiskError(e.to_string()))?;
return Err(VMCreationErrors::VMAlreadyExists(vm));
} }
if req.extra_ports.len() > 0 && req.public_ipv4 { if req.extra_ports.len() > 0 && req.public_ipv4 {
return Err(VMCreationErrors::NATandIPv4Conflict); return Err(VMCreationErrors::NATandIPv4Conflict);
@ -444,12 +485,20 @@ impl VM {
return Err(VMCreationErrors::DiskTooSmall); return Err(VMCreationErrors::DiskTooSmall);
} }
if let Err(kernel_err) = res.download_boot_file(req.kernel_url, req.kernel_sha.clone()) { if let Err(ovmf_err) = res.find_or_download_file(
crate::constants::OVMF_URL.to_string(),
crate::constants::OVMF_HASH.to_string(),
) {
return Err(VMCreationErrors::BootFileError(format!(
"Could not get OVMF: {ovmf_err:?}"
)));
};
if let Err(kernel_err) = res.find_or_download_file(req.kernel_url, req.kernel_sha.clone()) {
return Err(VMCreationErrors::BootFileError(format!( return Err(VMCreationErrors::BootFileError(format!(
"Could not get kernel: {kernel_err:?}" "Could not get kernel: {kernel_err:?}"
))); )));
}; };
if let Err(dtrfs_err) = res.download_boot_file(req.dtrfs_url, req.dtrfs_sha.clone()) { if let Err(dtrfs_err) = res.find_or_download_file(req.dtrfs_url, req.dtrfs_sha.clone()) {
return Err(VMCreationErrors::BootFileError(format!( return Err(VMCreationErrors::BootFileError(format!(
"Could not get dtrfs: {dtrfs_err:?}" "Could not get dtrfs: {dtrfs_err:?}"
))); )));
@ -457,13 +506,13 @@ impl VM {
let mut vm_nics = Vec::new(); let mut vm_nics = Vec::new();
if req.public_ipv4 { if req.public_ipv4 {
match res.available_ipv4(config) { match res.available_ipv4(&config.network_interfaces) {
Some(vmnic) => vm_nics.push(vmnic), Some(vmnic) => vm_nics.push(vmnic),
None => return Err(VMCreationErrors::IPv4NotAvailable), None => return Err(VMCreationErrors::IPv4NotAvailable),
} }
} }
if req.public_ipv6 { if req.public_ipv6 {
match res.available_ipv6(config) { match res.available_ipv6(&config.network_interfaces) {
Some(mut vmnic) => { Some(mut vmnic) => {
if let Some(mut existing_vmnic) = vm_nics.pop() { if let Some(mut existing_vmnic) = vm_nics.pop() {
if vmnic.if_config.device_name() == existing_vmnic.if_config.device_name() { if vmnic.if_config.device_name() == existing_vmnic.if_config.device_name() {
@ -478,7 +527,7 @@ impl VM {
} }
} }
None => { None => {
return Err(VMCreationErrors::IPv4NotAvailable); return Err(VMCreationErrors::IPv6NotAvailable);
} }
} }
} }
@ -532,18 +581,12 @@ impl VM {
return Err(VMCreationErrors::TooManyCores); return Err(VMCreationErrors::TooManyCores);
} }
if config.max_vcpu_reservation if config.max_vcpu_reservation
< res < res.reserved_vcpus.saturating_sub(self.vcpus).saturating_add(req.vcpus)
.reserved_vcpus
.saturating_sub(self.vcpus)
.saturating_add(req.vcpus)
{ {
return Err(VMCreationErrors::NotEnoughCPU); return Err(VMCreationErrors::NotEnoughCPU);
} }
if config.max_mem_reservation_mb if config.max_mem_reservation_mb
< res < res.reserved_memory.saturating_sub(self.memory_mb).saturating_add(req.memory_mb)
.reserved_memory
.saturating_sub(self.memory_mb)
.saturating_add(req.memory_mb)
{ {
return Err(VMCreationErrors::NotEnoughMemory); return Err(VMCreationErrors::NotEnoughMemory);
} }
@ -558,12 +601,14 @@ impl VM {
)); ));
} }
if let Err(kern_err) = res.download_boot_file(req.kernel_url, req.kernel_sha.clone()) { if let Err(kern_err) = res.find_or_download_file(req.kernel_url, req.kernel_sha.clone())
{
return Err(VMCreationErrors::BootFileError(format!( return Err(VMCreationErrors::BootFileError(format!(
"Could not get kernel: {kern_err:?}" "Could not get kernel: {kern_err:?}"
))); )));
}; };
if let Err(dtrfs_err) = res.download_boot_file(req.dtrfs_url, req.dtrfs_sha.clone()) { if let Err(dtrfs_err) = res.find_or_download_file(req.dtrfs_url, req.dtrfs_sha.clone())
{
return Err(VMCreationErrors::BootFileError(format!( return Err(VMCreationErrors::BootFileError(format!(
"Could not get dtrfs: {dtrfs_err:?}" "Could not get dtrfs: {dtrfs_err:?}"
))); )));
@ -578,9 +623,7 @@ impl VM {
res.reserved_memory = res.reserved_memory.saturating_sub(self.memory_mb); res.reserved_memory = res.reserved_memory.saturating_sub(self.memory_mb);
res.reserved_vcpus = res.reserved_vcpus.saturating_add(req.vcpus); res.reserved_vcpus = res.reserved_vcpus.saturating_add(req.vcpus);
res.reserved_vcpus = res.reserved_vcpus.saturating_sub(self.vcpus); res.reserved_vcpus = res.reserved_vcpus.saturating_sub(self.vcpus);
res.reserved_storage res.reserved_storage.entry(self.storage_dir.clone()).and_modify(|gb| {
.entry(self.storage_dir.clone())
.and_modify(|gb| {
*gb = gb.saturating_add(req.disk_size_gb); *gb = gb.saturating_add(req.disk_size_gb);
*gb = gb.saturating_sub(self.disk_size_gb); *gb = gb.saturating_sub(self.disk_size_gb);
}); });
@ -645,28 +688,25 @@ impl VM {
dir + &self.uuid + ".qcow2" dir + &self.uuid + ".qcow2"
} }
// If you change this here, you also have to change it in the CLI.
// The kernel params must match on both daemon and CLI to build the measurement.
pub fn kernel_params(&self) -> String { pub fn kernel_params(&self) -> String {
let mut ip_string = String::new(); let mut ip_string = String::new();
let mut i = 0; let mut i = 0;
if self.fw_ports.len() > 0 { if self.fw_ports.len() > 0 {
ip_string += &format!( ip_string += &format!("detee_net_eth{}={}_{}_{} ", i, "10.0.2.15", "24", "10.0.2.2");
"detee_net_eth{}={}_{}_{} ",
i, "10.0.2.15", "24", "10.0.2.2"
);
i += 1; i += 1;
} }
for nic in self.nics.iter() { for nic in self.nics.iter() {
for ip in nic.ips.iter() { for ip in nic.ips.iter() {
ip_string += &format!( ip_string +=
"detee_net_eth{}={}_{}_{} ", &format!("detee_net_eth{}={}_{}_{} ", i, ip.address, ip.mask, ip.gateway);
i, ip.address, ip.mask, ip.gateway
);
} }
i += 1; i += 1;
} }
let admin_key = format!("detee_admin={}", self.admin_key); let admin_key = format!("detee_admin={} ", self.admin_key);
let hostname = format!("detee_name={}", self.hostname); let hostname = format!("detee_name={}", self.hostname);
format!("{} {} {}", ip_string, admin_key, hostname) format!("{}{}{}", ip_string, admin_key, hostname)
} }
fn write_config(&self) -> Result<()> { fn write_config(&self) -> Result<()> {
@ -686,11 +726,7 @@ impl VM {
let mut i = 0; let mut i = 0;
for nic in self.nics.iter() { for nic in self.nics.iter() {
let mut interface = String::new(); let mut interface = String::new();
interface += &format!( interface += &format!(r#"export NETWORK_INTERFACE_{}="{}"#, i, nic.if_config.if_type());
r#"export NETWORK_INTERFACE_{}="{}"#,
i,
nic.if_config.if_type()
);
// device is currently ignored in case of NAT cause we assume QEMU userspace NAT // device is currently ignored in case of NAT cause we assume QEMU userspace NAT
if let Some(vtap_name) = nic.if_config.vtap_name() { if let Some(vtap_name) = nic.if_config.vtap_name() {
interface += &format!("_{}_{}", nic.if_config.device_name(), vtap_name); interface += &format!("_{}_{}", nic.if_config.device_name(), vtap_name);
@ -711,15 +747,9 @@ impl VM {
vars += "export NETWORK_INTERFACE_0000=NAT\n"; vars += "export NETWORK_INTERFACE_0000=NAT\n";
} }
vars += &format!( vars += &format!(r#"export KERNEL="{}""#, VM_BOOT_DIR.to_string() + &self.kernel_sha);
r#"export KERNEL="{}""#,
VM_BOOT_DIR.to_string() + &self.kernel_sha
);
vars += "\n"; vars += "\n";
vars += &format!( vars += &format!(r#"export INITRD="{}""#, VM_BOOT_DIR.to_string() + &self.dtrfs_sha);
r#"export INITRD="{}""#,
VM_BOOT_DIR.to_string() + &self.dtrfs_sha
);
vars += "\n"; vars += "\n";
vars += &format!(r#"export PARAMS="{}""#, self.kernel_params()); vars += &format!(r#"export PARAMS="{}""#, self.kernel_params());
vars += "\n"; vars += "\n";
@ -747,11 +777,7 @@ impl VM {
fn delete_vtap_interfaces(&self) -> Result<()> { fn delete_vtap_interfaces(&self) -> Result<()> {
for nic in self.nics.iter() { for nic in self.nics.iter() {
if let Some(name) = nic.if_config.vtap_name() { if let Some(name) = nic.if_config.vtap_name() {
let result = Command::new("ip") let result = Command::new("ip").arg("link").arg("del").arg(&name).output()?;
.arg("link")
.arg("del")
.arg(&name)
.output()?;
if !result.status.success() { if !result.status.success() {
return Err(anyhow!( return Err(anyhow!(
"Could not delete vtap interface {:?}:\n{:?}\n{:?}", "Could not delete vtap interface {:?}:\n{:?}\n{:?}",
@ -794,10 +820,7 @@ impl VM {
fn create_disk(&self) -> Result<()> { fn create_disk(&self) -> Result<()> {
if std::path::Path::new(&self.disk_path()).exists() { if std::path::Path::new(&self.disk_path()).exists() {
return Err(anyhow!( return Err(anyhow!("Could not create {}. The file already exists.", self.disk_path()));
"Could not create {}. The file already exists.",
self.disk_path()
));
} }
let result = Command::new("qemu-img") let result = Command::new("qemu-img")
@ -844,10 +867,8 @@ impl VM {
} }
fn systemctl_start_and_enable(vm_uuid: &str) -> Result<()> { fn systemctl_start_and_enable(vm_uuid: &str) -> Result<()> {
let result = Command::new("systemctl") let result =
.arg("start") Command::new("systemctl").arg("start").arg(vm_uuid.to_string() + ".service").output()?;
.arg(vm_uuid.to_string() + ".service")
.output()?;
if !result.status.success() { if !result.status.success() {
return Err(anyhow!( return Err(anyhow!(
"Could not reload systemctl daemon:\n{:?}\n{:?}", "Could not reload systemctl daemon:\n{:?}\n{:?}",
@ -857,10 +878,8 @@ fn systemctl_start_and_enable(vm_uuid: &str) -> Result<()> {
.unwrap_or("Could not grab stderr from creation script.".to_string()), .unwrap_or("Could not grab stderr from creation script.".to_string()),
)); ));
} }
let result = Command::new("systemctl") let result =
.arg("enable") Command::new("systemctl").arg("enable").arg(vm_uuid.to_string() + ".service").output()?;
.arg(vm_uuid.to_string() + ".service")
.output()?;
if !result.status.success() { if !result.status.success() {
return Err(anyhow!( return Err(anyhow!(
"Could not reload systemctl daemon:\n{:?}\n{:?}", "Could not reload systemctl daemon:\n{:?}\n{:?}",
@ -874,10 +893,8 @@ fn systemctl_start_and_enable(vm_uuid: &str) -> Result<()> {
} }
fn systemctl_stop_and_disable(vm_uuid: &str) -> Result<()> { fn systemctl_stop_and_disable(vm_uuid: &str) -> Result<()> {
let result = Command::new("systemctl") let result =
.arg("stop") Command::new("systemctl").arg("stop").arg(vm_uuid.to_string() + ".service").output()?;
.arg(vm_uuid.to_string() + ".service")
.output()?;
if !result.status.success() { if !result.status.success() {
return Err(anyhow!( return Err(anyhow!(
"Could not reload systemctl daemon:\n{:?}\n{:?}", "Could not reload systemctl daemon:\n{:?}\n{:?}",
@ -887,10 +904,8 @@ fn systemctl_stop_and_disable(vm_uuid: &str) -> Result<()> {
.unwrap_or("Could not grab stderr from creation script.".to_string()), .unwrap_or("Could not grab stderr from creation script.".to_string()),
)); ));
} }
let result = Command::new("systemctl") let result =
.arg("disable") Command::new("systemctl").arg("disable").arg(vm_uuid.to_string() + ".service").output()?;
.arg(vm_uuid.to_string() + ".service")
.output()?;
if !result.status.success() { if !result.status.success() {
return Err(anyhow!( return Err(anyhow!(
"Could not reload systemctl daemon:\n{:?}\n{:?}", "Could not reload systemctl daemon:\n{:?}\n{:?}",
@ -919,9 +934,7 @@ fn systemctl_reload() -> Result<()> {
fn download_and_check_sha(url: &str, sha: &str) -> Result<()> { fn download_and_check_sha(url: &str, sha: &str) -> Result<()> {
use reqwest::blocking::get; use reqwest::blocking::get;
use std::fs::File; use std::{fs::File, io::copy, path::Path};
use std::io::copy;
use std::path::Path;
let save_path = VM_BOOT_DIR.to_string() + sha; let save_path = VM_BOOT_DIR.to_string() + sha;
let response = get(url)?; let response = get(url)?;
if !response.status().is_success() { if !response.status().is_success() {
@ -958,41 +971,3 @@ fn compute_sha256<P: AsRef<Path>>(path: P) -> Result<String> {
let result = hasher.finalize(); let result = hasher.finalize();
Ok(format!("{:x}", result)) Ok(format!("{:x}", result))
} }
fn calc_ipv4_netmask(ip: Ipv4Addr, gateway: Ipv4Addr) -> String {
// Convert the IPs to u32 for easier bit manipulation
let ip_u32 = u32::from(ip);
let gateway_u32 = u32::from(gateway);
// Find the smallest common prefix
let mut prefix_len = 0;
for i in 1..=32 {
if (ip_u32 >> (32 - i)) == (gateway_u32 >> (32 - i)) {
prefix_len = i;
} else {
break;
}
}
// Return the mask as a string
prefix_len.to_string()
}
fn calc_ipv6_netmask(ip: Ipv6Addr, gateway: Ipv6Addr) -> String {
// Convert the IPs to u128 for easier bit manipulation
let ip_u128 = u128::from(ip);
let gateway_u128 = u128::from(gateway);
// Find the smallest common prefix
let mut prefix_len = 0;
for i in 1..=128 {
if (ip_u128 >> (128 - i)) == (gateway_u128 >> (128 - i)) {
prefix_len = i;
} else {
break;
}
}
// Return the mask as a string
prefix_len.to_string()
}

@ -1,25 +0,0 @@
#![allow(dead_code)]
// this is defined in the engine but we will mock it here for now
pub struct FinalizedTContract {
pub owner: String,
pub user: String,
pub alloc: ResourceAllocation,
pub kernel_uri: String,
pub kernel_sha: String,
pub dtrfs_uri: String,
pub dtrfs_sha: String,
}
#[derive(Default)]
pub struct ResourceAllocation {
pub vcpus: usize,
pub memory: usize,
pub storage: usize,
pub extra_ports: Vec<u16>,
// storage tier: not part of MVP
// pub storage_tier: usize,
pub public_ipv4: Option<String>,
pub public_ipv6: Option<String>,
}