[skip ci]

checkpoint
This commit is contained in:
Christien Rioux 2024-12-08 19:37:35 -05:00
parent 1c327fca00
commit 29f1e2da11
8 changed files with 730 additions and 337 deletions

View File

@ -103,6 +103,33 @@ impl<T: fmt::Debug + Clone> WeightedList<T> {
}
}
}
pub fn try_filter<F, E>(&self, mut filter: F) -> Result<Option<WeightedList<T>>, E>
where
F: FnMut(&T) -> Result<bool, E>,
{
match self {
WeightedList::Single(v) => {
if filter(v)? {
return Ok(Some(self.clone()));
}
return Ok(None);
}
WeightedList::List(vec) => {
let mut out = Vec::<Weighted<T>>::with_capacity(vec.len());
for v in vec {
if filter(v.item())? {
out.push(v.clone());
}
}
if out.is_empty() {
Ok(None)
} else {
Ok(Some(WeightedList::List(out)))
}
}
}
}
}
pub type Probability = f32;
@ -174,7 +201,7 @@ pub enum Instance {
)]
pub struct Machine {
#[serde(flatten)]
#[validate(custom(function = "validate_machine_location_exists", use_context))]
#[validate(custom(function = "validate_machine_location", use_context))]
pub location: MachineLocation,
#[serde(default)]
pub disable_capabilities: Vec<String>,
@ -183,10 +210,6 @@ pub struct Machine {
}
fn validate_machine(machine: &Machine, _context: &ValidateContext) -> Result<(), ValidationError> {
if machine.address4.is_none() && machine.address6.is_none() {
return Err(ValidationError::new("badaddr")
.with_message("machine must have at least one address".into()));
}
if machine.disable_capabilities.contains(&("".to_string())) {
return Err(ValidationError::new("badcap")
.with_message("machine has empty disabled capability".into()));
@ -201,11 +224,11 @@ fn validate_machine(machine: &Machine, _context: &ValidateContext) -> Result<(),
)]
pub struct Template {
#[serde(flatten)]
#[validate(custom(function = "validate_template_location_exists", use_context))]
#[validate(custom(function = "validate_template_location", use_context))]
pub location: TemplateLocation,
#[serde(flatten)]
#[validate(nested)]
pub limits: Limits,
pub limits: TemplateLimits,
#[serde(default)]
pub disable_capabilities: Vec<String>,
}
@ -222,17 +245,17 @@ fn validate_template(
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
#[validate(schema(function = "validate_limits"))]
pub struct Limits {
#[validate(schema(function = "validate_template_limits"))]
pub struct TemplateLimits {
#[validate(nested)]
pub machine_count: WeightedList<u32>,
}
fn validate_limits(limits: &Limits) -> Result<(), ValidationError> {
fn validate_template_limits(limits: &TemplateLimits) -> Result<(), ValidationError> {
limits.machine_count.try_for_each(|x| {
if *x == 0 {
return Err(ValidationError::new("badcount")
.with_message("limits has zero machine count".into()));
.with_message("template limits has zero machine count".into()));
}
Ok(())
})?;
@ -250,12 +273,27 @@ pub enum MachineLocation {
#[serde(default)]
address6: Option<Ipv6Addr>,
},
Network {
network: WeightedList<String>,
},
Blueprint {
blueprint: WeightedList<String>,
},
}
fn validate_machine_location(
value: &MachineLocation,
context: &ValidateContext,
) -> Result<(), ValidationError> {
match value {
MachineLocation::Specific {
network,
address4,
address6,
} => {
if address4.is_none() && address6.is_none() {
return Err(ValidationError::new("badaddr")
.with_message("machine must have at least one address".into()));
}
validate_network_exists(network, context)?;
}
}
Ok(())
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -265,6 +303,22 @@ pub enum TemplateLocation {
Blueprint { blueprint: WeightedList<String> },
}
fn validate_template_location(
value: &TemplateLocation,
context: &ValidateContext,
) -> Result<(), ValidationError> {
match value {
TemplateLocation::Network { network } => {
network.try_for_each(|m| validate_network_exists(m, context))?;
}
TemplateLocation::Blueprint { blueprint } => {
blueprint.try_for_each(|t| validate_blueprint_exists(t, context))?;
}
}
Ok(())
}
////////////////////////////////////////////////////////////////
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
@ -355,6 +409,8 @@ pub struct Blueprint {
#[serde(default)]
#[validate(custom(function = "validate_models_exist", use_context))]
pub model: Option<WeightedList<String>>,
#[validate(nested)]
pub limits: BlueprintLimits,
#[serde(default)]
#[validate(custom(function = "validate_blueprint_ipv4", use_context))]
pub ipv4: Option<BlueprintIpv4>,
@ -374,6 +430,25 @@ fn validate_blueprint(
Ok(())
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
#[validate(schema(function = "validate_blueprint_limits"))]
pub struct BlueprintLimits {
#[validate(nested)]
pub network_count: WeightedList<u32>,
}
fn validate_blueprint_limits(limits: &BlueprintLimits) -> Result<(), ValidationError> {
limits.network_count.try_for_each(|x| {
if *x == 0 {
return Err(ValidationError::new("badcount")
.with_message("blueprint limits has zero machine count".into()));
}
Ok(())
})?;
Ok(())
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlueprintIpv4 {
#[serde(default)]
@ -635,49 +710,6 @@ fn validate_instances_exist(
Ok(())
}
fn validate_machine_location_exists(
value: &MachineLocation,
context: &ValidateContext,
) -> Result<(), ValidationError> {
match value {
MachineLocation::Specific {
network,
address4,
address6,
} => {
if address4.is_none() && address6.is_none() {
return Err(ValidationError::new("badaddr")
.with_message("machine must have at least one address".into()));
}
validate_network_exists(network, context)?;
}
MachineLocation::Network { network } => {
network.try_for_each(|n| validate_network_exists(n, context))?;
}
MachineLocation::Blueprint { blueprint } => {
blueprint.try_for_each(|b| validate_blueprint_exists(b, context))?;
}
}
Ok(())
}
fn validate_template_location_exists(
value: &TemplateLocation,
context: &ValidateContext,
) -> Result<(), ValidationError> {
match value {
TemplateLocation::Network { network } => {
network.try_for_each(|m| validate_network_exists(m, context))?;
}
TemplateLocation::Blueprint { blueprint } => {
blueprint.try_for_each(|t| validate_blueprint_exists(t, context))?;
}
}
Ok(())
}
fn validate_network_exists(value: &str, context: &ValidateContext) -> Result<(), ValidationError> {
if !context.config.networks.contains_key(value) {
return Err(ValidationError::new("noexist").with_message("network does not exist".into()));

View File

@ -54,24 +54,23 @@ impl MachineRegistryInner {
.or_insert_with(|| ProfileState::default());
// Get the next instance from the definition
let Some(instance_def) = profile_def
.instances
.get(profile_state.next_instance_index)
let Some(instance_def) = profile_def.instances.get(profile_state.next_instance_index)
else {
//
return Err(MachineRegistryError::ProfileComplete);
};
match instance_def {
let machine_state = match instance_def {
config::Instance::Machine { machine } => {
let machine = self.unlocked_inner.srng.weighted_choice(machine);
let unlocked_inner = self.unlocked_inner.clone();
let machine_def = unlocked_inner
.config
.machines
.get(machine).cloned()
.get(machine)
.cloned()
.expect("config validation is broken");
self.get_or_create_machine_state_id(machine.clone(), machine_def)
self.get_or_create_machine_state(machine.clone(), machine_def)?
}
config::Instance::Template { template } => {
let template = self.unlocked_inner.srng.weighted_choice(template);
@ -79,31 +78,36 @@ impl MachineRegistryInner {
let template_def = unlocked_inner
.config
.templates
.get(template).cloned()
.get(template)
.cloned()
.expect("config validation is broken");
self.create_machine_state_from_template(template.clone(), template_def)
self.get_or_create_machine_state_from_template(template.clone(), template_def)?
}
}
};
Ok(machine_state.id())
}
pub fn release(&self, machine_id: MachineId) -> MachineRegistryResult<()> {
// xxx
// xxx remember machines and networks may not be 'named' if they are generated by templates and blueprints
Ok(())
}
///////////////////////////////////////////////////////////
/// Private Implementation
pub(super) fn get_or_create_machine_state_id(
pub(super) fn get_or_create_machine_state(
&mut self,
name: String,
machine_def: config::Machine,
) -> MachineRegistryResult<MachineId> {
) -> 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(machine_id);
return Ok(self
.machine_state_by_id
.get(&machine_id)
.cloned()
.expect("must exist"));
}
// Allocate a machine id
@ -114,9 +118,211 @@ impl MachineRegistryInner {
});
// Create a new machine state
let machine_state = match MachineState::try_new(self,
let machine_state = match MachineState::try_new(
self,
machine_id,
MachineStateName::Machine(name.clone()),
machine_def.clone(),
) {
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);
// Bind the name to the id
self.resolve_to_manager_machine
.resolve(&name, machine_id)
.expect("must resolve");
// Return the state
Ok(self
.machine_state_by_id
.get(&machine_id)
.cloned()
.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_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"));
}
// 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
});
// 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);
}
};
// Store the network state with its unique id
self.network_state_by_id.insert(network_id, network_state);
// Bind the name to the id
self.resolve_to_manager_network
.resolve(&name, network_id)
.expect("must resolve");
// Return the unique id
Ok(self
.network_state_by_id
.get(&network_id)
.cloned()
.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()
}
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)
}
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"));
}
// 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);
}
};
// 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_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()? {
return Err(MachineRegistryError::TemplateComplete);
}
// Pick or instantiate an available network
xxx add 'def()' selector to all types
let active_networks = match template_state.template_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| {
self.get_network_state_by_name(&n)
.map(|ns| ns.is_active())
.unwrap_or(Ok(true))
})?
else {
return Err(MachineRegistryError::NetworkComplete);
};
}
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| {
self.get_blueprint_state(&b)
.map(|bs| bs.is_active(self))
.unwrap_or(Ok(true))
})?
else {
return Err(MachineRegistryError::BlueprintComplete);
};
// Activate some blueprint and pick a network
}
};
// Weighted choice of network now that we have a candidate list
//let network =
config::Machine {
location: config::MachineLocation::Specific {
network: todo!(),
address4: None,
address6: None,
},
disable_capabilities: template_state.template_def.disable_capabilities.clone(),
bootstrap: false,
}
};
// 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
});
// 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,
@ -130,172 +336,82 @@ impl MachineRegistryInner {
// Store the machine state with its unique id
self.machine_state_by_id.insert(machine_id, machine_state);
// Bind the name to the id
self.resolve_to_manager_machine.resolve(&name, machine_id).expect("must resolve");
// Add to machines for this template
{
let template_state = self.get_template_state(&name).expect("must exist");
template_state.machines.insert(machine_id);
}
// Return the unique id
Ok(machine_id)
}
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_id(
&mut self,
name: String,
network_def: config::Network,
) -> MachineRegistryResult<NetworkId> {
// 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(network_id);
}
// 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
});
// Create a new network state
let network_state = match NetworkState::try_new(self,
NetworkStateName::Network(name.clone()),
network_def.clone(),
network_id,
){
Ok(v) => v,
Err(e) => {
// Release the network id
self.free_network_ids.push(network_id);
return Err(e);
}
};
// Store the network state with its unique id
self.network_state_by_id.insert(network_id, network_state);
// Bind the name to the id
self.resolve_to_manager_network.resolve(&name, network_id).expect("must resolve");
// Return the unique id
Ok(network_id)
}
pub(super) fn get_network_state_by_id (
&mut self,
network_id: NetworkId,
) -> MachineRegistryResult<&mut NetworkState> {
self
.network_state_by_id
.get_mut(&network_id).ok_or_else(|| MachineRegistryError::NetworkNotFound)
}
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"));
}
// 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);
}
};
// 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 create_machine_state_from_template(
&mut self,
name: String,
template_def: config::Template,
) -> MachineRegistryResult<MachineId> {
// Get the active template state
let template_state = self.get_or_create_template_state(&name, template_def)?;
if !template_state.is_active() {
return Err(MachineRegistryError::TemplateComplete);
}
// Make machine def from current template state
let machine_def = config::Machine {
location: template_state.template_def.location.clone(),
address4: template_state.template_def.
address6: todo!(),
disable_capabilities: todo!(),
bootstrap: todo!(),
};
}
pub(super) fn get_template_state(
&mut self,
name: &String,
) -> MachineRegistryResult<&mut TemplateState> {
self
.template_state_by_name
.get_mut(name).ok_or_else(|| MachineRegistryError::TemplateNotFound)
self.template_state_by_name
.get_mut(name)
.ok_or_else(|| MachineRegistryError::TemplateNotFound)
}
pub(super) fn get_or_create_network_state_from_location(
pub(super) fn get_or_create_network_state_from_machine_location(
&mut self,
location_def: &config::Location,
machine_location: &config::MachineLocation,
) -> MachineRegistryResult<NetworkState> {
match machine_location {
config::MachineLocation::Specific {
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 get_or_create_network_state_from_template_location(
&mut self,
template_location: &config::TemplateLocation,
) -> MachineRegistryResult<NetworkId> {
match location_def {
config::Location::Network { network } => {
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()
.get(name)
.cloned()
.expect("config validation is broken");
self.get_or_create_network_state(
name.clone(),
network_def,
)
self.get_or_create_network_state(name.clone(), network_def)
}
config::Location::Blueprint { blueprint } => {
config::TemplateLocation::Blueprint { blueprint } => {
let name = self.unlocked_inner.srng.weighted_choice(blueprint);
let blueprint_def = self
.unlocked_inner
.config
.blueprints
.get(name).cloned()
.get(name)
.cloned()
.expect("config validation is broken");
self.get_or_create_network_state_from_blueprint(
name.clone(),
blueprint_def)
self.get_or_create_network_state_from_blueprint(name.clone(), blueprint_def)
}
}
}
pub(super) fn get_blueprint_state(
&mut self,
name: &String,
) -> MachineRegistryResult<&mut BlueprintState> {
self
.blueprint_state_by_name
.get_mut(name).ok_or_else(|| MachineRegistryError::BlueprintNotFound)
self.blueprint_state_by_name
.get_mut(name)
.ok_or_else(|| MachineRegistryError::BlueprintNotFound)
}
pub(super) fn choose_allocation_v4(
@ -379,39 +495,92 @@ impl MachineRegistryInner {
// No available allocations left
Err(MachineRegistryError::NoAllocation)
}
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"));
}
// 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);
}
};
// 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_network_state_from_blueprint(
&mut self,
name: String,
blueprint_def: config::Blueprint,
) -> MachineRegistryResult<NetworkId> {
// 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);
}
xxx
self.with_blueprint_state(
name,
blueprint_def,
|blueprint_state| {
// Make network def from current blueprint state
let network_def = config::Network {
model: blueprint_state
.blueprint_def
.model
.as_ref()
.map(|model| self.unlocked_inner.srng.weighted_choice(model).clone()),
ipv4: blueprint_state
.blueprint_def
.ipv4
.as_ref()
.map(|bpv4| self.unlocked_inner.srng.weighted_choice(bpv4).clone()),
ipv6: blueprint_def
.ipv6
.as_ref()
.map(|bpv6| self.unlocked_inner.srng.weighted_choice(bpv6).clone()),
};
//xxx self.
//xxx
// Make machine def from current template state
let machine_def = config::Machine {
location: match template_state.template_def.location.clone() {
config::TemplateLocation::Network { network } => {
config::MachineLocation::Network { network }
}
config::TemplateLocation::Blueprint { blueprint } => {
config::MachineLocation::Blueprint { blueprint }
}
},
)
}
disable_capabilities: template_state.template_def.disable_capabilities.clone(),
bootstrap: false,
};
// 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
});
// 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

@ -25,6 +25,7 @@ pub enum MachineRegistryError {
ProfileNotFound,
ProfileComplete,
TemplateComplete,
NetworkComplete,
BlueprintComplete,
MachineNotFound,
NetworkNotFound,

View File

@ -1,7 +1,63 @@
use super::*;
#[derive(Debug)]
pub struct BlueprintState {
pub name: String,
pub blueprint_def: config::Blueprint,
struct BlueprintStateUnlockedInner {
name: String,
blueprint_def: config::Blueprint,
}
#[derive(Debug)]
struct BlueprintStateInner {
limit_network_count: u32,
networks: HashSet<NetworkId>,
}
#[derive(Debug, Clone)]
pub struct BlueprintState {
unlocked_inner: Arc<BlueprintStateUnlockedInner>,
inner: Arc<Mutex<BlueprintStateInner>>,
}
impl BlueprintState {
pub fn try_new(
machine_registry_inner: &mut MachineRegistryInner,
name: String,
blueprint_def: config::Blueprint,
) -> MachineRegistryResult<BlueprintState> {
let limit_network_count = *machine_registry_inner
.unlocked_inner
.srng
.weighted_choice(&blueprint_def.limits.network_count);
Ok(Self {
unlocked_inner: Arc::new(BlueprintStateUnlockedInner {
name,
blueprint_def,
}),
inner: Arc::new(Mutex::new(BlueprintStateInner {
limit_network_count,
networks: HashSet::new(),
})),
})
}
pub fn is_active(
&self,
machine_registry_inner: &MachineRegistryInner,
) -> 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);
}
}
// 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))
}
}

View File

@ -1,36 +1,48 @@
use super::*;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum MachineStateName {
Machine(String),
Template(String),
}
#[derive(Debug)]
pub struct MachineState {
/// The name of this machine state if it was made directly
/// or the name of the template used to create it
pub name: MachineStateName,
/// The definition this machine was created with
pub machine_def: config::Machine,
struct MachineStateInner {
/// The current network interfaces definition
pub interfaces: Vec<MachineStateInterface>,
interfaces: Vec<MachineStateInterface>,
}
#[derive(Debug)]
pub struct MachineStateInterface {
/// The network id
/// The network this interface belongs to
pub network_id: NetworkId,
/// The veilid NetworkInterface state
pub network_interface: NetworkInterface,
}
#[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,
}
#[derive(Debug, Clone)]
pub struct MachineState {
unlocked_inner: Arc<MachineStateUnlockedInner>,
inner: Arc<Mutex<MachineStateInner>>,
}
impl MachineState {
pub fn try_new(
machine_registry_inner: &mut MachineRegistryInner,
id: MachineId,
name: MachineStateName,
machine_def: config::Machine,
machine_id: MachineId,
) -> MachineRegistryResult<Self> {
// Build list of machinestate interfaces
let mut interfaces = Vec::<MachineStateInterface>::new();
@ -38,42 +50,31 @@ impl MachineState {
// Make default route interface
{
// Find existing network or create a new one from network or blueprint definition
let network_id = machine_registry_inner
.get_or_create_network_state_from_location(&machine_def.location)?;
let srng = machine_registry_inner.unlocked_inner.srng.clone();
let network_state = machine_registry_inner
.get_network_state_by_id(network_id)
.expect("must exist");
.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;
let machine_location = machine_def.location.clone();
let (allocate_v4, opt_address4, allocate_v6, opt_address6) = match machine_location {
config::MachineLocation::Specific {
network: _,
address4,
address6,
} => (
network_state.ipv4.is_some() && address4.is_some(),
network_state.is_ipv4() && address4.is_some(),
address4,
network_state.ipv6.is_some() && address6.is_some(),
network_state.is_ipv6() && address6.is_some(),
address6,
),
config::MachineLocation::Network { network: _ }
| config::MachineLocation::Blueprint { blueprint: _ } => (
network_state.ipv4.is_some(),
None,
network_state.ipv6.is_some(),
None,
),
};
if allocate_v4 {
let if_addr4 =
match network_state.allocate_address_v4(srng.clone(), machine_id, opt_address4)
{
match network_state.allocate_address_v4(srng.clone(), id, opt_address4) {
Ok(v) => v,
Err(e) => {
network_state
@ -93,8 +94,7 @@ impl MachineState {
}
if allocate_v6 {
let if_addr6 =
match network_state.allocate_address_v6(srng.clone(), machine_id, opt_address6)
{
match network_state.allocate_address_v6(srng.clone(), id, opt_address6) {
Ok(v) => v,
Err(e) => {
network_state
@ -126,21 +126,28 @@ impl MachineState {
};
interfaces.push(MachineStateInterface {
network_id,
network_id: network_state.id(),
network_interface,
});
}
// Create a localhost interface for this machine
Ok(Self {
name,
machine_def,
interfaces,
unlocked_inner: Arc::new(MachineStateUnlockedInner {
id,
name,
machine_def,
}),
inner: Arc::new(Mutex::new(MachineStateInner { interfaces })),
})
}
pub fn release(self, machine_registry_inner: &mut MachineRegistryInner) {
for intf in self.interfaces {
pub fn release(&self, machine_registry_inner: &mut MachineRegistryInner) {
let network_states = {
let mut inner = self.inner.lock();
core::mem::take(&mut inner.interfaces)
};
for intf in network_states {
let network_state = machine_registry_inner
.get_network_state_by_id(intf.network_id)
.expect("must exist");
@ -151,4 +158,12 @@ impl MachineState {
.expect("must succeed");
}
}
pub fn name(&self) -> MachineStateName {
self.unlocked_inner.name.clone()
}
pub fn id(&self) -> MachineId {
self.unlocked_inner.id
}
}

View File

@ -1,21 +1,37 @@
use super::*;
use ipnet::*;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum NetworkStateName {
Network(String),
Blueprint(String),
}
#[derive(Debug)]
pub struct NetworkState {
pub struct NetworkStateUnlockedInner {
/// The unique id of this network
id: NetworkId,
/// The name of this network state if it was made directly
/// or the name of the blueprint used to create it
pub name: NetworkStateName,
pub network_def: config::Network,
pub model: String,
pub ipv4: Option<NetworkStateIpv4>,
pub ipv6: Option<NetworkStateIpv6>,
name: NetworkStateName,
/// The network definition used to create this network
network_def: config::Network,
}
#[derive(Debug)]
pub struct NetworkStateInner {
/// The model chosen for this network
model: String,
/// IPv4 state if it is enabled
ipv4: Option<NetworkStateIpv4>,
/// IPv6 state if it is enabled
ipv6: Option<NetworkStateIpv6>,
}
#[derive(Debug, Clone)]
pub struct NetworkState {
unlocked_inner: Arc<NetworkStateUnlockedInner>,
inner: Arc<Mutex<NetworkStateInner>>,
}
#[derive(Debug)]
@ -44,9 +60,9 @@ pub type NetworkId = u64;
impl NetworkState {
pub fn try_new(
machine_registry_inner: &mut MachineRegistryInner,
id: NetworkId,
name: NetworkStateName,
network_def: config::Network,
network_id: NetworkId,
) -> MachineRegistryResult<Self> {
let model = network_def.model.clone().unwrap_or_else(|| {
machine_registry_inner
@ -114,21 +130,80 @@ impl NetworkState {
};
Ok(Self {
name,
network_def,
model,
ipv4,
ipv6,
unlocked_inner: Arc::new(NetworkStateUnlockedInner {
id,
name,
network_def,
}),
inner: Arc::new(Mutex::new(NetworkStateInner { model, ipv4, ipv6 })),
})
}
pub fn name(&self) -> NetworkStateName {
self.unlocked_inner.name.clone()
}
pub fn id(&self) -> NetworkId {
self.unlocked_inner.id
}
pub fn is_ipv4(&self) -> bool {
self.inner.lock().ipv4.is_some()
}
pub fn is_ipv6(&self) -> bool {
self.inner.lock().ipv6.is_some()
}
pub fn is_active(&self) -> MachineRegistryResult<bool> {
let inner = self.inner.lock();
let mut can_allocate = false;
if let Some(network_state_ipv4) = &inner.ipv4 {
let hosts_range = network_state_ipv4.allocation.hosts();
let Some(first_host) = std::iter::Iterator::min(hosts_range) else {
return Err(MachineRegistryError::NoAllocation);
};
let Some(last_host) = std::iter::Iterator::max(hosts_range) else {
return Err(MachineRegistryError::NoAllocation);
};
let first_host_bits = first_host.to_bits();
let last_host_bits = last_host.to_bits();
let count = last_host_bits - first_host_bits + 1;
if network_state_ipv4.machine_addresses.len() >= count.try_into().unwrap_or(usize::MAX)
{
can_allocate = false;
}
};
if let Some(network_state_ipv6) = &inner.ipv6 {
let hosts_range = network_state_ipv6.allocation.hosts();
let Some(first_host) = std::iter::Iterator::min(hosts_range) else {
return Err(MachineRegistryError::NoAllocation);
};
let Some(last_host) = std::iter::Iterator::max(hosts_range) else {
return Err(MachineRegistryError::NoAllocation);
};
let first_host_bits = first_host.to_bits();
let last_host_bits = last_host.to_bits();
let count = last_host_bits - first_host_bits + 1;
if network_state_ipv6.machine_addresses.len() >= count.try_into().unwrap_or(usize::MAX)
{
can_allocate = false;
}
};
Ok(can_allocate)
}
pub(super) fn allocate_address_v4(
&mut self,
&self,
srng: StableRng,
machine_id: MachineId,
opt_address: Option<Ipv4Addr>,
) -> MachineRegistryResult<Ifv4Addr> {
let Some(network_state_ipv4) = &mut self.ipv4 else {
let mut inner = self.inner.lock();
let Some(network_state_ipv4) = &mut inner.ipv4 else {
return Err(MachineRegistryError::NoAllocation);
};
@ -191,8 +266,16 @@ impl NetworkState {
Ok(ifaddr)
}
pub(super) fn release_address_v4(&mut self, addr: Ipv4Addr) -> MachineRegistryResult<()> {
if let Some(ipv4) = self.ipv4.as_mut() {
pub(super) fn release_address_v4(&self, addr: Ipv4Addr) -> MachineRegistryResult<()> {
let mut inner = self.inner.lock();
Self::release_address_v4_inner(&mut *inner, addr)
}
fn release_address_v4_inner(
inner: &mut NetworkStateInner,
addr: Ipv4Addr,
) -> MachineRegistryResult<()> {
if let Some(ipv4) = inner.ipv4.as_mut() {
if ipv4.machine_addresses.remove(&addr).is_some() {
return Ok(());
}
@ -200,48 +283,15 @@ impl NetworkState {
Err(MachineRegistryError::NoAllocation)
}
pub(super) fn release_address_v6(&mut self, addr: Ipv6Addr) -> MachineRegistryResult<()> {
if let Some(ipv6) = self.ipv6.as_mut() {
if ipv6.machine_addresses.remove(&addr).is_some() {
return Ok(());
}
}
Err(MachineRegistryError::NoAllocation)
}
pub(super) fn release_all_addresses<I: Iterator<Item = IpAddr>>(
&mut self,
addrs: I,
) -> MachineRegistryResult<()> {
let mut ok = true;
for addr in addrs {
match addr {
IpAddr::V4(ipv4_addr) => {
if self.release_address_v4(ipv4_addr).is_err() {
ok = false;
}
}
IpAddr::V6(ipv6_addr) => {
if self.release_address_v6(ipv6_addr).is_err() {
ok = false;
}
}
}
}
if ok {
Ok(())
} else {
Err(MachineRegistryError::NoAllocation)
}
}
pub(super) fn allocate_address_v6(
&mut self,
&self,
srng: StableRng,
machine_id: MachineId,
opt_address: Option<Ipv6Addr>,
) -> MachineRegistryResult<Ifv6Addr> {
let Some(network_state_ipv6) = &mut self.ipv6 else {
let mut inner = self.inner.lock();
let Some(network_state_ipv6) = &mut inner.ipv6 else {
return Err(MachineRegistryError::NoAllocation);
};
@ -303,4 +353,48 @@ impl NetworkState {
Ok(ifaddr)
}
pub(super) fn release_address_v6(&self, addr: Ipv6Addr) -> MachineRegistryResult<()> {
let mut inner = self.inner.lock();
Self::release_address_v6_inner(&mut *inner, addr)
}
fn release_address_v6_inner(
inner: &mut NetworkStateInner,
addr: Ipv6Addr,
) -> MachineRegistryResult<()> {
if let Some(ipv6) = inner.ipv6.as_mut() {
if ipv6.machine_addresses.remove(&addr).is_some() {
return Ok(());
}
}
Err(MachineRegistryError::NoAllocation)
}
pub(super) fn release_all_addresses<I: Iterator<Item = IpAddr>>(
&self,
addrs: I,
) -> MachineRegistryResult<()> {
let mut inner = self.inner.lock();
let mut ok = true;
for addr in addrs {
match addr {
IpAddr::V4(ipv4_addr) => {
if Self::release_address_v4_inner(&mut *inner, ipv4_addr).is_err() {
ok = false;
}
}
IpAddr::V6(ipv6_addr) => {
if Self::release_address_v6_inner(&mut *inner, ipv6_addr).is_err() {
ok = false;
}
}
}
}
if ok {
Ok(())
} else {
Err(MachineRegistryError::NoAllocation)
}
}
}

View File

@ -69,4 +69,14 @@ where
None => Err(ResolveToError::MissingSymbol),
}
}
pub fn get(&self, symbol: &T) -> Option<I> {
self.symbols
.get(symbol)
.map(|s| {
let inner = s.lock();
inner.clone()
})
.flatten()
}
}

View File

@ -1,11 +1,21 @@
use super::*;
#[derive(Debug)]
struct TemplateStateUnlockedInner {
name: String,
template_def: config::Template,
}
#[derive(Debug)]
struct TemplateStateInner {
limit_machine_count: u32,
machines: HashSet<MachineId>,
}
#[derive(Debug, Clone)]
pub struct TemplateState {
pub name: String,
pub template_def: config::Template,
pub limit_machine_count: u32,
pub machines: HashSet<MachineId>,
unlocked_inner: Arc<TemplateStateUnlockedInner>,
inner: Arc<Mutex<TemplateStateInner>>,
}
impl TemplateState {
@ -20,14 +30,20 @@ impl TemplateState {
.weighted_choice(&template_def.limits.machine_count);
Ok(Self {
name,
template_def,
limit_machine_count,
machines: HashSet::new(),
unlocked_inner: Arc::new(TemplateStateUnlockedInner { name, template_def }),
inner: Arc::new(Mutex::new(TemplateStateInner {
limit_machine_count,
machines: HashSet::new(),
})),
})
}
pub fn is_active(&self) -> bool {
self.machines.len() < self.limit_machine_count as usize
pub fn name(&self) -> String {
self.unlocked_inner.name.clone()
}
pub fn is_active(&self) -> MachineRegistryResult<bool> {
let inner = self.inner.lock();
Ok(inner.machines.len() < inner.limit_machine_count.try_into().unwrap_or(usize::MAX))
}
}