Merge pull request #6800 from amshinde/check-vm-capability

kata-ctl: Check for vm capability
This commit is contained in:
Archana Shinde 2023-06-21 23:52:46 -07:00 committed by GitHub
commit 2d329125fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 150 additions and 66 deletions

View File

@ -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> {

View File

@ -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);

View File

@ -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)?;

View File

@ -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,
&param_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,
&param_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")]

View File

@ -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);
}
}

View File

@ -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,
})
}

View File

@ -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>],
}