mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-03-06 05:05:57 -05:00
validation and start of generation
This commit is contained in:
parent
981674b870
commit
2cab1d75c1
@ -52,6 +52,7 @@ pub mod socket_tools;
|
||||
pub mod spawn;
|
||||
pub mod split_url;
|
||||
pub mod startup_lock;
|
||||
pub mod static_string_table;
|
||||
pub mod tick_task;
|
||||
pub mod timeout;
|
||||
pub mod timeout_or;
|
||||
@ -241,6 +242,8 @@ pub use split_url::*;
|
||||
#[doc(inline)]
|
||||
pub use startup_lock::*;
|
||||
#[doc(inline)]
|
||||
pub use static_string_table::*;
|
||||
#[doc(inline)]
|
||||
pub use tick_task::*;
|
||||
#[doc(inline)]
|
||||
pub use timeout::*;
|
||||
|
21
veilid-tools/src/static_string_table.rs
Normal file
21
veilid-tools/src/static_string_table.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use super::*;
|
||||
|
||||
static STRING_TABLE: std::sync::LazyLock<Mutex<BTreeSet<&'static str>>> =
|
||||
std::sync::LazyLock::new(|| Mutex::new(BTreeSet::new()));
|
||||
|
||||
pub trait ToStaticStr {
|
||||
fn to_static_str(&self) -> &'static str;
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ToStaticStr for T {
|
||||
fn to_static_str(&self) -> &'static str {
|
||||
let s = self.as_ref();
|
||||
let mut string_table = STRING_TABLE.lock();
|
||||
if let Some(v) = string_table.get(s) {
|
||||
return v;
|
||||
}
|
||||
let ss = Box::leak(s.to_owned().into_boxed_str());
|
||||
string_table.insert(ss);
|
||||
ss
|
||||
}
|
||||
}
|
@ -28,14 +28,18 @@ impl<T: fmt::Debug + Clone> Default for WeightedList<T> {
|
||||
impl<T: fmt::Debug + Clone> Validate for WeightedList<T> {
|
||||
fn validate(&self) -> Result<(), ValidationErrors> {
|
||||
let mut errors = ValidationErrors::new();
|
||||
if let Self::List(v) = self {
|
||||
if v.is_empty() {
|
||||
errors.add(
|
||||
"List",
|
||||
ValidationError::new("len")
|
||||
.with_message("weighted list must not be empty".into()),
|
||||
)
|
||||
match self {
|
||||
Self::List(v) => {
|
||||
if v.is_empty() {
|
||||
errors.add(
|
||||
"List",
|
||||
ValidationError::new("len")
|
||||
.with_message("weighted list must not be empty".into()),
|
||||
)
|
||||
}
|
||||
errors.merge_self("List", v.validate());
|
||||
}
|
||||
Self::Single(_addr) => {}
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
@ -46,6 +50,34 @@ impl<T: fmt::Debug + Clone> Validate for WeightedList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + Clone> WeightedList<T> {
|
||||
fn validate_once(&self) -> Result<(), ValidationError> {
|
||||
match self {
|
||||
Self::List(v) => {
|
||||
if v.is_empty() {
|
||||
return Err(ValidationError::new("len")
|
||||
.with_message("weighted list must not be empty".into()));
|
||||
}
|
||||
}
|
||||
Self::Single(_addr) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn try_for_each<E, F: FnMut(&T) -> Result<(), E>>(&self, mut f: F) -> Result<(), E> {
|
||||
match self {
|
||||
WeightedList::Single(v) => f(v),
|
||||
WeightedList::List(vec) => vec
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
Weighted::Weighted { item, weight: _ } => item,
|
||||
Weighted::Unweighted(item) => item,
|
||||
})
|
||||
.try_for_each(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Probability = f32;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -76,6 +108,21 @@ impl<T: fmt::Debug + Clone> Validate for Weighted<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + Clone> Weighted<T> {
|
||||
pub fn item(&self) -> &T {
|
||||
match self {
|
||||
Weighted::Weighted { item, weight: _ } => item,
|
||||
Weighted::Unweighted(item) => item,
|
||||
}
|
||||
}
|
||||
pub fn weight(&self) -> f32 {
|
||||
match self {
|
||||
Weighted::Weighted { item: _, weight } => *weight,
|
||||
Weighted::Unweighted(_) => 1.0f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(context = "ValidateContext<'v_a>")]
|
||||
pub struct Profile {
|
||||
@ -94,7 +141,10 @@ pub enum Instance {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(context = "ValidateContext<'v_a>")]
|
||||
#[validate(
|
||||
context = "ValidateContext<'v_a>",
|
||||
schema(function = "validate_machine", use_context)
|
||||
)]
|
||||
pub struct Machine {
|
||||
#[serde(flatten)]
|
||||
#[validate(custom(function = "validate_location_exists", use_context))]
|
||||
@ -109,23 +159,64 @@ pub struct Machine {
|
||||
pub bootstrap: bool,
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(context = "ValidateContext<'v_a>")]
|
||||
#[validate(
|
||||
context = "ValidateContext<'v_a>",
|
||||
schema(function = "validate_template", use_context)
|
||||
)]
|
||||
pub struct Template {
|
||||
#[serde(flatten)]
|
||||
#[validate(custom(function = "validate_location_exists", use_context))]
|
||||
pub location: Location,
|
||||
#[serde(flatten)]
|
||||
#[validate(nested)]
|
||||
pub limits: Limits,
|
||||
#[serde(default)]
|
||||
pub disable_capabilities: Vec<String>,
|
||||
}
|
||||
|
||||
fn validate_template(
|
||||
template: &Template,
|
||||
_context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
if template.disable_capabilities.contains(&("".to_string())) {
|
||||
return Err(ValidationError::new("badcap")
|
||||
.with_message("template has empty disabled capability".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(schema(function = "validate_limits"))]
|
||||
pub struct Limits {
|
||||
#[validate(nested)]
|
||||
pub machine_count: WeightedList<u32>,
|
||||
}
|
||||
|
||||
fn validate_limits(limits: &Limits) -> 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()));
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Location {
|
||||
@ -136,28 +227,66 @@ pub enum Location {
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(
|
||||
context = "ValidateContext<'v_a>",
|
||||
schema(function = "validate_network", use_context)
|
||||
)]
|
||||
pub struct Network {
|
||||
#[serde(default)]
|
||||
#[validate(custom(function = "validate_model_exists", use_context))]
|
||||
pub model: Option<String>,
|
||||
#[serde(default)]
|
||||
#[validate(custom(function = "validate_network_ipv4", use_context))]
|
||||
pub ipv4: Option<NetworkIpv4>,
|
||||
#[serde(default)]
|
||||
#[validate(custom(function = "validate_network_ipv6", use_context))]
|
||||
pub ipv6: Option<NetworkIpv6>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
fn validate_network(network: &Network, _context: &ValidateContext) -> Result<(), ValidationError> {
|
||||
if network.ipv4.is_none() && network.ipv6.is_none() {
|
||||
return Err(ValidationError::new("badaddr")
|
||||
.with_message("network must support at least one address type".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NetworkIpv4 {
|
||||
pub allocation: String,
|
||||
#[serde(default)]
|
||||
pub gateway: Option<NetworkGateway>,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
|
||||
fn validate_network_ipv4(
|
||||
network_ipv4: &NetworkIpv4,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
validate_allocation_exists(&network_ipv4.allocation, context)?;
|
||||
if let Some(gateway) = &network_ipv4.gateway {
|
||||
validate_network_gateway(gateway, context)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NetworkIpv6 {
|
||||
pub allocation: String,
|
||||
#[serde(default)]
|
||||
pub gateway: Option<NetworkGateway>,
|
||||
}
|
||||
|
||||
fn validate_network_ipv6(
|
||||
network_ipv6: &NetworkIpv6,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
validate_allocation_exists(&network_ipv6.allocation, context)?;
|
||||
if let Some(gateway) = &network_ipv6.gateway {
|
||||
validate_network_gateway(gateway, context)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
pub struct NetworkGateway {
|
||||
pub translation: Translation,
|
||||
@ -165,19 +294,46 @@ pub struct NetworkGateway {
|
||||
pub network: Option<String>,
|
||||
}
|
||||
|
||||
fn validate_network_gateway(
|
||||
gateway: &NetworkGateway,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
if let Some(network) = &gateway.network {
|
||||
validate_network_exists(network, context)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(
|
||||
context = "ValidateContext<'v_a>",
|
||||
schema(function = "validate_blueprint", use_context)
|
||||
)]
|
||||
pub struct Blueprint {
|
||||
#[serde(default)]
|
||||
#[validate(custom(function = "validate_models_exist", use_context))]
|
||||
pub model: WeightedList<String>,
|
||||
#[serde(default)]
|
||||
#[validate(custom(function = "validate_blueprint_ipv4", use_context))]
|
||||
pub ipv4: Option<BlueprintIpv4>,
|
||||
#[serde(default)]
|
||||
#[validate(custom(function = "validate_blueprint_ipv6", use_context))]
|
||||
pub ipv6: Option<BlueprintIpv6>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
fn validate_blueprint(
|
||||
blueprint: &Blueprint,
|
||||
_context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
if blueprint.ipv4.is_none() && blueprint.ipv6.is_none() {
|
||||
return Err(ValidationError::new("badaddr")
|
||||
.with_message("blueprint must support at least one address type".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BlueprintIpv4 {
|
||||
#[serde(default)]
|
||||
pub allocation: Option<String>,
|
||||
@ -185,7 +341,28 @@ pub struct BlueprintIpv4 {
|
||||
#[serde(default)]
|
||||
pub gateway: Option<BlueprintGateway>,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
|
||||
fn validate_blueprint_ipv4(
|
||||
blueprint_ipv4: &BlueprintIpv4,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
if let Some(allocation) = &blueprint_ipv4.allocation {
|
||||
validate_allocation_exists(allocation, context)?;
|
||||
}
|
||||
|
||||
if blueprint_ipv4.prefix > 32 {
|
||||
return Err(
|
||||
ValidationError::new("badprefix").with_message("ipv4 blueprint prefix too long".into())
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(gateway) = &blueprint_ipv4.gateway {
|
||||
validate_blueprint_gateway(gateway, context)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BlueprintIpv6 {
|
||||
#[serde(default)]
|
||||
pub allocation: Option<String>,
|
||||
@ -194,16 +371,47 @@ pub struct BlueprintIpv6 {
|
||||
pub gateway: Option<BlueprintGateway>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
fn validate_blueprint_ipv6(
|
||||
blueprint_ipv6: &BlueprintIpv6,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
if let Some(allocation) = &blueprint_ipv6.allocation {
|
||||
validate_allocation_exists(allocation, context)?;
|
||||
}
|
||||
|
||||
if blueprint_ipv6.prefix > 128 {
|
||||
return Err(
|
||||
ValidationError::new("badprefix").with_message("ipv6 blueprint prefix too long".into())
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(gateway) = &blueprint_ipv6.gateway {
|
||||
validate_blueprint_gateway(gateway, context)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BlueprintGateway {
|
||||
pub translation: WeightedList<Translation>,
|
||||
pub upnp: Probability,
|
||||
pub network: Option<String>,
|
||||
}
|
||||
|
||||
fn validate_blueprint_gateway(
|
||||
gateway: &BlueprintGateway,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
gateway.translation.validate_once()?;
|
||||
if let Some(network) = &gateway.network {
|
||||
validate_network_exists(network, context)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(schema(function = "validate_subnets"))]
|
||||
pub struct Subnets {
|
||||
#[serde(default)]
|
||||
pub subnet4: Vec<Ipv4Net>,
|
||||
@ -211,13 +419,35 @@ pub struct Subnets {
|
||||
pub subnet6: Vec<Ipv6Net>,
|
||||
}
|
||||
|
||||
fn validate_subnets(subnets: &Subnets) -> Result<(), ValidationError> {
|
||||
if subnets.subnet4.is_empty() && subnets.subnet6.is_empty() {
|
||||
return Err(ValidationError::new("badsub")
|
||||
.with_message("subnets must support at least one address type".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(schema(function = "validate_distance"))]
|
||||
pub struct Distance {
|
||||
pub min: f32,
|
||||
pub max: f32,
|
||||
}
|
||||
|
||||
fn validate_distance(distance: &Distance) -> Result<(), ValidationError> {
|
||||
if distance.min < 0.0 {
|
||||
return Err(ValidationError::new("baddist")
|
||||
.with_message("distance minimum must not be negative".into()));
|
||||
}
|
||||
if distance.max < distance.min {
|
||||
return Err(ValidationError::new("baddist")
|
||||
.with_message("distance maximum must not be less than the minimum".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(schema(function = "validate_distribution"))]
|
||||
pub struct Distribution {
|
||||
pub mean: f32,
|
||||
pub sigma: f32,
|
||||
@ -226,6 +456,22 @@ pub struct Distribution {
|
||||
pub max: f32,
|
||||
}
|
||||
|
||||
fn validate_distribution(distribution: &Distribution) -> Result<(), ValidationError> {
|
||||
if distribution.mean < 0.0 {
|
||||
return Err(ValidationError::new("baddistrib")
|
||||
.with_message("distribution mean must not be negative".into()));
|
||||
}
|
||||
if distribution.sigma < distribution.mean {
|
||||
return Err(ValidationError::new("baddistrib")
|
||||
.with_message("distribution sigma must not be less than the mean".into()));
|
||||
}
|
||||
if distribution.max < distribution.min {
|
||||
return Err(ValidationError::new("baddistrib")
|
||||
.with_message("distribution maximum must not be less than the minimum".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Translation {
|
||||
@ -242,17 +488,23 @@ impl Default for Translation {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(context = "ValidateContext<'v_a>")]
|
||||
pub struct Model {
|
||||
#[validate(nested)]
|
||||
pub latency: Distribution,
|
||||
#[serde(default)]
|
||||
#[validate(nested)]
|
||||
pub distance: Option<Distance>,
|
||||
#[serde(default)]
|
||||
#[validate(range(min = 0.0, max = 1.0))]
|
||||
pub loss: Probability,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(context = "ValidateContext<'v_a>")]
|
||||
pub struct Allocation {
|
||||
#[serde(flatten)]
|
||||
#[validate(nested)]
|
||||
pub subnets: Subnets,
|
||||
}
|
||||
|
||||
@ -306,22 +558,22 @@ impl Config {
|
||||
errors = e;
|
||||
}
|
||||
|
||||
errors.merge_self("profiles", validate_all_profiles(&out.profiles, &context));
|
||||
errors.merge_self("machines", validate_all_machines(&out.machines, &context));
|
||||
errors.merge_self("profiles", validate_all_with_args(&out.profiles, &context));
|
||||
errors.merge_self("machines", validate_all_with_args(&out.machines, &context));
|
||||
errors.merge_self(
|
||||
"templates",
|
||||
validate_all_templates(&out.templates, &context),
|
||||
validate_all_with_args(&out.templates, &context),
|
||||
);
|
||||
errors.merge_self("networks", validate_all_networks(&out.networks, &context));
|
||||
errors.merge_self("networks", validate_all_with_args(&out.networks, &context));
|
||||
errors.merge_self(
|
||||
"blueprints",
|
||||
validate_all_blueprints(&out.blueprints, &context),
|
||||
validate_all_with_args(&out.blueprints, &context),
|
||||
);
|
||||
errors.merge_self(
|
||||
"allocation",
|
||||
validate_all_allocations(&out.allocations, &context),
|
||||
validate_all_with_args(&out.allocations, &context),
|
||||
);
|
||||
errors.merge_self("models", validate_all_models(&out.models, &context));
|
||||
errors.merge_self("models", validate_all_with_args(&out.models, &context));
|
||||
|
||||
if !errors.is_empty() {
|
||||
return Err(ConfigError::ValidateError(errors));
|
||||
@ -335,6 +587,12 @@ fn validate_instances_exist(
|
||||
value: &Vec<Instance>,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
for v in value {
|
||||
match v {
|
||||
Instance::Machine { machine } => validate_machines_exist(machine, context)?,
|
||||
Instance::Template { template } => validate_templates_exist(template, context)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -342,61 +600,103 @@ fn validate_location_exists(
|
||||
value: &Location,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
match value {
|
||||
Location::Network { network } => {
|
||||
network.try_for_each(|m| validate_network_exists(m, context))?;
|
||||
}
|
||||
Location::Blueprint { blueprint } => {
|
||||
blueprint.try_for_each(|t| validate_blueprint_exists(t, context))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_network_exists(value: &str, context: &ValidateContext) -> Result<(), ValidationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_model_exists(value: &str, context: &ValidateContext) -> Result<(), ValidationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_all_profiles(
|
||||
value: &HashMap<String, Profile>,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationErrors> {
|
||||
for x in value.values() {
|
||||
x.validate_with_args(context)?
|
||||
if !context.config.networks.contains_key(value) {
|
||||
return Err(ValidationError::new("noexist").with_message("network does not exist".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_all_machines(
|
||||
value: &HashMap<String, Machine>,
|
||||
fn validate_blueprint_exists(
|
||||
value: &str,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationErrors> {
|
||||
) -> Result<(), ValidationError> {
|
||||
if !context.config.blueprints.contains_key(value) {
|
||||
return Err(ValidationError::new("noexist").with_message("blueprint does not exist".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn validate_all_templates(
|
||||
value: &HashMap<String, Template>,
|
||||
|
||||
fn validate_allocation_exists(
|
||||
value: &str,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationErrors> {
|
||||
) -> Result<(), ValidationError> {
|
||||
if !context.config.allocations.contains_key(value) {
|
||||
return Err(
|
||||
ValidationError::new("noexist").with_message("allocation does not exist".into())
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn validate_all_networks(
|
||||
value: &HashMap<String, Network>,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationErrors> {
|
||||
|
||||
fn validate_model_exists(value: &str, context: &ValidateContext) -> Result<(), ValidationError> {
|
||||
if !context.config.networks.contains_key(value) {
|
||||
return Err(ValidationError::new("noexist").with_message("model does not exist".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn validate_all_blueprints(
|
||||
value: &HashMap<String, Blueprint>,
|
||||
|
||||
fn validate_models_exist(
|
||||
value: &WeightedList<String>,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationErrors> {
|
||||
) -> Result<(), ValidationError> {
|
||||
value.try_for_each(|x| validate_model_exists(x, context))
|
||||
}
|
||||
|
||||
fn validate_machine_exists(value: &str, context: &ValidateContext) -> Result<(), ValidationError> {
|
||||
if !context.config.machines.contains_key(value) {
|
||||
return Err(ValidationError::new("noexist").with_message("machine does not exist".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn validate_all_allocations(
|
||||
value: &HashMap<String, Allocation>,
|
||||
|
||||
fn validate_machines_exist(
|
||||
value: &WeightedList<String>,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationErrors> {
|
||||
) -> Result<(), ValidationError> {
|
||||
value.try_for_each(|x| validate_machine_exists(x, context))
|
||||
}
|
||||
|
||||
fn validate_template_exists(value: &str, context: &ValidateContext) -> Result<(), ValidationError> {
|
||||
if !context.config.templates.contains_key(value) {
|
||||
return Err(ValidationError::new("noexist").with_message("template does not exist".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn validate_all_models(
|
||||
value: &HashMap<String, Model>,
|
||||
|
||||
fn validate_templates_exist(
|
||||
value: &WeightedList<String>,
|
||||
context: &ValidateContext,
|
||||
) -> Result<(), ValidationError> {
|
||||
value.try_for_each(|x| validate_template_exists(x, context))
|
||||
}
|
||||
|
||||
fn validate_all_with_args<'v_a, T: ValidateArgs<'v_a, Args = &'v_a ValidateContext<'v_a>>>(
|
||||
value: &HashMap<String, T>,
|
||||
context: &'v_a ValidateContext,
|
||||
) -> Result<(), ValidationErrors> {
|
||||
let mut errors = ValidationErrors::new();
|
||||
for (n, x) in value.values().enumerate() {
|
||||
errors.merge_self(
|
||||
format!("[{n}]").to_static_str(),
|
||||
x.validate_with_args(context),
|
||||
);
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
return Err(errors);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use rand::Rng;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Machine {}
|
||||
@ -71,12 +72,26 @@ impl MachineRegistry {
|
||||
|
||||
match instance_def {
|
||||
config::Instance::Machine { machine } => {
|
||||
self.create_machine(machine);
|
||||
let machine = self.weighted_choice(machine);
|
||||
let machine_def = self
|
||||
.unlocked_inner
|
||||
.config
|
||||
.machines
|
||||
.get(machine)
|
||||
.expect("config validation is broken");
|
||||
self.create_machine(machine_def).await
|
||||
}
|
||||
config::Instance::Template { template } => {
|
||||
let template = self.weighted_choice(template);
|
||||
let template_def = self
|
||||
.unlocked_inner
|
||||
.config
|
||||
.templates
|
||||
.get(template)
|
||||
.expect("config validation is broken");
|
||||
self.create_machine_from_template(template_def).await
|
||||
}
|
||||
config::Instance::Template { template } => todo!(),
|
||||
}
|
||||
|
||||
Ok(machine_id)
|
||||
}
|
||||
|
||||
pub async fn release(&self, machine_id: MachineId) -> MachineRegistryResult<()> {}
|
||||
@ -86,25 +101,88 @@ impl MachineRegistry {
|
||||
|
||||
async fn create_machine(
|
||||
&self,
|
||||
machine_def: config::Machine,
|
||||
machine_def: &config::Machine,
|
||||
) -> MachineRegistryResult<MachineId> {
|
||||
//
|
||||
// Get network from location
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn weighted_choice<T: fmt::Debug + Clone>(
|
||||
async fn create_machine_from_template(
|
||||
&self,
|
||||
weighted_list: &config::WeightedList<T>,
|
||||
) -> &T {
|
||||
template_def: &config::Template,
|
||||
) -> MachineRegistryResult<MachineId> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
async fn get_or_create_network_from_location(
|
||||
&self,
|
||||
location_def: &config::Location,
|
||||
) -> MachineRegistryResult<NetworkId> {
|
||||
match location_def {
|
||||
config::Location::Network { network } => {
|
||||
let network = self.weighted_choice(network);
|
||||
let network_def = self
|
||||
.unlocked_inner
|
||||
.config
|
||||
.networks
|
||||
.get(network)
|
||||
.expect("config validation is broken");
|
||||
self.get_or_create_network(network, network_def).await
|
||||
}
|
||||
config::Location::Blueprint { blueprint } => {
|
||||
let blueprint = self.weighted_choice(blueprint);
|
||||
let blueprint_def = self
|
||||
.unlocked_inner
|
||||
.config
|
||||
.blueprints
|
||||
.get(blueprint)
|
||||
.expect("config validation is broken");
|
||||
self.get_or_create_network_from_blueprint(blueprint, blueprint_def)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_or_create_network(
|
||||
&self,
|
||||
network: &String,
|
||||
network_def: &config::Network,
|
||||
) -> MachineRegistryResult<NetworkId> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
async fn get_or_create_network_from_blueprint(
|
||||
&self,
|
||||
blueprint: &String,
|
||||
blueprint_def: &config::Blueprint,
|
||||
) -> MachineRegistryResult<NetworkId> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn weighted_choice<'a, T: fmt::Debug + Clone>(
|
||||
&self,
|
||||
weighted_list: &'a config::WeightedList<T>,
|
||||
) -> &'a T {
|
||||
match weighted_list {
|
||||
config::WeightedList::Single(x) => x,
|
||||
config::WeightedList::List(vec) => {
|
||||
let total_weight = vec
|
||||
.iter()
|
||||
.map(|x| match x {
|
||||
config::Weighted::Weighted { item, weight } => weight,
|
||||
config::Weighted::Unweighted(item) => 1.0,
|
||||
})
|
||||
.reduce(|acc, x| acc + x);
|
||||
.map(|x| x.weight())
|
||||
.reduce(|acc, x| acc + x)
|
||||
.expect("config validation broken");
|
||||
|
||||
let r = rand::thread_rng().gen_range(0.0..=total_weight);
|
||||
let mut current_weight = 0.0f32;
|
||||
for x in vec {
|
||||
current_weight += x.weight();
|
||||
if r < current_weight {
|
||||
return x.item();
|
||||
}
|
||||
}
|
||||
// Catch f32 imprecision
|
||||
vec.last().expect("config validation broken").item()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,7 @@ struct RouterServerUnlockedInner {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RouterServerInner {
|
||||
//tcp_connections: HashMap<
|
||||
}
|
||||
struct RouterServerInner {}
|
||||
|
||||
/// Router server for virtual networking
|
||||
///
|
||||
|
Loading…
x
Reference in New Issue
Block a user