utils: Add utility functions to get cpu and distro details.

These functions is meant to be used for the kata-env command.

Fixes: #5688

Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
This commit is contained in:
Archana Shinde 2022-12-07 12:21:33 -08:00
parent d33e343613
commit 594b57d082
7 changed files with 199 additions and 151 deletions

View File

@ -533,57 +533,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "hypervisor"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"dbs-utils",
"dragonball",
"go-flag",
"kata-sys-util",
"kata-types",
"libc",
"logging",
"nix 0.24.2",
"persist",
"rand 0.8.5",
"seccompiler",
"serde",
"serde_json",
"shim-interface",
"slog",
"slog-scope",
"thiserror",
"tokio",
"vmm-sys-util 0.11.0",
]
[[package]]
name = "iana-time-zone"
version = "0.1.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
dependencies = [
"cxx",
"cxx-build",
]
[[package]]
name = "idna"
version = "0.3.0"
@ -910,21 +859,6 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "persist"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"kata-sys-util",
"kata-types",
"libc",
"safe-path",
"serde",
"serde_json",
"shim-interface",
]
[[package]]
name = "petgraph"
version = "0.5.1"
@ -1218,71 +1152,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "rlimit"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "347703a5ae47adf1e693144157be231dde38c72bd485925cae7407ad3e52480b"
dependencies = [
"libc",
]
[[package]]
name = "rtnetlink"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46f1cfa18f8cebe685373a2697915d7e0db3b4554918bba118385e0f71f258a7"
dependencies = [
"futures",
"log",
"netlink-packet-route",
"netlink-proto",
"nix 0.24.2",
"thiserror",
"tokio",
]
[[package]]
name = "runtimes"
version = "0.1.0"
dependencies = [
"anyhow",
"common",
"hyper",
"hyperlocal",
"hypervisor",
"kata-types",
"lazy_static",
"logging",
"oci",
"persist",
"shim-interface",
"slog",
"slog-scope",
"tokio",
"virt_container",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustix"
version = "0.34.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2079c267b8394eb529872c3cf92e181c378b41fea36e68130357b52493701d2e"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"winapi",
]
[[package]]
name = "rustls"
version = "0.20.6"
@ -1415,17 +1284,6 @@ dependencies = [
"serde",
]
[[package]]
name = "sha2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
[[package]]
name = "shim-interface"
version = "0.1.0"
@ -1436,15 +1294,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.7"

View File

@ -12,6 +12,8 @@ mod arch_specific {
use std::path::Path;
const KVM_DEV: &str = "/dev/kvm";
pub const ARCH_CPU_VENDOR_FIELD: &str = "CPU implementer";
pub const ARCH_CPU_MODEL_FIELD: &str = "CPU architecture";
// List of check functions
static CHECK_LIST: &[CheckItem] = &[CheckItem {

View File

@ -10,6 +10,9 @@ pub use arch_specific::*;
mod arch_specific {
use anyhow::Result;
pub const ARCH_CPU_VENDOR_FIELD: &str = "";
pub const ARCH_CPU_MODEL_FIELD: &str = "model";
pub fn check() -> Result<()> {
unimplemented!("Check not implemented in powerpc64le");
}

View File

@ -16,6 +16,9 @@ mod arch_specific {
const CPUINFO_FEATURES_TAG: &str = "features";
const CPU_FEATURES_REQ: &[&str] = &["sie"];
pub const ARCH_CPU_VENDOR_FIELD: &str = check::GENERIC_CPU_VENDOR_FIELD;
pub const ARCH_CPU_MODEL_FIELD: &str = "machine";
// check cpu
fn check_cpu() -> Result<()> {
println!("INFO: check CPU: s390x");

View File

@ -15,6 +15,8 @@ mod arch_specific {
const CPUINFO_FLAGS_TAG: &str = "flags";
const CPU_FLAGS_INTEL: &[&str] = &["lm", "sse4_1", "vmx"];
const CPU_ATTRIBS_INTEL: &[&str] = &["GenuineIntel"];
pub const ARCH_CPU_VENDOR_FIELD: &str = check::GENERIC_CPU_VENDOR_FIELD;
pub const ARCH_CPU_MODEL_FIELD: &str = check::GENERIC_CPU_MODEL_FIELD;
// List of check functions
static CHECK_LIST: &[CheckItem] = &[CheckItem {
@ -28,6 +30,7 @@ mod arch_specific {
Some(CHECK_LIST)
}
// check cpu
fn check_cpu(_args: &str) -> Result<()> {
println!("INFO: check CPU: x86_64");

View File

@ -23,6 +23,9 @@ const JSON_TYPE: &str = "application/json";
const USER_AGT: &str = "kata";
pub const GENERIC_CPU_VENDOR_FIELD: &str = "vendor_id";
pub const GENERIC_CPU_MODEL_FIELD: &str = "model name";
pub const PROC_CPUINFO: &str = "/proc/cpuinfo";
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]

View File

@ -5,6 +5,8 @@
#![allow(dead_code)]
use crate::arch::arch_specific;
use crate::check::get_single_cpu_info;
use anyhow::{anyhow, Context, Result};
use std::fs;
@ -38,9 +40,110 @@ pub fn get_kernel_version(proc_version_file: &str) -> Result<String> {
Ok(kernel_version)
}
const OS_RELEASE: &str = "/etc/os-release";
// Clear Linux has a different path (for stateless support)
const OS_RELEASE_CLR: &str = "/usr/lib/os-release";
const UNKNOWN: &str = "unknown";
fn get_field_fn(line: &str, delimiter: &str, file_name: &str) -> Result<String> {
let fields: Vec<&str> = line.split(delimiter).collect();
if fields.len() < 2 {
return Err(anyhow!("Unexpected file contents for {}", file_name));
} else {
let val = fields[1].trim();
Ok(String::from(val))
}
}
// Ref: https://www.freedesktop.org/software/systemd/man/os-release.html
pub fn get_distro_details(os_release: &str, os_release_clr: &str) -> Result<(String, String)> {
let files = vec![os_release, os_release_clr];
let mut name = String::new();
let mut version = String::new();
for release_file in files.iter() {
match fs::read_to_string(release_file) {
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
continue;
} else {
return Err(anyhow!(
"Error reading file {}: {}",
release_file,
e.to_string()
));
}
}
Ok(contents) => {
let lines = contents.lines();
for line in lines {
if line.starts_with("NAME=") && name.is_empty() {
name = get_field_fn(line, "=", release_file)?;
} else if line.starts_with("VERSION_ID=") && version.is_empty() {
version = get_field_fn(line, "=", release_file)?;
}
}
if !name.is_empty() && !version.is_empty() {
return Ok((name, version));
}
}
}
}
if name.is_empty() {
name = String::from(UNKNOWN);
}
if version.is_empty() {
version = String::from(UNKNOWN);
}
Ok((name, version))
}
pub fn get_generic_cpu_details(cpu_info_file: &str) -> Result<(String, String)> {
let cpu_info = get_single_cpu_info(cpu_info_file, "\n\n")?;
let lines = cpu_info.lines();
println!("Single cpu info: {}", cpu_info);
let mut vendor = String::new();
let mut model = String::new();
for line in lines {
if !arch_specific::ARCH_CPU_VENDOR_FIELD.is_empty()
&& line.starts_with(arch_specific::ARCH_CPU_VENDOR_FIELD)
{
vendor = get_field_fn(line, ":", cpu_info_file)?;
}
if !arch_specific::ARCH_CPU_MODEL_FIELD.is_empty()
&& line.starts_with(arch_specific::ARCH_CPU_MODEL_FIELD)
{
model = get_field_fn(line, ":", cpu_info_file)?;
}
}
if vendor.is_empty() && !arch_specific::ARCH_CPU_VENDOR_FIELD.is_empty() {
return Err(anyhow!(
"Cannot find cpu vendor field in file : {}",
cpu_info_file
));
}
if model.is_empty() && !arch_specific::ARCH_CPU_MODEL_FIELD.is_empty() {
return Err(anyhow!(
"Cannot find cpu model field in file : {}",
cpu_info_file
));
}
Ok((vendor, model))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::check::PROC_CPUINFO;
use std::io::Write;
use tempfile::tempdir;
@ -91,4 +194,86 @@ mod tests {
let expected = format!("unexpected contents in file {}", path.to_str().unwrap());
assert_eq!(actual, expected);
}
#[test]
fn test_get_distro_details_empty_files() {
let res = get_distro_details("xvz.xt", "bar.txt");
assert!(res.is_ok());
let (name, version) = res.unwrap();
assert_eq!(name, UNKNOWN);
assert_eq!(version, UNKNOWN);
}
#[test]
fn test_get_distro_details_valid_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("os-version");
let path = file_path.clone();
let mut file = fs::File::create(file_path).unwrap();
writeln!(file, "NAME=Ubuntu\nID_LIKE=debian\nVERSION_ID=20.04.4\n").unwrap();
let res = get_distro_details("/etc/foo.txt", path.to_str().unwrap());
let (name, version) = res.unwrap();
assert_eq!(name, "Ubuntu");
assert_eq!(version, "20.04.4");
}
#[test]
fn test_get_distro_details_system() {
let res = get_distro_details(OS_RELEASE, OS_RELEASE_CLR);
assert!(res.is_ok());
}
#[test]
fn get_generic_cpu_details_system() {
let res = get_generic_cpu_details(PROC_CPUINFO);
assert!(res.is_ok());
}
#[test]
fn get_generic_cpu_details_valid_contents() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("cpuinfo");
let path = file_path.clone();
let mut file = fs::File::create(file_path).unwrap();
let expected_vendor_id = "GenuineIntel";
let expected_model_name = "i7-1065G7 CPU";
let contents = format!(
"{} : {} \n{} : {}\n stepping: 5\n\n",
arch_specific::ARCH_CPU_VENDOR_FIELD,
expected_vendor_id,
arch_specific::ARCH_CPU_MODEL_FIELD,
expected_model_name
);
writeln!(file, "{}", contents).unwrap();
let res = get_generic_cpu_details(path.to_str().unwrap());
assert_eq!(res.as_ref().unwrap().0, expected_vendor_id);
assert_eq!(res.as_ref().unwrap().1, expected_model_name);
}
#[test]
fn get_generic_cpu_details_invalid_file() {
let res = get_generic_cpu_details("/tmp/missing.txt");
assert!(res.is_err());
}
#[test]
fn get_generic_cpu_details_invalid_contents() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("cpuinfo");
let path = file_path.clone();
let mut file = fs::File::create(file_path).unwrap();
writeln!(
file,
"vendor :GenuineIntel\nmodel_name=i7-1065G7 CPU\nstepping:5\n\n"
)
.unwrap();
let actual = get_generic_cpu_details(path.to_str().unwrap())
.unwrap_err()
.to_string();
let expected = format!(
r#"Cannot find cpu vendor field in file : {}"#,
path.to_str().unwrap()
);
assert_eq!(actual, expected);
}
}