feat: pci passthrough (#114)

* feat: pci passthrough

* feat: guest device management

* feat: addons mounting and kernel modules support

* feat: more pci work

* fix: kernel build squashfs fixes

* fix: e820entry should be available on all platforms
This commit is contained in:
Alex Zenla
2024-04-29 10:02:20 -07:00
committed by GitHub
parent bece7f33c7
commit 84920a88ab
33 changed files with 2294 additions and 1944 deletions

View File

@ -6,8 +6,8 @@ use krata::{
events::EventStream,
v1::{
common::{
guest_image_spec::Image, GuestImageSpec, GuestOciImageSpec, GuestSpec, GuestStatus,
GuestTaskSpec, GuestTaskSpecEnvVar, OciImageFormat,
guest_image_spec::Image, GuestImageSpec, GuestOciImageSpec, GuestSpec, GuestSpecDevice,
GuestStatus, GuestTaskSpec, GuestTaskSpecEnvVar, OciImageFormat,
},
control::{
control_service_client::ControlServiceClient, watch_events_reply::Event,
@ -50,6 +50,8 @@ pub struct LaunchCommand {
help = "Memory available to the guest, in megabytes"
)]
mem: u64,
#[arg[short = 'D', long = "device", help = "Devices to request for the guest"]]
device: Vec<String>,
#[arg[short, long, help = "Environment variables set in the guest"]]
env: Option<Vec<String>>,
#[arg(
@ -135,6 +137,11 @@ impl LaunchCommand {
working_directory: self.working_directory.unwrap_or_default(),
}),
annotations: vec![],
devices: self
.device
.iter()
.map(|name| GuestSpecDevice { name: name.clone() })
.collect(),
}),
};
let response = client

View File

@ -0,0 +1,128 @@
use anyhow::Result;
use clap::{Parser, ValueEnum};
use comfy_table::{presets::UTF8_FULL_CONDENSED, Cell, Color, Table};
use krata::{
events::EventStream,
v1::control::{control_service_client::ControlServiceClient, DeviceInfo, ListDevicesRequest},
};
use serde_json::Value;
use tonic::transport::Channel;
use crate::format::{kv2line, proto2dynamic, proto2kv};
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
enum ListDevicesFormat {
Table,
Json,
JsonPretty,
Jsonl,
Yaml,
KeyValue,
Simple,
}
#[derive(Parser)]
#[command(about = "List the devices on the hypervisor")]
pub struct ListDevicesCommand {
#[arg(short, long, default_value = "table", help = "Output format")]
format: ListDevicesFormat,
}
impl ListDevicesCommand {
pub async fn run(
self,
mut client: ControlServiceClient<Channel>,
_events: EventStream,
) -> Result<()> {
let reply = client
.list_devices(ListDevicesRequest {})
.await?
.into_inner();
let mut devices = reply.devices;
devices.sort_by(|a, b| a.name.cmp(&b.name));
match self.format {
ListDevicesFormat::Table => {
self.print_devices_table(devices)?;
}
ListDevicesFormat::Simple => {
for device in devices {
println!("{}\t{}\t{}", device.name, device.claimed, device.owner);
}
}
ListDevicesFormat::Json | ListDevicesFormat::JsonPretty | ListDevicesFormat::Yaml => {
let mut values = Vec::new();
for device in devices {
let message = proto2dynamic(device)?;
values.push(serde_json::to_value(message)?);
}
let value = Value::Array(values);
let encoded = if self.format == ListDevicesFormat::JsonPretty {
serde_json::to_string_pretty(&value)?
} else if self.format == ListDevicesFormat::Yaml {
serde_yaml::to_string(&value)?
} else {
serde_json::to_string(&value)?
};
println!("{}", encoded.trim());
}
ListDevicesFormat::Jsonl => {
for device in devices {
let message = proto2dynamic(device)?;
println!("{}", serde_json::to_string(&message)?);
}
}
ListDevicesFormat::KeyValue => {
self.print_key_value(devices)?;
}
}
Ok(())
}
fn print_devices_table(&self, devices: Vec<DeviceInfo>) -> Result<()> {
let mut table = Table::new();
table.load_preset(UTF8_FULL_CONDENSED);
table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
table.set_header(vec!["name", "status", "owner"]);
for device in devices {
let status_text = if device.claimed {
"claimed"
} else {
"available"
};
let status_color = if device.claimed {
Color::Blue
} else {
Color::Green
};
table.add_row(vec![
Cell::new(device.name),
Cell::new(status_text).fg(status_color),
Cell::new(device.owner),
]);
}
if table.is_empty() {
println!("no devices configured");
} else {
println!("{}", table);
}
Ok(())
}
fn print_key_value(&self, devices: Vec<DeviceInfo>) -> Result<()> {
for device in devices {
let kvs = proto2kv(device)?;
println!("{}", kv2line(kvs));
}
Ok(())
}
}

View File

@ -5,6 +5,7 @@ pub mod identify_host;
pub mod idm_snoop;
pub mod launch;
pub mod list;
pub mod list_devices;
pub mod logs;
pub mod metrics;
pub mod pull;
@ -24,8 +25,9 @@ use tonic::{transport::Channel, Request};
use self::{
attach::AttachCommand, destroy::DestroyCommand, exec::ExecCommand,
identify_host::IdentifyHostCommand, idm_snoop::IdmSnoopCommand, launch::LaunchCommand,
list::ListCommand, logs::LogsCommand, metrics::MetricsCommand, pull::PullCommand,
resolve::ResolveCommand, top::TopCommand, watch::WatchCommand,
list::ListCommand, list_devices::ListDevicesCommand, logs::LogsCommand,
metrics::MetricsCommand, pull::PullCommand, resolve::ResolveCommand, top::TopCommand,
watch::WatchCommand,
};
#[derive(Parser)]
@ -51,6 +53,7 @@ pub enum Commands {
Launch(LaunchCommand),
Destroy(DestroyCommand),
List(ListCommand),
ListDevices(ListDevicesCommand),
Attach(AttachCommand),
Pull(PullCommand),
Logs(LogsCommand),
@ -120,6 +123,10 @@ impl ControlCommand {
Commands::Exec(exec) => {
exec.run(client).await?;
}
Commands::ListDevices(list) => {
list.run(client, events).await?;
}
}
Ok(())
}