curses_display.py 9.68 KB
Newer Older
Mario Hock's avatar
Mario Hock committed
1
2
# -*- coding:utf-8 -*-

Mario Hock's avatar
Mario Hock committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Copyright (c) 2014,
# Karlsruhe Institute of Technology, Institute of Telematics
#
# This code is provided under the BSD 2-Clause License.
# Please refer to the LICENSE.txt file for further information.
#
# Author: Mario Hock
#
#
# Acknowledgment:
#
# This code is inspired from and contains some code primitives found in »bwtop«
# written by "Mahmoud Adel <mahmoud.adel2@gmail.com>" and distributed under
# the MIT license.
#
# For more information about »bwtop« please refer to its project site:
# https://github.com/mahmoudadel2/bwtop


Mario Hock's avatar
Mario Hock committed
22
23
24
25
26
27
'''
Curses display for »cpunetlog«.

'''
import curses
import time
Mario Hock's avatar
Mario Hock committed
28
import helpers
Mario Hock's avatar
Mario Hock committed
29
30
31
32
33
34
35
36

## XXX disable colors
disablecolorskipped = True

stdscr = None

## some "constants"/preferences
nics = None
Mario Hock's avatar
Mario Hock committed
37
nic_speeds = None
Mario Hock's avatar
Mario Hock committed
38
divisor = 1000000.0
Mario Hock's avatar
Mario Hock committed
39
rounding_digits = 2
Mario Hock's avatar
Mario Hock committed
40
unit = "MBits"
Mario Hock's avatar
Mario Hock committed
41

42
LOGGING_STATE_COLORS = {"Active": 3, "(Active)": 2, "Disabled": 1, "Standby": 4, "Enabled": 3}
Mario Hock's avatar
Mario Hock committed
43

44
45
46
47
48
49
50
51
52
53
CPU_TYPE_LABELS = { "user": "user: ",
                    "nice": "nice: ",
                    "system": "system: ",
                    "idle": "idle: ",
                    "iowait": "iowait: ",
                    "irq": "irq: ",
                    "softirq": "sftirq: ",
                    "steal": "steal: ",
                    "guest": "guest: ",
                    "guest_nice": "g_nice: " }
Mario Hock's avatar
Mario Hock committed
54
55
56
57

## Reference to the logging manager, to display its state.
logging_manager = None

Mario Hock's avatar
Mario Hock committed
58
## GUI, positions of fields
Mario Hock's avatar
Mario Hock committed
59
60
61
LABEL_Sent = 18
LABEL_Received = 48
LABEL_CPU_UTIL = LABEL_Sent
62
63
LABEL_CPU_1 = LABEL_Received-1
LABEL_CPU_2 = 63
Mario Hock's avatar
Mario Hock committed
64

65
66
COMMENT_WIDTH = 66

Mario Hock's avatar
Mario Hock committed
67

Mario Hock's avatar
..    
Mario Hock committed
68
## TODO ideas..
Mario Hock's avatar
Mario Hock committed
69
70
71
72
#   - Add an option to set a fixed max. net-speed manually (for comparison)
#       --> Maybe change colors, when speed higher than this max
#           (and switch to a scale with the real max. value for the nic.)
#
Mario Hock's avatar
..    
Mario Hock committed
73
#   - total (GB transferred)
Mario Hock's avatar
..    
Mario Hock committed
74
75
#   - "Total" field for CPU usage (as well)
#   - Also draw a bar for the totals?
Mario Hock's avatar
..    
Mario Hock committed
76
77
78

## TODO idea: smoothing..?

Mario Hock's avatar
Mario Hock committed
79

Mario Hock's avatar
Mario Hock committed
80

Mario Hock's avatar
Mario Hock committed
81
82
83
84
def _format_net_speed(speed):
    return str( round(speed / divisor, rounding_digits) )


Mario Hock's avatar
..    
Mario Hock committed
85
86
87
88
89
90
def _calculate_net_ratio( cur_speed, max_speed ):
    ratio = cur_speed / max_speed

    ratio = min( ratio, 1.0 )
    ratio = max( ratio, 0.0 )

Mario Hock's avatar
..    
Mario Hock committed
91
    return ratio
Mario Hock's avatar
..    
Mario Hock committed
92
93


Mario Hock's avatar
Mario Hock committed
94
95
96
97
def _display_cpu_bar(y, x, cpu):
    # Constants
    CPU_BAR_COLORS = ( curses.color_pair(3) | curses.A_REVERSE,    # user
                       curses.color_pair(4) | curses.A_REVERSE,    # system
98
                       curses.color_pair(5) | curses.A_REVERSE,    # irq / softirq
Mario Hock's avatar
Mario Hock committed
99
                       curses.color_pair(6) | curses.A_REVERSE,    # other
Mario Hock's avatar
Mario Hock committed
100
101
102
103
                       curses.color_pair(3) )                      # idle

    # Calculate proportions
    cpu_util = 100-cpu.idle
104
105
    other = 100 - sum( (cpu.user, cpu.system, cpu.irq, cpu.idle) )
    proportions = [cpu.user, cpu.system, cpu.irq, other, cpu.idle]
Mario Hock's avatar
Mario Hock committed
106
107
108
109
110
111
112
113
114
115
116
117

    # Prepare text.
    text = '{0:.2%}'.format((cpu_util)/100.0)
    split_text = helpers.split_proprtionally(text, proportions, 20)

    # Write text on screen (curses).
    stdscr.move(y,x)
    for s, options in zip(split_text, CPU_BAR_COLORS):
        stdscr.addstr(s, options)



Mario Hock's avatar
Mario Hock committed
118
119
120
121
122
123
124
125
126
127
def _display_logging_state(y, x):
    if ( not logging_manager ):
        stdscr.addstr(y, x, 'Disabled', curses.A_BOLD)

    else:
        state = logging_manager.get_logging_state()
        color = LOGGING_STATE_COLORS[state]

        stdscr.addstr(y, x, state, curses.A_BOLD | curses.color_pair(color))

Mario Hock's avatar
Mario Hock committed
128
129
130

def init():
    global stdscr
Mario Hock's avatar
Mario Hock committed
131
132
133
134
    global nic_speeds

    if not nic_speeds:
        nic_speeds = helpers.get_nic_speeds()
Mario Hock's avatar
Mario Hock committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

    stdscr = curses.initscr()
    curses.noecho()
    curses.cbreak()
    stdscr.keypad(True)
    curses.curs_set(False)
    stdscr.nodelay(True)

    curses.start_color()
    curses.use_default_colors()
    if not disablecolorskipped:
        curses.init_pair(1, -1, -1)
        curses.init_pair(2, -1, -1)
        curses.init_pair(3, -1, -1)
        curses.init_pair(4, -1, -1)
    else:
Mario Hock's avatar
Mario Hock committed
151
152
153
154
155
156
        curses.init_pair(1, curses.COLOR_MAGENTA, -1)         # "CPUX", "ethX"
        curses.init_pair(2, curses.COLOR_BLUE, -1)            # "util", "sent", "received", ...
        curses.init_pair(3, curses.COLOR_GREEN, -1)           # <values>
        curses.init_pair(4, curses.COLOR_YELLOW, -1)          # "Total", (<cpu-system> ?)
        curses.init_pair(5, curses.COLOR_CYAN, -1)            # <cpu-system> / <cpu-other>
        curses.init_pair(6, curses.COLOR_WHITE, -1)           # <cpu-system> / <cpu-other>
Mario Hock's avatar
Mario Hock committed
157
        curses.init_pair(7, curses.COLOR_RED, -1)
Mario Hock's avatar
Mario Hock committed
158
159

    ## Show some output to avoid upsetting the user
160
161
162
163
164
165
    try:
        stdscr.addstr(3, 3, "loading ...", curses.A_BOLD)
        stdscr.refresh()
    except:
        print( "\nDisplay Error! (Check terminal-size)" )

Mario Hock's avatar
Mario Hock committed
166
167
168
169



def display(measurement):
170
171
172
173
174
175
176
177
178
    try:
        return _display(measurement)
    except:
        print( "\nDisplay Error! (Check terminal-size)" )

        return True


def _display(measurement):
Mario Hock's avatar
Mario Hock committed
179
180
    global stdscr

Mario Hock's avatar
Mario Hock committed
181
    ## Press 'q' to quit.
Mario Hock's avatar
Mario Hock committed
182
183
184
185
    pressedkey = stdscr.getch()
    if pressedkey == ord('q'):
        return False

Mario Hock's avatar
Mario Hock committed
186
    ## Header
Mario Hock's avatar
Mario Hock committed
187
188
189
    stdscr.clear()
    stdscr.border(0)
    timenow = time.strftime("%H:%M:%S")
Mario Hock's avatar
Mario Hock committed
190
    stdscr.addstr(1, 1, 'CPUnetLOG', curses.A_BOLD)
Mario Hock's avatar
Mario Hock committed
191
192
193
194
    stdscr.addstr(1, LABEL_Sent, 'Time: {}'.format( timenow ), curses.A_BOLD)
    stdscr.addstr(1, 39, 'Interval: {}s'.format( round(measurement.timespan, 1) ), curses.A_BOLD)
    stdscr.addstr(1, 62, 'Logging: ', curses.A_BOLD)
    _display_logging_state(1, 71)
Mario Hock's avatar
Mario Hock committed
195
196
    stdscr.refresh()

Mario Hock's avatar
Mario Hock committed
197
198
199
200
201
    y = 3

    ## CPU ##
    num=1
    for cpu in measurement.cpu_times_percent:
Mario Hock's avatar
Mario Hock committed
202
        # static labels
Mario Hock's avatar
Mario Hock committed
203
        stdscr.addstr(y, 1, 'CPU{0}'.format( num ), curses.color_pair(1))
Mario Hock's avatar
Mario Hock committed
204
        stdscr.addstr(y, LABEL_CPU_UTIL, 'util: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
205
        stdscr.addstr(y, LABEL_CPU_UTIL+26, '|', curses.color_pair(2))
206
207
        #stdscr.addstr(y, LABEL_CPU_USER, 'type: ', curses.color_pair(2))
        #stdscr.addstr(y, LABEL_CPU_SYSTEM, 'system: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
208
209

        # CPU bar
Mario Hock's avatar
Mario Hock committed
210
        _display_cpu_bar( y, LABEL_CPU_UTIL+6, cpu )
Mario Hock's avatar
Mario Hock committed
211
212

        # user/system
213
        cpu_sorted = helpers.sort_named_tuple(cpu, skip="idle")
214
215
        t = '{0: >8}'.format( CPU_TYPE_LABELS[cpu_sorted[0][0]] )
        stdscr.addstr(y, LABEL_CPU_1, t, curses.color_pair(4))
216
        stdscr.addstr("{:>5.2f}%".format(cpu_sorted[0][1]), curses.color_pair(3))
217
218
219

        t = '{0: >8}'.format( CPU_TYPE_LABELS[cpu_sorted[1][0]] )
        stdscr.addstr(y, LABEL_CPU_2, t, curses.color_pair(4))
220
        stdscr.addstr("{:>5.2f}%".format(cpu_sorted[1][1]), curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
221
222
223
224
225
226
227
228
229
230

        num += 1
        y += 1




    ## Network ##

    y += 1
Mario Hock's avatar
Mario Hock committed
231
    stdscr.hline(y, 1, "-", 78)
Mario Hock's avatar
Mario Hock committed
232
233
    y += 1

Mario Hock's avatar
Mario Hock committed
234
235
236
237
238
239
240
241
242
243
    # display all nics (if not set otherwise)
    if nics:
        active_nics = nics
    else:
        active_nics = measurement.net_io.keys()

    sum_sending = 0
    sum_receiving = 0

    ## display the values
Mario Hock's avatar
Mario Hock committed
244
    for nic in sorted(active_nics):
Mario Hock's avatar
Mario Hock committed
245
246
        values = measurement.net_io[nic]

Mario Hock's avatar
Mario Hock committed
247
248
        _send = values.ratio["bytes_sent"] * 8  # Bits/s
        _recv = values.ratio["bytes_recv"] * 8  # Bits/s
Mario Hock's avatar
Mario Hock committed
249
        sending = _format_net_speed( _send )
Mario Hock's avatar
..    
Mario Hock committed
250
        send_ratio = _calculate_net_ratio( _send, nic_speeds[nic] )
Mario Hock's avatar
Mario Hock committed
251
        receiving = _format_net_speed( _recv )
Mario Hock's avatar
..    
Mario Hock committed
252
        receive_ratio = _calculate_net_ratio( _recv, nic_speeds[nic] )
Mario Hock's avatar
Mario Hock committed
253
254
255

        sum_sending += _send
        sum_receiving += _recv
Mario Hock's avatar
Mario Hock committed
256
257

        stdscr.addstr(y, 1, '{0}'.format(nic), curses.color_pair(1))
Mario Hock's avatar
Mario Hock committed
258
        stdscr.addstr(y, LABEL_Sent, 'Sent: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
259
        stdscr.addstr(y, LABEL_Sent+26, "|", curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
260
        stdscr.addstr(y, LABEL_Received, 'Received: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
261
        stdscr.addstr(y, LABEL_Received+30, "|", curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
262

Mario Hock's avatar
Mario Hock committed
263
        ## TODO rewrite in nice ^^ [see _display_cpu_bar()]
Mario Hock's avatar
Mario Hock committed
264
265
266
267
        ## XXX prototypical "inline"-coloring
        _snd_str = '{0} {1}/s'.format(sending, unit, send_ratio)
        _snd_str += " " * (20-len(_snd_str))
        _load_len = int(send_ratio * 20)
Mario Hock's avatar
Mario Hock committed
268
269
        stdscr.addstr(y, LABEL_Sent+6, _snd_str[0:_load_len], curses.color_pair(3)|curses.A_REVERSE)
        stdscr.addstr(y, LABEL_Sent+6+_load_len, _snd_str[_load_len:], curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
270
271
272
273

        _recv_str = '{0} {1}/s'.format(receiving, unit, send_ratio)
        _recv_str += " " * (20-len(_recv_str))
        _load_len = int(receive_ratio * 20)
Mario Hock's avatar
Mario Hock committed
274
275
        stdscr.addstr(y, LABEL_Received+10, _recv_str[0:_load_len], curses.color_pair(3)|curses.A_REVERSE)
        stdscr.addstr(y, LABEL_Received+10+_load_len, _recv_str[_load_len:], curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
276

Mario Hock's avatar
Mario Hock committed
277
        ## XXX TESTING
Mario Hock's avatar
Mario Hock committed
278
279
280
281
282
        #y += 1
        #stdscr.addstr(y, 25, "|" + " "*20 + "|", curses.color_pair(2))
        #stdscr.addstr(y, 26, " " * int(send_ratio * 20), curses.color_pair(3)|curses.A_REVERSE)
        #stdscr.addstr(y, 59, "|" + " "*20 + "|", curses.color_pair(2))
        #stdscr.addstr(y, 60, " " * int(receive_ratio * 20), curses.color_pair(3)|curses.A_REVERSE)
Mario Hock's avatar
Mario Hock committed
283

Mario Hock's avatar
Mario Hock committed
284
285
286
287
288
        y += 1

    ## Total
    y+=1
    stdscr.addstr(y, 1, 'Total:', curses.color_pair(4))
Mario Hock's avatar
Mario Hock committed
289
290
291
292
    stdscr.addstr(y, LABEL_Sent, 'Sent:', curses.color_pair(2))
    stdscr.addstr(y, LABEL_Sent+6, '{0} {1}/s'.format(_format_net_speed(sum_sending), unit), curses.color_pair(3))
    stdscr.addstr(y, LABEL_Received, 'Received:', curses.color_pair(2))
    stdscr.addstr(y, LABEL_Received+10, '{0} {1}/s'.format(_format_net_speed(sum_receiving),unit), curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
293

Mario Hock's avatar
Mario Hock committed
294
295
296
297
298
299
300


    ## Show logging comment
    comment = logging_manager.get_logging_comment()
    if ( comment ):
        y += 3
        stdscr.addstr(y, 3, 'Comment: ', curses.A_BOLD)
301
302
303
304
305

        parts = ( comment[i:i+COMMENT_WIDTH] for i in range(0, len(comment), COMMENT_WIDTH) )
        for part in parts:
            stdscr.addstr(y, 3+9, part)
            y += 1
Mario Hock's avatar
Mario Hock committed
306

Mario Hock's avatar
Mario Hock committed
307
308
309
310
311
312
313
314
315
316
317
318
319
    stdscr.refresh()

    return True


def close():
    global stdscr

    curses.nocbreak()
    stdscr.keypad(False)
    curses.echo()
    curses.curs_set(True)
    curses.endwin()