Compare commits

..

6 Commits

Author SHA1 Message Date
1d75dfb88a chore: release (#334)
Co-authored-by: edera-cultivation[bot] <165992271+edera-cultivation[bot]@users.noreply.github.com>
2024-08-15 19:06:56 +00:00
18bf370f74 feature(krata): first pass on cpu hotplug support (#340)
* fix(runtime): adjust memory resources inside a transaction

* feature(krata): first pass on cpu hotplug support
2024-08-15 08:06:04 +00:00
506d2ccf46 build(deps): bump serde_json in the dep-updates group (#339)
Bumps the dep-updates group with 1 update: [serde_json](https://github.com/serde-rs/json).


Updates `serde_json` from 1.0.124 to 1.0.125
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.124...1.0.125)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dep-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-15 06:20:52 +00:00
6096dee2fe chore(document-custom-kernels): Add initial documentation on custom kernels (#337) 2024-08-15 00:10:56 +00:00
bf3b73bf24 feature(exec): implement tty support (fixes #335) (#336) 2024-08-14 19:45:59 +00:00
87530edf70 feature(krata): dynamic resource allocation (closes #298) (#333) 2024-08-14 08:14:49 +00:00
33 changed files with 892 additions and 244 deletions

View File

@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.0.17](https://github.com/edera-dev/krata/compare/v0.0.16...v0.0.17) - 2024-08-15
### Added
- *(krata)* first pass on cpu hotplug support ([#340](https://github.com/edera-dev/krata/pull/340))
- *(exec)* implement tty support (fixes [#335](https://github.com/edera-dev/krata/pull/335)) ([#336](https://github.com/edera-dev/krata/pull/336))
- *(krata)* dynamic resource allocation (closes [#298](https://github.com/edera-dev/krata/pull/298)) ([#333](https://github.com/edera-dev/krata/pull/333))
### Other
- update Cargo.toml dependencies
## [0.0.16](https://github.com/edera-dev/krata/compare/v0.0.15...v0.0.16) - 2024-08-14 ## [0.0.16](https://github.com/edera-dev/krata/compare/v0.0.15...v0.0.16) - 2024-08-14
### Added ### Added

188
Cargo.lock generated
View File

@ -190,7 +190,7 @@ dependencies = [
"rustversion", "rustversion",
"serde", "serde",
"sync_wrapper 1.0.1", "sync_wrapper 1.0.1",
"tower", "tower 0.4.13",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
] ]
@ -431,7 +431,7 @@ version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7"
dependencies = [ dependencies = [
"crossterm", "crossterm 0.27.0",
"strum", "strum",
"strum_macros", "strum_macros",
"unicode-width", "unicode-width",
@ -439,13 +439,14 @@ dependencies = [
[[package]] [[package]]
name = "compact_str" name = "compact_str"
version = "0.7.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644"
dependencies = [ dependencies = [
"castaway", "castaway",
"cfg-if", "cfg-if",
"itoa", "itoa",
"rustversion",
"ryu", "ryu",
"static_assertions", "static_assertions",
] ]
@ -520,10 +521,23 @@ checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"crossterm_winapi", "crossterm_winapi",
"futures-core",
"libc", "libc",
"mio 0.8.11",
"parking_lot", "parking_lot",
"winapi",
]
[[package]]
name = "crossterm"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [
"bitflags 2.6.0",
"crossterm_winapi",
"futures-core",
"mio",
"parking_lot",
"rustix",
"signal-hook", "signal-hook",
"signal-hook-mio", "signal-hook-mio",
"winapi", "winapi",
@ -1148,7 +1162,7 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"tokio", "tokio",
"tower", "tower 0.4.13",
"tower-service", "tower-service",
"tracing", "tracing",
] ]
@ -1202,6 +1216,16 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "instability"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c"
dependencies = [
"quote",
"syn 2.0.74",
]
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.13" version = "0.1.13"
@ -1267,7 +1291,7 @@ dependencies = [
[[package]] [[package]]
name = "krata" name = "krata"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1290,7 +1314,7 @@ dependencies = [
"tokio-stream", "tokio-stream",
"tonic", "tonic",
"tonic-build", "tonic-build",
"tower", "tower 0.5.0",
"url", "url",
] ]
@ -1307,7 +1331,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-buildtools" name = "krata-buildtools"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"env_logger", "env_logger",
@ -1322,14 +1346,14 @@ dependencies = [
[[package]] [[package]]
name = "krata-ctl" name = "krata-ctl"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
"base64", "base64",
"clap", "clap",
"comfy-table", "comfy-table",
"crossterm", "crossterm 0.28.1",
"ctrlc", "ctrlc",
"env_logger", "env_logger",
"fancy-duration", "fancy-duration",
@ -1347,12 +1371,12 @@ dependencies = [
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tonic", "tonic",
"tower", "tower 0.5.0",
] ]
[[package]] [[package]]
name = "krata-daemon" name = "krata-daemon"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1384,14 +1408,14 @@ dependencies = [
[[package]] [[package]]
name = "krata-loopdev" name = "krata-loopdev"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"libc", "libc",
] ]
[[package]] [[package]]
name = "krata-network" name = "krata-network"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1415,7 +1439,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-oci" name = "krata-oci"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-compression", "async-compression",
@ -1442,7 +1466,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-runtime" name = "krata-runtime"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"backhand", "backhand",
@ -1483,7 +1507,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-xencall" name = "krata-xencall"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"libc", "libc",
@ -1496,7 +1520,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-xenclient" name = "krata-xenclient"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"env_logger", "env_logger",
@ -1514,7 +1538,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-xenevtchn" name = "krata-xenevtchn"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"libc", "libc",
@ -1526,7 +1550,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-xengnt" name = "krata-xengnt"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"libc", "libc",
"nix 0.29.0", "nix 0.29.0",
@ -1535,7 +1559,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-xenplatform" name = "krata-xenplatform"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"c2rust-bitfields", "c2rust-bitfields",
@ -1558,7 +1582,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-xenstore" name = "krata-xenstore"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"env_logger", "env_logger",
@ -1570,7 +1594,7 @@ dependencies = [
[[package]] [[package]]
name = "krata-zone" name = "krata-zone"
version = "0.0.16" version = "0.0.17"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cgroups-rs", "cgroups-rs",
@ -1585,6 +1609,7 @@ dependencies = [
"oci-spec", "oci-spec",
"path-absolutize", "path-absolutize",
"platform-info", "platform-info",
"pty-process",
"rtnetlink", "rtnetlink",
"serde", "serde",
"serde_json", "serde_json",
@ -1691,18 +1716,6 @@ dependencies = [
"adler", "adler",
] ]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.0.2" version = "1.0.2"
@ -1711,6 +1724,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
"log",
"wasi", "wasi",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
@ -2168,6 +2182,17 @@ dependencies = [
"prost", "prost",
] ]
[[package]]
name = "pty-process"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8749b545e244c90bf74a5767764cc2194f1888bb42f84015486a64c82bea5cc0"
dependencies = [
"libc",
"rustix",
"tokio",
]
[[package]] [[package]]
name = "quinn" name = "quinn"
version = "0.11.3" version = "0.11.3"
@ -2263,18 +2288,18 @@ dependencies = [
[[package]] [[package]]
name = "ratatui" name = "ratatui"
version = "0.27.0" version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" checksum = "5ba6a365afbe5615999275bea2446b970b10a41102500e27ce7678d50d978303"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"cassowary", "cassowary",
"compact_str", "compact_str",
"crossterm", "crossterm 0.28.1",
"instability",
"itertools", "itertools",
"lru", "lru",
"paste", "paste",
"stability",
"strum", "strum",
"strum_macros", "strum_macros",
"unicode-segmentation", "unicode-segmentation",
@ -2459,6 +2484,7 @@ checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"errno", "errno",
"itoa",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.52.0", "windows-sys 0.52.0",
@ -2565,9 +2591,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.124" version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -2650,7 +2676,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [ dependencies = [
"libc", "libc",
"mio 0.8.11", "mio",
"signal-hook", "signal-hook",
] ]
@ -2727,16 +2753,6 @@ 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 = "stability"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac"
dependencies = [
"quote",
"syn 2.0.74",
]
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.0" version = "1.2.0"
@ -2832,15 +2848,14 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.30.13" version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab"
dependencies = [ dependencies = [
"cfg-if",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
"memchr",
"ntapi", "ntapi",
"once_cell",
"rayon", "rayon",
"windows", "windows",
] ]
@ -2914,7 +2929,7 @@ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
"libc", "libc",
"mio 1.0.2", "mio",
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
@ -3052,7 +3067,7 @@ dependencies = [
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
"tokio-stream", "tokio-stream",
"tower", "tower 0.4.13",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing", "tracing",
@ -3091,6 +3106,16 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "tower"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36b837f86b25d7c0d7988f00a54e74739be6477f2aac6201b8f429a7569991b7"
dependencies = [
"tower-layer",
"tower-service",
]
[[package]] [[package]]
name = "tower-layer" name = "tower-layer"
version = "0.3.3" version = "0.3.3"
@ -3109,7 +3134,6 @@ version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [ dependencies = [
"log",
"pin-project-lite", "pin-project-lite",
"tracing-attributes", "tracing-attributes",
"tracing-core", "tracing-core",
@ -3390,9 +3414,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.52.0" version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
dependencies = [ dependencies = [
"windows-core", "windows-core",
"windows-targets 0.52.6", "windows-targets 0.52.6",
@ -3400,9 +3424,43 @@ dependencies = [
[[package]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.52.0" version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-implement"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.74",
]
[[package]]
name = "windows-interface"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.74",
]
[[package]]
name = "windows-result"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
dependencies = [ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]

View File

@ -18,7 +18,7 @@ members = [
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
version = "0.0.16" version = "0.0.17"
homepage = "https://krata.dev" homepage = "https://krata.dev"
license = "Apache-2.0" license = "Apache-2.0"
repository = "https://github.com/edera-dev/krata" repository = "https://github.com/edera-dev/krata"
@ -37,7 +37,7 @@ c2rust-bitfields = "0.18.0"
cgroups-rs = "0.3.4" cgroups-rs = "0.3.4"
circular-buffer = "0.1.7" circular-buffer = "0.1.7"
comfy-table = "7.1.1" comfy-table = "7.1.1"
crossterm = "0.27.0" crossterm = "0.28.1"
ctrlc = "3.4.5" ctrlc = "3.4.5"
elf = "0.7.4" elf = "0.7.4"
env_logger = "0.11.5" env_logger = "0.11.5"
@ -68,25 +68,26 @@ prost = "0.13.1"
prost-build = "0.13.1" prost-build = "0.13.1"
prost-reflect-build = "0.14.0" prost-reflect-build = "0.14.0"
prost-types = "0.13.1" prost-types = "0.13.1"
pty-process = "0.4.0"
rand = "0.8.5" rand = "0.8.5"
ratatui = "0.27.0" ratatui = "0.28.0"
redb = "2.1.1" redb = "2.1.1"
regex = "1.10.6" regex = "1.10.6"
rtnetlink = "0.14.1" rtnetlink = "0.14.1"
scopeguard = "1.2.0" scopeguard = "1.2.0"
serde_json = "1.0.124" serde_json = "1.0.125"
serde_yaml = "0.9" serde_yaml = "0.9"
sha256 = "1.5.0" sha256 = "1.5.0"
signal-hook = "0.3.17" signal-hook = "0.3.17"
slice-copy = "0.3.0" slice-copy = "0.3.0"
smoltcp = "0.11.0" smoltcp = "0.11.0"
sysinfo = "0.30.13" sysinfo = "0.31.2"
termtree = "0.5.1" termtree = "0.5.1"
thiserror = "1.0" thiserror = "1.0"
tokio-tun = "0.11.5" tokio-tun = "0.11.5"
toml = "0.8.19" toml = "0.8.19"
tonic-build = "0.12.1" tonic-build = "0.12.1"
tower = "0.4.13" tower = "0.5.0"
udp-stream = "0.0.12" udp-stream = "0.0.12"
url = "2.5.2" url = "2.5.2"
walkdir = "2" walkdir = "2"

View File

@ -16,7 +16,7 @@ oci-spec = { workspace = true }
scopeguard = { workspace = true } scopeguard = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tokio-stream = { workspace = true } tokio-stream = { workspace = true }
krata-oci = { path = "../oci", version = "^0.0.16" } krata-oci = { path = "../oci", version = "^0.0.17" }
krata-tokio-tar = { workspace = true } krata-tokio-tar = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }

View File

@ -20,7 +20,7 @@ env_logger = { workspace = true }
fancy-duration = { workspace = true } fancy-duration = { workspace = true }
human_bytes = { workspace = true } human_bytes = { workspace = true }
indicatif = { workspace = true } indicatif = { workspace = true }
krata = { path = "../krata", version = "^0.0.16" } krata = { path = "../krata", version = "^0.0.17" }
log = { workspace = true } log = { workspace = true }
prost-reflect = { workspace = true, features = ["serde"] } prost-reflect = { workspace = true, features = ["serde"] }
prost-types = { workspace = true } prost-types = { workspace = true }

View File

@ -21,6 +21,8 @@ pub struct ZoneExecCommand {
env: Option<Vec<String>>, env: Option<Vec<String>>,
#[arg(short = 'w', long, help = "Working directory")] #[arg(short = 'w', long, help = "Working directory")]
working_directory: Option<String>, working_directory: Option<String>,
#[arg(short = 't', long, help = "Allocate tty")]
tty: bool,
#[arg(help = "Zone to exec inside, either the name or the uuid")] #[arg(help = "Zone to exec inside, either the name or the uuid")]
zone: String, zone: String,
#[arg( #[arg(
@ -46,8 +48,10 @@ impl ZoneExecCommand {
.collect(), .collect(),
command: self.command, command: self.command,
working_directory: self.working_directory.unwrap_or_default(), working_directory: self.working_directory.unwrap_or_default(),
tty: self.tty,
}), }),
data: vec![], stdin: vec![],
stdin_closed: false,
}; };
let stream = StdioConsoleStream::stdin_stream_exec(initial).await; let stream = StdioConsoleStream::stdin_stream_exec(initial).await;
@ -57,7 +61,7 @@ impl ZoneExecCommand {
.await? .await?
.into_inner(); .into_inner();
let code = StdioConsoleStream::exec_output(response).await?; let code = StdioConsoleStream::exec_output(response, self.tty).await?;
std::process::exit(code); std::process::exit(code);
} }
} }

View File

@ -6,8 +6,9 @@ use krata::{
events::EventStream, events::EventStream,
v1::{ v1::{
common::{ common::{
zone_image_spec::Image, OciImageFormat, ZoneImageSpec, ZoneOciImageSpec, ZoneSpec, zone_image_spec::Image, OciImageFormat, ZoneImageSpec, ZoneOciImageSpec,
ZoneSpecDevice, ZoneState, ZoneTaskSpec, ZoneTaskSpecEnvVar, ZoneResourceSpec, ZoneSpec, ZoneSpecDevice, ZoneState, ZoneTaskSpec,
ZoneTaskSpecEnvVar,
}, },
control::{ control::{
control_service_client::ControlServiceClient, watch_events_reply::Event, control_service_client::ControlServiceClient, watch_events_reply::Event,
@ -38,19 +39,40 @@ pub struct ZoneLaunchCommand {
pull_update: bool, pull_update: bool,
#[arg(short, long, help = "Name of the zone")] #[arg(short, long, help = "Name of the zone")]
name: Option<String>, name: Option<String>,
#[arg(short, long, default_value_t = 1, help = "vCPUs available to the zone")]
cpus: u32,
#[arg( #[arg(
short, short = 'C',
long, long = "max-cpus",
default_value_t = 512, default_value_t = 4,
help = "Memory available to the zone, in megabytes" help = "Maximum vCPUs available for the zone"
)] )]
mem: u64, max_cpus: u32,
#[arg(
short = 'c',
long = "target-cpus",
default_value_t = 1,
help = "Target vCPUs for the zone to use"
)]
target_cpus: u32,
#[arg(
short = 'M',
long = "max-memory",
default_value_t = 1024,
help = "Maximum memory available to the zone, in megabytes"
)]
max_memory: u64,
#[arg(
short = 'm',
long = "target-memory",
default_value_t = 1024,
help = "Target memory for the zone to use, in megabytes"
)]
target_memory: u64,
#[arg[short = 'D', long = "device", help = "Devices to request for the zone"]] #[arg[short = 'D', long = "device", help = "Devices to request for the zone"]]
device: Vec<String>, device: Vec<String>,
#[arg[short, long, help = "Environment variables set in the zone"]] #[arg[short, long, help = "Environment variables set in the zone"]]
env: Option<Vec<String>>, env: Option<Vec<String>>,
#[arg(short = 't', long, help = "Allocate tty for task")]
tty: bool,
#[arg( #[arg(
short, short,
long, long,
@ -120,8 +142,12 @@ impl ZoneLaunchCommand {
image: Some(image), image: Some(image),
kernel, kernel,
initrd, initrd,
cpus: self.cpus, initial_resources: Some(ZoneResourceSpec {
mem: self.mem, max_memory: self.max_memory,
target_memory: self.target_memory,
max_cpus: self.max_cpus,
target_cpus: self.target_cpus,
}),
task: Some(ZoneTaskSpec { task: Some(ZoneTaskSpec {
environment: env_map(&self.env.unwrap_or_default()) environment: env_map(&self.env.unwrap_or_default())
.iter() .iter()
@ -132,6 +158,7 @@ impl ZoneLaunchCommand {
.collect(), .collect(),
command: self.command, command: self.command,
working_directory: self.working_directory.unwrap_or_default(), working_directory: self.working_directory.unwrap_or_default(),
tty: self.tty,
}), }),
annotations: vec![], annotations: vec![],
devices: self devices: self

View File

@ -14,6 +14,7 @@ use crate::cli::zone::logs::ZoneLogsCommand;
use crate::cli::zone::metrics::ZoneMetricsCommand; use crate::cli::zone::metrics::ZoneMetricsCommand;
use crate::cli::zone::resolve::ZoneResolveCommand; use crate::cli::zone::resolve::ZoneResolveCommand;
use crate::cli::zone::top::ZoneTopCommand; use crate::cli::zone::top::ZoneTopCommand;
use crate::cli::zone::update_resources::ZoneUpdateResourcesCommand;
use crate::cli::zone::watch::ZoneWatchCommand; use crate::cli::zone::watch::ZoneWatchCommand;
pub mod attach; pub mod attach;
@ -25,6 +26,7 @@ pub mod logs;
pub mod metrics; pub mod metrics;
pub mod resolve; pub mod resolve;
pub mod top; pub mod top;
mod update_resources;
pub mod watch; pub mod watch;
#[derive(Parser)] #[derive(Parser)]
@ -56,6 +58,7 @@ pub enum ZoneCommands {
Resolve(ZoneResolveCommand), Resolve(ZoneResolveCommand),
Top(ZoneTopCommand), Top(ZoneTopCommand),
Watch(ZoneWatchCommand), Watch(ZoneWatchCommand),
UpdateResources(ZoneUpdateResourcesCommand),
} }
impl ZoneCommands { impl ZoneCommands {
@ -84,6 +87,8 @@ impl ZoneCommands {
ZoneCommands::Top(top) => top.run(client, events).await, ZoneCommands::Top(top) => top.run(client, events).await,
ZoneCommands::Exec(exec) => exec.run(client).await, ZoneCommands::Exec(exec) => exec.run(client).await,
ZoneCommands::UpdateResources(update_resources) => update_resources.run(client).await,
} }
} }
} }

View File

@ -112,7 +112,7 @@ impl ZoneTopApp {
} }
fn render_frame(&mut self, frame: &mut Frame) { fn render_frame(&mut self, frame: &mut Frame) {
frame.render_widget(self, frame.size()); frame.render_widget(self, frame.area());
} }
fn handle_event(&mut self, event: Event) -> io::Result<()> { fn handle_event(&mut self, event: Event) -> io::Result<()> {

View File

@ -0,0 +1,93 @@
use anyhow::Result;
use clap::Parser;
use krata::v1::{
common::ZoneResourceSpec,
control::{control_service_client::ControlServiceClient, UpdateZoneResourcesRequest},
};
use crate::cli::resolve_zone;
use krata::v1::control::GetZoneRequest;
use tonic::{transport::Channel, Request};
#[derive(Parser)]
#[command(about = "Update the available resources to a zone")]
pub struct ZoneUpdateResourcesCommand {
#[arg(help = "Zone to update resources of, either the name or the uuid")]
zone: String,
#[arg(
short = 'C',
long = "max-cpus",
default_value_t = 0,
help = "Maximum vCPUs available to the zone (0 means previous value)"
)]
max_cpus: u32,
#[arg(
short = 'c',
long = "target-cpus",
default_value_t = 0,
help = "Target vCPUs for the zone to use (0 means previous value)"
)]
target_cpus: u32,
#[arg(
short = 'M',
long = "max-memory",
default_value_t = 0,
help = "Maximum memory available to the zone, in megabytes (0 means previous value)"
)]
max_memory: u64,
#[arg(
short = 'm',
long = "target-memory",
default_value_t = 0,
help = "Target memory for the zone to use, in megabytes (0 means previous value)"
)]
target_memory: u64,
}
impl ZoneUpdateResourcesCommand {
pub async fn run(self, mut client: ControlServiceClient<Channel>) -> Result<()> {
let zone_id = resolve_zone(&mut client, &self.zone).await?;
let zone = client
.get_zone(GetZoneRequest { zone_id })
.await?
.into_inner()
.zone
.unwrap_or_default();
let active_resources = zone
.status
.clone()
.unwrap_or_default()
.resource_status
.unwrap_or_default()
.active_resources
.unwrap_or_default();
client
.update_zone_resources(Request::new(UpdateZoneResourcesRequest {
zone_id: zone.id.clone(),
resources: Some(ZoneResourceSpec {
max_memory: if self.max_memory == 0 {
active_resources.max_memory
} else {
self.max_memory
},
target_memory: if self.target_memory == 0 {
active_resources.target_memory
} else {
self.target_memory
},
max_cpus: if self.max_cpus == 0 {
active_resources.max_cpus
} else {
self.max_cpus
},
target_cpus: if self.target_cpus == 0 {
active_resources.target_cpus
} else {
self.target_cpus
},
}),
}))
.await?;
Ok(())
}
}

View File

@ -62,11 +62,15 @@ impl StdioConsoleStream {
break; break;
} }
}; };
let data = buffer[0..size].to_vec(); let stdin = buffer[0..size].to_vec();
if size == 1 && buffer[0] == 0x1d { if size == 1 && buffer[0] == 0x1d {
break; break;
} }
yield ExecInsideZoneRequest { zone_id: String::default(), task: None, data }; let stdin_closed = size == 0;
yield ExecInsideZoneRequest { zone_id: String::default(), task: None, stdin, stdin_closed, };
if stdin_closed {
break;
}
} }
} }
} }
@ -88,7 +92,11 @@ impl StdioConsoleStream {
Ok(()) Ok(())
} }
pub async fn exec_output(mut stream: Streaming<ExecInsideZoneReply>) -> Result<i32> { pub async fn exec_output(mut stream: Streaming<ExecInsideZoneReply>, raw: bool) -> Result<i32> {
if raw && stdin().is_tty() {
enable_raw_mode()?;
StdioConsoleStream::register_terminal_restore_hook()?;
}
let mut stdout = stdout(); let mut stdout = stdout();
let mut stderr = stderr(); let mut stderr = stderr();
while let Some(reply) = stream.next().await { while let Some(reply) = stream.next().await {

View File

@ -19,9 +19,9 @@ clap = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
ipnetwork = { workspace = true } ipnetwork = { workspace = true }
krata = { path = "../krata", version = "^0.0.16" } krata = { path = "../krata", version = "^0.0.17" }
krata-oci = { path = "../oci", version = "^0.0.16" } krata-oci = { path = "../oci", version = "^0.0.17" }
krata-runtime = { path = "../runtime", version = "^0.0.16" } krata-runtime = { path = "../runtime", version = "^0.0.17" }
log = { workspace = true } log = { workspace = true }
prost = { workspace = true } prost = { workspace = true }
redb = { workspace = true } redb = { workspace = true }

View File

@ -6,6 +6,7 @@ use crate::{
}; };
use async_stream::try_stream; use async_stream::try_stream;
use futures::Stream; use futures::Stream;
use krata::v1::common::ZoneResourceStatus;
use krata::v1::control::{ use krata::v1::control::{
GetZoneReply, GetZoneRequest, SetHostPowerManagementPolicyReply, GetZoneReply, GetZoneRequest, SetHostPowerManagementPolicyReply,
SetHostPowerManagementPolicyRequest, SetHostPowerManagementPolicyRequest,
@ -25,8 +26,8 @@ use krata::{
HostCpuTopologyInfo, HostStatusReply, HostStatusRequest, ListDevicesReply, HostCpuTopologyInfo, HostStatusReply, HostStatusRequest, ListDevicesReply,
ListDevicesRequest, ListZonesReply, ListZonesRequest, PullImageReply, PullImageRequest, ListDevicesRequest, ListZonesReply, ListZonesRequest, PullImageReply, PullImageRequest,
ReadZoneMetricsReply, ReadZoneMetricsRequest, ResolveZoneIdReply, ResolveZoneIdRequest, ReadZoneMetricsReply, ReadZoneMetricsRequest, ResolveZoneIdReply, ResolveZoneIdRequest,
SnoopIdmReply, SnoopIdmRequest, WatchEventsReply, WatchEventsRequest, ZoneConsoleReply, SnoopIdmReply, SnoopIdmRequest, UpdateZoneResourcesReply, UpdateZoneResourcesRequest,
ZoneConsoleRequest, WatchEventsReply, WatchEventsRequest, ZoneConsoleReply, ZoneConsoleRequest,
}, },
}, },
}; };
@ -165,6 +166,7 @@ impl ControlService for DaemonControlService {
network_status: None, network_status: None,
exit_status: None, exit_status: None,
error_status: None, error_status: None,
resource_status: None,
host: self.glt.host_uuid().to_string(), host: self.glt.host_uuid().to_string(),
domid: u32::MAX, domid: u32::MAX,
}), }),
@ -224,6 +226,7 @@ impl ControlService for DaemonControlService {
.collect(), .collect(),
command: task.command, command: task.command,
working_directory: task.working_directory, working_directory: task.working_directory,
tty: task.tty,
})), })),
})), })),
}; };
@ -241,11 +244,12 @@ impl ControlService for DaemonControlService {
}.into()); }.into());
if let Ok(update) = update { if let Ok(update) = update {
if !update.data.is_empty() { if !update.stdin.is_empty() {
let _ = handle.update(IdmRequest { let _ = handle.update(IdmRequest {
request: Some(IdmRequestType::ExecStream(ExecStreamRequestUpdate { request: Some(IdmRequestType::ExecStream(ExecStreamRequestUpdate {
update: Some(Update::Stdin(ExecStreamRequestStdin { update: Some(Update::Stdin(ExecStreamRequestStdin {
data: update.data, data: update.stdin,
closed: update.stdin_closed,
})), })),
}))}).await; }))}).await;
} }
@ -261,7 +265,7 @@ impl ControlService for DaemonControlService {
error: update.error, error: update.error,
exit_code: update.exit_code, exit_code: update.exit_code,
stdout: update.stdout, stdout: update.stdout,
stderr: update.stderr stderr: update.stderr,
}; };
yield reply; yield reply;
}, },
@ -623,4 +627,87 @@ impl ControlService for DaemonControlService {
zone: zone.cloned(), zone: zone.cloned(),
})) }))
} }
async fn update_zone_resources(
&self,
request: Request<UpdateZoneResourcesRequest>,
) -> Result<Response<UpdateZoneResourcesReply>, Status> {
let request = request.into_inner();
let uuid = Uuid::from_str(&request.zone_id).map_err(|error| ApiError {
message: error.to_string(),
})?;
let Some(mut zone) = self.zones.read(uuid).await.map_err(ApiError::from)? else {
return Err(ApiError {
message: "zone not found".to_string(),
}
.into());
};
let Some(ref mut status) = zone.status else {
return Err(ApiError {
message: "zone state not available".to_string(),
}
.into());
};
if status.state() != ZoneState::Created {
return Err(ApiError {
message: "zone is in an invalid state".to_string(),
}
.into());
}
if status.domid == 0 || status.domid == u32::MAX {
return Err(ApiError {
message: "zone domid is invalid".to_string(),
}
.into());
}
let mut resources = request.resources.unwrap_or_default();
if resources.target_memory > resources.max_memory {
resources.max_memory = resources.target_memory;
}
if resources.target_cpus < 1 {
resources.target_cpus = 1;
}
let initial_resources = zone
.spec
.clone()
.unwrap_or_default()
.initial_resources
.unwrap_or_default();
if resources.target_cpus > initial_resources.max_cpus {
resources.target_cpus = initial_resources.max_cpus;
}
resources.max_cpus = initial_resources.max_cpus;
self.runtime
.set_memory_resources(
status.domid,
resources.target_memory * 1024 * 1024,
resources.max_memory * 1024 * 1024,
)
.await
.map_err(|error| ApiError {
message: format!("failed to set memory resources: {}", error),
})?;
self.runtime
.set_cpu_resources(status.domid, resources.target_cpus)
.await
.map_err(|error| ApiError {
message: format!("failed to set cpu resources: {}", error),
})?;
status.resource_status = Some(ZoneResourceStatus {
active_resources: Some(resources),
});
self.zones
.update(uuid, zone)
.await
.map_err(ApiError::from)?;
Ok(Response::new(UpdateZoneResourcesReply {}))
}
} }

View File

@ -137,6 +137,7 @@ impl DaemonEventGenerator {
network_status: zone.status.clone().unwrap_or_default().network_status, network_status: zone.status.clone().unwrap_or_default().network_status,
exit_status: Some(ZoneExitStatus { code }), exit_status: Some(ZoneExitStatus { code }),
error_status: None, error_status: None,
resource_status: zone.status.clone().unwrap_or_default().resource_status,
host: zone.status.clone().map(|x| x.host).unwrap_or_default(), host: zone.status.clone().map(|x| x.host).unwrap_or_default(),
domid: zone.status.clone().map(|x| x.domid).unwrap_or(u32::MAX), domid: zone.status.clone().map(|x| x.domid).unwrap_or(u32::MAX),
}); });

View File

@ -1,8 +1,8 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use futures::StreamExt; use futures::StreamExt;
use krata::launchcfg::LaunchPackedFormat; use krata::launchcfg::LaunchPackedFormat;
use krata::v1::common::ZoneOciImageSpec;
use krata::v1::common::{OciImageFormat, Zone, ZoneState, ZoneStatus}; use krata::v1::common::{OciImageFormat, Zone, ZoneState, ZoneStatus};
use krata::v1::common::{ZoneOciImageSpec, ZoneResourceStatus};
use krataoci::packer::{service::OciPackerService, OciPackedFormat}; use krataoci::packer::{service::OciPackerService, OciPackedFormat};
use kratart::launch::{PciBdf, PciDevice, PciRdmReservePolicy, ZoneLaunchNetwork}; use kratart::launch::{PciBdf, PciDevice, PciRdmReservePolicy, ZoneLaunchNetwork};
use kratart::{launch::ZoneLaunchRequest, Runtime}; use kratart::{launch::ZoneLaunchRequest, Runtime};
@ -76,7 +76,7 @@ impl ZoneCreator<'_> {
} }
pub async fn create(&self, uuid: Uuid, zone: &mut Zone) -> Result<ZoneReconcilerResult> { pub async fn create(&self, uuid: Uuid, zone: &mut Zone) -> Result<ZoneReconcilerResult> {
let Some(ref spec) = zone.spec else { let Some(ref mut spec) = zone.spec else {
return Err(anyhow!("zone spec not specified")); return Err(anyhow!("zone spec not specified"));
}; };
@ -176,6 +176,14 @@ impl ZoneCreator<'_> {
let reservation = self.ip_assignment.assign(uuid).await?; let reservation = self.ip_assignment.assign(uuid).await?;
let mut initial_resources = spec.initial_resources.unwrap_or_default();
if initial_resources.target_cpus < 1 {
initial_resources.target_cpus = 1;
}
if initial_resources.target_cpus > initial_resources.max_cpus {
initial_resources.max_cpus = initial_resources.target_cpus;
}
spec.initial_resources = Some(initial_resources);
let info = self let info = self
.runtime .runtime
.launch(ZoneLaunchRequest { .launch(ZoneLaunchRequest {
@ -189,8 +197,10 @@ impl ZoneCreator<'_> {
image, image,
kernel, kernel,
initrd, initrd,
vcpus: spec.cpus, target_cpus: initial_resources.target_cpus,
mem: spec.mem, max_cpus: initial_resources.max_cpus,
max_memory: initial_resources.max_memory,
target_memory: initial_resources.target_memory,
pcis, pcis,
env: task env: task
.environment .environment
@ -219,6 +229,9 @@ impl ZoneCreator<'_> {
network_status: Some(ip_reservation_to_network_status(&reservation)), network_status: Some(ip_reservation_to_network_status(&reservation)),
exit_status: None, exit_status: None,
error_status: None, error_status: None,
resource_status: Some(ZoneResourceStatus {
active_resources: Some(initial_resources),
}),
host: self.zlt.host_uuid().to_string(), host: self.zlt.host_uuid().to_string(),
domid: info.domid, domid: info.domid,
}); });

View File

@ -328,6 +328,7 @@ impl ZoneReconciler {
network_status: None, network_status: None,
exit_status: None, exit_status: None,
error_status: None, error_status: None,
resource_status: None,
host: self.zlt.host_uuid().to_string(), host: self.zlt.host_uuid().to_string(),
domid: domid.unwrap_or(u32::MAX), domid: domid.unwrap_or(u32::MAX),
}); });

View File

@ -45,10 +45,12 @@ message ExecStreamRequestStart {
repeated ExecEnvVar environment = 1; repeated ExecEnvVar environment = 1;
repeated string command = 2; repeated string command = 2;
string working_directory = 3; string working_directory = 3;
bool tty = 4;
} }
message ExecStreamRequestStdin { message ExecStreamRequestStdin {
bytes data = 1; bytes data = 1;
bool closed = 2;
} }
message ExecStreamRequestUpdate { message ExecStreamRequestUpdate {

View File

@ -21,11 +21,17 @@ message ZoneSpec {
ZoneImageSpec kernel = 3; ZoneImageSpec kernel = 3;
// If not specified, defaults to the daemon default initrd. // If not specified, defaults to the daemon default initrd.
ZoneImageSpec initrd = 4; ZoneImageSpec initrd = 4;
uint32 cpus = 5; ZoneResourceSpec initial_resources = 5;
uint64 mem = 6; ZoneTaskSpec task = 6;
ZoneTaskSpec task = 7; repeated ZoneSpecAnnotation annotations = 7;
repeated ZoneSpecAnnotation annotations = 8; repeated ZoneSpecDevice devices = 8;
repeated ZoneSpecDevice devices = 9; }
message ZoneResourceSpec {
uint64 max_memory = 1;
uint64 target_memory = 2;
uint32 max_cpus = 3;
uint32 target_cpus = 4;
} }
message ZoneImageSpec { message ZoneImageSpec {
@ -51,6 +57,7 @@ message ZoneTaskSpec {
repeated ZoneTaskSpecEnvVar environment = 1; repeated ZoneTaskSpecEnvVar environment = 1;
repeated string command = 2; repeated string command = 2;
string working_directory = 3; string working_directory = 3;
bool tty = 4;
} }
message ZoneTaskSpecEnvVar { message ZoneTaskSpecEnvVar {
@ -74,6 +81,7 @@ message ZoneStatus {
ZoneErrorStatus error_status = 4; ZoneErrorStatus error_status = 4;
string host = 5; string host = 5;
uint32 domid = 6; uint32 domid = 6;
ZoneResourceStatus resource_status = 7;
} }
enum ZoneState { enum ZoneState {
@ -103,6 +111,10 @@ message ZoneErrorStatus {
string message = 1; string message = 1;
} }
message ZoneResourceStatus {
ZoneResourceSpec active_resources = 1;
}
message ZoneMetricNode { message ZoneMetricNode {
string name = 1; string name = 1;
google.protobuf.Value value = 2; google.protobuf.Value value = 2;

View File

@ -26,6 +26,8 @@ service ControlService {
rpc GetZone(GetZoneRequest) returns (GetZoneReply); rpc GetZone(GetZoneRequest) returns (GetZoneReply);
rpc UpdateZoneResources(UpdateZoneResourcesRequest) returns (UpdateZoneResourcesReply);
rpc ListZones(ListZonesRequest) returns (ListZonesReply); rpc ListZones(ListZonesRequest) returns (ListZonesReply);
rpc AttachZoneConsole(stream ZoneConsoleRequest) returns (stream ZoneConsoleReply); rpc AttachZoneConsole(stream ZoneConsoleRequest) returns (stream ZoneConsoleReply);
@ -82,7 +84,8 @@ message ListZonesReply {
message ExecInsideZoneRequest { message ExecInsideZoneRequest {
string zone_id = 1; string zone_id = 1;
krata.v1.common.ZoneTaskSpec task = 2; krata.v1.common.ZoneTaskSpec task = 2;
bytes data = 3; bytes stdin = 3;
bool stdin_closed = 4;
} }
message ExecInsideZoneReply { message ExecInsideZoneReply {
@ -242,3 +245,10 @@ message SetHostPowerManagementPolicyRequest {
} }
message SetHostPowerManagementPolicyReply {} message SetHostPowerManagementPolicyReply {}
message UpdateZoneResourcesRequest {
string zone_id = 1;
krata.v1.common.ZoneResourceSpec resources = 2;
}
message UpdateZoneResourcesReply {}

View File

@ -16,7 +16,7 @@ clap = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
etherparse = { workspace = true } etherparse = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
krata = { path = "../krata", version = "^0.0.16" } krata = { path = "../krata", version = "^0.0.17" }
krata-advmac = { workspace = true } krata-advmac = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }

View File

@ -12,20 +12,20 @@ resolver = "2"
anyhow = { workspace = true } anyhow = { workspace = true }
backhand = { workspace = true } backhand = { workspace = true }
ipnetwork = { workspace = true } ipnetwork = { workspace = true }
krata = { path = "../krata", version = "^0.0.16" } krata = { path = "../krata", version = "^0.0.17" }
krata-advmac = { workspace = true } krata-advmac = { workspace = true }
krata-oci = { path = "../oci", version = "^0.0.16" } krata-oci = { path = "../oci", version = "^0.0.17" }
log = { workspace = true } log = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }
krata-loopdev = { path = "../loopdev", version = "^0.0.16" } krata-loopdev = { path = "../loopdev", version = "^0.0.17" }
krata-xencall = { path = "../xen/xencall", version = "^0.0.16" } krata-xencall = { path = "../xen/xencall", version = "^0.0.17" }
krata-xenclient = { path = "../xen/xenclient", version = "^0.0.16" } krata-xenclient = { path = "../xen/xenclient", version = "^0.0.17" }
krata-xenevtchn = { path = "../xen/xenevtchn", version = "^0.0.16" } krata-xenevtchn = { path = "../xen/xenevtchn", version = "^0.0.17" }
krata-xengnt = { path = "../xen/xengnt", version = "^0.0.16" } krata-xengnt = { path = "../xen/xengnt", version = "^0.0.17" }
krata-xenplatform = { path = "../xen/xenplatform", version = "^0.0.16" } krata-xenplatform = { path = "../xen/xenplatform", version = "^0.0.17" }
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.16" } krata-xenstore = { path = "../xen/xenstore", version = "^0.0.17" }
walkdir = { workspace = true } walkdir = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }

View File

@ -30,8 +30,10 @@ pub struct ZoneLaunchRequest {
pub initrd: Vec<u8>, pub initrd: Vec<u8>,
pub uuid: Option<Uuid>, pub uuid: Option<Uuid>,
pub name: Option<String>, pub name: Option<String>,
pub vcpus: u32, pub target_cpus: u32,
pub mem: u64, pub max_cpus: u32,
pub target_memory: u64,
pub max_memory: u64,
pub env: HashMap<String, String>, pub env: HashMap<String, String>,
pub run: Option<Vec<String>>, pub run: Option<Vec<String>>,
pub pcis: Vec<PciDevice>, pub pcis: Vec<PciDevice>,
@ -194,8 +196,10 @@ impl ZoneLauncher {
let config = DomainConfig { let config = DomainConfig {
base: BaseDomainConfig { base: BaseDomainConfig {
max_vcpus: request.vcpus, max_vcpus: request.max_cpus,
mem_mb: request.mem, target_vcpus: request.target_cpus,
max_mem_mb: request.max_memory,
target_mem_mb: request.target_memory,
kernel: request.kernel, kernel: request.kernel,
initrd: request.initrd, initrd: request.initrd,
cmdline, cmdline,

View File

@ -1,11 +1,11 @@
use std::{fs, path::PathBuf, str::FromStr, sync::Arc};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use krataloopdev::LoopControl; use krataloopdev::LoopControl;
use std::{fs, path::PathBuf, str::FromStr, sync::Arc};
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
use uuid::Uuid; use uuid::Uuid;
use xenclient::XenClient; use xenclient::XenClient;
use xenplatform::domain::XEN_EXTRA_MEMORY_KB;
use xenstore::{XsdClient, XsdInterface}; use xenstore::{XsdClient, XsdInterface};
use self::{ use self::{
@ -226,6 +226,65 @@ impl Runtime {
Ok(uuid) Ok(uuid)
} }
pub async fn set_memory_resources(
&self,
domid: u32,
target_memory_bytes: u64,
max_memory_bytes: u64,
) -> Result<()> {
let mut max_memory_bytes = max_memory_bytes + (XEN_EXTRA_MEMORY_KB * 1024);
if target_memory_bytes > max_memory_bytes {
max_memory_bytes = target_memory_bytes + (XEN_EXTRA_MEMORY_KB * 1024);
}
self.context
.xen
.call
.set_max_mem(domid, max_memory_bytes / 1024)
.await?;
let domain_path = self.context.xen.store.get_domain_path(domid).await?;
let tx = self.context.xen.store.transaction().await?;
let max_memory_path = format!("{}/memory/static-max", domain_path);
tx.write_string(max_memory_path, &(max_memory_bytes / 1024).to_string())
.await?;
let target_memory_path = format!("{}/memory/target", domain_path);
tx.write_string(
target_memory_path,
&(target_memory_bytes / 1024).to_string(),
)
.await?;
tx.commit().await?;
Ok(())
}
pub async fn set_cpu_resources(&self, domid: u32, target_cpus: u32) -> Result<()> {
let domain_path = self.context.xen.store.get_domain_path(domid).await?;
let cpus = self
.context
.xen
.store
.list(&format!("{}/cpu", domain_path))
.await?;
let tx = self.context.xen.store.transaction().await?;
for cpu in cpus {
let Some(id) = cpu.parse::<u32>().ok() else {
continue;
};
let available = if id >= target_cpus {
"offline"
} else {
"online"
};
tx.write_string(
format!("{}/cpu/{}/availability", domain_path, id),
available,
)
.await?;
}
tx.commit().await?;
Ok(())
}
pub async fn list(&self) -> Result<Vec<ZoneInfo>> { pub async fn list(&self) -> Result<Vec<ZoneInfo>> {
self.context.list().await self.context.list().await
} }

View File

@ -13,9 +13,9 @@ async-trait = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
krata-xencall = { path = "../xencall", version = "^0.0.16" } krata-xencall = { path = "../xencall", version = "^0.0.17" }
krata-xenplatform = { path = "../xenplatform", version = "^0.0.16" } krata-xenplatform = { path = "../xenplatform", version = "^0.0.17" }
krata-xenstore = { path = "../xenstore", version = "^0.0.16" } krata-xenstore = { path = "../xenstore", version = "^0.0.17" }
regex = { workspace = true } regex = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }

View File

@ -27,7 +27,9 @@ async fn main() -> Result<()> {
base: BaseDomainConfig { base: BaseDomainConfig {
uuid: Uuid::new_v4(), uuid: Uuid::new_v4(),
max_vcpus: 1, max_vcpus: 1,
mem_mb: 512, target_vcpus: 1,
max_mem_mb: 512,
target_mem_mb: 512,
enable_iommu: true, enable_iommu: true,
kernel: fs::read(&kernel_image_path).await?, kernel: fs::read(&kernel_image_path).await?,
initrd: fs::read(&initrd_path).await?, initrd: fs::read(&initrd_path).await?,

View File

@ -156,13 +156,13 @@ impl ClientTransaction {
self.tx self.tx
.write_string( .write_string(
format!("{}/memory/static-max", self.dom_path).as_str(), format!("{}/memory/static-max", self.dom_path).as_str(),
&(base.mem_mb * 1024).to_string(), &(base.max_mem_mb * 1024).to_string(),
) )
.await?; .await?;
self.tx self.tx
.write_string( .write_string(
format!("{}/memory/target", self.dom_path).as_str(), format!("{}/memory/target", self.dom_path).as_str(),
&(base.mem_mb * 1024).to_string(), &(base.target_mem_mb * 1024).to_string(),
) )
.await?; .await?;
self.tx self.tx
@ -194,7 +194,16 @@ impl ClientTransaction {
self.tx.mkdir(&path).await?; self.tx.mkdir(&path).await?;
self.tx.set_perms(&path, ro_perm).await?; self.tx.set_perms(&path, ro_perm).await?;
let path = format!("{}/cpu/{}/availability", self.dom_path, i); let path = format!("{}/cpu/{}/availability", self.dom_path, i);
self.tx.write_string(&path, "online").await?; self.tx
.write_string(
&path,
if i < base.target_vcpus {
"online"
} else {
"offline"
},
)
.await?;
self.tx.set_perms(&path, ro_perm).await?; self.tx.set_perms(&path, ro_perm).await?;
} }
Ok(()) Ok(())

View File

@ -16,7 +16,7 @@ flate2 = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
krata-xencall = { path = "../xencall", version = "^0.0.16" } krata-xencall = { path = "../xencall", version = "^0.0.17" }
memchr = { workspace = true } memchr = { workspace = true }
nix = { workspace = true } nix = { workspace = true }
regex = { workspace = true } regex = { workspace = true }

View File

@ -162,11 +162,13 @@ impl<I: BootImageLoader, P: BootSetupPlatform> BootSetup<I, P> {
pub async fn initialize( pub async fn initialize(
&mut self, &mut self,
initrd: &[u8], initrd: &[u8],
mem_mb: u64, target_mem_mb: u64,
max_mem_mb: u64,
max_vcpus: u32, max_vcpus: u32,
cmdline: &str, cmdline: &str,
) -> Result<BootDomain> { ) -> Result<BootDomain> {
let total_pages = mem_mb << (20 - self.platform.page_shift()); let target_pages = target_mem_mb << (20 - self.platform.page_shift());
let total_pages = max_mem_mb << (20 - self.platform.page_shift());
let image_info = self.image_loader.parse(self.platform.hvm()).await?; let image_info = self.image_loader.parse(self.platform.hvm()).await?;
let mut domain = BootDomain { let mut domain = BootDomain {
domid: self.domid, domid: self.domid,
@ -175,7 +177,7 @@ impl<I: BootImageLoader, P: BootSetupPlatform> BootSetup<I, P> {
virt_pgtab_end: 0, virt_pgtab_end: 0,
pfn_alloc_end: 0, pfn_alloc_end: 0,
total_pages, total_pages,
target_pages: total_pages, target_pages,
page_size: self.platform.page_size(), page_size: self.platform.page_size(),
image_info, image_info,
console_evtchn: 0, console_evtchn: 0,

View File

@ -9,6 +9,8 @@ use xencall::XenCall;
use crate::error::Result; use crate::error::Result;
pub const XEN_EXTRA_MEMORY_KB: u64 = 2048;
pub struct BaseDomainManager<P: BootSetupPlatform> { pub struct BaseDomainManager<P: BootSetupPlatform> {
call: XenCall, call: XenCall,
pub platform: Arc<P>, pub platform: Arc<P>,
@ -29,7 +31,7 @@ impl<P: BootSetupPlatform> BaseDomainManager<P> {
let domid = self.call.create_domain(domain).await?; let domid = self.call.create_domain(domain).await?;
self.call.set_max_vcpus(domid, config.max_vcpus).await?; self.call.set_max_vcpus(domid, config.max_vcpus).await?;
self.call self.call
.set_max_mem(domid, (config.mem_mb * 1024) + 2048) .set_max_mem(domid, (config.max_mem_mb * 1024) + XEN_EXTRA_MEMORY_KB)
.await?; .await?;
let loader = ElfImageLoader::load_file_kernel(&config.kernel)?; let loader = ElfImageLoader::load_file_kernel(&config.kernel)?;
let platform = (*self.platform).clone(); let platform = (*self.platform).clone();
@ -37,7 +39,8 @@ impl<P: BootSetupPlatform> BaseDomainManager<P> {
let mut domain = boot let mut domain = boot
.initialize( .initialize(
&config.initrd, &config.initrd,
config.mem_mb, config.target_mem_mb,
config.max_mem_mb,
config.max_vcpus, config.max_vcpus,
&config.cmdline, &config.cmdline,
) )
@ -63,7 +66,9 @@ pub struct BaseDomainConfig {
pub uuid: Uuid, pub uuid: Uuid,
pub owner_domid: u32, pub owner_domid: u32,
pub max_vcpus: u32, pub max_vcpus: u32,
pub mem_mb: u64, pub target_vcpus: u32,
pub max_mem_mb: u64,
pub target_mem_mb: u64,
pub kernel: Vec<u8>, pub kernel: Vec<u8>,
pub initrd: Vec<u8>, pub initrd: Vec<u8>,
pub cmdline: String, pub cmdline: String,

View File

@ -14,14 +14,15 @@ cgroups-rs = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
ipnetwork = { workspace = true } ipnetwork = { workspace = true }
krata = { path = "../krata", version = "^0.0.16" } krata = { path = "../krata", version = "^0.0.17" }
krata-xenstore = { path = "../xen/xenstore", version = "^0.0.16" } krata-xenstore = { path = "../xen/xenstore", version = "^0.0.17" }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
nix = { workspace = true, features = ["ioctl", "process", "fs"] } nix = { workspace = true, features = ["ioctl", "process", "fs"] }
oci-spec = { workspace = true } oci-spec = { workspace = true }
path-absolutize = { workspace = true } path-absolutize = { workspace = true }
platform-info = { workspace = true } platform-info = { workspace = true }
pty-process = { workspace = true, features = ["async"] }
rtnetlink = { workspace = true } rtnetlink = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }

View File

@ -1,12 +1,6 @@
use std::{collections::HashMap, process::Stdio}; use std::{collections::HashMap, process::Stdio};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
join,
process::Command,
};
use krata::idm::{ use krata::idm::{
client::IdmClientStreamResponseHandle, client::IdmClientStreamResponseHandle,
internal::{ internal::{
@ -15,6 +9,14 @@ use krata::idm::{
}, },
internal::{response::Response as ResponseType, Request, Response}, internal::{response::Response as ResponseType, Request, Response},
}; };
use libc::c_int;
use pty_process::{Pty, Size};
use tokio::process::Child;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
join,
process::Command,
};
use crate::childwait::ChildWait; use crate::childwait::ChildWait;
@ -52,7 +54,7 @@ impl ZoneExecTask {
if !env.contains_key("PATH") { if !env.contains_key("PATH") {
env.insert( env.insert(
"PATH".to_string(), "PATH".to_string(),
"/bin:/usr/bin:/usr/local/bin".to_string(), "/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin".to_string(),
); );
} }
@ -63,112 +65,196 @@ impl ZoneExecTask {
}; };
let mut wait_subscription = self.wait.subscribe().await?; let mut wait_subscription = self.wait.subscribe().await?;
let mut child = Command::new(exe)
.args(cmd)
.envs(env)
.current_dir(dir)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.kill_on_drop(true)
.spawn()
.map_err(|error| anyhow!("failed to spawn: {}", error))?;
let pid = child.id().ok_or_else(|| anyhow!("pid is not provided"))?; let code: c_int;
let mut stdin = child if start.tty {
.stdin let pty = Pty::new().map_err(|error| anyhow!("unable to allocate pty: {}", error))?;
.take() pty.resize(Size::new(24, 80))?;
.ok_or_else(|| anyhow!("stdin was missing"))?; let mut child = ChildDropGuard {
let mut stdout = child inner: pty_process::Command::new(exe)
.stdout .args(cmd)
.take() .envs(env)
.ok_or_else(|| anyhow!("stdout was missing"))?; .current_dir(dir)
let mut stderr = child .spawn(
.stderr &pty.pts()
.take() .map_err(|error| anyhow!("unable to allocate pts: {}", error))?,
.ok_or_else(|| anyhow!("stderr was missing"))?; )
.map_err(|error| anyhow!("failed to spawn: {}", error))?,
let stdout_handle = self.handle.clone(); kill: true,
let stdout_task = tokio::task::spawn(async move { };
let mut stdout_buffer = vec![0u8; 8 * 1024]; let pid = child
loop { .inner
let Ok(size) = stdout.read(&mut stdout_buffer).await else { .id()
break; .ok_or_else(|| anyhow!("pid is not provided"))?;
}; let (mut read, mut write) = pty.into_split();
if size > 0 { let pty_read_handle = self.handle.clone();
let response = Response { let pty_read_task = tokio::task::spawn(async move {
response: Some(ResponseType::ExecStream(ExecStreamResponseUpdate { let mut stdout_buffer = vec![0u8; 8 * 1024];
exited: false, loop {
exit_code: 0, let Ok(size) = read.read(&mut stdout_buffer).await else {
error: String::new(), break;
stdout: stdout_buffer[0..size].to_vec(),
stderr: vec![],
})),
}; };
let _ = stdout_handle.respond(response).await; if size > 0 {
} else { let response = Response {
break; response: Some(ResponseType::ExecStream(ExecStreamResponseUpdate {
exited: false,
exit_code: 0,
error: String::new(),
stdout: stdout_buffer[0..size].to_vec(),
stderr: vec![],
})),
};
let _ = pty_read_handle.respond(response).await;
} else {
break;
}
} }
} });
});
let stderr_handle = self.handle.clone(); let stdin_task = tokio::task::spawn(async move {
let stderr_task = tokio::task::spawn(async move { loop {
let mut stderr_buffer = vec![0u8; 8 * 1024]; let Some(request) = receiver.recv().await else {
loop { break;
let Ok(size) = stderr.read(&mut stderr_buffer).await else {
break;
};
if size > 0 {
let response = Response {
response: Some(ResponseType::ExecStream(ExecStreamResponseUpdate {
exited: false,
exit_code: 0,
error: String::new(),
stdout: vec![],
stderr: stderr_buffer[0..size].to_vec(),
})),
}; };
let _ = stderr_handle.respond(response).await;
} else { let Some(RequestType::ExecStream(update)) = request.request else {
break; continue;
};
let Some(Update::Stdin(update)) = update.update else {
continue;
};
if !update.data.is_empty() && write.write_all(&update.data).await.is_err() {
break;
}
if update.closed {
break;
}
} }
} });
});
let stdin_task = tokio::task::spawn(async move { code = loop {
loop { if let Ok(event) = wait_subscription.recv().await {
let Some(request) = receiver.recv().await else { if event.pid.as_raw() as u32 == pid {
break; break event.status;
}; }
let Some(RequestType::ExecStream(update)) = request.request else {
continue;
};
let Some(Update::Stdin(update)) = update.update else {
continue;
};
if stdin.write_all(&update.data).await.is_err() {
break;
} }
} };
});
let data_task = tokio::task::spawn(async move { child.kill = false;
let _ = join!(stdout_task, stderr_task);
let _ = join!(pty_read_task);
stdin_task.abort(); stdin_task.abort();
}); } else {
let mut child = Command::new(exe)
.args(cmd)
.envs(env)
.current_dir(dir)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.kill_on_drop(true)
.spawn()
.map_err(|error| anyhow!("failed to spawn: {}", error))?;
let code = loop { let pid = child.id().ok_or_else(|| anyhow!("pid is not provided"))?;
if let Ok(event) = wait_subscription.recv().await { let mut stdin = child
if event.pid.as_raw() as u32 == pid { .stdin
break event.status; .take()
.ok_or_else(|| anyhow!("stdin was missing"))?;
let mut stdout = child
.stdout
.take()
.ok_or_else(|| anyhow!("stdout was missing"))?;
let mut stderr = child
.stderr
.take()
.ok_or_else(|| anyhow!("stderr was missing"))?;
let stdout_handle = self.handle.clone();
let stdout_task = tokio::task::spawn(async move {
let mut stdout_buffer = vec![0u8; 8 * 1024];
loop {
let Ok(size) = stdout.read(&mut stdout_buffer).await else {
break;
};
if size > 0 {
let response = Response {
response: Some(ResponseType::ExecStream(ExecStreamResponseUpdate {
exited: false,
exit_code: 0,
error: String::new(),
stdout: stdout_buffer[0..size].to_vec(),
stderr: vec![],
})),
};
let _ = stdout_handle.respond(response).await;
} else {
break;
}
} }
} });
};
data_task.await?; let stderr_handle = self.handle.clone();
let stderr_task = tokio::task::spawn(async move {
let mut stderr_buffer = vec![0u8; 8 * 1024];
loop {
let Ok(size) = stderr.read(&mut stderr_buffer).await else {
break;
};
if size > 0 {
let response = Response {
response: Some(ResponseType::ExecStream(ExecStreamResponseUpdate {
exited: false,
exit_code: 0,
error: String::new(),
stdout: vec![],
stderr: stderr_buffer[0..size].to_vec(),
})),
};
let _ = stderr_handle.respond(response).await;
} else {
break;
}
}
});
let stdin_task = tokio::task::spawn(async move {
loop {
let Some(request) = receiver.recv().await else {
break;
};
let Some(RequestType::ExecStream(update)) = request.request else {
continue;
};
let Some(Update::Stdin(update)) = update.update else {
continue;
};
if stdin.write_all(&update.data).await.is_err() {
break;
}
}
});
let data_task = tokio::task::spawn(async move {
let _ = join!(stdout_task, stderr_task);
stdin_task.abort();
});
code = loop {
if let Ok(event) = wait_subscription.recv().await {
if event.pid.as_raw() as u32 == pid {
break event.status;
}
}
};
data_task.await?;
}
let response = Response { let response = Response {
response: Some(ResponseType::ExecStream(ExecStreamResponseUpdate { response: Some(ResponseType::ExecStream(ExecStreamResponseUpdate {
exited: true, exited: true,
@ -183,3 +269,16 @@ impl ZoneExecTask {
Ok(()) Ok(())
} }
} }
struct ChildDropGuard {
pub inner: Child,
pub kill: bool,
}
impl Drop for ChildDropGuard {
fn drop(&mut self) {
if self.kill {
drop(self.inner.start_kill());
}
}
}

View File

@ -2,7 +2,7 @@ use std::{ops::Add, path::Path};
use anyhow::Result; use anyhow::Result;
use krata::idm::internal::{MetricFormat, MetricNode}; use krata::idm::internal::{MetricFormat, MetricNode};
use sysinfo::Process; use sysinfo::{Process, ProcessesToUpdate};
pub struct MetricsCollector {} pub struct MetricsCollector {}
@ -38,7 +38,7 @@ impl MetricsCollector {
} }
fn collect_processes(&self, sysinfo: &mut sysinfo::System) -> Result<MetricNode> { fn collect_processes(&self, sysinfo: &mut sysinfo::System) -> Result<MetricNode> {
sysinfo.refresh_processes(); sysinfo.refresh_processes(ProcessesToUpdate::All);
let mut processes = Vec::new(); let mut processes = Vec::new();
let mut sysinfo_processes = sysinfo.processes().values().collect::<Vec<_>>(); let mut sysinfo_processes = sysinfo.processes().values().collect::<Vec<_>>();
sysinfo_processes.sort_by_key(|x| x.pid()); sysinfo_processes.sort_by_key(|x| x.pid());
@ -70,7 +70,11 @@ impl MetricsCollector {
metrics.push(MetricNode::raw_value("cwd", working_directory)); metrics.push(MetricNode::raw_value("cwd", working_directory));
} }
let cmdline = process.cmd().to_vec(); let cmdline = process
.cmd()
.iter()
.map(|x| x.to_string_lossy().to_string())
.collect::<Vec<_>>();
metrics.push(MetricNode::raw_value("cmdline", cmdline)); metrics.push(MetricNode::raw_value("cmdline", cmdline));
metrics.push(MetricNode::structural( metrics.push(MetricNode::structural(
"memory", "memory",

View File

@ -0,0 +1,131 @@
Custom Kernels in krata
=======================
Krata supports using custom kernels instead of the default Edera-provided
kernel both on a system-wide and zone-wide basis. Krata also supports using
a custom host kernel, as long as it meets certain technical requirements.
System-wide default kernel for zones
------------------------------------
The standard system-wide default kernel for zones is stored in
`/var/lib/krata/zone/kernel` which is the kernel image that should be
booted for the zone, and `/var/lib/krata/zone/addons.squashfs`,
which contains a set of kernel modules that should be mounted in the
zone.
Zone-wide alternative kernels via OCI
-------------------------------------
Krata also supports fetching alternative kernel images for use in zones
via OCI repositories. These kernel images are distributed like any other
OCI image, but are not intended to be directly executed by an OCI runtime.
To select an alternative kernel, you can supply the `-k` option to the
`kratactl zone launch` command that specifies an OCI tag to pull the
alternative kernel image from.
OCI-based kernel image contents
-------------------------------
OCI-based kernel images contain the following files:
* `/kernel/image`: The kernel image itself.
* `/kernel/addons.squashfs`: A squashfs file containing the kernel
modules for a given kernel image.
* `/kernel/metadata`: A file containing the following metadata fields
in `KEY=VALUE` format:
- `KERNEL_ARCH`: The kernel architecture (`x86_64` or `aarch64`)
- `KERNEL_VERSION`: The kernel version
- `KERNEL_FLAVOR`: The kernel flavor (examples: `standard`, `dom0` or `openpax`)
- `KERNEL_CONFIG`: The digest for the relevant configuration file stored in the OCI
repository
- `KERNEL_TAGS`: The OCI tags this kernel image was originally built for
(example: `latest`)
Minimum requirements for a zone-wide/system-wide kernel
-------------------------------------------------------
The following configuration options must be set:
```
CONFIG_XEN=y
CONFIG_XEN_PV=y
CONFIG_XEN_512GB=y
CONFIG_XEN_PV_SMP=y
CONFIG_XEN_PVHVM=y
CONFIG_XEN_PVHVM_SMP=y
CONFIG_XEN_PVHVM_GUEST=y
CONFIG_XEN_SAVE_RESTORE=y
CONFIG_XEN_PVH=y
CONFIG_XEN_PV_MSR_SAFE=y
CONFIG_PCI_XEN=y
CONFIG_NET_9P_XEN=y
CONFIG_XEN_PCIDEV_FRONTEND=y
CONFIG_XEN_BLKDEV_FRONTEND=y
CONFIG_XEN_NETDEV_FRONTEND=y
CONFIG_INPUT_XEN_KBDDEV_FRONTEND=y
CONFIG_HVC_XEN=y
CONFIG_HVC_XEN_FRONTEND=y
CONFIG_XEN_FBDEV_FRONTEND=m
CONFIG_XEN_BALLOON=y
CONFIG_XEN_BALLOON_MEMORY_HOTPLUG=y
CONFIG_XEN_MEMORY_HOTPLUG_LIMIT=512
CONFIG_XEN_SCRUB_PAGES_DEFAULT=y
CONFIG_XEN_DEV_EVTCHN=y
CONFIG_XEN_BACKEND=y
CONFIG_XENFS=y
CONFIG_XEN_COMPAT_XENFS=y
CONFIG_XEN_SYS_HYPERVISOR=y
CONFIG_XEN_XENBUS_FRONTEND=y
CONFIG_SWIOTLB_XEN=y
CONFIG_XEN_HAVE_PVMMU=y
CONFIG_XEN_EFI=y
CONFIG_XEN_AUTO_XLATE=y
CONFIG_XEN_ACPI=y
CONFIG_XEN_HAVE_VPMU=y
CONFIG_XEN_GRANT_DMA_OPS=y
CONFIG_XEN_VIRTIO=y
```
It is possible to copy these options into a `.config` file and then use
`make olddefconfig` to build the rest of the kernel configuration, which
you can then use to build a kernel as desired.
The [kernels][edera-kernels] repository provides some example configurations
and can generate a Dockerfile which will build a kernel image.
[edera-kernels]: https://github.com/edera-dev/kernels
Minimum requirements for a host kernel
--------------------------------------
The configuration options above are also required for a host kernel.
In addition, the following options are also required:
```
CONFIG_XEN_PV_DOM0=y
CONFIG_XEN_DOM0=y
CONFIG_PCI_XEN=y
CONFIG_XEN_PCIDEV_BACKEND=y
CONFIG_XEN_BLKDEV_BACKEND=y
CONFIG_XEN_NETDEV_BACKEND=y
CONFIG_XEN_SCSI_BACKEND=y
CONFIG_XEN_PVCALLS_BACKEND=y
CONFIG_TCG_XEN=m
CONFIG_XEN_WDT=y
CONFIG_XEN_DEV_EVTCHN=y
CONFIG_XEN_GNTDEV=y
CONFIG_XEN_GRANT_DEV_ALLOC=y
CONFIG_XEN_GRANT_DMA_ALLOC=y
CONFIG_SWIOTLB_XEN=y
CONFIG_XEN_PRIVCMD=y
CONFIG_XEN_ACPI_PROCESSOR=y
CONFIG_XEN_MCE_LOG=y
```
Build and install the kernel as you normally would for your system.
Assuming GRUB is the bootloader, it will automatically detect the new
host kernel when you run `grub-mkconfig` or `grub2-mkconfig`.