mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-02 12:50:54 +00:00
krata: event-based network backend startup and api enhancements
This commit is contained in:
parent
63c0feb053
commit
66465793cd
@ -7,11 +7,15 @@ resolver = "2"
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
prost = { workspace = true }
|
||||
prost-reflect = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tonic = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tokio-stream = { workspace = true }
|
||||
tower = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{dial::ControlDialAddress, v1::control::control_service_client::ControlServiceClient};
|
||||
#[cfg(not(unix))]
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Result;
|
||||
use krata::{dial::ControlDialAddress, v1::control::control_service_client::ControlServiceClient};
|
||||
#[cfg(unix)]
|
||||
use tokio::net::UnixStream;
|
||||
#[cfg(unix)]
|
@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::v1::control::{watch_events_reply::Event, WatchEventsReply};
|
||||
use anyhow::Result;
|
||||
use krata::v1::control::{watch_events_reply::Event, WatchEventsReply};
|
||||
use log::trace;
|
||||
use tokio::{sync::broadcast, task::JoinHandle};
|
||||
use tokio_stream::StreamExt;
|
@ -1,10 +1,13 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use prost_reflect::DescriptorPool;
|
||||
|
||||
pub mod dial;
|
||||
pub mod launchcfg;
|
||||
pub mod v1;
|
||||
|
||||
pub mod client;
|
||||
pub mod dial;
|
||||
pub mod events;
|
||||
pub mod launchcfg;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod ethtool;
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use krata::v1::control::control_service_client::ControlServiceClient;
|
||||
use krata::{events::EventStream, v1::control::control_service_client::ControlServiceClient};
|
||||
|
||||
use tokio::select;
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::{console::StdioConsoleStream, events::EventStream};
|
||||
use crate::console::StdioConsoleStream;
|
||||
|
||||
use super::resolve_guest;
|
||||
|
||||
|
@ -1,17 +1,20 @@
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use krata::v1::{
|
||||
use krata::{
|
||||
events::EventStream,
|
||||
v1::{
|
||||
common::GuestStatus,
|
||||
control::{
|
||||
control_service_client::ControlServiceClient, watch_events_reply::Event,
|
||||
DestroyGuestRequest,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use log::error;
|
||||
use tonic::{transport::Channel, Request};
|
||||
|
||||
use crate::{cli::resolve_guest, events::EventStream};
|
||||
use crate::cli::resolve_guest;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct DestroyCommand {
|
||||
|
@ -2,20 +2,24 @@ use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use krata::v1::{
|
||||
use krata::{
|
||||
events::EventStream,
|
||||
v1::{
|
||||
common::{
|
||||
guest_image_spec::Image, GuestEnvVar, GuestImageSpec, GuestOciImageSpec, GuestSpec,
|
||||
GuestStatus,
|
||||
guest_image_spec::Image, GuestImageSpec, GuestOciImageSpec, GuestSpec, GuestStatus,
|
||||
GuestTaskSpec, GuestTaskSpecEnvVar,
|
||||
},
|
||||
control::{
|
||||
control_service_client::ControlServiceClient, watch_events_reply::Event, CreateGuestRequest,
|
||||
control_service_client::ControlServiceClient, watch_events_reply::Event,
|
||||
CreateGuestRequest,
|
||||
},
|
||||
},
|
||||
};
|
||||
use log::error;
|
||||
use tokio::select;
|
||||
use tonic::{transport::Channel, Request};
|
||||
|
||||
use crate::{console::StdioConsoleStream, events::EventStream};
|
||||
use crate::console::StdioConsoleStream;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct LauchCommand {
|
||||
@ -51,14 +55,17 @@ impl LauchCommand {
|
||||
}),
|
||||
vcpus: self.cpus,
|
||||
mem: self.mem,
|
||||
env: env_map(&self.env.unwrap_or_default())
|
||||
task: Some(GuestTaskSpec {
|
||||
environment: env_map(&self.env.unwrap_or_default())
|
||||
.iter()
|
||||
.map(|(key, value)| GuestEnvVar {
|
||||
.map(|(key, value)| GuestTaskSpecEnvVar {
|
||||
key: key.clone(),
|
||||
value: value.clone(),
|
||||
})
|
||||
.collect(),
|
||||
run: self.run,
|
||||
command: self.run,
|
||||
}),
|
||||
annotations: vec![],
|
||||
}),
|
||||
};
|
||||
let response = client
|
||||
|
@ -1,20 +1,20 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::{Parser, ValueEnum};
|
||||
use cli_tables::Table;
|
||||
use krata::v1::{
|
||||
use krata::{
|
||||
events::EventStream,
|
||||
v1::{
|
||||
common::{guest_image_spec::Image, Guest},
|
||||
control::{
|
||||
control_service_client::ControlServiceClient, ListGuestsRequest, ResolveGuestRequest,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use serde_json::Value;
|
||||
use tonic::{transport::Channel, Request};
|
||||
|
||||
use crate::{
|
||||
events::EventStream,
|
||||
format::{guest_state_text, kv2line, proto2dynamic, proto2kv},
|
||||
};
|
||||
use crate::format::{guest_state_text, kv2line, proto2dynamic, proto2kv};
|
||||
|
||||
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
|
||||
enum ListFormat {
|
||||
@ -106,13 +106,13 @@ impl ListCommand {
|
||||
.state
|
||||
.as_ref()
|
||||
.and_then(|x| x.network.as_ref())
|
||||
.map(|x| x.ipv4.as_str())
|
||||
.map(|x| x.guest_ipv4.as_str())
|
||||
.unwrap_or("unknown");
|
||||
let ipv6 = guest
|
||||
.state
|
||||
.as_ref()
|
||||
.and_then(|x| x.network.as_ref())
|
||||
.map(|x| x.ipv6.as_str())
|
||||
.map(|x| x.guest_ipv6.as_str())
|
||||
.unwrap_or("unknown");
|
||||
let Some(spec) = guest.spec else {
|
||||
continue;
|
||||
|
@ -7,13 +7,15 @@ pub mod watch;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
use krata::v1::control::{
|
||||
use krata::{
|
||||
client::ControlClientProvider,
|
||||
events::EventStream,
|
||||
v1::control::{
|
||||
control_service_client::ControlServiceClient, ResolveGuestRequest, WatchEventsRequest,
|
||||
},
|
||||
};
|
||||
use tonic::{transport::Channel, Request};
|
||||
|
||||
use crate::{client::ControlClientProvider, events::EventStream};
|
||||
|
||||
use self::{
|
||||
attach::AttachCommand, destroy::DestroyCommand, launch::LauchCommand, list::ListCommand,
|
||||
resolve::ResolveCommand, watch::WatchCommand,
|
||||
|
@ -1,13 +1,13 @@
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, ValueEnum};
|
||||
use krata::v1::{common::Guest, control::watch_events_reply::Event};
|
||||
use krata::{
|
||||
events::EventStream,
|
||||
v1::{common::Guest, control::watch_events_reply::Event},
|
||||
};
|
||||
use prost_reflect::ReflectMessage;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
events::EventStream,
|
||||
format::{guest_state_text, kv2line, proto2dynamic, proto2kv},
|
||||
};
|
||||
use crate::format::{guest_state_text, kv2line, proto2dynamic, proto2kv};
|
||||
|
||||
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
|
||||
enum WatchFormat {
|
||||
|
@ -4,9 +4,12 @@ use crossterm::{
|
||||
terminal::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled},
|
||||
tty::IsTty,
|
||||
};
|
||||
use krata::v1::{
|
||||
use krata::{
|
||||
events::EventStream,
|
||||
v1::{
|
||||
common::GuestStatus,
|
||||
control::{watch_events_reply::Event, ConsoleDataReply, ConsoleDataRequest},
|
||||
},
|
||||
};
|
||||
use log::debug;
|
||||
use tokio::{
|
||||
@ -16,8 +19,6 @@ use tokio::{
|
||||
use tokio_stream::{Stream, StreamExt};
|
||||
use tonic::Streaming;
|
||||
|
||||
use crate::events::EventStream;
|
||||
|
||||
pub struct StdioConsoleStream;
|
||||
|
||||
impl StdioConsoleStream {
|
||||
|
@ -1,5 +1,3 @@
|
||||
pub mod cli;
|
||||
pub mod client;
|
||||
pub mod console;
|
||||
pub mod events;
|
||||
pub mod format;
|
||||
|
@ -105,6 +105,7 @@ impl ControlService for RuntimeControlService {
|
||||
network: None,
|
||||
exit_info: None,
|
||||
error_info: None,
|
||||
domid: u32::MAX,
|
||||
}),
|
||||
spec: Some(spec),
|
||||
}),
|
||||
|
@ -124,6 +124,7 @@ impl DaemonEventGenerator {
|
||||
network: guest.state.clone().unwrap_or_default().network,
|
||||
exit_info: Some(GuestExitInfo { code }),
|
||||
error_info: None,
|
||||
domid: guest.state.clone().map(|x| x.domid).unwrap_or(u32::MAX),
|
||||
});
|
||||
|
||||
self.guests.update(id, entry).await?;
|
||||
|
@ -8,7 +8,7 @@ use krata::v1::{
|
||||
},
|
||||
control::GuestChangedEvent,
|
||||
};
|
||||
use kratart::{launch::GuestLaunchRequest, Runtime};
|
||||
use kratart::{launch::GuestLaunchRequest, GuestInfo, Runtime};
|
||||
use log::{error, info, trace, warn};
|
||||
use tokio::{select, sync::mpsc::Receiver, task::JoinHandle, time::sleep};
|
||||
use uuid::Uuid;
|
||||
@ -92,10 +92,7 @@ impl GuestReconciler {
|
||||
} else {
|
||||
state.status = GuestStatus::Started.into();
|
||||
}
|
||||
state.network = Some(GuestNetworkState {
|
||||
ipv4: runtime.ipv4.map(|x| x.ip().to_string()).unwrap_or_default(),
|
||||
ipv6: runtime.ipv6.map(|x| x.ip().to_string()).unwrap_or_default(),
|
||||
});
|
||||
state.network = Some(guestinfo_to_networkstate(runtime));
|
||||
stored_guest.state = Some(state);
|
||||
}
|
||||
}
|
||||
@ -187,6 +184,8 @@ impl GuestReconciler {
|
||||
}
|
||||
};
|
||||
|
||||
let task = spec.task.as_ref().cloned().unwrap_or_default();
|
||||
|
||||
let info = self
|
||||
.runtime
|
||||
.launch(GuestLaunchRequest {
|
||||
@ -199,24 +198,22 @@ impl GuestReconciler {
|
||||
image: &oci.image,
|
||||
vcpus: spec.vcpus,
|
||||
mem: spec.mem,
|
||||
env: spec
|
||||
.env
|
||||
env: task
|
||||
.environment
|
||||
.iter()
|
||||
.map(|x| (x.key.clone(), x.value.clone()))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
run: empty_vec_optional(spec.run.clone()),
|
||||
run: empty_vec_optional(task.command.clone()),
|
||||
debug: false,
|
||||
})
|
||||
.await?;
|
||||
info!("started guest {}", uuid);
|
||||
guest.state = Some(GuestState {
|
||||
status: GuestStatus::Started.into(),
|
||||
network: Some(GuestNetworkState {
|
||||
ipv4: info.ipv4.map(|x| x.ip().to_string()).unwrap_or_default(),
|
||||
ipv6: info.ipv6.map(|x| x.ip().to_string()).unwrap_or_default(),
|
||||
}),
|
||||
network: Some(guestinfo_to_networkstate(&info)),
|
||||
exit_info: None,
|
||||
error_info: None,
|
||||
domid: info.domid,
|
||||
});
|
||||
Ok(true)
|
||||
}
|
||||
@ -232,6 +229,7 @@ impl GuestReconciler {
|
||||
network: None,
|
||||
exit_info: None,
|
||||
error_info: None,
|
||||
domid: guest.state.as_ref().map(|x| x.domid).unwrap_or(u32::MAX),
|
||||
});
|
||||
Ok(true)
|
||||
}
|
||||
@ -244,3 +242,14 @@ fn empty_vec_optional<T>(value: Vec<T>) -> Option<Vec<T>> {
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn guestinfo_to_networkstate(info: &GuestInfo) -> GuestNetworkState {
|
||||
GuestNetworkState {
|
||||
guest_ipv4: info.guest_ipv4.map(|x| x.to_string()).unwrap_or_default(),
|
||||
guest_ipv6: info.guest_ipv6.map(|x| x.to_string()).unwrap_or_default(),
|
||||
guest_mac: info.guest_mac.as_ref().cloned().unwrap_or_default(),
|
||||
gateway_ipv4: info.gateway_ipv4.map(|x| x.to_string()).unwrap_or_default(),
|
||||
gateway_ipv6: info.gateway_ipv6.map(|x| x.to_string()).unwrap_or_default(),
|
||||
gateway_mac: info.gateway_mac.as_ref().cloned().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use env_logger::Env;
|
||||
use krataguest::init::GuestInit;
|
||||
use krataguest::{death, init::GuestInit};
|
||||
use log::error;
|
||||
use std::env;
|
||||
|
||||
#[tokio::main]
|
||||
@ -19,6 +20,9 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
let mut guest = GuestInit::new();
|
||||
guest.init().await?;
|
||||
if let Err(error) = guest.init().await {
|
||||
error!("failed to initialize guest: {}", error);
|
||||
death(127).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::childwait::{ChildEvent, ChildWait};
|
||||
use crate::{
|
||||
childwait::{ChildEvent, ChildWait},
|
||||
death,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use nix::{libc::c_int, unistd::Pid};
|
||||
use tokio::{select, time::sleep};
|
||||
use xenstore::{XsdClient, XsdInterface};
|
||||
use nix::unistd::Pid;
|
||||
use tokio::select;
|
||||
|
||||
pub struct GuestBackground {
|
||||
child: Pid,
|
||||
@ -35,19 +35,8 @@ impl GuestBackground {
|
||||
|
||||
async fn child_event(&mut self, event: ChildEvent) -> Result<()> {
|
||||
if event.pid == self.child {
|
||||
self.death(event.status).await?;
|
||||
death(event.status).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn death(&mut self, code: c_int) -> Result<()> {
|
||||
let store = XsdClient::open().await?;
|
||||
store
|
||||
.write_string("krata/guest/exit-code", &code.to_string())
|
||||
.await?;
|
||||
drop(store);
|
||||
loop {
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,20 @@
|
||||
use std::{os::raw::c_int, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::time::sleep;
|
||||
use xenstore::{XsdClient, XsdInterface};
|
||||
|
||||
pub mod background;
|
||||
pub mod childwait;
|
||||
pub mod init;
|
||||
|
||||
pub async fn death(code: c_int) -> Result<()> {
|
||||
let store = XsdClient::open().await?;
|
||||
store
|
||||
.write_string("krata/guest/exit-code", &code.to_string())
|
||||
.await?;
|
||||
drop(store);
|
||||
loop {
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
|
@ -13,15 +13,16 @@ clap = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
etherparse = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
krata = { path = "../krata" }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rtnetlink = { workspace = true }
|
||||
smoltcp = { workspace = true }
|
||||
tonic = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tokio-tun = { workspace = true }
|
||||
udp-stream = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
xenstore = { path = "../xen/xenstore" }
|
||||
|
||||
[lib]
|
||||
name = "kratanet"
|
||||
@ -33,7 +34,3 @@ path = "bin/network.rs"
|
||||
[[example]]
|
||||
name = "ping"
|
||||
path = "examples/ping.rs"
|
||||
|
||||
[[example]]
|
||||
name = "autonet"
|
||||
path = "examples/autonet.rs"
|
||||
|
@ -1,15 +1,22 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use env_logger::Env;
|
||||
use krata::dial::ControlDialAddress;
|
||||
use kratanet::NetworkService;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct NetworkArgs {}
|
||||
struct NetworkArgs {
|
||||
#[arg(short, long, default_value = "unix:///var/lib/krata/daemon.socket")]
|
||||
connection: String,
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
||||
let _ = NetworkArgs::parse();
|
||||
let mut service = NetworkService::new().await?;
|
||||
let args = NetworkArgs::parse();
|
||||
let control_dial_address = ControlDialAddress::from_str(&args.connection)?;
|
||||
let mut service = NetworkService::new(control_dial_address).await?;
|
||||
service.watch().await
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use kratanet::autonet::AutoNetworkCollector;
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let mut collector = AutoNetworkCollector::new().await?;
|
||||
loop {
|
||||
let changeset = collector.read_changes().await?;
|
||||
println!("{:?}", changeset);
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
}
|
@ -1,11 +1,24 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::Result;
|
||||
use krata::{
|
||||
events::EventStream,
|
||||
v1::{
|
||||
common::Guest,
|
||||
control::{
|
||||
control_service_client::ControlServiceClient, watch_events_reply::Event,
|
||||
ListGuestsRequest, WatchEventsRequest,
|
||||
},
|
||||
},
|
||||
};
|
||||
use log::warn;
|
||||
use smoltcp::wire::{EthernetAddress, Ipv4Cidr, Ipv6Cidr};
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
use std::{collections::HashMap, str::FromStr, time::Duration};
|
||||
use tokio::{select, sync::broadcast::Receiver, time::sleep};
|
||||
use tonic::transport::Channel;
|
||||
use uuid::Uuid;
|
||||
use xenstore::{XsdClient, XsdInterface, XsdTransaction};
|
||||
|
||||
pub struct AutoNetworkCollector {
|
||||
client: XsdClient,
|
||||
pub struct AutoNetworkWatcher {
|
||||
control: ControlServiceClient<Channel>,
|
||||
pub events: EventStream,
|
||||
known: HashMap<Uuid, NetworkMetadata>,
|
||||
}
|
||||
|
||||
@ -36,116 +49,88 @@ pub struct AutoNetworkChangeset {
|
||||
pub removed: Vec<NetworkMetadata>,
|
||||
}
|
||||
|
||||
impl AutoNetworkCollector {
|
||||
pub async fn new() -> Result<AutoNetworkCollector> {
|
||||
Ok(AutoNetworkCollector {
|
||||
client: XsdClient::open().await?,
|
||||
impl AutoNetworkWatcher {
|
||||
pub async fn new(mut control: ControlServiceClient<Channel>) -> Result<AutoNetworkWatcher> {
|
||||
let watch_events_response = control.watch_events(WatchEventsRequest {}).await?;
|
||||
|
||||
Ok(AutoNetworkWatcher {
|
||||
control,
|
||||
events: EventStream::open(watch_events_response.into_inner()).await?,
|
||||
known: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn read(&mut self) -> Result<Vec<NetworkMetadata>> {
|
||||
let mut networks = Vec::new();
|
||||
let tx = self.client.transaction().await?;
|
||||
for domid_string in tx.list("/local/domain").await? {
|
||||
let Ok(domid) = domid_string.parse::<u32>() else {
|
||||
let mut all_guests: HashMap<Uuid, Guest> = HashMap::new();
|
||||
for guest in self
|
||||
.control
|
||||
.list_guests(ListGuestsRequest {})
|
||||
.await?
|
||||
.into_inner()
|
||||
.guests
|
||||
{
|
||||
let Ok(uuid) = Uuid::from_str(&guest.id) else {
|
||||
continue;
|
||||
};
|
||||
all_guests.insert(uuid, guest);
|
||||
}
|
||||
|
||||
let mut networks: Vec<NetworkMetadata> = Vec::new();
|
||||
for (uuid, guest) in &all_guests {
|
||||
let Some(ref state) = guest.state else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let dom_path = format!("/local/domain/{}", domid_string);
|
||||
let Some(uuid_string) = tx.read_string(&format!("{}/krata/uuid", dom_path)).await?
|
||||
else {
|
||||
if state.domid == u32::MAX {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(ref network) = state.network else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(uuid) = uuid_string.parse::<Uuid>() else {
|
||||
let Ok(guest_ipv4_cidr) = Ipv4Cidr::from_str(&network.guest_ipv4) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(guest) =
|
||||
AutoNetworkCollector::read_network_side(uuid, &tx, &dom_path, "guest").await
|
||||
else {
|
||||
let Ok(guest_ipv6_cidr) = Ipv6Cidr::from_str(&network.guest_ipv6) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(gateway) =
|
||||
AutoNetworkCollector::read_network_side(uuid, &tx, &dom_path, "gateway").await
|
||||
else {
|
||||
let Ok(guest_mac) = EthernetAddress::from_str(&network.guest_mac) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(gateway_ipv4_cidr) = Ipv4Cidr::from_str(&network.gateway_ipv4) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(gateway_ipv6_cidr) = Ipv6Cidr::from_str(&network.gateway_ipv6) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(gateway_mac) = EthernetAddress::from_str(&network.gateway_mac) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
networks.push(NetworkMetadata {
|
||||
domid,
|
||||
uuid,
|
||||
guest,
|
||||
gateway,
|
||||
domid: state.domid,
|
||||
uuid: *uuid,
|
||||
guest: NetworkSide {
|
||||
ipv4: guest_ipv4_cidr,
|
||||
ipv6: guest_ipv6_cidr,
|
||||
mac: guest_mac,
|
||||
},
|
||||
gateway: NetworkSide {
|
||||
ipv4: gateway_ipv4_cidr,
|
||||
ipv6: gateway_ipv6_cidr,
|
||||
mac: gateway_mac,
|
||||
},
|
||||
});
|
||||
}
|
||||
tx.commit().await?;
|
||||
Ok(networks)
|
||||
}
|
||||
|
||||
async fn read_network_side(
|
||||
uuid: Uuid,
|
||||
tx: &XsdTransaction,
|
||||
dom_path: &str,
|
||||
side: &str,
|
||||
) -> Result<NetworkSide> {
|
||||
let side_path = format!("{}/krata/network/{}", dom_path, side);
|
||||
let Some(ipv4) = tx.read_string(&format!("{}/ipv4", side_path)).await? else {
|
||||
return Err(anyhow!(
|
||||
"krata domain {} is missing {} ipv4 network entry",
|
||||
uuid,
|
||||
side
|
||||
));
|
||||
};
|
||||
|
||||
let Some(ipv6) = tx.read_string(&format!("{}/ipv6", side_path)).await? else {
|
||||
return Err(anyhow!(
|
||||
"krata domain {} is missing {} ipv6 network entry",
|
||||
uuid,
|
||||
side
|
||||
));
|
||||
};
|
||||
|
||||
let Some(mac) = tx.read_string(&format!("{}/mac", side_path)).await? else {
|
||||
return Err(anyhow!(
|
||||
"krata domain {} is missing {} mac address entry",
|
||||
uuid,
|
||||
side
|
||||
));
|
||||
};
|
||||
|
||||
let Ok(ipv4) = Ipv4Cidr::from_str(&ipv4) else {
|
||||
return Err(anyhow!(
|
||||
"krata domain {} has invalid {} ipv4 network cidr entry: {}",
|
||||
uuid,
|
||||
side,
|
||||
ipv4
|
||||
));
|
||||
};
|
||||
|
||||
let Ok(ipv6) = Ipv6Cidr::from_str(&ipv6) else {
|
||||
return Err(anyhow!(
|
||||
"krata domain {} has invalid {} ipv6 network cidr entry: {}",
|
||||
uuid,
|
||||
side,
|
||||
ipv6
|
||||
));
|
||||
};
|
||||
|
||||
let Ok(mac) = EthernetAddress::from_str(&mac) else {
|
||||
return Err(anyhow!(
|
||||
"krata domain {} has invalid {} mac address entry: {}",
|
||||
uuid,
|
||||
side,
|
||||
mac
|
||||
));
|
||||
};
|
||||
|
||||
Ok(NetworkSide { ipv4, ipv6, mac })
|
||||
}
|
||||
|
||||
pub async fn read_changes(&mut self) -> Result<AutoNetworkChangeset> {
|
||||
let mut seen: Vec<Uuid> = Vec::new();
|
||||
let mut added: Vec<NetworkMetadata> = Vec::new();
|
||||
@ -179,6 +164,27 @@ impl AutoNetworkCollector {
|
||||
Ok(AutoNetworkChangeset { added, removed })
|
||||
}
|
||||
|
||||
pub async fn wait(&mut self, receiver: &mut Receiver<Event>) -> Result<()> {
|
||||
loop {
|
||||
select! {
|
||||
x = receiver.recv() => match x {
|
||||
Ok(Event::GuestChanged(_)) => {
|
||||
break;
|
||||
},
|
||||
|
||||
Err(error) => {
|
||||
warn!("failed to receive event: {}", error);
|
||||
}
|
||||
},
|
||||
|
||||
_ = sleep(Duration::from_secs(10)) => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mark_unknown(&mut self, uuid: Uuid) -> Result<bool> {
|
||||
Ok(self.known.remove(&uuid).is_some())
|
||||
}
|
||||
|
@ -1,11 +1,17 @@
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
use autonet::{AutoNetworkChangeset, AutoNetworkCollector, NetworkMetadata};
|
||||
use autonet::{AutoNetworkChangeset, AutoNetworkWatcher, NetworkMetadata};
|
||||
use futures::{future::join_all, TryFutureExt};
|
||||
use hbridge::HostBridge;
|
||||
use krata::{
|
||||
client::ControlClientProvider,
|
||||
dial::ControlDialAddress,
|
||||
v1::{common::Guest, control::control_service_client::ControlServiceClient},
|
||||
};
|
||||
use log::warn;
|
||||
use tokio::{task::JoinHandle, time::sleep};
|
||||
use tonic::transport::Channel;
|
||||
use uuid::Uuid;
|
||||
use vbridge::VirtualBridge;
|
||||
|
||||
@ -26,17 +32,22 @@ const HOST_BRIDGE_MTU: usize = 1500;
|
||||
pub const EXTRA_MTU: usize = 20;
|
||||
|
||||
pub struct NetworkService {
|
||||
pub control: ControlServiceClient<Channel>,
|
||||
pub guests: HashMap<Uuid, Guest>,
|
||||
pub backends: HashMap<Uuid, JoinHandle<()>>,
|
||||
pub bridge: VirtualBridge,
|
||||
pub hbridge: HostBridge,
|
||||
}
|
||||
|
||||
impl NetworkService {
|
||||
pub async fn new() -> Result<NetworkService> {
|
||||
pub async fn new(control_address: ControlDialAddress) -> Result<NetworkService> {
|
||||
let control = ControlClientProvider::dial(control_address).await?;
|
||||
let bridge = VirtualBridge::new()?;
|
||||
let hbridge =
|
||||
HostBridge::new(HOST_BRIDGE_MTU + EXTRA_MTU, "krata0".to_string(), &bridge).await?;
|
||||
Ok(NetworkService {
|
||||
control,
|
||||
guests: HashMap::new(),
|
||||
backends: HashMap::new(),
|
||||
bridge,
|
||||
hbridge,
|
||||
@ -46,18 +57,19 @@ impl NetworkService {
|
||||
|
||||
impl NetworkService {
|
||||
pub async fn watch(&mut self) -> Result<()> {
|
||||
let mut collector = AutoNetworkCollector::new().await?;
|
||||
let mut watcher = AutoNetworkWatcher::new(self.control.clone()).await?;
|
||||
let mut receiver = watcher.events.subscribe();
|
||||
loop {
|
||||
let changeset = collector.read_changes().await?;
|
||||
self.process_network_changeset(&mut collector, changeset)
|
||||
let changeset = watcher.read_changes().await?;
|
||||
self.process_network_changeset(&mut watcher, changeset)
|
||||
.await?;
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
watcher.wait(&mut receiver).await?;
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_network_changeset(
|
||||
&mut self,
|
||||
collector: &mut AutoNetworkCollector,
|
||||
collector: &mut AutoNetworkWatcher,
|
||||
changeset: AutoNetworkChangeset,
|
||||
) -> Result<()> {
|
||||
for removal in &changeset.removed {
|
||||
|
@ -20,7 +20,7 @@ impl NatHandlerContext {
|
||||
}
|
||||
|
||||
pub async fn reclaim(&self) -> Result<()> {
|
||||
self.reclaim_sender.try_send(self.key)?;
|
||||
let _ = self.reclaim_sender.try_send(self.key);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +266,12 @@ impl AsyncRawSocketChannel {
|
||||
if error.kind() == ErrorKind::WouldBlock {
|
||||
continue;
|
||||
}
|
||||
|
||||
// device no longer exists
|
||||
if error.raw_os_error() == Some(6) {
|
||||
break;
|
||||
}
|
||||
|
||||
return Err(anyhow!("failed to read from raw socket: {}", error));
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
use std::net::{IpAddr, Ipv6Addr};
|
||||
use std::{fs, net::Ipv4Addr, str::FromStr};
|
||||
|
||||
use advmac::MacAddr6;
|
||||
@ -113,7 +113,7 @@ impl GuestLauncher {
|
||||
];
|
||||
let cmdline = cmdline_options.join(" ");
|
||||
|
||||
let container_mac_string = container_mac.to_string().replace('-', ":");
|
||||
let guest_mac_string = container_mac.to_string().replace('-', ":");
|
||||
let gateway_mac_string = gateway_mac.to_string().replace('-', ":");
|
||||
|
||||
let mut extra_keys = vec![
|
||||
@ -140,7 +140,7 @@ impl GuestLauncher {
|
||||
),
|
||||
(
|
||||
"krata/network/guest/mac".to_string(),
|
||||
container_mac_string.clone(),
|
||||
guest_mac_string.clone(),
|
||||
),
|
||||
(
|
||||
"krata/network/gateway/ipv4".to_string(),
|
||||
@ -182,7 +182,7 @@ impl GuestLauncher {
|
||||
],
|
||||
consoles: vec![],
|
||||
vifs: vec![DomainNetworkInterface {
|
||||
mac: &container_mac_string,
|
||||
mac: &guest_mac_string,
|
||||
mtu: 1500,
|
||||
bridge: None,
|
||||
script: None,
|
||||
@ -199,14 +199,24 @@ impl GuestLauncher {
|
||||
domid,
|
||||
image: request.image.to_string(),
|
||||
loops: vec![],
|
||||
ipv4: Some(IpNetwork::new(
|
||||
guest_ipv4: Some(IpNetwork::new(
|
||||
IpAddr::V4(guest_ipv4),
|
||||
ipv4_network_mask as u8,
|
||||
)?),
|
||||
ipv6: Some(IpNetwork::new(
|
||||
guest_ipv6: Some(IpNetwork::new(
|
||||
IpAddr::V6(guest_ipv6),
|
||||
ipv6_network_mask as u8,
|
||||
)?),
|
||||
guest_mac: Some(guest_mac_string.clone()),
|
||||
gateway_ipv4: Some(IpNetwork::new(
|
||||
IpAddr::V4(Ipv4Addr::from_str(gateway_ipv4)?),
|
||||
ipv4_network_mask as u8,
|
||||
)?),
|
||||
gateway_ipv6: Some(IpNetwork::new(
|
||||
IpAddr::V6(Ipv6Addr::from_str(gateway_ipv6)?),
|
||||
ipv4_network_mask as u8,
|
||||
)?),
|
||||
gateway_mac: Some(gateway_mac_string.clone()),
|
||||
state: GuestState { exit_code: None },
|
||||
}),
|
||||
Err(error) => {
|
||||
|
@ -45,8 +45,12 @@ pub struct GuestInfo {
|
||||
pub domid: u32,
|
||||
pub image: String,
|
||||
pub loops: Vec<ContainerLoopInfo>,
|
||||
pub ipv4: Option<IpNetwork>,
|
||||
pub ipv6: Option<IpNetwork>,
|
||||
pub guest_ipv4: Option<IpNetwork>,
|
||||
pub guest_ipv6: Option<IpNetwork>,
|
||||
pub guest_mac: Option<String>,
|
||||
pub gateway_ipv4: Option<IpNetwork>,
|
||||
pub gateway_ipv6: Option<IpNetwork>,
|
||||
pub gateway_mac: Option<String>,
|
||||
pub state: GuestState,
|
||||
}
|
||||
|
||||
@ -130,25 +134,57 @@ impl RuntimeContext {
|
||||
.store
|
||||
.read_string(&format!("{}/krata/loops", &dom_path))
|
||||
.await?;
|
||||
let ipv4 = self
|
||||
let guest_ipv4 = self
|
||||
.xen
|
||||
.store
|
||||
.read_string(&format!("{}/krata/network/guest/ipv4", &dom_path))
|
||||
.await?;
|
||||
let ipv6 = self
|
||||
let guest_ipv6 = self
|
||||
.xen
|
||||
.store
|
||||
.read_string(&format!("{}/krata/network/guest/ipv6", &dom_path))
|
||||
.await?;
|
||||
let guest_mac = self
|
||||
.xen
|
||||
.store
|
||||
.read_string(&format!("{}/krata/network/guest/mac", &dom_path))
|
||||
.await?;
|
||||
let gateway_ipv4 = self
|
||||
.xen
|
||||
.store
|
||||
.read_string(&format!("{}/krata/network/gateway/ipv4", &dom_path))
|
||||
.await?;
|
||||
let gateway_ipv6 = self
|
||||
.xen
|
||||
.store
|
||||
.read_string(&format!("{}/krata/network/gateway/ipv6", &dom_path))
|
||||
.await?;
|
||||
let gateway_mac = self
|
||||
.xen
|
||||
.store
|
||||
.read_string(&format!("{}/krata/network/gateway/mac", &dom_path))
|
||||
.await?;
|
||||
|
||||
let ipv4 = if let Some(ipv4) = ipv4 {
|
||||
IpNetwork::from_str(&ipv4).ok()
|
||||
let guest_ipv4 = if let Some(guest_ipv4) = guest_ipv4 {
|
||||
IpNetwork::from_str(&guest_ipv4).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let ipv6 = if let Some(ipv6) = ipv6 {
|
||||
IpNetwork::from_str(&ipv6).ok()
|
||||
let guest_ipv6 = if let Some(guest_ipv6) = guest_ipv6 {
|
||||
IpNetwork::from_str(&guest_ipv6).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let gateway_ipv4 = if let Some(gateway_ipv4) = gateway_ipv4 {
|
||||
IpNetwork::from_str(&gateway_ipv4).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let gateway_ipv6 = if let Some(gateway_ipv6) = gateway_ipv6 {
|
||||
IpNetwork::from_str(&gateway_ipv6).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -173,8 +209,12 @@ impl RuntimeContext {
|
||||
domid,
|
||||
image,
|
||||
loops,
|
||||
ipv4,
|
||||
ipv6,
|
||||
guest_ipv4,
|
||||
guest_ipv6,
|
||||
guest_mac,
|
||||
gateway_ipv4,
|
||||
gateway_ipv6,
|
||||
gateway_mac,
|
||||
state,
|
||||
});
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ build_and_run() {
|
||||
if [ "${KRATA_BUILD_INITRD}" = "1" ]
|
||||
then
|
||||
TARGET_ARCH="$(./hack/build/arch.sh)"
|
||||
./hack/initrd/build.sh -q
|
||||
./hack/initrd/build.sh ${CARGO_BUILD_FLAGS}
|
||||
sudo cp "target/initrd/initrd-${TARGET_ARCH}" "/var/lib/krata/guest/initrd"
|
||||
fi
|
||||
RUST_TARGET="$(./hack/build/target.sh)"
|
||||
|
@ -17,8 +17,8 @@ message GuestSpec {
|
||||
GuestImageSpec image = 2;
|
||||
uint32 vcpus = 3;
|
||||
uint64 mem = 4;
|
||||
repeated GuestEnvVar env = 5;
|
||||
repeated string run = 6;
|
||||
GuestTaskSpec task = 5;
|
||||
repeated GuestSpecAnnotation annotations = 6;
|
||||
}
|
||||
|
||||
message GuestImageSpec {
|
||||
@ -31,7 +31,17 @@ message GuestOciImageSpec {
|
||||
string image = 1;
|
||||
}
|
||||
|
||||
message GuestEnvVar {
|
||||
message GuestTaskSpec {
|
||||
repeated GuestTaskSpecEnvVar environment = 1;
|
||||
repeated string command = 2;
|
||||
}
|
||||
|
||||
message GuestTaskSpecEnvVar {
|
||||
string key = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message GuestSpecAnnotation {
|
||||
string key = 1;
|
||||
string value = 2;
|
||||
}
|
||||
@ -41,6 +51,7 @@ message GuestState {
|
||||
GuestNetworkState network = 2;
|
||||
GuestExitInfo exit_info = 3;
|
||||
GuestErrorInfo error_info = 4;
|
||||
uint32 domid = 5;
|
||||
}
|
||||
|
||||
enum GuestStatus {
|
||||
@ -54,8 +65,12 @@ enum GuestStatus {
|
||||
}
|
||||
|
||||
message GuestNetworkState {
|
||||
string ipv4 = 1;
|
||||
string ipv6 = 2;
|
||||
string guest_ipv4 = 1;
|
||||
string guest_ipv6 = 2;
|
||||
string guest_mac = 3;
|
||||
string gateway_ipv4 = 4;
|
||||
string gateway_ipv6 = 5;
|
||||
string gateway_mac = 6;
|
||||
}
|
||||
|
||||
message GuestExitInfo {
|
||||
|
Loading…
Reference in New Issue
Block a user