make monitor-mem.sh produce nicer output
[bwmon.git] / bwmon / model.py
blob4e2e7a330fcac11ae02c5b1f74d96fac803e0a39
1 # -*- coding: utf-8 -*-
3 from __future__ import absolute_import
5 import time
7 class MonitorEntry(object):
8 """Entity object for one monitoring "event"
10 This class encapsulates all data that's measured in
11 one single monitoring event.
12 """
13 def __init__(self, cmdline, inbytes, outbytes, timestamp=None):
14 """Creates a new MonitorEntry object
16 If the parameter timestamp is omitted, it will default to
17 "now" (i.e. the measurement has taken place right now).
19 @param cmdline: The command line of the measured process
20 @param inbytes: The current incoming total data, in bytes
21 @param outbytes: The current outgoing total data, in bytes
22 @param timestamp: Unix timestamp (UTC) of the event
23 """
24 if timestamp is None:
25 timestamp = time.time()
27 self.cmdline = cmdline
28 self.inbytes = int(inbytes)
29 self.outbytes = int(outbytes)
30 self.timestamp = timestamp
32 def __repr__(self):
33 """Create a string representation of this entry
35 This is mostl used for debugging, and will simply
36 return all the values in a user-readable string.
38 @return: The string representation of this object
39 """
40 return '<%s cmd="%s" in=%d out=%d time=%d>' % (self.__class__.__name__,
41 self.cmdline,
42 self.inbytes,
43 self.outbytes,
44 int(self.timestamp),)
46 class MonitorEntryCollection(object):
47 """A (linear) collection of multiple MonitorEntry objects
49 This object is used by the monitor and the aggregator to
50 combine several single MonitorEntry objects and provide
51 a unified view on the current state of the objects.
52 """
53 TIMEOUT = 60
55 def __init__(self, update_frequency, get_app=lambda x: x):
56 """Create a new MonitorEntryCollection object
58 The update_frequency should match the update frequency
59 of the "calling object" (either a simple Monitor or the
60 aggregator itself), as it's used for calculations that
61 will be exposed to the calling object.
63 @param update_frequency: Update frequency (in seconds)
64 @param get_app: Callback to determine the app from a command
65 """
66 self._data = []
67 self._latest = {}
68 self.update_frequency = update_frequency
69 self.get_app = get_app
71 def expire(self):
72 """Compact the internal state (expire old entries)
74 Remove all the measurement data that's too old and
75 has already been processed. This will usually be
76 called internally and remove all items older than
77 the "TIMEOUT" value for this class.
78 """
79 cutoff = time.time() - self.TIMEOUT
80 d = filter(lambda e: e.timestamp >= cutoff, self._data)
81 self._data = d
82 for key in self._latest.keys():
83 current, previous = self._latest[key]
84 if current is not None and current.timestamp < cutoff:
85 del self._latest[key]
87 def get_last_bytes(self, cmdline):
88 """Return a (inbytes, outbytes, timestamp) for a command
90 In case no measurement for the given command is
91 available, a (0, 0, 0) three-tuple will be returned.
93 Otherwise, the cumulative traffic data (inbytes, outbytes
94 and a UTC unix timestamp) will be returned, as of the
95 last measurement.
97 @return: A three-tuple or (0, 0, 0)
98 """
99 (current, previous) = self._latest.get(cmdline, (None, None))
101 if current is not None:
102 return (current.inbytes, current.outbytes, current.timestamp)
104 return (0, 0, 0)
106 def get_bandwidth(self, cmdline):
107 """Return a (inbytespersec, outbytespersec, timestamp) for a command
109 The semantics of this command are the same as the
110 get_last_bytes() command, but it returns the CURRENT
111 bandwidth usage (in bytes) instead of the cumulative
112 bytes transferred.
114 When the current bandwidth usage cannot be determined,
115 this function returns (0, 0, 0). This is the case when
116 not enough measurements are available (< 2) or the
117 command is not monitored at all by the installed monitors.
119 @return: A three-tuple or (0, 0, 0)
121 (current, previous) = self._latest.get(cmdline, (None, None))
122 if current is not None and previous is not None:
123 d_time = float(current.timestamp - previous.timestamp)
124 d_in = float(current.inbytes - previous.inbytes)
125 d_out = float(current.outbytes - previous.outbytes)
126 return (d_in/d_time, d_out/d_time, current.timestamp)
127 else:
128 return (0, 0, 0)
130 def add(self, entry):
131 """Add a new entry to this collection
133 This adds a new MonitorEntry to this collection and
134 makes sure that it is picked up from the aggregation
135 commands.
137 @param entry: A MonitorEntry object to be added
139 entry.cmdline = self.get_app(entry.cmdline)
140 (current, previous) = self._latest.get(entry.cmdline, (None, None))
142 # throttle comparison; don't always take the lastest two
143 #prev = current if current and (entry.timestamp - current.timestamp) else previous
144 #self._latest[entry.cmdline] = (entry, prev)
146 self._latest[entry.cmdline] = (entry, current)
147 self._data.append(entry)
149 def get_history(self, cmdline):
150 """Return the bandwidth history for a given command
152 @param cmdline: The command line / app name to be queried
154 x = []
155 last_inbytes = -1
156 for e in self._data:
157 if e.cmdline == cmdline:
158 if last_inbytes == -1:
159 last_inbytes = e.inbytes
160 x.append(e.inbytes-last_inbytes)
161 last_inbytes = e.inbytes
162 return x
164 def get_datapoints(self):
165 """TODO
167 @return: TODO
169 items = [self.get_history(cmdline) for bin, bout, cmdline in self.get_usage()]
170 if not items:
171 return [], []
173 def check_items(l):
174 return max(l) > 20.
175 items = filter(check_items, items)
177 return range(len(min(items, key=len))), items
179 def get_traffic(self):
180 """Get the current view on the traffic
182 This returns a generator object that will yield
183 (bytes_in, bytes_out, cmdline) tuples describing
184 the current traffic on a per-command/-app basis.
186 @return: Generator yielding (bytes_in, bytes_out, cmdline)
188 for cmdline in self._latest:
189 bytes_in, bytes_out, timestamp = self.get_last_bytes(cmdline)
190 yield (bytes_in, bytes_out, cmdline)
192 def get_usage(self):
193 """Get the current view on the bandwidth (usage)
195 This returns a generator object that will yield
196 (bytes_in, bytes_out, cmdline) tuples describing
197 the current bandwidth usage on a per-command/-app
198 basis.
200 @return: Generator yielding (bytes_in, bytes_out, cmdline)
202 cutoff = time.time() - self.update_frequency
203 for cmdline in self._latest:
204 bytes_in, bytes_out, timestamp = self.get_bandwidth(cmdline)
205 if timestamp > cutoff:
206 yield (bytes_in, bytes_out, cmdline)