mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-04-28 03:42:09 +00:00
Merge pull request #6800 from amshinde/check-vm-capability
kata-ctl: Check for vm capability
This commit is contained in:
commit
2d329125fd
@ -80,6 +80,11 @@ mod arch_specific {
|
||||
Some(CHECK_LIST)
|
||||
}
|
||||
|
||||
pub fn host_is_vmcontainer_capable() -> Result<bool> {
|
||||
// TODO: Not implemented
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
// Guest protection is not supported on ARM64.
|
||||
pub fn available_guest_protection() -> Result<check::GuestProtection, check::ProtectionError> {
|
||||
|
@ -33,6 +33,11 @@ mod arch_specific {
|
||||
// to the goloang implementation of function getCPUDetails()
|
||||
}
|
||||
|
||||
pub fn host_is_vmcontainer_capable() -> Result<bool> {
|
||||
// TODO: Not implemented
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn available_guest_protection() -> Result<check::GuestProtection, check::ProtectionError> {
|
||||
if !Uid::effective().is_root() {
|
||||
return Err(check::ProtectionError::NoPerms);
|
||||
|
@ -78,6 +78,21 @@ mod arch_specific {
|
||||
Some(CHECK_LIST)
|
||||
}
|
||||
|
||||
pub fn host_is_vmcontainer_capable() -> Result<bool> {
|
||||
let mut count = 0;
|
||||
if check_cpu().is_err() {
|
||||
count += 1;
|
||||
};
|
||||
|
||||
// TODO: Add additional checks for kernel modules
|
||||
|
||||
if count == 0 {
|
||||
return Ok(true);
|
||||
};
|
||||
|
||||
Err(anyhow!("System is not capable of running a VM"))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn retrieve_cpu_facilities() -> Result<HashMap<i32, bool>> {
|
||||
let f = std::fs::File::open(check::PROC_CPUINFO)?;
|
||||
|
@ -59,17 +59,29 @@ mod arch_specific {
|
||||
static MODULE_LIST: &[KernelModule] = &[
|
||||
KernelModule {
|
||||
name: "kvm",
|
||||
parameter: KernelParam {
|
||||
params: &[KernelParam {
|
||||
name: "kvmclock_periodic_sync",
|
||||
value: KernelParamType::Simple("Y"),
|
||||
},
|
||||
}],
|
||||
},
|
||||
KernelModule {
|
||||
name: "kvm_intel",
|
||||
parameter: KernelParam {
|
||||
params: &[KernelParam {
|
||||
name: "unrestricted_guest",
|
||||
value: KernelParamType::Predicate(unrestricted_guest_param_check),
|
||||
},
|
||||
}],
|
||||
},
|
||||
KernelModule {
|
||||
name: "vhost",
|
||||
params: &[],
|
||||
},
|
||||
KernelModule {
|
||||
name: "vhost_net",
|
||||
params: &[],
|
||||
},
|
||||
KernelModule {
|
||||
name: "vhost_vsock",
|
||||
params: &[],
|
||||
},
|
||||
];
|
||||
|
||||
@ -226,13 +238,9 @@ mod arch_specific {
|
||||
|
||||
let running_on_vmm_alt = running_on_vmm()?;
|
||||
|
||||
// Kernel param "unrestricted_guest" is not required when running under a hypervisor
|
||||
if running_on_vmm_alt {
|
||||
let msg = format!("You are running in a VM, where the kernel module '{}' parameter '{:}' has a value '{:}'. This causes conflict when running kata.",
|
||||
module,
|
||||
param_name,
|
||||
param_value_host
|
||||
);
|
||||
return Err(anyhow!(msg));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if param_value_host == expected_param_value.to_string() {
|
||||
@ -253,6 +261,38 @@ mod arch_specific {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_kernel_params(kernel_module: &KernelModule) -> Result<()> {
|
||||
const MODULES_PATH: &str = "/sys/module";
|
||||
|
||||
for param in kernel_module.params {
|
||||
let module_param_path = format!(
|
||||
"{}/{}/parameters/{}",
|
||||
MODULES_PATH, kernel_module.name, param.name
|
||||
);
|
||||
|
||||
// Here the currently loaded kernel parameter value
|
||||
// is retrieved and returned on success
|
||||
let param_value_host = std::fs::read_to_string(module_param_path)
|
||||
.map(|val| val.replace('\n', ""))
|
||||
.map_err(|_err| {
|
||||
anyhow!(
|
||||
"'{:}' kernel module parameter `{:}` not found.",
|
||||
kernel_module.name,
|
||||
param.name
|
||||
)
|
||||
})?;
|
||||
|
||||
check_kernel_param(
|
||||
kernel_module.name,
|
||||
param.name,
|
||||
¶m_value_host,
|
||||
param.value.clone(),
|
||||
)
|
||||
.map_err(|e| anyhow!(e.to_string()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_kernel_param(
|
||||
module: &str,
|
||||
param_name: &str,
|
||||
@ -282,19 +322,12 @@ mod arch_specific {
|
||||
info!(sl!(), "check kernel modules for: x86_64");
|
||||
|
||||
for module in MODULE_LIST {
|
||||
let module_loaded =
|
||||
check::check_kernel_module_loaded(module.name, module.parameter.name);
|
||||
let module_loaded = check::check_kernel_module_loaded(module);
|
||||
|
||||
match module_loaded {
|
||||
Ok(param_value_host) => {
|
||||
let parameter_check = check_kernel_param(
|
||||
module.name,
|
||||
module.parameter.name,
|
||||
¶m_value_host,
|
||||
module.parameter.value.clone(),
|
||||
);
|
||||
|
||||
match parameter_check {
|
||||
Ok(_) => {
|
||||
let check = check_kernel_params(module);
|
||||
match check {
|
||||
Ok(_v) => info!(sl!(), "{} Ok", module.name),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
@ -306,6 +339,23 @@ mod arch_specific {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn host_is_vmcontainer_capable() -> Result<bool> {
|
||||
let mut count = 0;
|
||||
if check_cpu("check_cpu").is_err() {
|
||||
count += 1;
|
||||
};
|
||||
|
||||
if check_kernel_modules("check_modules").is_err() {
|
||||
count += 1;
|
||||
};
|
||||
|
||||
if count == 0 {
|
||||
return Ok(true);
|
||||
};
|
||||
|
||||
Err(anyhow!("System is not capable of running a VM"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
@ -5,6 +5,9 @@
|
||||
|
||||
// Contains checks that are not architecture-specific
|
||||
|
||||
#[cfg(any(target_arch = "x86_64"))]
|
||||
use crate::types::KernelModule;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use nix::fcntl::{open, OFlag};
|
||||
use nix::sys::stat::Mode;
|
||||
@ -324,17 +327,16 @@ pub fn check_official_releases() -> Result<()> {
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86_64"))]
|
||||
pub fn check_kernel_module_loaded(module: &str, parameter: &str) -> Result<String, String> {
|
||||
pub fn check_kernel_module_loaded(kernel_module: &KernelModule) -> Result<(), String> {
|
||||
const MODPROBE_PARAMETERS_DRY_RUN: &str = "--dry-run";
|
||||
const MODPROBE_PARAMETERS_FIRST_TIME: &str = "--first-time";
|
||||
const MODULES_PATH: &str = "/sys/module";
|
||||
|
||||
let status_modinfo_success;
|
||||
|
||||
// Partial check w/ modinfo
|
||||
// verifies that the module exists
|
||||
match Command::new(MODINFO_PATH)
|
||||
.arg(module)
|
||||
.arg(kernel_module.name)
|
||||
.stdout(Stdio::piped())
|
||||
.output()
|
||||
{
|
||||
@ -361,7 +363,7 @@ pub fn check_kernel_module_loaded(module: &str, parameter: &str) -> Result<Strin
|
||||
match Command::new(MODPROBE_PATH)
|
||||
.arg(MODPROBE_PARAMETERS_DRY_RUN)
|
||||
.arg(MODPROBE_PARAMETERS_FIRST_TIME)
|
||||
.arg(module)
|
||||
.arg(kernel_module.name)
|
||||
.stdout(Stdio::piped())
|
||||
.output()
|
||||
{
|
||||
@ -371,8 +373,8 @@ pub fn check_kernel_module_loaded(module: &str, parameter: &str) -> Result<Strin
|
||||
|
||||
if status_modprobe_success && status_modinfo_success {
|
||||
// This condition is true in the case that the module exist, but is not already loaded
|
||||
let msg = format!("The kernel module `{:}` exist but is not already loaded. Try reloading it using 'modprobe {:}=Y'",
|
||||
module, module
|
||||
let msg = format!("The kernel module `{:}` exist but is not already loaded. Try reloading it using 'modprobe {:}'",
|
||||
kernel_module.name, kernel_module.name
|
||||
);
|
||||
return Err(msg);
|
||||
}
|
||||
@ -386,27 +388,15 @@ pub fn check_kernel_module_loaded(module: &str, parameter: &str) -> Result<Strin
|
||||
return Err(msg);
|
||||
}
|
||||
}
|
||||
|
||||
let module_path = format!("{}/{}/parameters/{}", MODULES_PATH, module, parameter);
|
||||
|
||||
// Here the currently loaded kernel parameter value
|
||||
// is retrieved and returned on success
|
||||
match read_file_contents(&module_path) {
|
||||
Ok(result) => Ok(result.replace('\n', "")),
|
||||
Err(_e) => {
|
||||
let msg = format!(
|
||||
"'{:}' kernel module parameter `{:}` not found.",
|
||||
module, parameter
|
||||
);
|
||||
Err(msg)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[cfg(any(target_arch = "x86_64"))]
|
||||
use crate::types::{KernelModule, KernelParam, KernelParamType};
|
||||
use semver::Version;
|
||||
use slog::warn;
|
||||
use std::fs;
|
||||
@ -612,12 +602,13 @@ mod tests {
|
||||
#[test]
|
||||
fn check_module_loaded() {
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
|
||||
struct TestData<'a> {
|
||||
module_name: &'a str,
|
||||
param_name: &'a str,
|
||||
kernel_module: &'a KernelModule<'a>,
|
||||
param_value: &'a str,
|
||||
result: Result<String>,
|
||||
result: Result<()>,
|
||||
}
|
||||
|
||||
let tests = &[
|
||||
@ -625,45 +616,58 @@ mod tests {
|
||||
TestData {
|
||||
module_name: "",
|
||||
param_name: "",
|
||||
kernel_module: &KernelModule {
|
||||
name: "",
|
||||
params: &[KernelParam {
|
||||
name: "",
|
||||
value: KernelParamType::Simple("Y"),
|
||||
}],
|
||||
},
|
||||
param_value: "",
|
||||
result: Err(anyhow!("modinfo: ERROR: Module {} not found.", "")),
|
||||
},
|
||||
TestData {
|
||||
module_name: "kvm",
|
||||
param_name: "",
|
||||
param_value: "",
|
||||
result: Err(anyhow!(
|
||||
"'{:}' kernel module parameter `{:}` not found.",
|
||||
"kvm",
|
||||
""
|
||||
)),
|
||||
},
|
||||
// Success scenarios
|
||||
TestData {
|
||||
module_name: "kvm",
|
||||
param_name: "",
|
||||
kernel_module: &KernelModule {
|
||||
name: "kvm",
|
||||
params: &[KernelParam {
|
||||
name: "nonexistantparam",
|
||||
value: KernelParamType::Simple("Y"),
|
||||
}],
|
||||
},
|
||||
param_value: "",
|
||||
result: Ok(()),
|
||||
},
|
||||
TestData {
|
||||
module_name: "kvm",
|
||||
param_name: "kvmclock_periodic_sync",
|
||||
kernel_module: &KernelModule {
|
||||
name: "kvm",
|
||||
params: &[KernelParam {
|
||||
name: "kvmclock_periodic_sync",
|
||||
value: KernelParamType::Simple("Y"),
|
||||
}],
|
||||
},
|
||||
param_value: "Y",
|
||||
result: Ok("Y".to_string()),
|
||||
result: Ok(()),
|
||||
},
|
||||
];
|
||||
|
||||
for (i, d) in tests.iter().enumerate() {
|
||||
let msg = format!("test[{}]: {:?}", i, d);
|
||||
let result = check_kernel_module_loaded(d.module_name, d.param_name);
|
||||
let msg = format!("test[{}]", i);
|
||||
let result = check_kernel_module_loaded(d.kernel_module);
|
||||
let msg = format!("{}, result: {:?}", msg, result);
|
||||
|
||||
if d.result.is_ok() {
|
||||
assert_eq!(
|
||||
result.as_ref().unwrap(),
|
||||
d.result.as_ref().unwrap(),
|
||||
"{}",
|
||||
msg
|
||||
);
|
||||
assert_eq!(result, Ok(()));
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected_error = format!("{}", &d.result.as_ref().unwrap_err());
|
||||
let actual_error = result.unwrap_err().to_string();
|
||||
println!("testing for {}", d.module_name);
|
||||
assert!(actual_error == expected_error, "{}", msg);
|
||||
}
|
||||
}
|
||||
|
@ -255,6 +255,12 @@ fn get_host_info() -> Result<HostInfo> {
|
||||
|
||||
let guest_protection = guest_protection.to_string();
|
||||
|
||||
let mut vm_container_capable = true;
|
||||
|
||||
if arch_specific::host_is_vmcontainer_capable().is_err() {
|
||||
vm_container_capable = false;
|
||||
}
|
||||
|
||||
let support_vsocks = utils::supports_vsocks(utils::VHOST_VSOCK_DEVICE)?;
|
||||
|
||||
Ok(HostInfo {
|
||||
@ -264,8 +270,7 @@ fn get_host_info() -> Result<HostInfo> {
|
||||
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,
|
||||
vm_container_capable,
|
||||
support_vsocks,
|
||||
})
|
||||
}
|
||||
|
@ -69,5 +69,5 @@ pub struct KernelParam<'a> {
|
||||
#[allow(dead_code)]
|
||||
pub struct KernelModule<'a> {
|
||||
pub name: &'a str,
|
||||
pub parameter: KernelParam<'a>,
|
||||
pub params: &'a [KernelParam<'a>],
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user