CCv0: Merge main into CCv0 branch

Merge remote-tracking branch 'upstream/main' into CCv0

Fixes: #5457
Signed-off-by: Megan Wright <megan.wright@ibm.com>
This commit is contained in:
Megan Wright 2022-10-19 10:03:41 +01:00
commit a06c6dd861
56 changed files with 1828 additions and 110 deletions

View File

@ -94,3 +94,26 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'force-skip-ci') }} if: ${{ !contains(github.event.pull_request.labels.*.name, 'force-skip-ci') }}
run: | run: |
cd ${GOPATH}/src/github.com/${{ github.repository }} && sudo -E PATH="$PATH" make test cd ${GOPATH}/src/github.com/${{ github.repository }} && sudo -E PATH="$PATH" make test
test-dragonball:
runs-on: self-hosted
env:
RUST_BACKTRACE: "1"
steps:
- uses: actions/checkout@v3
- name: Set env
if: ${{ !contains(github.event.pull_request.labels.*.name, 'force-skip-ci') }}
run: |
echo "GOPATH=${{ github.workspace }}" >> $GITHUB_ENV
- name: Install Rust
if: ${{ !contains(github.event.pull_request.labels.*.name, 'force-skip-ci') }}
run: |
./ci/install_rust.sh
PATH=$PATH:"$HOME/.cargo/bin"
- name: Run Unit Test
if: ${{ !contains(github.event.pull_request.labels.*.name, 'force-skip-ci') }}
run: |
cd src/dragonball
/root/.cargo/bin/cargo version
rustc --version
sudo -E env PATH=$PATH LIBC=gnu SUPPORT_VIRTUALIZATION=true make test

View File

@ -8,6 +8,7 @@ COMPONENTS =
COMPONENTS += libs COMPONENTS += libs
COMPONENTS += agent COMPONENTS += agent
COMPONENTS += dragonball
COMPONENTS += runtime COMPONENTS += runtime
COMPONENTS += runtime-rs COMPONENTS += runtime-rs
@ -15,9 +16,10 @@ COMPONENTS += runtime-rs
TOOLS = TOOLS =
TOOLS += agent-ctl TOOLS += agent-ctl
TOOLS += trace-forwarder TOOLS += kata-ctl
TOOLS += runk
TOOLS += log-parser TOOLS += log-parser
TOOLS += runk
TOOLS += trace-forwarder
STANDARD_TARGETS = build check clean install test vendor STANDARD_TARGETS = build check clean install test vendor

View File

@ -119,10 +119,8 @@ The table below lists the core parts of the project:
| [runtime](src/runtime) | core | Main component run by a container manager and providing a containerd shimv2 runtime implementation. | | [runtime](src/runtime) | core | Main component run by a container manager and providing a containerd shimv2 runtime implementation. |
| [runtime-rs](src/runtime-rs) | core | The Rust version runtime. | | [runtime-rs](src/runtime-rs) | core | The Rust version runtime. |
| [agent](src/agent) | core | Management process running inside the virtual machine / POD that sets up the container environment. | | [agent](src/agent) | core | Management process running inside the virtual machine / POD that sets up the container environment. |
| [libraries](src/libs) | core | Library crates shared by multiple Kata Container components or published to [`crates.io`](https://crates.io/index.html) |
| [`dragonball`](src/dragonball) | core | An optional built-in VMM brings out-of-the-box Kata Containers experience with optimizations on container workloads | | [`dragonball`](src/dragonball) | core | An optional built-in VMM brings out-of-the-box Kata Containers experience with optimizations on container workloads |
| [documentation](docs) | documentation | Documentation common to all components (such as design and install documentation). | | [documentation](docs) | documentation | Documentation common to all components (such as design and install documentation). |
| [libraries](src/libs) | core | Library crates shared by multiple Kata Container components or published to [`crates.io`](https://crates.io/index.html) |
| [tests](https://github.com/kata-containers/tests) | tests | Excludes unit tests which live with the main code. | | [tests](https://github.com/kata-containers/tests) | tests | Excludes unit tests which live with the main code. |
### Additional components ### Additional components
@ -135,6 +133,7 @@ The table below lists the remaining parts of the project:
| [kernel](https://www.kernel.org) | kernel | Linux kernel used by the hypervisor to boot the guest image. Patches are stored [here](tools/packaging/kernel). | | [kernel](https://www.kernel.org) | kernel | Linux kernel used by the hypervisor to boot the guest image. Patches are stored [here](tools/packaging/kernel). |
| [osbuilder](tools/osbuilder) | infrastructure | Tool to create "mini O/S" rootfs and initrd images and kernel for the hypervisor. | | [osbuilder](tools/osbuilder) | infrastructure | Tool to create "mini O/S" rootfs and initrd images and kernel for the hypervisor. |
| [`agent-ctl`](src/tools/agent-ctl) | utility | Tool that provides low-level access for testing the agent. | | [`agent-ctl`](src/tools/agent-ctl) | utility | Tool that provides low-level access for testing the agent. |
| [`kata-ctl`](src/tools/kata-ctl) | utility | Tool that provides advanced commands and debug facilities. |
| [`trace-forwarder`](src/tools/trace-forwarder) | utility | Agent tracing helper. | | [`trace-forwarder`](src/tools/trace-forwarder) | utility | Agent tracing helper. |
| [`runk`](src/tools/runk) | utility | Standard OCI container runtime based on the agent. | | [`runk`](src/tools/runk) | utility | Standard OCI container runtime based on the agent. |
| [`ci`](https://github.com/kata-containers/ci) | CI | Continuous Integration configuration files and scripts. | | [`ci`](https://github.com/kata-containers/ci) | CI | Continuous Integration configuration files and scripts. |

View File

@ -50,7 +50,7 @@ $ qemu_commit="$(get_from_kata_deps "assets.hypervisor.qemu.snp.commit")"
$ git clone -b "${qemu_branch}" "${qemu_url}" $ git clone -b "${qemu_branch}" "${qemu_url}"
$ pushd qemu $ pushd qemu
$ git checkout "${qemu_commit}" $ git checkout "${qemu_commit}"
$ ./configure --target-list=x86_64-softmmu --enable-debug $ ./configure --enable-virtfs --target-list=x86_64-softmmu --enable-debug
$ make -j "$(nproc)" $ make -j "$(nproc)"
$ popd $ popd
``` ```

View File

@ -83,7 +83,7 @@ $ git clone https://github.com/kata-containers/kata-containers.git
$ cd kata-containers/src/runtime-rs $ cd kata-containers/src/runtime-rs
$ make && sudo make install $ make && sudo make install
``` ```
After running the command above, the default config file `configuration.toml` will be installed under `/usr/share/defaults/kata-containers/`, the binary file `containerd-shim-kata-v2` will be installed under `/user/local/bin` . After running the command above, the default config file `configuration.toml` will be installed under `/usr/share/defaults/kata-containers/`, the binary file `containerd-shim-kata-v2` will be installed under `/usr/local/bin/` .
### Build Kata Containers Kernel ### Build Kata Containers Kernel
Follow the [Kernel installation guide](/tools/packaging/kernel/README.md). Follow the [Kernel installation guide](/tools/packaging/kernel/README.md).

View File

@ -43,6 +43,7 @@ vm-memory = { version = "0.9.0", features = ["backend-mmap"] }
[dev-dependencies] [dev-dependencies]
slog-term = "2.9.0" slog-term = "2.9.0"
slog-async = "2.7.0" slog-async = "2.7.0"
test-utils = { path = "../libs/test-utils" }
[features] [features]
acpi = [] acpi = []

View File

@ -2,10 +2,19 @@
# Copyright (c) 2019-2022 Ant Group. All rights reserved. # Copyright (c) 2019-2022 Ant Group. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
include ../../utils.mk
ifeq ($(ARCH), s390x)
default build check test clippy:
@echo "s390x not support currently"
exit 0
else
default: build default: build
build: build:
cargo build --all-features @echo "INFO: cargo build..."
cargo build --all-features --target $(TRIPLE)
check: clippy format check: clippy format
@ -15,6 +24,9 @@ clippy:
-- \ -- \
-D warnings -D warnings
vendor:
@echo "INFO: vendor do nothing.."
format: format:
@echo "INFO: cargo fmt..." @echo "INFO: cargo fmt..."
cargo fmt -- --check cargo fmt -- --check
@ -23,5 +35,13 @@ clean:
cargo clean cargo clean
test: test:
@echo "INFO: testing dragonball for development build" ifdef SUPPORT_VIRTUALIZATION
cargo test --all-features -- --nocapture cargo test --all-features --target $(TRIPLE) -- --nocapture
else
@echo "INFO: skip testing dragonball, it need virtualization support."
exit 0
endif
endif # ifeq ($(ARCH), s390x)
.DEFAULT_GOAL := default

View File

@ -35,8 +35,8 @@ use nix::unistd::dup;
#[cfg(feature = "atomic-guest-memory")] #[cfg(feature = "atomic-guest-memory")]
use vm_memory::GuestMemoryAtomic; use vm_memory::GuestMemoryAtomic;
use vm_memory::{ use vm_memory::{
address::Address, FileOffset, GuestAddress, GuestAddressSpace, GuestMemoryMmap, GuestMemoryRegion, address::Address, FileOffset, GuestAddress, GuestAddressSpace, GuestMemoryMmap,
GuestRegionMmap, GuestUsize, MemoryRegionAddress, MmapRegion, GuestMemoryRegion, GuestRegionMmap, GuestUsize, MemoryRegionAddress, MmapRegion,
}; };
use crate::resource_manager::ResourceManager; use crate::resource_manager::ResourceManager;
@ -270,7 +270,7 @@ impl AddressSpaceMgr {
let size = info let size = info
.size .size
.checked_shl(20) .checked_shl(20)
.ok_or_else(|| AddressManagerError::InvalidOperation)?; .ok_or(AddressManagerError::InvalidOperation)?;
// Guest memory does not intersect with the MMIO hole. // Guest memory does not intersect with the MMIO hole.
// TODO: make it work for ARM (issue #4307) // TODO: make it work for ARM (issue #4307)
@ -281,13 +281,13 @@ impl AddressSpaceMgr {
regions.push(region); regions.push(region);
start_addr = start_addr start_addr = start_addr
.checked_add(size) .checked_add(size)
.ok_or_else(|| AddressManagerError::InvalidOperation)?; .ok_or(AddressManagerError::InvalidOperation)?;
} else { } else {
// Add guest memory below the MMIO hole, avoid splitting the memory region // Add guest memory below the MMIO hole, avoid splitting the memory region
// if the available address region is small than MINIMAL_SPLIT_SPACE MiB. // if the available address region is small than MINIMAL_SPLIT_SPACE MiB.
let mut below_size = dbs_boot::layout::MMIO_LOW_START let mut below_size = dbs_boot::layout::MMIO_LOW_START
.checked_sub(start_addr) .checked_sub(start_addr)
.ok_or_else(|| AddressManagerError::InvalidOperation)?; .ok_or(AddressManagerError::InvalidOperation)?;
if below_size < (MINIMAL_SPLIT_SPACE) { if below_size < (MINIMAL_SPLIT_SPACE) {
below_size = 0; below_size = 0;
} else { } else {
@ -299,12 +299,12 @@ impl AddressSpaceMgr {
let above_start = dbs_boot::layout::MMIO_LOW_END + 1; let above_start = dbs_boot::layout::MMIO_LOW_END + 1;
let above_size = size let above_size = size
.checked_sub(below_size) .checked_sub(below_size)
.ok_or_else(|| AddressManagerError::InvalidOperation)?; .ok_or(AddressManagerError::InvalidOperation)?;
let region = self.create_region(above_start, above_size, info, &mut param)?; let region = self.create_region(above_start, above_size, info, &mut param)?;
regions.push(region); regions.push(region);
start_addr = above_start start_addr = above_start
.checked_add(above_size) .checked_add(above_size)
.ok_or_else(|| AddressManagerError::InvalidOperation)?; .ok_or(AddressManagerError::InvalidOperation)?;
} }
} }
@ -502,7 +502,7 @@ impl AddressSpaceMgr {
fn configure_numa(&self, mmap_reg: &MmapRegion, node_id: u32) -> Result<()> { fn configure_numa(&self, mmap_reg: &MmapRegion, node_id: u32) -> Result<()> {
let nodemask = 1_u64 let nodemask = 1_u64
.checked_shl(node_id) .checked_shl(node_id)
.ok_or_else(|| AddressManagerError::InvalidOperation)?; .ok_or(AddressManagerError::InvalidOperation)?;
let res = unsafe { let res = unsafe {
libc::syscall( libc::syscall(
libc::SYS_mbind, libc::SYS_mbind,

View File

@ -18,7 +18,7 @@ pub const DEFAULT_KERNEL_CMDLINE: &str = "reboot=k panic=1 pci=off nomodules 825
i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd"; i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd";
/// Strongly typed data structure used to configure the boot source of the microvm. /// Strongly typed data structure used to configure the boot source of the microvm.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, Default)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Default)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct BootSourceConfig { pub struct BootSourceConfig {
/// Path of the kernel image. /// Path of the kernel image.

View File

@ -10,7 +10,7 @@ use serde_derive::{Deserialize, Serialize};
/// When Dragonball starts, the instance state is Uninitialized. Once start_microvm method is /// When Dragonball starts, the instance state is Uninitialized. Once start_microvm method is
/// called, the state goes from Uninitialized to Starting. The state is changed to Running until /// called, the state goes from Uninitialized to Starting. The state is changed to Running until
/// the start_microvm method ends. Halting and Halted are currently unsupported. /// the start_microvm method ends. Halting and Halted are currently unsupported.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub enum InstanceState { pub enum InstanceState {
/// Microvm is not initialized. /// Microvm is not initialized.
Uninitialized, Uninitialized,
@ -29,7 +29,7 @@ pub enum InstanceState {
} }
/// The state of async actions /// The state of async actions
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub enum AsyncState { pub enum AsyncState {
/// Uninitialized /// Uninitialized
Uninitialized, Uninitialized,

View File

@ -10,7 +10,7 @@ pub const MAX_SUPPORTED_VCPUS: u8 = 254;
pub const MEMORY_HOTPLUG_ALIGHMENT: u8 = 64; pub const MEMORY_HOTPLUG_ALIGHMENT: u8 = 64;
/// Errors associated with configuring the microVM. /// Errors associated with configuring the microVM.
#[derive(Debug, PartialEq, thiserror::Error)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum VmConfigError { pub enum VmConfigError {
/// Cannot update the configuration of the microvm post boot. /// Cannot update the configuration of the microvm post boot.
#[error("update operation is not allowed after boot")] #[error("update operation is not allowed after boot")]

View File

@ -83,13 +83,13 @@ pub enum VmmActionError {
#[cfg(feature = "virtio-fs")] #[cfg(feature = "virtio-fs")]
/// The action `InsertFsDevice` failed either because of bad user input or an internal error. /// The action `InsertFsDevice` failed either because of bad user input or an internal error.
#[error("virtio-fs device: {0}")] #[error("virtio-fs device error: {0}")]
FsDevice(#[source] FsDeviceError), FsDevice(#[source] FsDeviceError),
} }
/// This enum represents the public interface of the VMM. Each action contains various /// This enum represents the public interface of the VMM. Each action contains various
/// bits of information (ids, paths, etc.). /// bits of information (ids, paths, etc.).
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum VmmAction { pub enum VmmAction {
/// Configure the boot source of the microVM using `BootSourceConfig`. /// Configure the boot source of the microVM using `BootSourceConfig`.
/// This action can only be called before the microVM has booted. /// This action can only be called before the microVM has booted.
@ -298,7 +298,6 @@ impl VmmService {
let mut cmdline = linux_loader::cmdline::Cmdline::new(dbs_boot::layout::CMDLINE_MAX_SIZE); let mut cmdline = linux_loader::cmdline::Cmdline::new(dbs_boot::layout::CMDLINE_MAX_SIZE);
let boot_args = boot_source_config let boot_args = boot_source_config
.boot_args .boot_args
.clone()
.unwrap_or_else(|| String::from(DEFAULT_KERNEL_CMDLINE)); .unwrap_or_else(|| String::from(DEFAULT_KERNEL_CMDLINE));
cmdline cmdline
.insert_str(boot_args) .insert_str(boot_args)
@ -634,3 +633,783 @@ fn handle_cpu_topology(
Ok(cpu_topology) Ok(cpu_topology)
} }
#[cfg(test)]
mod tests {
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use dbs_utils::epoll_manager::EpollManager;
use test_utils::skip_if_not_root;
use vmm_sys_util::tempfile::TempFile;
use super::*;
use crate::vmm::tests::create_vmm_instance;
struct TestData<'a> {
req: Option<VmmAction>,
vm_state: InstanceState,
f: &'a dyn Fn(VmmRequestResult),
}
impl<'a> TestData<'a> {
fn new(req: VmmAction, vm_state: InstanceState, f: &'a dyn Fn(VmmRequestResult)) -> Self {
Self {
req: Some(req),
vm_state,
f,
}
}
fn check_request(&mut self) {
let (to_vmm, from_api) = channel();
let (to_api, from_vmm) = channel();
let vmm = Arc::new(Mutex::new(create_vmm_instance()));
let mut vservice = VmmService::new(from_api, to_api);
let epoll_mgr = EpollManager::default();
let mut event_mgr = EventManager::new(&vmm, epoll_mgr).unwrap();
let mut v = vmm.lock().unwrap();
let vm = v.get_vm_mut().unwrap();
vm.set_instance_state(self.vm_state);
to_vmm.send(Box::new(self.req.take().unwrap())).unwrap();
assert!(vservice.run_vmm_action(&mut v, &mut event_mgr).is_ok());
let response = from_vmm.try_recv();
assert!(response.is_ok());
(self.f)(*response.unwrap());
}
}
#[test]
fn test_vmm_action_receive_unknown() {
skip_if_not_root!();
let (_to_vmm, from_api) = channel();
let (to_api, _from_vmm) = channel();
let vmm = Arc::new(Mutex::new(create_vmm_instance()));
let mut vservice = VmmService::new(from_api, to_api);
let epoll_mgr = EpollManager::default();
let mut event_mgr = EventManager::new(&vmm, epoll_mgr).unwrap();
let mut v = vmm.lock().unwrap();
assert!(vservice.run_vmm_action(&mut v, &mut event_mgr).is_ok());
}
#[should_panic]
#[test]
fn test_vmm_action_disconnected() {
let (to_vmm, from_api) = channel();
let (to_api, _from_vmm) = channel();
let vmm = Arc::new(Mutex::new(create_vmm_instance()));
let mut vservice = VmmService::new(from_api, to_api);
let epoll_mgr = EpollManager::default();
let mut event_mgr = EventManager::new(&vmm, epoll_mgr).unwrap();
let mut v = vmm.lock().unwrap();
drop(to_vmm);
vservice.run_vmm_action(&mut v, &mut event_mgr).unwrap();
}
#[test]
fn test_vmm_action_config_boot_source() {
skip_if_not_root!();
let kernel_file = TempFile::new().unwrap();
let tests = &mut [
// invalid state
TestData::new(
VmmAction::ConfigureBootSource(BootSourceConfig::default()),
InstanceState::Running,
&|result| {
if let Err(VmmActionError::BootSource(
BootSourceConfigError::UpdateNotAllowedPostBoot,
)) = result
{
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to configure boot source for VM: \
the update operation is not allowed after boot",
);
assert_eq!(err_string, expected_err);
} else {
panic!();
}
},
),
// invalid kernel file path
TestData::new(
VmmAction::ConfigureBootSource(BootSourceConfig::default()),
InstanceState::Uninitialized,
&|result| {
if let Err(VmmActionError::BootSource(
BootSourceConfigError::InvalidKernelPath(_),
)) = result
{
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to configure boot source for VM: \
the kernel file cannot be opened due to invalid kernel path or invalid permissions: \
No such file or directory (os error 2)");
assert_eq!(err_string, expected_err);
} else {
panic!();
}
},
),
//success
TestData::new(
VmmAction::ConfigureBootSource(BootSourceConfig {
kernel_path: kernel_file.as_path().to_str().unwrap().to_string(),
..Default::default()
}),
InstanceState::Uninitialized,
&|result| {
assert!(result.is_ok());
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[test]
fn test_vmm_action_set_vm_configuration() {
skip_if_not_root!();
let tests = &mut [
// invalid state
TestData::new(
VmmAction::SetVmConfiguration(VmConfigInfo::default()),
InstanceState::Running,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::MachineConfig(
VmConfigError::UpdateNotAllowedPostBoot
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to set configuration for the VM: \
update operation is not allowed after boot",
);
assert_eq!(err_string, expected_err);
},
),
// invalid cpu count (0)
TestData::new(
VmmAction::SetVmConfiguration(VmConfigInfo {
vcpu_count: 0,
..Default::default()
}),
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::MachineConfig(
VmConfigError::InvalidVcpuCount(0)
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to set configuration for the VM: \
the vCPU number '0' can only be 1 or an even number when hyperthreading is enabled");
assert_eq!(err_string, expected_err);
},
),
// invalid max cpu count (too small)
TestData::new(
VmmAction::SetVmConfiguration(VmConfigInfo {
vcpu_count: 4,
max_vcpu_count: 2,
..Default::default()
}),
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::MachineConfig(
VmConfigError::InvalidMaxVcpuCount(2)
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to set configuration for the VM: \
the max vCPU number '2' shouldn't less than vCPU count and can only be 1 or an even number when hyperthreading is enabled");
assert_eq!(err_string, expected_err);
},
),
// invalid cpu topology (larger than 254)
TestData::new(
VmmAction::SetVmConfiguration(VmConfigInfo {
vcpu_count: 254,
cpu_topology: CpuTopology {
threads_per_core: 2,
cores_per_die: 128,
dies_per_socket: 1,
sockets: 1,
},
..Default::default()
}),
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::MachineConfig(
VmConfigError::VcpuCountExceedsMaximum
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to set configuration for the VM: \
the vCPU number shouldn't large than 254",
);
assert_eq!(err_string, expected_err)
},
),
// cpu topology and max_vcpu_count are not matched - success
TestData::new(
VmmAction::SetVmConfiguration(VmConfigInfo {
vcpu_count: 16,
max_vcpu_count: 32,
cpu_topology: CpuTopology {
threads_per_core: 1,
cores_per_die: 128,
dies_per_socket: 1,
sockets: 1,
},
..Default::default()
}),
InstanceState::Uninitialized,
&|result| {
result.unwrap();
},
),
// invalid threads_per_core
TestData::new(
VmmAction::SetVmConfiguration(VmConfigInfo {
vcpu_count: 4,
max_vcpu_count: 4,
cpu_topology: CpuTopology {
threads_per_core: 4,
cores_per_die: 1,
dies_per_socket: 1,
sockets: 1,
},
..Default::default()
}),
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::MachineConfig(
VmConfigError::InvalidThreadsPerCore(4)
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to set configuration for the VM: \
the threads_per_core number '4' can only be 1 or 2",
);
assert_eq!(err_string, expected_err)
},
),
// invalid mem size
TestData::new(
VmmAction::SetVmConfiguration(VmConfigInfo {
mem_size_mib: 3,
..Default::default()
}),
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::MachineConfig(
VmConfigError::InvalidMemorySize(3)
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to set configuration for the VM: \
the memory size 0x3MiB is invalid",
);
assert_eq!(err_string, expected_err);
},
),
// invalid mem path
TestData::new(
VmmAction::SetVmConfiguration(VmConfigInfo {
mem_type: String::from("hugetlbfs"),
mem_file_path: String::from(""),
..Default::default()
}),
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::MachineConfig(
VmConfigError::InvalidMemFilePath(_)
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to set configuration for the VM: \
the memory file path is invalid",
);
assert_eq!(err_string, expected_err);
},
),
// success
TestData::new(
VmmAction::SetVmConfiguration(VmConfigInfo::default()),
InstanceState::Uninitialized,
&|result| {
assert!(result.is_ok());
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[test]
fn test_vmm_action_start_microvm() {
skip_if_not_root!();
let tests = &mut [
// invalid state (running)
TestData::new(VmmAction::StartMicroVm, InstanceState::Running, &|result| {
assert!(matches!(
result,
Err(VmmActionError::StartMicroVm(
StartMicroVmError::MicroVMAlreadyRunning
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to boot the VM: \
the virtual machine is already running",
);
assert_eq!(err_string, expected_err);
}),
// no kernel configuration
TestData::new(
VmmAction::StartMicroVm,
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::StartMicroVm(
StartMicroVmError::MissingKernelConfig
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to boot the VM: \
cannot start the virtual machine without kernel configuration",
);
assert_eq!(err_string, expected_err);
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[test]
fn test_vmm_action_shutdown_microvm() {
skip_if_not_root!();
let tests = &mut [
// success
TestData::new(
VmmAction::ShutdownMicroVm,
InstanceState::Uninitialized,
&|result| {
assert!(result.is_ok());
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[cfg(feature = "virtio-blk")]
#[test]
fn test_vmm_action_insert_block_device() {
skip_if_not_root!();
let dummy_file = TempFile::new().unwrap();
let dummy_path = dummy_file.as_path().to_owned();
let tests = &mut [
// invalid state
TestData::new(
VmmAction::InsertBlockDevice(BlockDeviceConfigInfo::default()),
InstanceState::Running,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::Block(
BlockDeviceError::UpdateNotAllowedPostBoot
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"virtio-blk device error: \
block device does not support runtime update",
);
assert_eq!(err_string, expected_err);
},
),
// success
TestData::new(
VmmAction::InsertBlockDevice(BlockDeviceConfigInfo {
path_on_host: dummy_path,
device_type: crate::device_manager::blk_dev_mgr::BlockDeviceType::RawBlock,
is_root_device: true,
part_uuid: None,
is_read_only: false,
is_direct: false,
no_drop: false,
drive_id: String::from("1"),
rate_limiter: None,
num_queues: BlockDeviceConfigInfo::default_num_queues(),
queue_size: 256,
use_shared_irq: None,
use_generic_irq: None,
}),
InstanceState::Uninitialized,
&|result| {
assert!(result.is_ok());
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[cfg(feature = "virtio-blk")]
#[test]
fn test_vmm_action_update_block_device() {
skip_if_not_root!();
let tests = &mut [
// invalid id
TestData::new(
VmmAction::UpdateBlockDevice(BlockDeviceConfigUpdateInfo {
drive_id: String::from("1"),
rate_limiter: None,
}),
InstanceState::Running,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::Block(BlockDeviceError::InvalidDeviceId(_)))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"virtio-blk device error: \
invalid block device id '1'",
);
assert_eq!(err_string, expected_err);
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[cfg(feature = "virtio-blk")]
#[test]
fn test_vmm_action_remove_block_device() {
skip_if_not_root!();
let tests = &mut [
// invalid state
TestData::new(
VmmAction::RemoveBlockDevice(String::from("1")),
InstanceState::Running,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::Block(
BlockDeviceError::UpdateNotAllowedPostBoot
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"virtio-blk device error: \
block device does not support runtime update",
);
assert_eq!(err_string, expected_err);
},
),
// invalid id
TestData::new(
VmmAction::RemoveBlockDevice(String::from("1")),
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::Block(BlockDeviceError::InvalidDeviceId(_)))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"virtio-blk device error: \
invalid block device id '1'",
);
assert_eq!(err_string, expected_err);
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[cfg(feature = "virtio-fs")]
#[test]
fn test_vmm_action_insert_fs_device() {
skip_if_not_root!();
let tests = &mut [
// invalid state
TestData::new(
VmmAction::InsertFsDevice(FsDeviceConfigInfo::default()),
InstanceState::Running,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::FsDevice(
FsDeviceError::UpdateNotAllowedPostBoot
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"virtio-fs device error: \
update operation is not allowed after boot",
);
assert_eq!(err_string, expected_err);
},
),
// success
TestData::new(
VmmAction::InsertFsDevice(FsDeviceConfigInfo::default()),
InstanceState::Uninitialized,
&|result| {
assert!(result.is_ok());
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[cfg(feature = "virtio-fs")]
#[test]
fn test_vmm_action_manipulate_fs_device() {
skip_if_not_root!();
let tests = &mut [
// invalid state
TestData::new(
VmmAction::ManipulateFsBackendFs(FsMountConfigInfo::default()),
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::FsDevice(FsDeviceError::MicroVMNotRunning))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"virtio-fs device error: \
vm is not running when attaching a backend fs",
);
assert_eq!(err_string, expected_err);
},
),
// invalid backend
TestData::new(
VmmAction::ManipulateFsBackendFs(FsMountConfigInfo::default()),
InstanceState::Running,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::FsDevice(
FsDeviceError::AttachBackendFailed(_)
))
));
let err_string = format!("{}", result.unwrap_err());
println!("{}", err_string);
let expected_err = String::from(
"virtio-fs device error: \
Fs device attach a backend fs failed",
);
assert_eq!(err_string, expected_err);
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[cfg(feature = "virtio-net")]
#[test]
fn test_vmm_action_insert_network_device() {
skip_if_not_root!();
let tests = &mut [
// hotplug unready
TestData::new(
VmmAction::InsertNetworkDevice(VirtioNetDeviceConfigInfo::default()),
InstanceState::Running,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::StartMicroVm(
StartMicroVmError::UpcallMissVsock
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to boot the VM: \
the upcall client needs a virtio-vsock device for communication",
);
assert_eq!(err_string, expected_err);
},
),
// success
TestData::new(
VmmAction::InsertNetworkDevice(VirtioNetDeviceConfigInfo::default()),
InstanceState::Uninitialized,
&|result| {
assert!(result.is_ok());
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[cfg(feature = "virtio-net")]
#[test]
fn test_vmm_action_update_network_interface() {
skip_if_not_root!();
let tests = &mut [
// invalid id
TestData::new(
VmmAction::UpdateNetworkInterface(VirtioNetDeviceConfigUpdateInfo {
iface_id: String::from("1"),
rx_rate_limiter: None,
tx_rate_limiter: None,
}),
InstanceState::Running,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::VirtioNet(
VirtioNetDeviceError::InvalidIfaceId(_)
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"virtio-net device error: \
invalid virtio-net iface id '1'",
);
assert_eq!(err_string, expected_err);
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
#[cfg(feature = "virtio-vsock")]
#[test]
fn test_vmm_action_insert_vsock_device() {
skip_if_not_root!();
let tests = &mut [
// invalid state
TestData::new(
VmmAction::InsertVsockDevice(VsockDeviceConfigInfo::default()),
InstanceState::Running,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::Vsock(
VsockDeviceError::UpdateNotAllowedPostBoot
))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to add virtio-vsock device: \
update operation is not allowed after boot",
);
assert_eq!(err_string, expected_err);
},
),
// invalid guest_cid
TestData::new(
VmmAction::InsertVsockDevice(VsockDeviceConfigInfo::default()),
InstanceState::Uninitialized,
&|result| {
assert!(matches!(
result,
Err(VmmActionError::Vsock(VsockDeviceError::GuestCIDInvalid(0)))
));
let err_string = format!("{}", result.unwrap_err());
let expected_err = String::from(
"failed to add virtio-vsock device: \
the guest CID 0 is invalid",
);
assert_eq!(err_string, expected_err);
},
),
// success
TestData::new(
VmmAction::InsertVsockDevice(VsockDeviceConfigInfo {
guest_cid: 3,
..Default::default()
}),
InstanceState::Uninitialized,
&|result| {
assert!(result.is_ok());
},
),
];
for t in tests.iter_mut() {
t.check_request();
}
}
}

View File

@ -46,7 +46,7 @@ pub trait ConfigItem {
} }
/// Struct to manage a group of configuration items. /// Struct to manage a group of configuration items.
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct ConfigInfos<T> pub struct ConfigInfos<T>
where where
T: ConfigItem + Clone, T: ConfigItem + Clone,
@ -316,7 +316,7 @@ where
} }
/// Configuration information for RateLimiter token bucket. /// Configuration information for RateLimiter token bucket.
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct TokenBucketConfigInfo { pub struct TokenBucketConfigInfo {
/// The size for the token bucket. A TokenBucket of `size` total capacity will take `refill_time` /// The size for the token bucket. A TokenBucket of `size` total capacity will take `refill_time`
/// milliseconds to go from zero tokens to total capacity. /// milliseconds to go from zero tokens to total capacity.
@ -349,7 +349,7 @@ impl From<&TokenBucketConfigInfo> for TokenBucket {
} }
/// Configuration information for RateLimiter objects. /// Configuration information for RateLimiter objects.
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct RateLimiterConfigInfo { pub struct RateLimiterConfigInfo {
/// Data used to initialize the RateLimiter::bandwidth bucket. /// Data used to initialize the RateLimiter::bandwidth bucket.
pub bandwidth: TokenBucketConfigInfo, pub bandwidth: TokenBucketConfigInfo,

View File

@ -106,7 +106,7 @@ pub enum BlockDeviceError {
} }
/// Type of low level storage device/protocol for virtio-blk devices. /// Type of low level storage device/protocol for virtio-blk devices.
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum BlockDeviceType { pub enum BlockDeviceType {
/// Unknown low level device type. /// Unknown low level device type.
Unknown, Unknown,
@ -131,7 +131,7 @@ impl BlockDeviceType {
} }
/// Configuration information for a block device. /// Configuration information for a block device.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct BlockDeviceConfigUpdateInfo { pub struct BlockDeviceConfigUpdateInfo {
/// Unique identifier of the drive. /// Unique identifier of the drive.
pub drive_id: String, pub drive_id: String,
@ -151,7 +151,7 @@ impl BlockDeviceConfigUpdateInfo {
} }
/// Configuration information for a block device. /// Configuration information for a block device.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct BlockDeviceConfigInfo { pub struct BlockDeviceConfigInfo {
/// Unique identifier of the drive. /// Unique identifier of the drive.
pub drive_id: String, pub drive_id: String,
@ -285,7 +285,6 @@ impl std::fmt::Debug for BlockDeviceInfo {
pub type BlockDeviceInfo = DeviceConfigInfo<BlockDeviceConfigInfo>; pub type BlockDeviceInfo = DeviceConfigInfo<BlockDeviceConfigInfo>;
/// Wrapper for the collection that holds all the Block Devices Configs /// Wrapper for the collection that holds all the Block Devices Configs
//#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[derive(Clone)] #[derive(Clone)]
pub struct BlockDeviceMgr { pub struct BlockDeviceMgr {
/// A list of `BlockDeviceInfo` objects. /// A list of `BlockDeviceInfo` objects.
@ -625,7 +624,7 @@ impl BlockDeviceMgr {
// we need to satisfy the condition by which a VMM can only have on root device // we need to satisfy the condition by which a VMM can only have on root device
if block_device_config.is_root_device { if block_device_config.is_root_device {
if self.has_root_block { if self.has_root_block {
return Err(BlockDeviceError::RootBlockDeviceAlreadyAdded); Err(BlockDeviceError::RootBlockDeviceAlreadyAdded)
} else { } else {
self.has_root_block = true; self.has_root_block = true;
self.read_only_root = block_device_config.is_read_only; self.read_only_root = block_device_config.is_read_only;

View File

@ -89,7 +89,7 @@ pub enum FsDeviceError {
} }
/// Configuration information for a vhost-user-fs device. /// Configuration information for a vhost-user-fs device.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct FsDeviceConfigInfo { pub struct FsDeviceConfigInfo {
/// vhost-user socket path. /// vhost-user socket path.
pub sock_path: String, pub sock_path: String,
@ -201,7 +201,7 @@ impl FsDeviceConfigInfo {
} }
/// Configuration information for virtio-fs. /// Configuration information for virtio-fs.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct FsDeviceConfigUpdateInfo { pub struct FsDeviceConfigUpdateInfo {
/// virtiofs mount tag name used inside the guest. /// virtiofs mount tag name used inside the guest.
/// used as the device name during mount. /// used as the device name during mount.
@ -242,7 +242,7 @@ impl ConfigItem for FsDeviceConfigInfo {
} }
/// Configuration information of manipulating backend fs for a virtiofs device. /// Configuration information of manipulating backend fs for a virtiofs device.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Default)]
pub struct FsMountConfigInfo { pub struct FsMountConfigInfo {
/// Mount operations, mount, update, umount /// Mount operations, mount, update, umount
pub ops: String, pub ops: String,

View File

@ -147,7 +147,11 @@ pub type Result<T> = ::std::result::Result<T, DeviceMgrError>;
/// Type of the dragonball virtio devices. /// Type of the dragonball virtio devices.
#[cfg(feature = "dbs-virtio-devices")] #[cfg(feature = "dbs-virtio-devices")]
pub type DbsVirtioDevice = Box< pub type DbsVirtioDevice = Box<
dyn VirtioDevice<GuestAddressSpaceImpl, virtio_queue::QueueStateSync, vm_memory::GuestRegionMmap>, dyn VirtioDevice<
GuestAddressSpaceImpl,
virtio_queue::QueueStateSync,
vm_memory::GuestRegionMmap,
>,
>; >;
/// Type of the dragonball virtio mmio devices. /// Type of the dragonball virtio mmio devices.
@ -791,13 +795,14 @@ impl DeviceManager {
fn allocate_mmio_device_resource( fn allocate_mmio_device_resource(
&self, &self,
) -> std::result::Result<DeviceResources, StartMicroVmError> { ) -> std::result::Result<DeviceResources, StartMicroVmError> {
let mut requests = Vec::new(); let requests = vec![
requests.push(ResourceConstraint::MmioAddress { ResourceConstraint::MmioAddress {
range: None, range: None,
align: MMIO_DEFAULT_CFG_SIZE, align: MMIO_DEFAULT_CFG_SIZE,
size: MMIO_DEFAULT_CFG_SIZE, size: MMIO_DEFAULT_CFG_SIZE,
}); },
requests.push(ResourceConstraint::LegacyIrq { irq: None }); ResourceConstraint::LegacyIrq { irq: None },
];
self.res_manager self.res_manager
.allocate_device_resources(&requests, false) .allocate_device_resources(&requests, false)
@ -997,7 +1002,7 @@ impl DeviceManager {
{ {
self.vsock_manager self.vsock_manager
.get_default_connector() .get_default_connector()
.map(|d| Some(d)) .map(Some)
.unwrap_or(None) .unwrap_or(None)
} }
#[cfg(not(feature = "virtio-vsock"))] #[cfg(not(feature = "virtio-vsock"))]

View File

@ -93,7 +93,7 @@ pub enum VirtioNetDeviceError {
} }
/// Configuration information for virtio net devices. /// Configuration information for virtio net devices.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct VirtioNetDeviceConfigUpdateInfo { pub struct VirtioNetDeviceConfigUpdateInfo {
/// ID of the guest network interface. /// ID of the guest network interface.
pub iface_id: String, pub iface_id: String,
@ -123,7 +123,7 @@ impl VirtioNetDeviceConfigUpdateInfo {
} }
/// Configuration information for virtio net devices. /// Configuration information for virtio net devices.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, Default)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Default)]
pub struct VirtioNetDeviceConfigInfo { pub struct VirtioNetDeviceConfigInfo {
/// ID of the guest network interface. /// ID of the guest network interface.
pub iface_id: String, pub iface_id: String,
@ -264,7 +264,7 @@ impl VirtioNetDeviceMgr {
config.use_generic_irq.unwrap_or(USE_GENERIC_IRQ), config.use_generic_irq.unwrap_or(USE_GENERIC_IRQ),
) )
.map_err(VirtioNetDeviceError::DeviceManager)?; .map_err(VirtioNetDeviceError::DeviceManager)?;
ctx.insert_hotplug_mmio_device(&dev.clone(), None) ctx.insert_hotplug_mmio_device(&dev, None)
.map_err(VirtioNetDeviceError::DeviceManager)?; .map_err(VirtioNetDeviceError::DeviceManager)?;
// live-upgrade need save/restore device from info.device. // live-upgrade need save/restore device from info.device.
mgr.info_list[device_index].set_device(dev); mgr.info_list[device_index].set_device(dev);

View File

@ -70,7 +70,7 @@ pub enum VsockDeviceError {
} }
/// Configuration information for a vsock device. /// Configuration information for a vsock device.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct VsockDeviceConfigInfo { pub struct VsockDeviceConfigInfo {
/// ID of the vsock device. /// ID of the vsock device.
pub id: String, pub id: String,

View File

@ -101,7 +101,6 @@ impl EventManager {
/// Poll pending events and invoke registered event handler. /// Poll pending events and invoke registered event handler.
/// ///
/// # Arguments: /// # Arguments:
/// * max_events: maximum number of pending events to handle
/// * timeout: maximum time in milliseconds to wait /// * timeout: maximum time in milliseconds to wait
pub fn handle_events(&self, timeout: i32) -> std::result::Result<usize, EpollError> { pub fn handle_events(&self, timeout: i32) -> std::result::Result<usize, EpollError> {
self.epoll_mgr self.epoll_mgr

View File

@ -210,14 +210,19 @@ mod x86_64 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use kvm_ioctls::Kvm;
use std::fs::File; use std::fs::File;
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, FromRawFd}; use std::os::unix::io::{AsRawFd, FromRawFd};
use kvm_ioctls::Kvm;
use test_utils::skip_if_not_root;
use super::*;
#[test] #[test]
fn test_create_kvm_context() { fn test_create_kvm_context() {
skip_if_not_root!();
let c = KvmContext::new(None).unwrap(); let c = KvmContext::new(None).unwrap();
assert!(c.max_memslots >= 32); assert!(c.max_memslots >= 32);
@ -234,6 +239,8 @@ mod tests {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[test] #[test]
fn test_get_supported_cpu_id() { fn test_get_supported_cpu_id() {
skip_if_not_root!();
let c = KvmContext::new(None).unwrap(); let c = KvmContext::new(None).unwrap();
let _ = c let _ = c
@ -244,6 +251,8 @@ mod tests {
#[test] #[test]
fn test_create_vm() { fn test_create_vm() {
skip_if_not_root!();
let c = KvmContext::new(None).unwrap(); let c = KvmContext::new(None).unwrap();
let _ = c.create_vm().unwrap(); let _ = c.create_vm().unwrap();

View File

@ -36,7 +36,7 @@ const PIO_MAX: u16 = 0xFFFF;
const MMIO_SPACE_RESERVED: u64 = 0x400_0000; const MMIO_SPACE_RESERVED: u64 = 0x400_0000;
/// Errors associated with resource management operations /// Errors associated with resource management operations
#[derive(Debug, PartialEq, thiserror::Error)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum ResourceError { pub enum ResourceError {
/// Unknown/unsupported resource type. /// Unknown/unsupported resource type.
#[error("unsupported resource type")] #[error("unsupported resource type")]
@ -569,9 +569,7 @@ impl ResourceManager {
Resource::KvmMemSlot(slot) => self.free_kvm_mem_slot(*slot), Resource::KvmMemSlot(slot) => self.free_kvm_mem_slot(*slot),
Resource::MacAddresss(_) => Ok(()), Resource::MacAddresss(_) => Ok(()),
}; };
if result.is_err() { result?;
return result;
}
} }
Ok(()) Ok(())
} }
@ -588,9 +586,9 @@ mod tests {
// Allocate/free shared IRQs multiple times. // Allocate/free shared IRQs multiple times.
assert_eq!(mgr.allocate_legacy_irq(true, None).unwrap(), SHARED_IRQ); assert_eq!(mgr.allocate_legacy_irq(true, None).unwrap(), SHARED_IRQ);
assert_eq!(mgr.allocate_legacy_irq(true, None).unwrap(), SHARED_IRQ); assert_eq!(mgr.allocate_legacy_irq(true, None).unwrap(), SHARED_IRQ);
mgr.free_legacy_irq(SHARED_IRQ); mgr.free_legacy_irq(SHARED_IRQ).unwrap();
mgr.free_legacy_irq(SHARED_IRQ); mgr.free_legacy_irq(SHARED_IRQ).unwrap();
mgr.free_legacy_irq(SHARED_IRQ); mgr.free_legacy_irq(SHARED_IRQ).unwrap();
// Allocate specified IRQs. // Allocate specified IRQs.
assert_eq!( assert_eq!(
@ -598,7 +596,7 @@ mod tests {
.unwrap(), .unwrap(),
LEGACY_IRQ_BASE + 10 LEGACY_IRQ_BASE + 10
); );
mgr.free_legacy_irq(LEGACY_IRQ_BASE + 10); mgr.free_legacy_irq(LEGACY_IRQ_BASE + 10).unwrap();
assert_eq!( assert_eq!(
mgr.allocate_legacy_irq(false, Some(LEGACY_IRQ_BASE + 10)) mgr.allocate_legacy_irq(false, Some(LEGACY_IRQ_BASE + 10))
.unwrap(), .unwrap(),
@ -635,19 +633,19 @@ mod tests {
let mgr = ResourceManager::new(None); let mgr = ResourceManager::new(None);
let msi = mgr.allocate_msi_irq(3).unwrap(); let msi = mgr.allocate_msi_irq(3).unwrap();
mgr.free_msi_irq(msi, 3); mgr.free_msi_irq(msi, 3).unwrap();
let msi = mgr.allocate_msi_irq(3).unwrap(); let msi = mgr.allocate_msi_irq(3).unwrap();
mgr.free_msi_irq(msi, 3); mgr.free_msi_irq(msi, 3).unwrap();
let irq = mgr.allocate_msi_irq_aligned(8).unwrap(); let irq = mgr.allocate_msi_irq_aligned(8).unwrap();
assert_eq!(irq & 0x7, 0); assert_eq!(irq & 0x7, 0);
mgr.free_msi_irq(msi, 8); mgr.free_msi_irq(msi, 8).unwrap();
let irq = mgr.allocate_msi_irq_aligned(8).unwrap(); let irq = mgr.allocate_msi_irq_aligned(8).unwrap();
assert_eq!(irq & 0x7, 0); assert_eq!(irq & 0x7, 0);
let irq = mgr.allocate_msi_irq_aligned(512).unwrap(); let irq = mgr.allocate_msi_irq_aligned(512).unwrap();
assert_eq!(irq, 512); assert_eq!(irq, 512);
mgr.free_msi_irq(irq, 512); mgr.free_msi_irq(irq, 512).unwrap();
let irq = mgr.allocate_msi_irq_aligned(512).unwrap(); let irq = mgr.allocate_msi_irq_aligned(512).unwrap();
assert_eq!(irq, 512); assert_eq!(irq, 512);
@ -690,9 +688,9 @@ mod tests {
}, },
]; ];
let resources = mgr.allocate_device_resources(&requests, false).unwrap(); let resources = mgr.allocate_device_resources(&requests, false).unwrap();
mgr.free_device_resources(&resources); mgr.free_device_resources(&resources).unwrap();
let resources = mgr.allocate_device_resources(&requests, false).unwrap(); let resources = mgr.allocate_device_resources(&requests, false).unwrap();
mgr.free_device_resources(&resources); mgr.free_device_resources(&resources).unwrap();
requests.push(ResourceConstraint::PioAddress { requests.push(ResourceConstraint::PioAddress {
range: Some((0xc000, 0xc000)), range: Some((0xc000, 0xc000)),
align: 0x1000, align: 0x1000,
@ -702,7 +700,7 @@ mod tests {
let resources = mgr let resources = mgr
.allocate_device_resources(&requests[0..requests.len() - 1], false) .allocate_device_resources(&requests[0..requests.len() - 1], false)
.unwrap(); .unwrap();
mgr.free_device_resources(&resources); mgr.free_device_resources(&resources).unwrap();
} }
#[test] #[test]
@ -721,7 +719,7 @@ mod tests {
let mgr = ResourceManager::new(None); let mgr = ResourceManager::new(None);
assert_eq!(mgr.allocate_kvm_mem_slot(1, None).unwrap(), 0); assert_eq!(mgr.allocate_kvm_mem_slot(1, None).unwrap(), 0);
assert_eq!(mgr.allocate_kvm_mem_slot(1, Some(200)).unwrap(), 200); assert_eq!(mgr.allocate_kvm_mem_slot(1, Some(200)).unwrap(), 200);
mgr.free_kvm_mem_slot(200); mgr.free_kvm_mem_slot(200).unwrap();
assert_eq!(mgr.allocate_kvm_mem_slot(1, Some(200)).unwrap(), 200); assert_eq!(mgr.allocate_kvm_mem_slot(1, Some(200)).unwrap(), 200);
assert_eq!( assert_eq!(
mgr.allocate_kvm_mem_slot(1, Some(KVM_USER_MEM_SLOTS)) mgr.allocate_kvm_mem_slot(1, Some(KVM_USER_MEM_SLOTS))

View File

@ -39,6 +39,7 @@ impl Vcpu {
/// vcpu thread to vmm thread. /// vcpu thread to vmm thread.
/// * `create_ts` - A timestamp used by the vcpu to calculate its lifetime. /// * `create_ts` - A timestamp used by the vcpu to calculate its lifetime.
/// * `support_immediate_exit` - whether kvm uses supports immediate_exit flag. /// * `support_immediate_exit` - whether kvm uses supports immediate_exit flag.
#[allow(clippy::too_many_arguments)]
pub fn new_aarch64( pub fn new_aarch64(
id: u8, id: u8,
vcpu_fd: Arc<VcpuFd>, vcpu_fd: Arc<VcpuFd>,

View File

@ -533,16 +533,11 @@ impl Vcpu {
fn check_io_port_info(&self, addr: u16, data: &[u8]) -> Result<bool> { fn check_io_port_info(&self, addr: u16, data: &[u8]) -> Result<bool> {
let mut checked = false; let mut checked = false;
match addr { // debug info signal
// debug info signal if addr == MAGIC_IOPORT_DEBUG_INFO && data.len() == 4 {
MAGIC_IOPORT_DEBUG_INFO => { let data = unsafe { std::ptr::read(data.as_ptr() as *const u32) };
if data.len() == 4 { log::warn!("KDBG: guest kernel debug info: 0x{:x}", data);
let data = unsafe { std::ptr::read(data.as_ptr() as *const u32) }; checked = true;
log::warn!("KDBG: guest kernel debug info: 0x{:x}", data);
checked = true;
}
}
_ => {}
}; };
Ok(checked) Ok(checked)
@ -771,6 +766,7 @@ pub mod tests {
use dbs_device::device_manager::IoManager; use dbs_device::device_manager::IoManager;
use kvm_ioctls::Kvm; use kvm_ioctls::Kvm;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use test_utils::skip_if_not_root;
use super::*; use super::*;
use crate::kvm_context::KvmContext; use crate::kvm_context::KvmContext;
@ -855,7 +851,7 @@ pub mod tests {
let kvm = Kvm::new().unwrap(); let kvm = Kvm::new().unwrap();
let vm = Arc::new(kvm.create_vm().unwrap()); let vm = Arc::new(kvm.create_vm().unwrap());
let kvm_context = KvmContext::new(Some(kvm.as_raw_fd())).unwrap(); let _kvm_context = KvmContext::new(Some(kvm.as_raw_fd())).unwrap();
let vcpu_fd = Arc::new(vm.create_vcpu(0).unwrap()); let vcpu_fd = Arc::new(vm.create_vcpu(0).unwrap());
let io_manager = IoManagerCached::new(Arc::new(ArcSwap::new(Arc::new(IoManager::new())))); let io_manager = IoManagerCached::new(Arc::new(ArcSwap::new(Arc::new(IoManager::new()))));
let reset_event_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let reset_event_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
@ -880,6 +876,8 @@ pub mod tests {
#[test] #[test]
fn test_vcpu_run_emulation() { fn test_vcpu_run_emulation() {
skip_if_not_root!();
let (mut vcpu, _) = create_vcpu(); let (mut vcpu, _) = create_vcpu();
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
@ -964,6 +962,8 @@ pub mod tests {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[test] #[test]
fn test_vcpu_check_io_port_info() { fn test_vcpu_check_io_port_info() {
skip_if_not_root!();
let (vcpu, _receiver) = create_vcpu(); let (vcpu, _receiver) = create_vcpu();
// debug info signal // debug info signal

View File

@ -774,7 +774,7 @@ impl VcpuManager {
self.reset_event_fd.as_ref().unwrap().try_clone().unwrap(), self.reset_event_fd.as_ref().unwrap().try_clone().unwrap(),
self.vcpu_state_event.try_clone().unwrap(), self.vcpu_state_event.try_clone().unwrap(),
self.vcpu_state_sender.clone(), self.vcpu_state_sender.clone(),
request_ts.clone(), request_ts,
self.support_immediate_exit, self.support_immediate_exit,
) )
.map_err(VcpuManagerError::Vcpu) .map_err(VcpuManagerError::Vcpu)

View File

@ -35,6 +35,7 @@ use crate::event_manager::EventManager;
/// * `device_info` - A hashmap containing the attached devices for building FDT device nodes. /// * `device_info` - A hashmap containing the attached devices for building FDT device nodes.
/// * `gic_device` - The GIC device. /// * `gic_device` - The GIC device.
/// * `initrd` - Information about an optional initrd. /// * `initrd` - Information about an optional initrd.
#[allow(clippy::borrowed_box)]
fn configure_system<T: DeviceInfoForFDT + Clone + Debug, M: GuestMemory>( fn configure_system<T: DeviceInfoForFDT + Clone + Debug, M: GuestMemory>(
guest_mem: &M, guest_mem: &M,
cmdline: &str, cmdline: &str,
@ -58,8 +59,9 @@ fn configure_system<T: DeviceInfoForFDT + Clone + Debug, M: GuestMemory>(
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
impl Vm { impl Vm {
/// Gets a reference to the irqchip of the VM /// Gets a reference to the irqchip of the VM
#[allow(clippy::borrowed_box)]
pub fn get_irqchip(&self) -> &Box<dyn GICDevice> { pub fn get_irqchip(&self) -> &Box<dyn GICDevice> {
&self.irqchip_handle.as_ref().unwrap() self.irqchip_handle.as_ref().unwrap()
} }
/// Creates the irq chip in-kernel device model. /// Creates the irq chip in-kernel device model.

View File

@ -67,7 +67,7 @@ pub enum VmError {
} }
/// Configuration information for user defined NUMA nodes. /// Configuration information for user defined NUMA nodes.
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct NumaRegionInfo { pub struct NumaRegionInfo {
/// memory size for this region (unit: MiB) /// memory size for this region (unit: MiB)
pub size: u64, pub size: u64,
@ -80,7 +80,7 @@ pub struct NumaRegionInfo {
} }
/// Information for cpu topology to guide guest init /// Information for cpu topology to guide guest init
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct CpuTopology { pub struct CpuTopology {
/// threads per core to indicate hyperthreading is enabled or not /// threads per core to indicate hyperthreading is enabled or not
pub threads_per_core: u8, pub threads_per_core: u8,
@ -104,7 +104,7 @@ impl Default for CpuTopology {
} }
/// Configuration information for virtual machine instance. /// Configuration information for virtual machine instance.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct VmConfigInfo { pub struct VmConfigInfo {
/// Number of vcpu to start. /// Number of vcpu to start.
pub vcpu_count: u8, pub vcpu_count: u8,
@ -814,3 +814,17 @@ impl Vm {
Err(StartMicroVmError::MicroVMAlreadyRunning) Err(StartMicroVmError::MicroVMAlreadyRunning)
} }
} }
#[cfg(test)]
pub mod tests {
use super::*;
impl Vm {
pub fn set_instance_state(&mut self, mstate: InstanceState) {
self.shared_info
.write()
.expect("Failed to start microVM because shared info couldn't be written due to poisoned lock")
.state = mstate;
}
}
}

View File

@ -189,6 +189,8 @@ impl Vmm {
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use test_utils::skip_if_not_root;
use super::*; use super::*;
pub fn create_vmm_instance() -> Vmm { pub fn create_vmm_instance() -> Vmm {
@ -210,6 +212,8 @@ pub(crate) mod tests {
#[test] #[test]
fn test_create_vmm_instance() { fn test_create_vmm_instance() {
skip_if_not_root!();
create_vmm_instance(); create_vmm_instance();
} }
} }

View File

@ -49,7 +49,7 @@ pub fn is_host_empty_dir(path: &str) -> bool {
false false
} }
// set_ephemeral_storage_type sets the mount type to 'ephemeral' // update_ephemeral_storage_type sets the mount type to 'ephemeral'
// if the mount source path is provisioned by k8s for ephemeral storage. // if the mount source path is provisioned by k8s for ephemeral storage.
// For the given pod ephemeral volume is created only once // For the given pod ephemeral volume is created only once
// backed by tmpfs inside the VM. For successive containers // backed by tmpfs inside the VM. For successive containers
@ -63,6 +63,8 @@ pub fn update_ephemeral_storage_type(oci_spec: &mut Spec) {
if is_ephemeral_volume(&m.source) { if is_ephemeral_volume(&m.source) {
m.r#type = String::from(mount::KATA_EPHEMERAL_VOLUME_TYPE); m.r#type = String::from(mount::KATA_EPHEMERAL_VOLUME_TYPE);
} else if is_host_empty_dir(&m.source) { } else if is_host_empty_dir(&m.source) {
// FIXME support disable_guest_empty_dir
// https://github.com/kata-containers/kata-containers/blob/02a51e75a7e0c6fce5e8abe3b991eeac87e09645/src/runtime/pkg/katautils/create.go#L105
m.r#type = String::from(mount::KATA_HOST_DIR_VOLUME_TYPE); m.r#type = String::from(mount::KATA_HOST_DIR_VOLUME_TYPE);
} }
} }

View File

@ -49,7 +49,7 @@ pub enum ShimIdInfo {
} }
/// get container type /// get container type
pub fn get_contaier_type(spec: &oci::Spec) -> Result<ContainerType, Error> { pub fn get_container_type(spec: &oci::Spec) -> Result<ContainerType, Error> {
for k in CRI_CONTAINER_TYPE_KEY_LIST.iter() { for k in CRI_CONTAINER_TYPE_KEY_LIST.iter() {
if let Some(type_value) = spec.annotations.get(*k) { if let Some(type_value) = spec.annotations.get(*k) {
match type_value.as_str() { match type_value.as_str() {
@ -67,7 +67,7 @@ pub fn get_contaier_type(spec: &oci::Spec) -> Result<ContainerType, Error> {
/// get shim id info /// get shim id info
pub fn get_shim_id_info() -> Result<ShimIdInfo, Error> { pub fn get_shim_id_info() -> Result<ShimIdInfo, Error> {
let spec = load_oci_spec()?; let spec = load_oci_spec()?;
match get_contaier_type(&spec)? { match get_container_type(&spec)? {
ContainerType::PodSandbox => Ok(ShimIdInfo::Sandbox), ContainerType::PodSandbox => Ok(ShimIdInfo::Sandbox),
ContainerType::PodContainer => { ContainerType::PodContainer => {
for k in CRI_SANDBOX_ID_KEY_LIST { for k in CRI_SANDBOX_ID_KEY_LIST {

View File

@ -13,7 +13,7 @@ pub const KATA_VOLUME_TYPE_PREFIX: &str = "kata:";
pub const KATA_GUEST_MOUNT_PREFIX: &str = "kata:guest-mount:"; pub const KATA_GUEST_MOUNT_PREFIX: &str = "kata:guest-mount:";
/// KATA_EPHEMERAL_DEV_TYPE creates a tmpfs backed volume for sharing files between containers. /// KATA_EPHEMERAL_DEV_TYPE creates a tmpfs backed volume for sharing files between containers.
pub const KATA_EPHEMERAL_VOLUME_TYPE: &str = "kata:ephemeral"; pub const KATA_EPHEMERAL_VOLUME_TYPE: &str = "ephemeral";
/// KATA_HOST_DIR_TYPE use for host empty dir /// KATA_HOST_DIR_TYPE use for host empty dir
pub const KATA_HOST_DIR_VOLUME_TYPE: &str = "kata:hostdir"; pub const KATA_HOST_DIR_VOLUME_TYPE: &str = "kata:hostdir";

View File

@ -31,7 +31,7 @@ test:
else else
##TARGET default: build code ##TARGET default: build code
default: runtime show-header default: runtime show-header
#TARGET test: run cargo tests ##TARGET test: run cargo tests
test: test:
@cargo test --all --target $(TRIPLE) $(EXTRA_RUSTFEATURES) -- --nocapture @cargo test --all --target $(TRIPLE) $(EXTRA_RUSTFEATURES) -- --nocapture
endif endif
@ -50,7 +50,6 @@ EXEC_PREFIX := $(PREFIX)/local
BINDIR := $(EXEC_PREFIX)/bin BINDIR := $(EXEC_PREFIX)/bin
else else
EXEC_PREFIX := $(PREFIX) EXEC_PREFIX := $(PREFIX)
##VAR BINDIR=<path> is a directory for installing executable programs
# when creating the kata-deploy image, the default installation path for go runtime is $(EXEC_PREFIX)/bin, so we put it here for multiple runtime # when creating the kata-deploy image, the default installation path for go runtime is $(EXEC_PREFIX)/bin, so we put it here for multiple runtime
BINDIR := $(EXEC_PREFIX)/runtime-rs/bin/ BINDIR := $(EXEC_PREFIX)/runtime-rs/bin/
endif endif
@ -73,7 +72,7 @@ HYPERVISOR_CLH = cloud-hypervisor
DEFAULT_HYPERVISOR ?= $(HYPERVISOR_DB) DEFAULT_HYPERVISOR ?= $(HYPERVISOR_DB)
# List of hypervisors this build system can generate configuration for. ##VAR HYPERVISOR=<hypervisor_name> List of hypervisors this build system can generate configuration for.
HYPERVISORS := $(HYPERVISOR_DB) $(HYPERVISOR_ACRN) $(HYPERVISOR_FC) $(HYPERVISOR_QEMU) $(HYPERVISOR_CLH) HYPERVISORS := $(HYPERVISOR_DB) $(HYPERVISOR_ACRN) $(HYPERVISOR_FC) $(HYPERVISOR_QEMU) $(HYPERVISOR_CLH)
DBVALIDHYPERVISORPATHS := [] DBVALIDHYPERVISORPATHS := []
@ -84,28 +83,28 @@ PKGLIBEXECDIR := $(LIBEXECDIR)/$(PROJECT_DIR)
FIRMWAREPATH := FIRMWAREPATH :=
FIRMWAREVOLUMEPATH := FIRMWAREVOLUMEPATH :=
# Default number of vCPUs ##VAR DEFVCPUS=<number> Default number of vCPUs
DEFVCPUS := 1 DEFVCPUS := 1
# Default maximum number of vCPUs ##VAR DEFMAXVCPUS=<number> Default maximum number of vCPUs
DEFMAXVCPUS := 0 DEFMAXVCPUS := 0
# Default memory size in MiB ##VAR DEFMEMSZ=<number> Default memory size in MiB
DEFMEMSZ := 2048 DEFMEMSZ := 2048
# Default memory slots ##VAR DEFMEMSLOTS=<number> Default memory slots
# Cases to consider : # Cases to consider :
# - nvdimm rootfs image # - nvdimm rootfs image
# - preallocated memory # - preallocated memory
# - vm template memory # - vm template memory
# - hugepage memory # - hugepage memory
DEFMEMSLOTS := 10 DEFMEMSLOTS := 10
#Default number of bridges ##VAR DEFBRIDGES=<number> Default number of bridges
DEFBRIDGES := 0 DEFBRIDGES := 0
DEFENABLEANNOTATIONS := [] DEFENABLEANNOTATIONS := []
DEFDISABLEGUESTSECCOMP := true DEFDISABLEGUESTSECCOMP := true
DEFDISABLEGUESTEMPTYDIR := false DEFDISABLEGUESTEMPTYDIR := false
#Default experimental features enabled ##VAR DEFAULTEXPFEATURES=[features] Default experimental features enabled
DEFAULTEXPFEATURES := [] DEFAULTEXPFEATURES := []
DEFDISABLESELINUX := false DEFDISABLESELINUX := false
#Default entropy source ##VAR DEFENTROPYSOURCE=[entropy_source] Default entropy source
DEFENTROPYSOURCE := /dev/urandom DEFENTROPYSOURCE := /dev/urandom
DEFVALIDENTROPYSOURCES := [\"/dev/urandom\",\"/dev/random\",\"\"] DEFVALIDENTROPYSOURCES := [\"/dev/urandom\",\"/dev/random\",\"\"]
DEFDISABLEBLOCK := false DEFDISABLEBLOCK := false
@ -116,8 +115,8 @@ ifeq ($(ARCH),x86_64)
DEFVIRTIOFSDAEMON := $(LIBEXECDIR)/virtiofsd DEFVIRTIOFSDAEMON := $(LIBEXECDIR)/virtiofsd
endif endif
DEFVALIDVIRTIOFSDAEMONPATHS := [\"$(DEFVIRTIOFSDAEMON)\"] DEFVALIDVIRTIOFSDAEMONPATHS := [\"$(DEFVIRTIOFSDAEMON)\"]
# Default DAX mapping cache size in MiB ##VAR DEFVIRTIOFSCACHESIZE=<cache_size> Default DAX mapping cache size in MiB
#if value is 0, DAX is not enabled # if value is 0, DAX is not enabled
DEFVIRTIOFSCACHESIZE ?= 0 DEFVIRTIOFSCACHESIZE ?= 0
DEFVIRTIOFSCACHE ?= auto DEFVIRTIOFSCACHE ?= auto
# Format example: # Format example:
@ -134,7 +133,7 @@ DEFFILEMEMBACKEND := ""
DEFVALIDFILEMEMBACKENDS := [\"$(DEFFILEMEMBACKEND)\"] DEFVALIDFILEMEMBACKENDS := [\"$(DEFFILEMEMBACKEND)\"]
DEFMSIZE9P := 8192 DEFMSIZE9P := 8192
DEFVFIOMODE := guest-kernel DEFVFIOMODE := guest-kernel
# Default cgroup model ##VAR DEFSANDBOXCGROUPONLY=<bool> Default cgroup model
DEFSANDBOXCGROUPONLY ?= false DEFSANDBOXCGROUPONLY ?= false
DEFSTATICRESOURCEMGMT_DB ?= false DEFSTATICRESOURCEMGMT_DB ?= false
DEFBINDMOUNTS := [] DEFBINDMOUNTS := []
@ -160,9 +159,9 @@ KNOWN_HYPERVISORS =
CONFDIR := $(DEFAULTSDIR)/$(PROJECT_DIR) CONFDIR := $(DEFAULTSDIR)/$(PROJECT_DIR)
SYSCONFDIR := $(SYSCONFDIR)/$(PROJECT_DIR) SYSCONFDIR := $(SYSCONFDIR)/$(PROJECT_DIR)
# Main configuration file location for stateless systems ##VAR CONFIG_PATH=<path> Main configuration file location for stateless systems
CONFIG_PATH := $(abspath $(CONFDIR)/$(CONFIG_FILE)) CONFIG_PATH := $(abspath $(CONFDIR)/$(CONFIG_FILE))
# Secondary configuration file location. Note that this takes precedence ##VAR SYSCONFIG=<path> Secondary configuration file location. Note that this takes precedence
# over CONFIG_PATH. # over CONFIG_PATH.
SYSCONFIG := $(abspath $(SYSCONFDIR)/$(CONFIG_FILE)) SYSCONFIG := $(abspath $(SYSCONFDIR)/$(CONFIG_FILE))
SHAREDIR := $(SHAREDIR) SHAREDIR := $(SHAREDIR)
@ -454,7 +453,7 @@ endif
@printf "\tassets path (PKGDATADIR) : %s\n" $(abspath $(PKGDATADIR)) @printf "\tassets path (PKGDATADIR) : %s\n" $(abspath $(PKGDATADIR))
@printf "\tshim path (PKGLIBEXECDIR) : %s\n" $(abspath $(PKGLIBEXECDIR)) @printf "\tshim path (PKGLIBEXECDIR) : %s\n" $(abspath $(PKGLIBEXECDIR))
@printf "\n" @printf "\n"
## help: Show help comments that start with `##VAR` and `##TARGET` ##TARGET help: Show help comments that start with `##VAR` and `##TARGET` in runtime-rs makefile
help: Makefile show-summary help: Makefile show-summary
@echo "========================== Help =============================" @echo "========================== Help ============================="
@echo "Variables:" @echo "Variables:"

View File

@ -97,6 +97,10 @@ Currently, only built-in `Dragonball` has been implemented.
Persist defines traits and functions to help different components save state to disk and load state from disk. Persist defines traits and functions to help different components save state to disk and load state from disk.
### helper libraries
Some helper libraries are maintained in [the library directory](../libs) so that they can be shared with other rust components.
## Build and install ## Build and install
```bash ```bash

View File

@ -55,6 +55,7 @@ pub struct ShareFsVolumeConfig {
pub target: String, pub target: String,
pub readonly: bool, pub readonly: bool,
pub mount_options: Vec<String>, pub mount_options: Vec<String>,
pub mount: oci::Mount,
} }
pub struct ShareFsMountResult { pub struct ShareFsMountResult {

View File

@ -67,7 +67,10 @@ impl ShareVirtioFsStandalone {
fn virtiofsd_args(&self, sock_path: &str) -> Result<Vec<String>> { fn virtiofsd_args(&self, sock_path: &str) -> Result<Vec<String>> {
let source_path = get_host_ro_shared_path(&self.config.id); let source_path = get_host_ro_shared_path(&self.config.id);
if !source_path.exists() { if !source_path.exists() {
return Err(anyhow!("The virtiofs shared path didn't exist")); return Err(anyhow!(
"The virtiofs shared path({:?}) didn't exist",
source_path
));
} }
let mut args: Vec<String> = vec![ let mut args: Vec<String> = vec![

View File

@ -8,12 +8,15 @@ use agent::Storage;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use async_trait::async_trait; use async_trait::async_trait;
use kata_types::k8s::is_watchable_mount; use kata_types::k8s::is_watchable_mount;
use kata_types::mount;
use nix::sys::stat::stat;
use std::fs; use std::fs;
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::path::Path; use std::path::Path;
const WATCHABLE_PATH_NAME: &str = "watchable"; const WATCHABLE_PATH_NAME: &str = "watchable";
const WATCHABLE_BIND_DEV_TYPE: &str = "watchable-bind"; const WATCHABLE_BIND_DEV_TYPE: &str = "watchable-bind";
const EPHEMERAL_PATH: &str = "/run/kata-containers/sandbox/ephemeral";
use super::{ use super::{
utils, ShareFsMount, ShareFsMountResult, ShareFsRootfsConfig, ShareFsVolumeConfig, utils, ShareFsMount, ShareFsMountResult, ShareFsRootfsConfig, ShareFsVolumeConfig,
@ -105,6 +108,51 @@ impl ShareFsMount for VirtiofsShareMount {
let storages = vec![watchable_storage]; let storages = vec![watchable_storage];
return Ok(ShareFsMountResult {
guest_path,
storages,
});
} else if config.mount.r#type == mount::KATA_EPHEMERAL_VOLUME_TYPE {
// refer to the golang `handleEphemeralStorage` code at
// https://github.com/kata-containers/kata-containers/blob/9516286f6dd5cfd6b138810e5d7c9e01cf6fc043/src/runtime/virtcontainers/kata_agent.go#L1354
let source = &config.mount.source;
let file_stat =
stat(Path::new(source)).with_context(|| format!("mount source {}", source))?;
// if volume's gid isn't root group(default group), this means there's
// an specific fsGroup is set on this local volume, then it should pass
// to guest.
let dir_options = if file_stat.st_gid != 0 {
vec![format!("fsgid={}", file_stat.st_gid)]
} else {
vec![]
};
let file_name = Path::new(source)
.file_name()
.context("get file name from mount.source")?;
let source = Path::new(EPHEMERAL_PATH)
.join(file_name)
.into_os_string()
.into_string()
.map_err(|e| anyhow!("failed to get ephemeral path {:?}", e))?;
// Create a storage struct so that kata agent is able to create
// tmpfs backed volume inside the VM
let ephemeral_storage = agent::Storage {
driver: String::from(mount::KATA_EPHEMERAL_VOLUME_TYPE),
driver_options: Vec::new(),
source: String::from("tmpfs"),
fs_type: String::from("tmpfs"),
fs_group: None,
options: dir_options,
mount_point: source.clone(),
};
guest_path = source;
let storages = vec![ephemeral_storage];
return Ok(ShareFsMountResult { return Ok(ShareFsMountResult {
guest_path, guest_path,
storages, storages,

View File

@ -10,6 +10,7 @@ use anyhow::{anyhow, Context, Result};
use super::Volume; use super::Volume;
use crate::share_fs::{ShareFs, ShareFsVolumeConfig}; use crate::share_fs::{ShareFs, ShareFsVolumeConfig};
use kata_types::mount;
// copy file to container's rootfs if filesystem sharing is not supported, otherwise // copy file to container's rootfs if filesystem sharing is not supported, otherwise
// bind mount it in the shared directory. // bind mount it in the shared directory.
@ -66,6 +67,7 @@ impl ShareFsVolume {
target: file_name, target: file_name,
readonly: false, readonly: false,
mount_options: m.options.clone(), mount_options: m.options.clone(),
mount: m.clone(),
}) })
.await .await
.context("share fs volume")?; .context("share fs volume")?;
@ -101,7 +103,8 @@ impl Volume for ShareFsVolume {
} }
pub(crate) fn is_share_fs_volume(m: &oci::Mount) -> bool { pub(crate) fn is_share_fs_volume(m: &oci::Mount) -> bool {
m.r#type == "bind" && !is_host_device(&m.destination) (m.r#type == "bind" || m.r#type == mount::KATA_EPHEMERAL_VOLUME_TYPE)
&& !is_host_device(&m.destination)
} }
fn is_host_device(dest: &str) -> bool { fn is_host_device(dest: &str) -> bool {

View File

@ -15,6 +15,7 @@ use common::{
ProcessType, ProcessType,
}, },
}; };
use kata_sys_util::k8s::update_ephemeral_storage_type;
use oci::{LinuxResources, Process as OCIProcess}; use oci::{LinuxResources, Process as OCIProcess};
use resource::ResourceManager; use resource::ResourceManager;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -110,6 +111,7 @@ impl Container {
.context("handler volumes")?; .context("handler volumes")?;
let mut oci_mounts = vec![]; let mut oci_mounts = vec![];
let mut storages = vec![]; let mut storages = vec![];
for v in volumes { for v in volumes {
let mut volume_mounts = v.get_volume_mount().context("get volume mount")?; let mut volume_mounts = v.get_volume_mount().context("get volume mount")?;
if !volume_mounts.is_empty() { if !volume_mounts.is_empty() {
@ -378,6 +380,9 @@ fn amend_spec(spec: &mut oci::Spec, disable_guest_seccomp: bool) -> Result<()> {
// hook should be done on host // hook should be done on host
spec.hooks = None; spec.hooks = None;
// special process K8s ephemeral volumes.
update_ephemeral_storage_type(spec);
if let Some(linux) = spec.linux.as_mut() { if let Some(linux) = spec.linux.as_mut() {
if disable_guest_seccomp { if disable_guest_seccomp {
linux.seccomp = None; linux.seccomp = None;

View File

@ -155,7 +155,7 @@ impl ServiceManager {
let handler = RuntimeHandlerManager::new(sid, sender) let handler = RuntimeHandlerManager::new(sid, sender)
.await .await
.context("new runtime handler")?; .context("new runtime handler")?;
handler.cleanup().await?; handler.cleanup().await.context("runtime handler cleanup")?;
let temp_dir = [KATA_PATH, sid].join("/"); let temp_dir = [KATA_PATH, sid].join("/");
if std::fs::metadata(temp_dir.as_str()).is_ok() { if std::fs::metadata(temp_dir.as_str()).is_ok() {
// try to remove dir and skip the result // try to remove dir and skip the result

View File

@ -6,6 +6,9 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use containerd_shim_protos::api; use containerd_shim_protos::api;
use kata_sys_util::spec::{get_bundle_path, get_container_type, load_oci_spec};
use kata_types::container::ContainerType;
use nix::{sys::signal::kill, sys::signal::SIGKILL, unistd::Pid};
use protobuf::Message; use protobuf::Message;
use std::{fs, path::Path}; use std::{fs, path::Path};
@ -14,7 +17,7 @@ use crate::{shim::ShimExecutor, Error};
impl ShimExecutor { impl ShimExecutor {
pub async fn delete(&mut self) -> Result<()> { pub async fn delete(&mut self) -> Result<()> {
self.args.validate(true).context("validate")?; self.args.validate(true).context("validate")?;
let rsp = self.do_cleanup().await.context("do cleanup")?; let rsp = self.do_cleanup().await.context("shim do cleanup")?;
rsp.write_to_writer(&mut std::io::stdout()) rsp.write_to_writer(&mut std::io::stdout())
.context(Error::FileWrite(format!("write {:?} to stdout", rsp)))?; .context(Error::FileWrite(format!("write {:?} to stdout", rsp)))?;
Ok(()) Ok(())
@ -41,9 +44,28 @@ impl ShimExecutor {
info!(sl!(), "remote socket path: {:?}", &file_path); info!(sl!(), "remote socket path: {:?}", &file_path);
fs::remove_file(file_path).ok(); fs::remove_file(file_path).ok();
} }
service::ServiceManager::cleanup(&self.args.id)
.await if let Err(e) = service::ServiceManager::cleanup(&self.args.id).await {
.context("cleanup")?; error!(
sl!(),
"failed to cleanup in service manager: {:?}. force shutdown shim process", e
);
let bundle_path = get_bundle_path().context("get bundle path")?;
if let Ok(spec) = load_oci_spec() {
if let Ok(ContainerType::PodSandbox) = get_container_type(&spec) {
// only force shutdown for sandbox container
if let Ok(shim_pid) = self.read_pid_file(&bundle_path) {
info!(sl!(), "force to shutdown shim process {}", shim_pid);
let pid = Pid::from_raw(shim_pid as i32);
if let Err(_e) = kill(pid, SIGKILL) {
// ignore kill errors
}
}
}
}
}
Ok(rsp) Ok(rsp)
} }
} }

1
src/tools/kata-ctl/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/vendor/

View File

@ -0,0 +1,23 @@
# Copyright (c) 2022 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
[package]
name = "kata-ctl"
version = "0.0.1"
authors = ["The Kata Containers community <kata-dev@lists.katacontainers.io>"]
edition = "2018"
[dependencies]
anyhow = "1.0.31"
clap = { version = "3.2.20", features = ["derive", "cargo"] }
serde_json = "1.0.85"
thiserror = "1.0.35"
# See: https://github.com/kata-containers/kata-containers/issues/5438
[target.'cfg(not(target_arch = "s390x"))'.dependencies]
reqwest = { version = "0.11", default-features = false, features = ["json", "blocking", "rustls-tls"] }
[dev-dependencies]
semver = "1.0.12"

View File

@ -0,0 +1,64 @@
# Copyright (c) 2022 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
include ../../../utils.mk
PROJECT_NAME = Kata Containers
PROJECT_URL = https://github.com/kata-containers
PROJECT_COMPONENT = kata-ctl
TARGET = $(PROJECT_COMPONENT)
VERSION_FILE := ./VERSION
VERSION := $(shell grep -v ^\# $(VERSION_FILE))
COMMIT_NO := $(shell git rev-parse HEAD 2>/dev/null || true)
COMMIT_NO_SHORT := $(shell git rev-parse --short HEAD 2>/dev/null || true)
COMMIT := $(if $(shell git status --porcelain --untracked-files=no 2>/dev/null || true),${COMMIT_NO}-dirty,${COMMIT_NO})
# Exported to allow cargo to see it
export KATA_CTL_VERSION := $(if $(COMMIT),$(VERSION)-$(COMMIT),$(VERSION))
GENERATED_CODE = src/ops/version.rs
GENERATED_REPLACEMENTS= \
KATA_CTL_VERSION
GENERATED_FILES := $(GENERATED_CODE)
.DEFAULT_GOAL := default
default: $(TARGET) build
$(TARGET): $(GENERATED_CODE)
build:
@RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo build --target $(TRIPLE) $(if $(findstring release,$(BUILD_TYPE)),--release) $(EXTRA_RUSTFEATURES)
$(GENERATED_FILES): %: %.in
@sed $(foreach r,$(GENERATED_REPLACEMENTS),-e 's|@$r@|$($r)|g') "$<" > "$@"
clean:
@cargo clean
@rm -f $(GENERATED_FILES)
vendor:
cargo vendor
test:
@RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo test --target $(TRIPLE) $(if $(findstring release,$(BUILD_TYPE)),--release) $(EXTRA_RUSTFEATURES)
install:
@RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo install --target $(TRIPLE) --path .
check: standard_rust_check
.PHONY: \
build \
check \
clean \
install \
test \
vendor

View File

@ -0,0 +1,49 @@
# Kata Containers control tool
## Overview
The `kata-ctl` tool is a rust rewrite of the
[`kata-runtime`](../../runtime/cmd/kata-runtime)
[utility program](../../../docs/design/architecture/README.md#utility-program).
The program provides a number of utility commands for:
- Using advanced Kata Containers features.
- Problem determination and debugging.
## Audience and environment
Users and administrators.
## Build the tool
```bash
$ make
```
## Install the tool
```bash
$ make install
```
## Run the tool
```bash
$ kata-ctl ...
```
For example, to determine if your system is capable of running Kata
Containers, run:
```bash
$ kata-ctl check all
```
### Full details
For a usage statement, run:
```bash
$ kata-ctl --help
```

1
src/tools/kata-ctl/VERSION Symbolic link
View File

@ -0,0 +1 @@
../../../VERSION

View File

@ -0,0 +1,15 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(target_arch = "aarch64")]
pub use arch_specific::*;
mod arch_specific {
use anyhow::Result;
pub fn check() -> Result<()> {
unimplemented!("Check not implemented in aarch64")
}
}

View File

@ -0,0 +1,38 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use anyhow::Result;
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use aarch64 as arch_specific;
#[cfg(target_arch = "powerpc64le")]
pub mod powerpc64le;
#[cfg(target_arch = "powerpc64le")]
pub use powerpc64le as arch_specific;
#[cfg(target_arch = "s390x")]
pub mod s390x;
#[cfg(target_arch = "s390x")]
pub use s390x as arch_specific;
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
#[cfg(target_arch = "x86_64")]
pub use x86_64 as arch_specific;
#[cfg(not(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "s390x",
target_arch = "x86_64"
)))]
compile_error!("unknown architecture");
pub fn check() -> Result<()> {
arch_specific::check()
}

View File

@ -0,0 +1,15 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(target_arch = "powerpc64le")]
pub use arch_specific::*;
mod arch_specific {
use anyhow::Result;
pub fn check() -> Result<()> {
unimplemented!("Check not implemented in powerpc64le");
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(target_arch = "s390x")]
pub use arch_specific::*;
mod arch_specific {
use anyhow::Result;
pub fn check() -> Result<()> {
unimplemented!("Check not implemented in s390x");
}
}

View File

@ -0,0 +1,55 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(target_arch = "x86_64")]
pub use arch_specific::*;
mod arch_specific {
use crate::check;
use anyhow::{anyhow, Result};
const PROC_CPUINFO: &str = "/proc/cpuinfo";
const CPUINFO_DELIMITER: &str = "\nprocessor";
const CPUINFO_FLAGS_TAG: &str = "flags";
const CPU_FLAGS_INTEL: &[&str] = &["lm", "sse4_1", "vmx"];
const CPU_ATTRIBS_INTEL: &[&str] = &["GenuineIntel"];
// check cpu
fn check_cpu() -> Result<()> {
println!("INFO: check CPU: x86_64");
let cpu_info = check::get_single_cpu_info(PROC_CPUINFO, CPUINFO_DELIMITER)?;
let cpu_flags = check::get_cpu_flags(&cpu_info, CPUINFO_FLAGS_TAG)
.map_err(|e| anyhow!("Error parsing CPU flags, file {:?}, {:?}", PROC_CPUINFO, e))?;
// perform checks
// TODO: Perform checks based on hypervisor type
// TODO: Add more information to output (see kata-check in go tool); adjust formatting
let missing_cpu_attributes = check::check_cpu_attribs(&cpu_info, CPU_ATTRIBS_INTEL)?;
if !missing_cpu_attributes.is_empty() {
eprintln!(
"WARNING: Missing CPU attributes {:?}",
missing_cpu_attributes
);
}
let missing_cpu_flags = check::check_cpu_flags(&cpu_flags, CPU_FLAGS_INTEL)?;
if !missing_cpu_flags.is_empty() {
eprintln!("WARNING: Missing CPU flags {:?}", missing_cpu_flags);
}
Ok(())
}
pub fn check() -> Result<()> {
println!("INFO: check: x86_64");
let _cpu_result = check_cpu();
// TODO: collect outcome of tests to determine if checks pass or not
Ok(())
}
}

View File

@ -0,0 +1,86 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use clap::{Args, Parser, Subcommand};
use thiserror::Error;
#[derive(Parser, Debug)]
#[clap(name = "kata-ctl", author, about = "Kata Containers control tool")]
pub struct KataCtlCli {
#[clap(subcommand)]
pub command: Commands,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
/// Test if system can run Kata Containers
Check(CheckArgument),
/// Directly assign a volume to Kata Containers to manage
DirectVolume,
/// Display settings
Env,
/// Enter into guest VM by debug console
Exec,
/// Manage VM factory
Factory,
/// Manage guest VM iptables
Iptables(IptablesCommand),
/// Gather metrics associated with infrastructure used to run a sandbox
Metrics(MetricsCommand),
/// Display version details
Version,
}
#[derive(Debug, Args, Error)]
#[error("Argument is not valid")]
pub struct CheckArgument {
#[clap(subcommand)]
pub command: CheckSubCommand,
}
#[derive(Debug, Subcommand)]
pub enum CheckSubCommand {
/// Run all checks
All,
/// Run all checks but excluding network checks.
NoNetworkChecks,
/// Only compare the current and latest available versions
CheckVersionOnly,
}
#[derive(Debug, Args)]
pub struct MetricsCommand {
#[clap(subcommand)]
pub metrics_cmd: MetricsSubCommand,
}
#[derive(Debug, Subcommand)]
pub enum MetricsSubCommand {
/// Arguments for metrics
MetricsArgs,
}
// #[derive(Parser, Debug)]
#[derive(Debug, Args)]
pub struct IptablesCommand {
#[clap(subcommand)]
pub iptables: IpTablesArguments,
}
#[derive(Debug, Subcommand)]
pub enum IpTablesArguments {
/// Configure iptables
Metrics,
}

View File

@ -0,0 +1,246 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
// Contains checks that are not architecture-specific
use anyhow::{anyhow, Result};
// See: https://github.com/kata-containers/kata-containers/issues/5438
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
use reqwest::header::{CONTENT_TYPE, USER_AGENT};
use serde_json::Value;
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
use std::collections::HashMap;
use std::fs;
const KATA_GITHUB_URL: &str =
"https://api.github.com/repos/kata-containers/kata-containers/releases/latest";
fn get_cpu_info(cpu_info_file: &str) -> Result<String> {
let contents = fs::read_to_string(cpu_info_file)?;
Ok(contents)
}
// get_single_cpu_info returns the contents of the first cpu from
// the specified cpuinfo file by parsing based on a specified delimiter
pub fn get_single_cpu_info(cpu_info_file: &str, substring: &str) -> Result<String> {
let contents = get_cpu_info(cpu_info_file)?;
if contents.is_empty() {
return Err(anyhow!("cpu_info string is empty"))?;
}
let subcontents: Vec<&str> = contents.split(substring).collect();
let result = subcontents
.first()
.ok_or("error splitting contents of cpuinfo")
.map_err(|e| anyhow!(e))?
.to_string();
Ok(result)
}
// get_cpu_flags returns a string of cpu flags from cpuinfo, passed in
// as a string
pub fn get_cpu_flags(cpu_info: &str, cpu_flags_tag: &str) -> Result<String> {
if cpu_info.is_empty() {
return Err(anyhow!("cpu_info string is empty"))?;
}
let subcontents: Vec<&str> = cpu_info.split('\n').collect();
for line in subcontents {
if line.starts_with(cpu_flags_tag) {
let line_data: Vec<&str> = line.split(':').collect();
let flags = line_data
.last()
.ok_or("error splitting flags in cpuinfo")
.map_err(|e| anyhow!(e))?
.to_string();
return Ok(flags);
}
}
Ok("".to_string())
}
// get_missing_strings searches for required (strings) in data and returns
// a vector containing the missing strings
fn get_missing_strings(data: &str, required: &'static [&'static str]) -> Result<Vec<String>> {
let mut missing: Vec<String> = Vec::new();
for item in required {
if !data.split_whitespace().any(|x| x == *item) {
missing.push(item.to_string());
}
}
Ok(missing)
}
pub fn check_cpu_flags(
retrieved_flags: &str,
required_flags: &'static [&'static str],
) -> Result<Vec<String>> {
let missing_flags = get_missing_strings(retrieved_flags, required_flags)?;
Ok(missing_flags)
}
pub fn check_cpu_attribs(
cpu_info: &str,
required_attribs: &'static [&'static str],
) -> Result<Vec<String>> {
let mut cpu_info_processed = cpu_info.replace('\t', "");
cpu_info_processed = cpu_info_processed.replace('\n', " ");
let missing_attribs = get_missing_strings(&cpu_info_processed, required_attribs)?;
Ok(missing_attribs)
}
pub fn run_network_checks() -> Result<()> {
Ok(())
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
fn get_kata_version_by_url(url: &str) -> std::result::Result<String, reqwest::Error> {
let content = reqwest::blocking::Client::new()
.get(url)
.header(CONTENT_TYPE, "application/json")
.header(USER_AGENT, "kata")
.send()?
.json::<HashMap<String, Value>>()?;
let version = content["tag_name"].as_str().unwrap();
Ok(version.to_string())
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
fn handle_reqwest_error(e: reqwest::Error) -> anyhow::Error {
if e.is_connect() {
return anyhow!(e).context("http connection failure: connection refused");
}
if e.is_timeout() {
return anyhow!(e).context("http connection failure: connection timeout");
}
if e.is_builder() {
return anyhow!(e).context("http connection failure: url malformed");
}
if e.is_decode() {
return anyhow!(e).context("http connection failure: unable to decode response body");
}
anyhow!(e).context("unknown http connection failure: {:?}")
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
pub fn check_version() -> Result<()> {
let version = get_kata_version_by_url(KATA_GITHUB_URL).map_err(handle_reqwest_error)?;
println!("Version: {}", version);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use semver::Version;
#[test]
fn test_get_cpu_info_empty_input() {
let expected = "No such file or directory (os error 2)";
let actual = get_cpu_info("").err().unwrap().to_string();
assert_eq!(expected, actual);
let actual = get_single_cpu_info("", "\nprocessor")
.err()
.unwrap()
.to_string();
assert_eq!(expected, actual);
}
#[test]
fn test_get_cpu_flags_empty_input() {
let expected = "cpu_info string is empty";
let actual = get_cpu_flags("", "").err().unwrap().to_string();
assert_eq!(expected, actual);
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
#[test]
fn check_version_by_empty_url() {
const TEST_URL: &str = "http:";
let expected = "builder error: empty host";
let actual = get_kata_version_by_url(TEST_URL).err().unwrap().to_string();
assert_eq!(expected, actual);
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
#[test]
fn check_version_by_garbage_url() {
const TEST_URL: &str = "_localhost_";
let expected = "builder error: relative URL without a base";
let actual = get_kata_version_by_url(TEST_URL).err().unwrap().to_string();
assert_eq!(expected, actual);
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
#[test]
fn check_version_by_invalid_url() {
const TEST_URL: &str = "http://localhost :80";
let expected = "builder error: invalid domain character";
let actual = get_kata_version_by_url(TEST_URL).err().unwrap().to_string();
assert_eq!(expected, actual);
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
#[test]
fn check_latest_version() {
let version = get_kata_version_by_url(KATA_GITHUB_URL).unwrap();
let v = Version::parse(&version).unwrap();
assert!(!v.major.to_string().is_empty());
assert!(!v.minor.to_string().is_empty());
assert!(!v.patch.to_string().is_empty());
}
}

View File

@ -0,0 +1,42 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
mod arch;
mod args;
mod check;
mod ops;
use anyhow::Result;
use clap::Parser;
use std::process::exit;
use args::{Commands, KataCtlCli};
use ops::check_ops::{
handle_check, handle_check_volume, handle_env, handle_exec, handle_factory, handle_iptables,
handle_metrics, handle_version,
};
fn real_main() -> Result<()> {
let args = KataCtlCli::parse();
match args.command {
Commands::Check(args) => handle_check(args),
Commands::DirectVolume => handle_check_volume(),
Commands::Env => handle_env(),
Commands::Exec => handle_exec(),
Commands::Factory => handle_factory(),
Commands::Iptables(args) => handle_iptables(args),
Commands::Metrics(args) => handle_metrics(args),
Commands::Version => handle_version(),
}
}
fn main() {
if let Err(e) = real_main() {
eprintln!("ERROR: {:#?}", e);
exit(1);
}
}

View File

@ -0,0 +1,7 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
pub mod check_ops;
pub mod version;

View File

@ -0,0 +1,79 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::arch;
use crate::check;
use crate::ops::version;
use crate::args::{CheckArgument, CheckSubCommand, IptablesCommand, MetricsCommand};
use anyhow::Result;
const NAME: &str = "kata-ctl";
pub fn handle_check(checkcmd: CheckArgument) -> Result<()> {
let command = checkcmd.command;
match command {
CheckSubCommand::All => {
// run architecture-specific tests
arch::check()?;
// run code that uses network checks
check::run_network_checks()?;
}
CheckSubCommand::NoNetworkChecks => {
// run architecture-specific tests
arch::check()?;
}
CheckSubCommand::CheckVersionOnly => {
// retrieve latest release
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "x86_64"
))]
check::check_version()?;
// See: https://github.com/kata-containers/kata-containers/issues/5438
#[cfg(target_arch = "s390x")]
unimplemented!("Network check not implemented on s390x")
}
}
Ok(())
}
pub fn handle_check_volume() -> Result<()> {
Ok(())
}
pub fn handle_env() -> Result<()> {
Ok(())
}
pub fn handle_exec() -> Result<()> {
Ok(())
}
pub fn handle_factory() -> Result<()> {
Ok(())
}
pub fn handle_iptables(_args: IptablesCommand) -> Result<()> {
Ok(())
}
pub fn handle_metrics(_args: MetricsCommand) -> Result<()> {
Ok(())
}
pub fn handle_version() -> Result<()> {
let version = version::get().unwrap();
println!("{} version {:?} (type: rust)", NAME, version);
Ok(())
}

View File

@ -0,0 +1,39 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
//
// WARNING: This file is auto-generated - DO NOT EDIT!
//
use clap::crate_version;
const KATA_CTL_VERSION: &str = "@KATA_CTL_VERSION@";
pub fn get() -> Result<String, String> {
if KATA_CTL_VERSION.trim().is_empty() {
Err("Unable to retrieve kata Version. Check that Kata is properly installed".to_string())
} else {
let version = format!("{}-{}", KATA_CTL_VERSION, crate_version!());
Ok(version)
}
}
#[cfg(test)]
mod tests {
use super::*;
use semver::Version;
#[test]
fn test_get() {
let version = get().unwrap();
let v = Version::parse(&version).unwrap();
assert!(!v.major.to_string().is_empty());
assert!(!v.minor.to_string().is_empty());
assert!(!v.patch.to_string().is_empty());
assert!(!v.pre.to_string().is_empty());
}
}

View File

@ -173,6 +173,7 @@ TRIPLE = $(ARCH)-unknown-linux-$(LIBC)
CWD := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) CWD := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
standard_rust_check: standard_rust_check:
@echo "standard rust check..."
cargo fmt -- --check cargo fmt -- --check
cargo clippy --all-targets --all-features --release \ cargo clippy --all-targets --all-features --release \
-- \ -- \