Now featuring capacities from UISP, applied and moved around for squashing.

This commit is contained in:
Herbert Wolverson 2024-04-19 13:51:28 -05:00
parent d13a17821a
commit b93486ee4c
7 changed files with 84 additions and 32 deletions

View File

@ -1,4 +1,5 @@
use crate::uisp_types::{UispSite, UispSiteType};
use crate::uisp_types::{UispDevice, UispSite, UispSiteType};
use lqos_config::Config;
use std::collections::HashSet;
use tracing::info;
use uisp::{DataLink, Device, Site};
@ -8,6 +9,8 @@ pub fn promote_access_points(
devices_raw: &[Device],
data_links_raw: &[DataLink],
sites_raw: &[Site],
devices: &[UispDevice],
config: &Config,
) {
let mut all_links = Vec::new();
sites.iter().for_each(|s| {
@ -28,14 +31,22 @@ pub fn promote_access_points(
sites[parent_site_id].name
);
}*/
let mut max_up_mbps = config.queues.generated_pn_upload_mbps;
let mut max_down_mbps = config.queues.generated_pn_download_mbps;
if let Some(ap) = devices.iter().find(|d| d.id == link.device_id) {
max_up_mbps = ap.upload;
max_down_mbps = ap.download;
}
let mut new_site = UispSite {
id: link.device_id,
name: link.device_name,
site_type: UispSiteType::AccessPoint,
uisp_parent_id: None,
parent_indices: HashSet::new(),
max_up_mbps: 0, // TODO: I need to read this from the device capacity
max_down_mbps: 0,
max_up_mbps,
max_down_mbps,
..Default::default()
};
new_site.parent_indices.insert(parent_site_id);

View File

@ -46,7 +46,9 @@ pub fn promote_clients_with_children(
suspended: sites[child_site].suspended,
..Default::default()
};
new_site.device_indices.extend_from_slice(&sites[child_site].device_indices);
new_site
.device_indices
.extend_from_slice(&sites[child_site].device_indices);
sites[child_site].device_indices.clear();
sites.push(new_site);
}

View File

@ -2,28 +2,28 @@ mod ap_promotion;
mod client_site_promotion;
mod parse;
mod root_site;
mod uisp_fetch;
mod utils;
mod squash_single_entry_aps;
mod tree_walk;
mod uisp_fetch;
mod utils;
use crate::errors::UispIntegrationError;
use crate::strategies::full::ap_promotion::promote_access_points;
use crate::strategies::full::client_site_promotion::promote_clients_with_children;
use crate::strategies::full::parse::parse_uisp_datasets;
use crate::strategies::full::root_site::{find_root_site, set_root_site};
use crate::strategies::full::uisp_fetch::load_uisp_data;
use crate::strategies::full::utils::{print_sites, warn_of_no_parents};
use lqos_config::Config;
use crate::strategies::full::squash_single_entry_aps::squash_single_aps;
use crate::strategies::full::tree_walk::walk_tree_for_routing;
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;
/// Attempt to construct a full hierarchy topology for the UISP network.
pub async fn build_full_network(config: Config) -> Result<(), UispIntegrationError> {
// 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) =
let (mut sites, data_links, devices) =
parse_uisp_datasets(&sites_raw, &data_links_raw, &devices_raw, &config);
// Check root sites
@ -33,7 +33,14 @@ pub async fn build_full_network(config: Config) -> Result<(), UispIntegrationErr
set_root_site(&mut sites, &root_site)?;
// Search for devices that provide links elsewhere
promote_access_points(&mut sites, &devices_raw, &data_links_raw, &sites_raw);
promote_access_points(
&mut sites,
&devices_raw,
&data_links_raw,
&sites_raw,
&devices,
&config,
);
// Sites that are clients but have children should be promoted
promote_clients_with_children(&mut sites)?;
@ -55,15 +62,20 @@ pub async fn build_full_network(config: Config) -> Result<(), UispIntegrationErr
Ok(())
}
fn walk_node(idx: usize, weight: u32, sites: &mut Vec<UispSite>, visited: &mut std::collections::HashSet<usize>) {
fn walk_node(
idx: usize,
weight: u32,
sites: &mut Vec<UispSite>,
visited: &mut std::collections::HashSet<usize>,
) {
if visited.contains(&idx) {
return;
}
visited.insert(idx);
for i in 0 .. sites.len() {
for i in 0..sites.len() {
if sites[i].parent_indices.contains(&idx) {
sites[i].route_weights.push((idx, weight));
walk_node(i, weight+10, sites, visited);
walk_node(i, weight + 10, sites, visited);
}
}
}
}

View File

@ -12,16 +12,18 @@ pub fn parse_uisp_datasets(
let (mut sites, data_links, devices) = (
parse_sites(sites_raw, config),
parse_data_links(data_links_raw, devices_raw),
parse_devices(devices_raw),
parse_devices(devices_raw, config),
);
// Assign devices to sites
for site in sites.iter_mut() {
devices.iter().enumerate().filter(|(_, device)|
device.site_id == site.id
).for_each(|(idx, _)| {
site.device_indices.push(idx);
});
devices
.iter()
.enumerate()
.filter(|(_, device)| device.site_id == site.id)
.for_each(|(idx, _)| {
site.device_indices.push(idx);
});
}
(sites, data_links, devices)
@ -49,10 +51,10 @@ fn parse_data_links(data_links_raw: &[DataLink], devices_raw: &[Device]) -> Vec<
data_links
}
fn parse_devices(devices_raw: &[Device]) -> Vec<UispDevice> {
fn parse_devices(devices_raw: &[Device], config: &Config) -> Vec<UispDevice> {
let mut devices: Vec<UispDevice> = devices_raw
.iter()
.map(|d| UispDevice::from_uisp(d))
.map(|d| UispDevice::from_uisp(d, config))
.collect();
info!("{} devices have been sucessfully parsed", devices.len());
devices

View File

@ -5,7 +5,10 @@ pub fn squash_single_aps(sites: &mut Vec<UispSite>) -> Result<(), UispIntegratio
let mut squashable = Vec::new();
for (idx, site) in sites.iter().enumerate() {
if site.site_type == UispSiteType::AccessPoint {
let target_count = sites.iter().filter(|s| s.parent_indices.contains(&idx)).count();
let target_count = sites
.iter()
.filter(|s| s.parent_indices.contains(&idx))
.count();
if target_count == 1 && site.parent_indices.len() == 1 {
//tracing::info!("Site {} has only one child and is therefore eligible for squashing.", site.name);
squashable.push(idx);
@ -15,15 +18,19 @@ pub fn squash_single_aps(sites: &mut Vec<UispSite>) -> Result<(), UispIntegratio
for squash_idx in squashable {
sites[squash_idx].site_type = UispSiteType::SquashDeleted;
sites[squash_idx].name += " (SQUASHED)";
let up = sites[squash_idx].max_up_mbps;
let down = sites[squash_idx].max_down_mbps;
let new_parent = *sites[squash_idx].parent_indices.iter().nth(0).unwrap();
sites.iter_mut().for_each(|s| {
if s.parent_indices.contains(&squash_idx) {
s.parent_indices.remove(&squash_idx);
s.parent_indices.insert(new_parent);
s.max_up_mbps = up;
s.max_down_mbps = down;
}
});
sites[squash_idx].parent_indices.clear();
}
Ok(())
}
}

View File

@ -2,7 +2,10 @@ use crate::errors::UispIntegrationError;
use crate::strategies::full::walk_node;
use crate::uisp_types::{UispSite, UispSiteType};
pub fn walk_tree_for_routing(sites: &mut Vec<UispSite>, root_site: &str) -> Result<(), UispIntegrationError> {
pub fn walk_tree_for_routing(
sites: &mut Vec<UispSite>,
root_site: &str,
) -> Result<(), UispIntegrationError> {
if let Some(root_idx) = sites.iter().position(|s| s.name == root_site) {
let mut visited = std::collections::HashSet::new();
let mut current_node = root_idx;
@ -16,12 +19,10 @@ pub fn walk_tree_for_routing(sites: &mut Vec<UispSite>, root_site: &str) -> Resu
for site in sites.iter_mut() {
if site.site_type != UispSiteType::Root && !site.route_weights.is_empty() {
// Sort to find the lowest exit
site.route_weights.sort_by(|a,b| {
a.1.cmp(&b.1)
});
site.route_weights.sort_by(|a, b| a.1.cmp(&b.1));
site.selected_parent = Some(site.route_weights[0].0);
}
}
Ok(())
}
}

View File

@ -1,3 +1,4 @@
use lqos_config::Config;
use uisp::Device;
/// Trimmed UISP device for easy use
@ -5,20 +6,36 @@ pub struct UispDevice {
pub id: String,
pub mac: String,
pub site_id: String,
pub download: u32,
pub upload: u32,
}
impl UispDevice {
pub fn from_uisp(device: &Device) -> Self {
pub fn from_uisp(device: &Device, config: &Config) -> Self {
let mac = if let Some(id) = &device.identification.mac {
id.clone()
} else {
"".to_string()
};
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;
}
if let Some(ul) = overview.uplinkCapacity {
upload = ul as u32 / 1000000;
}
}
Self {
id: device.get_id(),
mac,
site_id: device.get_site_id().unwrap_or("".to_string())
site_id: device.get_site_id().unwrap_or("".to_string()),
upload,
download,
}
}
}