runtime-rs: use qemu cmdline generation framework to launch VM

Deploy the framework added by the previous commit to generate qemu
command line and launch the VM.

We now properly store the child process object which allows us to
implement remaining Hypervisor functions necessary for a simple but
successful VM lifecycle, get_vmm_master_tid() and stop_vm().

Fixes #8184

Signed-off-by: Pavel Mores <pmores@redhat.com>
This commit is contained in:
Pavel Mores 2023-09-18 10:36:15 +02:00
parent f550d9a325
commit 25c8d5db5d
2 changed files with 81 additions and 33 deletions

View File

@ -3,19 +3,21 @@
// SPDX-License-Identifier: Apache-2.0
//
use anyhow::{anyhow, Context, Result};
use super::cmdline_generator::QemuCmdLine;
use crate::{
hypervisor_persist::HypervisorState, HypervisorConfig, MemoryConfig,
VcpuThreadIds, VsockDevice, HYPERVISOR_QEMU,
hypervisor_persist::HypervisorState, HypervisorConfig, MemoryConfig, VcpuThreadIds,
VsockDevice, HYPERVISOR_QEMU,
};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use kata_types::{
capabilities::{Capabilities, CapabilityBits},
config::KATA_PATH,
};
use async_trait::async_trait;
use std::collections::HashMap;
use persist::sandbox_persist::Persist;
use std::collections::HashMap;
use std::os::unix::io::AsRawFd;
use std::process::Child;
const VSOCK_SCHEME: &str = "vsock";
@ -24,6 +26,8 @@ pub struct QemuInner {
/// sandbox id
id: String,
qemu_process: Option<Child>,
config: HypervisorConfig,
devices: Vec<DeviceType>,
}
@ -32,6 +36,7 @@ impl QemuInner {
pub fn new() -> QemuInner {
QemuInner {
id: "".to_string(),
qemu_process: None,
config: Default::default(),
devices: Vec::new(),
}
@ -39,15 +44,8 @@ impl QemuInner {
pub(crate) async fn prepare_vm(&mut self, id: &str, _netns: Option<String>) -> Result<()> {
info!(sl!(), "Preparing QEMU VM");
self.id = id.to_string();
self.devices.push(DeviceType::Vsock(
VsockDevice::new(self.id.clone())
.await
.context("qemu: create agent vsock")?,
));
Ok(())
}
@ -57,28 +55,72 @@ impl QemuInner {
let vm_path = [KATA_PATH, self.id.as_str()].join("/");
std::fs::create_dir_all(vm_path)?;
let mut cmdline = QemuCmdLine::new(&self.id, &self.config)?;
// If there's a Vsock Device in `self.devices` the vhost-vsock file
// descriptor needs to stay open until the qemu process launches.
// This is why we need to store it in a variable at this scope.
let mut _vhost_fd = None;
for device in &mut self.devices {
match device {
DeviceType::ShareFs(share_fs_dev) => {
if share_fs_dev.config.fs_type == "virtio-fs" {
cmdline.add_virtiofs_share(
&share_fs_dev.config.sock_path,
&share_fs_dev.config.mount_tag,
share_fs_dev.config.queue_size,
);
}
}
DeviceType::Vsock(vsock_dev) => {
let fd = vsock_dev.init_config().await?;
cmdline.add_vsock(fd.as_raw_fd(), vsock_dev.config.guest_cid)?;
_vhost_fd = Some(fd);
}
DeviceType::Block(block_dev) => {
if block_dev.config.path_on_host == self.config.boot_info.initrd {
// If this block device represents initrd we ignore it here, it
// will be handled elsewhere by adding `-initrd` to the qemu
// command line.
continue;
}
match block_dev.config.driver_option.as_str() {
"nvdimm" => cmdline.add_nvdimm(
&block_dev.config.path_on_host,
block_dev.config.is_readonly,
)?,
unsupported => {
info!(sl!(), "unsupported block device driver: {}", unsupported)
}
}
}
_ => info!(sl!(), "qemu cmdline: unsupported device: {:?}", device),
}
}
// To get access to the VM console for debugging, enable the following
// line and replace its argument appropriately (open a terminal, run
// `tty` in it to get its device file path and use it as the argument).
//cmdline.add_serial_console("/dev/pts/23");
info!(sl!(), "qemu args: {}", cmdline.build().await?.join(" "));
let mut command = std::process::Command::new(&self.config.path);
command.args(cmdline.build().await?);
command
.arg("-kernel")
.arg(&self.config.boot_info.kernel)
.arg("-m")
.arg(format!("{}M", &self.config.memory_info.default_memory))
.arg("-initrd")
.arg(&self.config.boot_info.initrd)
.arg("-vga")
.arg("none")
.arg("-nodefaults")
.arg("-nographic");
command.spawn()?;
info!(sl!(), "qemu cmd: {:?}", command);
self.qemu_process = Some(command.spawn()?);
Ok(())
}
pub(crate) fn stop_vm(&mut self) -> Result<()> {
info!(sl!(), "Stopping QEMU VM");
todo!()
if let Some(ref mut qemu_process) = &mut self.qemu_process {
info!(sl!(), "QemuInner::stop_vm(): kill()'ing qemu");
qemu_process.kill().map_err(anyhow::Error::from)
} else {
Err(anyhow!("qemu process not running"))
}
}
pub(crate) fn pause_vm(&self) -> Result<()> {
@ -121,7 +163,16 @@ impl QemuInner {
pub(crate) async fn get_vmm_master_tid(&self) -> Result<u32> {
info!(sl!(), "QemuInner::get_vmm_master_tid()");
todo!()
if let Some(qemu_process) = &self.qemu_process {
info!(
sl!(),
"QemuInner::get_vmm_master_tid(): returning {}",
qemu_process.id()
);
Ok(qemu_process.id())
} else {
Err(anyhow!("qemu process not running"))
}
}
pub(crate) async fn get_ns_path(&self) -> Result<String> {
@ -237,11 +288,7 @@ impl QemuInner {
fn get_agent_vsock_dev(&self) -> Option<&VsockDevice> {
self.devices.iter().find_map(|dev| {
if let DeviceType::Vsock(vsock_dev) = dev {
if vsock_dev.id == self.id {
Some(vsock_dev)
} else {
None
}
Some(vsock_dev)
} else {
None
}

View File

@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
//
mod cmdline_generator;
mod inner;
use crate::device::DeviceType;