|
1 #!/usr/bin/env python |
|
2 # |
|
3 # $Id: iotop.py 1160 2011-10-14 18:50:36Z g.rodola@gmail.com $ |
|
4 # |
|
5 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. |
|
6 # Use of this source code is governed by a BSD-style license that can be |
|
7 # found in the LICENSE file. |
|
8 |
|
9 """ |
|
10 Shows real-time network statistics. |
|
11 |
|
12 Author: Giampaolo Rodola' <g.rodola@gmail.com> |
|
13 """ |
|
14 |
|
15 import sys |
|
16 import os |
|
17 if os.name != 'posix': |
|
18 sys.exit('platform not supported') |
|
19 import curses |
|
20 import atexit |
|
21 import time |
|
22 |
|
23 import psutil |
|
24 |
|
25 |
|
26 # --- curses stuff |
|
27 def tear_down(): |
|
28 win.keypad(0) |
|
29 curses.nocbreak() |
|
30 curses.echo() |
|
31 curses.endwin() |
|
32 |
|
33 win = curses.initscr() |
|
34 atexit.register(tear_down) |
|
35 curses.endwin() |
|
36 lineno = 0 |
|
37 |
|
38 def print_line(line, highlight=False): |
|
39 """A thin wrapper around curses's addstr().""" |
|
40 global lineno |
|
41 try: |
|
42 if highlight: |
|
43 line += " " * (win.getmaxyx()[1] - len(line)) |
|
44 win.addstr(lineno, 0, line, curses.A_REVERSE) |
|
45 else: |
|
46 win.addstr(lineno, 0, line, 0) |
|
47 except curses.error: |
|
48 lineno = 0 |
|
49 win.refresh() |
|
50 raise |
|
51 else: |
|
52 lineno += 1 |
|
53 # --- curses stuff |
|
54 |
|
55 |
|
56 def bytes2human(n): |
|
57 """ |
|
58 >>> bytes2human(10000) |
|
59 '9.8 K' |
|
60 >>> bytes2human(100001221) |
|
61 '95.4 M' |
|
62 """ |
|
63 symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') |
|
64 prefix = {} |
|
65 for i, s in enumerate(symbols): |
|
66 prefix[s] = 1 << (i+1)*10 |
|
67 for s in reversed(symbols): |
|
68 if n >= prefix[s]: |
|
69 value = float(n) / prefix[s] |
|
70 return '%.2f %s' % (value, s) |
|
71 return '%.2f B' % (n) |
|
72 |
|
73 def poll(interval): |
|
74 """Retrieve raw stats within an interval window.""" |
|
75 tot_before = psutil.net_io_counters() |
|
76 pnic_before = psutil.net_io_counters(pernic=True) |
|
77 # sleep some time |
|
78 time.sleep(interval) |
|
79 tot_after = psutil.net_io_counters() |
|
80 pnic_after = psutil.net_io_counters(pernic=True) |
|
81 return (tot_before, tot_after, pnic_before, pnic_after) |
|
82 |
|
83 |
|
84 def refresh_window(tot_before, tot_after, pnic_before, pnic_after): |
|
85 """Print stats on screen.""" |
|
86 global lineno |
|
87 |
|
88 # totals |
|
89 print_line("total bytes: sent: %-10s received: %s" \ |
|
90 % (bytes2human(tot_after.bytes_sent), |
|
91 bytes2human(tot_after.bytes_recv)) |
|
92 ) |
|
93 print_line("total packets: sent: %-10s received: %s" \ |
|
94 % (tot_after.packets_sent, tot_after.packets_recv) |
|
95 ) |
|
96 |
|
97 |
|
98 # per-network interface details: let's sort network interfaces so |
|
99 # that the ones which generated more traffic are shown first |
|
100 print_line("") |
|
101 nic_names = list(pnic_after.keys()) |
|
102 nic_names.sort(key=lambda x: sum(pnic_after[x]), reverse=True) |
|
103 for name in nic_names: |
|
104 stats_before = pnic_before[name] |
|
105 stats_after = pnic_after[name] |
|
106 templ = "%-15s %15s %15s" |
|
107 print_line(templ % (name, "TOTAL", "PER-SEC"), highlight=True) |
|
108 print_line(templ % ( |
|
109 "bytes-sent", |
|
110 bytes2human(stats_after.bytes_sent), |
|
111 bytes2human(stats_after.bytes_sent - stats_before.bytes_sent) + '/s', |
|
112 )) |
|
113 print_line(templ % ( |
|
114 "bytes-recv", |
|
115 bytes2human(stats_after.bytes_recv), |
|
116 bytes2human(stats_after.bytes_recv - stats_before.bytes_recv) + '/s', |
|
117 )) |
|
118 print_line(templ % ( |
|
119 "pkts-sent", |
|
120 stats_after.packets_sent, |
|
121 stats_after.packets_sent - stats_before.packets_sent, |
|
122 )) |
|
123 print_line(templ % ( |
|
124 "pkts-recv", |
|
125 stats_after.packets_recv, |
|
126 stats_after.packets_recv - stats_before.packets_recv, |
|
127 )) |
|
128 print_line("") |
|
129 win.refresh() |
|
130 lineno = 0 |
|
131 |
|
132 |
|
133 def main(): |
|
134 try: |
|
135 interval = 0 |
|
136 while 1: |
|
137 args = poll(interval) |
|
138 refresh_window(*args) |
|
139 interval = 1 |
|
140 except (KeyboardInterrupt, SystemExit): |
|
141 pass |
|
142 |
|
143 if __name__ == '__main__': |
|
144 main() |