From 26f89e040ebcedd5b4e5898ea5d3c9bbfb7c9699 Mon Sep 17 00:00:00 2001 From: Joakim Hamren Date: Mon, 10 Mar 2014 02:55:00 +0100 Subject: [PATCH] Improved the net module. --- psdash/net.py | 115 ++++++++++++++++++++++++++++++-------------------- psdash/web.py | 28 +++++++++++- 2 files changed, 97 insertions(+), 46 deletions(-) diff --git a/psdash/net.py b/psdash/net.py index 06814c9..9257a4f 100644 --- a/psdash/net.py +++ b/psdash/net.py @@ -6,83 +6,108 @@ import time import sys -SIOCGIFCONF = 0x8912 - -if sys.maxsize > (1 << 31): - ifreq = struct.Struct("16sH2xI16x") -else: - # TODO fix format for 32 bit - ifreq = struct.Struct("16sH2xI16x") - -ifconf = struct.Struct("iL") - class NetIOCounters(object): - _last_req = None - _last_req_time = None - - @classmethod - def request(cls): - io_counters = psutil.network_io_counters(pernic=True) + def __init__(self, pernic=True): + self.last_req = None + self.last_req_time = None + self.pernic = pernic + + def _get_net_io_counters(self): + """ + Fetch io counters from psutil and transform it to + dicts with the additional attributes defaulted + """ + counters = psutil.network_io_counters(pernic=self.pernic) res = {} - for name, io in io_counters.iteritems(): + for name, io in counters.iteritems(): res[name] = io._asdict() res[name].update({"tx_per_sec": 0, "rx_per_sec": 0}) - if not cls._last_req: - # no request to compare with so let's just return. - cls._last_req = io_counters - cls._last_req_time = time.time() - return res + return res + + def _set_last_request(self, counters): + self.last_req = counters + self.last_req_time = time.time() - elapsed = int(time.time() - cls._last_req_time) - elapsed = max(elapsed, 1) + def get(self): + return self.last_req - for name, io in io_counters.iteritems(): - last_io = cls._last_req.get(name) + def update(self): + counters = self._get_net_io_counters() + + if not self.last_req: + self._set_last_request(counters) + return counters + + time_delta = time.time() - self.last_req_time + if not time_delta: + return counters + + for name, io in counters.iteritems(): + last_io = self.last_req.get(name) if not last_io: continue - res[name].update({ - "rx_per_sec": (io.bytes_recv - last_io.bytes_recv) / elapsed, - "tx_per_sec": (io.bytes_sent - last_io.bytes_sent) / elapsed + counters[name].update({ + "rx_per_sec": (io["bytes_recv"] - last_io["bytes_recv"]) / time_delta, + "tx_per_sec": (io["bytes_sent"] - last_io["bytes_sent"]) / time_delta }) - cls._last_req = io_counters - cls._last_req_time = time.time() + self._set_last_request(counters) - return res + return counters -def get_network_interfaces(max_net_inf=10): - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - bufsize = ifreq.size * max_net_inf +def get_interface_addresses(max_interfaces=10): + """ + Get addresses of available network interfaces. + See netdevice(7) and ioctl(2) for details. + + Returns a list of dicts + """ + + SIOCGIFCONF = 0x8912 + + if sys.maxsize > (1 << 31): + ifreq = struct.Struct("16sH2xI16x") + else: + # TODO fix format for 32 bit + ifreq = struct.Struct("16sH2xI16x") + + # create request param struct + ifconf = struct.Struct("iL") + bufsize = ifreq.size * max_interfaces buf = array.array("B", "\0" * bufsize) ifconf_val = ifconf.pack(bufsize, buf.buffer_info()[0]) + + # make ioctl request + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ifconf_res = fcntl.ioctl(sock.fileno(), SIOCGIFCONF, ifconf_val) - + sock.close() + buflen, _ = ifconf.unpack(ifconf_res) resbuf = buf.tostring() - io_counters = NetIOCounters.request() - - interfaces = [] + addresses = [] for x in xrange(buflen / ifreq.size): + # read the size of the struct from the result buffer + # and unpack it. start = x * ifreq.size stop = start + ifreq.size - name, family, address = ifreq.unpack(resbuf[start:stop]) + + # transform the address to it's string representation ip = socket.inet_ntoa(struct.pack("I", address)) name = name.rstrip("\0") - inf = { + addr = { "name": name, "family": family, "ip": ip } - inf.update(io_counters.get(name, {})) - interfaces.append(inf) - - return interfaces + addresses.append(addr) + + return addresses diff --git a/psdash/web.py b/psdash/web.py index f6acd2c..f26dab8 100755 --- a/psdash/web.py +++ b/psdash/web.py @@ -6,11 +6,14 @@ import socket import os from datetime import datetime +import time +import threading import uuid -from net import get_network_interfaces from log import Logs +from net import NetIOCounters, get_interface_addresses logs = Logs() +net_io_counters = NetIOCounters() def get_disks(all_partitions=False): @@ -37,6 +40,16 @@ def get_users(): return users +def get_network_interfaces(): + io_counters = net_io_counters.get() + addresses = get_interface_addresses() + + for inf in addresses: + inf.update(io_counters.get(inf["name"], {})) + + return addresses + + app = Flask(__name__) app.config.from_envvar("PSDASH_CONFIG", silent=True) # If the secret key is not read from the config just set it to something. @@ -386,11 +399,24 @@ def parse_args(): return parser.parse_args() +def start_background_worker(sleep_time=3): + def work(): + while True: + net_io_counters.update() + time.sleep(sleep_time) + + t = threading.Thread(target=work) + t.daemon = True + t.start() + + def main(): args = parse_args() for log in args.logs: logs.add_available(log) + start_background_worker() + app.run( host=args.bind_host, port=args.port,