mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-01-26 22:37:05 -05:00
[skip ci] tracing + fix parsing
This commit is contained in:
parent
a16a54de0c
commit
87ba30d41f
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -6618,7 +6618,6 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"chrono",
|
||||
"clap 4.5.21",
|
||||
"config 0.14.1",
|
||||
"console_error_panic_hook",
|
||||
"ctrlc",
|
||||
"eyre",
|
||||
@ -6629,6 +6628,7 @@ dependencies = [
|
||||
"getrandom",
|
||||
"ifstructs",
|
||||
"imbl",
|
||||
"indent",
|
||||
"ipnet",
|
||||
"jni",
|
||||
"jni-sys",
|
||||
@ -6669,6 +6669,7 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"tracing-wasm",
|
||||
"validator",
|
||||
"veilid-bugsalot",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-bindgen-test",
|
||||
|
@ -52,14 +52,19 @@ virtual-network = []
|
||||
virtual-network-server = [
|
||||
"dep:async-tungstenite",
|
||||
"dep:clap",
|
||||
"dep:config",
|
||||
"dep:indent",
|
||||
"dep:ipnet",
|
||||
"dep:serde_yaml",
|
||||
"dep:validator",
|
||||
"dep:ws_stream_tungstenite",
|
||||
"dep:rand_chacha",
|
||||
]
|
||||
virtual-router = ["tracing", "virtual-network-server", "dep:time"]
|
||||
virtual-router = [
|
||||
"tracing",
|
||||
"virtual-network-server",
|
||||
"dep:time",
|
||||
"dep:bugsalot",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
tracing = { version = "0.1.40", features = [
|
||||
@ -99,6 +104,7 @@ imbl = { version = "3.0.0", features = ["serde"] }
|
||||
[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dependencies]
|
||||
async-io = { version = "1.13.0" }
|
||||
async-std = { version = "1.12.0", features = ["unstable"], optional = true }
|
||||
bugsalot = { package = "veilid-bugsalot", version = "0.2.0", optional = true }
|
||||
time = { version = "0.3.36", features = [
|
||||
"local-offset",
|
||||
"formatting",
|
||||
@ -111,6 +117,7 @@ futures-util = { version = "0.3.30", default-features = false, features = [
|
||||
"std",
|
||||
"io",
|
||||
] }
|
||||
indent = { version = "0.1.1", optional = true }
|
||||
libc = "0.2.155"
|
||||
nix = { version = "0.27.1", features = ["user"] }
|
||||
socket2 = { version = "0.5.7", features = ["all"] }
|
||||
@ -121,9 +128,6 @@ tokio-stream = { version = "0.1.15", features = ["net"], optional = true }
|
||||
ws_stream_tungstenite = { version = "0.14.0", optional = true }
|
||||
async-tungstenite = { version = "0.28.0", optional = true }
|
||||
clap = { version = "4", features = ["derive"], optional = true }
|
||||
config = { version = "^0", default-features = false, features = [
|
||||
"yaml",
|
||||
], optional = true }
|
||||
ipnet = { version = "2", features = ["serde"], optional = true }
|
||||
serde_yaml = { package = "serde_yaml_ng", version = "^0.10.0", optional = true }
|
||||
validator = { version = "0.19.0", features = ["derive"], optional = true }
|
||||
|
@ -27,7 +27,7 @@ cfg_if! {
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_IGNORE_LOG_TARGETS: &[&str] = &["tokio"];
|
||||
const DEFAULT_IGNORE_LOG_TARGETS: &[&str] = &["tokio", "runtime"];
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
#[group(multiple = false)]
|
||||
@ -70,6 +70,10 @@ struct CmdlineArgs {
|
||||
/// Instead of running the virtual router, print the configuration it would use to the console
|
||||
#[arg(long)]
|
||||
dump_config: bool,
|
||||
/// Wait for debugger to attach
|
||||
#[cfg(debug_assertions)]
|
||||
#[arg(long)]
|
||||
wait_for_debug: bool,
|
||||
|
||||
#[command(flatten)]
|
||||
logging: Logging,
|
||||
@ -116,7 +120,7 @@ fn setup_tracing(logging: &Logging) -> Result<(), String> {
|
||||
}
|
||||
|
||||
let layer = fmt::Layer::new()
|
||||
.compact()
|
||||
.pretty()
|
||||
.with_timer(timer)
|
||||
.with_ansi(true)
|
||||
.with_writer(std::io::stdout)
|
||||
@ -132,7 +136,14 @@ fn setup_tracing(logging: &Logging) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
fn main() {
|
||||
if let Err(e) = real_main() {
|
||||
eprintln!("{}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
fn real_main() -> Result<(), String> {
|
||||
let stop_source = StopSource::new();
|
||||
let stop_token = stop_source.token();
|
||||
let stop_mutex = Mutex::new(Some(stop_source));
|
||||
@ -148,6 +159,12 @@ fn main() -> Result<(), String> {
|
||||
|
||||
let args = CmdlineArgs::parse();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if args.wait_for_debug {
|
||||
use bugsalot::debugger;
|
||||
debugger::wait_until_attached(None).expect("state() not implemented on this platform");
|
||||
}
|
||||
|
||||
setup_tracing(&args.logging)?;
|
||||
|
||||
let initial_config = config::Config::new(&args.config_file, args.no_predefined_config)
|
||||
@ -188,9 +205,12 @@ fn main() -> Result<(), String> {
|
||||
None
|
||||
};
|
||||
|
||||
println!("Running...");
|
||||
router_server
|
||||
.run(stop_token)
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
.map_err(|e| e.to_string())?;
|
||||
println!("Done");
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
@ -9,10 +9,12 @@ const DEFAULT_CONFIG: &str = include_str!("default_config.yml");
|
||||
|
||||
#[derive(Debug, ThisError)]
|
||||
pub enum ConfigError {
|
||||
#[error("parse error")]
|
||||
ParseError(::config::ConfigError),
|
||||
#[error("validate error")]
|
||||
ValidateError(validator::ValidationErrors),
|
||||
#[error("io error: {0}")]
|
||||
IoError(std::io::Error),
|
||||
#[error("parse error: {0}: {1}")]
|
||||
ParseError(String, serde_yaml::Error),
|
||||
#[error("validate error: {0}")]
|
||||
ValidateError(String),
|
||||
#[error("no configuration files specified")]
|
||||
NoConfigFiles,
|
||||
}
|
||||
@ -227,6 +229,7 @@ pub struct Blueprint {
|
||||
#[serde(default)]
|
||||
#[validate(nested)]
|
||||
pub model: Option<WeightedList<String>>,
|
||||
#[serde(flatten)]
|
||||
#[validate(nested)]
|
||||
pub limits: BlueprintLimits,
|
||||
#[serde(default)]
|
||||
@ -407,9 +410,9 @@ fn validate_distribution(distribution: &Distribution) -> Result<(), ValidationEr
|
||||
return Err(ValidationError::new("baddistrib")
|
||||
.with_message("distribution mean must not be negative".into()));
|
||||
}
|
||||
if distribution.sigma < distribution.mean {
|
||||
if distribution.sigma < 0.0 {
|
||||
return Err(ValidationError::new("baddistrib")
|
||||
.with_message("distribution sigma must not be less than the mean".into()));
|
||||
.with_message("distribution sigma must not be negative".into()));
|
||||
}
|
||||
if distribution.max < distribution.min {
|
||||
return Err(ValidationError::new("baddistrib")
|
||||
@ -535,6 +538,48 @@ impl Validate for Config {
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_validation_errors(errors: ValidationErrors) -> String {
|
||||
let mut out = String::new();
|
||||
let errors = errors.into_errors();
|
||||
let mut keys: Vec<&str> = errors.keys().copied().collect();
|
||||
keys.sort();
|
||||
for k in keys {
|
||||
let v = errors.get(k).unwrap();
|
||||
let v_out = match v.clone() {
|
||||
validator::ValidationErrorsKind::Struct(validation_errors) => {
|
||||
expand_validation_errors(*validation_errors)
|
||||
}
|
||||
validator::ValidationErrorsKind::List(btree_map) => {
|
||||
let mut l_out = String::new();
|
||||
for (_, v) in btree_map {
|
||||
l_out += &expand_validation_errors(*v);
|
||||
}
|
||||
l_out
|
||||
}
|
||||
validator::ValidationErrorsKind::Field(vec) => {
|
||||
let mut v_out = String::new();
|
||||
for v in vec {
|
||||
v_out += &format!("{}\n", v);
|
||||
}
|
||||
v_out
|
||||
}
|
||||
};
|
||||
let v_out = indent::indent_all_by(4, v_out);
|
||||
|
||||
out += &format!("{k}:\n{v_out}\n");
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn map_validation_error<S: AsRef<str>>(
|
||||
name: S,
|
||||
) -> impl FnOnce(validator::ValidationErrors) -> ConfigError {
|
||||
let name = name.as_ref().to_string();
|
||||
move |errors| {
|
||||
ConfigError::ValidateError(format!("{name}: {}", expand_validation_errors(errors)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new<P: AsRef<Path>>(
|
||||
config_files: &[P],
|
||||
@ -543,19 +588,15 @@ impl Config {
|
||||
let mut out = Self::default();
|
||||
|
||||
if !no_predefined_config {
|
||||
out = load_predefined_config()
|
||||
.map_err(ConfigError::ParseError)?
|
||||
.try_deserialize()
|
||||
.map_err(ConfigError::ParseError)?;
|
||||
out.validate().map_err(ConfigError::ValidateError)?;
|
||||
out = load_predefined_config()?;
|
||||
out.validate()
|
||||
.map_err(map_validation_error("<predefined config>"))?;
|
||||
|
||||
// Load default config file
|
||||
if config_files.is_empty() {
|
||||
let cfg: Self = load_default_config()
|
||||
.map_err(ConfigError::ParseError)?
|
||||
.try_deserialize()
|
||||
.map_err(ConfigError::ParseError)?;
|
||||
cfg.validate().map_err(ConfigError::ValidateError)?;
|
||||
let cfg: Self = load_default_config()?;
|
||||
cfg.validate()
|
||||
.map_err(map_validation_error("<default config>"))?;
|
||||
|
||||
out = out.combine(cfg)?;
|
||||
}
|
||||
@ -568,11 +609,11 @@ impl Config {
|
||||
|
||||
// Load specified config files
|
||||
for config_file in config_files {
|
||||
let cfg: Self = load_config_file(config_file)
|
||||
.map_err(ConfigError::ParseError)?
|
||||
.try_deserialize()
|
||||
.map_err(ConfigError::ParseError)?;
|
||||
cfg.validate().map_err(ConfigError::ValidateError)?;
|
||||
let cfg: Self = load_config_file(config_file)?;
|
||||
cfg.validate().map_err(map_validation_error(format!(
|
||||
"{}",
|
||||
config_file.as_ref().to_string_lossy()
|
||||
)))?;
|
||||
|
||||
out = out.combine(cfg)?;
|
||||
}
|
||||
@ -603,8 +644,8 @@ impl Config {
|
||||
models: self.models.into_iter().chain(other.models).collect(),
|
||||
};
|
||||
|
||||
// Validate config
|
||||
out.validate().map_err(ConfigError::ValidateError)?;
|
||||
// Validate config (should never fail if combine inputs also validated)
|
||||
out.validate().map_err(map_validation_error("<combined>"))?;
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
@ -620,42 +661,18 @@ fn validate_hash_map<T: Validate>(value: &HashMap<String, T>) -> Result<(), Vali
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_predefined_config() -> Result<::config::Config, ::config::ConfigError> {
|
||||
::config::Config::builder()
|
||||
.add_source(::config::File::from_str(
|
||||
PREDEFINED_CONFIG,
|
||||
::config::FileFormat::Yaml,
|
||||
))
|
||||
.build()
|
||||
fn load_predefined_config() -> Result<Config, ConfigError> {
|
||||
serde_yaml::from_str(PREDEFINED_CONFIG)
|
||||
.map_err(|x| ConfigError::ParseError("<predefined config>".to_string(), x))
|
||||
}
|
||||
|
||||
fn load_default_config() -> Result<::config::Config, ::config::ConfigError> {
|
||||
::config::Config::builder()
|
||||
.add_source(::config::File::from_str(
|
||||
PREDEFINED_CONFIG,
|
||||
::config::FileFormat::Yaml,
|
||||
))
|
||||
.add_source(::config::File::from_str(
|
||||
DEFAULT_CONFIG,
|
||||
::config::FileFormat::Yaml,
|
||||
))
|
||||
.build()
|
||||
fn load_default_config() -> Result<Config, ConfigError> {
|
||||
serde_yaml::from_str(DEFAULT_CONFIG)
|
||||
.map_err(|x| ConfigError::ParseError("<default config>".to_string(), x))
|
||||
}
|
||||
|
||||
fn load_config_file<P: AsRef<Path>>(
|
||||
config_file: P,
|
||||
) -> Result<::config::Config, ::config::ConfigError> {
|
||||
let config_path = config_file.as_ref();
|
||||
let Some(config_file_str) = config_path.to_str() else {
|
||||
return Err(::config::ConfigError::Message(
|
||||
"config file path is not valid UTF-8".to_owned(),
|
||||
));
|
||||
};
|
||||
let config = ::config::Config::builder()
|
||||
.add_source(::config::File::new(
|
||||
config_file_str,
|
||||
::config::FileFormat::Yaml,
|
||||
))
|
||||
.build()?;
|
||||
Ok(config)
|
||||
fn load_config_file<P: AsRef<Path>>(config_file: P) -> Result<Config, ConfigError> {
|
||||
let rdr = std::fs::File::open(&config_file).map_err(ConfigError::IoError)?;
|
||||
serde_yaml::from_reader(rdr)
|
||||
.map_err(|x| ConfigError::ParseError(config_file.as_ref().to_string_lossy().to_string(), x))
|
||||
}
|
||||
|
@ -117,7 +117,10 @@ networks:
|
||||
|
||||
# # Predefined networks
|
||||
# $internet:
|
||||
# allocation: "$internet"
|
||||
# ipv4:
|
||||
# allocation: "$internet"
|
||||
# ipv6:
|
||||
# allocation: "$internet"
|
||||
# model: "$internet"
|
||||
|
||||
#################################################################
|
||||
|
@ -91,6 +91,7 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
Ok(opt_allocation.is_some())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub fn reserve_allocation_v4(
|
||||
&mut self,
|
||||
allocation: Ipv4Net,
|
||||
@ -113,6 +114,7 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
Ok(scope)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub fn reserve_allocation_v6(
|
||||
&mut self,
|
||||
allocation: Ipv6Net,
|
||||
@ -157,6 +159,7 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
overlaps
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, srng), err)]
|
||||
pub fn allocate_random_v4(
|
||||
&mut self,
|
||||
srng: &mut StableRng,
|
||||
@ -181,6 +184,7 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, srng), err)]
|
||||
pub fn allocate_random_v6(
|
||||
&mut self,
|
||||
srng: &mut StableRng,
|
||||
@ -205,6 +209,7 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub fn release_allocation_v4(
|
||||
&mut self,
|
||||
allocation: Ipv4Net,
|
||||
@ -222,6 +227,7 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
Ok(opt_tag)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub fn release_allocation_v6(
|
||||
&mut self,
|
||||
allocation: Ipv6Net,
|
||||
@ -274,6 +280,7 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
false
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub fn clear_ipv4<F: FnMut(Ipv4Net, &T) -> bool>(
|
||||
&mut self,
|
||||
mut check: F,
|
||||
@ -296,6 +303,7 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub fn clear_ipv6<F: FnMut(Ipv6Net, &T) -> bool>(
|
||||
&mut self,
|
||||
mut check: F,
|
||||
|
@ -45,6 +45,7 @@ impl GlobalStateManagerInner {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub fn execute_config(&mut self, cfg: config::Config) -> GlobalStateManagerResult<()> {
|
||||
// Create random number generator
|
||||
if let Some(seed) = cfg.seed {
|
||||
@ -71,77 +72,77 @@ impl GlobalStateManagerInner {
|
||||
|
||||
// Import all models
|
||||
for (name, model) in cfg.models {
|
||||
self.execute_config_model(name, model)?;
|
||||
self.execute_config_model(&name, &model)?;
|
||||
}
|
||||
|
||||
// Create all profile states
|
||||
for (name, profile) in cfg.profiles {
|
||||
self.execute_config_profile(name, profile)?;
|
||||
self.execute_config_profile(&name, &profile)?;
|
||||
}
|
||||
|
||||
// Create all network states
|
||||
// Don't process gateways yet because they will depend on networks existing
|
||||
for (name, network) in cfg.networks.clone() {
|
||||
for (name, network) in &cfg.networks {
|
||||
self.execute_config_network(name, network)?;
|
||||
}
|
||||
// Process all ipv4 and ipv6 configurations
|
||||
for (name, network) in cfg.networks.clone() {
|
||||
for (name, network) in &cfg.networks {
|
||||
if let Some(ipv4) = network.ipv4.as_ref() {
|
||||
self.execute_config_network_ipv4(name.clone(), ipv4)?;
|
||||
self.execute_config_network_ipv4(name, ipv4)?;
|
||||
}
|
||||
if let Some(ipv6) = network.ipv6.as_ref() {
|
||||
self.execute_config_network_ipv6(name.clone(), ipv6)?;
|
||||
self.execute_config_network_ipv6(name, ipv6)?;
|
||||
}
|
||||
}
|
||||
// Process all network gateways
|
||||
for (name, network) in cfg.networks {
|
||||
for (name, network) in &cfg.networks {
|
||||
if let Some(ipv4) = network.ipv4.as_ref() {
|
||||
if let Some(ipv4gw) = ipv4.gateway.as_ref() {
|
||||
self.execute_config_network_ipv4_gateway(name.clone(), ipv4gw)?;
|
||||
self.execute_config_network_ipv4_gateway(name, ipv4gw)?;
|
||||
}
|
||||
}
|
||||
if let Some(ipv6) = network.ipv6.as_ref() {
|
||||
if let Some(ipv6gw) = ipv6.gateway.as_ref() {
|
||||
self.execute_config_network_ipv6_gateway(name.clone(), ipv6gw)?;
|
||||
self.execute_config_network_ipv6_gateway(name, ipv6gw)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create all blueprint states
|
||||
// Don't process gateways yet because they will depend on blueprints existing
|
||||
for (name, blueprint) in cfg.blueprints.clone() {
|
||||
for (name, blueprint) in &cfg.blueprints {
|
||||
self.execute_config_blueprint(name, blueprint)?;
|
||||
}
|
||||
// Process all ipv4 and ipv6 configurations
|
||||
for (name, blueprint) in cfg.blueprints.clone() {
|
||||
for (name, blueprint) in &cfg.blueprints {
|
||||
if let Some(ipv4) = blueprint.ipv4.as_ref() {
|
||||
self.execute_config_blueprint_ipv4(name.clone(), ipv4)?;
|
||||
self.execute_config_blueprint_ipv4(name, ipv4)?;
|
||||
}
|
||||
if let Some(ipv6) = blueprint.ipv6.as_ref() {
|
||||
self.execute_config_blueprint_ipv6(name.clone(), ipv6)?;
|
||||
self.execute_config_blueprint_ipv6(name, ipv6)?;
|
||||
}
|
||||
}
|
||||
// Process all blueprint gateways
|
||||
for (name, blueprint) in cfg.blueprints {
|
||||
for (name, blueprint) in &cfg.blueprints {
|
||||
if let Some(ipv4) = blueprint.ipv4.as_ref() {
|
||||
if let Some(ipv4gw) = ipv4.gateway.as_ref() {
|
||||
self.execute_config_blueprint_ipv4_gateway(name.clone(), ipv4gw)?;
|
||||
self.execute_config_blueprint_ipv4_gateway(name, ipv4gw)?;
|
||||
}
|
||||
}
|
||||
if let Some(ipv6) = blueprint.ipv6.as_ref() {
|
||||
if let Some(ipv6gw) = ipv6.gateway.as_ref() {
|
||||
self.execute_config_blueprint_ipv6_gateway(name.clone(), ipv6gw)?;
|
||||
self.execute_config_blueprint_ipv6_gateway(name, ipv6gw)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create all template states
|
||||
for (name, template) in cfg.templates {
|
||||
for (name, template) in &cfg.templates {
|
||||
self.execute_config_template(name, template)?;
|
||||
}
|
||||
|
||||
// Create all machine states
|
||||
for (name, machine) in cfg.machines {
|
||||
for (name, machine) in &cfg.machines {
|
||||
self.execute_config_machine(name, machine)?;
|
||||
}
|
||||
|
||||
@ -332,81 +333,86 @@ impl GlobalStateManagerInner {
|
||||
&mut self.blueprint_state_registry
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
fn execute_config_allocations(
|
||||
&mut self,
|
||||
config_allocations: &HashMap<String, config::Allocation>,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
for (allocation_name, allocation_config) in config_allocations {
|
||||
if self.allocations.contains_key(allocation_name) {
|
||||
return Err(GlobalStateManagerError::DuplicateName(
|
||||
allocation_name.clone(),
|
||||
));
|
||||
for (name, allocation_config) in config_allocations {
|
||||
if self.allocations.contains_key(name) {
|
||||
return Err(GlobalStateManagerError::DuplicateName(name.clone()));
|
||||
}
|
||||
let address_pool =
|
||||
self.resolve_address_pool(allocation_name.clone(), config_allocations)?;
|
||||
self.allocations.insert(
|
||||
allocation_name.clone(),
|
||||
Arc::new(Allocation {
|
||||
config: allocation_config.clone(),
|
||||
address_pool,
|
||||
}),
|
||||
);
|
||||
let address_pool = self.resolve_address_pool(name.clone(), config_allocations)?;
|
||||
|
||||
let allocation = Arc::new(Allocation {
|
||||
config: allocation_config.clone(),
|
||||
address_pool,
|
||||
});
|
||||
debug!("Added allocation: {}: {:?}", name, allocation);
|
||||
self.allocations.insert(name.clone(), allocation);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
#[instrument(level = "debug", skip(self, model), err)]
|
||||
fn execute_config_model(
|
||||
&mut self,
|
||||
name: String,
|
||||
model: config::Model,
|
||||
name: &str,
|
||||
model: &config::Model,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
if self.models.contains_key(&name) {
|
||||
return Err(GlobalStateManagerError::DuplicateName(name));
|
||||
if self.models.contains_key(name) {
|
||||
return Err(GlobalStateManagerError::DuplicateName(name.to_owned()));
|
||||
}
|
||||
self.models.insert(name, model);
|
||||
debug!("Added model: {}: {:?}", name, model);
|
||||
self.models.insert(name.to_owned(), model.to_owned());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, profile), err)]
|
||||
fn execute_config_profile(
|
||||
&mut self,
|
||||
name: String,
|
||||
profile: config::Profile,
|
||||
name: &str,
|
||||
profile: &config::Profile,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
if self
|
||||
.profile_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.is_some()
|
||||
{
|
||||
return Err(GlobalStateManagerError::DuplicateName(name));
|
||||
return Err(GlobalStateManagerError::DuplicateName(name.to_owned()));
|
||||
}
|
||||
|
||||
let id = self.profile_state_registry.allocate_id();
|
||||
let state = ProfileState::new(id, name, profile);
|
||||
let state = ProfileState::new(id, name.to_owned(), profile.clone());
|
||||
self.profile_state_registry
|
||||
.attach_state(state)
|
||||
.expect("must attach");
|
||||
|
||||
debug!("Added profile: {}: {:?}", name, profile);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, network), err)]
|
||||
fn execute_config_network(
|
||||
&mut self,
|
||||
name: String,
|
||||
network: config::Network,
|
||||
name: &str,
|
||||
network: &config::Network,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
if self
|
||||
.network_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.is_some()
|
||||
{
|
||||
return Err(GlobalStateManagerError::DuplicateName(name));
|
||||
return Err(GlobalStateManagerError::DuplicateName(name.to_owned()));
|
||||
}
|
||||
|
||||
let id = self.network_state_registry.allocate_id();
|
||||
let state = {
|
||||
let mut network_state = NetworkState::new(id, Some(name), NetworkOrigin::Direct);
|
||||
let mut network_state =
|
||||
NetworkState::new(id, Some(name.to_owned()), NetworkOrigin::Direct);
|
||||
|
||||
// Set model
|
||||
let model_name = self.or_default_model(network.model)?;
|
||||
let model_name = self.or_default_model(network.model.to_owned())?;
|
||||
let model = self
|
||||
.models
|
||||
.get(&model_name)
|
||||
@ -427,17 +433,20 @@ impl GlobalStateManagerInner {
|
||||
self.network_state_registry
|
||||
.attach_state(state)
|
||||
.expect("must attach");
|
||||
|
||||
debug!("Added network: {}: {:?}", name, network);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, ipv4), err)]
|
||||
fn execute_config_network_ipv4(
|
||||
&mut self,
|
||||
name: String,
|
||||
name: &str,
|
||||
ipv4: &config::NetworkIpv4,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let network_state_id = self
|
||||
.network_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.expect("must exist");
|
||||
let mut network_state = self
|
||||
.network_state_registry
|
||||
@ -470,14 +479,15 @@ impl GlobalStateManagerInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, ipv6), err)]
|
||||
fn execute_config_network_ipv6(
|
||||
&mut self,
|
||||
name: String,
|
||||
name: &str,
|
||||
ipv6: &config::NetworkIpv6,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let network_state_id = self
|
||||
.network_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.expect("must exist");
|
||||
let mut network_state = self
|
||||
.network_state_registry
|
||||
@ -510,14 +520,15 @@ impl GlobalStateManagerInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, ipv4gw), err)]
|
||||
fn execute_config_network_ipv4_gateway(
|
||||
&mut self,
|
||||
name: String,
|
||||
name: &str,
|
||||
ipv4gw: &config::NetworkGateway,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let network_state_id = self
|
||||
.network_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.expect("must exist");
|
||||
let mut network_state = self
|
||||
.network_state_registry
|
||||
@ -550,14 +561,15 @@ impl GlobalStateManagerInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, ipv6gw), err)]
|
||||
fn execute_config_network_ipv6_gateway(
|
||||
&mut self,
|
||||
name: String,
|
||||
name: &str,
|
||||
ipv6gw: &config::NetworkGateway,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let network_state_id = self
|
||||
.network_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.expect("must exist");
|
||||
let mut network_state = self
|
||||
.network_state_registry
|
||||
@ -590,25 +602,26 @@ impl GlobalStateManagerInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, blueprint), err)]
|
||||
fn execute_config_blueprint(
|
||||
&mut self,
|
||||
name: String,
|
||||
blueprint: config::Blueprint,
|
||||
name: &str,
|
||||
blueprint: &config::Blueprint,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
if self
|
||||
.blueprint_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.is_some()
|
||||
{
|
||||
return Err(GlobalStateManagerError::DuplicateName(name));
|
||||
return Err(GlobalStateManagerError::DuplicateName(name.to_owned()));
|
||||
}
|
||||
|
||||
let id = self.blueprint_state_registry.allocate_id();
|
||||
let state = {
|
||||
let mut blueprint_state = BlueprintState::new(id, name);
|
||||
let mut blueprint_state = BlueprintState::new(id, name.to_owned());
|
||||
|
||||
// Set model
|
||||
let model = match blueprint.model {
|
||||
let model = match blueprint.model.to_owned() {
|
||||
Some(x) => x,
|
||||
None => WeightedList::Single(
|
||||
self.default_model
|
||||
@ -620,6 +633,7 @@ impl GlobalStateManagerInner {
|
||||
blueprint_state.set_limit_network_count(
|
||||
blueprint
|
||||
.limits
|
||||
.to_owned()
|
||||
.network_count
|
||||
.map(|wl| self.srng().weighted_choice(wl)),
|
||||
);
|
||||
@ -634,17 +648,21 @@ impl GlobalStateManagerInner {
|
||||
self.blueprint_state_registry
|
||||
.attach_state(state)
|
||||
.expect("must attach");
|
||||
|
||||
debug!("Added blueprint: {}: {:?}", name, blueprint);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, ipv4), err)]
|
||||
fn execute_config_blueprint_ipv4(
|
||||
&mut self,
|
||||
name: String,
|
||||
name: &str,
|
||||
ipv4: &config::BlueprintIpv4,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let blueprint_state_id = self
|
||||
.blueprint_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.expect("must exist");
|
||||
let mut blueprint_state = self
|
||||
.blueprint_state_registry
|
||||
@ -697,14 +715,15 @@ impl GlobalStateManagerInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, ipv4gw), err)]
|
||||
fn execute_config_blueprint_ipv4_gateway(
|
||||
&mut self,
|
||||
name: String,
|
||||
name: &str,
|
||||
ipv4gw: &config::BlueprintGateway,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let blueprint_state_id = self
|
||||
.blueprint_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.expect("must exist");
|
||||
let mut blueprint_state = self
|
||||
.blueprint_state_registry
|
||||
@ -747,14 +766,15 @@ impl GlobalStateManagerInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, ipv6), err)]
|
||||
fn execute_config_blueprint_ipv6(
|
||||
&mut self,
|
||||
name: String,
|
||||
name: &str,
|
||||
ipv6: &config::BlueprintIpv6,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let blueprint_state_id = self
|
||||
.blueprint_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.expect("must exist");
|
||||
let mut blueprint_state = self
|
||||
.blueprint_state_registry
|
||||
@ -807,14 +827,15 @@ impl GlobalStateManagerInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, ipv6gw), err)]
|
||||
fn execute_config_blueprint_ipv6_gateway(
|
||||
&mut self,
|
||||
name: String,
|
||||
name: &str,
|
||||
ipv6gw: &config::BlueprintGateway,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let blueprint_state_id = self
|
||||
.blueprint_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.expect("must exist");
|
||||
let mut blueprint_state = self
|
||||
.blueprint_state_registry
|
||||
@ -857,25 +878,26 @@ impl GlobalStateManagerInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, template), err)]
|
||||
fn execute_config_template(
|
||||
&mut self,
|
||||
name: String,
|
||||
template: config::Template,
|
||||
name: &str,
|
||||
template: &config::Template,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
if self
|
||||
.template_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.is_some()
|
||||
{
|
||||
return Err(GlobalStateManagerError::DuplicateName(name));
|
||||
return Err(GlobalStateManagerError::DuplicateName(name.to_owned()));
|
||||
}
|
||||
|
||||
let id = self.template_state_registry.allocate_id();
|
||||
let state = {
|
||||
let mut template_state = TemplateState::new(id, name);
|
||||
let mut template_state = TemplateState::new(id, name.to_owned());
|
||||
|
||||
template_state.set_disable_capabilities(template.disable_capabilities);
|
||||
if let Some(wl) = template.limits.machine_count {
|
||||
template_state.set_disable_capabilities(template.disable_capabilities.to_owned());
|
||||
if let Some(wl) = template.limits.to_owned().machine_count {
|
||||
template_state.set_limit_machine_count(Some(self.srng().weighted_choice(wl)));
|
||||
}
|
||||
template_state
|
||||
@ -912,33 +934,37 @@ impl GlobalStateManagerInner {
|
||||
self.template_state_registry
|
||||
.attach_state(state)
|
||||
.expect("must attach");
|
||||
|
||||
debug!("Added template: {}: {:?}", name, template);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, machine), err)]
|
||||
fn execute_config_machine(
|
||||
&mut self,
|
||||
name: String,
|
||||
machine: config::Machine,
|
||||
name: &str,
|
||||
machine: &config::Machine,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
if self
|
||||
.machine_state_registry
|
||||
.get_state_id_by_name(&name)
|
||||
.get_state_id_by_name(name)
|
||||
.is_some()
|
||||
{
|
||||
return Err(GlobalStateManagerError::DuplicateName(name));
|
||||
return Err(GlobalStateManagerError::DuplicateName(name.to_owned()));
|
||||
}
|
||||
|
||||
let id = self.machine_state_registry.allocate_id();
|
||||
let state = {
|
||||
let mut machine_state = MachineState::new(id, Some(name), MachineOrigin::Config);
|
||||
let mut machine_state =
|
||||
MachineState::new(id, Some(name.to_owned()), MachineOrigin::Config);
|
||||
|
||||
machine_state.set_disable_capabilities(machine.disable_capabilities);
|
||||
machine_state.set_disable_capabilities(machine.disable_capabilities.to_owned());
|
||||
machine_state.set_bootstrap(machine.bootstrap);
|
||||
|
||||
// Create primary interface
|
||||
let interface_name = machine_state.allocate_interface(None, None)?;
|
||||
|
||||
match machine.location {
|
||||
match machine.location.to_owned() {
|
||||
config::MachineLocation::Network {
|
||||
network,
|
||||
address4,
|
||||
@ -980,9 +1006,11 @@ impl GlobalStateManagerInner {
|
||||
self.machine_state_registry
|
||||
.attach_state(state)
|
||||
.expect("must attach");
|
||||
debug!("Added machine: {}: {:?}", name, machine);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, config_allocations), err)]
|
||||
fn resolve_address_pool(
|
||||
&self,
|
||||
allocation_name: String,
|
||||
@ -1009,6 +1037,9 @@ impl GlobalStateManagerInner {
|
||||
}
|
||||
|
||||
// Reserve out any allocations that used this as their pool
|
||||
let mut scope4_allocs: Vec<Ipv4Net> = Vec::new();
|
||||
let mut scope6_allocs: Vec<Ipv6Net> = Vec::new();
|
||||
|
||||
for (k, v) in config_allocations {
|
||||
// Exclude our own allocation
|
||||
if *k == allocation_name {
|
||||
@ -1018,7 +1049,8 @@ impl GlobalStateManagerInner {
|
||||
let pool = self.or_default_pool(scope4.pool4.clone())?;
|
||||
if pool == allocation_name {
|
||||
for s in &scope4.scope4 {
|
||||
address_pool.reserve_allocation_v4(*s, None)?;
|
||||
scope4_allocs.push(*s);
|
||||
scope4_allocs = Ipv4Net::aggregate(&scope4_allocs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1026,12 +1058,20 @@ impl GlobalStateManagerInner {
|
||||
let pool = self.or_default_pool(scope6.pool6.clone())?;
|
||||
if pool == allocation_name {
|
||||
for s in &scope6.scope6 {
|
||||
address_pool.reserve_allocation_v6(*s, None)?;
|
||||
scope6_allocs.push(*s);
|
||||
scope6_allocs = Ipv6Net::aggregate(&scope6_allocs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for s in scope4_allocs {
|
||||
address_pool.reserve_allocation_v4(s, None)?;
|
||||
}
|
||||
for s in scope6_allocs {
|
||||
address_pool.reserve_allocation_v6(s, None)?;
|
||||
}
|
||||
|
||||
Ok(address_pool)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,245 @@
|
||||
use super::*;
|
||||
|
||||
/// Locations where a network can be instantiated when a blueprint is generated
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BlueprintLocationsList {
|
||||
/// Network will be a new allocation
|
||||
Allocations { allocations: WeightedList<String> },
|
||||
/// Network will be allocated as a subnet of an existing network
|
||||
Networks {
|
||||
networks: WeightedList<NetworkStateId>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkLocation<T> {
|
||||
pub scope: Vec<T>,
|
||||
pub reserve: Vec<T>,
|
||||
pub super_net: Option<NetworkStateId>,
|
||||
}
|
||||
|
||||
impl BlueprintLocationsList {
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub fn pick_v4(
|
||||
&self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
prefix: &WeightedList<u8>,
|
||||
) -> GlobalStateManagerResult<Option<NetworkLocation<Ipv4Net>>> {
|
||||
// Get maximum prefix
|
||||
let max_prefix = prefix
|
||||
.iter()
|
||||
.max()
|
||||
.copied()
|
||||
.expect("must have at least one element");
|
||||
|
||||
// Get addresses for network
|
||||
match self {
|
||||
BlueprintLocationsList::Allocations { allocations } => {
|
||||
// Get allocations which have subnets that would fit
|
||||
// our maximum requested prefix
|
||||
let Some(address_pools) = allocations.try_filter_map(|allocation_name| {
|
||||
let allocation = gsm_inner
|
||||
.allocations()
|
||||
.get(allocation_name)
|
||||
.expect("must exist");
|
||||
if allocation.address_pool.can_allocate_v4(max_prefix)? {
|
||||
Ok(Some(allocation.address_pool.clone()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Pick an address pool
|
||||
let mut address_pool = gsm_inner.srng().weighted_choice(address_pools);
|
||||
|
||||
// Pick a prefix length that would fit in the subnet
|
||||
let opt_subnet = prefix
|
||||
.try_filter(|p| address_pool.can_allocate_v4(*p))?
|
||||
.as_ref()
|
||||
.map(|wl| {
|
||||
let subnet_prefix = *gsm_inner.srng().weighted_choice_ref(wl);
|
||||
|
||||
address_pool.allocate_random_v4(gsm_inner.srng(), subnet_prefix, ())
|
||||
})
|
||||
.transpose()?
|
||||
.flatten();
|
||||
let Some(subnet) = opt_subnet else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(NetworkLocation {
|
||||
scope: vec![subnet],
|
||||
reserve: Vec::new(),
|
||||
super_net: None,
|
||||
}))
|
||||
}
|
||||
BlueprintLocationsList::Networks { networks } => {
|
||||
// Get networks which have subnets that would fit
|
||||
// our maximum requested prefix
|
||||
let Some(available_networks) = networks.try_filter(|network_id| {
|
||||
let super_network_state = gsm_inner
|
||||
.network_states()
|
||||
.get_state(*network_id)
|
||||
.expect("must exist");
|
||||
|
||||
Ok(super_network_state.can_allocate_subnet_v4(None, max_prefix))
|
||||
})?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Pick a network
|
||||
let super_network_id = *gsm_inner.srng().weighted_choice_ref(&available_networks);
|
||||
let mut super_network_state = gsm_inner
|
||||
.network_states()
|
||||
.get_state(super_network_id)
|
||||
.expect("must exist");
|
||||
|
||||
// Pick a prefix that fits in this network and allocate from it
|
||||
let opt_subnet = prefix
|
||||
.filter(|p| super_network_state.can_allocate_subnet_v4(None, *p))
|
||||
.as_ref()
|
||||
.map(|wl| {
|
||||
let subnet_prefix = *gsm_inner.srng().weighted_choice_ref(wl);
|
||||
|
||||
// Allocate subnet from this network
|
||||
super_network_state.allocate_subnet_v4(
|
||||
gsm_inner,
|
||||
OwnerTag::Network(super_network_state.id()),
|
||||
None,
|
||||
subnet_prefix,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
let Some(subnet) = opt_subnet else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Update network state
|
||||
gsm_inner
|
||||
.network_states_mut()
|
||||
.set_state(super_network_state);
|
||||
|
||||
Ok(Some(NetworkLocation {
|
||||
scope: vec![subnet],
|
||||
reserve: Vec::new(),
|
||||
super_net: Some(super_network_id),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub fn pick_v6(
|
||||
&self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
prefix: &WeightedList<u8>,
|
||||
) -> GlobalStateManagerResult<Option<NetworkLocation<Ipv6Net>>> {
|
||||
// Get maximum prefix
|
||||
let max_prefix = prefix
|
||||
.iter()
|
||||
.max()
|
||||
.copied()
|
||||
.expect("must have at least one element");
|
||||
|
||||
// Get addresses for network
|
||||
match self {
|
||||
BlueprintLocationsList::Allocations { allocations } => {
|
||||
// Get allocations which have subnets that would fit
|
||||
// our maximum requested prefix
|
||||
let Some(address_pools) = allocations.try_filter_map(|allocation_name| {
|
||||
let allocation = gsm_inner
|
||||
.allocations()
|
||||
.get(allocation_name)
|
||||
.expect("must exist");
|
||||
if allocation.address_pool.can_allocate_v6(max_prefix)? {
|
||||
Ok(Some(allocation.address_pool.clone()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Pick an address pool
|
||||
let mut address_pool = gsm_inner.srng().weighted_choice(address_pools);
|
||||
|
||||
// Pick a prefix length that would fit in the subnet
|
||||
let opt_subnet = prefix
|
||||
.try_filter(|p| address_pool.can_allocate_v6(*p))?
|
||||
.as_ref()
|
||||
.map(|wl| {
|
||||
let subnet_prefix = *gsm_inner.srng().weighted_choice_ref(wl);
|
||||
|
||||
address_pool.allocate_random_v6(gsm_inner.srng(), subnet_prefix, ())
|
||||
})
|
||||
.transpose()?
|
||||
.flatten();
|
||||
let Some(subnet) = opt_subnet else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(NetworkLocation {
|
||||
scope: vec![subnet],
|
||||
reserve: Vec::new(),
|
||||
super_net: None,
|
||||
}))
|
||||
}
|
||||
BlueprintLocationsList::Networks { networks } => {
|
||||
// Get networks which have subnets that would fit
|
||||
// our maximum requested prefix
|
||||
let Some(available_networks) = networks.try_filter(|network_id| {
|
||||
let super_network_state = gsm_inner
|
||||
.network_states()
|
||||
.get_state(*network_id)
|
||||
.expect("must exist");
|
||||
|
||||
Ok(super_network_state.can_allocate_subnet_v6(None, max_prefix))
|
||||
})?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Pick a network
|
||||
let super_network_id = *gsm_inner.srng().weighted_choice_ref(&available_networks);
|
||||
let mut super_network_state = gsm_inner
|
||||
.network_states()
|
||||
.get_state(super_network_id)
|
||||
.expect("must exist");
|
||||
|
||||
// Pick a prefix that fits in this network and allocate from it
|
||||
let opt_subnet = prefix
|
||||
.filter(|p| super_network_state.can_allocate_subnet_v6(None, *p))
|
||||
.as_ref()
|
||||
.map(|wl| {
|
||||
let subnet_prefix = *gsm_inner.srng().weighted_choice_ref(wl);
|
||||
|
||||
// Allocate subnet from this network
|
||||
super_network_state.allocate_subnet_v6(
|
||||
gsm_inner,
|
||||
OwnerTag::Network(super_network_state.id()),
|
||||
None,
|
||||
subnet_prefix,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
let Some(subnet) = opt_subnet else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Update network state
|
||||
gsm_inner
|
||||
.network_states_mut()
|
||||
.set_state(super_network_state);
|
||||
|
||||
Ok(Some(NetworkLocation {
|
||||
scope: vec![subnet],
|
||||
reserve: Vec::new(),
|
||||
super_net: Some(super_network_id),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -82,6 +82,7 @@ impl BlueprintState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_limit_network_count(&mut self, limit_network_count: Option<usize>) {
|
||||
// Update fields
|
||||
self.fields = Arc::new(BlueprintStateFields {
|
||||
@ -90,6 +91,7 @@ impl BlueprintState {
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_model(&mut self, model: WeightedList<String>) {
|
||||
let model = Some(model.map(|x| Arc::new(x.clone())));
|
||||
// Update fields
|
||||
@ -99,6 +101,7 @@ impl BlueprintState {
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn clear_ipv4(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -118,6 +121,7 @@ impl BlueprintState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, _gsm_inner), err)]
|
||||
pub fn clear_ipv4_gateway(
|
||||
&mut self,
|
||||
_gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -141,6 +145,7 @@ impl BlueprintState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn set_ipv4(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -166,6 +171,7 @@ impl BlueprintState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn set_ipv4_gateway(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -198,6 +204,7 @@ impl BlueprintState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn clear_ipv6(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -217,6 +224,7 @@ impl BlueprintState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, _gsm_inner), err)]
|
||||
pub fn clear_ipv6_gateway(
|
||||
&mut self,
|
||||
_gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -240,6 +248,7 @@ impl BlueprintState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn set_ipv6(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -265,6 +274,7 @@ impl BlueprintState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn set_ipv6_gateway(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -314,6 +324,7 @@ impl BlueprintState {
|
||||
ok
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
fn generate_model_inner(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -338,6 +349,7 @@ impl BlueprintState {
|
||||
}
|
||||
|
||||
/// Network filter that ensures we can allocate an ipv4 gateway address on a network
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
fn gateway_network_filter_v4(
|
||||
&self,
|
||||
gsm_inner: &GlobalStateManagerInner,
|
||||
@ -353,6 +365,7 @@ impl BlueprintState {
|
||||
}
|
||||
|
||||
/// Network filter that ensures we can allocate an ipv4 gateway address on a network
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
fn gateway_network_filter_v6(
|
||||
&self,
|
||||
gsm_inner: &GlobalStateManagerInner,
|
||||
@ -367,6 +380,7 @@ impl BlueprintState {
|
||||
Ok(can_allocate)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
fn generate_ipv4_inner(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -464,6 +478,7 @@ impl BlueprintState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
fn generate_ipv6_inner(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -561,6 +576,7 @@ impl BlueprintState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn generate(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -612,6 +628,7 @@ impl BlueprintState {
|
||||
Ok(network_state_id)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, callback), err)]
|
||||
pub fn for_each_network_id<F, R>(&self, mut callback: F) -> GlobalStateManagerResult<Option<R>>
|
||||
where
|
||||
F: FnMut(NetworkStateId) -> GlobalStateManagerResult<Option<R>>,
|
||||
@ -624,6 +641,7 @@ impl BlueprintState {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn on_network_released(&mut self, network_id: NetworkStateId) {
|
||||
// Remove network from list
|
||||
let pos = self
|
||||
|
@ -60,6 +60,7 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner))]
|
||||
pub fn release(mut self, gsm_inner: &mut GlobalStateManagerInner) {
|
||||
self.release_all_interfaces(gsm_inner)
|
||||
.expect("must succeed");
|
||||
@ -74,6 +75,7 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_disable_capabilities(&mut self, disable_capabilities: Vec<String>) {
|
||||
self.fields = Arc::new(MachineStateFields {
|
||||
disable_capabilities: disable_capabilities.into(),
|
||||
@ -81,6 +83,7 @@ impl MachineState {
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_bootstrap(&mut self, bootstrap: bool) {
|
||||
self.fields = Arc::new(MachineStateFields {
|
||||
bootstrap,
|
||||
@ -99,6 +102,7 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub fn allocate_interface(
|
||||
&mut self,
|
||||
interface_name: Option<String>,
|
||||
@ -144,6 +148,7 @@ impl MachineState {
|
||||
intfs
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn allocate_address_ipv4(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -210,6 +215,7 @@ impl MachineState {
|
||||
Ok(ifv4_addr)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn allocate_address_ipv6(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -275,6 +281,7 @@ impl MachineState {
|
||||
Ok(ifv6_addr)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn attach_network(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -310,6 +317,7 @@ impl MachineState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn detach_network(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -353,6 +361,7 @@ impl MachineState {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn release_address(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -415,6 +424,7 @@ impl MachineState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn release_all_addresses(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -445,6 +455,39 @@ impl MachineState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn release_all_interfaces(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let interface_names: Vec<String> = self
|
||||
.fields
|
||||
.interfaces
|
||||
.keys()
|
||||
.map(|x| (**x).clone())
|
||||
.collect();
|
||||
for interface_name in interface_names {
|
||||
let interface_key = Arc::new(interface_name);
|
||||
let Some(mut machine_state_interface) =
|
||||
self.fields.interfaces.get(&interface_key).cloned()
|
||||
else {
|
||||
return Err(GlobalStateManagerError::InvalidName(
|
||||
(*interface_key).clone(),
|
||||
));
|
||||
};
|
||||
|
||||
Self::detach_network_inner(gsm_inner, &mut machine_state_interface)?;
|
||||
}
|
||||
|
||||
// Update fields
|
||||
self.fields = Arc::new(MachineStateFields {
|
||||
interfaces: imbl::HashMap::new(),
|
||||
..(*self.fields).clone()
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn detach_network_inner(
|
||||
@ -514,38 +557,6 @@ impl MachineState {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn release_all_interfaces(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
let interface_names: Vec<String> = self
|
||||
.fields
|
||||
.interfaces
|
||||
.keys()
|
||||
.map(|x| (**x).clone())
|
||||
.collect();
|
||||
for interface_name in interface_names {
|
||||
let interface_key = Arc::new(interface_name);
|
||||
let Some(mut machine_state_interface) =
|
||||
self.fields.interfaces.get(&interface_key).cloned()
|
||||
else {
|
||||
return Err(GlobalStateManagerError::InvalidName(
|
||||
(*interface_key).clone(),
|
||||
));
|
||||
};
|
||||
|
||||
Self::detach_network_inner(gsm_inner, &mut machine_state_interface)?;
|
||||
}
|
||||
|
||||
// Update fields
|
||||
self.fields = Arc::new(MachineStateFields {
|
||||
interfaces: imbl::HashMap::new(),
|
||||
..(*self.fields).clone()
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl State for MachineState {
|
||||
|
@ -139,6 +139,7 @@ impl NetworkState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner))]
|
||||
pub fn release(self, gsm_inner: &mut GlobalStateManagerInner) {
|
||||
if let NetworkOrigin::Blueprint(generating_blueprint) = self.immutable.origin {
|
||||
let mut blueprint_state = gsm_inner
|
||||
@ -150,6 +151,7 @@ impl NetworkState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_model(&mut self, params: NetworkStateModelParams) {
|
||||
self.fields = Arc::new(NetworkStateFields {
|
||||
model: NetworkStateModel { params },
|
||||
@ -157,6 +159,7 @@ impl NetworkState {
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn clear_ipv4(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -188,6 +191,7 @@ impl NetworkState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn clear_ipv4_gateway(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -232,6 +236,7 @@ impl NetworkState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn set_ipv4(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -262,6 +267,7 @@ impl NetworkState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn set_ipv4_gateway(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -361,6 +367,7 @@ impl NetworkState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn clear_ipv6(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -392,6 +399,7 @@ impl NetworkState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn clear_ipv6_gateway(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -436,6 +444,7 @@ impl NetworkState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn set_ipv6(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -465,6 +474,7 @@ impl NetworkState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn set_ipv6_gateway(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -588,6 +598,7 @@ impl NetworkState {
|
||||
Ok(can_allocate)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn allocate_address_v4(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -617,6 +628,7 @@ impl NetworkState {
|
||||
self.can_allocate_subnet_v4(opt_address, 32)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn allocate_subnet_v4(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -674,6 +686,7 @@ impl NetworkState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub fn release_address_v4(
|
||||
&mut self,
|
||||
addr: Ipv4Addr,
|
||||
@ -681,6 +694,7 @@ impl NetworkState {
|
||||
self.release_subnet_v4(Ipv4Net::new(addr, 32).expect("must succeed"))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub fn release_subnet_v4(
|
||||
&mut self,
|
||||
net: Ipv4Net,
|
||||
@ -696,6 +710,7 @@ impl NetworkState {
|
||||
Ok(opt_tag)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn allocate_address_v6(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -726,6 +741,7 @@ impl NetworkState {
|
||||
self.can_allocate_subnet_v6(opt_address, 128)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn allocate_subnet_v6(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -783,6 +799,7 @@ impl NetworkState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub fn release_address_v6(
|
||||
&mut self,
|
||||
addr: Ipv6Addr,
|
||||
@ -790,6 +807,7 @@ impl NetworkState {
|
||||
self.release_subnet_v6(Ipv6Net::new(addr, 128).expect("must succeed"))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub fn release_subnet_v6(
|
||||
&mut self,
|
||||
net: Ipv6Net,
|
||||
|
@ -30,6 +30,7 @@ impl ProfileState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn next_instance(&mut self) -> Option<config::Instance> {
|
||||
let instance_index = {
|
||||
let instance_index = self.fields.next_instance_index;
|
||||
|
@ -1,82 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ResolveToError {
|
||||
MissingSymbol,
|
||||
AlreadyResolved,
|
||||
}
|
||||
|
||||
pub type ResolveToResult<T> = Result<T, ResolveToError>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ResolvesTo<I>
|
||||
where
|
||||
I: Clone + fmt::Debug,
|
||||
{
|
||||
value: Arc<Mutex<Option<I>>>,
|
||||
}
|
||||
|
||||
impl<I> ResolvesTo<I>
|
||||
where
|
||||
I: Clone + fmt::Debug,
|
||||
{
|
||||
pub fn get(&self) -> Option<I> {
|
||||
self.value.lock().clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ResolveToManager<T, I>
|
||||
where
|
||||
T: PartialEq + Eq + PartialOrd + Ord + fmt::Debug,
|
||||
I: Clone + fmt::Debug,
|
||||
{
|
||||
symbols: BTreeMap<Arc<T>, Arc<Mutex<Option<I>>>>,
|
||||
}
|
||||
|
||||
impl<T, I> ResolveToManager<T, I>
|
||||
where
|
||||
T: PartialEq + Eq + PartialOrd + Ord + fmt::Debug,
|
||||
I: Clone + fmt::Debug,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
symbols: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, symbol: T) -> ResolvesTo<I> {
|
||||
let symbol = Arc::new(symbol);
|
||||
let value = self
|
||||
.symbols
|
||||
.entry(symbol.clone())
|
||||
.or_insert_with(|| Arc::new(Mutex::new(None)))
|
||||
.clone();
|
||||
|
||||
ResolvesTo { value }
|
||||
}
|
||||
|
||||
pub fn resolve(&mut self, symbol: &T, value: I) -> ResolveToResult<()> {
|
||||
match self.symbols.get_mut(symbol) {
|
||||
Some(s) => {
|
||||
let mut inner = s.lock();
|
||||
if inner.is_some() {
|
||||
return Err(ResolveToError::AlreadyResolved);
|
||||
}
|
||||
*inner = Some(value);
|
||||
Ok(())
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ impl<S: State> core::hash::Hash for StateId<S> {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StateRegistry<S: State> {
|
||||
state_id_by_name: imbl::HashMap<Arc<String>, StateIdInternal>,
|
||||
state_id_by_name: imbl::HashMap<String, StateIdInternal>,
|
||||
state_by_id: imbl::HashMap<StateIdInternal, Option<S>>,
|
||||
next_state_id: StateIdInternal,
|
||||
free_state_ids: imbl::Vector<StateIdInternal>,
|
||||
@ -122,9 +122,10 @@ impl<S: State> StateRegistry<S> {
|
||||
return Err(GlobalStateManagerError::DuplicateName(name));
|
||||
}
|
||||
// Register the named state
|
||||
self.state_id_by_name
|
||||
.insert(Arc::new(name), id.0)
|
||||
.expect("should not have a duplicated name here");
|
||||
assert!(
|
||||
self.state_id_by_name.insert(name, id.0).is_none(),
|
||||
"should not have a duplicated name here"
|
||||
);
|
||||
}
|
||||
|
||||
// Attach the state to the state slot
|
||||
@ -171,7 +172,7 @@ impl<S: State> StateRegistry<S> {
|
||||
self.state_by_id.insert(state.id().0, Some(state));
|
||||
}
|
||||
|
||||
pub fn get_state_id_by_name(&self, name: &String) -> Option<StateId<S>> {
|
||||
pub fn get_state_id_by_name(&self, name: &str) -> Option<StateId<S>> {
|
||||
// Get the id associated with this name
|
||||
let id = self.state_id_by_name.get(name)?;
|
||||
Some(StateId::new(*id))
|
||||
|
@ -0,0 +1,185 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum BlueprintAvailability {
|
||||
Existing(NetworkState),
|
||||
Generate(BlueprintState),
|
||||
}
|
||||
|
||||
/// Locations where a machine can be instantiated when a template is generated
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TemplateLocationsList {
|
||||
Networks {
|
||||
networks: WeightedList<NetworkStateId>,
|
||||
},
|
||||
Blueprints {
|
||||
blueprints: WeightedList<BlueprintStateId>,
|
||||
},
|
||||
}
|
||||
|
||||
impl TemplateLocationsList {
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub fn can_pick<F>(
|
||||
&self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
mut network_filter: F,
|
||||
) -> GlobalStateManagerResult<bool>
|
||||
where
|
||||
F: FnMut(&GlobalStateManagerInner, NetworkStateId) -> GlobalStateManagerResult<bool>,
|
||||
{
|
||||
match self {
|
||||
TemplateLocationsList::Networks { networks } => {
|
||||
// Filter the weighted list of networks to those that are still active and or not yet started
|
||||
if networks
|
||||
.try_filter(|id| {
|
||||
let network_state = gsm_inner.network_states().get_state(*id)?;
|
||||
self.is_network_available(gsm_inner, network_state, &mut network_filter)
|
||||
})?
|
||||
.is_none()
|
||||
{
|
||||
return Ok(false);
|
||||
};
|
||||
}
|
||||
TemplateLocationsList::Blueprints { blueprints } => {
|
||||
// Filter the weighted list of blueprints to those that are still active or not yet started and can allocate
|
||||
if blueprints
|
||||
.try_filter(|id| {
|
||||
let blueprint_state = gsm_inner.blueprint_states().get_state(*id)?;
|
||||
|
||||
self.is_blueprint_available(gsm_inner, blueprint_state, &mut network_filter)
|
||||
.map(|x| x.is_some())
|
||||
})?
|
||||
.is_none()
|
||||
{
|
||||
return Ok(false);
|
||||
};
|
||||
}
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub fn pick<F>(
|
||||
&self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
mut network_filter: F,
|
||||
) -> GlobalStateManagerResult<Option<NetworkState>>
|
||||
where
|
||||
F: FnMut(&GlobalStateManagerInner, NetworkStateId) -> GlobalStateManagerResult<bool>,
|
||||
{
|
||||
// Get a network to generate the machine on
|
||||
let network_state = match self {
|
||||
TemplateLocationsList::Networks { networks } => {
|
||||
// Filter the weighted list of networks to those that are still active and or not yet started
|
||||
let Some(available_networks) = networks.try_filter_map(|id| {
|
||||
let network_state = gsm_inner.network_states().get_state(*id)?;
|
||||
if self.is_network_available(
|
||||
gsm_inner,
|
||||
network_state.clone(),
|
||||
&mut network_filter,
|
||||
)? {
|
||||
Ok(Some(network_state))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Weighted choice of network now that we have a candidate list
|
||||
let network_state = gsm_inner.srng().weighted_choice(available_networks);
|
||||
|
||||
// Return network state to use
|
||||
network_state
|
||||
}
|
||||
TemplateLocationsList::Blueprints { blueprints } => {
|
||||
// Filter the weighted list of blueprints to those that are still active or not yet started and can allocate
|
||||
let Some(available_blueprints) = blueprints.try_filter_map(|id| {
|
||||
let blueprint_state = gsm_inner.blueprint_states().get_state(*id)?;
|
||||
|
||||
self.is_blueprint_available(gsm_inner, blueprint_state, &mut network_filter)
|
||||
})?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Weighted choice of blueprint now that we have a candidate list
|
||||
match gsm_inner.srng().weighted_choice(available_blueprints) {
|
||||
BlueprintAvailability::Existing(network_state) => network_state,
|
||||
BlueprintAvailability::Generate(mut blueprint_state) => {
|
||||
// Generate network state from blueprint state
|
||||
let network_state_id = blueprint_state.generate(gsm_inner)?;
|
||||
|
||||
// Update blueprint state
|
||||
gsm_inner.blueprint_states_mut().set_state(blueprint_state);
|
||||
|
||||
// Return network state
|
||||
gsm_inner.network_states().get_state(network_state_id)?
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Some(network_state))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
fn is_network_available<F>(
|
||||
&self,
|
||||
gsm_inner: &GlobalStateManagerInner,
|
||||
network_state: NetworkState,
|
||||
mut network_filter: F,
|
||||
) -> GlobalStateManagerResult<bool>
|
||||
where
|
||||
F: FnMut(&GlobalStateManagerInner, NetworkStateId) -> GlobalStateManagerResult<bool>,
|
||||
{
|
||||
// If the network is not active, it is not available
|
||||
if !network_state.is_active()? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Check the network filter
|
||||
if !network_filter(gsm_inner, network_state.id())? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
fn is_blueprint_available<F>(
|
||||
&self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
blueprint_state: BlueprintState,
|
||||
mut network_filter: F,
|
||||
) -> GlobalStateManagerResult<Option<BlueprintAvailability>>
|
||||
where
|
||||
F: FnMut(&GlobalStateManagerInner, NetworkStateId) -> GlobalStateManagerResult<bool>,
|
||||
{
|
||||
// See if the networks generated from this blueprint so far have availability
|
||||
// in this template
|
||||
if let Some(available_network_state) = blueprint_state.for_each_network_id(|id| {
|
||||
// Check the network's availability
|
||||
let network_state = gsm_inner.network_states().get_state(id)?;
|
||||
if self.is_network_available(gsm_inner, network_state.clone(), &mut network_filter)? {
|
||||
// We found one
|
||||
return Ok(Some(network_state));
|
||||
}
|
||||
// Try next network
|
||||
Ok(None)
|
||||
})? {
|
||||
// We found a usable network
|
||||
return Ok(Some(BlueprintAvailability::Existing(
|
||||
available_network_state,
|
||||
)));
|
||||
}
|
||||
|
||||
// If the blueprint is active, it is available because it can make a new network
|
||||
if blueprint_state.is_active(gsm_inner) {
|
||||
return Ok(Some(BlueprintAvailability::Generate(blueprint_state)));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
@ -47,6 +47,7 @@ impl TemplateState {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_disable_capabilities(&mut self, disable_capabilities: Vec<String>) {
|
||||
let disable_capabilities =
|
||||
imbl::Vector::from_iter(disable_capabilities.into_iter().map(Arc::new));
|
||||
@ -57,6 +58,7 @@ impl TemplateState {
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_networks_list(&mut self, networks: WeightedList<NetworkStateId>) {
|
||||
let locations_list = Some(TemplateLocationsList::Networks { networks });
|
||||
|
||||
@ -67,6 +69,7 @@ impl TemplateState {
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_blueprints_list(&mut self, blueprints: WeightedList<BlueprintStateId>) {
|
||||
let locations_list = Some(TemplateLocationsList::Blueprints { blueprints });
|
||||
|
||||
@ -77,6 +80,7 @@ impl TemplateState {
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn clear_locations_list(&mut self) {
|
||||
let locations_list = None;
|
||||
|
||||
@ -87,6 +91,7 @@ impl TemplateState {
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_limit_machine_count(&mut self, limit_machine_count: Option<usize>) {
|
||||
// Update fields
|
||||
self.fields = Arc::new(TemplateStateFields {
|
||||
@ -95,6 +100,7 @@ impl TemplateState {
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn set_limit_machines_per_network(
|
||||
&mut self,
|
||||
limit_machines_per_network: Option<WeightedList<usize>>,
|
||||
@ -142,6 +148,7 @@ impl TemplateState {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, gsm_inner), err)]
|
||||
pub fn generate(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
@ -237,6 +244,7 @@ impl TemplateState {
|
||||
Ok(machine_state_id)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn on_machine_released(&mut self, machine_state_id: MachineStateId) {
|
||||
let machines = self.fields.machines.without(&machine_state_id);
|
||||
let mut machines_per_network = self.fields.machines_per_network.clone();
|
||||
|
@ -28,7 +28,10 @@ default_pool: "$internet"
|
||||
networks:
|
||||
# Predefined networks
|
||||
$internet:
|
||||
allocation: "$internet"
|
||||
ipv4:
|
||||
allocation: "$internet"
|
||||
ipv6:
|
||||
allocation: "$internet"
|
||||
model: "$internet"
|
||||
|
||||
#################################################################
|
||||
|
Loading…
x
Reference in New Issue
Block a user