From 538ac84c64a9a10d7862af159c32aafa26f1b846 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Tue, 10 Dec 2024 08:54:14 +0000 Subject: [PATCH] tappd: Add API SysInfo --- Cargo.lock | 124 ++++++++++++++++++++++++++++++-- tappd/Cargo.toml | 1 + tappd/rpc/proto/tappd_rpc.proto | 49 +++++++++++++ tappd/src/http_routes.rs | 5 ++ tappd/src/models.rs | 19 ++++- tappd/src/rpc_service.rs | 48 ++++++++++++- tappd/templates/dashboard.html | 81 ++++++++++++++++++++- 7 files changed, 317 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 967b8247..805a36c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -786,6 +786,25 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -1455,7 +1474,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows", + "windows 0.48.0", ] [[package]] @@ -1977,7 +1996,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -2555,6 +2574,15 @@ dependencies = [ "instant", ] +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3367,6 +3395,26 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "rcgen" version = "0.13.1" @@ -4537,6 +4585,20 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "sysinfo" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -4594,6 +4656,7 @@ dependencies = [ "serde", "serde_json", "sha2", + "sysinfo", "tappd-rpc", "tdx-attest", "tokio", @@ -5419,6 +5482,16 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -5428,17 +5501,60 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result 0.1.2", + "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.87", +] + +[[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.87", +] + [[package]] name = "windows-registry" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-strings", "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -5454,7 +5570,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] diff --git a/tappd/Cargo.toml b/tappd/Cargo.toml index 9bf0a3eb..af02e31a 100644 --- a/tappd/Cargo.toml +++ b/tappd/Cargo.toml @@ -28,3 +28,4 @@ ra-rpc = { workspace = true, features = ["rocket"] } tappd-rpc = { workspace = true } ra-tls = { workspace = true } tdx-attest = { workspace = true } +sysinfo = "0.33.0" diff --git a/tappd/rpc/proto/tappd_rpc.proto b/tappd/rpc/proto/tappd_rpc.proto index 626a371a..350a3e67 100644 --- a/tappd/rpc/proto/tappd_rpc.proto +++ b/tappd/rpc/proto/tappd_rpc.proto @@ -66,6 +66,8 @@ message TdxQuoteResponse { service Worker { // Get worker info rpc Info(google.protobuf.Empty) returns (WorkerInfo) {} + // Get system info + rpc SysInfo(google.protobuf.Empty) returns (SystemInfo) {} // Get worker containers rpc ListContainers(google.protobuf.Empty) returns (ListContainersResponse) {} } @@ -104,3 +106,50 @@ message WorkerInfo { // TCB info string tcb_info = 4; } + +// The system info +message SystemInfo { + // Operating system + string os_name = 1; + // Operating system version + string os_version = 2; + // Kernel version + string kernel_version = 3; + // Cpu model + string cpu_model = 4; + // Number of logical CPUs + uint32 num_cpus = 5; + // Total memory + uint64 total_memory = 6; + // Available memory + uint64 available_memory = 7; + // Used memory + uint64 used_memory = 8; + // Free memory + uint64 free_memory = 9; + // Total swap memory + uint64 total_swap = 10; + // Used swap memory + uint64 used_swap = 11; + // Free swap memory + uint64 free_swap = 12; + // Uptime + uint64 uptime = 13; + // Load average + uint32 loadavg_one = 14; + uint32 loadavg_five = 15; + uint32 loadavg_fifteen = 16; + // Disks + repeated DiskInfo disks = 17; +} + +message DiskInfo { + // Device name + string name = 1; + // Mount point + string mount_point = 2; + // Total size + uint64 total_size = 3; + // Free size + uint64 free_size = 5; +} diff --git a/tappd/src/http_routes.rs b/tappd/src/http_routes.rs index de3ad2c6..8bedcdaf 100644 --- a/tappd/src/http_routes.rs +++ b/tappd/src/http_routes.rs @@ -74,6 +74,10 @@ async fn index(state: &State) -> Result, String> { .await .map_err(|e| format!("Failed to get worker info: {}", e))?; + let handler = ExternalRpcHandler::construct(state, None) + .map_err(|e| format!("Failed to construct RPC handler: {}", e))?; + let system_info = handler.sys_info().await.unwrap_or_default(); + let containers = list_containers().await.unwrap_or_default().containers; let model = crate::models::Dashboard { app_id, @@ -81,6 +85,7 @@ async fn index(state: &State) -> Result, String> { app_cert, tcb_info, containers, + system_info, }; match model.render() { Ok(html) => Ok(RawHtml(html)), diff --git a/tappd/src/models.rs b/tappd/src/models.rs index 75470505..3d54b838 100644 --- a/tappd/src/models.rs +++ b/tappd/src/models.rs @@ -1,5 +1,5 @@ use rinja::Template; -use tappd_rpc::Container; +use tappd_rpc::{Container, SystemInfo}; mod filters { use anyhow::Result; @@ -8,6 +8,22 @@ mod filters { let name = s.map(|s| s.as_str()).unwrap_or_default(); Ok(name.strip_prefix("/").unwrap_or(name)) } + + pub fn hsize(s: &u64) -> Result { + // convert bytes to human readable size + let mut size = *s as f64; + let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + let mut unit_index = 0; + while size >= 1024.0 && unit_index < units.len() - 1 { + size /= 1024.0; + unit_index += 1; + } + Ok(format!( + "{:.2} {}", + size, + units.get(unit_index).unwrap_or(&"?") + )) + } } #[derive(Template)] @@ -18,4 +34,5 @@ pub struct Dashboard { pub app_cert: String, pub tcb_info: String, pub containers: Vec, + pub system_info: SystemInfo, } diff --git a/tappd/src/rpc_service.rs b/tappd/src/rpc_service.rs index 2ef74cc7..230dc694 100644 --- a/tappd/src/rpc_service.rs +++ b/tappd/src/rpc_service.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{path::Path, sync::Arc}; use anyhow::{bail, Context, Result}; use bollard::{container::ListContainersOptions, Docker}; @@ -13,8 +13,8 @@ use serde_json::json; use tappd_rpc::{ tappd_server::{TappdRpc, TappdServer}, worker_server::{WorkerRpc, WorkerServer}, - Container, DeriveKeyArgs, DeriveKeyResponse, ListContainersResponse, TdxQuoteArgs, - TdxQuoteResponse, WorkerInfo, + Container, DeriveKeyArgs, DeriveKeyResponse, DiskInfo, ListContainersResponse, SystemInfo, + TdxQuoteArgs, TdxQuoteResponse, WorkerInfo, }; use tdx_attest::eventlog::read_event_logs; @@ -149,6 +149,48 @@ impl WorkerRpc for ExternalRpcHandler { }) } + async fn sys_info(self) -> Result { + use sysinfo::System; + + let system = System::new_all(); + let cpus = system.cpus(); + + let disks = sysinfo::Disks::new_with_refreshed_list(); + let disks = disks + .list() + .iter() + .filter(|d| d.mount_point() == Path::new("/")) + .map(|d| DiskInfo { + name: d.name().to_string_lossy().to_string(), + mount_point: d.mount_point().to_string_lossy().to_string(), + total_size: d.total_space(), + free_size: d.available_space(), + }) + .collect::>(); + let avg = System::load_average(); + Ok(SystemInfo { + os_name: System::name().unwrap_or_default(), + os_version: System::os_version().unwrap_or_default(), + kernel_version: System::kernel_version().unwrap_or_default(), + cpu_model: cpus.get(0).map_or("".into(), |cpu| { + format!("{} @{} MHz", cpu.name(), cpu.frequency()) + }), + num_cpus: cpus.len() as _, + total_memory: system.total_memory(), + available_memory: system.available_memory(), + used_memory: system.used_memory(), + free_memory: system.free_memory(), + total_swap: system.total_swap(), + used_swap: system.used_swap(), + free_swap: system.free_swap(), + uptime: System::uptime(), + loadavg_one: (avg.one * 100.0) as u32, + loadavg_five: (avg.five * 100.0) as u32, + loadavg_fifteen: (avg.fifteen * 100.0) as u32, + disks, + }) + } + async fn list_containers(self) -> Result { list_containers().await } diff --git a/tappd/templates/dashboard.html b/tappd/templates/dashboard.html index d07d9d4f..5571eea3 100644 --- a/tappd/templates/dashboard.html +++ b/tappd/templates/dashboard.html @@ -105,15 +105,90 @@ margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } + + .info-grid { + display: grid; + gap: 12px; + margin-top: 16px; + } + + .info-row { + display: grid; + grid-template-columns: 200px 1fr; + align-items: center; + padding: 4px 12px; + background-color: #f8f9fa; + border-radius: 6px; + } + + .info-label { + font-weight: 600; + color: var(--primary-color); + } + + .info-value { + font-family: monospace; + background-color: white; + padding: 6px 12px; + border-radius: 4px; + border: 1px solid var(--border-color); + }

Worker Information

-

App ID: {{app_id}}

-

Instance ID: {{instance_id}}

+

System Information

+
+
+
App ID
+
{{app_id}}
+
+
+
Instance ID
+
{{instance_id}}
+
+
+
Operating System
+
{{system_info.os_name}} {{system_info.os_version}}
+
+
+
Kernel Version
+
{{system_info.kernel_version}}
+
+
+
CPU
+
{{system_info.cpu_model}} ({{system_info.num_cpus}} cores)
+
+
+
Memory Usage
+
Used: {{system_info.used_memory|hsize}} / Total: {{system_info.total_memory|hsize}} + (Available: {{system_info.available_memory|hsize}})
+
+
+
Swap Usage
+
Used: {{system_info.used_swap|hsize}} / Total: {{system_info.total_swap|hsize}} (Free: + {{system_info.free_swap|hsize}})
+
+
+
Load Average
+
1min: {{system_info.loadavg_one as f32 / 100.0}}%, 5min: {{system_info.loadavg_five as f32 / 100.0}}%, 15min: + {{system_info.loadavg_fifteen as f32 / 100.0}}%
+
+
+
System Uptime
+
{{system_info.uptime}}
+
+ {% for disk in system_info.disks %} +
+
Disk
+
Free: {{disk.free_size|hsize}} / Total: {{disk.total_size|hsize}}
+
+ {% endfor %} +
+

Deployed Containers

@@ -144,4 +219,6 @@

App Certificate

+ + \ No newline at end of file