[skip ci] network and blueprint code

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

View File

@ -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<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
}
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
}
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
}
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
}
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
}

View File

@ -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,

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,
}
#[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)]
struct BlueprintStateInner {
limit_network_count: Option<usize>,
networks: Vec<NetworkStateId>,
model: Option<WeightedList<String>>,
ipv4: Option<BlueprintStateIpv4>,
ipv6: Option<BlueprintStateIpv6>,
}
#[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<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> {
let inner = self.inner.lock();
@ -49,16 +165,77 @@ impl BlueprintState {
Ok(true)
}
pub fn generate(
&self,
machine_registry_inner: &MachineRegistryInner,
) -> MachineRegistryResult<NetworkState> {
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<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
F: FnMut(NetworkStateId) -> MachineRegistryResult<Option<R>>,
F: Fn(NetworkStateId) -> MachineRegistryResult<Option<R>>,
{
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 {

View File

@ -2,8 +2,14 @@ use super::*;
#[derive(Debug)]
struct MachineStateInner {
/// The template generating this machine
generating_template: Option<TemplateStateId>,
/// The current network interfaces definition
interfaces: HashMap<String, MachineStateInterface>,
/// Capabilities to disable on this machine
disable_capabilities: Vec<String>,
/// 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<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 {
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 {

View File

@ -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::*;

View File

@ -11,12 +11,10 @@ struct NetworkStateUnlockedInner {
#[derive(Debug)]
struct NetworkStateInner {
/// Network latency distribution
latency: Option<config::Distribution>,
/// Distance simulation metric
distance: Option<config::Distance>,
/// Packet loss probability
loss: Probability,
/// The blueprint generating this network
generating_blueprint: Option<BlueprintStateId>,
/// Model for this network
model: NetworkStateModel,
/// IPv4 state if it is enabled
ipv4: Option<NetworkStateIpv4>,
/// IPv6 state if it is enabled
@ -24,26 +22,57 @@ struct NetworkStateInner {
}
#[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,
gateway: Option<NetworkGatewayState>,
}
#[derive(Debug)]
struct NetworkStateIpv4 {
params: NetworkStateIpv4Params,
gateway: Option<NetworkStateIpv4Gateway>,
machine_addresses: HashMap<Ipv4Addr, MachineStateId>,
}
#[derive(Debug)]
struct NetworkStateIpv6 {
struct NetworkStateIpv6Params {
allocation: Ipv6Net,
gateway: Option<NetworkGatewayState>,
}
#[derive(Debug)]
struct NetworkStateIpv6 {
params: NetworkStateIpv6Params,
gateway: Option<NetworkStateIpv6Gateway>,
machine_addresses: HashMap<Ipv6Addr, MachineStateId>,
}
#[derive(Debug)]
struct NetworkGatewayState {
translation: config::Translation, // xxx replace with translation state
struct NetworkStateGatewayParams {
translation: config::Translation,
upnp: bool,
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)]
pub struct NetworkState {
unlocked_inner: Arc<NetworkStateUnlockedInner>,
@ -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<NetworkStateGatewayParams>,
) {
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<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 {
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)

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
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<S: State> StateAllocator<S> {
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
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<S> {

View File

@ -22,6 +22,12 @@ enum LocationsList {
},
}
#[derive(Debug, Clone)]
enum BlueprintAvailability {
Existing(NetworkState),
Generate(BlueprintState),
}
#[derive(Debug)]
struct TemplateStateInner {
limit_machine_count: Option<usize>,
@ -29,6 +35,7 @@ struct TemplateStateInner {
locations_list: Option<LocationsList>,
machines: HashSet<MachineStateId>,
machines_per_network: HashMap<NetworkStateId, PerNetworkInfo>,
disable_capabilities: Vec<String>,
}
#[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<String>) {
let mut inner = self.inner.lock();
inner.disable_capabilities = disable_capabilities;
}
pub fn set_networks_list(&self, networks: WeightedList<NetworkStateId>) {
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<Availability<NetworkStateId>> {
) -> MachineRegistryResult<Option<BlueprintAvailability>> {
// 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 {