diff --git a/src/libs/kata-types/src/config/default.rs b/src/libs/kata-types/src/config/default.rs index 4bf9e6089d..32138a11cc 100644 --- a/src/libs/kata-types/src/config/default.rs +++ b/src/libs/kata-types/src/config/default.rs @@ -18,9 +18,11 @@ lazy_static! { "/usr/share/defaults/kata-containers/configuration.toml", ]; } + pub const DEFAULT_AGENT_NAME: &str = "kata-agent"; pub const DEFAULT_AGENT_VSOCK_PORT: u32 = 1024; pub const DEFAULT_AGENT_LOG_PORT: u32 = 1025; +pub const DEFAULT_AGENT_DBG_CONSOLE_PORT: u32 = 1026; pub const DEFAULT_AGENT_TYPE_NAME: &str = AGENT_NAME_KATA; pub const DEFAULT_RUNTIME_NAME: &str = RUNTIME_NAME_VIRTCONTAINER; diff --git a/src/libs/kata-types/src/config/hypervisor/mod.rs b/src/libs/kata-types/src/config/hypervisor/mod.rs index 0df6693226..d749b73dfe 100644 --- a/src/libs/kata-types/src/config/hypervisor/mod.rs +++ b/src/libs/kata-types/src/config/hypervisor/mod.rs @@ -50,6 +50,8 @@ const VIRTIO_FS: &str = "virtio-fs"; const VIRTIO_FS_INLINE: &str = "inline-virtio-fs"; const MAX_BRIDGE_SIZE: u32 = 5; +const KERNEL_PARAM_DELIMITER: &str = " "; + lazy_static! { static ref HYPERVISOR_PLUGINS: Mutex>> = Mutex::new(HashMap::new()); @@ -237,6 +239,16 @@ impl BootInfo { Ok(()) } + /// Add kernel parameters to bootinfo. It is always added before the original + /// to let the original one takes priority + pub fn add_kernel_params(&mut self, params: Vec) { + let mut p = params; + if !self.kernel_params.is_empty() { + p.push(self.kernel_params.clone()); // [new_params0, new_params1, ..., original_params] + } + self.kernel_params = p.join(KERNEL_PARAM_DELIMITER); + } + /// Validate guest kernel image annotaion pub fn validate_boot_path(&self, path: &str) -> Result<()> { validate_path!(path, "path {} is invalid{}")?; @@ -1067,4 +1079,31 @@ mod tests { assert!(get_hypervisor_plugin("dragonball").is_some()); assert!(get_hypervisor_plugin("dragonball2").is_none()); } + + #[test] + fn test_add_kernel_params() { + let mut boot_info = BootInfo { + ..Default::default() + }; + let params = vec![ + String::from("foo"), + String::from("bar"), + String::from("baz=faz"), + ]; + boot_info.add_kernel_params(params); + + assert_eq!(boot_info.kernel_params, String::from("foo bar baz=faz")); + + let new_params = vec![ + String::from("boo=far"), + String::from("a"), + String::from("b=c"), + ]; + boot_info.add_kernel_params(new_params); + + assert_eq!( + boot_info.kernel_params, + String::from("boo=far a b=c foo bar baz=faz") + ); + } } diff --git a/src/libs/kata-types/src/config/mod.rs b/src/libs/kata-types/src/config/mod.rs index e837e59c69..863dd7590e 100644 --- a/src/libs/kata-types/src/config/mod.rs +++ b/src/libs/kata-types/src/config/mod.rs @@ -23,6 +23,7 @@ mod drop_in; pub mod hypervisor; pub use self::agent::Agent; +use self::default::DEFAULT_AGENT_DBG_CONSOLE_PORT; pub use self::hypervisor::{ BootInfo, DragonballConfig, Hypervisor, QemuConfig, HYPERVISOR_NAME_DRAGONBALL, HYPERVISOR_NAME_QEMU, @@ -33,6 +34,24 @@ pub use self::runtime::{Runtime, RuntimeVendor, RUNTIME_NAME_VIRTCONTAINER}; pub use self::agent::AGENT_NAME_KATA; +// TODO: let agent use the constants here for consistency +/// Debug console enabled flag for agent +pub const DEBUG_CONSOLE_FLAG: &str = "agent.debug_console"; +/// Tracing enabled flag for agent +pub const TRACE_MODE_OPTION: &str = "agent.trace"; +/// Tracing enabled +pub const TRACE_MODE_ENABLE: &str = "true"; +/// Log level setting key for agent, if debugged mode on, set to debug +pub const LOG_LEVEL_OPTION: &str = "agent.log"; +/// logging level: debug +pub const LOG_LEVEL_DEBUG: &str = "debug"; +/// Option of which port will the debug console connect to +pub const DEBUG_CONSOLE_VPORT_OPTION: &str = "agent.debug_console_vport"; +/// Option of which port the agent's log will connect to +pub const LOG_VPORT_OPTION: &str = "agent.log_vport"; +/// Option of setting the container's pipe size +pub const CONTAINER_PIPE_SIZE_OPTION: &str = "agent.container_pipe_size"; + /// Trait to manipulate global Kata configuration information. pub trait ConfigPlugin: Send + Sync { /// Get the plugin name. @@ -151,7 +170,32 @@ impl TomlConfig { Ok(()) } - /// Probe configuration file according to the default configuration file list. + /// Get agent-specfic kernel parameters for further Hypervisor config revision + pub fn get_agent_kernel_params(&self) -> Result> { + let mut kv = HashMap::new(); + if let Some(cfg) = self.agent.get(&self.runtime.agent_name) { + if cfg.debug { + kv.insert(LOG_LEVEL_OPTION.to_string(), LOG_LEVEL_DEBUG.to_string()); + } + if cfg.enable_tracing { + kv.insert(TRACE_MODE_OPTION.to_string(), TRACE_MODE_ENABLE.to_string()); + } + if cfg.container_pipe_size > 0 { + let container_pipe_size = cfg.container_pipe_size.to_string(); + kv.insert(CONTAINER_PIPE_SIZE_OPTION.to_string(), container_pipe_size); + } + if cfg.debug_console_enabled { + kv.insert(DEBUG_CONSOLE_FLAG.to_string(), "".to_string()); + kv.insert( + DEBUG_CONSOLE_VPORT_OPTION.to_string(), + DEFAULT_AGENT_DBG_CONSOLE_PORT.to_string(), + ); + } + } + Ok(kv) + } + + /// Probe configuration file according to the default configuration file list. fn get_default_config_file() -> Result { for f in default::DEFAULT_RUNTIME_CONFIGURATIONS.iter() { if let Ok(path) = fs::canonicalize(f) { @@ -303,4 +347,28 @@ mod tests { let patterns = ["/usr/share".to_string(), "/bin/*".to_string()]; validate_path_pattern(&patterns, "/bin/ls").unwrap(); } + + #[test] + fn test_get_agent_kernel_params() { + let mut config = TomlConfig { + ..Default::default() + }; + let agent_config = Agent { + debug: true, + enable_tracing: true, + container_pipe_size: 20, + debug_console_enabled: true, + ..Default::default() + }; + let agent_name = "test_agent"; + config.runtime.agent_name = agent_name.to_string(); + config.agent.insert(agent_name.to_owned(), agent_config); + + let kv = config.get_agent_kernel_params().unwrap(); + assert_eq!(kv.get("agent.log").unwrap(), "debug"); + assert_eq!(kv.get("agent.trace").unwrap(), "true"); + assert_eq!(kv.get("agent.container_pipe_size").unwrap(), "20"); + kv.get("agent.debug_console").unwrap(); + assert_eq!(kv.get("agent.debug_console_vport").unwrap(), "1026"); // 1026 is the default port + } } diff --git a/src/runtime-rs/crates/hypervisor/src/dragonball/inner.rs b/src/runtime-rs/crates/hypervisor/src/dragonball/inner.rs index 786088633b..851f2b66ae 100644 --- a/src/runtime-rs/crates/hypervisor/src/dragonball/inner.rs +++ b/src/runtime-rs/crates/hypervisor/src/dragonball/inner.rs @@ -91,6 +91,7 @@ impl DragonballInner { kernel_params.append(&mut KernelParams::from_string( &self.config.boot_info.kernel_params, )); + info!(sl!(), "prepared kernel_params={:?}", kernel_params); // set boot source let kernel_path = self.config.boot_info.kernel.clone(); diff --git a/src/runtime-rs/crates/hypervisor/src/kernel_param.rs b/src/runtime-rs/crates/hypervisor/src/kernel_param.rs index d8b20b5972..39bef9a641 100644 --- a/src/runtime-rs/crates/hypervisor/src/kernel_param.rs +++ b/src/runtime-rs/crates/hypervisor/src/kernel_param.rs @@ -7,6 +7,7 @@ use anyhow::{anyhow, Result}; use crate::{VM_ROOTFS_DRIVER_BLK, VM_ROOTFS_DRIVER_PMEM}; +use kata_types::config::LOG_VPORT_OPTION; // Port where the agent will send the logs. Logs are sent through the vsock in cases // where the hypervisor has no console.sock, i.e dragonball @@ -28,6 +29,18 @@ impl Param { value: value.to_owned(), } } + + pub fn to_string(&self) -> Result { + if self.key.is_empty() && self.value.is_empty() { + Err(anyhow!("Empty key and value")) + } else if self.key.is_empty() { + Err(anyhow!("Empty key")) + } else if self.value.is_empty() { + Ok(self.key.to_string()) + } else { + Ok(format!("{}{}{}", self.key, KERNEL_KV_DELIMITER, self.value)) + } + } } #[derive(Debug, PartialEq)] @@ -48,7 +61,7 @@ impl KernelParams { ]; if debug { - params.push(Param::new("agent.log_vport", VSOCK_LOGS_PORT)); + params.push(Param::new(LOG_VPORT_OPTION, VSOCK_LOGS_PORT)); } Self { params } @@ -129,18 +142,7 @@ impl KernelParams { let mut parameters: Vec = Vec::new(); for param in &self.params { - if param.key.is_empty() && param.value.is_empty() { - return Err(anyhow!("Empty key and value")); - } else if param.key.is_empty() { - return Err(anyhow!("Empty key")); - } else if param.value.is_empty() { - parameters.push(param.key.to_string()); - } else { - parameters.push(format!( - "{}{}{}", - param.key, KERNEL_KV_DELIMITER, param.value - )); - } + parameters.push(param.to_string()?); } Ok(parameters.join(KERNEL_PARAM_DELIMITER)) @@ -153,6 +155,20 @@ mod tests { use super::*; + #[test] + fn test_params() { + let param1 = Param::new("", ""); + let param2 = Param::new("", "foo"); + let param3 = Param::new("foo", ""); + + assert!(param1.to_string().is_err()); + assert!(param2.to_string().is_err()); + assert_eq!(param3.to_string().unwrap(), String::from("foo")); + + let param4 = Param::new("foo", "bar"); + assert_eq!(param4.to_string().unwrap(), String::from("foo=bar")); + } + #[test] fn test_kernel_params() -> Result<()> { let expect_params_string = "k1=v1 k2=v2 k3=v3".to_string(); diff --git a/src/runtime-rs/crates/runtimes/Cargo.toml b/src/runtime-rs/crates/runtimes/Cargo.toml index e9306d16be..910c192d1b 100644 --- a/src/runtime-rs/crates/runtimes/Cargo.toml +++ b/src/runtime-rs/crates/runtimes/Cargo.toml @@ -19,6 +19,7 @@ kata-types = { path = "../../../libs/kata-types" } logging = { path = "../../../libs/logging"} oci = { path = "../../../libs/oci" } persist = { path = "../persist" } +hypervisor = { path = "../hypervisor" } # runtime handler linux_container = { path = "./linux_container", optional = true } virt_container = { path = "./virt_container", optional = true } diff --git a/src/runtime-rs/crates/runtimes/src/manager.rs b/src/runtime-rs/crates/runtimes/src/manager.rs index 8bf03470ef..0a2c894a18 100644 --- a/src/runtime-rs/crates/runtimes/src/manager.rs +++ b/src/runtime-rs/crates/runtimes/src/manager.rs @@ -14,6 +14,7 @@ use common::{ types::{Request, Response}, RuntimeHandler, RuntimeInstance, Sandbox, }; +use hypervisor::Param; use kata_types::{annotations::Annotation, config::TomlConfig}; #[cfg(feature = "linux")] use linux_container::LinuxContainer; @@ -335,6 +336,7 @@ fn load_config(spec: &oci::Spec, option: &Option>) -> Result let (mut toml_config, _) = TomlConfig::load_from_file(&config_path).context("load toml config")?; annotation.update_config_by_annotation(&mut toml_config)?; + update_agent_kernel_params(&mut toml_config)?; // validate configuration and return the error toml_config.validate()?; @@ -358,3 +360,20 @@ fn load_config(spec: &oci::Spec, option: &Option>) -> Result info!(sl!(), "get config content {:?}", &toml_config); Ok(toml_config) } + +// this update the agent-specfic kernel parameters into hypervisor's bootinfo +// the agent inside the VM will read from file cmdline to get the params and function +fn update_agent_kernel_params(config: &mut TomlConfig) -> Result<()> { + let mut params = vec![]; + if let Ok(kv) = config.get_agent_kernel_params() { + for (k, v) in kv.into_iter() { + if let Ok(s) = Param::new(k.as_str(), v.as_str()).to_string() { + params.push(s); + } + } + if let Some(h) = config.hypervisor.get_mut(&config.runtime.hypervisor_name) { + h.boot_info.add_kernel_params(params); + } + } + Ok(()) +}