Mercurial > lbo > hg > myi3stat
view src/metrics/net.rs @ 22:83f8cbf8cc58
Add net (interface) metric
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 14 Feb 2016 17:54:31 +0100 |
parents | |
children | fc3487a89d63 |
line wrap: on
line source
use framework::*; use helper::commaseparated_to_vec; use helper::get_procfs_file_lines; extern crate regex; use self::regex::Regex; use std::collections::BTreeSet; // Interface name, transmitted bytes, received bytes. Used for both rates and counters! type IFStat = (String, u64, u64); struct NetInterfaceMetric; impl NetInterfaceMetric { /// Obtain current counters from /proc/net/dev fn get_stats(ifs: BTreeSet<String>) -> Vec<IFStat> { let ifstats; let mut processed_stats = Vec::with_capacity(ifs.len()); match get_procfs_file_lines(String::from("net/dev")) { None => ifstats = Vec::new(), Some(st) => ifstats = st, } // RX TX // * * * * // iface |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed // eth0: 1037503524 872642 0 0 0 0 0 10482 40971427 300143 0 1 0 0 0 0 let re = Regex::new(r"^\s*([a-z0-9]+):\s+(\d+)\s+(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)\s+(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+$").unwrap(); for line in ifstats { match re.captures(&line) { None => continue, Some(caps) => { if ifs.contains(caps.at(1).unwrap()) { processed_stats.push(( String::from(caps.at(1).unwrap()), u64::from_str_radix(caps.at(2).unwrap(), 10).unwrap(), u64::from_str_radix(caps.at(4).unwrap(), 10).unwrap() )); } } } } processed_stats } /// Convert a number into a string with nice unit fn make_nice_rate(i: u64) -> String { let units = vec!["", "K", "M", "G"]; let mut u = 0; let mut f = i as f64; loop { if f / 1024. > 1. { f = f / 1024.; u += 1; } else { break; } } assert!(u < 4); format!("{:6.1}{:1}", f, units[u]) } /// Format a series of IFStat tuples fn format_stats(stats: Vec<IFStat>) -> String { stats.into_iter() .fold(String::new(), |mut acc, (i, rx, tx)| { acc.push_str(&format!("{}: rx:{} tx:{} ", i, NetInterfaceMetric::make_nice_rate(rx), NetInterfaceMetric::make_nice_rate(tx))); acc }) } /// Parse a "{rx} {tx}" string into an IFStat tuple fn str_to_ifstat(name: String, s: String) -> IFStat { let re = Regex::new(r"^(\d+) (\d+)$").unwrap(); match re.captures(&s) { None => (name, 0, 0), Some(caps) => { let rx = u64::from_str_radix(caps.at(1).unwrap(), 10).unwrap(); let tx = u64::from_str_radix(caps.at(2).unwrap(), 10).unwrap(); (name, rx, tx) } } } } impl Metric for NetInterfaceMetric { fn init(&self, st: &mut MetricState, initarg: Option<String>) { match initarg { None => (), Some(s) => { let wanted_ifs: BTreeSet<String> = commaseparated_to_vec(s).into_iter().collect(); st.set(String::from("ifs"), State::BTS(wanted_ifs)); } } } fn render(&self, st: &mut MetricState) -> RenderResult { let interval = (MetricState::now() - st.last_called) as u64; let interfaces; match st.get(String::from("ifs")) { State::BTS(ifs) => interfaces = ifs, _ => return RenderResult::new(String::from("n/a"), Color::Red), } // Get current counters let newstats = NetInterfaceMetric::get_stats(interfaces); let mut rates: Vec<IFStat> = Vec::new(); // this is the final output for (ifname, rx, tx) in newstats { // Obtain previous rx/tx counts from state let oldstat; match st.get(ifname.clone()) { State::S(o) => oldstat = NetInterfaceMetric::str_to_ifstat(ifname.clone(), o), _ => oldstat = (ifname.clone(), rx, tx), } // calculate rate over last interval match oldstat { (ifname, oldrx, oldtx) => { rates.push((ifname.clone(), 1000 * (rx - oldrx) / interval, 1000 * (tx - oldtx) / interval)); // Store current counters in state st.set(ifname.clone(), State::S(format!("{} {}", rx, tx))); } } } RenderResult::new(NetInterfaceMetric::format_stats(rates), Color::Green) } } pub fn make_net_metric() -> Box<Metric> { Box::new(NetInterfaceMetric) }