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) Some(CHECK_LIST)
} }
pub fn host_is_vmcontainer_capable() -> Result<bool> {
// TODO: Not implemented
Ok(true)
}
#[allow(dead_code)] #[allow(dead_code)]
// Guest protection is not supported on ARM64. // Guest protection is not supported on ARM64.
pub fn available_guest_protection() -> Result<check::GuestProtection, check::ProtectionError> { 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() // 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> { pub fn available_guest_protection() -> Result<check::GuestProtection, check::ProtectionError> {
if !Uid::effective().is_root() { if !Uid::effective().is_root() {
return Err(check::ProtectionError::NoPerms); return Err(check::ProtectionError::NoPerms);

View File

@ -78,6 +78,21 @@ mod arch_specific {
Some(CHECK_LIST) 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)] #[allow(dead_code)]
fn retrieve_cpu_facilities() -> Result<HashMap<i32, bool>> { fn retrieve_cpu_facilities() -> Result<HashMap<i32, bool>> {
let f = std::fs::File::open(check::PROC_CPUINFO)?; let f = std::fs::File::open(check::PROC_CPUINFO)?;

View File

@ -59,17 +59,29 @@ mod arch_specific {
static MODULE_LIST: &[KernelModule] = &[ static MODULE_LIST: &[KernelModule] = &[
KernelModule { KernelModule {
name: "kvm", name: "kvm",
parameter: KernelParam { params: &[KernelParam {
name: "kvmclock_periodic_sync", name: "kvmclock_periodic_sync",
value: KernelParamType::Simple("Y"), value: KernelParamType::Simple("Y"),
}, }],
}, },
KernelModule { KernelModule {
name: "kvm_intel", name: "kvm_intel",
parameter: KernelParam { params: &[KernelParam {
name: "unrestricted_guest", name: "unrestricted_guest",
value: KernelParamType::Predicate(unrestricted_guest_param_check), 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()?; 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 { 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.", return Ok(());
module,
param_name,
param_value_host
);
return Err(anyhow!(msg));
} }
if param_value_host == expected_param_value.to_string() { 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( fn check_kernel_param(
module: &str, module: &str,
param_name: &str, param_name: &str,
@ -282,19 +322,12 @@ mod arch_specific {
info!(sl!(), "check kernel modules for: x86_64"); info!(sl!(), "check kernel modules for: x86_64");
for module in MODULE_LIST { for module in MODULE_LIST {
let module_loaded = let module_loaded = check::check_kernel_module_loaded(module);
check::check_kernel_module_loaded(module.name, module.parameter.name);
match module_loaded { match module_loaded {
Ok(param_value_host) => { Ok(_) => {
let parameter_check = check_kernel_param( let check = check_kernel_params(module);
module.name, match check {
module.parameter.name,
&param_value_host,
module.parameter.value.clone(),
);
match parameter_check {
Ok(_v) => info!(sl!(), "{} Ok", module.name), Ok(_v) => info!(sl!(), "{} Ok", module.name),
Err(e) => return Err(e), Err(e) => return Err(e),
} }
@ -306,6 +339,23 @@ mod arch_specific {
} }
Ok(()) 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")] #[cfg(target_arch = "x86_64")]

View File

@ -5,6 +5,9 @@
// Contains checks that are not architecture-specific // Contains checks that are not architecture-specific
#[cfg(any(target_arch = "x86_64"))]
use crate::types::KernelModule;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use nix::fcntl::{open, OFlag}; use nix::fcntl::{open, OFlag};
use nix::sys::stat::Mode; use nix::sys::stat::Mode;
@ -324,17 +327,16 @@ pub fn check_official_releases() -> Result<()> {
} }
#[cfg(any(target_arch = "x86_64"))] #[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_DRY_RUN: &str = "--dry-run";
const MODPROBE_PARAMETERS_FIRST_TIME: &str = "--first-time"; const MODPROBE_PARAMETERS_FIRST_TIME: &str = "--first-time";
const MODULES_PATH: &str = "/sys/module";
let status_modinfo_success; let status_modinfo_success;
// Partial check w/ modinfo // Partial check w/ modinfo
// verifies that the module exists // verifies that the module exists
match Command::new(MODINFO_PATH) match Command::new(MODINFO_PATH)
.arg(module) .arg(kernel_module.name)
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.output() .output()
{ {
@ -361,7 +363,7 @@ pub fn check_kernel_module_loaded(module: &str, parameter: &str) -> Result<Strin
match Command::new(MODPROBE_PATH) match Command::new(MODPROBE_PATH)
.arg(MODPROBE_PARAMETERS_DRY_RUN) .arg(MODPROBE_PARAMETERS_DRY_RUN)
.arg(MODPROBE_PARAMETERS_FIRST_TIME) .arg(MODPROBE_PARAMETERS_FIRST_TIME)
.arg(module) .arg(kernel_module.name)
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.output() .output()
{ {
@ -371,8 +373,8 @@ pub fn check_kernel_module_loaded(module: &str, parameter: &str) -> Result<Strin
if status_modprobe_success && status_modinfo_success { if status_modprobe_success && status_modinfo_success {
// This condition is true in the case that the module exist, but is not already loaded // 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'", let msg = format!("The kernel module `{:}` exist but is not already loaded. Try reloading it using 'modprobe {:}'",
module, module kernel_module.name, kernel_module.name
); );
return Err(msg); return Err(msg);
} }
@ -386,27 +388,15 @@ pub fn check_kernel_module_loaded(module: &str, parameter: &str) -> Result<Strin
return Err(msg); return Err(msg);
} }
} }
Ok(())
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)
}
}
} }
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] #[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[cfg(any(target_arch = "x86_64"))]
use crate::types::{KernelModule, KernelParam, KernelParamType};
use semver::Version; use semver::Version;
use slog::warn; use slog::warn;
use std::fs; use std::fs;
@ -612,12 +602,13 @@ mod tests {
#[test] #[test]
fn check_module_loaded() { fn check_module_loaded() {
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)]
struct TestData<'a> { struct TestData<'a> {
module_name: &'a str, module_name: &'a str,
param_name: &'a str, param_name: &'a str,
kernel_module: &'a KernelModule<'a>,
param_value: &'a str, param_value: &'a str,
result: Result<String>, result: Result<()>,
} }
let tests = &[ let tests = &[
@ -625,45 +616,58 @@ mod tests {
TestData { TestData {
module_name: "", module_name: "",
param_name: "", param_name: "",
kernel_module: &KernelModule {
name: "",
params: &[KernelParam {
name: "",
value: KernelParamType::Simple("Y"),
}],
},
param_value: "", param_value: "",
result: Err(anyhow!("modinfo: ERROR: Module {} not found.", "")), 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 // 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 { TestData {
module_name: "kvm", module_name: "kvm",
param_name: "kvmclock_periodic_sync", param_name: "kvmclock_periodic_sync",
kernel_module: &KernelModule {
name: "kvm",
params: &[KernelParam {
name: "kvmclock_periodic_sync",
value: KernelParamType::Simple("Y"),
}],
},
param_value: "Y", param_value: "Y",
result: Ok("Y".to_string()), result: Ok(()),
}, },
]; ];
for (i, d) in tests.iter().enumerate() { for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d); let msg = format!("test[{}]", i);
let result = check_kernel_module_loaded(d.module_name, d.param_name); let result = check_kernel_module_loaded(d.kernel_module);
let msg = format!("{}, result: {:?}", msg, result); let msg = format!("{}, result: {:?}", msg, result);
if d.result.is_ok() { if d.result.is_ok() {
assert_eq!( assert_eq!(result, Ok(()));
result.as_ref().unwrap(),
d.result.as_ref().unwrap(),
"{}",
msg
);
continue; continue;
} }
let expected_error = format!("{}", &d.result.as_ref().unwrap_err()); let expected_error = format!("{}", &d.result.as_ref().unwrap_err());
let actual_error = result.unwrap_err().to_string(); let actual_error = result.unwrap_err().to_string();
println!("testing for {}", d.module_name);
assert!(actual_error == expected_error, "{}", msg); 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 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)?; let support_vsocks = utils::supports_vsocks(utils::VHOST_VSOCK_DEVICE)?;
Ok(HostInfo { Ok(HostInfo {
@ -264,8 +270,7 @@ fn get_host_info() -> Result<HostInfo> {
cpu: host_cpu, cpu: host_cpu,
memory: memory_info, memory: memory_info,
available_guest_protection: guest_protection, available_guest_protection: guest_protection,
// TODO: See https://github.com/kata-containers/kata-containers/issues/6727 vm_container_capable,
vm_container_capable: true,
support_vsocks, support_vsocks,
}) })
} }

View File

@ -69,5 +69,5 @@ pub struct KernelParam<'a> {
#[allow(dead_code)] #[allow(dead_code)]
pub struct KernelModule<'a> { pub struct KernelModule<'a> {
pub name: &'a str, pub name: &'a str,
pub parameter: KernelParam<'a>, pub params: &'a [KernelParam<'a>],
} }