mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-01-13 00:09:47 -05:00
[skip ci] refactor, extract stateallocator
This commit is contained in:
parent
65629f03e9
commit
68fc6f97eb
@ -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;
|
||||
|
@ -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)
|
||||
// }
|
||||
}
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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,
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user