diff --git a/crates/krata/Cargo.toml b/crates/krata/Cargo.toml index a9225ae..ee5ee3c 100644 --- a/crates/krata/Cargo.toml +++ b/crates/krata/Cargo.toml @@ -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] diff --git a/crates/kratactl/src/client.rs b/crates/krata/src/client.rs similarity index 96% rename from crates/kratactl/src/client.rs rename to crates/krata/src/client.rs index 7a0171f..33df667 100644 --- a/crates/kratactl/src/client.rs +++ b/crates/krata/src/client.rs @@ -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)] diff --git a/crates/kratactl/src/events.rs b/crates/krata/src/events.rs similarity index 95% rename from crates/kratactl/src/events.rs rename to crates/krata/src/events.rs index af20e99..9809175 100644 --- a/crates/kratactl/src/events.rs +++ b/crates/krata/src/events.rs @@ -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; diff --git a/crates/krata/src/lib.rs b/crates/krata/src/lib.rs index 75c578e..0e63c67 100644 --- a/crates/krata/src/lib.rs +++ b/crates/krata/src/lib.rs @@ -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; diff --git a/crates/kratactl/src/cli/attach.rs b/crates/kratactl/src/cli/attach.rs index f82eb93..4d57177 100644 --- a/crates/kratactl/src/cli/attach.rs +++ b/crates/kratactl/src/cli/attach.rs @@ -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; diff --git a/crates/kratactl/src/cli/destroy.rs b/crates/kratactl/src/cli/destroy.rs index f86a992..d6fd9b3 100644 --- a/crates/kratactl/src/cli/destroy.rs +++ b/crates/kratactl/src/cli/destroy.rs @@ -1,17 +1,20 @@ use anyhow::Result; use clap::Parser; -use krata::v1::{ - common::GuestStatus, - control::{ - control_service_client::ControlServiceClient, watch_events_reply::Event, - DestroyGuestRequest, +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 { diff --git a/crates/kratactl/src/cli/launch.rs b/crates/kratactl/src/cli/launch.rs index 547e162..5f3e607 100644 --- a/crates/kratactl/src/cli/launch.rs +++ b/crates/kratactl/src/cli/launch.rs @@ -2,20 +2,24 @@ use std::collections::HashMap; use anyhow::Result; use clap::Parser; -use krata::v1::{ - common::{ - guest_image_spec::Image, GuestEnvVar, GuestImageSpec, GuestOciImageSpec, GuestSpec, - GuestStatus, - }, - control::{ - control_service_client::ControlServiceClient, watch_events_reply::Event, CreateGuestRequest, +use krata::{ + events::EventStream, + v1::{ + common::{ + guest_image_spec::Image, GuestImageSpec, GuestOciImageSpec, GuestSpec, GuestStatus, + GuestTaskSpec, GuestTaskSpecEnvVar, + }, + control::{ + 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()) - .iter() - .map(|(key, value)| GuestEnvVar { - key: key.clone(), - value: value.clone(), - }) - .collect(), - run: self.run, + task: Some(GuestTaskSpec { + environment: env_map(&self.env.unwrap_or_default()) + .iter() + .map(|(key, value)| GuestTaskSpecEnvVar { + key: key.clone(), + value: value.clone(), + }) + .collect(), + command: self.run, + }), + annotations: vec![], }), }; let response = client diff --git a/crates/kratactl/src/cli/list.rs b/crates/kratactl/src/cli/list.rs index 09083db..4a0e83b 100644 --- a/crates/kratactl/src/cli/list.rs +++ b/crates/kratactl/src/cli/list.rs @@ -1,20 +1,20 @@ use anyhow::{anyhow, Result}; use clap::{Parser, ValueEnum}; use cli_tables::Table; -use krata::v1::{ - common::{guest_image_spec::Image, Guest}, - control::{ - control_service_client::ControlServiceClient, ListGuestsRequest, ResolveGuestRequest, +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; diff --git a/crates/kratactl/src/cli/mod.rs b/crates/kratactl/src/cli/mod.rs index 38ea970..9877b39 100644 --- a/crates/kratactl/src/cli/mod.rs +++ b/crates/kratactl/src/cli/mod.rs @@ -7,13 +7,15 @@ pub mod watch; use anyhow::{anyhow, Result}; use clap::{Parser, Subcommand}; -use krata::v1::control::{ - control_service_client::ControlServiceClient, ResolveGuestRequest, WatchEventsRequest, +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, diff --git a/crates/kratactl/src/cli/watch.rs b/crates/kratactl/src/cli/watch.rs index 0ec9fe7..1bce103 100644 --- a/crates/kratactl/src/cli/watch.rs +++ b/crates/kratactl/src/cli/watch.rs @@ -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 { diff --git a/crates/kratactl/src/console.rs b/crates/kratactl/src/console.rs index 149e6b8..ff46212 100644 --- a/crates/kratactl/src/console.rs +++ b/crates/kratactl/src/console.rs @@ -4,9 +4,12 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled}, tty::IsTty, }; -use krata::v1::{ - common::GuestStatus, - control::{watch_events_reply::Event, ConsoleDataReply, ConsoleDataRequest}, +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 { diff --git a/crates/kratactl/src/lib.rs b/crates/kratactl/src/lib.rs index 7d37b0a..1dde7a0 100644 --- a/crates/kratactl/src/lib.rs +++ b/crates/kratactl/src/lib.rs @@ -1,5 +1,3 @@ pub mod cli; -pub mod client; pub mod console; -pub mod events; pub mod format; diff --git a/crates/kratad/src/control.rs b/crates/kratad/src/control.rs index 91cfb73..5185bb8 100644 --- a/crates/kratad/src/control.rs +++ b/crates/kratad/src/control.rs @@ -105,6 +105,7 @@ impl ControlService for RuntimeControlService { network: None, exit_info: None, error_info: None, + domid: u32::MAX, }), spec: Some(spec), }), diff --git a/crates/kratad/src/event.rs b/crates/kratad/src/event.rs index 877b627..aecdec9 100644 --- a/crates/kratad/src/event.rs +++ b/crates/kratad/src/event.rs @@ -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?; diff --git a/crates/kratad/src/reconcile/guest.rs b/crates/kratad/src/reconcile/guest.rs index d381688..d48d391 100644 --- a/crates/kratad/src/reconcile/guest.rs +++ b/crates/kratad/src/reconcile/guest.rs @@ -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::>(), - 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(value: Vec) -> Option> { 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(), + } +} diff --git a/crates/krataguest/bin/init.rs b/crates/krataguest/bin/init.rs index 5d5d569..11d7b6c 100644 --- a/crates/krataguest/bin/init.rs +++ b/crates/krataguest/bin/init.rs @@ -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(()) } diff --git a/crates/krataguest/src/background.rs b/crates/krataguest/src/background.rs index fee48b5..5b4ff27 100644 --- a/crates/krataguest/src/background.rs +++ b/crates/krataguest/src/background.rs @@ -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; - } - } } diff --git a/crates/krataguest/src/lib.rs b/crates/krataguest/src/lib.rs index b29f859..cfddbce 100644 --- a/crates/krataguest/src/lib.rs +++ b/crates/krataguest/src/lib.rs @@ -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; + } +} diff --git a/crates/kratanet/Cargo.toml b/crates/kratanet/Cargo.toml index e1f37f3..9ddb505 100644 --- a/crates/kratanet/Cargo.toml +++ b/crates/kratanet/Cargo.toml @@ -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" diff --git a/crates/kratanet/bin/network.rs b/crates/kratanet/bin/network.rs index 4af5041..68f1359 100644 --- a/crates/kratanet/bin/network.rs +++ b/crates/kratanet/bin/network.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 } diff --git a/crates/kratanet/examples/autonet.rs b/crates/kratanet/examples/autonet.rs deleted file mode 100644 index 758a86f..0000000 --- a/crates/kratanet/examples/autonet.rs +++ /dev/null @@ -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; - } -} diff --git a/crates/kratanet/src/autonet.rs b/crates/kratanet/src/autonet.rs index f9adfd4..f6a5155 100644 --- a/crates/kratanet/src/autonet.rs +++ b/crates/kratanet/src/autonet.rs @@ -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, + pub events: EventStream, known: HashMap, } @@ -36,116 +49,88 @@ pub struct AutoNetworkChangeset { pub removed: Vec, } -impl AutoNetworkCollector { - pub async fn new() -> Result { - Ok(AutoNetworkCollector { - client: XsdClient::open().await?, +impl AutoNetworkWatcher { + pub async fn new(mut control: ControlServiceClient) -> Result { + 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> { - 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::() else { + let mut all_guests: HashMap = 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 = 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::() 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 { - 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 { let mut seen: Vec = Vec::new(); let mut added: Vec = Vec::new(); @@ -179,6 +164,27 @@ impl AutoNetworkCollector { Ok(AutoNetworkChangeset { added, removed }) } + pub async fn wait(&mut self, receiver: &mut Receiver) -> 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 { Ok(self.known.remove(&uuid).is_some()) } diff --git a/crates/kratanet/src/lib.rs b/crates/kratanet/src/lib.rs index 0d2b1aa..027da73 100644 --- a/crates/kratanet/src/lib.rs +++ b/crates/kratanet/src/lib.rs @@ -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, + pub guests: HashMap, pub backends: HashMap>, pub bridge: VirtualBridge, pub hbridge: HostBridge, } impl NetworkService { - pub async fn new() -> Result { + pub async fn new(control_address: ControlDialAddress) -> Result { + 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 { diff --git a/crates/kratanet/src/nat/handler.rs b/crates/kratanet/src/nat/handler.rs index e1943e8..1ce300b 100644 --- a/crates/kratanet/src/nat/handler.rs +++ b/crates/kratanet/src/nat/handler.rs @@ -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(()) } } diff --git a/crates/kratanet/src/raw_socket.rs b/crates/kratanet/src/raw_socket.rs index 043caa6..11bc854 100644 --- a/crates/kratanet/src/raw_socket.rs +++ b/crates/kratanet/src/raw_socket.rs @@ -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)); } }; diff --git a/crates/kratart/src/launch/mod.rs b/crates/kratart/src/launch/mod.rs index 4be2640..db97d2b 100644 --- a/crates/kratart/src/launch/mod.rs +++ b/crates/kratart/src/launch/mod.rs @@ -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) => { diff --git a/crates/kratart/src/lib.rs b/crates/kratart/src/lib.rs index ba64aaf..06085e5 100644 --- a/crates/kratart/src/lib.rs +++ b/crates/kratart/src/lib.rs @@ -45,8 +45,12 @@ pub struct GuestInfo { pub domid: u32, pub image: String, pub loops: Vec, - pub ipv4: Option, - pub ipv6: Option, + pub guest_ipv4: Option, + pub guest_ipv6: Option, + pub guest_mac: Option, + pub gateway_ipv4: Option, + pub gateway_ipv6: Option, + pub gateway_mac: Option, 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, }); } diff --git a/hack/debug/common.sh b/hack/debug/common.sh index 74258bb..f9c3683 100644 --- a/hack/debug/common.sh +++ b/hack/debug/common.sh @@ -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)" diff --git a/proto/krata/v1/common.proto b/proto/krata/v1/common.proto index 4c99569..9b5ddba 100644 --- a/proto/krata/v1/common.proto +++ b/proto/krata/v1/common.proto @@ -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 {