diff --git a/vmm/rpc/proto/vmm_rpc.proto b/vmm/rpc/proto/vmm_rpc.proto index 64de42e8c..6ef77320e 100644 --- a/vmm/rpc/proto/vmm_rpc.proto +++ b/vmm/rpc/proto/vmm_rpc.proto @@ -35,6 +35,15 @@ message VmInfo { string shutdown_progress = 12; // Image version string image_version = 13; + // Events + repeated GuestEvent events = 14; +} + +message GuestEvent { + string event = 1; + string body = 2; + // Timestamp in milliseconds since Unix epoch + uint64 timestamp = 3; } message Id { diff --git a/vmm/src/app.rs b/vmm/src/app.rs index f97c9f2c4..61245ccdd 100644 --- a/vmm/src/app.rs +++ b/vmm/src/app.rs @@ -17,10 +17,11 @@ use id_pool::IdPool; use ra_rpc::client::RaClient; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeSet, HashMap, VecDeque}; use std::net::IpAddr; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex, MutexGuard}; +use std::time::SystemTime; use supervisor_client::SupervisorClient; use tracing::{error, info}; @@ -411,6 +412,17 @@ impl App { let Some(vm) = state.vms.values_mut().find(|vm| vm.config.cid == cid) else { bail!("VM not found"); }; + vm.state.events.push_back(pb::GuestEvent { + event: event.into(), + body: body.clone(), + timestamp: SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64, + }); + while vm.state.events.len() > self.config.event_buffer_size { + vm.state.events.pop_front(); + } match event { "boot.progress" => { vm.state.boot_progress = body; @@ -648,6 +660,7 @@ struct VmStateMut { boot_error: String, shutdown_progress: String, devices: GpuConfig, + events: VecDeque, } impl VmStateMut { diff --git a/vmm/src/app/qemu.rs b/vmm/src/app/qemu.rs index b69248366..fccd0a6c7 100644 --- a/vmm/src/app/qemu.rs +++ b/vmm/src/app/qemu.rs @@ -51,6 +51,7 @@ pub struct VmInfo { pub shutdown_progress: String, pub image_version: String, pub gateway_enabled: bool, + pub events: Vec, } #[derive(Debug, Builder)] @@ -177,6 +178,7 @@ impl VmInfo { app_id: self.manifest.app_id.clone(), instance_id: self.instance_id.as_deref().map(Into::into), exited_at: self.exited_at.clone(), + events: self.events.clone(), } } } @@ -222,6 +224,7 @@ impl VmState { shutdown_progress: self.state.shutdown_progress.clone(), image_version: self.config.image.info.version.clone(), gateway_enabled: self.config.gateway_enabled, + events: self.state.events.clone().into(), } } } diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 343652263..c4d15395d 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -269,6 +269,9 @@ pub struct Config { #[serde(default)] pub node_name: String, + /// The buffer size in VMM process for guest events + pub event_buffer_size: usize, + /// CVM configuration pub cvm: CvmConfig, /// Gateway configuration diff --git a/vmm/vmm.toml b/vmm/vmm.toml index 568c2b2a4..2d98f5511 100644 --- a/vmm/vmm.toml +++ b/vmm/vmm.toml @@ -11,6 +11,7 @@ log_level = "debug" address = "unix:./vmm.sock" reuse = true kms_url = "http://127.0.0.1:8081" +event_buffer_size = 20 [cvm] qemu_path = ""