From 19f35ef20a1cbc79435e0c8e83462ca82701d655 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Mon, 26 Aug 2024 15:05:57 -0400 Subject: [PATCH] feature(krata): implement network reservation list (#366) --- crates/ctl/src/cli/mod.rs | 5 + crates/ctl/src/cli/network/mod.rs | 43 ++++++ .../ctl/src/cli/network/reservation/list.rs | 125 ++++++++++++++++++ crates/ctl/src/cli/network/reservation/mod.rs | 43 ++++++ crates/daemon/src/control/get_host_status.rs | 10 +- .../src/control/list_network_reservations.rs | 28 ++++ crates/daemon/src/control/mod.rs | 28 +++- crates/daemon/src/db/mod.rs | 2 +- crates/daemon/src/db/{ip.rs => network.rs} | 52 +++++--- crates/daemon/src/lib.rs | 26 ++-- .../daemon/src/{ip => network}/assignment.rs | 54 ++++---- crates/daemon/src/{ip => network}/mod.rs | 0 crates/daemon/src/reconcile/zone/create.rs | 10 +- crates/daemon/src/reconcile/zone/mod.rs | 14 +- crates/krata/proto/krata/v1/common.proto | 10 ++ crates/krata/proto/krata/v1/control.proto | 8 ++ 16 files changed, 381 insertions(+), 77 deletions(-) create mode 100644 crates/ctl/src/cli/network/mod.rs create mode 100644 crates/ctl/src/cli/network/reservation/list.rs create mode 100644 crates/ctl/src/cli/network/reservation/mod.rs create mode 100644 crates/daemon/src/control/list_network_reservations.rs rename crates/daemon/src/db/{ip.rs => network.rs} (59%) rename crates/daemon/src/{ip => network}/assignment.rs (77%) rename crates/daemon/src/{ip => network}/mod.rs (100%) diff --git a/crates/ctl/src/cli/mod.rs b/crates/ctl/src/cli/mod.rs index 9da1f0a..6e3ac8a 100644 --- a/crates/ctl/src/cli/mod.rs +++ b/crates/ctl/src/cli/mod.rs @@ -1,6 +1,7 @@ pub mod device; pub mod host; pub mod image; +pub mod network; pub mod zone; use crate::cli::device::DeviceCommand; @@ -14,6 +15,7 @@ use krata::{ events::EventStream, v1::control::{control_service_client::ControlServiceClient, ResolveZoneIdRequest}, }; +use network::NetworkCommand; use tonic::{transport::Channel, Request}; #[derive(Parser)] @@ -36,6 +38,7 @@ pub struct ControlCommand { pub enum ControlCommands { Zone(ZoneCommand), Image(ImageCommand), + Network(NetworkCommand), Device(DeviceCommand), Host(HostCommand), } @@ -57,6 +60,8 @@ impl ControlCommands { match self { ControlCommands::Zone(zone) => zone.run(client, events).await, + ControlCommands::Network(network) => network.run(client, events).await, + ControlCommands::Image(image) => image.run(client, events).await, ControlCommands::Device(device) => device.run(client, events).await, diff --git a/crates/ctl/src/cli/network/mod.rs b/crates/ctl/src/cli/network/mod.rs new file mode 100644 index 0000000..5a692b2 --- /dev/null +++ b/crates/ctl/src/cli/network/mod.rs @@ -0,0 +1,43 @@ +use anyhow::Result; +use clap::{Parser, Subcommand}; +use reservation::NetworkReservationCommand; +use tonic::transport::Channel; + +use krata::events::EventStream; +use krata::v1::control::control_service_client::ControlServiceClient; + +pub mod reservation; + +#[derive(Parser)] +#[command(about = "Manage the network on the isolation engine")] +pub struct NetworkCommand { + #[command(subcommand)] + subcommand: NetworkCommands, +} + +impl NetworkCommand { + pub async fn run( + self, + client: ControlServiceClient, + events: EventStream, + ) -> Result<()> { + self.subcommand.run(client, events).await + } +} + +#[derive(Subcommand)] +pub enum NetworkCommands { + Reservation(NetworkReservationCommand), +} + +impl NetworkCommands { + pub async fn run( + self, + client: ControlServiceClient, + events: EventStream, + ) -> Result<()> { + match self { + NetworkCommands::Reservation(reservation) => reservation.run(client, events).await, + } + } +} diff --git a/crates/ctl/src/cli/network/reservation/list.rs b/crates/ctl/src/cli/network/reservation/list.rs new file mode 100644 index 0000000..eafc994 --- /dev/null +++ b/crates/ctl/src/cli/network/reservation/list.rs @@ -0,0 +1,125 @@ +use anyhow::Result; +use clap::{Parser, ValueEnum}; +use comfy_table::{presets::UTF8_FULL_CONDENSED, Cell, Table}; +use krata::{ + events::EventStream, + v1::{ + common::NetworkReservation, + control::{control_service_client::ControlServiceClient, ListNetworkReservationsRequest}, + }, +}; + +use serde_json::Value; +use tonic::transport::Channel; + +use crate::format::{kv2line, proto2dynamic, proto2kv}; + +#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)] +enum NetworkReservationListFormat { + Table, + Json, + JsonPretty, + Jsonl, + Yaml, + KeyValue, + Simple, +} + +#[derive(Parser)] +#[command(about = "List network reservation information")] +pub struct NetworkReservationListCommand { + #[arg(short, long, default_value = "table", help = "Output format")] + format: NetworkReservationListFormat, +} + +impl NetworkReservationListCommand { + pub async fn run( + self, + mut client: ControlServiceClient, + _events: EventStream, + ) -> Result<()> { + let reply = client + .list_network_reservations(ListNetworkReservationsRequest {}) + .await? + .into_inner(); + let mut reservations = reply.reservations; + + reservations.sort_by(|a, b| a.uuid.cmp(&b.uuid)); + + match self.format { + NetworkReservationListFormat::Table => { + self.print_reservations_table(reservations)?; + } + + NetworkReservationListFormat::Simple => { + for reservation in reservations { + println!( + "{}\t{}\t{}\t{}", + reservation.uuid, reservation.ipv4, reservation.ipv6, reservation.mac + ); + } + } + + NetworkReservationListFormat::Json + | NetworkReservationListFormat::JsonPretty + | NetworkReservationListFormat::Yaml => { + let mut values = Vec::new(); + for device in reservations { + let message = proto2dynamic(device)?; + values.push(serde_json::to_value(message)?); + } + let value = Value::Array(values); + let encoded = if self.format == NetworkReservationListFormat::JsonPretty { + serde_json::to_string_pretty(&value)? + } else if self.format == NetworkReservationListFormat::Yaml { + serde_yaml::to_string(&value)? + } else { + serde_json::to_string(&value)? + }; + println!("{}", encoded.trim()); + } + + NetworkReservationListFormat::Jsonl => { + for device in reservations { + let message = proto2dynamic(device)?; + println!("{}", serde_json::to_string(&message)?); + } + } + + NetworkReservationListFormat::KeyValue => { + self.print_key_value(reservations)?; + } + } + + Ok(()) + } + + fn print_reservations_table(&self, reservations: Vec) -> Result<()> { + let mut table = Table::new(); + table.load_preset(UTF8_FULL_CONDENSED); + table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic); + table.set_header(vec!["uuid", "ipv4", "ipv6", "mac"]); + for reservation in reservations { + table.add_row(vec![ + Cell::new(reservation.uuid), + Cell::new(reservation.ipv4), + Cell::new(reservation.ipv6), + Cell::new(reservation.mac), + ]); + } + if table.is_empty() { + println!("no network reservations found"); + } else { + println!("{}", table); + } + Ok(()) + } + + fn print_key_value(&self, reservations: Vec) -> Result<()> { + for reservation in reservations { + let kvs = proto2kv(reservation)?; + println!("{}", kv2line(kvs)); + } + Ok(()) + } +} diff --git a/crates/ctl/src/cli/network/reservation/mod.rs b/crates/ctl/src/cli/network/reservation/mod.rs new file mode 100644 index 0000000..ef13a9b --- /dev/null +++ b/crates/ctl/src/cli/network/reservation/mod.rs @@ -0,0 +1,43 @@ +use anyhow::Result; +use clap::{Parser, Subcommand}; +use list::NetworkReservationListCommand; +use tonic::transport::Channel; + +use krata::events::EventStream; +use krata::v1::control::control_service_client::ControlServiceClient; + +pub mod list; + +#[derive(Parser)] +#[command(about = "Manage network reservations")] +pub struct NetworkReservationCommand { + #[command(subcommand)] + subcommand: NetworkReservationCommands, +} + +impl NetworkReservationCommand { + pub async fn run( + self, + client: ControlServiceClient, + events: EventStream, + ) -> Result<()> { + self.subcommand.run(client, events).await + } +} + +#[derive(Subcommand)] +pub enum NetworkReservationCommands { + List(NetworkReservationListCommand), +} + +impl NetworkReservationCommands { + pub async fn run( + self, + client: ControlServiceClient, + events: EventStream, + ) -> Result<()> { + match self { + NetworkReservationCommands::List(list) => list.run(client, events).await, + } + } +} diff --git a/crates/daemon/src/control/get_host_status.rs b/crates/daemon/src/control/get_host_status.rs index c30d1d7..40774a3 100644 --- a/crates/daemon/src/control/get_host_status.rs +++ b/crates/daemon/src/control/get_host_status.rs @@ -1,21 +1,21 @@ use crate::command::DaemonCommand; -use crate::ip::assignment::IpAssignment; +use crate::network::assignment::NetworkAssignment; use crate::zlt::ZoneLookupTable; use anyhow::Result; use krata::v1::control::{GetHostStatusReply, GetHostStatusRequest}; pub struct GetHostStatusRpc { - ip: IpAssignment, + network: NetworkAssignment, zlt: ZoneLookupTable, } impl GetHostStatusRpc { - pub fn new(ip: IpAssignment, zlt: ZoneLookupTable) -> Self { - Self { ip, zlt } + pub fn new(ip: NetworkAssignment, zlt: ZoneLookupTable) -> Self { + Self { network: ip, zlt } } pub async fn process(self, _request: GetHostStatusRequest) -> Result { - let host_reservation = self.ip.retrieve(self.zlt.host_uuid()).await?; + let host_reservation = self.network.retrieve(self.zlt.host_uuid()).await?; Ok(GetHostStatusReply { host_domid: self.zlt.host_domid(), host_uuid: self.zlt.host_uuid().to_string(), diff --git a/crates/daemon/src/control/list_network_reservations.rs b/crates/daemon/src/control/list_network_reservations.rs new file mode 100644 index 0000000..940c469 --- /dev/null +++ b/crates/daemon/src/control/list_network_reservations.rs @@ -0,0 +1,28 @@ +use anyhow::Result; + +use krata::v1::{ + common::NetworkReservation, + control::{ListNetworkReservationsReply, ListNetworkReservationsRequest}, +}; + +use crate::network::assignment::NetworkAssignment; + +pub struct ListNetworkReservationsRpc { + network: NetworkAssignment, +} + +impl ListNetworkReservationsRpc { + pub fn new(network: NetworkAssignment) -> Self { + Self { network } + } + + pub async fn process( + self, + _request: ListNetworkReservationsRequest, + ) -> Result { + let state = self.network.read_reservations().await?; + let reservations: Vec = + state.into_values().map(|x| x.into()).collect::>(); + Ok(ListNetworkReservationsReply { reservations }) + } +} diff --git a/crates/daemon/src/control/mod.rs b/crates/daemon/src/control/mod.rs index 455d63c..f7ee43f 100644 --- a/crates/daemon/src/control/mod.rs +++ b/crates/daemon/src/control/mod.rs @@ -2,6 +2,7 @@ use std::pin::Pin; use anyhow::Error; use futures::Stream; +use list_network_reservations::ListNetworkReservationsRpc; use tokio::sync::mpsc::Sender; use tonic::{Request, Response, Status, Streaming}; use uuid::Uuid; @@ -17,8 +18,8 @@ use krata::v1::control::{ WatchEventsRequest, ZoneConsoleReply, ZoneConsoleRequest, }; use krata::v1::control::{ - GetZoneReply, GetZoneRequest, SetHostPowerManagementPolicyReply, - SetHostPowerManagementPolicyRequest, + GetZoneReply, GetZoneRequest, ListNetworkReservationsReply, ListNetworkReservationsRequest, + SetHostPowerManagementPolicyReply, SetHostPowerManagementPolicyRequest, }; use krataoci::packer::service::OciPackerService; use kratart::Runtime; @@ -41,7 +42,7 @@ use crate::control::snoop_idm::SnoopIdmRpc; use crate::control::update_zone_resources::UpdateZoneResourcesRpc; use crate::control::watch_events::WatchEventsRpc; use crate::db::zone::ZoneStore; -use crate::ip::assignment::IpAssignment; +use crate::network::assignment::NetworkAssignment; use crate::{ console::DaemonConsoleHandle, devices::DaemonDeviceManager, event::DaemonEventContext, idm::DaemonIdmHandle, zlt::ZoneLookupTable, @@ -55,6 +56,7 @@ pub mod get_host_cpu_topology; pub mod get_host_status; pub mod get_zone; pub mod list_devices; +pub mod list_network_reservations; pub mod list_zones; pub mod pull_image; pub mod read_hypervisor_console; @@ -91,7 +93,7 @@ pub struct DaemonControlService { console: DaemonConsoleHandle, idm: DaemonIdmHandle, zones: ZoneStore, - ip: IpAssignment, + network: NetworkAssignment, zone_reconciler_notify: Sender, packer: OciPackerService, runtime: Runtime, @@ -106,7 +108,7 @@ impl DaemonControlService { console: DaemonConsoleHandle, idm: DaemonIdmHandle, zones: ZoneStore, - ip: IpAssignment, + network: NetworkAssignment, zone_reconciler_notify: Sender, packer: OciPackerService, runtime: Runtime, @@ -118,7 +120,7 @@ impl DaemonControlService { console, idm, zones, - ip, + network, zone_reconciler_notify, packer, runtime, @@ -134,7 +136,7 @@ impl ControlService for DaemonControlService { ) -> Result, Status> { let request = request.into_inner(); adapt( - GetHostStatusRpc::new(self.ip.clone(), self.zlt.clone()) + GetHostStatusRpc::new(self.network.clone(), self.zlt.clone()) .process(request) .await, ) @@ -191,6 +193,18 @@ impl ControlService for DaemonControlService { ) } + async fn list_network_reservations( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + adapt( + ListNetworkReservationsRpc::new(self.network.clone()) + .process(request) + .await, + ) + } + type PullImageStream = Pin> + Send + 'static>>; diff --git a/crates/daemon/src/db/mod.rs b/crates/daemon/src/db/mod.rs index 5d2f70b..3fa509e 100644 --- a/crates/daemon/src/db/mod.rs +++ b/crates/daemon/src/db/mod.rs @@ -3,7 +3,7 @@ use redb::Database; use std::path::Path; use std::sync::Arc; -pub mod ip; +pub mod network; pub mod zone; #[derive(Clone)] diff --git a/crates/daemon/src/db/ip.rs b/crates/daemon/src/db/network.rs similarity index 59% rename from crates/daemon/src/db/ip.rs rename to crates/daemon/src/db/network.rs index d169d0d..fad726a 100644 --- a/crates/daemon/src/db/ip.rs +++ b/crates/daemon/src/db/network.rs @@ -1,6 +1,7 @@ use crate::db::KrataDatabase; use advmac::MacAddr6; use anyhow::Result; +use krata::v1::common::NetworkReservation as ApiNetworkReservation; use log::error; use redb::{ReadableTable, TableDefinition}; use serde::{Deserialize, Serialize}; @@ -8,24 +9,25 @@ use std::collections::HashMap; use std::net::{Ipv4Addr, Ipv6Addr}; use uuid::Uuid; -const IP_RESERVATION_TABLE: TableDefinition = TableDefinition::new("ip-reservation"); +const NETWORK_RESERVATION_TABLE: TableDefinition = + TableDefinition::new("network-reservation"); #[derive(Clone)] -pub struct IpReservationStore { +pub struct NetworkReservationStore { db: KrataDatabase, } -impl IpReservationStore { +impl NetworkReservationStore { pub fn open(db: KrataDatabase) -> Result { let write = db.database.begin_write()?; - let _ = write.open_table(IP_RESERVATION_TABLE); + let _ = write.open_table(NETWORK_RESERVATION_TABLE); write.commit()?; - Ok(IpReservationStore { db }) + Ok(NetworkReservationStore { db }) } - pub async fn read(&self, id: Uuid) -> Result> { + pub async fn read(&self, id: Uuid) -> Result> { let read = self.db.database.begin_read()?; - let table = read.open_table(IP_RESERVATION_TABLE)?; + let table = read.open_table(NETWORK_RESERVATION_TABLE)?; let Some(entry) = table.get(id.to_u128_le())? else { return Ok(None); }; @@ -33,26 +35,26 @@ impl IpReservationStore { Ok(Some(serde_json::from_slice(bytes)?)) } - pub async fn list(&self) -> Result> { + pub async fn list(&self) -> Result> { enum ListEntry { - Valid(Uuid, IpReservation), + Valid(Uuid, NetworkReservation), Invalid(Uuid), } - let mut reservations: HashMap = HashMap::new(); + let mut reservations: HashMap = HashMap::new(); let corruptions = { let read = self.db.database.begin_read()?; - let table = read.open_table(IP_RESERVATION_TABLE)?; + let table = read.open_table(NETWORK_RESERVATION_TABLE)?; table .iter()? .flat_map(|result| { result.map(|(key, value)| { let uuid = Uuid::from_u128_le(key.value()); - match serde_json::from_slice::(value.value()) { + match serde_json::from_slice::(value.value()) { Ok(reservation) => ListEntry::Valid(uuid, reservation), Err(error) => { error!( - "found invalid ip reservation in database for uuid {}: {}", + "found invalid network reservation in database for uuid {}: {}", uuid, error ); ListEntry::Invalid(uuid) @@ -73,7 +75,7 @@ impl IpReservationStore { if !corruptions.is_empty() { let write = self.db.database.begin_write()?; - let mut table = write.open_table(IP_RESERVATION_TABLE)?; + let mut table = write.open_table(NETWORK_RESERVATION_TABLE)?; for corruption in corruptions { table.remove(corruption.to_u128_le())?; } @@ -82,10 +84,10 @@ impl IpReservationStore { Ok(reservations) } - pub async fn update(&self, id: Uuid, entry: IpReservation) -> Result<()> { + pub async fn update(&self, id: Uuid, entry: NetworkReservation) -> Result<()> { let write = self.db.database.begin_write()?; { - let mut table = write.open_table(IP_RESERVATION_TABLE)?; + let mut table = write.open_table(NETWORK_RESERVATION_TABLE)?; let bytes = serde_json::to_vec(&entry)?; table.insert(id.to_u128_le(), bytes.as_slice())?; } @@ -96,7 +98,7 @@ impl IpReservationStore { pub async fn remove(&self, id: Uuid) -> Result<()> { let write = self.db.database.begin_write()?; { - let mut table = write.open_table(IP_RESERVATION_TABLE)?; + let mut table = write.open_table(NETWORK_RESERVATION_TABLE)?; table.remove(id.to_u128_le())?; } write.commit()?; @@ -105,7 +107,7 @@ impl IpReservationStore { } #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct IpReservation { +pub struct NetworkReservation { pub uuid: String, pub ipv4: Ipv4Addr, pub ipv6: Ipv6Addr, @@ -116,3 +118,17 @@ pub struct IpReservation { pub gateway_ipv6: Ipv6Addr, pub gateway_mac: MacAddr6, } + +impl From for ApiNetworkReservation { + fn from(val: NetworkReservation) -> Self { + ApiNetworkReservation { + uuid: val.uuid, + ipv4: format!("{}/{}", val.ipv4, val.ipv4_prefix), + ipv6: format!("{}/{}", val.ipv6, val.ipv6_prefix), + mac: val.mac.to_string().to_lowercase().replace('-', ":"), + gateway_ipv4: format!("{}/{}", val.gateway_ipv4, val.ipv4_prefix), + gateway_ipv6: format!("{}/{}", val.gateway_ipv6, val.ipv6_prefix), + gateway_mac: val.gateway_mac.to_string().to_lowercase().replace('-', ":"), + } + } +} diff --git a/crates/daemon/src/lib.rs b/crates/daemon/src/lib.rs index 455fc76..56c81ad 100644 --- a/crates/daemon/src/lib.rs +++ b/crates/daemon/src/lib.rs @@ -1,7 +1,7 @@ -use crate::db::ip::IpReservationStore; +use crate::db::network::NetworkReservationStore; use crate::db::zone::ZoneStore; use crate::db::KrataDatabase; -use crate::ip::assignment::IpAssignment; +use crate::network::assignment::NetworkAssignment; use anyhow::{anyhow, Result}; use config::DaemonConfig; use console::{DaemonConsole, DaemonConsoleHandle}; @@ -37,18 +37,19 @@ pub mod db; pub mod devices; pub mod event; pub mod idm; -pub mod ip; pub mod metrics; +pub mod network; pub mod oci; pub mod reconcile; pub mod zlt; + pub struct Daemon { store: String, _config: Arc, zlt: ZoneLookupTable, devices: DaemonDeviceManager, zones: ZoneStore, - ip: IpAssignment, + network: NetworkAssignment, events: DaemonEventContext, zone_reconciler_task: JoinHandle<()>, zone_reconciler_notify: Sender, @@ -127,9 +128,14 @@ impl Daemon { let runtime_for_reconciler = runtime.dupe().await?; let ipv4_network = Ipv4Network::from_str(&config.network.ipv4.subnet)?; let ipv6_network = Ipv6Network::from_str(&config.network.ipv6.subnet)?; - let ip_reservation_store = IpReservationStore::open(database)?; - let ip = - IpAssignment::new(host_uuid, ipv4_network, ipv6_network, ip_reservation_store).await?; + let network_reservation_store = NetworkReservationStore::open(database)?; + let network = NetworkAssignment::new( + host_uuid, + ipv4_network, + ipv6_network, + network_reservation_store, + ) + .await?; debug!("initializing zone reconciler"); let zone_reconciler = ZoneReconciler::new( devices.clone(), @@ -142,7 +148,7 @@ impl Daemon { kernel_path, initrd_path, addons_path, - ip.clone(), + network.clone(), config.clone(), )?; @@ -165,7 +171,7 @@ impl Daemon { zlt, devices, zones, - ip, + network, events, zone_reconciler_task, zone_reconciler_notify, @@ -186,7 +192,7 @@ impl Daemon { self.console.clone(), self.idm.clone(), self.zones.clone(), - self.ip.clone(), + self.network.clone(), self.zone_reconciler_notify.clone(), self.packer.clone(), self.runtime.clone(), diff --git a/crates/daemon/src/ip/assignment.rs b/crates/daemon/src/network/assignment.rs similarity index 77% rename from crates/daemon/src/ip/assignment.rs rename to crates/daemon/src/network/assignment.rs index 6528ec0..7053ff0 100644 --- a/crates/daemon/src/ip/assignment.rs +++ b/crates/daemon/src/network/assignment.rs @@ -9,37 +9,37 @@ use std::{ use tokio::sync::RwLock; use uuid::Uuid; -use crate::db::ip::{IpReservation, IpReservationStore}; +use crate::db::network::{NetworkReservation, NetworkReservationStore}; #[derive(Default, Clone)] -pub struct IpAssignmentState { - pub ipv4: HashMap, - pub ipv6: HashMap, +pub struct NetworkAssignmentState { + pub ipv4: HashMap, + pub ipv6: HashMap, } #[derive(Clone)] -pub struct IpAssignment { +pub struct NetworkAssignment { ipv4_network: Ipv4Network, ipv6_network: Ipv6Network, gateway_ipv4: Ipv4Addr, gateway_ipv6: Ipv6Addr, gateway_mac: MacAddr6, - store: IpReservationStore, - state: Arc>, + store: NetworkReservationStore, + state: Arc>, } -impl IpAssignment { +impl NetworkAssignment { pub async fn new( host_uuid: Uuid, ipv4_network: Ipv4Network, ipv6_network: Ipv6Network, - store: IpReservationStore, + store: NetworkReservationStore, ) -> Result { - let mut state = IpAssignment::fetch_current_state(&store).await?; + let mut state = NetworkAssignment::fetch_current_state(&store).await?; let gateway_reservation = if let Some(reservation) = store.read(Uuid::nil()).await? { reservation } else { - IpAssignment::allocate( + NetworkAssignment::allocate( &mut state, &store, Uuid::nil(), @@ -53,7 +53,7 @@ impl IpAssignment { }; if store.read(host_uuid).await?.is_none() { - let _ = IpAssignment::allocate( + let _ = NetworkAssignment::allocate( &mut state, &store, host_uuid, @@ -66,7 +66,7 @@ impl IpAssignment { .await?; } - let assignment = IpAssignment { + let assignment = NetworkAssignment { ipv4_network, ipv6_network, gateway_ipv4: gateway_reservation.ipv4, @@ -78,9 +78,11 @@ impl IpAssignment { Ok(assignment) } - async fn fetch_current_state(store: &IpReservationStore) -> Result { + async fn fetch_current_state( + store: &NetworkReservationStore, + ) -> Result { let reservations = store.list().await?; - let mut state = IpAssignmentState::default(); + let mut state = NetworkAssignmentState::default(); for reservation in reservations.values() { state.ipv4.insert(reservation.ipv4, reservation.clone()); state.ipv6.insert(reservation.ipv6, reservation.clone()); @@ -90,15 +92,15 @@ impl IpAssignment { #[allow(clippy::too_many_arguments)] async fn allocate( - state: &mut IpAssignmentState, - store: &IpReservationStore, + state: &mut NetworkAssignmentState, + store: &NetworkReservationStore, uuid: Uuid, ipv4_network: Ipv4Network, ipv6_network: Ipv6Network, gateway_ipv4: Option, gateway_ipv6: Option, gateway_mac: Option, - ) -> Result { + ) -> Result { let found_ipv4: Option = ipv4_network .iter() .filter(|ip| { @@ -136,7 +138,7 @@ impl IpAssignment { mac.set_local(true); mac.set_multicast(false); - let reservation = IpReservation { + let reservation = NetworkReservation { uuid: uuid.to_string(), ipv4, ipv6, @@ -153,9 +155,9 @@ impl IpAssignment { Ok(reservation) } - pub async fn assign(&self, uuid: Uuid) -> Result { + pub async fn assign(&self, uuid: Uuid) -> Result { let mut state = self.state.write().await; - let reservation = IpAssignment::allocate( + let reservation = NetworkAssignment::allocate( &mut state, &self.store, uuid, @@ -181,18 +183,22 @@ impl IpAssignment { Ok(()) } - pub async fn retrieve(&self, uuid: Uuid) -> Result> { + pub async fn retrieve(&self, uuid: Uuid) -> Result> { self.store.read(uuid).await } pub async fn reload(&self) -> Result<()> { let mut state = self.state.write().await; - let intermediate = IpAssignment::fetch_current_state(&self.store).await?; + let intermediate = NetworkAssignment::fetch_current_state(&self.store).await?; *state = intermediate; Ok(()) } - pub async fn read(&self) -> Result { + pub async fn read(&self) -> Result { Ok(self.state.read().await.clone()) } + + pub async fn read_reservations(&self) -> Result> { + self.store.list().await + } } diff --git a/crates/daemon/src/ip/mod.rs b/crates/daemon/src/network/mod.rs similarity index 100% rename from crates/daemon/src/ip/mod.rs rename to crates/daemon/src/network/mod.rs diff --git a/crates/daemon/src/reconcile/zone/create.rs b/crates/daemon/src/reconcile/zone/create.rs index d6c1917..90f2b56 100644 --- a/crates/daemon/src/reconcile/zone/create.rs +++ b/crates/daemon/src/reconcile/zone/create.rs @@ -14,8 +14,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use crate::config::{DaemonConfig, DaemonPciDeviceRdmReservePolicy}; use crate::devices::DaemonDeviceManager; -use crate::ip::assignment::IpAssignment; -use crate::reconcile::zone::ip_reservation_to_network_status; +use crate::network::assignment::NetworkAssignment; +use crate::reconcile::zone::network_reservation_to_network_status; use crate::{reconcile::zone::ZoneReconcilerResult, zlt::ZoneLookupTable}; use krata::v1::common::zone_image_spec::Image; use tokio::fs::{self, File}; @@ -29,7 +29,7 @@ pub struct ZoneCreator<'a> { pub initrd_path: &'a Path, pub addons_path: &'a Path, pub packer: &'a OciPackerService, - pub ip_assignment: &'a IpAssignment, + pub network_assignment: &'a NetworkAssignment, pub zlt: &'a ZoneLookupTable, pub runtime: &'a Runtime, pub config: &'a DaemonConfig, @@ -174,7 +174,7 @@ impl ZoneCreator<'_> { } } - let reservation = self.ip_assignment.assign(uuid).await?; + let reservation = self.network_assignment.assign(uuid).await?; let mut initial_resources = spec.initial_resources.unwrap_or_default(); if initial_resources.target_cpus < 1 { @@ -236,7 +236,7 @@ impl ZoneCreator<'_> { info!("created zone {}", uuid); zone.status = Some(ZoneStatus { state: ZoneState::Created.into(), - network_status: Some(ip_reservation_to_network_status(&reservation)), + network_status: Some(network_reservation_to_network_status(&reservation)), exit_status: None, error_status: None, resource_status: Some(ZoneResourceStatus { diff --git a/crates/daemon/src/reconcile/zone/mod.rs b/crates/daemon/src/reconcile/zone/mod.rs index fb9d01a..7c702c6 100644 --- a/crates/daemon/src/reconcile/zone/mod.rs +++ b/crates/daemon/src/reconcile/zone/mod.rs @@ -7,8 +7,8 @@ use std::{ use self::create::ZoneCreator; use crate::config::DaemonConfig; -use crate::db::ip::IpReservation; -use crate::ip::assignment::IpAssignment; +use crate::db::network::NetworkReservation; +use crate::network::assignment::NetworkAssignment; use crate::{ db::zone::ZoneStore, devices::DaemonDeviceManager, @@ -62,7 +62,7 @@ pub struct ZoneReconciler { tasks: Arc>>, zone_reconciler_notify: Sender, zone_reconcile_lock: Arc>, - ip_assignment: IpAssignment, + ip_assignment: NetworkAssignment, config: Arc, } @@ -79,7 +79,7 @@ impl ZoneReconciler { kernel_path: PathBuf, initrd_path: PathBuf, modules_path: PathBuf, - ip_assignment: IpAssignment, + ip_assignment: NetworkAssignment, config: Arc, ) -> Result { Ok(Self { @@ -195,7 +195,7 @@ impl ZoneReconciler { if let Some(reservation) = self.ip_assignment.retrieve(uuid).await? { status.network_status = - Some(ip_reservation_to_network_status(&reservation)); + Some(network_reservation_to_network_status(&reservation)); } stored_zone.status = Some(status); } @@ -286,7 +286,7 @@ impl ZoneReconciler { initrd_path: &self.initrd_path, addons_path: &self.addons_path, packer: &self.packer, - ip_assignment: &self.ip_assignment, + network_assignment: &self.ip_assignment, zlt: &self.zlt, runtime: &self.runtime, config: &self.config, @@ -369,7 +369,7 @@ impl ZoneReconciler { } } -pub fn ip_reservation_to_network_status(ip: &IpReservation) -> ZoneNetworkStatus { +pub fn network_reservation_to_network_status(ip: &NetworkReservation) -> ZoneNetworkStatus { ZoneNetworkStatus { zone_ipv4: format!("{}/{}", ip.ipv4, ip.ipv4_prefix), zone_ipv6: format!("{}/{}", ip.ipv6, ip.ipv6_prefix), diff --git a/crates/krata/proto/krata/v1/common.proto b/crates/krata/proto/krata/v1/common.proto index cc14d6a..1b098ab 100644 --- a/crates/krata/proto/krata/v1/common.proto +++ b/crates/krata/proto/krata/v1/common.proto @@ -139,3 +139,13 @@ message TerminalSize { uint32 rows = 1; uint32 columns = 2; } + +message NetworkReservation { + string uuid = 1; + string ipv4 = 2; + string ipv6 = 3; + string mac = 4; + string gateway_ipv4 = 5; + string gateway_ipv6 = 6; + string gateway_mac = 7; +} diff --git a/crates/krata/proto/krata/v1/control.proto b/crates/krata/proto/krata/v1/control.proto index eeca1d6..f9bd774 100644 --- a/crates/krata/proto/krata/v1/control.proto +++ b/crates/krata/proto/krata/v1/control.proto @@ -17,6 +17,8 @@ service ControlService { rpc ListDevices(ListDevicesRequest) returns (ListDevicesReply); + rpc ListNetworkReservations(ListNetworkReservationsRequest) returns (ListNetworkReservationsReply); + rpc PullImage(PullImageRequest) returns (stream PullImageReply); rpc CreateZone(CreateZoneRequest) returns (CreateZoneReply); @@ -265,3 +267,9 @@ message ReadHypervisorConsoleRequest {} message ReadHypervisorConsoleReply { string data = 1; } + +message ListNetworkReservationsRequest {} + +message ListNetworkReservationsReply { + repeated krata.v1.common.NetworkReservation reservations = 1; +}