Add documentation and cleanup

This commit is contained in:
Herbert Wolverson 2024-04-30 13:31:22 -05:00
parent 3696c14fc9
commit 1952498ce2
23 changed files with 197 additions and 0 deletions

View File

@ -1,5 +1,6 @@
use thiserror::Error;
/// Error types for UISP Integration
#[derive(Error, Debug, PartialEq)]
pub enum UispIntegrationError {
#[error("Unable to load configuration")]

View File

@ -5,12 +5,16 @@ use lqos_config::Config;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use tracing::info;
/// Represents a set of IP ranges that are allowed or ignored.
pub struct IpRanges {
/// The allowed IP ranges
allowed: IpNetworkTable<bool>,
/// The ignored IP ranges
ignored: IpNetworkTable<bool>,
}
impl IpRanges {
/// Creates a new IpRanges from a configuration.
pub fn new(config: &Config) -> Result<Self, UispIntegrationError> {
info!("Building allowed/excluded IP range lookups from configuration file");
@ -54,6 +58,7 @@ impl IpRanges {
Ok(Self { allowed, ignored })
}
/// Checks if an IP address is permitted.
pub fn is_permitted(&self, ip: IpAddr) -> bool {
if let Some(_allow) = self.allowed.longest_match(ip) {
if let Some(_deny) = self.ignored.longest_match(ip) {

View File

@ -2,6 +2,8 @@
//! be ported back to Python, with Rust support structures - but I'll iterate
//! faster in Rust.
#[warn(missing_docs)]
mod errors;
pub mod ip_ranges;
mod strategies;

View File

@ -7,6 +7,7 @@ use std::fs;
use std::path::Path;
use tracing::{error, info};
/// Represents a shaped device in the ShapedDevices.csv file.
#[derive(Serialize, Debug)]
struct ShapedDevice {
pub circuit_id: String,
@ -24,6 +25,11 @@ struct ShapedDevice {
pub comment: String,
}
/// Builds a flat network for UISP
///
/// # Arguments
/// * `config` - The configuration
/// * `ip_ranges` - The IP ranges to use for the network
pub async fn build_flat_network(
config: Config,
ip_ranges: IpRanges,

View File

@ -4,6 +4,16 @@ use std::collections::HashSet;
use tracing::info;
use uisp::{DataLink, Device, Site};
/// Finds access points that are connected to other sites and promotes them to their own site.
/// This is useful for sites that have multiple APs, but are currently represented as a single site.
///
/// # Arguments
/// * `sites` - The list of sites to modify
/// * `devices_raw` - The list of devices
/// * `data_links_raw` - The list of data links
/// * `sites_raw` - The list of sites
/// * `devices` - The list of devices with their speeds
/// * `config` - The configuration
pub fn promote_access_points(
sites: &mut Vec<UispSite>,
devices_raw: &[Device],

View File

@ -10,6 +10,26 @@ pub type BandwidthOverrides = HashMap<String, (f32, f32)>;
/// Attempts to load integrationUISPbandwidths.csv to use for
/// bandwidth overrides. Returns an empty set if not found.
/// Returns an error if the file is found but cannot be read.
///
/// The file should be a CSV with the following columns:
///
/// | Parent Node | Down | Up |
/// |-------------|------|----|
/// | Site1 | 100 | 10 |
/// | Site2 | 200 | 20 |
///
/// The Parent Node should match the name of the site in UISP.
/// The Down and Up columns should be the desired bandwidth in Mbps.
///
/// If the file is found, the overrides will be applied to the sites
/// in the `UispSite` array by the `apply_bandwidth_overrides` function.
///
/// # Arguments
/// * `config` - The configuration
///
/// # Returns
/// * A `BandwidthOverrides` map of site names to bandwidth overrides
pub fn get_site_bandwidth_overrides(
config: &Config,
) -> Result<BandwidthOverrides, UispIntegrationError> {
@ -69,6 +89,11 @@ fn numeric_string_to_f32(text: &str) -> Option<f32> {
}
}
/// Applies the bandwidth overrides to the sites in the array.
///
/// # Arguments
/// * `sites` - The list of sites to modify
/// * `bandwidth_overrides` - The bandwidth overrides to apply
pub fn apply_bandwidth_overrides(sites: &mut [UispSite], bandwidth_overrides: &BandwidthOverrides) {
for site in sites.iter_mut() {
if let Some((up, down)) = bandwidth_overrides.get(&site.name) {
@ -82,3 +107,26 @@ pub fn apply_bandwidth_overrides(sites: &mut [UispSite], bandwidth_overrides: &B
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_numeric_string_to_f32_valid_float() {
let result = numeric_string_to_f32("3.14");
assert_eq!(result, Some(3.14));
}
#[test]
fn test_numeric_string_to_f32_valid_integer() {
let result = numeric_string_to_f32("42");
assert_eq!(result, Some(42.0));
}
#[test]
fn test_numeric_string_to_f32_invalid_string() {
let result = numeric_string_to_f32("abc");
assert_eq!(result, None);
}
}

View File

@ -3,6 +3,15 @@ use crate::uisp_types::{UispSite, UispSiteType};
use std::collections::HashSet;
use tracing::info;
/// Promotes client sites with multiple child sites to a new site type.
/// This is useful for sites that have multiple child sites, but are currently represented as a single site.
///
/// # Arguments
/// * `sites` - The list of sites to modify
///
/// # Returns
/// * An `Ok` if the operation was successful
/// * An `Err` if the operation failed
pub fn promote_clients_with_children(
sites: &mut Vec<UispSite>,
) -> Result<(), UispIntegrationError> {

View File

@ -33,6 +33,16 @@ use crate::uisp_types::{UispSite, UispSiteType};
use lqos_config::Config;
/// Attempt to construct a full hierarchy topology for the UISP network.
/// This function will load the UISP data, parse it into a more usable format,
/// and then attempt to build a full network topology.
///
/// # Arguments
/// * `config` - The configuration
/// * `ip_ranges` - The IP ranges to use for the network
///
/// # Returns
/// * An `Ok` if the operation was successful
/// * An `Err` if the operation failed
pub async fn build_full_network(
config: Config,
ip_ranges: IpRanges,

View File

@ -5,6 +5,16 @@ use std::fs::write;
use std::path::Path;
use tracing::{error, info};
/// Writes the network.json file for UISP
///
/// # Arguments
/// * `config` - The configuration
/// * `sites` - The list of sites
/// * `root_idx` - The index of the root site
///
/// # Returns
/// * An `Ok` if the operation was successful
/// * An `Err` if the operation failed
pub fn write_network_file(
config: &Config,
sites: &[UispSite],

View File

@ -4,6 +4,17 @@ use lqos_config::Config;
use tracing::info;
use uisp::{DataLink, Device, Site};
/// Parses the UISP datasets into a more usable format.
///
/// # Arguments
/// * `sites_raw` - The raw site data
/// * `data_links_raw` - The raw data link data
/// * `devices_raw` - The raw device data
/// * `config` - The configuration
/// * `ip_ranges` - The IP ranges to use for the network
///
/// # Returns
/// * A tuple containing the parsed sites, data links, and devices
pub fn parse_uisp_datasets(
sites_raw: &[Site],
data_links_raw: &[DataLink],

View File

@ -65,6 +65,12 @@ fn handle_multiple_internet_connected_sites(
Ok(root_site_name)
}
/// Sets the root site in the site list.
/// If there are multiple root sites, it will return an error.
///
/// # Arguments
/// * `sites` - The list of sites
/// * `root_site` - The name of the root site
pub fn set_root_site(sites: &mut [UispSite], root_site: &str) -> Result<(), UispIntegrationError> {
if let Some(root) = sites.iter_mut().find(|s| s.name == root_site) {
root.site_type = UispSiteType::Root;

View File

@ -5,13 +5,39 @@ use serde::Deserialize;
use std::path::Path;
use tracing::{error, info};
/// Represents a route override in the integrationUISProutes.csv file.
#[derive(Deserialize, Debug)]
pub struct RouteOverride {
/// The site to override the route from.
pub from_site: String,
/// The site to override the route to.
pub to_site: String,
/// The cost of the route.
pub cost: u32,
}
/// Attempts to load integrationUISProutes.csv to use for
/// route overrides. Returns an empty set if not found.
/// Returns an error if the file is found but cannot be read.
///
/// The file should be a CSV with the following columns:
///
/// | From Site | To Site | Cost |
/// |-----------|---------|------|
/// | Site1 | Site2 | 100 |
/// | Site2 | Site3 | 200 |
///
/// The From Site and To Site should match the name of the site in UISP.
///
/// If the file is found, the overrides will be applied to the routes
/// in the `UispSite` array by the `apply_route_overrides` function.
///
/// # Arguments
/// * `config` - The configuration
///
/// # Returns
/// * An `Ok(Vec)` of `RouteOverride` objects
/// * An `Err` if the file is found but cannot be read
pub fn get_route_overrides(config: &Config) -> Result<Vec<RouteOverride>, UispIntegrationError> {
let file_path = Path::new(&config.lqos_directory).join("integrationUISProutes.csv");
if file_path.exists() {

View File

@ -5,6 +5,7 @@ use serde::Serialize;
use std::path::Path;
use tracing::{error, info};
/// Represents a shaped device in the ShapedDevices.csv file.
#[derive(Serialize, Debug)]
struct ShapedDevice {
pub circuit_id: String,
@ -22,6 +23,13 @@ struct ShapedDevice {
pub comment: String,
}
/// Writes the ShapedDevices.csv file for UISP
///
/// # Arguments
/// * `config` - The configuration
/// * `sites` - The list of sites
/// * `root_idx` - The index of the root site
/// * `devices` - The list of devices
pub fn write_shaped_devices(
config: &Config,
sites: &[UispSite],

View File

@ -1,6 +1,15 @@
use crate::errors::UispIntegrationError;
use crate::uisp_types::{UispSite, UispSiteType};
/// Squashes single entry access points
///
/// This function will squash access points that have only one child site.
///
/// # Arguments
/// * `sites` - The list of sites to modify
///
/// # Returns
/// * An `Ok` if the operation was successful
pub fn squash_single_aps(sites: &mut [UispSite]) -> Result<(), UispIntegrationError> {
let mut squashable = Vec::new();
for (idx, site) in sites.iter().enumerate() {

View File

@ -2,6 +2,14 @@ use crate::errors::UispIntegrationError;
use crate::strategies::full::routes_override::RouteOverride;
use crate::uisp_types::{UispSite, UispSiteType};
/// Walks the tree to determine the best route for each site
///
/// This function will walk the tree to determine the best route for each site.
///
/// # Arguments
/// * `sites` - The list of sites
/// * `root_site` - The name of the root site
/// * `overrides` - The list of route overrides
pub fn walk_tree_for_routing(
sites: &mut Vec<UispSite>,
root_site: &str,

View File

@ -46,6 +46,14 @@ fn iterate_child_sites(sites: &[UispSite], parent: usize, indent: usize) {
});
}
/// Warns if there are any sites with no parents, and promotes them to be parented off of the root
/// site.
///
/// # Arguments
/// * `sites` - The list of sites
/// * `devices_raw` - The raw device data
/// * `root_idx` - The index of the root site
/// * `config` - The configuration
pub fn warn_of_no_parents_and_promote(
sites: &mut Vec<UispSite>,
devices_raw: &[Device],

View File

@ -1,6 +1,12 @@
use crate::uisp_types::UispSite;
use lqos_config::Config;
/// Corrects zero capacity sites by setting their capacity to the parent's capacity.
/// If the site has no parent, the capacity is set to the default generated capacity.
///
/// # Arguments
/// * `sites` - The list of sites to correct
/// * `config` - The configuration
pub fn correct_zero_capacity_sites(sites: &mut [UispSite], config: &Config) {
for i in 0..sites.len() {
if sites[i].max_down_mbps == 0 {

View File

@ -6,6 +6,7 @@ use crate::ip_ranges::IpRanges;
use lqos_config::Config;
use tracing::{error, info};
/// Builds the network using the selected strategy.
pub async fn build_with_strategy(
config: Config,
ip_ranges: IpRanges,

View File

@ -1,3 +1,4 @@
/// Detected Access Point
#[derive(Debug)]
pub struct DetectedAccessPoint {
pub site_id: String,

View File

@ -11,6 +11,10 @@ pub struct UispDataLink {
}
impl UispDataLink {
/// Converts a UISP DataLink into a UispDataLink.
///
/// # Arguments
/// * `value` - The UISP DataLink to convert
pub fn from_uisp(value: &DataLink) -> Option<Self> {
let mut from_site_id = String::new();
let mut to_site_id = String::new();

View File

@ -17,6 +17,12 @@ pub struct UispDevice {
}
impl UispDevice {
/// Creates a new UispDevice from a UISP device
///
/// # Arguments
/// * `device` - The device to convert
/// * `config` - The configuration
/// * `ip_ranges` - The IP ranges to use for the network
pub fn from_uisp(device: &Device, config: &Config, ip_ranges: &IpRanges) -> Self {
let mut ipv4 = HashSet::new();
let mut ipv6 = HashSet::new();

View File

@ -40,6 +40,7 @@ impl Default for UispSite {
}
impl UispSite {
/// Converts a UISP Site into a UispSite.
pub fn from_uisp(value: &Site, config: &Config) -> Self {
let mut uisp_parent_id = None;

View File

@ -28,6 +28,7 @@ impl Display for UispSiteType {
}
impl UispSiteType {
/// Converts a UISP site record into a UispSiteType
pub fn from_uisp_record(site: &Site) -> Result<Self, UispIntegrationError> {
if let Some(id) = &site.identification {
if let Some(t) = &id.site_type {