From f09b367d051641a693e72eef0c5a8bb8f1f372b4 Mon Sep 17 00:00:00 2001 From: Christien Rioux Date: Mon, 16 Dec 2024 20:26:43 -0500 Subject: [PATCH] [skip ci] network and blueprint code --- .../machine_registry_inner.rs | 34 ++- .../router_server/machine_registry/mod.rs | 3 +- .../machine_registry/state/availability.rs | 29 --- .../machine_registry/state/blueprint_state.rs | 199 +++++++++++++++- .../machine_registry/state/machine_state.rs | 60 +++-- .../machine_registry/state/mod.rs | 2 - .../machine_registry/state/network_state.rs | 223 +++++++++++------- .../machine_registry/state/state_allocator.rs | 10 +- .../machine_registry/state/template_state.rs | 190 ++++++++------- 9 files changed, 509 insertions(+), 241 deletions(-) delete mode 100644 veilid-tools/src/virtual_network/router_server/machine_registry/state/availability.rs diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/machine_registry_inner.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/machine_registry_inner.rs index ce57ba36..c5aa72a0 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/machine_registry_inner.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/machine_registry_inner.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use super::*; #[derive(Debug)] @@ -137,11 +135,9 @@ impl MachineRegistryInner { self.allocated_machines.remove(&id); } else { // Was a templated machine, so remove the machine state - let Some(machine_state) = self.machine_states().get_state(id)? else { - return Err(MachineRegistryError::InvalidId); - }; + let machine_state = self.machine_states().get_state(id)?; machine_state.release(self); - self.machine_states().release_id(id)?; + self.machine_states_mut().release_id(id)?; } Ok(()) @@ -150,19 +146,35 @@ impl MachineRegistryInner { /////////////////////////////////////////////////////////// /// Private Implementation - pub(super) fn profile_states(&mut self) -> &mut StateAllocator { + pub(super) fn profile_states(&self) -> &StateAllocator { + &self.profile_state_allocator + } + pub(super) fn machine_states(&self) -> &StateAllocator { + &self.machine_state_allocator + } + pub(super) fn template_states(&self) -> &StateAllocator { + &self.template_state_allocator + } + pub(super) fn network_states(&self) -> &StateAllocator { + &self.network_state_allocator + } + pub(super) fn blueprint_states(&self) -> &StateAllocator { + &self.blueprint_state_allocator + } + + pub(super) fn profile_states_mut(&mut self) -> &mut StateAllocator { &mut self.profile_state_allocator } - pub(super) fn machine_states(&mut self) -> &mut StateAllocator { + pub(super) fn machine_states_mut(&mut self) -> &mut StateAllocator { &mut self.machine_state_allocator } - pub(super) fn template_states(&mut self) -> &mut StateAllocator { + pub(super) fn template_states_mut(&mut self) -> &mut StateAllocator { &mut self.template_state_allocator } - pub(super) fn network_states(&mut self) -> &mut StateAllocator { + pub(super) fn network_states_mut(&mut self) -> &mut StateAllocator { &mut self.network_state_allocator } - pub(super) fn blueprint_states(&mut self) -> &mut StateAllocator { + pub(super) fn blueprint_states_mut(&mut self) -> &mut StateAllocator { &mut self.blueprint_state_allocator } diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/mod.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/mod.rs index 17129721..3cb3e014 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/mod.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/mod.rs @@ -3,7 +3,6 @@ mod machine_registry_inner; mod state; use super::*; -use ipnet::*; use address_pool::*; use machine_registry_inner::*; @@ -23,7 +22,7 @@ pub enum MachineRegistryError { InvalidId, InvalidName, AlreadyAttached, - AlreadyDetached, + NotAttached, DuplicateName, ProfileComplete, TemplateComplete, diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/availability.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/availability.rs deleted file mode 100644 index 4604bf67..00000000 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/availability.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::*; - -pub enum Availability { - None, - Existing(T), - New, -} - -impl Copy for Availability {} - -impl Clone for Availability { - fn clone(&self) -> Self { - match self { - Availability::None => Availability::None, - Availability::Existing(x) => Availability::Existing(x.clone()), - Availability::New => Availability::New, - } - } -} - -impl fmt::Debug for Availability { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Availability::None => f.write_str("None"), - Availability::Existing(x) => f.debug_tuple("Existing").field(x).finish(), - Availability::New => f.write_str("New"), - } - } -} diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/blueprint_state.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/blueprint_state.rs index 665ff729..ae26edc9 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/blueprint_state.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/blueprint_state.rs @@ -6,10 +6,56 @@ struct BlueprintStateUnlockedInner { name: String, } +#[derive(Debug)] +pub struct BlueprintStateIpv4Params { + pub allocation: Option, + pub additional_prefix: u8, + pub gateway: Option, +} + +#[derive(Debug)] +pub struct BlueprintStateIpv6Params { + pub allocation: Option, + pub additional_prefix: u8, + pub gateway: Option, +} + +#[derive(Debug)] +pub struct BlueprintStateGatewayParams { + pub translation: WeightedList, + pub upnp: Probability, + pub network: Option, +} + +#[derive(Debug)] +struct BlueprintStateIpv4 { + params: BlueprintStateIpv4Params, + gateway: Option, +} + +#[derive(Debug)] +struct BlueprintStateIpv6 { + params: BlueprintStateIpv6Params, + gateway: Option, +} + +#[derive(Debug)] +struct BlueprintStateIpv4Gateway { + params: BlueprintStateGatewayParams, +} + +#[derive(Debug)] +struct BlueprintStateIpv6Gateway { + params: BlueprintStateGatewayParams, +} + #[derive(Debug)] struct BlueprintStateInner { limit_network_count: Option, networks: Vec, + model: Option>, + ipv4: Option, + ipv6: Option, } #[derive(Debug, Clone)] @@ -27,6 +73,9 @@ impl BlueprintState { inner: Arc::new(Mutex::new(BlueprintStateInner { limit_network_count: None, networks: Vec::new(), + model: None, + ipv4: None, + ipv6: None, })), }) } @@ -36,6 +85,73 @@ impl BlueprintState { inner.limit_network_count = limit_network_count; } + pub fn set_model(&self, model: WeightedList) { + let mut inner = self.inner.lock(); + inner.model = Some(model); + } + + pub fn set_ipv4( + &self, + params: BlueprintStateIpv4Params, + gateway_params: Option, + ) { + let mut inner = self.inner.lock(); + if inner.ipv4.is_none() { + inner.ipv4 = Some(BlueprintStateIpv4 { + params, + gateway: None, + }); + } else { + inner.ipv4.as_mut().map(|ipv4| { + ipv4.params = params; + }); + } + let ipv4 = inner.ipv4.as_mut().expect("must exist"); + + if ipv4.gateway.is_some() { + if let Some(gateway_params) = gateway_params { + ipv4.gateway.as_mut().expect("must exist").params = gateway_params; + } else { + ipv4.gateway = None; + } + } else if let Some(gateway_params) = gateway_params { + ipv4.gateway = Some(BlueprintStateIpv4Gateway { + params: gateway_params, + }) + } + } + + pub fn set_ipv6( + &self, + params: BlueprintStateIpv6Params, + gateway_params: Option, + ) { + let mut inner = self.inner.lock(); + if inner.ipv6.is_none() { + inner.ipv6 = Some(BlueprintStateIpv6 { + params, + gateway: None, + }); + } else { + inner.ipv6.as_mut().map(|ipv6| { + ipv6.params = params; + }); + } + let ipv6 = inner.ipv6.as_mut().expect("must exist"); + + if ipv6.gateway.is_some() { + if let Some(gateway_params) = gateway_params { + ipv6.gateway.as_mut().expect("must exist").params = gateway_params; + } else { + ipv6.gateway = None; + } + } else if let Some(gateway_params) = gateway_params { + ipv6.gateway = Some(BlueprintStateIpv6Gateway { + params: gateway_params, + }) + } + } + pub fn is_active(&self) -> MachineRegistryResult { let inner = self.inner.lock(); @@ -49,16 +165,77 @@ impl BlueprintState { Ok(true) } - pub fn generate( - &self, - machine_registry_inner: &MachineRegistryInner, - ) -> MachineRegistryResult { + fn generate_model_inner( + inner: &mut BlueprintStateInner, + machine_registry_inner: &mut MachineRegistryInner, + network_state: NetworkState, + ) -> MachineRegistryResult<()> { + xxx do generation + } + + fn generate_ipv4_inner( + inner: &mut BlueprintStateInner, + machine_registry_inner: &mut MachineRegistryInner, + network_state: NetworkState, + ) -> MachineRegistryResult<()> { // } - pub fn for_each_network_id(&self, mut callback: F) -> MachineRegistryResult> + fn generate_ipv6_inner( + inner: &mut BlueprintStateInner, + machine_registry_inner: &mut MachineRegistryInner, + network_state: NetworkState, + ) -> MachineRegistryResult<()> { + // + } + + pub fn generate( + &self, + machine_registry_inner: &mut MachineRegistryInner, + ) -> MachineRegistryResult { + let mut inner = self.inner.lock(); + + // See if there's room for another network + if let Some(limit_network_count) = inner.limit_network_count { + if inner.networks.len() >= limit_network_count { + return Err(MachineRegistryError::BlueprintComplete); + } + } + + // Allocate a network id + let network_state_id = machine_registry_inner.network_states_mut().allocate_id(); + + // Create an anonymous network state + let network_state = NetworkState::new(network_state_id, None); + + if let Err(e) = (|| { + Self::generate_model_inner(&mut *inner, machine_registry_inner, network_state.clone())?; + Self::generate_ipv4_inner(&mut *inner, machine_registry_inner, network_state.clone())?; + Self::generate_ipv6_inner(&mut *inner, machine_registry_inner, network_state.clone())?; + Ok(()) + })() { + // Release the network state and id if things failed to allocate + network_state.release(machine_registry_inner); + machine_registry_inner + .network_states_mut() + .release_id(network_state_id); + return Err(e); + } + + // Attach the state to the id + machine_registry_inner + .network_states_mut() + .attach_state(network_state.clone()); + + // Record the newly instantiated network + inner.networks.push(network_state_id); + + Ok(network_state) + } + + pub fn for_each_network_id(&self, callback: F) -> MachineRegistryResult> where - F: FnMut(NetworkStateId) -> MachineRegistryResult>, + F: Fn(NetworkStateId) -> MachineRegistryResult>, { let inner = self.inner.lock(); for network_id in &inner.networks { @@ -68,6 +245,16 @@ impl BlueprintState { } Ok(None) } + + pub fn on_network_released(&self, network_id: NetworkStateId) { + let mut inner = self.inner.lock(); + let pos = inner + .networks + .iter() + .position(|id| *id == network_id) + .expect("must exist"); + inner.networks.remove(pos); + } } impl State for BlueprintState { diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/machine_state.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/machine_state.rs index e22458e7..48b6159c 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/machine_state.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/machine_state.rs @@ -2,8 +2,14 @@ use super::*; #[derive(Debug)] struct MachineStateInner { + /// The template generating this machine + generating_template: Option, /// The current network interfaces definition interfaces: HashMap, + /// Capabilities to disable on this machine + disable_capabilities: Vec, + /// If this machine is a bootstrap + bootstrap: bool, } #[derive(Debug)] @@ -36,19 +42,45 @@ impl MachineState { Self { unlocked_inner: Arc::new(MachineStateUnlockedInner { id, opt_name }), inner: Arc::new(Mutex::new(MachineStateInner { + generating_template: None, interfaces: HashMap::new(), + disable_capabilities: Vec::new(), + bootstrap: false, })), } } pub fn release(&self, machine_registry_inner: &mut MachineRegistryInner) { self.release_all_interfaces(machine_registry_inner); + let inner = self.inner.lock(); + if let Some(generating_template) = inner.generating_template { + let template_state = machine_registry_inner + .template_states_mut() + .get_state(generating_template) + .expect("must exist"); + template_state.on_machine_released(self.id()); + } } pub fn external_id(&self) -> MachineId { self.unlocked_inner.id.0 } + pub fn set_generating_template(&self, generating_template: TemplateStateId) { + let mut inner = self.inner.lock(); + inner.generating_template = Some(generating_template); + } + + pub fn set_disable_capabilities(&self, disable_capabilities: Vec) { + let mut inner = self.inner.lock(); + inner.disable_capabilities = disable_capabilities; + } + + pub fn set_bootstrap(&self, bootstrap: bool) { + let mut inner = self.inner.lock(); + inner.bootstrap = bootstrap + } + fn next_free_interface_name_inner(inner: &MachineStateInner) -> String { let mut inum = 0usize; loop { @@ -111,12 +143,9 @@ impl MachineState { }; // Get the network state - let Some(network_state) = machine_registry_inner + let network_state = machine_registry_inner .network_states() - .get_state(intf.network_id)? - else { - return Err(MachineRegistryError::NetworkNotFound); - }; + .get_state(intf.network_id)?; // Allocate interface address let is_dynamic = opt_address.is_none(); @@ -151,12 +180,9 @@ impl MachineState { }; // Get the network state - let Some(network_state) = machine_registry_inner + let network_state = machine_registry_inner .network_states() - .get_state(intf.network_id)? - else { - return Err(MachineRegistryError::NetworkNotFound); - }; + .get_state(intf.network_id)?; // Allocate interface address let is_dynamic = opt_address.is_none(); @@ -190,12 +216,9 @@ impl MachineState { }; // Get the network state - let Some(network_state) = machine_registry_inner + let network_state = machine_registry_inner .network_states() - .get_state(intf.network_id)? - else { - return Err(MachineRegistryError::NetworkNotFound); - }; + .get_state(intf.network_id)?; // Release the address from the network match address { @@ -230,12 +253,9 @@ impl MachineState { }; // Get the network state - let Some(network_state) = machine_registry_inner + let network_state = machine_registry_inner .network_states() - .get_state(intf.network_id)? - else { - return Err(MachineRegistryError::NetworkNotFound); - }; + .get_state(intf.network_id)?; // Release the addresses from the network for addr in &intf.network_interface.addrs { diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/mod.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/mod.rs index afc59525..c952f2da 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/mod.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/mod.rs @@ -1,4 +1,3 @@ -mod availability; mod blueprint_state; mod machine_state; mod network_state; @@ -10,7 +9,6 @@ use std::marker::PhantomData; use super::*; -pub use availability::*; pub use blueprint_state::*; pub use machine_state::*; pub use network_state::*; diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/network_state.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/network_state.rs index 65fd4adb..f5c9966e 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/network_state.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/network_state.rs @@ -11,12 +11,10 @@ struct NetworkStateUnlockedInner { #[derive(Debug)] struct NetworkStateInner { - /// Network latency distribution - latency: Option, - /// Distance simulation metric - distance: Option, - /// Packet loss probability - loss: Probability, + /// The blueprint generating this network + generating_blueprint: Option, + /// Model for this network + model: NetworkStateModel, /// IPv4 state if it is enabled ipv4: Option, /// IPv6 state if it is enabled @@ -24,26 +22,57 @@ struct NetworkStateInner { } #[derive(Debug)] -struct NetworkStateIpv4 { +struct NetworkStateModel { + /// Network latency distribution + latency: Option, + /// Distance simulation metric + distance: Option, + /// Packet loss probability + loss: Probability, +} + +#[derive(Debug)] +struct NetworkStateIpv4Params { allocation: Ipv4Net, - gateway: Option, +} + +#[derive(Debug)] +struct NetworkStateIpv4 { + params: NetworkStateIpv4Params, + gateway: Option, machine_addresses: HashMap, } #[derive(Debug)] -struct NetworkStateIpv6 { +struct NetworkStateIpv6Params { allocation: Ipv6Net, - gateway: Option, +} +#[derive(Debug)] +struct NetworkStateIpv6 { + params: NetworkStateIpv6Params, + gateway: Option, machine_addresses: HashMap, } #[derive(Debug)] -struct NetworkGatewayState { - translation: config::Translation, // xxx replace with translation state +struct NetworkStateGatewayParams { + translation: config::Translation, upnp: bool, network: Option, } +#[derive(Debug)] +struct NetworkStateIpv4Gateway { + params: NetworkStateGatewayParams, + // xxx add translation state +} + +#[derive(Debug)] +struct NetworkStateIpv6Gateway { + params: NetworkStateGatewayParams, + // xxx add translation state +} + #[derive(Debug, Clone)] pub struct NetworkState { unlocked_inner: Arc, @@ -57,79 +86,101 @@ impl NetworkState { Self { unlocked_inner: Arc::new(NetworkStateUnlockedInner { id, opt_name }), inner: Arc::new(Mutex::new(NetworkStateInner { - latency: None, - distance: None, - loss: 0.0, + generating_blueprint: None, + model: NetworkStateModel { + latency: None, + distance: None, + loss: 0.0, + }, ipv4: None, ipv6: None, })), } } - // let model = network_def.model.clone().unwrap_or_else(|| { - // machine_registry_inner - // .unlocked_inner - // .config - // .default_model - // .clone() - // }); - // let ipv4 = match network_def.ipv4.as_ref() { - // Some(ipv4) => Some(NetworkStateIpv4 { - // allocation: machine_registry_inner.choose_allocation_v4( - // machine_registry_inner - // .unlocked_inner - // .config - // .allocations - // .get(&ipv4.allocation) - // .cloned() - // .ok_or(MachineRegistryError::InvalidAllocationName)?, - // 0, - // )?, - // gateway: match ipv4.gateway.as_ref() { - // Some(v4gw) => Some(NetworkGatewayState { - // translation: v4gw.translation, - // upnp: v4gw.upnp, - // network: v4gw.network.clone().map(|gwname| { - // machine_registry_inner - // .resolve_to_manager_network - // .add(gwname) - // }), - // }), - // None => None, - // }, - // machine_addresses: HashMap::new(), - // }), - // None => None, - // }; + pub fn release(&self, machine_registry_inner: &mut MachineRegistryInner) { + let inner = self.inner.lock(); + if let Some(generating_blueprint) = inner.generating_blueprint { + let blueprint_state = machine_registry_inner + .blueprint_states_mut() + .get_state(generating_blueprint) + .expect("must exist"); + blueprint_state.on_network_released(self.id()); + } + } - // let ipv6 = match network_def.ipv6.as_ref() { - // Some(ipv6) => Some(NetworkStateIpv6 { - // allocation: machine_registry_inner.choose_allocation_v6( - // machine_registry_inner - // .unlocked_inner - // .config - // .allocations - // .get(&ipv6.allocation) - // .cloned() - // .ok_or(MachineRegistryError::InvalidAllocationName)?, - // 0, - // )?, - // gateway: match ipv6.gateway.as_ref() { - // Some(v6gw) => Some(NetworkGatewayState { - // translation: v6gw.translation, - // upnp: v6gw.upnp, - // network: v6gw.network.clone().map(|gwname| { - // machine_registry_inner - // .resolve_to_manager_network - // .add(gwname) - // }), - // }), - // None => None, - // }, - // machine_addresses: HashMap::new(), - // }), - // None => None, - // }; + pub fn set_model(&self, model: NetworkStateModel) { + let mut inner = self.inner.lock(); + inner.model = model; + } + + pub fn set_ipv4( + &self, + params: NetworkStateIpv4Params, + gateway_params: Option, + ) { + let mut inner = self.inner.lock(); + if inner.ipv4.is_none() { + inner.ipv4 = Some(NetworkStateIpv4 { + params, + gateway: None, + machine_addresses: HashMap::new(), + }); + } else { + inner.ipv4.as_mut().map(|ipv4| { + ipv4.params = params; + }); + } + let ipv4 = inner.ipv4.as_mut().expect("must exist"); + + if ipv4.gateway.is_some() { + if let Some(gateway_params) = gateway_params { + ipv4.gateway.as_mut().expect("must exist").params = gateway_params; + } else { + ipv4.gateway = None; + } + } else if let Some(gateway_params) = gateway_params { + ipv4.gateway = Some(NetworkStateIpv4Gateway { + params: gateway_params, + }) + } + } + pub fn set_ipv6( + &self, + params: NetworkStateIpv6Params, + gateway_params: Option, + ) { + let mut inner = self.inner.lock(); + if inner.ipv6.is_none() { + inner.ipv6 = Some(NetworkStateIpv6 { + params, + gateway: None, + machine_addresses: HashMap::new(), + }); + } else { + inner.ipv6.as_mut().map(|ipv6| { + ipv6.params = params; + }); + } + let ipv6 = inner.ipv6.as_mut().expect("must exist"); + + if ipv6.gateway.is_some() { + if let Some(gateway_params) = gateway_params { + ipv6.gateway.as_mut().expect("must exist").params = gateway_params; + } else { + ipv6.gateway = None; + } + } else if let Some(gateway_params) = gateway_params { + ipv6.gateway = Some(NetworkStateIpv6Gateway { + params: gateway_params, + }) + } + } + + pub fn set_generating_blueprint(&self, generating_blueprint: BlueprintStateId) { + let mut inner = self.inner.lock(); + inner.generating_blueprint = Some(generating_blueprint); + } pub fn is_ipv4(&self) -> bool { self.inner.lock().ipv4.is_some() @@ -143,7 +194,7 @@ impl NetworkState { let inner = self.inner.lock(); let mut can_allocate = false; if let Some(network_state_ipv4) = &inner.ipv4 { - let hosts_range = network_state_ipv4.allocation.hosts(); + let hosts_range = network_state_ipv4.params.allocation.hosts(); let Some(first_host) = std::iter::Iterator::min(hosts_range) else { return Err(MachineRegistryError::NoAllocation); }; @@ -160,7 +211,7 @@ impl NetworkState { } }; if let Some(network_state_ipv6) = &inner.ipv6 { - let hosts_range = network_state_ipv6.allocation.hosts(); + let hosts_range = network_state_ipv6.params.allocation.hosts(); let Some(first_host) = std::iter::Iterator::min(hosts_range) else { return Err(MachineRegistryError::NoAllocation); }; @@ -194,7 +245,7 @@ impl NetworkState { // for now we just pick randomly and then increment until we find a free allocation // not enough addresses in any address space are allocated to warrant a more efficient algorithm - let hosts_range = network_state_ipv4.allocation.hosts(); + let hosts_range = network_state_ipv4.params.allocation.hosts(); let Some(first_host) = std::iter::Iterator::min(hosts_range) else { return Err(MachineRegistryError::NoAllocation); }; @@ -245,8 +296,8 @@ impl NetworkState { // Make interface address let ifaddr = Ifv4Addr { ip, - netmask: network_state_ipv4.allocation.netmask(), - broadcast: Some(network_state_ipv4.allocation.broadcast()), + netmask: network_state_ipv4.params.allocation.netmask(), + broadcast: Some(network_state_ipv4.params.allocation.broadcast()), }; Ok(ifaddr) @@ -284,7 +335,7 @@ impl NetworkState { // for now we just pick randomly and then increment until we find a free allocation // not enough addresses in any address space are allocated to warrant a more efficient algorithm - let hosts_range = network_state_ipv6.allocation.hosts(); + let hosts_range = network_state_ipv6.params.allocation.hosts(); let Some(first_host) = std::iter::Iterator::min(hosts_range) else { return Err(MachineRegistryError::NoAllocation); }; @@ -335,8 +386,8 @@ impl NetworkState { // Make interface address let ifaddr = Ifv6Addr { ip, - netmask: network_state_ipv6.allocation.netmask(), - broadcast: Some(network_state_ipv6.allocation.broadcast()), + netmask: network_state_ipv6.params.allocation.netmask(), + broadcast: Some(network_state_ipv6.params.allocation.broadcast()), }; Ok(ifaddr) diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/state_allocator.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/state_allocator.rs index 00739412..4d7c8070 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/state_allocator.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/state_allocator.rs @@ -147,7 +147,7 @@ impl StateAllocator { // Take the state out of the slot and ensure the state slot isn't detached already let Some(state) = opt_state.take() else { - return Err(MachineRegistryError::AlreadyDetached); + return Err(MachineRegistryError::NotAttached); }; // Release the name if it exists @@ -162,13 +162,15 @@ impl StateAllocator { Ok(state) } - pub fn get_state(&self, id: StateId) -> MachineRegistryResult> { + pub fn get_state(&self, id: StateId) -> MachineRegistryResult { // Get the allocator slot let Some(opt_state) = self.state_by_id.get(&id.0).cloned() else { return Err(MachineRegistryError::InvalidId); }; - - Ok(opt_state) + let Some(state) = opt_state else { + return Err(MachineRegistryError::NotAttached); + }; + Ok(state) } pub fn get_state_by_name(&self, name: &String) -> Option { diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/template_state.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/template_state.rs index 51c377a0..6b25e99f 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/template_state.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/template_state.rs @@ -22,6 +22,12 @@ enum LocationsList { }, } +#[derive(Debug, Clone)] +enum BlueprintAvailability { + Existing(NetworkState), + Generate(BlueprintState), +} + #[derive(Debug)] struct TemplateStateInner { limit_machine_count: Option, @@ -29,6 +35,7 @@ struct TemplateStateInner { locations_list: Option, machines: HashSet, machines_per_network: HashMap, + disable_capabilities: Vec, } #[derive(Debug, Clone)] @@ -49,10 +56,16 @@ impl TemplateState { locations_list: None, machines: HashSet::new(), machines_per_network: HashMap::new(), + disable_capabilities: Vec::new(), })), } } + pub fn set_disable_capabilities(&self, disable_capabilities: Vec) { + let mut inner = self.inner.lock(); + inner.disable_capabilities = disable_capabilities; + } + pub fn set_networks_list(&self, networks: WeightedList) { let mut inner = self.inner.lock(); inner.locations_list = Some(LocationsList::Networks { networks }) @@ -107,38 +120,35 @@ impl TemplateState { Ok(true) } -xxx should this be sensitive to already generated blueprint networks? - fn is_blueprint_available_inner( inner: &TemplateStateInner, machine_registry_inner: &MachineRegistryInner, blueprint_state: BlueprintState, - ) -> MachineRegistryResult> { + ) -> MachineRegistryResult> { // See if the networks generated from this blueprint so far have availability // in this template - if let Some(available_network_id) = blueprint_state.for_each_network_id(|id| { + if let Some(available_network_state) = blueprint_state.for_each_network_id(|id| { // Check the network's availability - let network_state = machine_registry_inner - .network_states() - .get_state(id)? - .expect("must exist"); - if Self::is_network_available_inner(inner, network_state)? { + let network_state = machine_registry_inner.network_states().get_state(id)?; + if Self::is_network_available_inner(inner, network_state.clone())? { // We found one - return Ok(Some(id)); + return Ok(Some(network_state)); } // Try next network Ok(None) })? { // We found a usable network - return Ok(Availability::Existing(available_network_id)); + return Ok(Some(BlueprintAvailability::Existing( + available_network_state, + ))); } // If the blueprint is active, it is available because it can make a new network if blueprint_state.is_active()? { - return Ok(Availability::New); + return Ok(Some(BlueprintAvailability::Generate(blueprint_state))); } - Ok(Availability::None) + Ok(None) } pub fn is_active( @@ -163,12 +173,9 @@ xxx should this be sensitive to already generated blueprint networks? // Filter the weighted list of networks to those that are still active and or not yet started if networks .try_filter(|id| { - machine_registry_inner - .network_states() - .get_state(*id)? - .clone() - .map(|ns| Self::is_network_available_inner(&*inner, ns)) - .unwrap_or(Ok(true)) + let network_state = + machine_registry_inner.network_states().get_state(*id)?; + Self::is_network_available_inner(&*inner, network_state) })? .is_none() { @@ -179,19 +186,15 @@ xxx should this be sensitive to already generated blueprint networks? // Filter the weighted list of blueprints to those that are still active or not yet started and can allocate if blueprints .try_filter(|id| { - machine_registry_inner - .blueprint_states() - .get_state(*id)? - .clone() - .map(|bs| { - Self::is_blueprint_available_inner( - &*inner, - machine_registry_inner, - bs, - ) - .map(|x| !matches!(x, Availability::None)) - }) - .unwrap_or(Ok(true)) + let blueprint_state = + machine_registry_inner.blueprint_states().get_state(*id)?; + + Self::is_blueprint_available_inner( + &*inner, + machine_registry_inner, + blueprint_state, + ) + .map(|x| x.is_some()) })? .is_none() { @@ -225,90 +228,115 @@ xxx should this be sensitive to already generated blueprint networks? let network_state = match locations_list { LocationsList::Networks { networks } => { // Filter the weighted list of networks to those that are still active and or not yet started - let Some(active_networks) = networks.try_filter(|id| { - machine_registry_inner - .network_states() - .get_state(*id)? - .clone() - .map(|ns| Self::is_network_available_inner(&*inner, ns)) - .unwrap_or(Ok(true)) + let Some(available_networks) = networks.try_filter_map(|id| { + let network_state = machine_registry_inner.network_states().get_state(*id)?; + if Self::is_network_available_inner(&*inner, network_state.clone())? { + Ok(Some(network_state)) + } else { + Ok(None) + } })? else { return Err(MachineRegistryError::NetworkComplete); }; // Weighted choice of network now that we have a candidate list - let network_id = machine_registry_inner - .srng() - .weighted_choice(&active_networks); - - // Get the fixed network let network_state = machine_registry_inner - .network_states() - .get_state(*network_id)? - .expect("must exist"); + .srng() + .weighted_choice(&available_networks); // Return network state to use - network_state + network_state.clone() } LocationsList::Blueprints { blueprints } => { // Filter the weighted list of blueprints to those that are still active or not yet started and can allocate - let Some(active_blueprints) = blueprints.try_filter(|id| { - machine_registry_inner - .blueprint_states() - .get_state(*id)? - .clone() - .map(|bs| { - Self::is_blueprint_available_inner(&*inner, machine_registry_inner, bs) - .map(|x| !matches!(x, Availability::None)) - }) - .unwrap_or(Ok(true)) + let Some(available_blueprints) = blueprints.try_filter_map(|id| { + let blueprint_state = + machine_registry_inner.blueprint_states().get_state(*id)?; + + Self::is_blueprint_available_inner( + &*inner, + machine_registry_inner, + blueprint_state, + ) })? else { return Err(MachineRegistryError::BlueprintComplete); }; // Weighted choice of blueprint now that we have a candidate list - let blueprint_id = machine_registry_inner + match machine_registry_inner .srng() - .weighted_choice(&active_blueprints); - - xxx do not always generate... use most recent network for this blueprint in this template. - - // Instantiate a blueprint network - let blueprint_state = machine_registry_inner - .blueprint_states() - .get_state(*blueprint_id)? - .expect("must exist"); - - blueprint_state.generate(machine_registry_inner)? + .weighted_choice(&available_blueprints) + { + BlueprintAvailability::Existing(network_state) => network_state.clone(), + BlueprintAvailability::Generate(blueprint_state) => { + blueprint_state.generate(machine_registry_inner)? + } + } } }; // Allocate a machine id - let machine_id = machine_registry_inner.machine_states().allocate_id(); + let machine_state_id = machine_registry_inner.machine_states_mut().allocate_id(); // Create an anonymous machine state - let mut machine_state = MachineState::new(machine_id, None); + let machine_state = MachineState::new(machine_state_id, None); - // Build out the machine state from the template - //inner. + if let Err(e) = (|| { + // Build out the machine state from the template + machine_state.set_disable_capabilities(inner.disable_capabilities.clone()); + machine_state.set_bootstrap(false); + + // Make the default route interface + let vin0 = machine_state.allocate_interface(network_state.id(), None, None)?; + if network_state.is_ipv4() { + machine_state.allocate_address_ipv4(machine_registry_inner, &vin0, None, None)?; + } + if network_state.is_ipv6() { + machine_state.allocate_address_ipv6(machine_registry_inner, &vin0, None, None)?; + } + Ok(()) + })() { + // Release the machine state and id if things failed to allocate + machine_state.release(machine_registry_inner); + machine_registry_inner + .machine_states_mut() + .release_id(machine_state_id); + return Err(e); + } // Attach the state to the id machine_registry_inner - .machine_states() + .machine_states_mut() .attach_state(machine_state.clone()); // Record the newly instantiated machine - inner.machines.insert(machine_id); - let per_network_info = inner.machines_per_network.entry(network_state).or_insert_with(|| { - let limit_machine_count = inner.limit_machines_per_network.map(|wl| machine_registry_inner.srng().weighted_choice(&wl)).copied(); - PerNetworkInfo{ limit_machine_count, machines: HashSet::new() } - }); - per_network_info.machines.insert(machine_id); + inner.machines.insert(machine_state_id); + let limit_machines_per_network = inner.limit_machines_per_network.clone(); + let per_network_info = inner + .machines_per_network + .entry(network_state.id()) + .or_insert_with(|| { + let limit_machine_count = limit_machines_per_network + .map(|wl| machine_registry_inner.srng().weighted_choice(&wl).clone()); + PerNetworkInfo { + limit_machine_count, + machines: HashSet::new(), + } + }); + per_network_info.machines.insert(machine_state_id); Ok(machine_state) } + + pub fn on_machine_released(&self, machine_state_id: MachineStateId) { + let mut inner = self.inner.lock(); + inner.machines.remove(&machine_state_id); + for (_network_id, pni) in &mut inner.machines_per_network { + pni.machines.remove(&machine_state_id); + } + } } impl State for TemplateState {