[skip ci] refactor, extract stateallocator

This commit is contained in:
Christien Rioux 2024-12-14 19:25:13 -05:00
parent 65629f03e9
commit 68fc6f97eb
10 changed files with 1143 additions and 704 deletions

View File

@ -130,6 +130,39 @@ impl<T: fmt::Debug + Clone> WeightedList<T> {
}
}
}
pub fn try_filter_map<F, S, E>(&self, mut filter: F) -> Result<Option<WeightedList<S>>, E>
where
F: FnMut(&T) -> Result<Option<S>, E>,
S: fmt::Debug + Clone,
{
match self {
WeightedList::Single(v) => {
if let Some(item) = filter(v)? {
return Ok(Some(WeightedList::Single(item)));
}
return Ok(None);
}
WeightedList::List(vec) => {
let mut out = Vec::<Weighted<S>>::with_capacity(vec.len());
for v in vec {
if let Some(item) = filter(v.item())? {
out.push(match v {
Weighted::Weighted { item: _, weight } => Weighted::Weighted {
item,
weight: *weight,
},
Weighted::Unweighted(_) => Weighted::Unweighted(item),
});
}
}
if out.is_empty() {
Ok(None)
} else {
Ok(Some(WeightedList::List(out)))
}
}
}
}
}
pub type Probability = f32;

View File

@ -2,19 +2,12 @@ use super::*;
#[derive(Debug)]
pub(super) struct MachineRegistryInner {
pub unlocked_inner: Arc<MachineRegistryUnlockedInner>,
pub resolve_to_manager_machine: ResolveToManager<String, MachineId>,
pub resolve_to_manager_network: ResolveToManager<String, NetworkId>,
//
profile_state_by_name: HashMap<String, ProfileState>,
machine_state_by_id: HashMap<MachineId, MachineState>,
network_state_by_id: HashMap<NetworkId, NetworkState>,
template_state_by_name: HashMap<String, TemplateState>,
blueprint_state_by_name: HashMap<String, BlueprintState>,
next_machine_id: u64,
free_machine_ids: Vec<u64>,
next_network_id: u64,
free_network_ids: Vec<u64>,
unlocked_inner: Arc<MachineRegistryUnlockedInner>,
profile_state_allocator: StateAllocator<ProfileState>,
machine_state_allocator: StateAllocator<MachineState>,
template_state_allocator: StateAllocator<TemplateState>,
network_state_allocator: StateAllocator<NetworkState>,
blueprint_state_allocator: StateAllocator<BlueprintState>,
address_pool: AddressPool,
}
@ -26,17 +19,11 @@ impl MachineRegistryInner {
let srng = unlocked_inner.srng.clone();
MachineRegistryInner {
unlocked_inner,
machine_state_by_id: HashMap::new(),
profile_state_by_name: HashMap::new(),
network_state_by_id: HashMap::new(),
template_state_by_name: HashMap::new(),
blueprint_state_by_name: HashMap::new(),
next_machine_id: 0,
free_machine_ids: Vec::new(),
next_network_id: 0,
free_network_ids: Vec::new(),
resolve_to_manager_machine: ResolveToManager::new(),
resolve_to_manager_network: ResolveToManager::new(),
profile_state_allocator: StateAllocator::new(),
machine_state_allocator: StateAllocator::new(),
template_state_allocator: StateAllocator::new(),
network_state_allocator: StateAllocator::new(),
blueprint_state_allocator: StateAllocator::new(),
address_pool: AddressPool::new(srng),
}
}
@ -49,20 +36,19 @@ impl MachineRegistryInner {
// Get current profile state, creating one if we have not yet started executing the profile
let profile_state = self
.profile_state_by_name
.entry(profile)
.or_insert_with(|| ProfileState::default());
.profile_state_allocator
.get_or_create_by_name(profile, |id, name| {
ProfileState::new(id, name, profile_def.clone())
});
// Get the next instance from the definition
let Some(instance_def) = profile_def.instances.get(profile_state.next_instance_index)
else {
//
let Some(instance_def) = profile_state.next_instance() else {
return Err(MachineRegistryError::ProfileComplete);
};
let machine_state = match instance_def {
config::Instance::Machine { machine } => {
let machine = self.unlocked_inner.srng.weighted_choice(machine);
let machine = self.unlocked_inner.srng.weighted_choice(&machine);
let unlocked_inner = self.unlocked_inner.clone();
let machine_def = unlocked_inner
.config
@ -73,7 +59,7 @@ impl MachineRegistryInner {
self.get_or_create_machine_state(machine.clone(), machine_def)?
}
config::Instance::Template { template } => {
let template = self.unlocked_inner.srng.weighted_choice(template);
let template = self.unlocked_inner.srng.weighted_choice(&template);
let unlocked_inner = self.unlocked_inner.clone();
let template_def = unlocked_inner
.config
@ -87,7 +73,7 @@ impl MachineRegistryInner {
Ok(machine_state.id())
}
pub fn release(&self, machine_id: MachineId) -> MachineRegistryResult<()> {
pub fn release(&mut self, machine_id: MachineId) -> MachineRegistryResult<()> {
// xxx
// xxx remember machines and networks may not be 'named' if they are generated by templates and blueprints
Ok(())
@ -96,18 +82,36 @@ impl MachineRegistryInner {
///////////////////////////////////////////////////////////
/// Private Implementation
pub(super) fn profile_states(&mut self) -> &mut StateAllocator<ProfileState> {
&mut self.profile_state_allocator
}
pub(super) fn machine_states(&mut self) -> &mut StateAllocator<MachineState> {
&mut self.machine_state_allocator
}
pub(super) fn template_states(&mut self) -> &mut StateAllocator<TemplateState> {
&mut self.template_state_allocator
}
pub(super) fn network_states(&mut self) -> &mut StateAllocator<NetworkState> {
&mut self.network_state_allocator
}
pub(super) fn blueprint_states(&mut self) -> &mut StateAllocator<BlueprintState> {
&mut self.blueprint_state_allocator
}
pub(super) fn get_or_create_machine_state(
&mut self,
name: String,
machine_def: config::Machine,
opt_name: Option<String>,
params: config::Machine,
) -> MachineRegistryResult<MachineState> {
// Ensure we don't already have this machine created (name must be unique)
if let Some(machine_id) = self.resolve_to_manager_machine.add(name.clone()).get() {
return Ok(self
.machine_state_by_id
.get(&machine_id)
.cloned()
.expect("must exist"));
if let Some(name) = &opt_name {
if let Some(machine_id) = self.resolve_to_manager_machine.add(name.clone()).get() {
return Ok(self
.machine_state_by_id
.get(&machine_id)
.cloned()
.expect("must exist"));
}
}
// Allocate a machine id
@ -148,404 +152,409 @@ impl MachineRegistryInner {
.expect("must exist"))
}
pub(super) fn get_machine_state_by_id(
&mut self,
machine_id: MachineId,
) -> MachineRegistryResult<&mut MachineState> {
self.machine_state_by_id
.get_mut(&machine_id)
.ok_or_else(|| MachineRegistryError::MachineNotFound)
}
// pub(super) fn get_machine_state_by_id(
// &mut self,
// machine_id: MachineId,
// ) -> MachineRegistryResult<&mut MachineState> {
// self.machine_state_by_id
// .get_mut(&machine_id)
// .ok_or_else(|| MachineRegistryError::MachineNotFound)
// }
pub(super) fn get_or_create_network_state(
&mut self,
name: String,
network_def: config::Network,
) -> MachineRegistryResult<NetworkState> {
// Ensure we don't already have this network created (name must be unique)
if let Some(network_id) = self.resolve_to_manager_network.add(name.clone()).get() {
return Ok(self
.network_state_by_id
.get(&network_id)
.cloned()
.expect("must exist"));
}
// pub(super) fn get_or_create_network_state(
// &mut self,
// name: String,
// ) -> MachineRegistryResult<NetworkState> {
// Allocate a network id
let network_id = self.free_network_ids.pop().unwrap_or_else(|| {
let x = self.next_network_id;
self.next_network_id += 1;
x
});
// // Get the network def from the config
// let Some(network_def) = self.unlocked_inner.config.networks.get(&name).cloned() else {
// return Err(MachineRegistryError::NetworkNotFound);
// };
// Create a new network state
let network_state = match NetworkState::try_new(
self,
network_id,
NetworkStateName::Network(name.clone()),
network_def.clone(),
) {
Ok(v) => v,
Err(e) => {
// Release the network id
self.free_network_ids.push(network_id);
return Err(e);
}
};
// // Ensure we don't already have this network created (name must be unique)
// if let Some(network_id) = self.resolve_to_manager_network.add(name.clone()).get() {
// return Ok(self
// .network_state_by_id
// .get(&network_id)
// .cloned()
// .expect("must exist"));
// }
// Store the network state with its unique id
self.network_state_by_id.insert(network_id, network_state);
// // Allocate a network id
// let network_id = self.free_network_ids.pop().unwrap_or_else(|| {
// let x = self.next_network_id;
// self.next_network_id += 1;
// x
// });
// Bind the name to the id
self.resolve_to_manager_network
.resolve(&name, network_id)
.expect("must resolve");
// // Create a new network state
// let network_state = match NetworkState::try_new(
// self,
// network_id,
// NetworkStateName::Network(name.clone()),
// network_def.clone(),
// ) {
// Ok(v) => v,
// Err(e) => {
// // Release the network id
// self.free_network_ids.push(network_id);
// return Err(e);
// }
// };
// Return the unique id
Ok(self
.network_state_by_id
.get(&network_id)
.cloned()
.expect("must exist"))
}
// // Store the network state with its unique id
// self.network_state_by_id.insert(network_id, network_state);
pub(super) fn get_network_state_by_name(&self, name: &String) -> Option<NetworkState> {
let network_id = self.resolve_to_manager_network.get(name)?;
self.network_state_by_id.get(&network_id).cloned()
}
// // Bind the name to the id
// self.resolve_to_manager_network
// .resolve(&name, network_id)
// .expect("must resolve");
pub(super) fn get_network_state_by_id(
&self,
network_id: NetworkId,
) -> MachineRegistryResult<NetworkState> {
self.network_state_by_id
.get(&network_id)
.cloned()
.ok_or_else(|| MachineRegistryError::NetworkNotFound)
}
// // Return the unique id
// Ok(self
// .network_state_by_id
// .get(&network_id)
// .cloned()
// .expect("must exist"))
// }
pub(super) fn get_or_create_template_state(
&mut self,
name: &String,
template_def: config::Template,
) -> MachineRegistryResult<&mut TemplateState> {
// Ensure we don't already have this template created (name must be unique)
if self.template_state_by_name.contains_key(name) {
return Ok(self
.template_state_by_name
.get_mut(name)
.expect("must exist"));
}
// pub(super) fn get_network_state_by_name(&self, name: &String) -> Option<NetworkState> {
// let network_id = self.resolve_to_manager_network.get(name)?;
// self.network_state_by_id.get(&network_id).cloned()
// }
// Create a new template state
let template_state = match TemplateState::try_new(self, name.clone(), template_def.clone())
{
Ok(v) => v,
Err(e) => {
return Err(e);
}
};
// pub(super) fn get_network_state_by_id(
// &self,
// network_id: NetworkId,
// ) -> MachineRegistryResult<NetworkState> {
// self.network_state_by_id
// .get(&network_id)
// .cloned()
// .ok_or_else(|| MachineRegistryError::NetworkNotFound)
// }
// Store the template state with its name
self.template_state_by_name
.insert(name.clone(), template_state);
Ok(self
.template_state_by_name
.get_mut(name)
.expect("must exist"))
}
// pub(super) fn get_or_create_template_state(
// &mut self,
// name: &String,
// template_def: config::Template,
// ) -> MachineRegistryResult<&mut TemplateState> {
// // Ensure we don't already have this template created (name must be unique)
// if self.template_state_by_name.contains_key(name) {
// return Ok(self
// .template_state_by_name
// .get_mut(name)
// .expect("must exist"));
// }
pub(super) fn get_or_create_machine_state_from_template(
&mut self,
name: String,
template_def: config::Template,
) -> MachineRegistryResult<MachineState> {
// Make machine def from current template state
let machine_def = {
// Get the active template state
let template_state = self.get_or_create_template_state(&name, template_def)?;
if !template_state.is_active(self)? {
return Err(MachineRegistryError::TemplateComplete);
}
// // Create a new template state
// let template_state = match TemplateState::try_new(self, name.clone(), template_def.clone())
// {
// Ok(v) => v,
// Err(e) => {
// return Err(e);
// }
// };
// Pick or instantiate an available network
template_state.instantiate(self)?
// // Store the template state with its name
// self.template_state_by_name
// .insert(name.clone(), template_state);
// Ok(self
// .template_state_by_name
// .get_mut(name)
// .expect("must exist"))
// }
xxx how to pass through per-network limits
};
// pub(super) fn get_or_create_machine_state_from_template(
// &mut self,
// name: String,
// template_def: config::Template,
// ) -> MachineRegistryResult<MachineState> {
// // Make machine def from current template state
// let machine_def = {
// // Get the active template state
// let template_state = self.get_or_create_template_state(&name, template_def)?;
// if !template_state.is_active(self)? {
// return Err(MachineRegistryError::TemplateComplete);
// }
// Allocate a machine id
let machine_id = self.free_machine_ids.pop().unwrap_or_else(|| {
let x = self.next_machine_id;
self.next_machine_id += 1;
x
});
// // Pick or instantiate an available network
// template_state.generate(self)?
// Create a new machine state
let machine_state = match MachineState::try_new(
self,
machine_id,
MachineStateName::Template(name.clone()),
machine_def.clone(),
) {
Ok(v) => v,
Err(e) => {
// Release the machine id
self.free_machine_ids.push(machine_id);
return Err(e);
}
};
// xxx how to pass through per-network limits
// };
// Store the machine state with its unique id
self.machine_state_by_id.insert(machine_id, machine_state);
// // Allocate a machine id
// let machine_id = self.free_machine_ids.pop().unwrap_or_else(|| {
// let x = self.next_machine_id;
// self.next_machine_id += 1;
// x
// });
// Add to machines for this template
{
let template_state = self.get_template_state(&name).expect("must exist");
template_state.machines.insert(machine_id);
}
// // Create a new machine state
// let machine_state = match MachineState::try_new(
// self,
// machine_id,
// MachineStateName::Template(name.clone()),
// machine_def.clone(),
// ) {
// Ok(v) => v,
// Err(e) => {
// // Release the machine id
// self.free_machine_ids.push(machine_id);
// return Err(e);
// }
// };
// Return the unique id
Ok(machine_id)
}
pub(super) fn get_template_state(&self, name: &String) -> MachineRegistryResult<TemplateState> {
self.template_state_by_name
.get(name)
.cloned()
.ok_or_else(|| MachineRegistryError::TemplateNotFound)
}
// // Store the machine state with its unique id
// self.machine_state_by_id.insert(machine_id, machine_state);
pub(super) fn get_or_create_network_state_from_machine_location(
&mut self,
machine_location: &config::MachineLocation,
) -> MachineRegistryResult<NetworkState> {
match machine_location {
config::MachineLocation::Network {
network: name,
address4: _,
address6: _,
} => {
let network_def = self
.unlocked_inner
.config
.networks
.get(name)
.cloned()
.expect("config validation is broken");
self.get_or_create_network_state(name.clone(), network_def)
}
// // Add to machines for this template
// {
// let template_state = self.get_template_state(&name).expect("must exist");
// template_state.machines.insert(machine_id);
// }
config::MachineLocation::Blueprint { blueprint: name } => {
let blueprint_def = self
.unlocked_inner
.config
.blueprints
.get(name)
.cloned()
.expect("config validation is broken");
self.get_or_create_network_state_from_blueprint(name.clone(), blueprint_def)
}
}
}
pub(super) fn get_or_create_network_state_from_template_location(
&mut self,
template_location: &config::TemplateLocation,
) -> MachineRegistryResult<NetworkId> {
match template_location {
config::TemplateLocation::Network { network } => {
let name = self.unlocked_inner.srng.weighted_choice(network);
let network_def = self
.unlocked_inner
.config
.networks
.get(name)
.cloned()
.expect("config validation is broken");
self.get_or_create_network_state(name.clone(), network_def)
}
config::TemplateLocation::Blueprint { blueprint } => {
let name = self.unlocked_inner.srng.weighted_choice(blueprint);
let blueprint_def = self
.unlocked_inner
.config
.blueprints
.get(name)
.cloned()
.expect("config validation is broken");
self.get_or_create_network_state_from_blueprint(name.clone(), blueprint_def)
}
}
}
// // Return the unique id
// Ok(machine_id)
// }
// pub(super) fn get_template_state(&self, name: &String) -> MachineRegistryResult<TemplateState> {
// self.template_state_by_name
// .get(name)
// .cloned()
// .ok_or_else(|| MachineRegistryError::TemplateNotFound)
// }
pub(super) fn get_blueprint_state(
&self,
name: &String,
) -> MachineRegistryResult<BlueprintState> {
self.blueprint_state_by_name
.get(name)
.cloned()
.ok_or_else(|| MachineRegistryError::BlueprintNotFound)
}
// pub(super) fn get_or_create_network_state_from_machine_location(
// &mut self,
// machine_location: &config::MachineLocation,
// ) -> MachineRegistryResult<NetworkState> {
// match machine_location {
// config::MachineLocation::Network {
// network: name,
// address4: _,
// address6: _,
// } => {
// let network_def = self
// .unlocked_inner
// .config
// .networks
// .get(name)
// .cloned()
// .expect("config validation is broken");
// self.get_or_create_network_state(name.clone(), network_def)
// }
pub(super) fn choose_allocation_v4(
&mut self,
allocation: config::Allocation,
additional_prefix: u8,
) -> MachineRegistryResult<Ipv4Net> {
// Get allocation subnet candidates
let mut subnet4 = allocation
.subnets
.subnet4
.clone()
.ok_or(MachineRegistryError::NoAllocation)?;
// config::MachineLocation::Blueprint { blueprint: name } => {
// let blueprint_def = self
// .unlocked_inner
// .config
// .blueprints
// .get(name)
// .cloned()
// .expect("config validation is broken");
// self.get_or_create_network_state_from_blueprint(name.clone(), blueprint_def)
// }
// }
// }
// pub(super) fn get_or_create_network_state_from_template_location(
// &mut self,
// template_location: &config::TemplateLocation,
// ) -> MachineRegistryResult<NetworkState> {
// match template_location {
// config::TemplateLocation::Network { network } => {
// let name = self.unlocked_inner.srng.weighted_choice(network);
// let network_def = self
// .unlocked_inner
// .config
// .networks
// .get(name)
// .cloned()
// .expect("config validation is broken");
// self.get_or_create_network_state(name.clone(), network_def)
// }
// config::TemplateLocation::Blueprint { blueprint } => {
// let name = self.unlocked_inner.srng.weighted_choice(blueprint);
// let blueprint_def = self
// .unlocked_inner
// .config
// .blueprints
// .get(name)
// .cloned()
// .expect("config validation is broken");
// self.get_or_create_network_state_from_blueprint(name.clone(), blueprint_def)
// }
// }
// }
loop {
// Pick a compatible subnet from the allocation
let subnet = self.unlocked_inner.srng.weighted_choice(&subnet4);
// pub(super) fn get_blueprint_state(
// &self,
// name: &String,
// ) -> MachineRegistryResult<BlueprintState> {
// self.blueprint_state_by_name
// .get(name)
// .cloned()
// .ok_or_else(|| MachineRegistryError::BlueprintNotFound)
// }
// Allocate within the subnet
match self
.address_pool
.add_random_subnet_v4(subnet, additional_prefix)
{
Some(a) => {
// Got a sub-allocation
return Ok(a);
}
None => {
// No sub-allocation left in this subnet,
// remove the subnet so we can choose again
let Some(next_subnet4) = subnet4.filter(|x| x == subnet) else {
// No subnets left
break;
};
subnet4 = next_subnet4;
}
}
}
// pub(super) fn choose_allocation_v4(
// &mut self,
// allocation: config::Allocation,
// additional_prefix: u8,
// ) -> MachineRegistryResult<Ipv4Net> {
// // Get allocation subnet candidates
// let mut subnet4 = allocation
// .subnets
// .subnet4
// .clone()
// .ok_or(MachineRegistryError::NoAllocation)?;
// No available allocations left
Err(MachineRegistryError::NoAllocation)
}
// loop {
// // Pick a compatible subnet from the allocation
// let subnet = self.unlocked_inner.srng.weighted_choice(&subnet4);
pub(super) fn choose_allocation_v6(
&mut self,
allocation: config::Allocation,
additional_prefix: u8,
) -> MachineRegistryResult<Ipv6Net> {
// Get allocation subnet candidates
let mut subnet6 = allocation
.subnets
.subnet6
.clone()
.ok_or(MachineRegistryError::NoAllocation)?;
// // Allocate within the subnet
// match self
// .address_pool
// .add_random_subnet_v4(subnet, additional_prefix)
// {
// Some(a) => {
// // Got a sub-allocation
// return Ok(a);
// }
// None => {
// // No sub-allocation left in this subnet,
// // remove the subnet so we can choose again
// let Some(next_subnet4) = subnet4.filter(|x| x == subnet) else {
// // No subnets left
// break;
// };
// subnet4 = next_subnet4;
// }
// }
// }
loop {
// Pick a compatible subnet from the allocation
let subnet = self.unlocked_inner.srng.weighted_choice(&subnet6);
// // No available allocations left
// Err(MachineRegistryError::NoAllocation)
// }
// Allocate within the subnet
match self
.address_pool
.add_random_subnet_v6(subnet, additional_prefix)
{
Some(a) => {
// Got a sub-allocation
return Ok(a);
}
None => {
// No sub-allocation left in this subnet,
// remove the subnet so we can choose again
let Some(next_subnet6) = subnet6.filter(|x| x == subnet) else {
// No subnets left
break;
};
subnet6 = next_subnet6;
}
}
}
// pub(super) fn choose_allocation_v6(
// &mut self,
// allocation: config::Allocation,
// additional_prefix: u8,
// ) -> MachineRegistryResult<Ipv6Net> {
// // Get allocation subnet candidates
// let mut subnet6 = allocation
// .subnets
// .subnet6
// .clone()
// .ok_or(MachineRegistryError::NoAllocation)?;
// No available allocations left
Err(MachineRegistryError::NoAllocation)
}
// loop {
// // Pick a compatible subnet from the allocation
// let subnet = self.unlocked_inner.srng.weighted_choice(&subnet6);
pub(super) fn get_or_create_blueprint_state(
&mut self,
name: &String,
blueprint_def: config::Blueprint,
) -> MachineRegistryResult<BlueprintState> {
// Ensure we don't already have this blueprint created (name must be unique)
if self.blueprint_state_by_name.contains_key(name) {
return Ok(self
.blueprint_state_by_name
.get(name)
.cloned()
.expect("must exist"));
}
// // Allocate within the subnet
// match self
// .address_pool
// .add_random_subnet_v6(subnet, additional_prefix)
// {
// Some(a) => {
// // Got a sub-allocation
// return Ok(a);
// }
// None => {
// // No sub-allocation left in this subnet,
// // remove the subnet so we can choose again
// let Some(next_subnet6) = subnet6.filter(|x| x == subnet) else {
// // No subnets left
// break;
// };
// subnet6 = next_subnet6;
// }
// }
// }
// Create a new blueprint state
let blueprint_state =
match BlueprintState::try_new(self, name.clone(), blueprint_def.clone()) {
Ok(v) => v,
Err(e) => {
return Err(e);
}
};
// // No available allocations left
// Err(MachineRegistryError::NoAllocation)
// }
// Store the blueprint state with its name
self.blueprint_state_by_name
.insert(name.clone(), blueprint_state);
Ok(self
.blueprint_state_by_name
.get(name)
.cloned()
.expect("must exist"))
}
// pub(super) fn get_or_create_blueprint_state(
// &mut self,
// name: &String,
// blueprint_def: config::Blueprint,
// ) -> MachineRegistryResult<BlueprintState> {
// // Ensure we don't already have this blueprint created (name must be unique)
// if self.blueprint_state_by_name.contains_key(name) {
// return Ok(self
// .blueprint_state_by_name
// .get(name)
// .cloned()
// .expect("must exist"));
// }
pub(super) fn get_or_create_network_state_from_blueprint(
&mut self,
name: String,
blueprint_def: config::Blueprint,
) -> MachineRegistryResult<NetworkState> {
// Get the active blueprint state
let blueprint_state = self.get_or_create_blueprint_state(&name, blueprint_def)?;
if !blueprint_state.is_active(self)? {
return Err(MachineRegistryError::BlueprintComplete);
}
// // Create a new blueprint state
// let blueprint_state =
// match BlueprintState::try_new(self, name.clone(), blueprint_def.clone()) {
// Ok(v) => v,
// Err(e) => {
// return Err(e);
// }
// };
// Make network def from current blueprint state
let machine_def = config::Network {
model: self.unlocked_inner.srng.weighted_choice(blueprint_state),
ipv4: todo!(),
ipv6: todo!(),
};
// // Store the blueprint state with its name
// self.blueprint_state_by_name
// .insert(name.clone(), blueprint_state);
// Ok(self
// .blueprint_state_by_name
// .get(name)
// .cloned()
// .expect("must exist"))
// }
// Allocate a machine id
let machine_id = self.free_machine_ids.pop().unwrap_or_else(|| {
let x = self.next_machine_id;
self.next_machine_id += 1;
x
});
// pub(super) fn get_or_create_network_state_from_blueprint(
// &mut self,
// name: String,
// blueprint_def: config::Blueprint,
// ) -> MachineRegistryResult<NetworkState> {
// // Get the active blueprint state
// let blueprint_state = self.get_or_create_blueprint_state(&name, blueprint_def)?;
// if !blueprint_state.is_active(self)? {
// return Err(MachineRegistryError::BlueprintComplete);
// }
// Create a new machine state
let machine_state = match MachineState::try_new(
self,
MachineStateName::Template(name.clone()),
machine_def.clone(),
machine_id,
) {
Ok(v) => v,
Err(e) => {
// Release the machine id
self.free_machine_ids.push(machine_id);
return Err(e);
}
};
// // Make network def from current blueprint state
// let machine_def = config::Network {
// model: self.unlocked_inner.srng.weighted_choice(blueprint_state),
// ipv4: todo!(),
// ipv6: todo!(),
// };
// Store the machine state with its unique id
self.machine_state_by_id.insert(machine_id, machine_state);
// // Allocate a machine id
// let machine_id = self.free_machine_ids.pop().unwrap_or_else(|| {
// let x = self.next_machine_id;
// self.next_machine_id += 1;
// x
// });
// Return the unique id
Ok(machine_id)
}
// // Create a new machine state
// let machine_state = match MachineState::try_new(
// self,
// MachineStateName::Template(name.clone()),
// machine_def.clone(),
// machine_id,
// ) {
// Ok(v) => v,
// Err(e) => {
// // Release the machine id
// self.free_machine_ids.push(machine_id);
// return Err(e);
// }
// };
// // Store the machine state with its unique id
// self.machine_state_by_id.insert(machine_id, machine_state);
// // Return the unique id
// Ok(machine_id)
// }
}

View File

@ -0,0 +1,29 @@
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

@ -9,7 +9,7 @@ struct BlueprintStateUnlockedInner {
#[derive(Debug)]
struct BlueprintStateInner {
limit_network_count: Option<u32>,
networks: HashSet<NetworkId>,
networks: Vec<NetworkId>,
}
#[derive(Debug, Clone)]
@ -38,7 +38,7 @@ impl BlueprintState {
}),
inner: Arc::new(Mutex::new(BlueprintStateInner {
limit_network_count,
networks: HashSet::new(),
networks: Vec::new(),
})),
})
}
@ -47,23 +47,36 @@ impl BlueprintState {
&self.unlocked_inner.blueprint_def
}
pub fn is_active(
&self,
machine_registry_inner: &MachineRegistryInner,
) -> MachineRegistryResult<bool> {
pub fn is_active(&self) -> MachineRegistryResult<bool> {
let inner = self.inner.lock();
// Check to see if any of our networks are still active, if so the blueprint is still active
for network_id in &inner.networks {
let network_state = machine_registry_inner
.get_network_state_by_id(*network_id)
.expect("must exist");
if network_state.is_active()? {
return Ok(true);
// 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.try_into().unwrap_or(usize::MAX) {
return Ok(false);
}
}
// If no existing networks are active, then we see if there's room for another
Ok(inner.networks.len() < inner.limit_network_count.try_into().unwrap_or(usize::MAX))
Ok(true)
}
pub fn generate(
&self,
machine_registry_inner: &MachineRegistryInner,
) -> MachineRegistryResult<config::Network> {
//
}
pub fn for_each_network_id<F, R>(&self, mut callback: F) -> MachineRegistryResult<Option<R>>
where
F: FnMut(NetworkId) -> MachineRegistryResult<Option<R>>,
{
let inner = self.inner.lock();
for network_id in &inner.networks {
if let Some(res) = callback(*network_id)? {
return Ok(Some(res));
}
}
Ok(None)
}
}

View File

@ -1,11 +1,5 @@
use super::*;
#[derive(Debug, Clone)]
pub enum MachineStateName {
Machine(String),
Template(String),
}
#[derive(Debug)]
struct MachineStateInner {
/// The current network interfaces definition
@ -15,7 +9,7 @@ struct MachineStateInner {
#[derive(Debug)]
pub struct MachineStateInterface {
/// The network this interface belongs to
pub network_id: NetworkId,
pub network_id: NetworkStateId,
/// The veilid NetworkInterface state
pub network_interface: NetworkInterface,
}
@ -23,12 +17,9 @@ pub struct MachineStateInterface {
#[derive(Debug)]
struct MachineStateUnlockedInner {
/// The id of this machine
id: MachineId,
/// The name of this machine state if it was made directly
/// or the name of the template used to create it
name: MachineStateName,
/// The definition this machine was created with
machine_def: config::Machine,
id: MachineStateId,
/// The name of this machine if it is named
opt_name: Option<String>,
}
#[derive(Debug, Clone)]
@ -37,123 +28,30 @@ pub struct MachineState {
inner: Arc<Mutex<MachineStateInner>>,
}
pub type MachineStateId = StateId<MachineState>;
impl MachineState {
pub fn try_new(
machine_registry_inner: &mut MachineRegistryInner,
id: MachineId,
name: MachineStateName,
machine_def: config::Machine,
) -> MachineRegistryResult<Self> {
// Build list of machinestate interfaces
let mut interfaces = Vec::<MachineStateInterface>::new();
// Make default route interface
{
// Find existing network or create a new one from network or blueprint definition
let network_state = machine_registry_inner
.get_or_create_network_state_from_machine_location(&machine_def.location)?;
let srng = machine_registry_inner.unlocked_inner.srng.clone();
// Build list of default route interface addresses
let mut addrs = Vec::<InterfaceAddress>::new();
// Make the default route interface
let machine_location = machine_def.location.clone();
let (allocate_v4, opt_address4, allocate_v6, opt_address6) = match machine_location {
config::MachineLocation::Network {
network: _,
address4,
address6,
} => (
network_state.is_ipv4() && address4.is_some(),
address4,
network_state.is_ipv6() && address6.is_some(),
address6,
),
config::MachineLocation::Blueprint { blueprint: _ } => {
(network_state.is_ipv4(), None, network_state.is_ipv6(), None)
}
};
if allocate_v4 {
let if_addr4 =
match network_state.allocate_address_v4(srng.clone(), id, opt_address4) {
Ok(v) => v,
Err(e) => {
network_state
.release_all_addresses(addrs.iter().map(|x| x.if_addr.ip()))
.expect("must succeed");
return Err(e);
}
};
addrs.push(InterfaceAddress {
if_addr: IfAddr::V4(if_addr4),
flags: AddressFlags {
is_dynamic: false,
is_temporary: false,
is_preferred: true,
},
});
}
if allocate_v6 {
let if_addr6 =
match network_state.allocate_address_v6(srng.clone(), id, opt_address6) {
Ok(v) => v,
Err(e) => {
network_state
.release_all_addresses(addrs.iter().map(|x| x.if_addr.ip()))
.expect("must succeed");
return Err(e);
}
};
addrs.push(InterfaceAddress {
if_addr: IfAddr::V6(if_addr6),
flags: AddressFlags {
is_dynamic: false,
is_temporary: false,
is_preferred: true,
},
});
}
// Allocate an address on the network and make an veilid-style interface record for it
let network_interface = NetworkInterface {
name: "vin0".to_owned(),
flags: InterfaceFlags {
is_loopback: false,
is_running: true,
is_point_to_point: false,
has_default_route: true,
},
addrs,
};
interfaces.push(MachineStateInterface {
network_id: network_state.id(),
network_interface,
});
}
pub fn new(id: MachineStateId, opt_name: Option<String>) -> Self {
// Create a localhost interface for this machine
Ok(Self {
unlocked_inner: Arc::new(MachineStateUnlockedInner {
id,
name,
machine_def,
}),
inner: Arc::new(Mutex::new(MachineStateInner { interfaces })),
})
Self {
unlocked_inner: Arc::new(MachineStateUnlockedInner { id, opt_name }),
inner: Arc::new(Mutex::new(MachineStateInner {
interfaces: Vec::new(),
})),
}
}
pub fn release(&self, machine_registry_inner: &mut MachineRegistryInner) {
let network_states = {
let interfaces = {
let mut inner = self.inner.lock();
core::mem::take(&mut inner.interfaces)
};
for intf in network_states {
for intf in interfaces {
let network_state = machine_registry_inner
.get_network_state_by_id(intf.network_id)
.expect("must exist");
.network_states()
.get_state(intf.network_id)
.expect("must exist")
.expect("must be bound");
let addrs = &intf.network_interface.addrs;
network_state
@ -162,15 +60,124 @@ impl MachineState {
}
}
pub fn name(&self) -> MachineStateName {
self.unlocked_inner.name.clone()
pub fn external_id(&self) -> MachineId {
self.unlocked_inner.id.0
}
pub fn def(&self) -> &config::Machine {
&self.unlocked_inner.machine_def
fn next_free_interface_name(&self) -> String {}
xxx implement this
pub fn allocate_interface(
&self,
machine_registry_inner: &mut MachineRegistryInner,
network_id: NetworkStateId,
address4: Option<Ipv4Addr>,
address6: Option<Ipv6Addr>,
) -> MachineRegistryResult<String> {
// Find existing network or create a new one from network or blueprint definition
let network_state = match params {
MachineParameters::Direct {
network_id,
disable_capabilities: _,
bootstrap: _,
} => todo!(),
MachineParameters::Config { name, def } => {
machine_registry_inner
.get_or_create_network_state_from_machine_location(&def.location)?;
}
};
let srng = machine_registry_inner.unlocked_inner.srng.clone();
// Build list of default route interface addresses
let mut addrs = Vec::<InterfaceAddress>::new();
// Make the default route interface
let machine_location = machine_def.location.clone();
let (allocate_v4, opt_address4, allocate_v6, opt_address6) = match machine_location {
config::MachineLocation::Network {
network: _,
address4,
address6,
} => (
network_state.is_ipv4() && address4.is_some(),
address4,
network_state.is_ipv6() && address6.is_some(),
address6,
),
config::MachineLocation::Blueprint { blueprint: _ } => {
(network_state.is_ipv4(), None, network_state.is_ipv6(), None)
}
};
if allocate_v4 {
let if_addr4 = match network_state.allocate_address_v4(srng.clone(), id, opt_address4) {
Ok(v) => v,
Err(e) => {
network_state
.release_all_addresses(addrs.iter().map(|x| x.if_addr.ip()))
.expect("must succeed");
return Err(e);
}
};
addrs.push(InterfaceAddress {
if_addr: IfAddr::V4(if_addr4),
flags: AddressFlags {
is_dynamic: false,
is_temporary: false,
is_preferred: true,
},
});
}
if allocate_v6 {
let if_addr6 = match network_state.allocate_address_v6(srng.clone(), id, opt_address6) {
Ok(v) => v,
Err(e) => {
network_state
.release_all_addresses(addrs.iter().map(|x| x.if_addr.ip()))
.expect("must succeed");
return Err(e);
}
};
addrs.push(InterfaceAddress {
if_addr: IfAddr::V6(if_addr6),
flags: AddressFlags {
is_dynamic: false,
is_temporary: false,
is_preferred: true,
},
});
}
// Allocate an address on the network and make an veilid-style interface record for it
let network_interface = NetworkInterface {
name: "vin0".to_owned(),
flags: InterfaceFlags {
is_loopback: false,
is_running: true,
is_point_to_point: false,
has_default_route: true,
},
addrs,
};
interfaces.push(MachineStateInterface {
network_id: network_state.id(),
network_interface,
});
}
pub fn id(&self) -> MachineId {
self.unlocked_inner.id
pub fn release_interface(&self) -> () {
//
}
}
impl State for MachineState {
fn id(&self) -> StateId<Self> {
self.unlocked_inner.id.clone()
}
fn name(&self) -> Option<String> {
self.unlocked_inner.opt_name.clone()
}
}

View File

@ -1,15 +1,19 @@
mod availability;
mod blueprint_state;
mod machine_state;
mod network_state;
mod profile_state;
mod resolves_to;
mod state_allocator;
mod template_state;
use std::marker::PhantomData;
use super::*;
pub use availability::*;
pub use blueprint_state::*;
pub use machine_state::*;
pub use network_state::*;
pub use profile_state::*;
pub use resolves_to::*;
pub use state_allocator::*;
pub use template_state::*;

View File

@ -1,155 +1,135 @@
use super::*;
use ipnet::*;
#[derive(Debug, Clone)]
pub enum NetworkStateName {
Network(String),
Blueprint(String),
}
#[derive(Debug)]
pub struct NetworkStateUnlockedInner {
struct NetworkStateUnlockedInner {
/// The unique id of this network
id: NetworkId,
id: NetworkStateId,
/// The name of this network state if it was made directly
/// or the name of the blueprint used to create it
name: NetworkStateName,
/// The network definition used to create this network
network_def: config::Network,
opt_name: Option<String>,
}
#[derive(Debug)]
pub struct NetworkStateInner {
/// The model chosen for this network
model: String,
struct NetworkStateInner {
/// Network latency distribution
latency: Option<config::Distribution>,
/// Distance simulation metric
distance: Option<config::Distance>,
/// Packet loss probability
loss: config::Probability,
/// IPv4 state if it is enabled
ipv4: Option<NetworkStateIpv4>,
/// IPv6 state if it is enabled
ipv6: Option<NetworkStateIpv6>,
}
#[derive(Debug)]
struct NetworkStateIpv4 {
allocation: Ipv4Net,
gateway: Option<NetworkGatewayState>,
machine_addresses: HashMap<Ipv4Addr, MachineId>,
}
#[derive(Debug)]
struct NetworkStateIpv6 {
allocation: Ipv6Net,
gateway: Option<NetworkGatewayState>,
machine_addresses: HashMap<Ipv6Addr, MachineId>,
}
#[derive(Debug)]
struct NetworkGatewayState {
translation: config::Translation, // xxx replace with translation state
upnp: bool,
network: Option<NetworkStateId>,
}
#[derive(Debug, Clone)]
pub struct NetworkState {
unlocked_inner: Arc<NetworkStateUnlockedInner>,
inner: Arc<Mutex<NetworkStateInner>>,
}
#[derive(Debug)]
pub struct NetworkStateIpv4 {
pub allocation: Ipv4Net,
pub gateway: Option<NetworkGatewayState>,
pub machine_addresses: HashMap<Ipv4Addr, MachineId>,
}
#[derive(Debug)]
pub struct NetworkStateIpv6 {
pub allocation: Ipv6Net,
pub gateway: Option<NetworkGatewayState>,
pub machine_addresses: HashMap<Ipv6Addr, MachineId>,
}
#[derive(Debug)]
pub struct NetworkGatewayState {
pub translation: config::Translation, // xxx replace with translation state
pub upnp: bool,
pub network: Option<ResolvesTo<NetworkId>>,
}
pub type NetworkId = u64;
pub type NetworkStateId = StateId<NetworkState>;
impl NetworkState {
pub fn try_new(
machine_registry_inner: &mut MachineRegistryInner,
id: NetworkId,
name: NetworkStateName,
network_def: config::Network,
) -> MachineRegistryResult<Self> {
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,
};
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,
};
Ok(Self {
unlocked_inner: Arc::new(NetworkStateUnlockedInner {
id,
name,
network_def,
}),
inner: Arc::new(Mutex::new(NetworkStateInner { model, ipv4, ipv6 })),
})
pub fn new(id: NetworkStateId, opt_name: Option<String>) -> Self {
Self {
unlocked_inner: Arc::new(NetworkStateUnlockedInner { id, opt_name }),
inner: Arc::new(Mutex::new(NetworkStateInner {
latency: None,
distance: None,
loss: 0.0,
ipv4: None,
ipv6: None,
})),
}
}
pub fn name(&self) -> NetworkStateName {
self.unlocked_inner.name.clone()
}
// 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 def(&self) -> &config::Network {
&self.unlocked_inner.network_def
}
pub fn id(&self) -> NetworkId {
self.unlocked_inner.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 is_ipv4(&self) -> bool {
self.inner.lock().ipv4.is_some()
@ -199,7 +179,7 @@ impl NetworkState {
Ok(can_allocate)
}
pub(super) fn allocate_address_v4(
pub fn allocate_address_v4(
&self,
srng: StableRng,
machine_id: MachineId,
@ -270,7 +250,7 @@ impl NetworkState {
Ok(ifaddr)
}
pub(super) fn release_address_v4(&self, addr: Ipv4Addr) -> MachineRegistryResult<()> {
pub fn release_address_v4(&self, addr: Ipv4Addr) -> MachineRegistryResult<()> {
let mut inner = self.inner.lock();
Self::release_address_v4_inner(&mut *inner, addr)
}
@ -287,7 +267,7 @@ impl NetworkState {
Err(MachineRegistryError::NoAllocation)
}
pub(super) fn allocate_address_v6(
pub fn allocate_address_v6(
&self,
srng: StableRng,
machine_id: MachineId,
@ -358,7 +338,7 @@ impl NetworkState {
Ok(ifaddr)
}
pub(super) fn release_address_v6(&self, addr: Ipv6Addr) -> MachineRegistryResult<()> {
pub fn release_address_v6(&self, addr: Ipv6Addr) -> MachineRegistryResult<()> {
let mut inner = self.inner.lock();
Self::release_address_v6_inner(&mut *inner, addr)
}
@ -374,7 +354,7 @@ impl NetworkState {
Err(MachineRegistryError::NoAllocation)
}
pub(super) fn release_all_addresses<I: Iterator<Item = IpAddr>>(
pub fn release_all_addresses<I: Iterator<Item = IpAddr>>(
&self,
addrs: I,
) -> MachineRegistryResult<()> {
@ -402,3 +382,13 @@ impl NetworkState {
}
}
}
impl State for NetworkState {
fn id(&self) -> StateId<Self> {
self.unlocked_inner.id.clone()
}
fn name(&self) -> Option<String> {
self.unlocked_inner.opt_name.clone()
}
}

View File

@ -1,4 +1,55 @@
#[derive(Debug, Default)]
pub struct ProfileState {
pub next_instance_index: usize,
use super::*;
#[derive(Debug)]
struct ProfileStateInner {
next_instance_index: usize,
}
#[derive(Debug)]
struct ProfileStateUnlockedInner {
id: ProfileStateId,
name: String,
def: config::Profile,
}
#[derive(Debug, Clone)]
pub struct ProfileState {
unlocked_inner: Arc<ProfileStateUnlockedInner>,
inner: Arc<Mutex<ProfileStateInner>>,
}
pub type ProfileStateId = StateId<ProfileState>;
impl ProfileState {
pub fn new(id: ProfileStateId, name: String, def: config::Profile) -> Self {
Self {
unlocked_inner: Arc::new(ProfileStateUnlockedInner { id, name, def }),
inner: Arc::new(Mutex::new(ProfileStateInner {
next_instance_index: 0,
})),
}
}
pub fn next_instance(&self) -> Option<config::Instance> {
let instance_index = {
let mut inner = self.inner.lock();
let instance_index = inner.next_instance_index;
if instance_index >= self.unlocked_inner.def.instances.len() {
return None;
}
inner.next_instance_index += 1;
instance_index
};
Some(self.unlocked_inner.def.instances[instance_index].clone())
}
}
impl State for ProfileState {
fn id(&self) -> StateId<Self> {
self.unlocked_inner.id.clone()
}
fn name(&self) -> Option<String> {
Some(self.unlocked_inner.name.clone())
}
}

View File

@ -0,0 +1,211 @@
use super::*;
#[derive(ThisError, Debug)]
pub enum StateAllocatorReleaseError {
#[error("invalid state id")]
InvalidId,
}
pub type StateAllocatorReleaseResult<T> = Result<T, StateAllocatorReleaseError>;
#[derive(ThisError, Debug)]
pub enum StateAllocatorAttachError {
#[error("invalid state id")]
InvalidId,
#[error("state already attached")]
AlreadyAttached,
#[error("duplicate name")]
DuplicateName,
}
pub type StateAllocatorAttachResult<T> = Result<T, StateAllocatorAttachError>;
#[derive(ThisError, Debug)]
pub enum StateAllocatorDetachError {
#[error("invalid state id")]
InvalidId,
#[error("state already detached")]
AlreadyDetached,
}
pub type StateAllocatorDetachResult<T> = Result<T, StateAllocatorDetachError>;
#[derive(ThisError, Debug)]
pub enum StateAllocatorGetStateError {
#[error("invalid state id")]
InvalidId,
}
pub type StateAllocatorGetStateResult<T> = Result<T, StateAllocatorGetStateError>;
#[derive(ThisError, Debug)]
pub enum StateAllocatorGetOrCreateByNameError {
#[error("duplicate name")]
DuplicateName,
}
pub type StateAllocatorGetOrCreateByNameResult<T> = Result<T, StateAllocatorGetOrCreateByNameError>;
pub trait State: fmt::Debug + Clone {
fn id(&self) -> StateId<Self>;
fn name(&self) -> Option<String>;
}
type StateIdInternal = u64;
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct StateId<S: State>(pub StateIdInternal, core::marker::PhantomData<S>);
#[derive(Debug)]
pub struct StateAllocator<S: State> {
state_id_by_name: HashMap<String, StateIdInternal>,
state_by_id: HashMap<StateIdInternal, Option<S>>,
next_state_id: StateIdInternal,
free_state_ids: Vec<StateIdInternal>,
}
impl<S: State> StateAllocator<S> {
pub fn new() -> Self {
Self {
state_id_by_name: HashMap::new(),
state_by_id: HashMap::new(),
next_state_id: 0,
free_state_ids: Vec::new(),
}
}
pub fn get_or_create_by_name<F: FnOnce(StateId<S>, String) -> S>(
&mut self,
name: String,
create: F,
) -> S {
if let Some(v) = self.get_state_by_name(&name) {
return v.clone();
}
let id = self.allocate_id();
let state = create(id, name);
self.attach_state(state.clone())
.expect("should always attach");
state
}
pub fn allocate_id(&mut self) -> StateId<S> {
// Allocate new internal id
let state_id = self.free_state_ids.pop().unwrap_or_else(|| {
let x = self.next_state_id;
self.next_state_id += 1;
x
});
// Associate with an empty state slot
self.state_by_id.insert(state_id, None);
// Return the type-safe wrapped id
StateId(state_id, PhantomData {})
}
pub fn release_id(&mut self, id: StateId<S>) -> StateAllocatorReleaseResult<()> {
// Remove id to state mapping
let Some(old_opt_state) = self.state_by_id.remove(&id.0) else {
return Err(StateAllocatorReleaseError::InvalidId);
};
// Release state if it is attached
if let Some(old_state) = old_opt_state {
// Release name of state if it is named
if let Some(name) = old_state.name() {
self.state_id_by_name
.remove(&name)
.expect("named states should be registered");
}
}
// Keep old id in the free list
self.free_state_ids.push(id.0);
Ok(())
}
pub fn attach_state(&mut self, state: S) -> StateAllocatorAttachResult<()> {
// Get the id from the state
let id = state.id();
// Get the allocator slot
let Some(opt_state) = self.state_by_id.get_mut(&id.0) else {
return Err(StateAllocatorAttachError::InvalidId);
};
// Ensure the state slot isn't attached already
if opt_state.is_some() {
return Err(StateAllocatorAttachError::AlreadyAttached);
}
// Ensure the name isn't duplicated
if let Some(name) = state.name() {
if self.state_id_by_name.contains_key(&name) {
return Err(StateAllocatorAttachError::DuplicateName);
}
// Register the named state
self.state_id_by_name
.insert(name, id.0)
.expect("should not have a duplicated name here");
}
// Attach the state to the state slot
*opt_state = Some(state);
Ok(())
}
pub fn detach_state(&mut self, id: StateId<S>) -> StateAllocatorDetachResult<S> {
// Get the allocator slot
let Some(opt_state) = self.state_by_id.get_mut(&id.0) else {
return Err(StateAllocatorDetachError::InvalidId);
};
// 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(StateAllocatorDetachError::AlreadyDetached);
};
// Release the name if it exists
if let Some(name) = state.name() {
let dead_name_id = self
.state_id_by_name
.remove(&name)
.expect("name should be registered");
assert_eq!(dead_name_id, id.0, "name id and state id should match");
}
Ok(state)
}
pub fn get_state(&self, id: StateId<S>) -> StateAllocatorGetStateResult<Option<S>> {
// Get the allocator slot
let Some(opt_state) = self.state_by_id.get(&id.0).cloned() else {
return Err(StateAllocatorGetStateError::InvalidId);
};
Ok(opt_state)
}
pub fn get_state_by_name(&self, name: &String) -> Option<S> {
// Get the id associated with this name
let Some(id) = self.state_id_by_name.get(name) else {
return None;
};
// Get the allocator slot
let opt_state = self
.state_by_id
.get(&id)
.cloned()
.expect("id should always be valid");
// The state should be attached otherwise we screwed up
let state = opt_state.expect("named states should always be attached");
Some(state)
}
}
impl<S: State> Default for StateAllocator<S> {
fn default() -> Self {
Self::new()
}
}

View File

@ -6,11 +6,17 @@ struct TemplateStateUnlockedInner {
template_def: config::Template,
}
#[derive(Debug)]
struct PerNetworkInfo {
limit_machine_count: u32,
machines: HashSet<MachineId>,
}
#[derive(Debug)]
struct TemplateStateInner {
limit_machine_count: Option<u32>,
limit_machines_per_network: u32,
machines: HashSet<MachineId>,
machines_per_network: HashMap<NetworkId, PerNetworkInfo>,
}
#[derive(Debug, Clone)]
@ -31,17 +37,13 @@ impl TemplateState {
.srng
.weighted_choice(mc)
});
let limit_machines_per_network = *machine_registry_inner
.unlocked_inner
.srng
.weighted_choice(&template_def.limits.machines_per_network);
Ok(Self {
unlocked_inner: Arc::new(TemplateStateUnlockedInner { name, template_def }),
inner: Arc::new(Mutex::new(TemplateStateInner {
limit_machine_count,
limit_machines_per_network,
machines: HashSet::new(),
machines_per_network: HashMap::new(),
})),
})
}
@ -54,26 +56,91 @@ impl TemplateState {
&self.unlocked_inner.template_def
}
fn is_network_available_inner(
inner: &TemplateStateInner,
network_state: NetworkState,
) -> MachineRegistryResult<bool> {
// If the network is not active, it is not available
if !network_state.is_active()? {
return Ok(false);
}
// Get the per network info
let Some(pni) = inner.machines_per_network.get(&network_state.id()) else {
// If we haven't allocated anything in the network yet it is
// by definition available
return Ok(true);
};
// If this template has not yet allocated the maximum number of machines per-network
// for this network, then it is available
if pni.machines.len() < pni.limit_machine_count.try_into().unwrap_or(usize::MAX) {
return Ok(true);
}
Ok(false)
}
fn is_blueprint_available_inner(
inner: &TemplateStateInner,
machine_registry_inner: &MachineRegistryInner,
blueprint_state: BlueprintState,
) -> MachineRegistryResult<Availability<NetworkId>> {
// 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(|network_id| {
// Check the network's availability
let network_state = machine_registry_inner.get_network_state_by_id(network_id)?;
if Self::is_network_available_inner(inner, network_state)? {
// We found one
return Ok(Some(network_id));
}
// Try next network
Ok(None)
})? {
// We found a usable network
return Ok(Availability::Existing(available_network_id));
}
// If the blueprint is active, it is available because it can make a new network
if blueprint_state.is_active()? {
return Ok(Availability::New);
}
Ok(Availability::None)
}
pub fn is_active(
&self,
machine_registry_inner: &mut MachineRegistryInner,
) -> MachineRegistryResult<bool> {
let inner = self.inner.lock();
// See if we have reached our machine limit
if let Some(limit_machine_count) = inner.limit_machine_count {
if inner.machines.len() < limit_machine_count.try_into().unwrap_or(usize::MAX) {
return Ok(false);
}
}
// See if any of our existing networks have room to allocate (machines could have been removed)
for (_network_id, pni) in &inner.machines_per_network {
// If this template has not yet allocated the maximum number of machines per-network
// for this network, then it is available
if pni.machines.len() < pni.limit_machine_count.try_into().unwrap_or(usize::MAX) {
return Ok(true);
}
}
// If existing networks are all full, we'd have to allocate one, see if we'd be able to do that
match self.def().location.clone() {
config::TemplateLocation::Network { network } => {
// Filter the weighted list of networks to those that are still active or not yet started and can allocate
// Filter the weighted list of networks to those that are still active and or not yet started
if network
.try_filter(|n| {
machine_registry_inner
.get_network_state_by_name(&n)
.clone()
.map(|ns| ns.is_active())
.map(|ns| Self::is_network_available_inner(&*inner, ns))
.unwrap_or(Ok(true))
})?
.is_none()
@ -88,7 +155,14 @@ impl TemplateState {
machine_registry_inner
.get_blueprint_state(&b)
.clone()
.map(|bs| bs.is_active(machine_registry_inner))
.map(|bs| {
Self::is_blueprint_available_inner(
&*inner,
machine_registry_inner,
bs,
)
.map(|x| !matches!(x, Availability::None))
})
.unwrap_or(Ok(true))
})?
.is_none()
@ -101,19 +175,26 @@ impl TemplateState {
Ok(true)
}
pub fn instantiate(
pub fn generate(
&self,
machine_registry_inner: &mut MachineRegistryInner,
) -> MachineRegistryResult<config::Machine> {
) -> MachineRegistryResult<MachineState> {
let mut inner = self.inner.lock();
if let Some(limit_machine_count) = inner.limit_machine_count {
if inner.machines.len() < limit_machine_count.try_into().unwrap_or(usize::MAX) {
return Err(MachineRegistryError::TemplateComplete);
}
}
// Pick or instantiate an available network
let location = match self.def().location.clone() {
let network_state = match self.def().location.clone() {
config::TemplateLocation::Network { network } => {
// Filter the weighted list of networks to those that are still active or not yet started and can allocate
let Some(active_networks) = network.try_filter(|n| {
machine_registry_inner
.get_network_state_by_name(&n)
.clone()
.map(|ns| ns.is_active())
.map(|ns| Self::is_network_available_inner(&*inner, ns))
.unwrap_or(Ok(true))
})?
else {
@ -121,24 +202,27 @@ impl TemplateState {
};
// Weighted choice of network now that we have a candidate list
let network_name = machine_registry_inner
let network = machine_registry_inner
.unlocked_inner
.srng
.weighted_choice(&active_networks);
config::MachineLocation::Network {
network: network_name.clone(),
address4: None,
address6: None,
}
// Instantiate the network if it doesn't yet exist
let network_state = machine_registry_inner.get_or_create_network_state(network.clone())?;
// Return network state to use
network_state
}
config::TemplateLocation::Blueprint { blueprint } => {
// Filter the weighted list of blueprints to those that are still active or not yet started and can allocate
let Some(active_blueprints) = blueprint.try_filter(|b| {
let Some(active_blueprints) = blueprint.try_filter_map(|b| {
machine_registry_inner
.get_blueprint_state(&b)
.clone()
.map(|bs| bs.is_active(machine_registry_inner))
.unwrap_or(Ok(true))
.map(|bs| {
Self::is_blueprint_available_inner(inner, machine_registry_inner, bs)
})
.unwrap_or(Ok(Some()))
})?
else {
return Err(MachineRegistryError::BlueprintComplete);
@ -155,9 +239,17 @@ impl TemplateState {
}
}
};
xxx
// Add to machines for this template
{
let template_state = self.get_template_state(&name).expect("must exist");
template_state.machines.insert(machine_id);
}
Ok(config::Machine {
location,
// Return the unique id
Ok(machine_id)
Ok(MachineParameters::Direct {
disable_capabilities: self.def().disable_capabilities.clone(),
bootstrap: false,
})