Include 'back' links - so to/from order is irrelevant.

This commit is contained in:
Herbert Wolverson 2024-04-22 08:58:05 -05:00
parent b93486ee4c
commit cfab0ae924
8 changed files with 93 additions and 6 deletions

View File

@ -14,3 +14,5 @@ tracing-subscriber = "0.3.18"
lqos_config = { path = "../lqos_config" }
uisp = { path = "../uisp" }
thiserror = "1.0.58"
serde = { version = "1.0.198", features = ["derive"] }
csv = "1.3.0"

View File

@ -14,4 +14,6 @@ pub enum UispIntegrationError {
NoRootSite,
#[error("Unknown Site Type")]
UnknownSiteType,
#[error("CSV Error")]
CsvError,
}

View File

@ -0,0 +1,51 @@
use std::collections::HashMap;
use std::path::Path;
use csv::ReaderBuilder;
use tracing::{error, info};
use serde::{Deserialize, Serialize};
use crate::errors::UispIntegrationError;
pub type BandwidthOverrides = HashMap<String, (f32, f32)>;
/// Attempts to load integrationUISPbandwidths.csv to use for
/// bandwidth overrides. Returns an empty set if not found.
pub fn get_site_bandwidth_overrides() -> Result<BandwidthOverrides, UispIntegrationError> {
info!("Looking for integrationUISPbandwidths.csv");
let file_path = Path::new("integrationUISPbandwidths.csv");
if file_path.exists() {
let reader = ReaderBuilder::new()
.comment(Some(b'#'))
.trim(csv::Trim::All)
.from_path(file_path);
if reader.is_err() {
error!("Unable to read integrationUISPbandwidths.csv");
error!("{:?}", reader);
return Err(UispIntegrationError::CsvError);
}
let mut reader = reader.unwrap();
let mut overrides = HashMap::new();
for result in reader.deserialize::<IntegrationBandwidthRow>() {
if let Ok(result) = result {
overrides.insert(
result.parent_node,
(result.download_mbps, result.upload_mbps)
);
}
}
info!("Loaded {} bandwidth overrides", overrides.len());
return Ok(overrides);
}
info!("No bandwidth overrides loaded.");
Ok(HashMap::new())
}
#[derive(Serialize, Deserialize)]
struct IntegrationBandwidthRow {
#[serde(rename = "ParentNode")]
pub parent_node: String,
#[serde(rename = "Download Mbs")]
pub download_mbps: f32,
#[serde(rename = "Upload Mbps")]
pub upload_mbps: f32,
}

View File

@ -6,6 +6,7 @@ mod squash_single_entry_aps;
mod tree_walk;
mod uisp_fetch;
mod utils;
mod bandwidth_overrides;
use crate::errors::UispIntegrationError;
use crate::strategies::full::ap_promotion::promote_access_points;
@ -18,13 +19,18 @@ use crate::strategies::full::uisp_fetch::load_uisp_data;
use crate::strategies::full::utils::{print_sites, warn_of_no_parents};
use crate::uisp_types::UispSite;
use lqos_config::Config;
use crate::strategies::full::bandwidth_overrides::get_site_bandwidth_overrides;
pub use bandwidth_overrides::BandwidthOverrides;
/// Attempt to construct a full hierarchy topology for the UISP network.
pub async fn build_full_network(config: Config) -> Result<(), UispIntegrationError> {
// Load any bandwidth overrides
let bandwidth_overrides = get_site_bandwidth_overrides()?;
// Obtain the UISP data and transform it into easier to work with types
let (sites_raw, devices_raw, data_links_raw) = load_uisp_data(config.clone()).await?;
let (mut sites, data_links, devices) =
parse_uisp_datasets(&sites_raw, &data_links_raw, &devices_raw, &config);
parse_uisp_datasets(&sites_raw, &data_links_raw, &devices_raw, &config, &bandwidth_overrides);
// Check root sites
let root_site = find_root_site(&config, &mut sites, &data_links)?;

View File

@ -2,15 +2,17 @@ use crate::uisp_types::{UispDataLink, UispDevice, UispSite};
use lqos_config::Config;
use tracing::info;
use uisp::{DataLink, Device, Site};
use crate::strategies::full::bandwidth_overrides::BandwidthOverrides;
pub fn parse_uisp_datasets(
sites_raw: &[Site],
data_links_raw: &[DataLink],
devices_raw: &[Device],
config: &Config,
bandwidth_overrides: &BandwidthOverrides,
) -> (Vec<UispSite>, Vec<UispDataLink>, Vec<UispDevice>) {
let (mut sites, data_links, devices) = (
parse_sites(sites_raw, config),
parse_sites(sites_raw, config, bandwidth_overrides),
parse_data_links(data_links_raw, devices_raw),
parse_devices(devices_raw, config),
);
@ -29,10 +31,10 @@ pub fn parse_uisp_datasets(
(sites, data_links, devices)
}
fn parse_sites(sites_raw: &[Site], config: &Config) -> Vec<UispSite> {
fn parse_sites(sites_raw: &[Site], config: &Config, bandwidth_overrides: &BandwidthOverrides) -> Vec<UispSite> {
let mut sites: Vec<UispSite> = sites_raw
.iter()
.map(|s| UispSite::from_uisp(s, &config))
.map(|s| UispSite::from_uisp(s, &config, bandwidth_overrides))
.collect();
info!("{} sites have been successfully parsed", sites.len());
sites

View File

@ -4,6 +4,7 @@ mod full;
use crate::errors::UispIntegrationError;
use lqos_config::Config;
use tracing::{error, info};
pub use full::BandwidthOverrides;
pub async fn build_with_strategy(config: Config) -> Result<(), UispIntegrationError> {
// Select a Strategy

View File

@ -21,7 +21,6 @@ impl UispDevice {
let mut download = config.queues.generated_pn_download_mbps;
let mut upload = config.queues.generated_pn_upload_mbps;
if let Some(overview) = &device.overview {
println!("{:?}", overview);
if let Some(dl) = overview.downlinkCapacity {
download = dl as u32 / 1000000;
}

View File

@ -4,6 +4,7 @@ use lqos_config::Config;
use std::collections::HashSet;
use tracing::warn;
use uisp::{DataLink, Device, Site};
use crate::strategies::BandwidthOverrides;
/// Shortened/flattened version of the UISP Site type.
#[derive(Debug)]
@ -40,7 +41,7 @@ impl Default for UispSite {
}
impl UispSite {
pub fn from_uisp(value: &Site, config: &Config) -> Self {
pub fn from_uisp(value: &Site, config: &Config, bandwidth_overrides: &BandwidthOverrides) -> Self {
let mut uisp_parent_id = None;
if let Some(id) = &value.identification {
@ -74,6 +75,12 @@ impl UispSite {
}
}
if let Some((up,down)) = bandwidth_overrides.get(&value.name_or_blank()) {
// Apply the overrides
max_down_mbps = *down as u32;
max_up_mbps = *up as u32;
}
Self {
id: value.id.clone(),
name: value.name_or_blank(),
@ -125,6 +132,23 @@ impl UispSite {
}
}
}
// The "I'm the TO the device case"
if let Some(to_device) = &dl.to.device {
if to_device.identification.id == potential_ap_id {
if let Some(to_site) = &dl.to.site {
if to_site.identification.id != self.id {
// We have a data link from this device that goes to
// another site.
if let Some(remote_site) =
sites.iter().find(|s| s.id == to_site.identification.id)
{
potential_ap.child_sites.push(remote_site.id.clone());
}
}
}
}
}
}
if !potential_ap.child_sites.is_empty() {
links.push(potential_ap);