diff --git a/src/tools/kata-ctl/Cargo.lock b/src/tools/kata-ctl/Cargo.lock index e31c8622d..333470cc5 100644 --- a/src/tools/kata-ctl/Cargo.lock +++ b/src/tools/kata-ctl/Cargo.lock @@ -742,10 +742,12 @@ dependencies = [ "slog-scope", "strum", "strum_macros", + "sys-info", "tempfile", "test-utils", "thiserror", "tokio", + "toml", "url", "vmm-sys-util", ] @@ -1653,6 +1655,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sys-info" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "take_mut" version = "0.2.2" diff --git a/src/tools/kata-ctl/Cargo.toml b/src/tools/kata-ctl/Cargo.toml index d2fc5fb1e..642c9a8a8 100644 --- a/src/tools/kata-ctl/Cargo.toml +++ b/src/tools/kata-ctl/Cargo.toml @@ -25,6 +25,8 @@ serde = { version = "1.0.149", features = ["derive"] } url = "2.3.1" futures = "0.3.24" base64 = "0.13.0" +toml = "0.5.8" +sys-info = "0.9.1" shim-interface = { path = "../../libs/shim-interface"} kata-types = { path = "../../libs/kata-types" } diff --git a/src/tools/kata-ctl/src/args.rs b/src/tools/kata-ctl/src/args.rs index ff489f82b..84fac00a0 100644 --- a/src/tools/kata-ctl/src/args.rs +++ b/src/tools/kata-ctl/src/args.rs @@ -23,7 +23,7 @@ pub enum Commands { DirectVolume(DirectVolumeCommand), /// Display settings - Env, + Env(EnvArgument), /// Enter into guest VM by debug console Exec(ExecArguments), @@ -69,6 +69,12 @@ pub enum CheckSubCommand { List, } +#[derive(Debug, Args)] +pub struct EnvArgument { + /// Format output as JSON + #[arg(long)] + pub json: bool, +} #[derive(Debug, Args)] pub struct MetricsCommand { #[clap(subcommand)] diff --git a/src/tools/kata-ctl/src/main.rs b/src/tools/kata-ctl/src/main.rs index 980a94771..fe64f10e1 100644 --- a/src/tools/kata-ctl/src/main.rs +++ b/src/tools/kata-ctl/src/main.rs @@ -17,8 +17,9 @@ use std::process::exit; use args::{Commands, KataCtlCli}; use ops::check_ops::{ - handle_check, handle_env, handle_factory, handle_iptables, handle_metrics, handle_version, + handle_check, handle_factory, handle_iptables, handle_metrics, handle_version, }; +use ops::env_ops::handle_env; use ops::exec_ops::handle_exec; use ops::volume_ops::handle_direct_volume; @@ -29,7 +30,7 @@ fn real_main() -> Result<()> { Commands::Check(args) => handle_check(args), Commands::DirectVolume(args) => handle_direct_volume(args), Commands::Exec(args) => handle_exec(args), - Commands::Env => handle_env(), + Commands::Env(args) => handle_env(args), Commands::Factory => handle_factory(), Commands::Iptables(args) => handle_iptables(args), Commands::Metrics(args) => handle_metrics(args), diff --git a/src/tools/kata-ctl/src/ops.rs b/src/tools/kata-ctl/src/ops.rs index d5d4fe162..f90f55cb2 100644 --- a/src/tools/kata-ctl/src/ops.rs +++ b/src/tools/kata-ctl/src/ops.rs @@ -4,6 +4,7 @@ // pub mod check_ops; +pub mod env_ops; pub mod exec_ops; pub mod version; pub mod volume_ops; diff --git a/src/tools/kata-ctl/src/ops/check_ops.rs b/src/tools/kata-ctl/src/ops/check_ops.rs index f2dbea702..298e214c5 100644 --- a/src/tools/kata-ctl/src/ops/check_ops.rs +++ b/src/tools/kata-ctl/src/ops/check_ops.rs @@ -107,10 +107,6 @@ pub fn handle_check(checkcmd: CheckArgument) -> Result<()> { Ok(()) } -pub fn handle_env() -> Result<()> { - Ok(()) -} - pub fn handle_factory() -> Result<()> { Ok(()) } diff --git a/src/tools/kata-ctl/src/ops/env_ops.rs b/src/tools/kata-ctl/src/ops/env_ops.rs new file mode 100644 index 000000000..894cc7020 --- /dev/null +++ b/src/tools/kata-ctl/src/ops/env_ops.rs @@ -0,0 +1,431 @@ +// Copyright (c) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Contains checks that are not architecture-specific + +use crate::arch::arch_specific; +use crate::args::EnvArgument; +use crate::check; +use crate::ops::version; +use crate::utils; +use kata_types::config::TomlConfig; + +use anyhow::{anyhow, Context, Result}; +use serde::{Deserialize, Serialize}; +use std::process::Command; +use sys_info; + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct HostInfo { + #[serde(default)] + available_guest_protection: String, + #[serde(default)] + kernel: String, + #[serde(default)] + architecture: String, + #[serde(default)] + vm_container_capable: bool, + #[serde(default)] + support_vsocks: bool, + #[serde(default)] + distro: DistroInfo, + #[serde(default)] + cpu: CPUInfo, + #[serde(default)] + memory: MemoryInfo, +} + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct DistroInfo { + #[serde(default)] + name: String, + #[serde(default)] + version: String, +} + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct CPUInfo { + #[serde(default)] + vendor: String, + #[serde(default)] + model: String, + #[serde(default)] + cpus: usize, +} + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct MemoryInfo { + #[serde(default)] + total: u64, + #[serde(default)] + available: u64, + #[serde(default)] + free: u64, +} + +// Semantic version for the output of the command. +// +// XXX: Increment for every change to the output format +// (meaning any change to the EnvInfo type). +const FORMAT_VERSION: &str = "0.0.1-kata-ctl"; + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct MetaInfo { + #[serde(default)] + version: String, +} + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct VersionInfo { + #[serde(default)] + semver: String, + #[serde(default)] + commit: String, +} + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct RuntimeConfigInfo { + #[serde(default)] + path: String, +} + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct RuntimeInfo { + #[serde(default)] + path: String, + #[serde(default)] + guest_selinux_label: String, + #[serde(default)] + pub experimental: Vec, + #[serde(default)] + debug: bool, + #[serde(default)] + trace: bool, + #[serde(default)] + disable_guest_seccomp: bool, + #[serde(default)] + disable_new_net_ns: bool, + #[serde(default)] + sandbox_cgroup_only: bool, + #[serde(default)] + static_sandbox_resource_mgmt: bool, + #[serde(default)] + config: RuntimeConfigInfo, + #[serde(default)] + version: VersionInfo, +} + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct AgentInfo { + #[serde(default)] + debug: bool, + #[serde(default)] + trace: bool, +} +// KernelInfo stores kernel details +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct KernelInfo { + #[serde(default)] + path: String, + #[serde(default)] + parameters: String, +} + +// InitrdInfo stores initrd image details +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct InitrdInfo { + #[serde(default)] + path: String, +} + +// ImageInfo stores root filesystem image details +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct ImageInfo { + #[serde(default)] + path: String, +} + +// HypervisorInfo stores hypervisor details +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct HypervisorInfo { + #[serde(default)] + machine_type: String, + #[serde(default)] + machine_accelerators: String, + #[serde(default)] + version: String, + #[serde(default)] + path: String, + #[serde(default)] + block_device_driver: String, + #[serde(default)] + entropy_source: String, + #[serde(default)] + shared_fs: String, + #[serde(default)] + virtio_fs_daemon: String, + #[serde(default)] + msize_9p: u32, + #[serde(default)] + memory_slots: u32, + #[serde(default)] + pcie_root_port: u32, + #[serde(default)] + hotplug_vfio_on_rootbus: bool, + #[serde(default)] + debug: bool, + #[serde(default)] + enable_iommu: bool, + #[serde(default)] + enable_iommu_platform: bool, + #[serde(default)] + default_vcpus: i32, + #[serde(default)] + cpu_features: String, +} + +// EnvInfo collects all information that will be displayed by the +// env command. +// +// XXX: Any changes must be coupled with a change to formatVersion. +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct EnvInfo { + #[serde(default)] + kernel: KernelInfo, + #[serde(default)] + meta: MetaInfo, + #[serde(default)] + image: ImageInfo, + #[serde(default)] + initrd: InitrdInfo, + #[serde(default)] + hypervisor: HypervisorInfo, + #[serde(default)] + runtime: RuntimeInfo, + #[serde(default)] + host: HostInfo, + #[serde(default)] + agent: AgentInfo, +} + +pub fn get_meta_info() -> MetaInfo { + MetaInfo { + version: String::from(FORMAT_VERSION), + } +} + +pub fn get_memory_info() -> Result { + let mem_info = sys_info::mem_info().context("get host memory information")?; + Ok(MemoryInfo { + total: mem_info.total, + available: mem_info.avail, + free: mem_info.free, + }) +} + +fn get_host_info() -> Result { + let host_kernel_version = utils::get_kernel_version(utils::PROC_VERSION_FILE)?; + let (host_distro_name, host_distro_version) = + utils::get_distro_details(utils::OS_RELEASE, utils::OS_RELEASE_CLR)?; + let (cpu_vendor, cpu_model) = utils::get_generic_cpu_details(check::PROC_CPUINFO)?; + + let host_distro = DistroInfo { + name: host_distro_name, + version: host_distro_version, + }; + + let cores: usize = std::thread::available_parallelism() + .context("get available parallelism")? + .into(); + + let host_cpu = CPUInfo { + vendor: cpu_vendor, + model: cpu_model, + cpus: cores, + }; + + let memory_info = get_memory_info()?; + + let guest_protection = + arch_specific::available_guest_protection().map_err(|e| anyhow!(e.to_string()))?; + + let guest_protection = guest_protection.to_string(); + + let support_vsocks = utils::supports_vsocks(utils::VHOST_VSOCK_DEVICE)?; + + Ok(HostInfo { + kernel: host_kernel_version, + architecture: String::from(std::env::consts::ARCH), + distro: host_distro, + cpu: host_cpu, + memory: memory_info, + available_guest_protection: guest_protection, + // TODO: See https://github.com/kata-containers/kata-containers/issues/6727 + vm_container_capable: true, + support_vsocks, + }) +} + +pub fn get_runtime_info(toml_config: &TomlConfig) -> Result { + let version = VersionInfo { + semver: String::from(version::VERSION), + commit: String::from(version::COMMIT), + }; + + let config_path = TomlConfig::get_default_config_file(); + let mut toml_path = String::new(); + if config_path.is_ok() { + let p = config_path?; + let path_str = p.to_str(); + toml_path = match path_str { + Some(s) => String::from(s), + None => String::new(), + }; + } + + Ok(RuntimeInfo { + // TODO: Needs to be implemented: https://github.com/kata-containers/kata-containers/issues/6518 + path: String::from("not implemented yet. See: https://github.com/kata-containers/kata-containers/issues/6518"), + version, + experimental: toml_config.runtime.experimental.clone(), + // TODO: See https://github.com/kata-containers/kata-containers/issues/6667 + guest_selinux_label: String::from("not implemented yet: See https://github.com/kata-containers/kata-containers/issues/6667"), + debug: toml_config.runtime.debug, + trace: toml_config.runtime.enable_tracing, + disable_guest_seccomp: toml_config.runtime.disable_guest_seccomp, + disable_new_net_ns: toml_config.runtime.disable_new_netns, + sandbox_cgroup_only: toml_config.runtime.sandbox_cgroup_only, + static_sandbox_resource_mgmt: toml_config.runtime.static_sandbox_resource_mgmt, + config: RuntimeConfigInfo { path: toml_path }, + } +} + +pub fn get_agent_info(toml_config: &TomlConfig) -> Result { + let agent_config = toml_config + .agent + .get(&toml_config.runtime.agent_name) + .ok_or("could not find agent config in configuration") + .map_err(|e| anyhow!(e))?; + + Ok(AgentInfo { + debug: agent_config.debug, + trace: agent_config.enable_tracing, + }) +} + +pub fn get_command_version(cmd: &str) -> Result { + // Path is empty in case of dragonball hypervisor + if cmd.is_empty() { + return Ok("unknown".to_string()); + } + let output = Command::new(cmd) + .arg("--version") + .output() + .map_err(|e| anyhow!(e))?; + + let version = String::from_utf8(output.stdout).map_err(|e| anyhow!(e))?; + + Ok(version) +} + +pub fn get_hypervisor_info( + toml_config: &TomlConfig, +) -> Result<(HypervisorInfo, ImageInfo, KernelInfo, InitrdInfo)> { + let hypervisor_config = toml_config + .hypervisor + .get(&toml_config.runtime.hypervisor_name) + .ok_or("could not find hypervisor config in configuration") + .map_err(|e| anyhow!(e))?; + + let version = + get_command_version(&hypervisor_config.path).context("error getting hypervisor version")?; + + let hypervisor_info = HypervisorInfo { + machine_type: hypervisor_config.machine_info.machine_type.to_string(), + machine_accelerators: hypervisor_config + .machine_info + .machine_accelerators + .to_string(), + version, + path: hypervisor_config.path.to_string(), + block_device_driver: hypervisor_config + .blockdev_info + .block_device_driver + .to_string(), + entropy_source: hypervisor_config.machine_info.entropy_source.to_string(), + shared_fs: hypervisor_config + .shared_fs + .shared_fs + .clone() + .unwrap_or_else(|| String::from("none")), + virtio_fs_daemon: hypervisor_config.shared_fs.virtio_fs_daemon.to_string(), + msize_9p: hypervisor_config.shared_fs.msize_9p, + memory_slots: hypervisor_config.memory_info.memory_slots, + pcie_root_port: hypervisor_config.device_info.pcie_root_port, + hotplug_vfio_on_rootbus: hypervisor_config.device_info.hotplug_vfio_on_root_bus, + debug: hypervisor_config.debug_info.enable_debug, + enable_iommu: hypervisor_config.device_info.enable_iommu, + enable_iommu_platform: hypervisor_config.device_info.enable_iommu_platform, + default_vcpus: hypervisor_config.cpu_info.default_vcpus, + cpu_features: hypervisor_config.cpu_info.cpu_features.to_string(), + }; + + let image_info = ImageInfo { + path: hypervisor_config.boot_info.image.clone(), + }; + + let kernel_info = KernelInfo { + path: hypervisor_config.boot_info.kernel.to_string(), + parameters: hypervisor_config.boot_info.kernel_params.to_string(), + }; + + let initrd_info = InitrdInfo { + path: hypervisor_config.boot_info.initrd.to_string(), + }; + + Ok((hypervisor_info, image_info, kernel_info, initrd_info)) +} + +pub fn get_env_info(toml_config: &TomlConfig) -> Result { + let metainfo = get_meta_info(); + + let runtime_info = get_runtime_info(toml_config).context("get runtime info")?; + + let agent_info = get_agent_info(toml_config).context("get agent configuration")?; + + let host_info = get_host_info().context("get host information")?; + + let (hypervisor_info, _image_info, kernel_info, initrd_info) = + get_hypervisor_info(toml_config).context("get hypervisor configuration")?; + + let env_info = EnvInfo { + meta: metainfo, + runtime: runtime_info, + kernel: kernel_info, + image: _image_info, + initrd: initrd_info, + hypervisor: hypervisor_info, + host: host_info, + agent: agent_info, + }; + + Ok(env_info) +} + +pub fn handle_env(env_args: EnvArgument) -> Result<()> { + let (toml_config, _) = TomlConfig::load_raw_from_file("").context("load toml config")?; + + let env_info = get_env_info(&toml_config)?; + + if env_args.json { + let serialized_json = serde_json::to_string_pretty(&env_info)?; + println!("{}", serialized_json); + } else { + let toml = toml::to_string(&env_info)?; + println!("{}", toml); + } + + Ok(()) +} diff --git a/src/tools/kata-ctl/src/utils.rs b/src/tools/kata-ctl/src/utils.rs index f05f3bb10..371b5165d 100644 --- a/src/tools/kata-ctl/src/utils.rs +++ b/src/tools/kata-ctl/src/utils.rs @@ -145,7 +145,12 @@ pub fn get_generic_cpu_details(cpu_info_file: &str) -> Result<(String, String)> pub const VHOST_VSOCK_DEVICE: &str = "/dev/vhost-vsock"; pub fn supports_vsocks(vsock_path: &str) -> Result { - let metadata = fs::metadata(vsock_path)?; + let metadata = fs::metadata(vsock_path).map_err(|err| { + anyhow!( + "Host system does not support vhost-vsock (try running (`sudo modprobe vhost_vsock`) : {}", + err.to_string() + ) + })?; Ok(metadata.is_file()) }