From 4d144941ddbe42521e69377fa670741949d1de8a Mon Sep 17 00:00:00 2001 From: Thomas Perl Date: Mon, 31 May 2010 14:17:40 +0200 Subject: [PATCH] Refactor monitor to use a proper class-based design --- bwmon/model.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bwmon/monitor.py | 40 +++++++++++++++++++++------------ runmonitor.py | 10 ++++++--- 3 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 bwmon/model.py diff --git a/bwmon/model.py b/bwmon/model.py new file mode 100644 index 0000000..2918b9e --- /dev/null +++ b/bwmon/model.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +import time + +class MonitorEntry(object): + def __init__(self, cmdline, inbytes, outbytes, timestamp=None): + if timestamp is None: + timestamp = time.time() + + self.cmdline = cmdline + self.inbytes = int(inbytes) + self.outbytes = int(outbytes) + self.timestamp = timestamp + + def __repr__(self): + return '<%s cmd="%s" in=%d out=%d time=%d>' % (self.__class__.__name__, + self.cmdline, + self.inbytes, + self.outbytes, + int(self.timestamp),) + +class MonitorEntryCollection(object): + TIMEOUT = 60*5 + + def __init__(self): + self._data = [] + self._latest = {} + + def expire(self): + cutoff = time.time() - self.TIMEOUT + self._data = filter(lambda e: e.timestamp >= cutoff, self._data) + + def get_last_bytes(self, cmdline): + (current, previous) = self._latest.get(cmdline, (None, None)) + + if current is not None: + return (current.inbytes, current.outbytes) + + return (0, 0) + + def get_bandwidth(self, cmdline): + (current, previous) = self._latest.get(cmdline, (None, None)) + if current is not None and previous is not None: + d_time = float(current.timestamp - previous.timestamp) + d_in = float(current.inbytes - previous.inbytes) + d_out = float(current.outbytes - previous.outbytes) + return (d_in/d_time, d_out/d_time) + else: + return (0, 0) + + def add(self, entry): + (current, previous) = self._latest.get(entry.cmdline, (None, None)) + + self._latest[entry.cmdline] = (entry, current) + self._data.append(entry) + + def get_traffic(self): + for cmdline in self._latest: + bytes_in, bytes_out = self.get_last_bytes(cmdline) + yield (bytes_in, bytes_out, cmdline) + + def get_usage(self): + for cmdline in self._latest: + bytes_in, bytes_out = self.get_bandwidth(cmdline) + yield (bytes_in, bytes_out, cmdline) + diff --git a/bwmon/monitor.py b/bwmon/monitor.py index 050000f..91aad65 100644 --- a/bwmon/monitor.py +++ b/bwmon/monitor.py @@ -4,6 +4,7 @@ from __future__ import absolute_import from bwmon import proc from bwmon import util +from bwmon import model import collections import time @@ -11,13 +12,16 @@ import sys import copy import re +BANDWIDTH, TRAFFIC = range(2) + class Monitor(object): def __init__(self): self.fd_map = {} + self.sample_time = time.time() self.conntrack = {} self.last_conntrack = {} self.connections = {} - self.tracking = {} + self.entries = model.MonitorEntryCollection() self.include_filter = [] self.exclude_filter = [] @@ -26,12 +30,15 @@ class Monitor(object): self.last_conntrack = copy.deepcopy(self.conntrack) self.conntrack.update(proc.parse_ip_conntrack()) self.connections.update(proc.get_connections()) + self.entries.expire() + self.sample_time = time.time() def set_filter(self, include_filter, exclude_filter): self.include_filter = [re.compile(f) for f in include_filter] if include_filter else [] self.exclude_filter = [re.compile(f) for f in exclude_filter] if exclude_filter else [] def convert(self): + entries = collections.defaultdict(lambda: (0, 0)) for con in self.connections.itervalues(): inode = con.get('inode', None) process = self.fd_map.get(inode, None) @@ -58,29 +65,34 @@ class Monitor(object): else: new_byte[direction] = int(self.conntrack[k]['bytes']) - if process['cmd'] in self.tracking: - old_in, old_out = self.tracking[process['cmd']] - else: - old_in = 0 - old_out = 0 + current_in, current_out = entries[process['cmd']] + new_in, new_out = (new_byte['in'], new_byte['out']) - self.tracking[process['cmd']] = (old_in + new_byte['in'], old_out + new_byte['out']) + entries[process['cmd']] = (current_in + new_in, current_out + new_out) - def output(self): - def compare(a, b): - return cmp(a[1], b[1]) + for key in entries: + new_in, new_out = entries[key] + old_in, old_out = self.entries.get_last_bytes(key) + entry = model.MonitorEntry(key, old_in + new_in, old_out + new_out, self.sample_time) + self.entries.add(entry) + def output(self, mode=TRAFFIC): util.clear() - for cmd, bytes in sorted(self.tracking.iteritems(), cmp=compare): + if mode == TRAFFIC: + entries = sorted(self.entries.get_traffic()) + else: + entries = sorted(self.entries.get_usage()) + + for bytes_in, bytes_out, cmd in entries: if len(cmd) > 60: cmd = cmd[:57] + '...' - print '%10d / %10d -- %s' % (bytes[0], bytes[1], cmd) + print '%10d / %10d -- %s' % (bytes_in, bytes_out, cmd) sys.stdout.flush() - def loop(self): + def loop(self, mode): while True: self.update() self.convert() - self.output() + self.output(mode) time.sleep(1) diff --git a/runmonitor.py b/runmonitor.py index 2be7dc0..314b5f4 100644 --- a/runmonitor.py +++ b/runmonitor.py @@ -15,10 +15,14 @@ if __name__ == '__main__': parser = OptionParser() parser.add_option('--include', dest='include_filter', type='string', action='append', help='include only processes that match the given regex') parser.add_option('--exclude', dest='exclude_filter', type='string', action='append', help='exclude processes that match the given regex') + parser.add_option('--bandwidth', dest='bandwidth', action='store_true', default=False, help='print bandwidth instead of traffic') (options, args) = parser.parse_args() - monitor = monitor.Monitor() - monitor.set_filter(options.include_filter, options.exclude_filter) - monitor.loop() + m = monitor.Monitor() + m.set_filter(options.include_filter, options.exclude_filter) + if options.bandwidth: + m.loop(monitor.BANDWIDTH) + else: + m.loop(monitor.TRAFFIC) -- 2.11.4.GIT