mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-08-17 15:38:00 +00:00
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:
commit
a06c6dd861
23
.github/workflows/static-checks.yaml
vendored
23
.github/workflows/static-checks.yaml
vendored
@ -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
|
||||||
|
6
Makefile
6
Makefile
@ -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
|
||||||
|
|
||||||
|
@ -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. |
|
||||||
|
@ -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
|
||||||
```
|
```
|
||||||
|
@ -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).
|
||||||
|
@ -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 = []
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
||||||
|
@ -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")]
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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"))]
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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))
|
||||||
|
@ -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>,
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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";
|
||||||
|
@ -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:"
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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![
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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
1
src/tools/kata-ctl/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/vendor/
|
23
src/tools/kata-ctl/Cargo.toml
Normal file
23
src/tools/kata-ctl/Cargo.toml
Normal 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"
|
64
src/tools/kata-ctl/Makefile
Normal file
64
src/tools/kata-ctl/Makefile
Normal 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
|
49
src/tools/kata-ctl/README.md
Normal file
49
src/tools/kata-ctl/README.md
Normal 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
1
src/tools/kata-ctl/VERSION
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../VERSION
|
15
src/tools/kata-ctl/src/arch/aarch64/mod.rs
Normal file
15
src/tools/kata-ctl/src/arch/aarch64/mod.rs
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
38
src/tools/kata-ctl/src/arch/mod.rs
Normal file
38
src/tools/kata-ctl/src/arch/mod.rs
Normal 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()
|
||||||
|
}
|
15
src/tools/kata-ctl/src/arch/powerpc64le/mod.rs
Normal file
15
src/tools/kata-ctl/src/arch/powerpc64le/mod.rs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
15
src/tools/kata-ctl/src/arch/s390x/mod.rs
Normal file
15
src/tools/kata-ctl/src/arch/s390x/mod.rs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
55
src/tools/kata-ctl/src/arch/x86_64/mod.rs
Normal file
55
src/tools/kata-ctl/src/arch/x86_64/mod.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
86
src/tools/kata-ctl/src/args.rs
Normal file
86
src/tools/kata-ctl/src/args.rs
Normal 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,
|
||||||
|
}
|
246
src/tools/kata-ctl/src/check.rs
Normal file
246
src/tools/kata-ctl/src/check.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
42
src/tools/kata-ctl/src/main.rs
Normal file
42
src/tools/kata-ctl/src/main.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
7
src/tools/kata-ctl/src/ops.rs
Normal file
7
src/tools/kata-ctl/src/ops.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
pub mod check_ops;
|
||||||
|
pub mod version;
|
79
src/tools/kata-ctl/src/ops/check_ops.rs
Normal file
79
src/tools/kata-ctl/src/ops/check_ops.rs
Normal 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(())
|
||||||
|
}
|
39
src/tools/kata-ctl/src/ops/version.rs.in
Normal file
39
src/tools/kata-ctl/src/ops/version.rs.in
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
1
utils.mk
1
utils.mk
@ -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 \
|
||||||
-- \
|
-- \
|
||||||
|
Loading…
Reference in New Issue
Block a user