[skip ci] network and blueprint code

This commit is contained in:
Christien Rioux 2024-12-16 20:26:43 -05:00
parent c0680aa712
commit 8906ad1d88
9 changed files with 509 additions and 241 deletions

View File

@ -1,5 +1,3 @@
use std::marker::PhantomData;
use super::*; use super::*;
#[derive(Debug)] #[derive(Debug)]
@ -137,11 +135,9 @@ impl MachineRegistryInner {
self.allocated_machines.remove(&id); self.allocated_machines.remove(&id);
} else { } else {
// Was a templated machine, so remove the machine state // Was a templated machine, so remove the machine state
let Some(machine_state) = self.machine_states().get_state(id)? else { let machine_state = self.machine_states().get_state(id)?;
return Err(MachineRegistryError::InvalidId);
};
machine_state.release(self); machine_state.release(self);
self.machine_states().release_id(id)?; self.machine_states_mut().release_id(id)?;
} }
Ok(()) Ok(())
@ -150,19 +146,35 @@ impl MachineRegistryInner {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
/// Private Implementation /// Private Implementation
pub(super) fn profile_states(&mut self) -> &mut StateAllocator<ProfileState> { pub(super) fn profile_states(&self) -> &StateAllocator<ProfileState> {
&self.profile_state_allocator
}
pub(super) fn machine_states(&self) -> &StateAllocator<MachineState> {
&self.machine_state_allocator
}
pub(super) fn template_states(&self) -> &StateAllocator<TemplateState> {
&self.template_state_allocator
}
pub(super) fn network_states(&self) -> &StateAllocator<NetworkState> {
&self.network_state_allocator
}
pub(super) fn blueprint_states(&self) -> &StateAllocator<BlueprintState> {
&self.blueprint_state_allocator
}
pub(super) fn profile_states_mut(&mut self) -> &mut StateAllocator<ProfileState> {
&mut self.profile_state_allocator &mut self.profile_state_allocator
} }
pub(super) fn machine_states(&mut self) -> &mut StateAllocator<MachineState> { pub(super) fn machine_states_mut(&mut self) -> &mut StateAllocator<MachineState> {
&mut self.machine_state_allocator &mut self.machine_state_allocator
} }
pub(super) fn template_states(&mut self) -> &mut StateAllocator<TemplateState> { pub(super) fn template_states_mut(&mut self) -> &mut StateAllocator<TemplateState> {
&mut self.template_state_allocator &mut self.template_state_allocator
} }
pub(super) fn network_states(&mut self) -> &mut StateAllocator<NetworkState> { pub(super) fn network_states_mut(&mut self) -> &mut StateAllocator<NetworkState> {
&mut self.network_state_allocator &mut self.network_state_allocator
} }
pub(super) fn blueprint_states(&mut self) -> &mut StateAllocator<BlueprintState> { pub(super) fn blueprint_states_mut(&mut self) -> &mut StateAllocator<BlueprintState> {
&mut self.blueprint_state_allocator &mut self.blueprint_state_allocator
} }

View File

@ -3,7 +3,6 @@ mod machine_registry_inner;
mod state; mod state;
use super::*; use super::*;
use ipnet::*;
use address_pool::*; use address_pool::*;
use machine_registry_inner::*; use machine_registry_inner::*;
@ -23,7 +22,7 @@ pub enum MachineRegistryError {
InvalidId, InvalidId,
InvalidName, InvalidName,
AlreadyAttached, AlreadyAttached,
AlreadyDetached, NotAttached,
DuplicateName, DuplicateName,
ProfileComplete, ProfileComplete,
TemplateComplete, TemplateComplete,

View File

@ -1,29 +0,0 @@
use super::*;
pub enum Availability<T> {
None,
Existing(T),
New,
}
impl<T: Copy> Copy for Availability<T> {}
impl<T: Clone> Clone for Availability<T> {
fn clone(&self) -> Self {
match self {
Availability::None => Availability::None,
Availability::Existing(x) => Availability::Existing(x.clone()),
Availability::New => Availability::New,
}
}
}
impl<T: fmt::Debug> fmt::Debug for Availability<T> {
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"),
}
}
}

View File

@ -6,10 +6,56 @@ struct BlueprintStateUnlockedInner {
name: String, name: String,
} }
#[derive(Debug)]
pub struct BlueprintStateIpv4Params {
pub allocation: Option<String>,
pub additional_prefix: u8,
pub gateway: Option<BlueprintStateGatewayParams>,
}
#[derive(Debug)]
pub struct BlueprintStateIpv6Params {
pub allocation: Option<String>,
pub additional_prefix: u8,
pub gateway: Option<BlueprintStateGatewayParams>,
}
#[derive(Debug)]
pub struct BlueprintStateGatewayParams {
pub translation: WeightedList<config::Translation>,
pub upnp: Probability,
pub network: Option<NetworkStateId>,
}
#[derive(Debug)]
struct BlueprintStateIpv4 {
params: BlueprintStateIpv4Params,
gateway: Option<BlueprintStateIpv4Gateway>,
}
#[derive(Debug)]
struct BlueprintStateIpv6 {
params: BlueprintStateIpv6Params,
gateway: Option<BlueprintStateIpv6Gateway>,
}
#[derive(Debug)]
struct BlueprintStateIpv4Gateway {
params: BlueprintStateGatewayParams,
}
#[derive(Debug)]
struct BlueprintStateIpv6Gateway {
params: BlueprintStateGatewayParams,
}
#[derive(Debug)] #[derive(Debug)]
struct BlueprintStateInner { struct BlueprintStateInner {
limit_network_count: Option<usize>, limit_network_count: Option<usize>,
networks: Vec<NetworkStateId>, networks: Vec<NetworkStateId>,
model: Option<WeightedList<String>>,
ipv4: Option<BlueprintStateIpv4>,
ipv6: Option<BlueprintStateIpv6>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -27,6 +73,9 @@ impl BlueprintState {
inner: Arc::new(Mutex::new(BlueprintStateInner { inner: Arc::new(Mutex::new(BlueprintStateInner {
limit_network_count: None, limit_network_count: None,
networks: Vec::new(), networks: Vec::new(),
model: None,
ipv4: None,
ipv6: None,
})), })),
}) })
} }
@ -36,6 +85,73 @@ impl BlueprintState {
inner.limit_network_count = limit_network_count; inner.limit_network_count = limit_network_count;
} }
pub fn set_model(&self, model: WeightedList<String>) {
let mut inner = self.inner.lock();
inner.model = Some(model);
}
pub fn set_ipv4(
&self,
params: BlueprintStateIpv4Params,
gateway_params: Option<BlueprintStateGatewayParams>,
) {
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<BlueprintStateGatewayParams>,
) {
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<bool> { pub fn is_active(&self) -> MachineRegistryResult<bool> {
let inner = self.inner.lock(); let inner = self.inner.lock();
@ -49,16 +165,77 @@ impl BlueprintState {
Ok(true) Ok(true)
} }
pub fn generate( fn generate_model_inner(
&self, inner: &mut BlueprintStateInner,
machine_registry_inner: &MachineRegistryInner, machine_registry_inner: &mut MachineRegistryInner,
) -> MachineRegistryResult<NetworkState> { 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<F, R>(&self, mut callback: F) -> MachineRegistryResult<Option<R>> 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<NetworkState> {
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<F, R>(&self, callback: F) -> MachineRegistryResult<Option<R>>
where where
F: FnMut(NetworkStateId) -> MachineRegistryResult<Option<R>>, F: Fn(NetworkStateId) -> MachineRegistryResult<Option<R>>,
{ {
let inner = self.inner.lock(); let inner = self.inner.lock();
for network_id in &inner.networks { for network_id in &inner.networks {
@ -68,6 +245,16 @@ impl BlueprintState {
} }
Ok(None) 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 { impl State for BlueprintState {

View File

@ -2,8 +2,14 @@ use super::*;
#[derive(Debug)] #[derive(Debug)]
struct MachineStateInner { struct MachineStateInner {
/// The template generating this machine
generating_template: Option<TemplateStateId>,
/// The current network interfaces definition /// The current network interfaces definition
interfaces: HashMap<String, MachineStateInterface>, interfaces: HashMap<String, MachineStateInterface>,
/// Capabilities to disable on this machine
disable_capabilities: Vec<String>,
/// If this machine is a bootstrap
bootstrap: bool,
} }
#[derive(Debug)] #[derive(Debug)]
@ -36,19 +42,45 @@ impl MachineState {
Self { Self {
unlocked_inner: Arc::new(MachineStateUnlockedInner { id, opt_name }), unlocked_inner: Arc::new(MachineStateUnlockedInner { id, opt_name }),
inner: Arc::new(Mutex::new(MachineStateInner { inner: Arc::new(Mutex::new(MachineStateInner {
generating_template: None,
interfaces: HashMap::new(), interfaces: HashMap::new(),
disable_capabilities: Vec::new(),
bootstrap: false,
})), })),
} }
} }
pub fn release(&self, machine_registry_inner: &mut MachineRegistryInner) { pub fn release(&self, machine_registry_inner: &mut MachineRegistryInner) {
self.release_all_interfaces(machine_registry_inner); 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 { pub fn external_id(&self) -> MachineId {
self.unlocked_inner.id.0 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<String>) {
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 { fn next_free_interface_name_inner(inner: &MachineStateInner) -> String {
let mut inum = 0usize; let mut inum = 0usize;
loop { loop {
@ -111,12 +143,9 @@ impl MachineState {
}; };
// Get the network state // Get the network state
let Some(network_state) = machine_registry_inner let network_state = machine_registry_inner
.network_states() .network_states()
.get_state(intf.network_id)? .get_state(intf.network_id)?;
else {
return Err(MachineRegistryError::NetworkNotFound);
};
// Allocate interface address // Allocate interface address
let is_dynamic = opt_address.is_none(); let is_dynamic = opt_address.is_none();
@ -151,12 +180,9 @@ impl MachineState {
}; };
// Get the network state // Get the network state
let Some(network_state) = machine_registry_inner let network_state = machine_registry_inner
.network_states() .network_states()
.get_state(intf.network_id)? .get_state(intf.network_id)?;
else {
return Err(MachineRegistryError::NetworkNotFound);
};
// Allocate interface address // Allocate interface address
let is_dynamic = opt_address.is_none(); let is_dynamic = opt_address.is_none();
@ -190,12 +216,9 @@ impl MachineState {
}; };
// Get the network state // Get the network state
let Some(network_state) = machine_registry_inner let network_state = machine_registry_inner
.network_states() .network_states()
.get_state(intf.network_id)? .get_state(intf.network_id)?;
else {
return Err(MachineRegistryError::NetworkNotFound);
};
// Release the address from the network // Release the address from the network
match address { match address {
@ -230,12 +253,9 @@ impl MachineState {
}; };
// Get the network state // Get the network state
let Some(network_state) = machine_registry_inner let network_state = machine_registry_inner
.network_states() .network_states()
.get_state(intf.network_id)? .get_state(intf.network_id)?;
else {
return Err(MachineRegistryError::NetworkNotFound);
};
// Release the addresses from the network // Release the addresses from the network
for addr in &intf.network_interface.addrs { for addr in &intf.network_interface.addrs {

View File

@ -1,4 +1,3 @@
mod availability;
mod blueprint_state; mod blueprint_state;
mod machine_state; mod machine_state;
mod network_state; mod network_state;
@ -10,7 +9,6 @@ use std::marker::PhantomData;
use super::*; use super::*;
pub use availability::*;
pub use blueprint_state::*; pub use blueprint_state::*;
pub use machine_state::*; pub use machine_state::*;
pub use network_state::*; pub use network_state::*;

View File

@ -11,12 +11,10 @@ struct NetworkStateUnlockedInner {
#[derive(Debug)] #[derive(Debug)]
struct NetworkStateInner { struct NetworkStateInner {
/// Network latency distribution /// The blueprint generating this network
latency: Option<config::Distribution>, generating_blueprint: Option<BlueprintStateId>,
/// Distance simulation metric /// Model for this network
distance: Option<config::Distance>, model: NetworkStateModel,
/// Packet loss probability
loss: Probability,
/// IPv4 state if it is enabled /// IPv4 state if it is enabled
ipv4: Option<NetworkStateIpv4>, ipv4: Option<NetworkStateIpv4>,
/// IPv6 state if it is enabled /// IPv6 state if it is enabled
@ -24,26 +22,57 @@ struct NetworkStateInner {
} }
#[derive(Debug)] #[derive(Debug)]
struct NetworkStateIpv4 { struct NetworkStateModel {
/// Network latency distribution
latency: Option<config::Distribution>,
/// Distance simulation metric
distance: Option<config::Distance>,
/// Packet loss probability
loss: Probability,
}
#[derive(Debug)]
struct NetworkStateIpv4Params {
allocation: Ipv4Net, allocation: Ipv4Net,
gateway: Option<NetworkGatewayState>, }
#[derive(Debug)]
struct NetworkStateIpv4 {
params: NetworkStateIpv4Params,
gateway: Option<NetworkStateIpv4Gateway>,
machine_addresses: HashMap<Ipv4Addr, MachineStateId>, machine_addresses: HashMap<Ipv4Addr, MachineStateId>,
} }
#[derive(Debug)] #[derive(Debug)]
struct NetworkStateIpv6 { struct NetworkStateIpv6Params {
allocation: Ipv6Net, allocation: Ipv6Net,
gateway: Option<NetworkGatewayState>, }
#[derive(Debug)]
struct NetworkStateIpv6 {
params: NetworkStateIpv6Params,
gateway: Option<NetworkStateIpv6Gateway>,
machine_addresses: HashMap<Ipv6Addr, MachineStateId>, machine_addresses: HashMap<Ipv6Addr, MachineStateId>,
} }
#[derive(Debug)] #[derive(Debug)]
struct NetworkGatewayState { struct NetworkStateGatewayParams {
translation: config::Translation, // xxx replace with translation state translation: config::Translation,
upnp: bool, upnp: bool,
network: Option<NetworkStateId>, network: Option<NetworkStateId>,
} }
#[derive(Debug)]
struct NetworkStateIpv4Gateway {
params: NetworkStateGatewayParams,
// xxx add translation state
}
#[derive(Debug)]
struct NetworkStateIpv6Gateway {
params: NetworkStateGatewayParams,
// xxx add translation state
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NetworkState { pub struct NetworkState {
unlocked_inner: Arc<NetworkStateUnlockedInner>, unlocked_inner: Arc<NetworkStateUnlockedInner>,
@ -57,79 +86,101 @@ impl NetworkState {
Self { Self {
unlocked_inner: Arc::new(NetworkStateUnlockedInner { id, opt_name }), unlocked_inner: Arc::new(NetworkStateUnlockedInner { id, opt_name }),
inner: Arc::new(Mutex::new(NetworkStateInner { inner: Arc::new(Mutex::new(NetworkStateInner {
latency: None, generating_blueprint: None,
distance: None, model: NetworkStateModel {
loss: 0.0, latency: None,
distance: None,
loss: 0.0,
},
ipv4: None, ipv4: None,
ipv6: None, ipv6: None,
})), })),
} }
} }
// let model = network_def.model.clone().unwrap_or_else(|| { pub fn release(&self, machine_registry_inner: &mut MachineRegistryInner) {
// machine_registry_inner let inner = self.inner.lock();
// .unlocked_inner if let Some(generating_blueprint) = inner.generating_blueprint {
// .config let blueprint_state = machine_registry_inner
// .default_model .blueprint_states_mut()
// .clone() .get_state(generating_blueprint)
// }); .expect("must exist");
// let ipv4 = match network_def.ipv4.as_ref() { blueprint_state.on_network_released(self.id());
// 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,
// };
// let ipv6 = match network_def.ipv6.as_ref() { pub fn set_model(&self, model: NetworkStateModel) {
// Some(ipv6) => Some(NetworkStateIpv6 { let mut inner = self.inner.lock();
// allocation: machine_registry_inner.choose_allocation_v6( inner.model = model;
// machine_registry_inner }
// .unlocked_inner
// .config pub fn set_ipv4(
// .allocations &self,
// .get(&ipv6.allocation) params: NetworkStateIpv4Params,
// .cloned() gateway_params: Option<NetworkStateGatewayParams>,
// .ok_or(MachineRegistryError::InvalidAllocationName)?, ) {
// 0, let mut inner = self.inner.lock();
// )?, if inner.ipv4.is_none() {
// gateway: match ipv6.gateway.as_ref() { inner.ipv4 = Some(NetworkStateIpv4 {
// Some(v6gw) => Some(NetworkGatewayState { params,
// translation: v6gw.translation, gateway: None,
// upnp: v6gw.upnp, machine_addresses: HashMap::new(),
// network: v6gw.network.clone().map(|gwname| { });
// machine_registry_inner } else {
// .resolve_to_manager_network inner.ipv4.as_mut().map(|ipv4| {
// .add(gwname) ipv4.params = params;
// }), });
// }), }
// None => None, let ipv4 = inner.ipv4.as_mut().expect("must exist");
// },
// machine_addresses: HashMap::new(), if ipv4.gateway.is_some() {
// }), if let Some(gateway_params) = gateway_params {
// None => None, 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<NetworkStateGatewayParams>,
) {
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 { pub fn is_ipv4(&self) -> bool {
self.inner.lock().ipv4.is_some() self.inner.lock().ipv4.is_some()
@ -143,7 +194,7 @@ impl NetworkState {
let inner = self.inner.lock(); let inner = self.inner.lock();
let mut can_allocate = false; let mut can_allocate = false;
if let Some(network_state_ipv4) = &inner.ipv4 { 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 { let Some(first_host) = std::iter::Iterator::min(hosts_range) else {
return Err(MachineRegistryError::NoAllocation); return Err(MachineRegistryError::NoAllocation);
}; };
@ -160,7 +211,7 @@ impl NetworkState {
} }
}; };
if let Some(network_state_ipv6) = &inner.ipv6 { 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 { let Some(first_host) = std::iter::Iterator::min(hosts_range) else {
return Err(MachineRegistryError::NoAllocation); 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 // 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 // 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 { let Some(first_host) = std::iter::Iterator::min(hosts_range) else {
return Err(MachineRegistryError::NoAllocation); return Err(MachineRegistryError::NoAllocation);
}; };
@ -245,8 +296,8 @@ impl NetworkState {
// Make interface address // Make interface address
let ifaddr = Ifv4Addr { let ifaddr = Ifv4Addr {
ip, ip,
netmask: network_state_ipv4.allocation.netmask(), netmask: network_state_ipv4.params.allocation.netmask(),
broadcast: Some(network_state_ipv4.allocation.broadcast()), broadcast: Some(network_state_ipv4.params.allocation.broadcast()),
}; };
Ok(ifaddr) Ok(ifaddr)
@ -284,7 +335,7 @@ impl NetworkState {
// for now we just pick randomly and then increment until we find a free allocation // 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 // 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 { let Some(first_host) = std::iter::Iterator::min(hosts_range) else {
return Err(MachineRegistryError::NoAllocation); return Err(MachineRegistryError::NoAllocation);
}; };
@ -335,8 +386,8 @@ impl NetworkState {
// Make interface address // Make interface address
let ifaddr = Ifv6Addr { let ifaddr = Ifv6Addr {
ip, ip,
netmask: network_state_ipv6.allocation.netmask(), netmask: network_state_ipv6.params.allocation.netmask(),
broadcast: Some(network_state_ipv6.allocation.broadcast()), broadcast: Some(network_state_ipv6.params.allocation.broadcast()),
}; };
Ok(ifaddr) Ok(ifaddr)

View File

@ -147,7 +147,7 @@ impl<S: State> StateAllocator<S> {
// Take the state out of the slot and ensure the state slot isn't detached already // Take the state out of the slot and ensure the state slot isn't detached already
let Some(state) = opt_state.take() else { let Some(state) = opt_state.take() else {
return Err(MachineRegistryError::AlreadyDetached); return Err(MachineRegistryError::NotAttached);
}; };
// Release the name if it exists // Release the name if it exists
@ -162,13 +162,15 @@ impl<S: State> StateAllocator<S> {
Ok(state) Ok(state)
} }
pub fn get_state(&self, id: StateId<S>) -> MachineRegistryResult<Option<S>> { pub fn get_state(&self, id: StateId<S>) -> MachineRegistryResult<S> {
// Get the allocator slot // Get the allocator slot
let Some(opt_state) = self.state_by_id.get(&id.0).cloned() else { let Some(opt_state) = self.state_by_id.get(&id.0).cloned() else {
return Err(MachineRegistryError::InvalidId); return Err(MachineRegistryError::InvalidId);
}; };
let Some(state) = opt_state else {
Ok(opt_state) return Err(MachineRegistryError::NotAttached);
};
Ok(state)
} }
pub fn get_state_by_name(&self, name: &String) -> Option<S> { pub fn get_state_by_name(&self, name: &String) -> Option<S> {

View File

@ -22,6 +22,12 @@ enum LocationsList {
}, },
} }
#[derive(Debug, Clone)]
enum BlueprintAvailability {
Existing(NetworkState),
Generate(BlueprintState),
}
#[derive(Debug)] #[derive(Debug)]
struct TemplateStateInner { struct TemplateStateInner {
limit_machine_count: Option<usize>, limit_machine_count: Option<usize>,
@ -29,6 +35,7 @@ struct TemplateStateInner {
locations_list: Option<LocationsList>, locations_list: Option<LocationsList>,
machines: HashSet<MachineStateId>, machines: HashSet<MachineStateId>,
machines_per_network: HashMap<NetworkStateId, PerNetworkInfo>, machines_per_network: HashMap<NetworkStateId, PerNetworkInfo>,
disable_capabilities: Vec<String>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -49,10 +56,16 @@ impl TemplateState {
locations_list: None, locations_list: None,
machines: HashSet::new(), machines: HashSet::new(),
machines_per_network: HashMap::new(), machines_per_network: HashMap::new(),
disable_capabilities: Vec::new(),
})), })),
} }
} }
pub fn set_disable_capabilities(&self, disable_capabilities: Vec<String>) {
let mut inner = self.inner.lock();
inner.disable_capabilities = disable_capabilities;
}
pub fn set_networks_list(&self, networks: WeightedList<NetworkStateId>) { pub fn set_networks_list(&self, networks: WeightedList<NetworkStateId>) {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
inner.locations_list = Some(LocationsList::Networks { networks }) inner.locations_list = Some(LocationsList::Networks { networks })
@ -107,38 +120,35 @@ impl TemplateState {
Ok(true) Ok(true)
} }
xxx should this be sensitive to already generated blueprint networks?
fn is_blueprint_available_inner( fn is_blueprint_available_inner(
inner: &TemplateStateInner, inner: &TemplateStateInner,
machine_registry_inner: &MachineRegistryInner, machine_registry_inner: &MachineRegistryInner,
blueprint_state: BlueprintState, blueprint_state: BlueprintState,
) -> MachineRegistryResult<Availability<NetworkStateId>> { ) -> MachineRegistryResult<Option<BlueprintAvailability>> {
// See if the networks generated from this blueprint so far have availability // See if the networks generated from this blueprint so far have availability
// in this template // 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 // Check the network's availability
let network_state = machine_registry_inner let network_state = machine_registry_inner.network_states().get_state(id)?;
.network_states() if Self::is_network_available_inner(inner, network_state.clone())? {
.get_state(id)?
.expect("must exist");
if Self::is_network_available_inner(inner, network_state)? {
// We found one // We found one
return Ok(Some(id)); return Ok(Some(network_state));
} }
// Try next network // Try next network
Ok(None) Ok(None)
})? { })? {
// We found a usable network // 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 the blueprint is active, it is available because it can make a new network
if blueprint_state.is_active()? { 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( 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 // Filter the weighted list of networks to those that are still active and or not yet started
if networks if networks
.try_filter(|id| { .try_filter(|id| {
machine_registry_inner let network_state =
.network_states() machine_registry_inner.network_states().get_state(*id)?;
.get_state(*id)? Self::is_network_available_inner(&*inner, network_state)
.clone()
.map(|ns| Self::is_network_available_inner(&*inner, ns))
.unwrap_or(Ok(true))
})? })?
.is_none() .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 // Filter the weighted list of blueprints to those that are still active or not yet started and can allocate
if blueprints if blueprints
.try_filter(|id| { .try_filter(|id| {
machine_registry_inner let blueprint_state =
.blueprint_states() machine_registry_inner.blueprint_states().get_state(*id)?;
.get_state(*id)?
.clone() Self::is_blueprint_available_inner(
.map(|bs| { &*inner,
Self::is_blueprint_available_inner( machine_registry_inner,
&*inner, blueprint_state,
machine_registry_inner, )
bs, .map(|x| x.is_some())
)
.map(|x| !matches!(x, Availability::None))
})
.unwrap_or(Ok(true))
})? })?
.is_none() .is_none()
{ {
@ -225,90 +228,115 @@ xxx should this be sensitive to already generated blueprint networks?
let network_state = match locations_list { let network_state = match locations_list {
LocationsList::Networks { networks } => { LocationsList::Networks { networks } => {
// Filter the weighted list of networks to those that are still active and or not yet started // 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| { let Some(available_networks) = networks.try_filter_map(|id| {
machine_registry_inner let network_state = machine_registry_inner.network_states().get_state(*id)?;
.network_states() if Self::is_network_available_inner(&*inner, network_state.clone())? {
.get_state(*id)? Ok(Some(network_state))
.clone() } else {
.map(|ns| Self::is_network_available_inner(&*inner, ns)) Ok(None)
.unwrap_or(Ok(true)) }
})? })?
else { else {
return Err(MachineRegistryError::NetworkComplete); return Err(MachineRegistryError::NetworkComplete);
}; };
// Weighted choice of network now that we have a candidate list // 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 let network_state = machine_registry_inner
.network_states() .srng()
.get_state(*network_id)? .weighted_choice(&available_networks);
.expect("must exist");
// Return network state to use // Return network state to use
network_state network_state.clone()
} }
LocationsList::Blueprints { blueprints } => { LocationsList::Blueprints { blueprints } => {
// Filter the weighted list of blueprints to those that are still active or not yet started and can allocate // 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| { let Some(available_blueprints) = blueprints.try_filter_map(|id| {
machine_registry_inner let blueprint_state =
.blueprint_states() machine_registry_inner.blueprint_states().get_state(*id)?;
.get_state(*id)?
.clone() Self::is_blueprint_available_inner(
.map(|bs| { &*inner,
Self::is_blueprint_available_inner(&*inner, machine_registry_inner, bs) machine_registry_inner,
.map(|x| !matches!(x, Availability::None)) blueprint_state,
}) )
.unwrap_or(Ok(true))
})? })?
else { else {
return Err(MachineRegistryError::BlueprintComplete); return Err(MachineRegistryError::BlueprintComplete);
}; };
// Weighted choice of blueprint now that we have a candidate list // Weighted choice of blueprint now that we have a candidate list
let blueprint_id = machine_registry_inner match machine_registry_inner
.srng() .srng()
.weighted_choice(&active_blueprints); .weighted_choice(&available_blueprints)
{
xxx do not always generate... use most recent network for this blueprint in this template. BlueprintAvailability::Existing(network_state) => network_state.clone(),
BlueprintAvailability::Generate(blueprint_state) => {
// Instantiate a blueprint network blueprint_state.generate(machine_registry_inner)?
let blueprint_state = machine_registry_inner }
.blueprint_states() }
.get_state(*blueprint_id)?
.expect("must exist");
blueprint_state.generate(machine_registry_inner)?
} }
}; };
// Allocate a machine id // 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 // 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 if let Err(e) = (|| {
//inner. // 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 // Attach the state to the id
machine_registry_inner machine_registry_inner
.machine_states() .machine_states_mut()
.attach_state(machine_state.clone()); .attach_state(machine_state.clone());
// Record the newly instantiated machine // Record the newly instantiated machine
inner.machines.insert(machine_id); inner.machines.insert(machine_state_id);
let per_network_info = inner.machines_per_network.entry(network_state).or_insert_with(|| { let limit_machines_per_network = inner.limit_machines_per_network.clone();
let limit_machine_count = inner.limit_machines_per_network.map(|wl| machine_registry_inner.srng().weighted_choice(&wl)).copied(); let per_network_info = inner
PerNetworkInfo{ limit_machine_count, machines: HashSet::new() } .machines_per_network
}); .entry(network_state.id())
per_network_info.machines.insert(machine_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) 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 { impl State for TemplateState {