mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-03 21:21:32 +00:00
krata: event-based network backend startup and api enhancements
This commit is contained in:
@ -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));
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user