diff --git a/python/libpt/__init__.pyi b/python/libpt/__init__.pyi index b10b548..9dc6a5e 100644 --- a/python/libpt/__init__.pyi +++ b/python/libpt/__init__.pyi @@ -4,6 +4,8 @@ `libpt` is originally implemented in rust, but offers a python module too. """ from . import logger +from . import networking +from . import common def is_loaded() -> bool: """ diff --git a/python/libpt/common/__init__.pyi b/python/libpt/common/__init__.pyi new file mode 100644 index 0000000..11472a8 --- /dev/null +++ b/python/libpt/common/__init__.pyi @@ -0,0 +1,7 @@ +""" +common functionalities + +This module implements common functionality useful for many use cases, such as macros, +Formatting functions and more. +""" +from . import printing diff --git a/python/libpt/common/printing.pyi b/python/libpt/common/printing.pyi new file mode 100644 index 0000000..6cecacc --- /dev/null +++ b/python/libpt/common/printing.pyi @@ -0,0 +1,14 @@ +""" +tools that make printing stuff better +""" +def divider() -> str: + """ + Quickly get a one line visual divider + """ + ... + +def print_divider(): + """ + Quickly print a one line visual divider + """ + ... diff --git a/python/libpt/networking/__init__.pyi b/python/libpt/networking/__init__.pyi new file mode 100644 index 0000000..9ac1f3b --- /dev/null +++ b/python/libpt/networking/__init__.pyi @@ -0,0 +1,7 @@ +""" +# various networking tools + +The networking module contains various tools related to connections. For example, it contains a +tool that has the purpose to check if your connection is consistently available. +""" +from . import monitoring diff --git a/python/libpt/networking/monitoring/__init__.pyi b/python/libpt/networking/monitoring/__init__.pyi new file mode 100644 index 0000000..fe0e3e9 --- /dev/null +++ b/python/libpt/networking/monitoring/__init__.pyi @@ -0,0 +1,7 @@ +""" +# monitor your network + +This module offers functions to monitor your network. +""" + +from . import uptime diff --git a/python/libpt/networking/monitoring/uptime.pyi b/python/libpt/networking/monitoring/uptime.pyi new file mode 100644 index 0000000..bedece7 --- /dev/null +++ b/python/libpt/networking/monitoring/uptime.pyi @@ -0,0 +1,84 @@ +""" +# monitor your network uptime + +This method offers a way to monitor your networks/hosts uptime. This is achieved by making +HTTPS requests to a given list of +""" + +class UptimeStatus: + """ + Describes an uptime status + + UptimeStatus describes the result of an uptime check. + """ + """ true if the [`UptimeStatus`] is considered successful""" + success: bool + """ the percentage of reachable urls out of the total urls""" + success_ratio: int + """ the percentage of reachable urls out of the total urls that need to be reachable in order + for this [`UptimeStatus`] to be considered a success. + """ + success_ratio_target: int + """ the number of reachable [`urls`](UptimeStatus::urls) """ + reachable: int + """URL list cant be ported to python, use UptimeStatus.urls()""" + __urls: ... + + def __init__(self, success_ratio_target: int, url_strs: list[str]) -> None: + """ + create a new UptimeStatus and check it + + `success_ratio_target` should never be more than 100 (it represents a success percentage) + """ + ... + + def check(self) -> None: + """ + checks if the stored urls + + Makes the actual https requests and updates fields accordingly. + + This method can block some time, as the web requests are implemented as blocking and + executed by the shared library (not in python) + """ + ... + + def calc_success(self) -> None: + """ + calculate the success based on the `reachable` and `total` + + Calculates the ratio of [reachable]/ + (length of [__urls]). + + Calculates a [`success_ratio`] from that, by multiplying with 100, then flooring. + + If the [`success_ratio`] is greater than or equal to the [`success_ratio_target`], + the [`UptimeStatus`] will be considered a success. + + In the special case that no URLs to check for have been provided, the check will be + considered a success, but the [`success_ratio`] will be `0`. + + Note: does not check for networking, use [`check()`] for that. + """ + ... + + def urls(self) -> list[str]: + """ + get urls for python + + Since [`__urls`] has no python equivalent, return the urls as a `list[str]` in + Python. + """ + ... + +def continuous_uptime_monitor(success_ratio_target: int, urls: list[str], interval: int) -> None: + """ + Uptime monitor + + This function continuously monitors the uptime of your host/network. + + On change of status, an update will be logged at INFO Level, containing + information on your current status, including timestamps of the last up/down time and durations + since. + """ + ... diff --git a/src/bin/main/args.rs b/src/bin/main/args.rs index c109a60..b8f6526 100644 --- a/src/bin/main/args.rs +++ b/src/bin/main/args.rs @@ -37,6 +37,9 @@ static LONG_ABOUT_ROOT: &'static str = r##" //// STATICS /////////////////////////////////////////////////////////////////////////////////////// /// ## Main struct for parsing CLI arguments +/// +/// This struct describes the complete commandline options/arguments that [pt](crate) can take. It +/// makes use of composition to build a complex system of commands, subcommands, flags and options. #[derive(Debug, Clone, Parser)] #[command( author, @@ -49,12 +52,13 @@ r#"libpt: {version}{about-section}Author: {usage-heading} {usage}{all-args}{tab}"# )] pub struct Cli { + // clap_verbosity_flag seems to make this a global option implicitly /// set a verbosity, multiple allowed (f.e. -vvv) #[command(flatten)] pub verbose: Verbosity, /// show logger meta - #[arg(short, long)] + #[arg(short, long, global = true)] pub log_meta: bool, /// choose a subcommand @@ -63,19 +67,20 @@ pub struct Cli { } //// ENUMS ///////////////////////////////////////////////////////////////////////////////////////// -/// # defines the top level commands +/// ## defines the top level commands #[derive(Debug, Clone, Subcommand)] #[non_exhaustive] pub enum Commands { /// networking commands Net { + /// Networking subcommands #[command(subcommand)] command: NetCommands, }, } //////////////////////////////////////////////////////////////////////////////////////////////////// -/// defines the networking commands +/// ## defines the networking commands #[derive(Debug, Clone, Subcommand)] #[non_exhaustive] pub enum NetCommands { diff --git a/src/bin/main/mod.rs b/src/bin/main/mod.rs index 6cfc299..8306d32 100644 --- a/src/bin/main/mod.rs +++ b/src/bin/main/mod.rs @@ -1,6 +1,8 @@ //! # Main executable of pt //! -//! This module contains all code specific to the executable version of [`libpt`]: `pt`. +//! This module contains all code specific to the executable version of [`libpt`]: [`pt`](crate). +//! +//! //// ATTRIBUTES //////////////////////////////////////////////////////////////////////////////////// // we want docs @@ -22,7 +24,7 @@ use env_logger; use clap::Parser; -mod args; +pub mod args; use args::*; //// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// @@ -38,10 +40,8 @@ use args::*; //// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// //// PUBLIC FUNCTIONS ////////////////////////////////////////////////////////////////////////////// - -//// PRIVATE FUNCTIONS ///////////////////////////////////////////////////////////////////////////// -/// ## Main function of the `pt` binary -fn main() { +/// ## Main function of the [`pt`](crate) binary +pub fn main() { let cli = Cli::parse(); if cli.log_meta { // set up our logger to use the given verbosity @@ -68,8 +68,8 @@ fn main() { } //////////////////////////////////////////////////////////////////////////////////////////////////// -/// ## Process `Net` subcommands -fn net(cli: &Cli, command: NetCommands) { +/// ## Process [`Net`](args::NetCommands) subcommands +pub fn net(cli: &Cli, command: NetCommands) { match command { NetCommands::Monitor { repeat, @@ -97,6 +97,8 @@ fn net(cli: &Cli, command: NetCommands) { println!("{}", status); } } - NetCommands::Discover {} => {} + NetCommands::Discover {} => {todo!()} } } + +//// PRIVATE FUNCTIONS ///////////////////////////////////////////////////////////////////////////// diff --git a/src/common/printing.rs b/src/common/printing.rs index 588f3f9..de68728 100644 --- a/src/common/printing.rs +++ b/src/common/printing.rs @@ -12,7 +12,9 @@ //// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// // reimport our macros to this module, so the user does not get confused when importing the macros pub use crate::divider; -pub use crate::println_divider; +pub use crate::print_divider; + +use pyo3::prelude::*; //// TYPES ///////////////////////////////////////////////////////////////////////////////////////// @@ -32,7 +34,7 @@ macro_rules! divider { //////////////////////////////////////////////////////////////////////////////////////////////////// /// Quickly print a one line visual divider #[macro_export] -macro_rules! println_divider { +macro_rules! print_divider { () => {{ println!("{}", divider!()) }}; @@ -45,5 +47,17 @@ macro_rules! println_divider { //// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// //// PUBLIC FUNCTIONS ////////////////////////////////////////////////////////////////////////////// +/// python interface for [`divider`], can also be used with rust +#[pyfunction] +pub fn divider() -> String { + divider!() +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// python interface for [`print_divider`], can also be used with rust +#[pyfunction] +pub fn print_divider() { + print_divider!() +} //// PRIVATE FUNCTIONS ///////////////////////////////////////////////////////////////////////////// diff --git a/src/lib.rs b/src/lib.rs index 98f1954..143f333 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -//! # root module of `libpt` +//! # `libpt` //! //! [`libpt`](crate) contains my personal code. It is compiled as all of the following: //! @@ -42,11 +42,71 @@ pub fn is_loaded() -> bool { //// PRIVATE FUNCTIONS ///////////////////////////////////////////////////////////////////////////// /// ## Python module: logger #[pymodule] -fn py_logger(py: Python, m: &PyModule) -> PyResult<()> { - let logger_module = PyModule::new(py, "logger")?; - logger_module.add_class::()?; +fn py_logger(py: Python, parent: &PyModule) -> PyResult<()> { + let module = PyModule::new(py, "logger")?; + module.add_class::()?; - m.add_submodule(logger_module)?; + parent.add_submodule(module)?; + Ok(()) +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// ## Python module: common +#[pymodule] +fn py_common(py: Python, parent: &PyModule) -> PyResult<()> { + let module = PyModule::new(py, "common")?; + py_common_printing(py, module)?; + + parent.add_submodule(module)?; + Ok(()) +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// ## Python module: common.printing +#[pymodule] +fn py_common_printing(py: Python, parent: &PyModule) -> PyResult<()> { + let module = PyModule::new(py, "printing")?; + module.add_function(wrap_pyfunction!(common::printing::divider, module)?)?; + module.add_function(wrap_pyfunction!(common::printing::print_divider, module)?)?; + + parent.add_submodule(module)?; + Ok(()) +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// ## Python module: networking +#[pymodule] +fn py_networking(py: Python, parent: &PyModule) -> PyResult<()> { + let module = PyModule::new(py, "networking")?; + py_networking_monitoring(py, module)?; + + parent.add_submodule(module)?; + Ok(()) +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// ## Python module: networking.monitoring +#[pymodule] +fn py_networking_monitoring(py: Python, parent: &PyModule) -> PyResult<()> { + let module = PyModule::new(py, "monitoring")?; + py_networking_monitoring_uptime(py, module)?; + + parent.add_submodule(module)?; + Ok(()) +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// ## Python module: networking.monitoring.uptime +#[pymodule] +fn py_networking_monitoring_uptime(py: Python, parent: &PyModule) -> PyResult<()> { + let module = PyModule::new(py, "uptime")?; + module.add_class::()?; + module.add_function(wrap_pyfunction!( + networking::monitoring::uptime::continuous_uptime_monitor, + module + )?)?; + + parent.add_submodule(module)?; Ok(()) } @@ -58,7 +118,9 @@ fn py_logger(py: Python, m: &PyModule) -> PyResult<()> { fn _libpt(py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(is_loaded, m)?)?; - // load logger module + // load sub modules + py_common(py, m)?; py_logger(py, m)?; + py_networking(py, m)?; Ok(()) } diff --git a/src/networking/monitoring/uptime.rs b/src/networking/monitoring/uptime.rs index 9003c1a..19c5560 100644 --- a/src/networking/monitoring/uptime.rs +++ b/src/networking/monitoring/uptime.rs @@ -2,6 +2,8 @@ //! //! This method offers a way to monitor your networks/hosts uptime. This is achieved by making //! HTTPS requests to a given list of +//! +//! Warning: This module is not unit tested. //// ATTRIBUTES //////////////////////////////////////////////////////////////////////////////////// // we want docs @@ -26,6 +28,8 @@ use reqwest::{self, Url}; use humantime::{format_duration, format_rfc3339}; use std::time::SystemTime; +use pyo3::prelude::*; + use crate::divider; //// TYPES ///////////////////////////////////////////////////////////////////////////////////////// @@ -45,6 +49,7 @@ pub const DEFAULT_CHECK_URLS: &'static [&'static str] = /// ## Describes an uptime status /// /// [`UptimeStatus`] describes the result of an uptime check. +#[pyclass] pub struct UptimeStatus { /// true if the [`UptimeStatus`] is considered successful pub success: bool, @@ -60,9 +65,11 @@ pub struct UptimeStatus { } //// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// +/// Main implementation impl UptimeStatus { /// ## create a new `UptimeStatus` and perform it's check - pub fn new(success_ratio_target: u8, urls_str: &Vec) -> Self { + pub fn new(success_ratio_target: u8, url_strs: &Vec) -> Self { + assert!(success_ratio_target <= 100); let mut status = UptimeStatus { success: false, success_ratio: 0, @@ -70,7 +77,7 @@ impl UptimeStatus { reachable: 0, urls: Vec::new(), }; - for s in urls_str { + for s in url_strs { let url = reqwest::Url::from_str(&s); if url.is_ok() { status.urls.push(url.unwrap()); @@ -136,6 +143,45 @@ impl UptimeStatus { trace!("calculated success as: {}", self.success) } } +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// Implementation of the Python interface +#[pymethods] +impl UptimeStatus { + /// calls [`new()`](UptimeStatus::new) with python compatible arguments + #[new] + pub fn py_new(success_ratio_target: u8, url_strs: Vec) -> Self { + Self::new(success_ratio_target, &url_strs) + } + + /// Same as [`check()`](UptimeStatus::check) + #[pyo3(name = "check")] + pub fn py_check(&mut self) { + self.check(); + } + + /// Same as [`calc_success()`](UptimeStatus::calc_success) + #[pyo3(name = "calc_success")] + pub fn py_calc_success(&mut self) { + self.calc_success(); + } + + /// ## get urls for python + /// + /// Since [`UptimeStatus::urls`] has no python equivalent, return the urls as a `list[str]` in + /// Python. + /// + /// Practically, this is also handy for rust implementations that want to access the URLs as + /// [Strings](String). + pub fn urls(&self) -> Vec { + let mut url_strs: Vec = Vec::new(); + + for url in self.urls.clone() { + url_strs.push(url.to_string()); + } + + return url_strs; + } +} //////////////////////////////////////////////////////////////////////////////////////////////////// impl fmt::Debug for UptimeStatus { @@ -181,6 +227,7 @@ impl fmt::Display for UptimeStatus { /// On change of status, an update will be logged at [INFO Level](log::Level::Info), containing /// information on your current status, including timestamps of the last up/down time and durations /// since. +#[pyfunction] pub fn continuous_uptime_monitor(success_ratio_target: u8, urls: Vec, interval: u64) { if urls.len() == 0 { error!("No URLs provided. There is nothing to monitor.");